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]
573 DefaultConfig,
574}
575
576#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
585pub struct IcpFeatures {
586 pub registry: Option<IcpFeaturesConfig>,
589 pub cycles_minting: Option<IcpFeaturesConfig>,
593 pub icp_token: Option<IcpFeaturesConfig>,
596 pub cycles_token: Option<IcpFeaturesConfig>,
599 pub nns_governance: Option<IcpFeaturesConfig>,
603 pub sns: Option<IcpFeaturesConfig>,
607 pub ii: Option<IcpFeaturesConfig>,
610 pub nns_ui: Option<IcpFeaturesConfig>,
614 pub bitcoin: Option<IcpFeaturesConfig>,
617 pub dogecoin: Option<IcpFeaturesConfig>,
620 pub canister_migration: Option<IcpFeaturesConfig>,
623}
624
625#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
626pub enum InitialTime {
627 Timestamp(RawTime),
631 AutoProgress(AutoProgressConfig),
635}
636
637#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
638pub enum IncompleteStateFlag {
639 #[default]
640 Disabled,
641 Enabled,
642}
643
644#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
645pub struct InstanceConfig {
646 pub subnet_config_set: ExtendedSubnetConfigSet,
647 pub http_gateway_config: Option<InstanceHttpGatewayConfig>,
648 pub state_dir: Option<PathBuf>,
649 pub icp_config: Option<IcpConfig>,
650 pub log_level: Option<String>,
651 pub bitcoind_addr: Option<Vec<SocketAddr>>,
652 pub dogecoind_addr: Option<Vec<SocketAddr>>,
653 pub icp_features: Option<IcpFeatures>,
654 pub incomplete_state: Option<IncompleteStateFlag>,
655 pub initial_time: Option<InitialTime>,
656}
657
658#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, Default, JsonSchema)]
659pub struct ExtendedSubnetConfigSet {
660 pub nns: Option<SubnetSpec>,
661 pub sns: Option<SubnetSpec>,
662 pub ii: Option<SubnetSpec>,
663 pub fiduciary: Option<SubnetSpec>,
664 pub bitcoin: Option<SubnetSpec>,
665 pub system: Vec<SubnetSpec>,
666 pub application: Vec<SubnetSpec>,
667 pub verified_application: Vec<SubnetSpec>,
668}
669
670#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
672pub struct SubnetSpec {
673 state_config: SubnetStateConfig,
674 instruction_config: SubnetInstructionConfig,
675}
676
677impl SubnetSpec {
678 pub fn with_state_dir(mut self, path: PathBuf) -> SubnetSpec {
679 self.state_config = SubnetStateConfig::FromPath(path);
680 self
681 }
682
683 pub fn with_benchmarking_instruction_config(mut self) -> SubnetSpec {
684 self.instruction_config = SubnetInstructionConfig::Benchmarking;
685 self
686 }
687
688 pub fn get_state_path(&self) -> Option<PathBuf> {
689 self.state_config.get_path()
690 }
691
692 pub fn get_instruction_config(&self) -> SubnetInstructionConfig {
693 self.instruction_config.clone()
694 }
695
696 pub fn is_supported(&self) -> bool {
697 match &self.state_config {
698 SubnetStateConfig::New => true,
699 SubnetStateConfig::FromPath(..) => true,
700 SubnetStateConfig::FromBlobStore(..) => false,
701 }
702 }
703}
704
705impl Default for SubnetSpec {
706 fn default() -> Self {
707 Self {
708 state_config: SubnetStateConfig::New,
709 instruction_config: SubnetInstructionConfig::Production,
710 }
711 }
712}
713
714#[derive(
716 Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema,
717)]
718pub enum SubnetInstructionConfig {
719 Production,
721 Benchmarking,
723}
724
725#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema)]
728pub enum SubnetStateConfig {
729 New,
731 FromPath(PathBuf),
734 FromBlobStore(BlobId),
737}
738
739impl SubnetStateConfig {
740 pub fn get_path(&self) -> Option<PathBuf> {
741 match self {
742 SubnetStateConfig::FromPath(path) => Some(path.clone()),
743 SubnetStateConfig::FromBlobStore(_) => None,
744 SubnetStateConfig::New => None,
745 }
746 }
747}
748
749impl ExtendedSubnetConfigSet {
750 #[allow(clippy::type_complexity)]
752 pub fn get_named(&self) -> Vec<(SubnetKind, Option<PathBuf>, SubnetInstructionConfig)> {
753 use SubnetKind::*;
754 vec![
755 (self.nns.clone(), NNS),
756 (self.sns.clone(), SNS),
757 (self.ii.clone(), II),
758 (self.fiduciary.clone(), Fiduciary),
759 (self.bitcoin.clone(), Bitcoin),
760 ]
761 .into_iter()
762 .filter(|(mb, _)| mb.is_some())
763 .map(|(mb, kind)| {
764 let spec = mb.unwrap();
765 (kind, spec.get_state_path(), spec.get_instruction_config())
766 })
767 .collect()
768 }
769
770 pub fn validate(&self) -> Result<(), String> {
771 if !self.system.is_empty()
772 || !self.application.is_empty()
773 || !self.verified_application.is_empty()
774 || self.nns.is_some()
775 || self.sns.is_some()
776 || self.ii.is_some()
777 || self.fiduciary.is_some()
778 || self.bitcoin.is_some()
779 {
780 return Ok(());
781 }
782 Err("ExtendedSubnetConfigSet must contain at least one subnet".to_owned())
783 }
784
785 pub fn try_with_icp_features(mut self, icp_features: &IcpFeatures) -> Result<Self, String> {
786 let check_empty_subnet = |subnet: &Option<SubnetSpec>, subnet_desc, icp_feature| {
787 if let Some(config) = subnet
788 && !matches!(config.state_config, SubnetStateConfig::New)
789 {
790 return Err(format!(
791 "The {subnet_desc} subnet must be empty when specifying the `{icp_feature}` ICP feature."
792 ));
793 }
794 Ok(())
795 };
796 let IcpFeatures {
799 registry,
800 cycles_minting,
801 icp_token,
802 cycles_token,
803 nns_governance,
804 sns,
805 ii,
806 nns_ui,
807 bitcoin,
808 dogecoin,
809 canister_migration,
810 } = icp_features;
811 for (flag, icp_feature_str) in [
813 (registry, "registry"),
814 (cycles_minting, "cycles_minting"),
815 (icp_token, "icp_token"),
816 (nns_governance, "nns_governance"),
817 (sns, "sns"),
818 (nns_ui, "nns_ui"),
819 (canister_migration, "canister_migration"),
820 ] {
821 if flag.is_some() {
822 check_empty_subnet(&self.nns, "NNS", icp_feature_str)?;
823 self.nns = Some(self.nns.unwrap_or_default());
824 }
825 }
826 for (flag, icp_feature_str) in [(cycles_token, "cycles_token"), (ii, "ii")] {
828 if flag.is_some() {
829 check_empty_subnet(&self.ii, "II", icp_feature_str)?;
830 self.ii = Some(self.ii.unwrap_or_default());
831 }
832 }
833 for (flag, icp_feature_str) in [(sns, "sns")] {
835 if flag.is_some() {
836 check_empty_subnet(&self.sns, "SNS", icp_feature_str)?;
837 self.sns = Some(self.sns.unwrap_or_default());
838 }
839 }
840 for (flag, icp_feature_str) in [(bitcoin, "bitcoin"), (dogecoin, "dogecoin")] {
842 if flag.is_some() {
843 check_empty_subnet(&self.bitcoin, "Bitcoin", icp_feature_str)?;
844 self.bitcoin = Some(self.bitcoin.unwrap_or_default());
845 }
846 }
847 Ok(self)
848 }
849}
850
851#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
853pub struct SubnetConfig {
854 pub subnet_kind: SubnetKind,
855 pub subnet_seed: [u8; 32],
856 pub instruction_config: SubnetInstructionConfig,
858 pub canister_ranges: Vec<CanisterIdRange>,
860}
861
862#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
863pub struct CanisterIdRange {
864 pub start: RawCanisterId,
865 pub end: RawCanisterId,
866}
867
868impl CanisterIdRange {
869 fn contains(&self, canister_id: Principal) -> bool {
870 Principal::from_slice(&self.start.canister_id) <= canister_id
871 && canister_id <= Principal::from_slice(&self.end.canister_id)
872 }
873}
874
875#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema)]
876pub struct Topology {
877 pub subnet_configs: BTreeMap<SubnetId, SubnetConfig>,
878 pub default_effective_canister_id: RawCanisterId,
879}
880
881impl Topology {
882 pub fn get_subnet(&self, canister_id: Principal) -> Option<SubnetId> {
883 self.subnet_configs
884 .iter()
885 .find(|(_, config)| {
886 config
887 .canister_ranges
888 .iter()
889 .any(|r| r.contains(canister_id))
890 })
891 .map(|(subnet_id, _)| subnet_id)
892 .copied()
893 }
894
895 pub fn get_app_subnets(&self) -> Vec<SubnetId> {
896 self.find_subnets(SubnetKind::Application, None)
897 }
898
899 pub fn get_verified_app_subnets(&self) -> Vec<SubnetId> {
900 self.find_subnets(SubnetKind::VerifiedApplication, None)
901 }
902
903 pub fn get_benchmarking_app_subnets(&self) -> Vec<SubnetId> {
904 self.find_subnets(
905 SubnetKind::Application,
906 Some(SubnetInstructionConfig::Benchmarking),
907 )
908 }
909
910 pub fn get_bitcoin(&self) -> Option<SubnetId> {
911 self.find_subnet(SubnetKind::Bitcoin)
912 }
913
914 pub fn get_fiduciary(&self) -> Option<SubnetId> {
915 self.find_subnet(SubnetKind::Fiduciary)
916 }
917
918 pub fn get_ii(&self) -> Option<SubnetId> {
919 self.find_subnet(SubnetKind::II)
920 }
921
922 pub fn get_nns(&self) -> Option<SubnetId> {
923 self.find_subnet(SubnetKind::NNS)
924 }
925
926 pub fn get_sns(&self) -> Option<SubnetId> {
927 self.find_subnet(SubnetKind::SNS)
928 }
929
930 pub fn get_system_subnets(&self) -> Vec<SubnetId> {
931 self.find_subnets(SubnetKind::System, None)
932 }
933
934 fn find_subnets(
935 &self,
936 kind: SubnetKind,
937 instruction_config: Option<SubnetInstructionConfig>,
938 ) -> Vec<SubnetId> {
939 self.subnet_configs
940 .iter()
941 .filter(|(_, config)| {
942 config.subnet_kind == kind
943 && instruction_config
944 .as_ref()
945 .map(|instruction_config| config.instruction_config == *instruction_config)
946 .unwrap_or(true)
947 })
948 .map(|(id, _)| *id)
949 .collect()
950 }
951
952 fn find_subnet(&self, kind: SubnetKind) -> Option<SubnetId> {
953 self.subnet_configs
954 .iter()
955 .find(|(_, config)| config.subnet_kind == kind)
956 .map(|(id, _)| *id)
957 }
958}
959
960#[derive(
961 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
962)]
963pub enum CanisterHttpMethod {
964 GET,
965 POST,
966 HEAD,
967}
968
969#[derive(
970 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
971)]
972pub struct CanisterHttpHeader {
973 pub name: String,
974 pub value: String,
975}
976
977#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
978pub struct RawCanisterHttpRequest {
979 pub subnet_id: RawSubnetId,
980 pub request_id: u64,
981 pub http_method: CanisterHttpMethod,
982 pub url: String,
983 pub headers: Vec<CanisterHttpHeader>,
984 #[serde(deserialize_with = "base64::deserialize")]
985 #[serde(serialize_with = "base64::serialize")]
986 pub body: Vec<u8>,
987 pub max_response_bytes: Option<u64>,
988}
989
990#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
991pub struct CanisterHttpRequest {
992 pub subnet_id: Principal,
993 pub request_id: u64,
994 pub http_method: CanisterHttpMethod,
995 pub url: String,
996 pub headers: Vec<CanisterHttpHeader>,
997 #[serde(deserialize_with = "base64::deserialize")]
998 #[serde(serialize_with = "base64::serialize")]
999 pub body: Vec<u8>,
1000 pub max_response_bytes: Option<u64>,
1001}
1002
1003impl From<RawCanisterHttpRequest> for CanisterHttpRequest {
1004 fn from(raw_canister_http_request: RawCanisterHttpRequest) -> Self {
1005 Self {
1006 subnet_id: candid::Principal::from_slice(
1007 &raw_canister_http_request.subnet_id.subnet_id,
1008 ),
1009 request_id: raw_canister_http_request.request_id,
1010 http_method: raw_canister_http_request.http_method,
1011 url: raw_canister_http_request.url,
1012 headers: raw_canister_http_request.headers,
1013 body: raw_canister_http_request.body,
1014 max_response_bytes: raw_canister_http_request.max_response_bytes,
1015 }
1016 }
1017}
1018
1019impl From<CanisterHttpRequest> for RawCanisterHttpRequest {
1020 fn from(canister_http_request: CanisterHttpRequest) -> Self {
1021 Self {
1022 subnet_id: canister_http_request.subnet_id.into(),
1023 request_id: canister_http_request.request_id,
1024 http_method: canister_http_request.http_method,
1025 url: canister_http_request.url,
1026 headers: canister_http_request.headers,
1027 body: canister_http_request.body,
1028 max_response_bytes: canister_http_request.max_response_bytes,
1029 }
1030 }
1031}
1032
1033#[derive(
1034 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
1035)]
1036pub struct CanisterHttpReply {
1037 pub status: u16,
1038 pub headers: Vec<CanisterHttpHeader>,
1039 #[serde(deserialize_with = "base64::deserialize")]
1040 #[serde(serialize_with = "base64::serialize")]
1041 pub body: Vec<u8>,
1042}
1043
1044#[derive(
1045 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
1046)]
1047pub struct CanisterHttpReject {
1048 pub reject_code: u64,
1049 pub message: String,
1050}
1051
1052#[derive(
1053 Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, JsonSchema,
1054)]
1055pub enum CanisterHttpResponse {
1056 CanisterHttpReply(CanisterHttpReply),
1057 CanisterHttpReject(CanisterHttpReject),
1058}
1059
1060#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
1061pub struct RawMockCanisterHttpResponse {
1062 pub subnet_id: RawSubnetId,
1063 pub request_id: u64,
1064 pub response: CanisterHttpResponse,
1065 pub additional_responses: Vec<CanisterHttpResponse>,
1066}
1067
1068#[derive(Clone, Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
1069pub struct MockCanisterHttpResponse {
1070 pub subnet_id: Principal,
1071 pub request_id: u64,
1072 pub response: CanisterHttpResponse,
1073 pub additional_responses: Vec<CanisterHttpResponse>,
1074}
1075
1076impl From<RawMockCanisterHttpResponse> for MockCanisterHttpResponse {
1077 fn from(raw_mock_canister_http_response: RawMockCanisterHttpResponse) -> Self {
1078 Self {
1079 subnet_id: candid::Principal::from_slice(
1080 &raw_mock_canister_http_response.subnet_id.subnet_id,
1081 ),
1082 request_id: raw_mock_canister_http_response.request_id,
1083 response: raw_mock_canister_http_response.response,
1084 additional_responses: raw_mock_canister_http_response.additional_responses,
1085 }
1086 }
1087}
1088
1089impl From<MockCanisterHttpResponse> for RawMockCanisterHttpResponse {
1090 fn from(mock_canister_http_response: MockCanisterHttpResponse) -> Self {
1091 Self {
1092 subnet_id: RawSubnetId {
1093 subnet_id: mock_canister_http_response.subnet_id.as_slice().to_vec(),
1094 },
1095 request_id: mock_canister_http_response.request_id,
1096 response: mock_canister_http_response.response,
1097 additional_responses: mock_canister_http_response.additional_responses,
1098 }
1099 }
1100}
1101
1102#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
1103pub struct RawCanisterSnapshotDownload {
1104 pub sender: RawPrincipalId,
1105 pub canister_id: RawCanisterId,
1106 #[serde(deserialize_with = "base64::deserialize")]
1107 #[serde(serialize_with = "base64::serialize")]
1108 pub snapshot_id: Vec<u8>,
1109 pub snapshot_dir: PathBuf,
1110}
1111
1112#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
1113pub struct RawCanisterSnapshotUpload {
1114 pub sender: RawPrincipalId,
1115 pub canister_id: RawCanisterId,
1116 pub replace_snapshot: Option<RawCanisterSnapshotId>,
1117 pub snapshot_dir: PathBuf,
1118}
1119
1120#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
1121pub struct RawCanisterSnapshotId {
1122 #[serde(deserialize_with = "base64::deserialize")]
1123 #[serde(serialize_with = "base64::serialize")]
1124 pub snapshot_id: Vec<u8>,
1125}