1use crate::{
2 cdk::types::Principal,
3 dto::{
4 error::Error,
5 template::{
6 TemplateChunkInput, TemplateChunkResponse, TemplateChunkSetInfoResponse,
7 TemplateChunkSetPrepareInput, TemplateManifestInput, WasmStoreAdminCommand,
8 WasmStoreAdminResponse, WasmStoreCatalogEntryResponse,
9 WasmStorePublicationStateResponse, WasmStoreRetiredStoreStatusResponse,
10 WasmStoreStatusResponse,
11 },
12 },
13 ids::{CanisterRole, TemplateId, TemplateVersion, WasmStoreBinding, WasmStoreGcStatus},
14 ops::{
15 config::ConfigOps,
16 ic::IcOps,
17 runtime::template::WasmStoreCatalogOps,
18 storage::{
19 state::subnet::SubnetStateOps,
20 template::{TemplateManifestOps, WasmStoreGcExecutionStats, WasmStoreLimits},
21 },
22 },
23 workflow::runtime::template::WasmStorePublicationWorkflow,
24};
25
26const ROOT_WASM_STORE_BOOTSTRAP_TEMPLATE_ID: TemplateId = TemplateId::new("embedded:wasm_store");
27const ROOT_WASM_STORE_BOOTSTRAP_BINDING: WasmStoreBinding = WasmStoreBinding::new("bootstrap");
28
29pub struct EmbeddedTemplateApi;
34
35impl EmbeddedTemplateApi {
36 pub fn import_embedded_release_set(wasms: &'static [(CanisterRole, &[u8])]) {
38 WasmStorePublicationWorkflow::import_embedded_release_set(wasms);
39 }
40}
41
42pub struct WasmStoreBootstrapApi;
47
48impl WasmStoreBootstrapApi {
49 fn ensure_root_wasm_store_bootstrap_template(template_id: &TemplateId) -> Result<(), Error> {
51 if template_id == &ROOT_WASM_STORE_BOOTSTRAP_TEMPLATE_ID {
52 Ok(())
53 } else {
54 Err(Error::invalid(format!(
55 "bootstrap only accepts template '{ROOT_WASM_STORE_BOOTSTRAP_TEMPLATE_ID}'"
56 )))
57 }
58 }
59
60 fn normalize_root_wasm_store_bootstrap_manifest(
62 request: TemplateManifestInput,
63 ) -> Result<TemplateManifestInput, Error> {
64 if request.role != CanisterRole::WASM_STORE {
65 return Err(Error::invalid(format!(
66 "bootstrap only accepts role '{}'",
67 CanisterRole::WASM_STORE
68 )));
69 }
70
71 Self::ensure_root_wasm_store_bootstrap_template(&request.template_id)?;
72
73 let now_secs = IcOps::now_secs();
74
75 Ok(TemplateManifestInput {
76 template_id: ROOT_WASM_STORE_BOOTSTRAP_TEMPLATE_ID,
77 role: CanisterRole::WASM_STORE,
78 version: request.version,
79 payload_hash: request.payload_hash,
80 payload_size_bytes: request.payload_size_bytes,
81 store_binding: ROOT_WASM_STORE_BOOTSTRAP_BINDING,
82 chunking_mode: crate::ids::TemplateChunkingMode::Chunked,
83 manifest_state: crate::ids::TemplateManifestState::Approved,
84 approved_at: Some(now_secs),
85 created_at: now_secs,
86 })
87 }
88
89 pub fn import_embedded_release_catalog(entries: Vec<WasmStoreCatalogEntryResponse>) {
91 WasmStoreCatalogOps::import_embedded(entries);
92 }
93
94 pub fn stage_root_wasm_store_manifest(request: TemplateManifestInput) -> Result<(), Error> {
96 Self::stage_manifest(Self::normalize_root_wasm_store_bootstrap_manifest(request)?);
97 Ok(())
98 }
99
100 pub fn prepare_root_wasm_store_chunk_set(
102 request: TemplateChunkSetPrepareInput,
103 ) -> Result<TemplateChunkSetInfoResponse, Error> {
104 Self::ensure_root_wasm_store_bootstrap_template(&request.template_id)?;
105 Self::prepare_chunk_set(request)
106 }
107
108 pub fn publish_root_wasm_store_chunk(request: TemplateChunkInput) -> Result<(), Error> {
110 Self::ensure_root_wasm_store_bootstrap_template(&request.template_id)?;
111 Self::publish_chunk(request)
112 }
113
114 pub fn stage_manifest(input: TemplateManifestInput) {
116 TemplateManifestOps::replace_approved_from_input(input);
117 }
118
119 pub fn prepare_chunk_set(
121 request: TemplateChunkSetPrepareInput,
122 ) -> Result<TemplateChunkSetInfoResponse, Error> {
123 let now_secs = IcOps::now_secs();
124 TemplateManifestOps::prepare_chunk_set_from_input(request, now_secs).map_err(Error::from)
125 }
126
127 pub fn publish_chunk(request: TemplateChunkInput) -> Result<(), Error> {
129 TemplateManifestOps::publish_chunk_from_input(request).map_err(Error::from)
130 }
131}
132
133pub struct WasmStorePublicationApi;
138
139impl WasmStorePublicationApi {
140 pub async fn admin(cmd: WasmStoreAdminCommand) -> Result<WasmStoreAdminResponse, Error> {
142 WasmStorePublicationWorkflow::handle_admin(cmd)
143 .await
144 .map_err(Error::from)
145 }
146
147 pub async fn publish_current_release_set_to_store(store_pid: Principal) -> Result<(), Error> {
149 WasmStorePublicationWorkflow::publish_current_release_set_to_store(store_pid)
150 .await
151 .map_err(Error::from)
152 }
153
154 pub async fn publish_current_release_set_to_current_store() -> Result<(), Error> {
156 WasmStorePublicationWorkflow::publish_current_release_set_to_current_store()
157 .await
158 .map_err(Error::from)
159 }
160
161 pub fn set_current_publication_store_binding(binding: WasmStoreBinding) -> Result<(), Error> {
163 WasmStorePublicationWorkflow::set_current_publication_store_binding(binding)
164 .map_err(Error::from)
165 }
166
167 pub fn clear_current_publication_store_binding() {
169 WasmStorePublicationWorkflow::clear_current_publication_store_binding();
170 }
171
172 #[must_use]
174 pub fn retire_detached_publication_store_binding() -> Option<WasmStoreBinding> {
175 WasmStorePublicationWorkflow::retire_detached_publication_store_binding()
176 }
177
178 #[must_use]
180 pub fn publication_store_state() -> WasmStorePublicationStateResponse {
181 SubnetStateOps::publication_store_state_response()
182 }
183
184 pub async fn retired_publication_store_status()
186 -> Result<Option<WasmStoreRetiredStoreStatusResponse>, Error> {
187 WasmStorePublicationWorkflow::retired_publication_store_status()
188 .await
189 .map_err(Error::from)
190 }
191
192 pub async fn prepare_retired_publication_store_for_gc()
194 -> Result<Option<WasmStoreBinding>, Error> {
195 WasmStorePublicationWorkflow::prepare_retired_publication_store_for_gc()
196 .await
197 .map_err(Error::from)
198 }
199
200 pub async fn begin_retired_publication_store_gc() -> Result<Option<WasmStoreBinding>, Error> {
202 WasmStorePublicationWorkflow::begin_retired_publication_store_gc()
203 .await
204 .map_err(Error::from)
205 }
206
207 pub async fn complete_retired_publication_store_gc() -> Result<Option<WasmStoreBinding>, Error>
209 {
210 WasmStorePublicationWorkflow::complete_retired_publication_store_gc()
211 .await
212 .map_err(Error::from)
213 }
214
215 pub async fn finalize_retired_publication_store_binding()
217 -> Result<Option<(WasmStoreBinding, Principal)>, Error> {
218 WasmStorePublicationWorkflow::finalize_retired_publication_store_binding()
219 .await
220 .map_err(Error::from)
221 }
222
223 pub async fn delete_finalized_publication_store(
225 binding: WasmStoreBinding,
226 store_pid: Principal,
227 ) -> Result<(), Error> {
228 WasmStorePublicationWorkflow::delete_finalized_publication_store(binding, store_pid)
229 .await
230 .map_err(Error::from)
231 }
232}
233
234pub struct WasmStoreApi;
239
240impl WasmStoreApi {
241 fn current_store_limits() -> Result<WasmStoreLimits, Error> {
242 let store = ConfigOps::current_wasm_store()?;
243
244 Ok(WasmStoreLimits {
245 max_store_bytes: store.max_store_bytes(),
246 max_templates: store.max_templates(),
247 max_template_versions_per_template: store.max_template_versions_per_template(),
248 })
249 }
250
251 fn current_store_headroom_bytes() -> Result<Option<u64>, Error> {
252 Ok(ConfigOps::current_wasm_store()?.headroom_bytes())
253 }
254
255 pub fn import_embedded_release_set(wasms: &'static [(CanisterRole, &[u8])]) {
257 WasmStorePublicationWorkflow::import_embedded_release_set_to_local_store(wasms);
258 }
259
260 pub fn template_catalog() -> Result<Vec<WasmStoreCatalogEntryResponse>, Error> {
262 Ok(TemplateManifestOps::approved_catalog_response())
263 }
264
265 pub fn template_status(gc: WasmStoreGcStatus) -> Result<WasmStoreStatusResponse, Error> {
267 Ok(TemplateManifestOps::store_status_response(
268 Self::current_store_limits()?,
269 Self::current_store_headroom_bytes()?,
270 gc,
271 ))
272 }
273
274 pub fn prepare_chunk_set(
276 request: TemplateChunkSetPrepareInput,
277 ) -> Result<TemplateChunkSetInfoResponse, Error> {
278 let now_secs = IcOps::now_secs();
279 TemplateManifestOps::prepare_chunk_set_in_store_from_input(
280 request,
281 now_secs,
282 Self::current_store_limits()?,
283 )
284 .map_err(Error::from)
285 }
286
287 pub fn publish_chunk(request: TemplateChunkInput) -> Result<(), Error> {
289 TemplateManifestOps::publish_chunk_in_store_from_input(
290 request,
291 Self::current_store_limits()?,
292 )
293 .map_err(Error::from)
294 }
295
296 pub async fn execute_local_store_gc() -> Result<WasmStoreGcExecutionStats, Error> {
298 TemplateManifestOps::execute_local_store_gc()
299 .await
300 .map_err(Error::from)
301 }
302
303 pub fn template_info(
305 template_id: TemplateId,
306 version: TemplateVersion,
307 ) -> Result<TemplateChunkSetInfoResponse, Error> {
308 TemplateManifestOps::chunk_set_info_response(&template_id, &version).map_err(Error::from)
309 }
310
311 pub fn template_chunk(
313 template_id: TemplateId,
314 version: TemplateVersion,
315 chunk_index: u32,
316 ) -> Result<TemplateChunkResponse, Error> {
317 TemplateManifestOps::chunk_response(&template_id, &version, chunk_index)
318 .map_err(Error::from)
319 }
320}