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(network: Option<&str>) -> bool {
102 network.is_none_or(|network| network == "local" || network.starts_with("http://"))
103}
104
105pub fn query_ready(network: Option<&str>, canister: &str) -> Result<bool, ReplicaQueryError> {
107 let bytes = local_query(network, canister, "canic_ready")?;
108 Decode!(&bytes, bool).map_err(ReplicaQueryError::Candid)
109}
110
111pub fn query_ready_from_root(
113 network: Option<&str>,
114 canister: &str,
115 icp_root: &Path,
116) -> Result<bool, ReplicaQueryError> {
117 let bytes = local_query_from_root(network, canister, "canic_ready", icp_root)?;
118 Decode!(&bytes, bool).map_err(ReplicaQueryError::Candid)
119}
120
121pub fn query_bootstrap_status_from_root(
123 network: Option<&str>,
124 canister: &str,
125 icp_root: &Path,
126) -> Result<BootstrapStatusResponse, ReplicaQueryError> {
127 let bytes = local_query_from_root(network, canister, "canic_bootstrap_status", icp_root)?;
128 decode_bootstrap_status_response(&bytes)
129}
130
131pub(crate) fn query_cycle_balance(
133 network: Option<&str>,
134 canister: &str,
135) -> Result<u128, ReplicaQueryError> {
136 let bytes = local_query(network, canister, "canic_cycle_balance")?;
137 decode_cycle_balance_response(&bytes)
138}
139
140pub fn query_cycle_balance_from_root(
142 network: Option<&str>,
143 canister: &str,
144 icp_root: &Path,
145) -> Result<u128, ReplicaQueryError> {
146 let bytes = local_query_from_root(network, canister, "canic_cycle_balance", icp_root)?;
147 decode_cycle_balance_response(&bytes)
148}
149
150pub fn query_subnet_registry_entries(
152 network: Option<&str>,
153 root: &str,
154) -> Result<Vec<RegistryEntry>, ReplicaQueryError> {
155 let response = query_subnet_registry_response(network, root)?;
156 registry_entries_from_response(response).map_err(Into::into)
157}
158
159pub fn query_subnet_registry_entries_from_root(
161 network: Option<&str>,
162 root: &str,
163 icp_root: &Path,
164) -> Result<Vec<RegistryEntry>, ReplicaQueryError> {
165 let response = query_subnet_registry_response_from_root(network, root, icp_root)?;
166 registry_entries_from_response(response).map_err(Into::into)
167}
168
169pub fn query_subnet_registry_roles_from_root(
171 network: Option<&str>,
172 root: &str,
173 icp_root: &Path,
174) -> Result<Vec<String>, ReplicaQueryError> {
175 Ok(
176 query_subnet_registry_entries_from_root(network, root, icp_root)?
177 .into_iter()
178 .filter_map(|entry| entry.role)
179 .collect(),
180 )
181}
182
183fn query_subnet_registry_response(
184 network: Option<&str>,
185 root: &str,
186) -> Result<SubnetRegistryResponse, ReplicaQueryError> {
187 let bytes = local_query(network, root, "canic_subnet_registry")?;
188 decode_subnet_registry_response(&bytes)
189}
190
191fn query_subnet_registry_response_from_root(
192 network: Option<&str>,
193 root: &str,
194 icp_root: &Path,
195) -> Result<SubnetRegistryResponse, ReplicaQueryError> {
196 let bytes = local_query_from_root(network, root, "canic_subnet_registry", icp_root)?;
197 decode_subnet_registry_response(&bytes)
198}