1use crate::RejectResponse;
6use candid::Principal;
7use hex;
8use reqwest::Response;
9use schemars::JsonSchema;
10use serde::de::DeserializeOwned;
11use serde::{Deserialize, Serialize};
12use std::collections::BTreeMap;
13use std::net::SocketAddr;
14use std::path::PathBuf;
15use strum_macros::EnumIter;
16
17pub type InstanceId = usize;
18
19#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
20pub struct AutoProgressConfig {
21 pub artificial_delay_ms: Option<u64>,
22}
23
24#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
25pub enum HttpGatewayBackend {
26 Replica(String),
27 PocketIcInstance(InstanceId),
28}
29
30#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
31pub struct HttpsConfig {
32 pub cert_path: String,
33 pub key_path: String,
34}
35
36#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
37pub struct InstanceHttpGatewayConfig {
38 pub ip_addr: Option<String>,
39 pub port: Option<u16>,
40 pub domains: Option<Vec<String>>,
41 pub https_config: Option<HttpsConfig>,
42}
43
44#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
45pub struct HttpGatewayConfig {
46 pub ip_addr: Option<String>,
47 pub port: Option<u16>,
48 pub forward_to: HttpGatewayBackend,
49 pub domains: Option<Vec<String>>,
50 pub https_config: Option<HttpsConfig>,
51}
52
53#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
54pub struct HttpGatewayDetails {
55 pub instance_id: InstanceId,
56 pub port: u16,
57 pub forward_to: HttpGatewayBackend,
58 pub domains: Option<Vec<String>>,
59 pub https_config: Option<HttpsConfig>,
60}
61
62#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
63pub struct HttpGatewayInfo {
64 pub instance_id: InstanceId,
65 pub port: u16,
66}
67
68#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
69pub enum CreateHttpGatewayResponse {
70 Created(HttpGatewayInfo),
71 Error { message: String },
72}
73
74#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
75pub enum CreateInstanceResponse {
76 Created {
77 instance_id: InstanceId,
78 topology: Topology,
79 http_gateway_info: Option<HttpGatewayInfo>,
80 },
81 Error {
82 message: String,
83 },
84}
85
86#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
87pub struct RawTime {
88 pub nanos_since_epoch: u64,
89}
90
91#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema, PartialEq, Eq, Hash)]
97pub enum RawEffectivePrincipal {
98 None,
99 SubnetId(
100 #[serde(deserialize_with = "base64::deserialize")]
101 #[serde(serialize_with = "base64::serialize")]
102 Vec<u8>,
103 ),
104 CanisterId(
105 #[serde(deserialize_with = "base64::deserialize")]
106 #[serde(serialize_with = "base64::serialize")]
107 Vec<u8>,
108 ),
109}
110
111impl std::fmt::Display for RawEffectivePrincipal {
112 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
113 match self {
114 RawEffectivePrincipal::None => write!(f, "None"),
115 RawEffectivePrincipal::SubnetId(subnet_id) => {
116 let principal = Principal::from_slice(subnet_id);
117 write!(f, "SubnetId({})", principal)
118 }
119 RawEffectivePrincipal::CanisterId(canister_id) => {
120 let principal = Principal::from_slice(canister_id);
121 write!(f, "CanisterId({})", principal)
122 }
123 }
124 }
125}
126
127#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
128pub struct RawMessageId {
129 pub effective_principal: RawEffectivePrincipal,
130 #[serde(deserialize_with = "base64::deserialize")]
131 #[serde(serialize_with = "base64::serialize")]
132 pub message_id: Vec<u8>,
133}
134
135#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
136pub struct RawIngressStatusArgs {
137 pub raw_message_id: RawMessageId,
138 pub raw_caller: Option<RawPrincipalId>,
139}
140
141#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
142pub struct RawCanisterCall {
143 #[serde(deserialize_with = "base64::deserialize")]
144 #[serde(serialize_with = "base64::serialize")]
145 pub sender: Vec<u8>,
146 #[serde(deserialize_with = "base64::deserialize")]
147 #[serde(serialize_with = "base64::serialize")]
148 pub canister_id: Vec<u8>,
149 pub effective_principal: RawEffectivePrincipal,
150 pub method: String,
151 #[serde(deserialize_with = "base64::deserialize")]
152 #[serde(serialize_with = "base64::serialize")]
153 pub payload: Vec<u8>,
154}
155
156#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
157pub enum RawCanisterResult {
158 Ok(
159 #[serde(deserialize_with = "base64::deserialize")]
160 #[serde(serialize_with = "base64::serialize")]
161 Vec<u8>,
162 ),
163 Err(RejectResponse),
164}
165
166impl From<Result<Vec<u8>, RejectResponse>> for RawCanisterResult {
167 fn from(result: Result<Vec<u8>, RejectResponse>) -> Self {
168 match result {
169 Ok(data) => RawCanisterResult::Ok(data),
170 Err(reject_response) => RawCanisterResult::Err(reject_response),
171 }
172 }
173}
174
175impl From<RawCanisterResult> for Result<Vec<u8>, RejectResponse> {
176 fn from(result: RawCanisterResult) -> Self {
177 match result {
178 RawCanisterResult::Ok(data) => Ok(data),
179 RawCanisterResult::Err(reject_response) => Err(reject_response),
180 }
181 }
182}
183
184#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
185pub struct RawSetStableMemory {
186 #[serde(deserialize_with = "base64::deserialize")]
187 #[serde(serialize_with = "base64::serialize")]
188 pub canister_id: Vec<u8>,
189 pub blob_id: BlobId,
190}
191
192#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
193pub struct RawStableMemory {
194 #[serde(deserialize_with = "base64::deserialize")]
195 #[serde(serialize_with = "base64::serialize")]
196 pub blob: Vec<u8>,
197}
198
199#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
200pub struct ApiError {
201 message: String,
202}
203
204#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
205pub struct StartedOrBusyResponse {
206 pub state_label: String,
207 pub op_id: String,
208}
209
210#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
211#[serde(untagged)]
212pub enum ApiResponse<T> {
213 Success(T),
214 Busy { state_label: String, op_id: String },
215 Started { state_label: String, op_id: String },
216 Error { message: String },
217}
218
219impl<T: DeserializeOwned> ApiResponse<T> {
220 pub async fn from_response(resp: Response) -> Self {
221 match resp.status() {
222 reqwest::StatusCode::OK => {
223 let result = resp.json::<T>().await;
224 match result {
225 Ok(t) => ApiResponse::Success(t),
226 Err(e) => ApiResponse::Error {
227 message: format!("Could not parse response: {}", e),
228 },
229 }
230 }
231 reqwest::StatusCode::ACCEPTED => {
232 let result = resp.json::<StartedOrBusyResponse>().await;
233 match result {
234 Ok(StartedOrBusyResponse { state_label, op_id }) => {
235 ApiResponse::Started { state_label, op_id }
236 }
237 Err(e) => ApiResponse::Error {
238 message: format!("Could not parse response: {}", e),
239 },
240 }
241 }
242 reqwest::StatusCode::CONFLICT => {
243 let result = resp.json::<StartedOrBusyResponse>().await;
244 match result {
245 Ok(StartedOrBusyResponse { state_label, op_id }) => {
246 ApiResponse::Busy { state_label, op_id }
247 }
248 Err(e) => ApiResponse::Error {
249 message: format!("Could not parse response: {}", e),
250 },
251 }
252 }
253 _ => {
254 let result = resp.json::<ApiError>().await;
255 match result {
256 Ok(e) => ApiResponse::Error { message: e.message },
257 Err(e) => ApiResponse::Error {
258 message: format!("Could not parse error: {}", e),
259 },
260 }
261 }
262 }
263 }
264}
265
266#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
267pub struct RawAddCycles {
268 #[serde(deserialize_with = "base64::deserialize")]
269 #[serde(serialize_with = "base64::serialize")]
270 pub canister_id: Vec<u8>,
271 pub amount: u128,
272}
273
274#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
275pub struct RawCycles {
276 pub cycles: u128,
277}
278
279#[derive(Clone, Serialize, Eq, PartialEq, Ord, PartialOrd, Deserialize, Debug, JsonSchema)]
280pub struct RawPrincipalId {
281 #[serde(deserialize_with = "base64::deserialize")]
283 #[serde(serialize_with = "base64::serialize")]
284 pub principal_id: Vec<u8>,
285}
286
287impl From<Principal> for RawPrincipalId {
288 fn from(principal: Principal) -> Self {
289 Self {
290 principal_id: principal.as_slice().to_vec(),
291 }
292 }
293}
294
295impl From<RawPrincipalId> for Principal {
296 fn from(raw_principal_id: RawPrincipalId) -> Self {
297 Principal::from_slice(&raw_principal_id.principal_id)
298 }
299}
300
301#[derive(Clone, Serialize, Eq, PartialEq, Ord, PartialOrd, Deserialize, Debug, JsonSchema)]
302pub struct RawCanisterId {
303 #[serde(deserialize_with = "base64::deserialize")]
305 #[serde(serialize_with = "base64::serialize")]
306 pub canister_id: Vec<u8>,
307}
308
309impl From<Principal> for RawCanisterId {
310 fn from(principal: Principal) -> Self {
311 Self {
312 canister_id: principal.as_slice().to_vec(),
313 }
314 }
315}
316
317impl From<RawCanisterId> for Principal {
318 fn from(raw_canister_id: RawCanisterId) -> Self {
319 Principal::from_slice(&raw_canister_id.canister_id)
320 }
321}
322
323#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema, PartialEq, Eq, Hash)]
324pub struct RawSubnetId {
325 #[serde(deserialize_with = "base64::deserialize")]
326 #[serde(serialize_with = "base64::serialize")]
327 pub subnet_id: Vec<u8>,
328}
329
330pub type SubnetId = Principal;
331
332impl From<Principal> for RawSubnetId {
333 fn from(principal: Principal) -> Self {
334 Self {
335 subnet_id: principal.as_slice().to_vec(),
336 }
337 }
338}
339
340impl From<RawSubnetId> for Principal {
341 fn from(val: RawSubnetId) -> Self {
342 Principal::from_slice(&val.subnet_id)
343 }
344}
345
346#[derive(
347 Clone, Serialize, Deserialize, Debug, JsonSchema, PartialEq, Eq, PartialOrd, Ord, Hash,
348)]
349pub struct RawNodeId {
350 #[serde(deserialize_with = "base64::deserialize")]
351 #[serde(serialize_with = "base64::serialize")]
352 pub node_id: Vec<u8>,
353}
354
355impl From<RawNodeId> for Principal {
356 fn from(val: RawNodeId) -> Self {
357 Principal::from_slice(&val.node_id)
358 }
359}
360
361impl From<Principal> for RawNodeId {
362 fn from(principal: Principal) -> Self {
363 Self {
364 node_id: principal.as_slice().to_vec(),
365 }
366 }
367}
368
369#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
370pub struct TickConfigs {
371 pub blockmakers: Option<BlockmakerConfigs>,
372}
373
374#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
375pub struct BlockmakerConfigs {
376 pub blockmakers_per_subnet: Vec<RawSubnetBlockmaker>,
377}
378
379#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
380pub struct RawSubnetBlockmaker {
381 pub subnet: RawSubnetId,
382 pub blockmaker: RawNodeId,
383 pub failed_blockmakers: Vec<RawNodeId>,
384}
385
386#[derive(Serialize, Deserialize, JsonSchema)]
387pub struct RawVerifyCanisterSigArg {
388 #[serde(deserialize_with = "base64::deserialize")]
389 #[serde(serialize_with = "base64::serialize")]
390 pub msg: Vec<u8>,
391 #[serde(deserialize_with = "base64::deserialize")]
392 #[serde(serialize_with = "base64::serialize")]
393 pub sig: Vec<u8>,
394 #[serde(deserialize_with = "base64::deserialize")]
395 #[serde(serialize_with = "base64::serialize")]
396 pub pubkey: Vec<u8>,
397 #[serde(deserialize_with = "base64::deserialize")]
398 #[serde(serialize_with = "base64::serialize")]
399 pub root_pubkey: Vec<u8>,
400}
401
402#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, JsonSchema)]
403pub struct BlobId(
404 #[serde(deserialize_with = "base64::deserialize")]
405 #[serde(serialize_with = "base64::serialize")]
406 pub Vec<u8>,
407);
408
409impl std::fmt::Display for BlobId {
410 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
411 write!(f, "BlobId{{{}}}", hex::encode(self.0.clone()))
412 }
413}
414
415#[derive(Clone, Debug)]
416pub struct BinaryBlob {
417 pub data: Vec<u8>,
418 pub compression: BlobCompression,
419}
420
421#[derive(Clone, Copy, Debug, Eq, PartialEq, JsonSchema)]
422pub enum BlobCompression {
423 Gzip,
424 NoCompression,
425}
426
427#[allow(deprecated)]
430pub mod base64 {
431 use serde::{Deserialize, Serialize};
432 use serde::{Deserializer, Serializer};
433
434 pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
435 let base64 = base64::encode(v);
436 String::serialize(&base64, s)
437 }
438
439 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
440 let base64 = String::deserialize(d)?;
441 base64::decode(base64.as_bytes()).map_err(serde::de::Error::custom)
442 }
443}
444
445#[derive(
448 Debug,
449 Clone,
450 Copy,
451 Eq,
452 Hash,
453 PartialEq,
454 Ord,
455 PartialOrd,
456 Serialize,
457 Deserialize,
458 JsonSchema,
459 EnumIter,
460)]
461pub enum SubnetKind {
462 Application,
463 Bitcoin,
464 Fiduciary,
465 II,
466 NNS,
467 SNS,
468 System,
469 VerifiedApplication,
470}
471
472#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
475pub struct SubnetConfigSet {
476 pub nns: bool,
477 pub sns: bool,
478 pub ii: bool,
479 pub fiduciary: bool,
480 pub bitcoin: bool,
481 pub system: usize,
482 pub application: usize,
483 pub verified_application: usize,
484}
485
486impl SubnetConfigSet {
487 pub fn validate(&self) -> Result<(), String> {
488 if self.system > 0
489 || self.application > 0
490 || self.verified_application > 0
491 || self.nns
492 || self.sns
493 || self.ii
494 || self.fiduciary
495 || self.bitcoin
496 {
497 return Ok(());
498 }
499 Err("SubnetConfigSet must contain at least one subnet".to_owned())
500 }
501}
502
503impl From<SubnetConfigSet> for ExtendedSubnetConfigSet {
504 fn from(
505 SubnetConfigSet {
506 nns,
507 sns,
508 ii,
509 fiduciary: fid,
510 bitcoin,
511 system,
512 application,
513 verified_application,
514 }: SubnetConfigSet,
515 ) -> Self {
516 ExtendedSubnetConfigSet {
517 nns: if nns {
518 Some(SubnetSpec::default())
519 } else {
520 None
521 },
522 sns: if sns {
523 Some(SubnetSpec::default())
524 } else {
525 None
526 },
527 ii: if ii {
528 Some(SubnetSpec::default())
529 } else {
530 None
531 },
532 fiduciary: if fid {
533 Some(SubnetSpec::default())
534 } else {
535 None
536 },
537 bitcoin: if bitcoin {
538 Some(SubnetSpec::default())
539 } else {
540 None
541 },
542 system: vec![SubnetSpec::default(); system],
543 application: vec![SubnetSpec::default(); application],
544 verified_application: vec![SubnetSpec::default(); verified_application],
545 }
546 }
547}
548
549#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
550pub enum IcpConfigFlag {
551 Disabled,
552 Enabled,
553}
554
555#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
557pub struct IcpConfig {
558 pub beta_features: Option<IcpConfigFlag>,
560 pub canister_backtrace: Option<IcpConfigFlag>,
562 pub function_name_length_limits: Option<IcpConfigFlag>,
564 pub canister_execution_rate_limiting: Option<IcpConfigFlag>,
567}
568
569#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
570pub enum IcpFeaturesConfig {
571 #[default]
572 DefaultConfig,
573}
574
575#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
579pub struct IcpFeatures {
580 pub registry: Option<IcpFeaturesConfig>,
581 pub cycles_minting: Option<IcpFeaturesConfig>,
583 pub icp_token: Option<IcpFeaturesConfig>,
584 pub cycles_token: Option<IcpFeaturesConfig>,
585 pub nns_governance: Option<IcpFeaturesConfig>,
586 pub sns: Option<IcpFeaturesConfig>,
587 pub ii: Option<IcpFeaturesConfig>,
588 pub nns_ui: Option<IcpFeaturesConfig>,
589}
590
591#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
592pub enum InitialTime {
593 Timestamp(RawTime),
597 AutoProgress(AutoProgressConfig),
601}
602
603#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
604pub enum IncompleteStateFlag {
605 #[default]
606 Disabled,
607 Enabled,
608}
609
610#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
611pub struct InstanceConfig {
612 pub subnet_config_set: ExtendedSubnetConfigSet,
613 pub http_gateway_config: Option<InstanceHttpGatewayConfig>,
614 pub state_dir: Option<PathBuf>,
615 pub icp_config: Option<IcpConfig>,
616 pub log_level: Option<String>,
617 pub bitcoind_addr: Option<Vec<SocketAddr>>,
618 pub icp_features: Option<IcpFeatures>,
619 pub incomplete_state: Option<IncompleteStateFlag>,
620 pub initial_time: Option<InitialTime>,
621}
622
623#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
624pub struct ExtendedSubnetConfigSet {
625 pub nns: Option<SubnetSpec>,
626 pub sns: Option<SubnetSpec>,
627 pub ii: Option<SubnetSpec>,
628 pub fiduciary: Option<SubnetSpec>,
629 pub bitcoin: Option<SubnetSpec>,
630 pub system: Vec<SubnetSpec>,
631 pub application: Vec<SubnetSpec>,
632 pub verified_application: Vec<SubnetSpec>,
633}
634
635#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
637pub struct SubnetSpec {
638 state_config: SubnetStateConfig,
639 instruction_config: SubnetInstructionConfig,
640}
641
642impl SubnetSpec {
643 pub fn with_state_dir(mut self, path: PathBuf) -> SubnetSpec {
644 self.state_config = SubnetStateConfig::FromPath(path);
645 self
646 }
647
648 pub fn with_benchmarking_instruction_config(mut self) -> SubnetSpec {
649 self.instruction_config = SubnetInstructionConfig::Benchmarking;
650 self
651 }
652
653 pub fn get_state_path(&self) -> Option<PathBuf> {
654 self.state_config.get_path()
655 }
656
657 pub fn get_instruction_config(&self) -> SubnetInstructionConfig {
658 self.instruction_config.clone()
659 }
660
661 pub fn is_supported(&self) -> bool {
662 match &self.state_config {
663 SubnetStateConfig::New => true,
664 SubnetStateConfig::FromPath(..) => true,
665 SubnetStateConfig::FromBlobStore(..) => false,
666 }
667 }
668}
669
670impl Default for SubnetSpec {
671 fn default() -> Self {
672 Self {
673 state_config: SubnetStateConfig::New,
674 instruction_config: SubnetInstructionConfig::Production,
675 }
676 }
677}
678
679#[derive(
681 Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema,
682)]
683pub enum SubnetInstructionConfig {
684 Production,
686 Benchmarking,
688}
689
690#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
693pub enum SubnetStateConfig {
694 New,
696 FromPath(PathBuf),
699 FromBlobStore(BlobId),
702}
703
704impl SubnetStateConfig {
705 pub fn get_path(&self) -> Option<PathBuf> {
706 match self {
707 SubnetStateConfig::FromPath(path) => Some(path.clone()),
708 SubnetStateConfig::FromBlobStore(_) => None,
709 SubnetStateConfig::New => None,
710 }
711 }
712}
713
714impl ExtendedSubnetConfigSet {
715 #[allow(clippy::type_complexity)]
717 pub fn get_named(&self) -> Vec<(SubnetKind, Option<PathBuf>, SubnetInstructionConfig)> {
718 use SubnetKind::*;
719 vec![
720 (self.nns.clone(), NNS),
721 (self.sns.clone(), SNS),
722 (self.ii.clone(), II),
723 (self.fiduciary.clone(), Fiduciary),
724 (self.bitcoin.clone(), Bitcoin),
725 ]
726 .into_iter()
727 .filter(|(mb, _)| mb.is_some())
728 .map(|(mb, kind)| {
729 let spec = mb.unwrap();
730 (kind, spec.get_state_path(), spec.get_instruction_config())
731 })
732 .collect()
733 }
734
735 pub fn validate(&self) -> Result<(), String> {
736 if !self.system.is_empty()
737 || !self.application.is_empty()
738 || !self.verified_application.is_empty()
739 || self.nns.is_some()
740 || self.sns.is_some()
741 || self.ii.is_some()
742 || self.fiduciary.is_some()
743 || self.bitcoin.is_some()
744 {
745 return Ok(());
746 }
747 Err("ExtendedSubnetConfigSet must contain at least one subnet".to_owned())
748 }
749
750 pub fn try_with_icp_features(mut self, icp_features: &IcpFeatures) -> Result<Self, String> {
751 let check_empty_subnet = |subnet: &Option<SubnetSpec>, subnet_desc, icp_feature| {
752 if let Some(config) = subnet {
753 if !matches!(config.state_config, SubnetStateConfig::New) {
754 return Err(format!(
755 "The {} subnet must be empty when specifying the `{}` ICP feature.",
756 subnet_desc, icp_feature
757 ));
758 }
759 }
760 Ok(())
761 };
762 let IcpFeatures {
765 registry,
766 cycles_minting,
767 icp_token,
768 cycles_token,
769 nns_governance,
770 sns,
771 ii,
772 nns_ui,
773 } = icp_features;
774 for (flag, icp_feature_str) in [
776 (registry, "registry"),
777 (cycles_minting, "cycles_minting"),
778 (icp_token, "icp_token"),
779 (nns_governance, "nns_governance"),
780 (sns, "sns"),
781 (nns_ui, "nns_ui"),
782 ] {
783 if flag.is_some() {
784 check_empty_subnet(&self.nns, "NNS", icp_feature_str)?;
785 self.nns = Some(self.nns.unwrap_or_default());
786 }
787 }
788 for (flag, icp_feature_str) in [(cycles_token, "cycles_token"), (ii, "ii")] {
790 if flag.is_some() {
791 check_empty_subnet(&self.ii, "II", icp_feature_str)?;
792 self.ii = Some(self.ii.unwrap_or_default());
793 }
794 }
795 for (flag, icp_feature_str) in [(sns, "sns")] {
797 if flag.is_some() {
798 check_empty_subnet(&self.sns, "SNS", icp_feature_str)?;
799 self.sns = Some(self.sns.unwrap_or_default());
800 }
801 }
802 Ok(self)
803 }
804}
805
806#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
808pub struct SubnetConfig {
809 pub subnet_kind: SubnetKind,
810 pub subnet_seed: [u8; 32],
811 pub instruction_config: SubnetInstructionConfig,
813 pub canister_ranges: Vec<CanisterIdRange>,
815}
816
817#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
818pub struct CanisterIdRange {
819 pub start: RawCanisterId,
820 pub end: RawCanisterId,
821}
822
823impl CanisterIdRange {
824 fn contains(&self, canister_id: Principal) -> bool {
825 Principal::from_slice(&self.start.canister_id) <= canister_id
826 && canister_id <= Principal::from_slice(&self.end.canister_id)
827 }
828}
829
830#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
831pub struct Topology {
832 pub subnet_configs: BTreeMap<SubnetId, SubnetConfig>,
833 pub default_effective_canister_id: RawCanisterId,
834}
835
836impl Topology {
837 pub fn get_subnet(&self, canister_id: Principal) -> Option<SubnetId> {
838 self.subnet_configs
839 .iter()
840 .find(|(_, config)| {
841 config
842 .canister_ranges
843 .iter()
844 .any(|r| r.contains(canister_id))
845 })
846 .map(|(subnet_id, _)| subnet_id)
847 .copied()
848 }
849
850 pub fn get_app_subnets(&self) -> Vec<SubnetId> {
851 self.find_subnets(SubnetKind::Application, None)
852 }
853
854 pub fn get_verified_app_subnets(&self) -> Vec<SubnetId> {
855 self.find_subnets(SubnetKind::VerifiedApplication, None)
856 }
857
858 pub fn get_benchmarking_app_subnets(&self) -> Vec<SubnetId> {
859 self.find_subnets(
860 SubnetKind::Application,
861 Some(SubnetInstructionConfig::Benchmarking),
862 )
863 }
864
865 pub fn get_bitcoin(&self) -> Option<SubnetId> {
866 self.find_subnet(SubnetKind::Bitcoin)
867 }
868
869 pub fn get_fiduciary(&self) -> Option<SubnetId> {
870 self.find_subnet(SubnetKind::Fiduciary)
871 }
872
873 pub fn get_ii(&self) -> Option<SubnetId> {
874 self.find_subnet(SubnetKind::II)
875 }
876
877 pub fn get_nns(&self) -> Option<SubnetId> {
878 self.find_subnet(SubnetKind::NNS)
879 }
880
881 pub fn get_sns(&self) -> Option<SubnetId> {
882 self.find_subnet(SubnetKind::SNS)
883 }
884
885 pub fn get_system_subnets(&self) -> Vec<SubnetId> {
886 self.find_subnets(SubnetKind::System, None)
887 }
888
889 fn find_subnets(
890 &self,
891 kind: SubnetKind,
892 instruction_config: Option<SubnetInstructionConfig>,
893 ) -> Vec<SubnetId> {
894 self.subnet_configs
895 .iter()
896 .filter(|(_, config)| {
897 config.subnet_kind == kind
898 && instruction_config
899 .as_ref()
900 .map(|instruction_config| config.instruction_config == *instruction_config)
901 .unwrap_or(true)
902 })
903 .map(|(id, _)| *id)
904 .collect()
905 }
906
907 fn find_subnet(&self, kind: SubnetKind) -> Option<SubnetId> {
908 self.subnet_configs
909 .iter()
910 .find(|(_, config)| config.subnet_kind == kind)
911 .map(|(id, _)| *id)
912 }
913}
914
915#[derive(
916 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
917)]
918pub enum CanisterHttpMethod {
919 GET,
920 POST,
921 HEAD,
922}
923
924#[derive(
925 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
926)]
927pub struct CanisterHttpHeader {
928 pub name: String,
929 pub value: String,
930}
931
932#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
933pub struct RawCanisterHttpRequest {
934 pub subnet_id: RawSubnetId,
935 pub request_id: u64,
936 pub http_method: CanisterHttpMethod,
937 pub url: String,
938 pub headers: Vec<CanisterHttpHeader>,
939 #[serde(deserialize_with = "base64::deserialize")]
940 #[serde(serialize_with = "base64::serialize")]
941 pub body: Vec<u8>,
942 pub max_response_bytes: Option<u64>,
943}
944
945#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
946pub struct CanisterHttpRequest {
947 pub subnet_id: Principal,
948 pub request_id: u64,
949 pub http_method: CanisterHttpMethod,
950 pub url: String,
951 pub headers: Vec<CanisterHttpHeader>,
952 #[serde(deserialize_with = "base64::deserialize")]
953 #[serde(serialize_with = "base64::serialize")]
954 pub body: Vec<u8>,
955 pub max_response_bytes: Option<u64>,
956}
957
958impl From<RawCanisterHttpRequest> for CanisterHttpRequest {
959 fn from(raw_canister_http_request: RawCanisterHttpRequest) -> Self {
960 Self {
961 subnet_id: candid::Principal::from_slice(
962 &raw_canister_http_request.subnet_id.subnet_id,
963 ),
964 request_id: raw_canister_http_request.request_id,
965 http_method: raw_canister_http_request.http_method,
966 url: raw_canister_http_request.url,
967 headers: raw_canister_http_request.headers,
968 body: raw_canister_http_request.body,
969 max_response_bytes: raw_canister_http_request.max_response_bytes,
970 }
971 }
972}
973
974impl From<CanisterHttpRequest> for RawCanisterHttpRequest {
975 fn from(canister_http_request: CanisterHttpRequest) -> Self {
976 Self {
977 subnet_id: canister_http_request.subnet_id.into(),
978 request_id: canister_http_request.request_id,
979 http_method: canister_http_request.http_method,
980 url: canister_http_request.url,
981 headers: canister_http_request.headers,
982 body: canister_http_request.body,
983 max_response_bytes: canister_http_request.max_response_bytes,
984 }
985 }
986}
987
988#[derive(
989 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
990)]
991pub struct CanisterHttpReply {
992 pub status: u16,
993 pub headers: Vec<CanisterHttpHeader>,
994 #[serde(deserialize_with = "base64::deserialize")]
995 #[serde(serialize_with = "base64::serialize")]
996 pub body: Vec<u8>,
997}
998
999#[derive(
1000 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
1001)]
1002pub struct CanisterHttpReject {
1003 pub reject_code: u64,
1004 pub message: String,
1005}
1006
1007#[derive(
1008 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
1009)]
1010pub enum CanisterHttpResponse {
1011 CanisterHttpReply(CanisterHttpReply),
1012 CanisterHttpReject(CanisterHttpReject),
1013}
1014
1015#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
1016pub struct RawMockCanisterHttpResponse {
1017 pub subnet_id: RawSubnetId,
1018 pub request_id: u64,
1019 pub response: CanisterHttpResponse,
1020 pub additional_responses: Vec<CanisterHttpResponse>,
1021}
1022
1023#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
1024pub struct MockCanisterHttpResponse {
1025 pub subnet_id: Principal,
1026 pub request_id: u64,
1027 pub response: CanisterHttpResponse,
1028 pub additional_responses: Vec<CanisterHttpResponse>,
1029}
1030
1031impl From<RawMockCanisterHttpResponse> for MockCanisterHttpResponse {
1032 fn from(raw_mock_canister_http_response: RawMockCanisterHttpResponse) -> Self {
1033 Self {
1034 subnet_id: candid::Principal::from_slice(
1035 &raw_mock_canister_http_response.subnet_id.subnet_id,
1036 ),
1037 request_id: raw_mock_canister_http_response.request_id,
1038 response: raw_mock_canister_http_response.response,
1039 additional_responses: raw_mock_canister_http_response.additional_responses,
1040 }
1041 }
1042}
1043
1044impl From<MockCanisterHttpResponse> for RawMockCanisterHttpResponse {
1045 fn from(mock_canister_http_response: MockCanisterHttpResponse) -> Self {
1046 Self {
1047 subnet_id: RawSubnetId {
1048 subnet_id: mock_canister_http_response.subnet_id.as_slice().to_vec(),
1049 },
1050 request_id: mock_canister_http_response.request_id,
1051 response: mock_canister_http_response.response,
1052 additional_responses: mock_canister_http_response.additional_responses,
1053 }
1054 }
1055}