use crate::{
InternalError, InternalErrorOrigin,
dto::{
abi::v1::{CanisterInitAuthority, CanisterInitPayload},
env::EnvBootstrapArgs,
fleet_activation::FleetActivationPhase,
},
ids::{CanisterRole, ManagedCanisterBinding},
log::Topic,
ops::{
config::ConfigOps,
ic::release_build::ReleaseBuildOps,
runtime::{fleet_activation::FleetActivationRuntimeOps, memory::MemoryRegistryOps},
storage::{fleet_activation::FleetActivationOps, state::fleet::FleetStateOps},
},
workflow::{
env::EnvWorkflow,
runtime::{
RuntimeWorkflow, auth::RuntimeAuthWorkflow, log_memory_summary,
rebuild_derived_storage_indexes,
},
},
};
pub fn init_nonroot_canister(
canister_role: CanisterRole,
payload: CanisterInitPayload,
application_init_args: Option<Vec<u8>>,
) -> Result<(), InternalError> {
let CanisterInitPayload {
install_id,
release_build_id,
authority,
} = payload;
let fleet = match &authority {
CanisterInitAuthority::Infrastructure { fleet, .. } => fleet.clone(),
CanisterInitAuthority::Component { binding, .. } => binding.authority.binding.fleet.clone(),
CanisterInitAuthority::ComponentChild { binding, .. } => {
binding.component.authority.binding.fleet.clone()
}
};
let component_binding = match &authority {
CanisterInitAuthority::Infrastructure { .. } => None,
CanisterInitAuthority::Component { binding, .. } => {
Some(ManagedCanisterBinding::Component(binding.clone()))
}
CanisterInitAuthority::ComponentChild { binding, .. } => {
Some(ManagedCanisterBinding::ComponentChild(binding.clone()))
}
};
initialize_nonroot_base(&canister_role)?;
FleetActivationRuntimeOps::set_managed();
let embedded_release_build_id = ReleaseBuildOps::embedded_release_build_id()?;
FleetActivationOps::initialize_nonroot_prepared(
fleet,
install_id,
release_build_id,
embedded_release_build_id,
component_binding,
application_init_args,
)
.map_err(crate::ops::storage::StorageOpsError::from)?;
register_managed_nonroot_authority(&canister_role, authority)?;
Ok(())
}
pub fn init_local_nonroot_canister(
canister_role: CanisterRole,
env: EnvBootstrapArgs,
) -> Result<(), InternalError> {
initialize_nonroot_base(&canister_role)?;
FleetActivationRuntimeOps::set_standalone_local();
EnvWorkflow::init_env_from_args(env, canister_role.clone()).map_err(|err| {
InternalError::invariant(
InternalErrorOrigin::Workflow,
format!("env import failed: {err}"),
)
})?;
register_nonroot_runtime_contract(&canister_role)?;
RuntimeWorkflow::start_all()
}
fn initialize_nonroot_base(canister_role: &CanisterRole) -> Result<(), InternalError> {
MemoryRegistryOps::bootstrap_registry().map_err(|err| {
InternalError::invariant(
InternalErrorOrigin::Workflow,
format!("memory init failed: {err}"),
)
})?;
rebuild_derived_storage_indexes()?;
crate::log::set_ready();
crate::log!(Topic::Init, Info, "🏁 init: {}", canister_role);
log_memory_summary();
Ok(())
}
fn register_managed_nonroot_authority(
canister_role: &CanisterRole,
authority: CanisterInitAuthority,
) -> Result<(), InternalError> {
match authority {
CanisterInitAuthority::Infrastructure { env, .. } => {
if !canister_role.is_wasm_store() {
return Err(InternalError::invalid_input(
"application Canisters require Registry-issued Component init authority",
));
}
EnvWorkflow::init_env_from_args(env, canister_role.clone()).map_err(|err| {
InternalError::invariant(
InternalErrorOrigin::Workflow,
format!("infrastructure env import failed: {err}"),
)
})?;
}
CanisterInitAuthority::Component { root, binding } => {
EnvWorkflow::init_component(&root, binding, canister_role)?;
}
CanisterInitAuthority::ComponentChild { root, binding } => {
EnvWorkflow::init_component_child(&root, binding, canister_role)?;
}
}
register_nonroot_runtime_contract(canister_role)
}
fn register_nonroot_runtime_contract(canister_role: &CanisterRole) -> Result<(), InternalError> {
let app_mode = ConfigOps::app_init_mode().map_err(|err| {
InternalError::invariant(
InternalErrorOrigin::Workflow,
format!("app mode init failed: {err}"),
)
})?;
FleetStateOps::init_mode(app_mode);
let canister_cfg = ConfigOps::current_canister()?;
RuntimeAuthWorkflow::ensure_nonroot_crypto_contract(canister_role, &canister_cfg)?;
Ok(())
}
pub fn post_upgrade_nonroot_canister_after_memory_init(
canister_role: CanisterRole,
) -> Result<bool, InternalError> {
FleetActivationRuntimeOps::set_managed();
restore_nonroot_after_upgrade(canister_role)?;
let active = FleetActivationOps::status(false)
.map_err(crate::ops::storage::StorageOpsError::from)?
.phase
== FleetActivationPhase::Active;
if active {
RuntimeWorkflow::start_all()?;
}
Ok(active)
}
pub fn post_upgrade_local_nonroot_canister_after_memory_init(
canister_role: CanisterRole,
) -> Result<bool, InternalError> {
FleetActivationRuntimeOps::set_standalone_local();
restore_nonroot_after_upgrade(canister_role)?;
RuntimeWorkflow::start_all()?;
Ok(true)
}
fn restore_nonroot_after_upgrade(canister_role: CanisterRole) -> Result<(), InternalError> {
rebuild_derived_storage_indexes()?;
crate::log::set_ready();
crate::log!(
Topic::Init,
Info,
"🏁 post_upgrade_nonroot_canister: {}",
canister_role
);
log_memory_summary();
let canister_cfg = ConfigOps::current_canister().map_err(|err| {
InternalError::invariant(
InternalErrorOrigin::Workflow,
format!("current canister config unavailable during post-upgrade runtime init: {err}"),
)
})?;
RuntimeAuthWorkflow::ensure_nonroot_crypto_contract(&canister_role, &canister_cfg)?;
Ok(())
}