1use crate::subnet_catalog::RoutingRange;
2use thiserror::Error as ThisError;
3
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum SubnetCatalogField {
12 Principal,
14 CanisterPrincipal,
16 SubnetListEntry,
18 RoutingTableRange,
20 RoutingTableSubnetId,
22 RoutingRangeStart,
24 RoutingRangeEnd,
26 Network,
28 RegistryCanister,
30 RegistryVersion,
32 SourceEndpoint,
34 SubnetPrincipal,
36 CollectionTimestamp,
38 Classification,
40 AgreementDigest,
42 CatalogDigest,
44 Provenance,
46}
47
48impl std::fmt::Display for SubnetCatalogField {
49 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 formatter.write_str(match self {
51 Self::Principal => "principal",
52 Self::CanisterPrincipal => "canister_principal",
53 Self::SubnetListEntry => "subnet_list_entry",
54 Self::RoutingTableRange => "routing_table_range",
55 Self::RoutingTableSubnetId => "routing_range.subnet_principal",
56 Self::RoutingRangeStart => "start_canister_id",
57 Self::RoutingRangeEnd => "end_canister_id",
58 Self::Network => "provenance.network",
59 Self::RegistryCanister => "provenance.registry_canister_id",
60 Self::RegistryVersion => "provenance.registry_version",
61 Self::SourceEndpoint => "provenance.source_endpoints",
62 Self::SubnetPrincipal => "subnet_principal",
63 Self::CollectionTimestamp => "provenance.fetched_at",
64 Self::Classification => "classification",
65 Self::AgreementDigest => "provenance.agreement_digest",
66 Self::CatalogDigest => "catalog_digest",
67 Self::Provenance => "provenance",
68 })
69 }
70}
71
72#[derive(Debug, ThisError)]
79pub enum CatalogError {
80 #[error(transparent)]
82 Json(#[from] serde_json::Error),
83
84 #[error("unsupported subnet catalog schema version {found}; supported version is {supported}")]
86 UnsupportedSchemaVersion {
87 found: u32,
89 supported: u32,
91 },
92
93 #[error("subnet catalog network is {actual:?}; expected {expected:?}")]
95 NetworkMismatch {
96 expected: String,
98 actual: String,
100 },
101
102 #[error("subnet catalog Registry canister is {actual}; expected {expected}")]
104 RegistryCanisterMismatch {
105 expected: String,
107 actual: String,
109 },
110
111 #[error("subnet catalog Registry version must be greater than zero")]
113 InvalidRegistryVersion,
114
115 #[error("subnet catalog must contain at least one subnet")]
117 EmptySubnets,
118
119 #[error("subnet catalog must contain at least one routing range")]
121 EmptyRoutingRanges,
122
123 #[error("invalid principal in {field}: {value}: {reason}")]
125 InvalidPrincipal {
126 field: SubnetCatalogField,
128 value: String,
130 reason: String,
132 },
133
134 #[error("duplicate subnet principal in catalog: {subnet_principal}")]
136 DuplicateSubnet {
137 subnet_principal: String,
139 },
140
141 #[error("noncanonical subnet order: {previous} must sort before {current}")]
143 NonCanonicalSubnetOrder {
144 previous: String,
146 current: String,
148 },
149
150 #[error("routing range references unknown subnet: {subnet_principal}")]
152 UnknownRoutingSubnet {
153 subnet_principal: String,
155 },
156
157 #[error(
159 "invalid routing range for {subnet_principal}: start {start_canister_id} sorts after end {end_canister_id}"
160 )]
161 InvalidRoutingRange {
162 subnet_principal: String,
164 start_canister_id: String,
166 end_canister_id: String,
168 },
169
170 #[error("overlapping routing ranges: {first} overlaps {second}")]
172 OverlappingRoutingRanges {
173 first: Box<RoutingRange>,
175 second: Box<RoutingRange>,
177 },
178
179 #[error("noncanonical routing range order: {previous} must sort before {current}")]
181 NonCanonicalRoutingOrder {
182 previous: Box<RoutingRange>,
184 current: Box<RoutingRange>,
186 },
187
188 #[error(
190 "subnet {subnet_principal} kind {actual} contradicts Registry subnet_type={registry_subnet_type}; expected {expected}"
191 )]
192 SubnetKindMismatch {
193 subnet_principal: String,
195 registry_subnet_type: i32,
197 expected: String,
199 actual: String,
201 },
202
203 #[error(
205 "subnet {subnet_principal} charges_apply_by_default={actual} contradicts expected {expected}"
206 )]
207 ChargingPolicyMismatch {
208 subnet_principal: String,
210 expected: bool,
212 actual: bool,
214 },
215
216 #[error("subnet {subnet_principal} classification field {field} is invalid: {reason}")]
218 ClassificationMismatch {
219 subnet_principal: String,
221 field: &'static str,
223 reason: String,
225 },
226
227 #[error("invalid catalog timestamp in {field}: {value:?}")]
229 InvalidTimestamp {
230 field: &'static str,
232 value: String,
234 },
235
236 #[error(
238 "catalog timestamp in {field} is in the future: {value}; latest accepted unix time is {latest_allowed_unix_secs}"
239 )]
240 FutureTimestamp {
241 field: &'static str,
243 value: String,
245 latest_allowed_unix_secs: u64,
247 },
248
249 #[error("invalid catalog source endpoint {endpoint:?}: {reason}")]
251 InvalidSourceEndpoint {
252 endpoint: String,
254 reason: String,
256 },
257
258 #[error("invalid catalog provenance field {field}: {reason}")]
260 InvalidProvenance {
261 field: &'static str,
263 reason: String,
265 },
266
267 #[error("classification policy schema {found} is unsupported; expected {supported}")]
269 ClassificationPolicyVersionMismatch {
270 found: u32,
272 supported: u32,
274 },
275
276 #[error("classification policy digest mismatch: found {actual}; expected {expected}")]
278 ClassificationPolicyDigestMismatch {
279 expected: String,
281 actual: String,
283 },
284
285 #[error(
287 "resolver policy mismatch: version {actual_version} backend {actual_backend:?}; expected version {expected_version} backend {expected_backend:?}"
288 )]
289 ResolverPolicyMismatch {
290 expected_version: u32,
292 actual_version: u32,
294 expected_backend: String,
296 actual_backend: String,
298 },
299
300 #[error("invalid catalog digest {value:?}; expected 64 lowercase hexadecimal characters")]
302 InvalidCatalogDigest {
303 value: String,
305 },
306
307 #[error(
309 "invalid catalog agreement digest {value:?}; expected 64 lowercase hexadecimal characters"
310 )]
311 InvalidAgreementDigest {
312 value: String,
314 },
315
316 #[error("catalog agreement digest mismatch: found {actual}; expected {expected}")]
318 AgreementDigestMismatch {
319 expected: String,
321 actual: String,
323 },
324
325 #[error("catalog digest mismatch: found {actual}; expected {expected}")]
327 CatalogDigestMismatch {
328 expected: String,
330 actual: String,
332 },
333
334 #[error("subnet principal {subnet_principal} was not found in the cached catalog")]
336 UnknownSubnet {
337 subnet_principal: String,
339 },
340
341 #[error("principal prefix {prefix:?} did not match cached subnet principals")]
343 PrincipalPrefixNotFound {
344 prefix: String,
346 },
347
348 #[error("principal prefix {prefix:?} is ambiguous; matches: {matches:?}")]
350 AmbiguousPrincipalPrefix {
351 prefix: String,
353 matches: Vec<String>,
355 },
356
357 #[error(
359 "canister principal {canister_principal} was not covered by cached routing ranges at registry_version={registry_version}, catalog_schema_version={catalog_schema_version}"
360 )]
361 RouteNotFound {
362 canister_principal: String,
364 registry_version: u64,
366 catalog_schema_version: u32,
368 },
369}