canic_core/ops/request/
response.rs

1//! Root-side handlers that fulfil orchestration requests.
2//!
3//! The root canister exposes `canic_response`, which accepts a
4//! [`Request`](crate::ops::request::Request) and returns a [`Response`]. This
5//! module contains the implementations for create/upgrade/cycle flows plus the
6//! corresponding response payloads.
7
8use crate::{
9    Error,
10    interface::ic::{canister::upgrade_canister, deposit_cycles},
11    ops::{
12        canister::create_and_install_canister,
13        model::memory::topology::subnet::SubnetCanisterRegistryOps,
14        prelude::*,
15        request::{
16            CreateCanisterParent, CreateCanisterRequest, CyclesRequest, Request, RequestOpsError,
17            UpgradeCanisterRequest,
18        },
19        wasm::WasmOps,
20    },
21};
22
23///
24/// Response
25/// Response payloads produced by root for orchestration requests.
26///
27
28#[derive(CandidType, Clone, Debug, Deserialize)]
29pub enum Response {
30    CreateCanister(CreateCanisterResponse),
31    UpgradeCanister(UpgradeCanisterResponse),
32    Cycles(CyclesResponse),
33}
34
35///
36/// CreateCanisterResponse
37/// Result of creating and installing a new canister.
38///
39
40#[derive(CandidType, Clone, Debug, Deserialize)]
41pub struct CreateCanisterResponse {
42    pub new_canister_pid: Principal,
43}
44
45///
46/// UpgradeCanisterResponse
47/// Result of an upgrade request (currently empty, reserved for metadata)
48///
49
50#[derive(CandidType, Clone, Debug, Deserialize)]
51pub struct UpgradeCanisterResponse {}
52
53///
54/// CyclesResponse
55/// Result of transferring cycles to a child canister
56///
57
58#[derive(CandidType, Clone, Debug, Deserialize)]
59pub struct CyclesResponse {
60    pub cycles_transferred: u128,
61}
62
63/// Handle a root-bound orchestration request and produce a [`Response`].
64pub async fn response(req: Request) -> Result<Response, Error> {
65    OpsError::require_root()?;
66
67    match req {
68        Request::CreateCanister(req) => create_canister_response(&req).await,
69        Request::UpgradeCanister(req) => upgrade_canister_response(&req).await,
70        Request::Cycles(req) => cycles_response(&req).await,
71    }
72}
73
74// create_canister_response
75async fn create_canister_response(req: &CreateCanisterRequest) -> Result<Response, Error> {
76    let caller = msg_caller();
77
78    // Look up parent
79    let parent_pid = match &req.parent {
80        CreateCanisterParent::Canister(pid) => *pid,
81        CreateCanisterParent::Root => canister_self(),
82        CreateCanisterParent::ThisCanister => caller,
83
84        CreateCanisterParent::Parent => SubnetCanisterRegistryOps::try_get_parent(caller)
85            .map_err(|_| RequestOpsError::ParentNotFound(caller))?,
86
87        CreateCanisterParent::Directory(ty) => {
88            SubnetCanisterRegistryOps::try_get_type(ty)
89                .map_err(|_| RequestOpsError::CanisterRoleNotFound(ty.clone()))?
90                .pid
91        }
92    };
93
94    let new_canister_pid =
95        create_and_install_canister(&req.canister_role, parent_pid, req.extra_arg.clone()).await?;
96
97    Ok(Response::CreateCanister(CreateCanisterResponse {
98        new_canister_pid,
99    }))
100}
101
102// upgrade_canister_response
103async fn upgrade_canister_response(req: &UpgradeCanisterRequest) -> Result<Response, Error> {
104    let caller = msg_caller();
105    let registry_entry = SubnetCanisterRegistryOps::try_get(req.canister_pid)
106        .map_err(|_| RequestOpsError::ChildNotFound(req.canister_pid))?;
107
108    if registry_entry.parent_pid != Some(caller) {
109        return Err(RequestOpsError::NotChildOfCaller(req.canister_pid, caller).into());
110    }
111
112    // Use the registry's type to avoid trusting request payload.
113    let wasm = WasmOps::try_get(&registry_entry.ty)?;
114    upgrade_canister(registry_entry.pid, wasm.bytes()).await?;
115    SubnetCanisterRegistryOps::update_module_hash(registry_entry.pid, wasm.module_hash())?;
116
117    Ok(Response::UpgradeCanister(UpgradeCanisterResponse {}))
118}
119
120// cycles_response
121async fn cycles_response(req: &CyclesRequest) -> Result<Response, Error> {
122    deposit_cycles(msg_caller(), req.cycles).await?;
123
124    let cycles_transferred = req.cycles;
125
126    Ok(Response::Cycles(CyclesResponse { cycles_transferred }))
127}