use crate::constants::HRATLS_APP_PORT; use crate::sgx::get_one_contract; use crate::sgx::grpc_brain::new_app; use crate::sgx::grpc_dtpm::attest_and_send_config; use crate::sgx::package_entry_from_name; use detee_shared::app_proto::NewAppRes; use detee_shared::sgx::types::brain::AppDeployConfig; use detee_shared::sgx::types::dtpm::DtpmConfig; use detee_shared::sgx::types::dtpm::EnvironmentEntry; use tokio_retry::strategy::FixedInterval; use tokio_retry::Retry; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Reqwest(#[from] reqwest::Error), #[error(transparent)] Serde(#[from] serde_yaml::Error), #[error("{0}")] PublicPackage(std::string::String), #[error("{0}")] Brain(#[from] crate::sgx::grpc_brain::Error), #[error("{0}")] Dtpm(#[from] crate::sgx::grpc_dtpm::Error), #[error("{0}")] Deployment(String), } pub async fn hratls_url_and_mr_enclave_from_app_id(app_id: &str) -> (String, Option<[u8; 32]>) { let app_contract = get_one_contract(app_id).await; if app_contract.is_err() { eprintln!("Could not find App contract with ID: {}", app_id); std::process::exit(1); } let app_contract = app_contract.unwrap(); let mr_enclave = app_contract .public_package_mr_enclave .clone() .filter(|vec| vec.len() == 32) .and_then(|vec| vec.try_into().ok()); let public_ip = app_contract.public_ipv4.clone(); let dtpm_port = app_contract .mapped_ports .iter() .find(|port| port.app_port == HRATLS_APP_PORT) .unwrap() .host_port; (format!("https://{public_ip}:{dtpm_port}"), mr_enclave) } pub async fn fetch_config(package_name: &str) -> Result { let index_package_entry = package_entry_from_name(package_name) .ok_or(Error::PublicPackage("package not found for ".to_string() + package_name))?; let launch_config_url = index_package_entry.launch_config_url.clone(); let launch_config_str = reqwest::get(launch_config_url).await?.text().await?; let launch_config = serde_yaml::from_str::(&launch_config_str)?; Ok(launch_config) } pub fn calculate_nanolp_for_app( vcpus: u32, memory_mb: u32, disk_size_mb: u32, hours: u64, node_price: u64, ) -> u64 { // this calculation needs to match the calculation of the network let total_units = (vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_mb as f64 / 10000f64); let locked_nano = (hours as f64 * 60f64 * total_units * node_price as f64) as u64; // TODO: change all println to eprintln println!( "Node price: {}/unit/minute. Total Units for hardware requested: {:.4}. Locking {} LP (offering the App for {} hours).", node_price as f64 / 1_000_000_000.0, total_units, locked_nano as f64 / 1_000_000_000.0, hours ); locked_nano } pub fn override_envs_and_args_launch_config( mut launch_config: DtpmConfig, envs: Vec, args: Vec, ) -> DtpmConfig { if envs.is_empty() && args.is_empty() { return launch_config; } for env in envs { let mut env = env.split("="); let key = env.next().expect("environment variable must be in the format 'key=value'"); let value = env.next().expect("environment variable pair must be in the format 'key=value'"); if launch_config.environments.iter().any(|env| env.name == key) { let existing_env = launch_config.environments.iter_mut().find(|env| env.name == key).unwrap(); existing_env.name = key.to_string(); existing_env.value = value.to_string(); } else { launch_config .environments .push(EnvironmentEntry { name: key.to_string(), value: value.to_string() }); } } for arg in args { launch_config.child_processes.first_mut().unwrap().arguments.push(arg); } launch_config } pub async fn deploy_new_app_and_update_config( app_deploy_config: AppDeployConfig, launch_config: Option, ) -> Result { let new_app_res = new_app(app_deploy_config).await?; if new_app_res.error.is_empty() { if let Some(launch_config) = launch_config { println!("Deploying..."); tokio::time::sleep(tokio::time::Duration::from_millis(3100)).await; Retry::spawn(FixedInterval::from_millis(500).take(5), || { log::debug!("retrying attestation and launch config update"); attest_and_send_config(launch_config.clone(), &new_app_res.uuid) }) .await?; Ok(new_app_res) } else { Ok(new_app_res) } } else { Err(Error::Deployment(new_app_res.error)) } }