canic_core/workflow/runtime/
mod.rs1pub mod cycles;
2pub mod log;
3pub mod random;
4
5use crate::{
6 VERSION,
7 cdk::{
8 api::{canister_self, trap},
9 println,
10 types::Principal,
11 },
12 dto::{abi::v1::CanisterInitPayload, env::EnvView, subnet::SubnetIdentity},
13 ids::{CanisterRole, SubnetRole},
14 infra::ic::{Network, build_network},
15 log::Topic,
16 ops::{
17 adapter::directory::{app_directory_from_view, subnet_directory_from_view},
18 runtime::{env::EnvOps, memory::MemoryRegistryOps},
19 storage::{
20 directory::{AppDirectoryOps, SubnetDirectoryOps},
21 registry::SubnetRegistryOps,
22 },
23 },
24 workflow,
25};
26use canic_memory::runtime::init_eager_tls;
27
28pub struct Runtime;
34
35impl Runtime {
36 pub fn start_all() {
38 workflow::runtime::cycles::scheduler::start();
39 workflow::runtime::log::retention::start();
40 workflow::runtime::random::scheduler::start();
41 }
42
43 pub fn start_all_root() {
45 EnvOps::require_root().unwrap_or_else(|e| fatal("start_all_root", e));
46
47 Self::start_all();
49
50 workflow::pool::scheduler::start();
52 }
53}
54
55fn fatal(phase: &str, err: impl std::fmt::Display) -> ! {
62 let msg = format!("canic init failed during {phase}: {err}");
63 println!("[canic] FATAL: {msg}");
64 trap(&msg);
65}
66
67fn init_memory_or_trap(phase: &str) {
68 if let Err(err) = MemoryRegistryOps::init_memory() {
69 fatal(phase, format!("memory init failed: {err}"));
70 }
71}
72
73fn ensure_nonroot_env(canister_role: CanisterRole, mut env: EnvView) -> EnvView {
74 let mut missing = Vec::new();
75
76 if env.prime_root_pid.is_none() {
77 missing.push("prime_root_pid");
78 }
79 if env.subnet_role.is_none() {
80 missing.push("subnet_role");
81 }
82 if env.subnet_pid.is_none() {
83 missing.push("subnet_pid");
84 }
85 if env.root_pid.is_none() {
86 missing.push("root_pid");
87 }
88 if env.canister_role.is_none() {
89 missing.push("canister_role");
90 }
91 if env.parent_pid.is_none() {
92 missing.push("parent_pid");
93 }
94
95 if missing.is_empty() {
96 return env;
97 }
98
99 if build_network() == Some(Network::Ic) {
100 fatal(
101 "nonroot_init",
102 format!("missing env fields on ic: {}", missing.join(", ")),
103 );
104 }
105
106 let root_pid = Principal::from_slice(&[0xBB; 29]);
108 let subnet_pid = Principal::from_slice(&[0xAA; 29]);
109
110 env.prime_root_pid.get_or_insert(root_pid);
111 env.subnet_role.get_or_insert(SubnetRole::PRIME);
112 env.subnet_pid.get_or_insert(subnet_pid);
113 env.root_pid.get_or_insert(root_pid);
114 env.canister_role.get_or_insert(canister_role);
115 env.parent_pid.get_or_insert(root_pid);
116
117 env
118}
119
120pub fn init_root_canister(identity: SubnetIdentity) {
126 init_eager_tls();
128 init_memory_or_trap("init_root_canister");
129 crate::log::set_ready();
130
131 println!("");
133 println!("");
134 println!("");
135 crate::log!(
136 Topic::Init,
137 Info,
138 "🔧 --------------------- canic v{VERSION} -----------------------",
139 );
140 crate::log!(Topic::Init, Info, "🏁 init: root ({identity:?})");
141
142 let self_pid = canister_self();
144 EnvOps::set_canister_role(CanisterRole::ROOT);
145 EnvOps::set_root_pid(self_pid);
146
147 match identity {
148 SubnetIdentity::Prime => {
149 EnvOps::set_prime_root_pid(self_pid);
150 EnvOps::set_subnet_role(SubnetRole::PRIME);
151 EnvOps::set_subnet_pid(self_pid);
152 }
153 SubnetIdentity::Standard(params) => {
154 EnvOps::set_prime_root_pid(params.prime_root_pid);
155 EnvOps::set_subnet_role(params.subnet_type);
156 EnvOps::set_subnet_pid(self_pid);
157 }
158 SubnetIdentity::Manual(subnet_pid) => {
159 EnvOps::set_prime_root_pid(self_pid);
160 EnvOps::set_subnet_role(SubnetRole::PRIME);
161 EnvOps::set_subnet_pid(subnet_pid);
162 }
163 }
164
165 SubnetRegistryOps::register_root(self_pid);
166
167 Runtime::start_all_root();
169}
170
171pub fn post_upgrade_root_canister() {
176 init_eager_tls();
178 init_memory_or_trap("post_upgrade_root_canister");
179 crate::log::set_ready();
180 crate::log!(Topic::Init, Info, "🏁 post_upgrade_root_canister");
181
182 Runtime::start_all_root();
186}
187
188pub fn init_nonroot_canister(canister_role: CanisterRole, payload: CanisterInitPayload) {
193 init_eager_tls();
195 init_memory_or_trap("init_nonroot_canister");
196 crate::log::set_ready();
197 crate::log!(Topic::Init, Info, "🏁 init: {}", canister_role);
198
199 let env = ensure_nonroot_env(canister_role, payload.env);
201 if let Err(err) = EnvOps::import(env) {
202 fatal("init_nonroot_canister", format!("env import failed: {err}"));
203 }
204
205 AppDirectoryOps::import(app_directory_from_view(payload.app_directory));
206 SubnetDirectoryOps::import(subnet_directory_from_view(payload.subnet_directory));
207
208 Runtime::start_all();
210}
211
212pub fn post_upgrade_nonroot_canister(canister_role: CanisterRole) {
217 init_eager_tls();
219 init_memory_or_trap("post_upgrade_nonroot_canister");
220 crate::log::set_ready();
221 crate::log!(
222 Topic::Init,
223 Info,
224 "🏁 post_upgrade_nonroot_canister: {}",
225 canister_role
226 );
227
228 Runtime::start_all();
232}