use crate::config::model::dfinity::{
DEFAULT_IC_GATEWAY, DEFAULT_IC_GATEWAY_TRAILING_SLASH, NetworkType, PlaygroundConfig,
};
use crate::config::model::local_server_descriptor::LocalServerDescriptor;
use crate::error::network_config::NetworkConfigError;
use crate::error::network_config::NetworkConfigError::{NetworkHasNoProviders, NetworkMustBeLocal};
use crate::error::uri::UriError;
use candid::Principal;
use slog::Logger;
use std::path::PathBuf;
use url::Url;
pub const MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID: Principal =
Principal::from_slice(&[0, 0, 0, 0, 0, 48, 0, 97, 1, 1]);
pub const PLAYGROUND_NETWORK_NAME: &str = "playground";
pub const MOTOKO_PLAYGROUND_CANISTER_TIMEOUT_SECONDS: u64 = 1200;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NetworkTypeDescriptor {
Ephemeral {
wallet_config_path: Option<PathBuf>,
},
Playground {
playground_canister: Principal,
canister_timeout_seconds: u64,
},
Persistent,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NetworkDescriptor {
pub name: String,
pub providers: Vec<String>,
pub r#type: NetworkTypeDescriptor,
pub is_ic: bool,
pub is_ad_hoc: bool,
pub local_server_descriptor: Option<LocalServerDescriptor>,
}
impl NetworkTypeDescriptor {
pub fn new(
r#type: NetworkType,
ephemeral_wallet_config_path: Option<PathBuf>,
playground: Option<PlaygroundConfig>,
) -> Result<Self, NetworkConfigError> {
if let Some(playground_config) = playground {
Ok(NetworkTypeDescriptor::Playground {
playground_canister: Principal::from_text(&playground_config.playground_canister)
.map_err(|e| {
NetworkConfigError::ParsePrincipalFailed(
playground_config.playground_canister,
e,
)
})?,
canister_timeout_seconds: playground_config.timeout_seconds,
})
} else {
match r#type {
NetworkType::Ephemeral => Ok(NetworkTypeDescriptor::Ephemeral {
wallet_config_path: ephemeral_wallet_config_path,
}),
NetworkType::Persistent => Ok(NetworkTypeDescriptor::Persistent),
}
}
}
}
impl NetworkDescriptor {
pub fn ic() -> Self {
NetworkDescriptor {
name: "ic".to_string(),
providers: vec![DEFAULT_IC_GATEWAY.to_string()],
r#type: NetworkTypeDescriptor::Persistent,
is_ic: true,
is_ad_hoc: false,
local_server_descriptor: None,
}
}
#[allow(clippy::ptr_arg)]
pub fn is_ic(network_name: &str, providers: &Vec<String>) -> bool {
let name_match = matches!(
network_name,
"ic" | DEFAULT_IC_GATEWAY | DEFAULT_IC_GATEWAY_TRAILING_SLASH
);
let provider_match = {
providers.len() == 1
&& matches!(
providers.first().unwrap().as_str(),
DEFAULT_IC_GATEWAY | DEFAULT_IC_GATEWAY_TRAILING_SLASH
)
};
name_match || provider_match
}
pub fn is_playground(&self) -> bool {
matches!(self.r#type, NetworkTypeDescriptor::Playground { .. })
}
pub fn first_provider(&self) -> Result<&str, NetworkConfigError> {
match self.providers.first() {
Some(provider) => Ok(provider),
None => Err(NetworkHasNoProviders(self.name.clone())),
}
}
pub fn local_server_descriptor(&self) -> Result<&LocalServerDescriptor, NetworkConfigError> {
match &self.local_server_descriptor {
Some(p) => Ok(p),
None => Err(NetworkMustBeLocal(self.name.clone())),
}
}
pub(crate) fn default_playground_network() -> Self {
Self {
name: PLAYGROUND_NETWORK_NAME.to_string(),
providers: vec![DEFAULT_IC_GATEWAY.to_string()],
r#type: NetworkTypeDescriptor::Playground {
playground_canister: MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID,
canister_timeout_seconds: MOTOKO_PLAYGROUND_CANISTER_TIMEOUT_SECONDS,
},
is_ic: true,
is_ad_hoc: false,
local_server_descriptor: None,
}
}
fn replica_endpoints(&self) -> Result<Vec<Url>, NetworkConfigError> {
self.providers
.iter()
.map(|s| {
Url::parse(s).map_err(|e| {
NetworkConfigError::ParseProviderUrlFailed(Box::new(s.to_string()), e)
})
})
.collect()
}
pub fn get_replica_urls(
&self,
logger: Option<&Logger>,
) -> Result<Vec<Url>, NetworkConfigError> {
if self.name == "local" {
let local_server_descriptor = self.local_server_descriptor()?;
if let Some(port) = local_server_descriptor.get_running_pocketic_port(logger)? {
let mut socket_addr = local_server_descriptor.bind_address;
socket_addr.set_port(port);
let url = format!("http://{socket_addr}");
let url =
Url::parse(&url).map_err(|e| UriError::UrlParseError(url.to_string(), e))?;
return Ok(vec![url]);
}
}
self.replica_endpoints()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn ic_by_netname() {
assert!(NetworkDescriptor::is_ic("ic", &vec![]));
assert!(NetworkDescriptor::is_ic(DEFAULT_IC_GATEWAY, &vec![]));
assert!(NetworkDescriptor::is_ic(
DEFAULT_IC_GATEWAY_TRAILING_SLASH,
&vec![]
));
}
#[test]
fn ic_by_provider() {
assert!(NetworkDescriptor::is_ic(
"not_ic",
&vec![DEFAULT_IC_GATEWAY.to_string()]
));
assert!(NetworkDescriptor::is_ic(
"not_ic",
&vec![DEFAULT_IC_GATEWAY_TRAILING_SLASH.to_string()]
));
}
#[test]
fn ic_by_netname_fail() {
assert!(!NetworkDescriptor::is_ic("not_ic", &vec![]));
}
#[test]
fn ic_by_provider_fail_string() {
assert!(!NetworkDescriptor::is_ic(
"not_ic",
&vec!["not_ic_provider".to_string()]
));
}
#[test]
fn ic_by_provider_fail_unique() {
assert!(!NetworkDescriptor::is_ic(
"not_ic",
&vec![
DEFAULT_IC_GATEWAY.to_string(),
"some_other_provider".to_string()
]
));
}
#[test]
fn playground_canister_id() {
assert_eq!(
MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID,
Principal::from_text("mwrha-maaaa-aaaab-qabqq-cai").unwrap()
)
}
}