canic_core/ops/request/
response.rs1use crate::{
9 Error,
10 interface::ic::{canister::upgrade_canister, deposit_cycles},
11 log::Topic,
12 ops::{
13 canister::create_and_install_canister,
14 model::memory::topology::subnet::SubnetCanisterRegistryOps,
15 prelude::*,
16 request::{
17 CreateCanisterParent, CreateCanisterRequest, CyclesRequest, Request, RequestOpsError,
18 UpgradeCanisterRequest,
19 },
20 wasm::WasmOps,
21 },
22};
23
24#[derive(CandidType, Clone, Debug, Deserialize)]
30pub enum Response {
31 CreateCanister(CreateCanisterResponse),
32 UpgradeCanister(UpgradeCanisterResponse),
33 Cycles(CyclesResponse),
34}
35
36#[derive(CandidType, Clone, Debug, Deserialize)]
42pub struct CreateCanisterResponse {
43 pub new_canister_pid: Principal,
44}
45
46#[derive(CandidType, Clone, Debug, Deserialize)]
52pub struct UpgradeCanisterResponse {}
53
54#[derive(CandidType, Clone, Debug, Deserialize)]
60pub struct CyclesResponse {
61 pub cycles_transferred: u128,
62}
63
64pub async fn response(req: Request) -> Result<Response, Error> {
66 OpsError::require_root()?;
67
68 match req {
69 Request::CreateCanister(req) => create_canister_response(&req).await,
70 Request::UpgradeCanister(req) => upgrade_canister_response(&req).await,
71 Request::Cycles(req) => cycles_response(&req).await,
72 }
73}
74
75async fn create_canister_response(req: &CreateCanisterRequest) -> Result<Response, Error> {
77 let caller = msg_caller();
78 let role = req.canister_role.clone();
79 let parent_desc = format!("{:?}", &req.parent);
80
81 let result: Result<Response, Error> = async {
82 let parent_pid = match &req.parent {
84 CreateCanisterParent::Canister(pid) => *pid,
85 CreateCanisterParent::Root => canister_self(),
86 CreateCanisterParent::ThisCanister => caller,
87
88 CreateCanisterParent::Parent => SubnetCanisterRegistryOps::try_get_parent(caller)
89 .map_err(|_| RequestOpsError::ParentNotFound(caller))?,
90
91 CreateCanisterParent::Directory(ty) => {
92 SubnetCanisterRegistryOps::try_get_type(ty)
93 .map_err(|_| RequestOpsError::CanisterRoleNotFound(ty.clone()))?
94 .pid
95 }
96 };
97
98 let new_canister_pid =
99 create_and_install_canister(&req.canister_role, parent_pid, req.extra_arg.clone())
100 .await?;
101
102 Ok(Response::CreateCanister(CreateCanisterResponse {
103 new_canister_pid,
104 }))
105 }
106 .await;
107
108 if let Err(err) = &result {
109 log!(
110 Topic::CanisterLifecycle,
111 Warn,
112 "create_canister_response failed (caller={caller}, role={role}, parent={parent_desc}): {err}"
113 );
114 }
115
116 result
117}
118
119async fn upgrade_canister_response(req: &UpgradeCanisterRequest) -> Result<Response, Error> {
121 let caller = msg_caller();
122 let registry_entry = SubnetCanisterRegistryOps::try_get(req.canister_pid)
123 .map_err(|_| RequestOpsError::ChildNotFound(req.canister_pid))?;
124
125 if registry_entry.parent_pid != Some(caller) {
126 return Err(RequestOpsError::NotChildOfCaller(req.canister_pid, caller).into());
127 }
128
129 let wasm = WasmOps::try_get(®istry_entry.ty)?;
131 upgrade_canister(registry_entry.pid, wasm.bytes()).await?;
132 SubnetCanisterRegistryOps::update_module_hash(registry_entry.pid, wasm.module_hash())?;
133
134 Ok(Response::UpgradeCanister(UpgradeCanisterResponse {}))
135}
136
137async fn cycles_response(req: &CyclesRequest) -> Result<Response, Error> {
139 deposit_cycles(msg_caller(), req.cycles).await?;
140
141 let cycles_transferred = req.cycles;
142
143 Ok(Response::Cycles(CyclesResponse { cycles_transferred }))
144}