pub mod fixture;
use crate::{
cdk::{
bounded_cell::BoundedCell,
structures::{DefaultMemoryImpl, memory::RuntimeMemory},
},
config::{
ComponentDeploymentLabel, ComponentDeploymentLimits, ComponentDeploymentPurpose,
FleetServiceMemberPurpose, FleetServicePlacementPolicy,
},
storage::prelude::*,
};
use canic_contracts::ids::{
CanisterRole, ComponentBinding, ComponentDeploymentConfigurationDigest,
ComponentGroupMemberPath, ComponentGroupPlacementId, ComponentGroupSpecId, ComponentInstanceId,
ComponentSpecId, FleetBinding, FleetRegistryAuthority, FleetServiceId, FleetSubnetRootBinding,
FleetSubnetRootReleaseSet, ManagedCanisterBinding, ReleaseBuildId, SubnetId,
};
use std::cell::RefCell;
pub const MAX_FLEET_ACTIVATION_RECORD_BYTES: u32 = 2_097_152;
pub const MAX_RETAINED_PREPARED_CREDENTIAL_GENERATIONS: usize = 2;
std::thread_local! {
static FLEET_ACTIVATION: RefCell<
BoundedCell<Option<FleetActivationRecord>, RuntimeMemory<DefaultMemoryImpl>>,
> = RefCell::new(BoundedCell::init(crate::ic_memory_key!(
authority = CANIC_CORE_MEMORY_AUTHORITY,
key = "canic.core.fleet.activation.v1",
), None));
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetActivationIdentityRecord {
pub fleet: FleetBinding,
pub operation_id: [u8; 32],
pub release_build_id: ReleaseBuildId,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum FleetCascadeActivationEvidenceRecord {
Source {
cascade_manifest_hash: [u8; 32],
},
Applied {
state_snapshot_hash: [u8; 32],
topology_snapshot_hash: [u8; 32],
},
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetCredentialGenerationRefRecord {
pub generation: u64,
pub manifest_hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetActivationEvidenceRecord {
pub cascade: Option<FleetCascadeActivationEvidenceRecord>,
pub credential: Option<FleetCredentialGenerationRefRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum FleetActivationStateRecord {
Prepared {
identity: FleetActivationIdentityRecord,
evidence: FleetActivationEvidenceRecord,
application_init_args: Option<Vec<u8>>,
},
Active {
identity: FleetActivationIdentityRecord,
evidence: FleetActivationEvidenceRecord,
activated_at_ns: u64,
},
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetCascadeManifestEntryRecord {
pub principal: Principal,
pub state_snapshot_hash: [u8; 32],
pub topology_snapshot_hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetCredentialManifestEntryRecord {
pub root_issuer: Principal,
pub subject_canister: Principal,
pub not_before_ns: u64,
pub expires_at_ns: u64,
pub key_identity_hash: [u8; 32],
pub cert_hash: [u8; 32],
pub proof_hash: [u8; 32],
pub bundle_hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetCredentialManifestRecord {
pub fleet: crate::ids::FleetKey,
pub activation_id: [u8; 32],
pub generation: u64,
pub root_policy_set_hash: [u8; 32],
pub renewal_template_set_hash: [u8; 32],
pub entries: Vec<FleetCredentialManifestEntryRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetSubnetRootAuthorityRecord {
pub binding: FleetSubnetRootBinding,
pub initial_release_set: FleetSubnetRootReleaseSet,
pub expected_module_hash: [u8; 32],
pub wasm_store_activation: crate::ids::FleetSubnetWasmStoreActivationAuthority,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetSubnetWasmStoreAuthorityRecord {
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(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(
target_pointer_width = "64",
expect(
clippy::large_enum_variant,
reason = "the bounded reinstall-only record preserves the protected context's direct field shape"
)
)]
pub enum ProtectedComponentDeploymentRecord {
UngroupedOrdinary {
binding: ComponentBinding,
},
GroupMember {
binding: ComponentBinding,
configuration_digest: ComponentDeploymentConfigurationDigest,
group_placement: ComponentGroupPlacementId,
component_group: ComponentGroupSpecId,
member_path: ComponentGroupMemberPath,
purpose: ComponentDeploymentPurpose,
labels: Vec<ComponentDeploymentLabel>,
limits: ComponentDeploymentLimits,
},
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentRuntimeRecord {
pub root_install_id: [u8; 32],
pub component_install_id: [u8; 32],
pub fixture: Option<fixture::FixtureAssignmentRecord>,
pub binding: ManagedCanisterBinding,
pub deployment: ProtectedComponentDeploymentRecord,
pub directory: Option<ComponentRuntimeDirectoryRecord>,
pub activation: Option<ComponentRuntimeActivationRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentRuntimeDirectoryAuthorityRecord {
pub fleet: FleetDirectorySnapshotRecord,
pub component: ComponentDirectoryHeadRecord,
pub component_group: Option<ComponentGroupDirectoryRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetDirectorySnapshotRecord {
pub provenance: FleetDirectoryProvenanceRecord,
pub fleet_subnet_roots: Vec<FleetSubnetRootDirectoryEntryRecord>,
pub services: Vec<FleetDirectoryServiceRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetDirectoryServiceComponentRecord {
pub member_purpose: FleetServiceMemberPurpose,
pub component: ComponentInstanceId,
pub fleet_subnet_root: Principal,
pub canister_id: Principal,
pub group_placement: ComponentGroupPlacementId,
pub member_path: ComponentGroupMemberPath,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetDirectoryServiceRecord {
pub service: FleetServiceId,
pub role: CanisterRole,
pub component_spec: ComponentSpecId,
pub mode: FleetServiceModeRecord,
pub placement: FleetServicePlacementPolicy,
pub members: Vec<FleetDirectoryServiceComponentRecord>,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum FleetServiceModeRecord {
AuthorityReplica,
ActivePool,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentGroupDirectoryProvenanceRecord {
pub authority: FleetRegistryAuthority,
pub fleet_subnet_root: Principal,
pub group_placement: ComponentGroupPlacementId,
pub component_group: ComponentGroupSpecId,
pub operation_id: [u8; 32],
pub plan_hash: [u8; 32],
pub placement_receipt_content_hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentGroupDirectoryMemberRecord {
pub member_path: ComponentGroupMemberPath,
pub component_spec: ComponentSpecId,
pub purpose: ComponentDeploymentPurpose,
pub labels: Vec<ComponentDeploymentLabel>,
pub binding: ComponentBinding,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentGroupDirectoryRecord {
pub provenance: ComponentGroupDirectoryProvenanceRecord,
pub members: Vec<ComponentGroupDirectoryMemberRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetDirectoryProvenanceRecord {
pub registry: FleetRegistryVersionRecord,
pub source_fleet_subnet_root: Principal,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetRegistryVersionRecord {
pub authority: FleetRegistryAuthority,
pub revision: u64,
pub content_hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct FleetSubnetRootDirectoryEntryRecord {
pub placement_subnet: SubnetId,
pub fleet_subnet_root: Principal,
pub status: FleetSubnetRootStatusRecord,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum FleetSubnetRootStatusRecord {
Joining,
Active,
Draining,
Removed,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentDirectoryHeadRecord {
pub provenance: ComponentDirectoryProvenanceRecord,
pub descendant_count: u32,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentDirectoryProvenanceRecord {
pub component: ComponentBinding,
pub source_fleet_subnet_root: Principal,
pub component_registry_revision: u64,
pub component_registry_content_hash: [u8; 32],
pub synchronized_at_ns: u64,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentRuntimeDirectoryRecord {
pub authority: ComponentRuntimeDirectoryAuthorityRecord,
pub authority_hash: [u8; 32],
pub direct_children_hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ComponentRuntimeActivationRecord {
pub directory: ComponentRuntimeDirectoryRecord,
pub activated_at_ns: u64,
pub startup: ApplicationStartupRecord,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ApplicationStartupRecord {
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub arguments: Option<Vec<u8>>,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub release: Option<crate::model::caller_authority::CallerPublicationRecord>,
pub initialized: bool,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OrdinaryActivationRecord {
pub state: FleetActivationStateRecord,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub prepared_state_snapshot_hash: Option<[u8; 32]>,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub prepared_topology_snapshot_hash: Option<[u8; 32]>,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub component_runtime: Option<ComponentRuntimeRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RootActivationRecord {
pub state: FleetActivationStateRecord,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub prepared_state_snapshot_hash: Option<[u8; 32]>,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub prepared_topology_snapshot_hash: Option<[u8; 32]>,
pub root_authority: FleetSubnetRootAuthorityRecord,
pub wasm_store_authority: FleetSubnetWasmStoreAuthorityRecord,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub cascade_manifest: Option<Vec<FleetCascadeManifestEntryRecord>>,
pub credential_manifests: Vec<FleetCredentialManifestRecord>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct WasmStoreActivationRecord {
pub state: FleetActivationStateRecord,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub prepared_state_snapshot_hash: Option<[u8; 32]>,
#[serde(deserialize_with = "crate::cdk::serialize::required_option")]
pub prepared_topology_snapshot_hash: Option<[u8; 32]>,
pub wasm_store_authority: FleetSubnetWasmStoreAuthorityRecord,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FleetActivationRecord {
pub bytes: Vec<u8>,
}
impl FleetActivationRecord {
pub const STATE_CONTRACT_NAME: &'static str = "FleetActivationRecord";
}
impl crate::cdk::structures::storable::Storable for FleetActivationRecord {
const BOUND: crate::cdk::structures::storable::Bound =
crate::cdk::structures::storable::Bound::Bounded {
max_size: MAX_FLEET_ACTIVATION_RECORD_BYTES,
is_fixed_size: false,
};
fn to_bytes(&self) -> std::borrow::Cow<'_, [u8]> {
std::borrow::Cow::Borrowed(&self.bytes)
}
fn into_bytes(self) -> Vec<u8> {
self.bytes
}
fn from_bytes(bytes: std::borrow::Cow<'_, [u8]>) -> Self {
Self {
bytes: bytes.into_owned(),
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FleetActivationData {
pub record: Option<FleetActivationRecord>,
}
impl FleetActivationData {
pub const STATE_CONTRACT_NAME: &'static str = "FleetActivationData";
}
pub struct FleetActivation;
impl FleetActivation {
pub(crate) fn get() -> Option<FleetActivationRecord> {
FLEET_ACTIVATION.with_borrow(|store| store.get().clone())
}
pub(crate) fn initialize(record: FleetActivationRecord) -> bool {
FLEET_ACTIVATION.with_borrow_mut(|store| {
if store.get().is_some() {
return false;
}
store.set(Some(record));
true
})
}
pub(crate) fn replace(record: FleetActivationRecord) -> bool {
FLEET_ACTIVATION.with_borrow_mut(|store| {
if store.get().is_none() {
return false;
}
store.set(Some(record));
true
})
}
#[cfg(test)]
pub(crate) fn import(data: FleetActivationData) {
FLEET_ACTIVATION.with_borrow_mut(|store| {
store.set(data.record);
});
}
}