canic_core/ops/runtime/
mod.rs

1pub mod cycles;
2pub mod log;
3pub mod metrics;
4
5use crate::{
6    VERSION,
7    cdk::{
8        api::{canister_self, trap},
9        println,
10        types::Principal,
11    },
12    ids::{CanisterRole, SubnetRole},
13    log::Topic,
14    ops::{
15        ic::{Network, build_network},
16        service::TimerService,
17        storage::{
18            CanisterInitPayload,
19            directory::{AppDirectoryOps, SubnetDirectoryOps},
20            env::{EnvData, EnvOps},
21            memory::MemoryRegistryOps,
22            topology::{SubnetCanisterRegistryOps, SubnetIdentity},
23        },
24    },
25};
26use canic_memory::runtime::init_eager_tls;
27
28fn init_memory_or_trap(phase: &str) {
29    if let Err(err) = MemoryRegistryOps::init_memory() {
30        println!("[canic] FATAL: memory init failed during {phase}: {err}");
31        let msg = format!("canic init failed during {phase}: memory init failed: {err}");
32        trap(&msg);
33    }
34}
35
36fn ensure_nonroot_env(canister_role: CanisterRole, mut env: EnvData) -> EnvData {
37    let mut missing = Vec::new();
38    if env.prime_root_pid.is_none() {
39        missing.push("prime_root_pid");
40    }
41    if env.subnet_role.is_none() {
42        missing.push("subnet_role");
43    }
44    if env.subnet_pid.is_none() {
45        missing.push("subnet_pid");
46    }
47    if env.root_pid.is_none() {
48        missing.push("root_pid");
49    }
50    if env.canister_role.is_none() {
51        missing.push("canister_role");
52    }
53    if env.parent_pid.is_none() {
54        missing.push("parent_pid");
55    }
56
57    if missing.is_empty() {
58        return env;
59    }
60
61    assert!(
62        build_network() != Some(Network::Ic),
63        "nonroot init missing env fields on ic: {}",
64        missing.join(", ")
65    );
66
67    let root_pid = Principal::from_slice(&[0xBB; 29]);
68    let subnet_pid = Principal::from_slice(&[0xAA; 29]);
69
70    env.prime_root_pid.get_or_insert(root_pid);
71    env.subnet_role.get_or_insert(SubnetRole::PRIME);
72    env.subnet_pid.get_or_insert(subnet_pid);
73    env.root_pid.get_or_insert(root_pid);
74    env.canister_role.get_or_insert(canister_role);
75    env.parent_pid.get_or_insert(root_pid);
76
77    env
78}
79
80/// root_init
81/// Bootstraps the root canister runtime and environment.
82pub fn root_init(identity: SubnetIdentity) {
83    // --- Phase 1: Init base systems ---
84
85    // log - clear some space
86    println!("");
87    println!("");
88    println!("");
89    crate::log!(
90        Topic::Init,
91        Info,
92        "🔧 --------------------- 'canic v{VERSION} -----------------------",
93    );
94    crate::log!(Topic::Init, Info, "🏁 init: root ({identity:?})");
95
96    // init
97    init_eager_tls();
98    init_memory_or_trap("root_init");
99
100    // --- Phase 2: Env registration ---
101    let self_pid = canister_self();
102    EnvOps::set_canister_role(CanisterRole::ROOT);
103    EnvOps::set_root_pid(self_pid);
104
105    match identity {
106        SubnetIdentity::Prime => {
107            EnvOps::set_prime_root_pid(self_pid);
108            EnvOps::set_subnet_role(SubnetRole::PRIME);
109            EnvOps::set_subnet_pid(self_pid);
110        }
111        SubnetIdentity::Standard(params) => {
112            EnvOps::set_prime_root_pid(params.prime_root_pid);
113            EnvOps::set_subnet_role(params.subnet_type);
114            EnvOps::set_subnet_pid(self_pid);
115        }
116        SubnetIdentity::Manual(subnet_pid) => {
117            EnvOps::set_prime_root_pid(self_pid);
118            EnvOps::set_subnet_role(SubnetRole::PRIME);
119            EnvOps::set_subnet_pid(subnet_pid);
120        }
121    }
122
123    SubnetCanisterRegistryOps::register_root(self_pid);
124
125    // --- Phase 3: Service startup ---
126    TimerService::start_all_root();
127}
128
129/// root_post_upgrade
130pub fn root_post_upgrade() {
131    // --- Phase 1: Init base systems ---
132    crate::log!(Topic::Init, Info, "🏁 post_upgrade: root");
133    init_eager_tls();
134    init_memory_or_trap("root_post_upgrade");
135
136    // --- Phase 2: Env registration ---
137
138    // --- Phase 3: Service startup ---
139    TimerService::start_all_root();
140}
141
142/// nonroot_init
143pub fn nonroot_init(canister_role: CanisterRole, payload: CanisterInitPayload) {
144    // --- Phase 1: Init base systems ---
145    crate::log!(Topic::Init, Info, "🏁 init: {}", canister_role);
146    init_eager_tls();
147    init_memory_or_trap("nonroot_init");
148
149    // --- Phase 2: Payload registration ---
150    let env = ensure_nonroot_env(canister_role, payload.env);
151    if let Err(err) = EnvOps::import(env) {
152        println!("[canic] FATAL: env import failed during nonroot_init: {err}");
153        let msg = format!("canic init failed during nonroot_init: env import failed: {err}");
154        trap(&msg);
155    }
156    AppDirectoryOps::import(payload.app_directory);
157    SubnetDirectoryOps::import(payload.subnet_directory);
158
159    // --- Phase 3: Service startup ---
160    TimerService::start_all();
161}
162
163/// nonroot_post_upgrade
164pub fn nonroot_post_upgrade(canister_role: CanisterRole) {
165    // --- Phase 1: Init base systems ---
166    crate::log!(Topic::Init, Info, "🏁 post_upgrade: {}", canister_role);
167    init_eager_tls();
168    init_memory_or_trap("nonroot_post_upgrade");
169
170    // --- Phase 2: Env registration ---
171
172    // --- Phase 3: Service startup ---
173    TimerService::start_all();
174}