Skip to main content

ic_query/cloud_engine/
mod.rs

1//! CloudEngine control-plane and official Dashboard report models, adapters, and renderers.
2
3#[cfg(feature = "cloud-engine-host")]
4mod build;
5#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
6mod list;
7mod model;
8mod node;
9mod provider;
10#[cfg(feature = "cloud-engine-host")]
11mod source;
12mod text;
13#[cfg(feature = "cloud-engine-host")]
14mod wire;
15
16#[cfg(feature = "cloud-engine-host")]
17use crate::runtime::RuntimeError;
18#[cfg(feature = "cloud-engine-host")]
19use thiserror::Error as ThisError;
20
21#[cfg(feature = "cloud-engine-host")]
22pub use build::{
23    build_cloud_engine_operator_report, build_cloud_engine_operator_report_with_source,
24    build_cloud_engine_prices_report, build_cloud_engine_prices_report_with_source,
25};
26#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
27pub use list::{build_cloud_engine_list_report, build_cloud_engine_list_report_with_sources};
28#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
29pub use model::{CloudEngineListReport, CloudEngineListRow, CloudEngineOperatorLookupStatus};
30pub use model::{
31    CloudEngineNodeType, CloudEngineOperatorReport, CloudEnginePriceRow, CloudEnginePricesReport,
32    CloudEngineReportContext,
33};
34#[cfg(feature = "cloud-engine-host")]
35pub use model::{
36    CloudEngineOperatorBindingSourceData, CloudEngineOperatorSourceData,
37    CloudEnginePricesSourceData,
38};
39pub use node::{
40    CLOUD_ENGINE_NODE_INCLUDED_STATUSES, CLOUD_ENGINE_NODE_REWARD_TYPE, CloudEngineNodeInfoReport,
41    CloudEngineNodeInfoRequest, CloudEngineNodeListReport, CloudEngineNodeListRequest,
42    CloudEngineNodeRow, MAX_CLOUD_ENGINE_NODE_ROWS, cloud_engine_node_info_report_text,
43    cloud_engine_node_list_report_text,
44};
45#[cfg(feature = "dashboard-host")]
46pub use node::{
47    CloudEngineNodeInfoSourceData, CloudEngineNodeListSourceData, CloudEngineNodeSource,
48    build_cloud_engine_node_info_report, build_cloud_engine_node_info_report_with_source,
49    build_cloud_engine_node_list_report, build_cloud_engine_node_list_report_with_source,
50};
51pub use provider::{
52    CloudEngineProviderInfoReport, CloudEngineProviderInfoRequest, CloudEngineProviderListReport,
53    CloudEngineProviderListRequest, CloudEngineProviderLocation, CloudEngineProviderRow,
54    MAX_CLOUD_ENGINE_PROVIDER_LOCATIONS, MAX_CLOUD_ENGINE_PROVIDER_SOURCE_ROWS,
55    cloud_engine_provider_info_report_text, cloud_engine_provider_list_report_text,
56};
57#[cfg(feature = "dashboard-host")]
58pub use provider::{
59    CloudEngineProviderInfoSourceData, CloudEngineProviderListSourceData,
60    CloudEngineProviderSource, build_cloud_engine_provider_info_report,
61    build_cloud_engine_provider_info_report_with_source, build_cloud_engine_provider_list_report,
62    build_cloud_engine_provider_list_report_with_source,
63};
64#[cfg(feature = "cloud-engine-host")]
65pub use source::{
66    CloudEngineOperatorBindingSource, CloudEngineSource, CloudEngineSourceRequest,
67    LiveCloudEngineSource,
68};
69#[cfg(all(feature = "cloud-engine-host", feature = "subnet-catalog-host"))]
70pub use text::cloud_engine_list_report_text;
71pub use text::{cloud_engine_operator_report_text, cloud_engine_prices_report_text};
72
73/// Mainnet CloudEngine control-plane registry canister principal.
74pub const MAINNET_CLOUD_ENGINE_CANISTER_ID: &str = "q6cfj-fyaaa-aaaar-qb77q-cai";
75
76/// Default replica endpoint used for live CloudEngine control-plane queries.
77pub const DEFAULT_CLOUD_ENGINE_SOURCE_ENDPOINT: &str = "https://icp-api.io";
78
79/// Default official Dashboard endpoint for CloudEngine provider and Type4 node reports.
80pub const DEFAULT_CLOUD_ENGINE_DASHBOARD_SOURCE_ENDPOINT: &str =
81    "https://ic-api.internetcomputer.org/api/v3";
82
83/// Maximum marketplace rows accepted from one live response.
84pub const MAX_CLOUD_ENGINE_PRICE_ROWS: usize = 1_000;
85
86/// Maximum Registry CloudEngine Subnets followed by one list invocation.
87pub const MAX_CLOUD_ENGINE_LIST_ROWS: usize = 100;
88
89/// Maximum claimed domains accepted from one engine operator.
90pub const MAX_CLOUD_ENGINE_DOMAINS: usize = 100;
91
92/// Maximum decimal digits accepted for one marketplace cycle amount.
93pub const MAX_CLOUD_ENGINE_CYCLE_DECIMAL_DIGITS: usize = 256;
94
95#[cfg(feature = "cloud-engine-host")]
96const CLOUD_ENGINE_REPORT_SCHEMA_VERSION: u32 = 1;
97#[cfg(feature = "cloud-engine-host")]
98const CLOUD_ENGINE_AUTHORITY: &str = "cloud_engine_control_plane_canister";
99
100///
101/// CloudEngineHostError
102///
103/// Failure while collecting or validating one live CloudEngine report.
104///
105
106#[cfg(feature = "cloud-engine-host")]
107#[derive(Debug, ThisError)]
108pub enum CloudEngineHostError {
109    /// The requested network is not the supported mainnet identity.
110    #[error(
111        "`icq cloud-engine` supports only the mainnet `ic` network\n\nThese reports query the mainnet CloudEngine control-plane canister.\n\nTry:\n  icq --network ic cloud-engine prices"
112    )]
113    UnsupportedNetwork {
114        /// Rejected network identity.
115        network: String,
116    },
117
118    /// A requested principal is invalid.
119    #[error("invalid {field}: {reason}")]
120    InvalidPrincipal {
121        /// Principal field being parsed.
122        field: &'static str,
123        /// Principal parsing failure.
124        reason: String,
125    },
126
127    /// The IC agent could not be constructed for the requested endpoint.
128    #[error("failed to build IC agent for {endpoint}: {reason}")]
129    AgentBuild {
130        /// Endpoint used to build the agent.
131        endpoint: String,
132        /// Agent construction failure.
133        reason: String,
134    },
135
136    /// The built-in CloudEngine control-plane principal could not be parsed.
137    #[error("invalid built-in CloudEngine canister principal: {reason}")]
138    CanisterId {
139        /// Principal parsing failure.
140        reason: String,
141    },
142
143    /// A CloudEngine control-plane query failed.
144    #[error("CloudEngine agent call {method} failed: {reason}")]
145    AgentCall {
146        /// Canister method being queried.
147        method: &'static str,
148        /// Agent call failure.
149        reason: String,
150    },
151
152    /// A CloudEngine query argument could not be Candid encoded.
153    #[error("failed to encode Candid {message}: {reason}")]
154    CandidEncode {
155        /// Candid request type.
156        message: &'static str,
157        /// Encoding failure.
158        reason: String,
159    },
160
161    /// A CloudEngine response could not be Candid decoded.
162    #[error("failed to decode Candid {message}: {reason}")]
163    CandidDecode {
164        /// Candid response type.
165        message: &'static str,
166        /// Decoding failure.
167        reason: String,
168    },
169
170    /// A custom CloudEngine source returned inconsistent or excessive data.
171    #[error("invalid CloudEngine source data: {reason}")]
172    InvalidSourceData {
173        /// Deterministic source-contract failure.
174        reason: String,
175    },
176
177    /// The Registry-backed Subnet Catalog could not supply the list inventory.
178    #[cfg(feature = "subnet-catalog-host")]
179    #[error(transparent)]
180    SubnetCatalog(#[from] crate::subnet_catalog::SubnetCatalogHostError),
181
182    /// The synchronous host runtime could not execute the live query.
183    #[error(transparent)]
184    Runtime(#[from] RuntimeError),
185}
186
187#[cfg(feature = "cloud-engine-host")]
188fn enforce_mainnet_network(network: &str) -> Result<(), CloudEngineHostError> {
189    crate::network::enforce_mainnet_network_with(network, |network| {
190        CloudEngineHostError::UnsupportedNetwork { network }
191    })
192}
193
194#[cfg(all(test, feature = "cloud-engine-host"))]
195mod tests;