canic_host/replica_query/
mod.rs1mod cbor;
8mod status;
9mod transport;
10mod wire;
11
12use self::{
13 transport::{local_query, local_query_from_root},
14 wire::{
15 decode_bootstrap_status_response, decode_cycle_balance_response,
16 decode_subnet_registry_response,
17 },
18};
19use crate::registry::{RegistryEntry, RegistryParseError, registry_entries_from_response};
20use std::{error::Error, fmt, path::Path};
21
22use candid::Decode;
23use canic_core::dto::{
24 error::Error as CanicError, state::BootstrapStatusResponse, topology::SubnetRegistryResponse,
25};
26
27pub use self::{
28 status::{local_replica_root_key_from_root, local_replica_status_reachable_from_root},
29 transport::local_replica_endpoint_from_root,
30};
31
32#[derive(Debug)]
37pub enum ReplicaQueryError {
38 Candid(candid::Error),
39 Canister(CanicError),
40 Cbor(String),
41 Io(std::io::Error),
42 Query(String),
43 Registry(RegistryParseError),
44 Rejected { code: u64, message: String },
45}
46
47impl fmt::Display for ReplicaQueryError {
48 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::Candid(err) => write!(formatter, "{err}"),
52 Self::Canister(err) => write!(formatter, "{err}"),
53 Self::Cbor(err) => formatter.write_str(err),
54 Self::Io(err) => write!(formatter, "{err}"),
55 Self::Query(message) => write!(formatter, "{message}"),
56 Self::Registry(err) => write!(formatter, "{err}"),
57 Self::Rejected { code, message } => {
58 write!(
59 formatter,
60 "local replica rejected query: code={code} message={message}"
61 )
62 }
63 }
64 }
65}
66
67impl Error for ReplicaQueryError {
68 fn source(&self) -> Option<&(dyn Error + 'static)> {
70 match self {
71 Self::Candid(err) => Some(err),
72 Self::Io(err) => Some(err),
73 Self::Registry(err) => Some(err),
74 Self::Canister(_) | Self::Cbor(_) | Self::Query(_) | Self::Rejected { .. } => None,
75 }
76 }
77}
78
79impl From<std::io::Error> for ReplicaQueryError {
80 fn from(err: std::io::Error) -> Self {
82 Self::Io(err)
83 }
84}
85
86impl From<cbor::CborError> for ReplicaQueryError {
87 fn from(err: cbor::CborError) -> Self {
89 Self::Cbor(err.to_string())
90 }
91}
92
93impl From<RegistryParseError> for ReplicaQueryError {
94 fn from(err: RegistryParseError) -> Self {
95 Self::Registry(err)
96 }
97}
98
99#[must_use]
101pub fn should_use_local_replica_query(environment: Option<&str>) -> bool {
102 environment
103 .is_none_or(|environment| environment == "local" || environment.starts_with("http://"))
104}
105
106pub fn query_ready(environment: Option<&str>, canister: &str) -> Result<bool, ReplicaQueryError> {
108 let bytes = local_query(environment, canister, "canic_ready")?;
109 Decode!(&bytes, bool).map_err(ReplicaQueryError::Candid)
110}
111
112pub fn query_ready_from_root(
114 environment: Option<&str>,
115 canister: &str,
116 icp_root: &Path,
117) -> Result<bool, ReplicaQueryError> {
118 let bytes = local_query_from_root(environment, canister, "canic_ready", icp_root)?;
119 Decode!(&bytes, bool).map_err(ReplicaQueryError::Candid)
120}
121
122pub fn query_bootstrap_status_from_root(
124 environment: Option<&str>,
125 canister: &str,
126 icp_root: &Path,
127) -> Result<BootstrapStatusResponse, ReplicaQueryError> {
128 let bytes = local_query_from_root(environment, canister, "canic_bootstrap_status", icp_root)?;
129 decode_bootstrap_status_response(&bytes)
130}
131
132pub(crate) fn query_cycle_balance(
134 environment: Option<&str>,
135 canister: &str,
136) -> Result<u128, ReplicaQueryError> {
137 let bytes = local_query(environment, canister, "canic_cycle_balance")?;
138 decode_cycle_balance_response(&bytes)
139}
140
141pub fn query_cycle_balance_from_root(
143 environment: Option<&str>,
144 canister: &str,
145 icp_root: &Path,
146) -> Result<u128, ReplicaQueryError> {
147 let bytes = local_query_from_root(environment, canister, "canic_cycle_balance", icp_root)?;
148 decode_cycle_balance_response(&bytes)
149}
150
151pub fn query_subnet_registry_entries(
153 environment: Option<&str>,
154 root: &str,
155) -> Result<Vec<RegistryEntry>, ReplicaQueryError> {
156 let response = query_subnet_registry_response(environment, root)?;
157 registry_entries_from_response(response).map_err(Into::into)
158}
159
160pub fn query_subnet_registry_entries_from_root(
162 environment: Option<&str>,
163 root: &str,
164 icp_root: &Path,
165) -> Result<Vec<RegistryEntry>, ReplicaQueryError> {
166 let response = query_subnet_registry_response_from_root(environment, root, icp_root)?;
167 registry_entries_from_response(response).map_err(Into::into)
168}
169
170pub fn query_subnet_registry_roles_from_root(
172 environment: Option<&str>,
173 root: &str,
174 icp_root: &Path,
175) -> Result<Vec<String>, ReplicaQueryError> {
176 Ok(
177 query_subnet_registry_entries_from_root(environment, root, icp_root)?
178 .into_iter()
179 .filter_map(|entry| entry.role)
180 .collect(),
181 )
182}
183
184fn query_subnet_registry_response(
185 environment: Option<&str>,
186 root: &str,
187) -> Result<SubnetRegistryResponse, ReplicaQueryError> {
188 let bytes = local_query(environment, root, "canic_subnet_registry")?;
189 decode_subnet_registry_response(&bytes)
190}
191
192fn query_subnet_registry_response_from_root(
193 environment: Option<&str>,
194 root: &str,
195 icp_root: &Path,
196) -> Result<SubnetRegistryResponse, ReplicaQueryError> {
197 let bytes = local_query_from_root(environment, root, "canic_subnet_registry", icp_root)?;
198 decode_subnet_registry_response(&bytes)
199}