Redirect to pubsub node and some bug fixes #8
							
								
								
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1182,7 +1182,7 @@ dependencies = [ | |||||||
| [[package]] | [[package]] | ||||||
| name = "detee-shared" | name = "detee-shared" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=surreal_brain#fb38352e1b47837b14f32d8df5ae7f6b17202aae" | source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=surreal_brain_app#0b195b4589e4ec689af7ddca27dc051716ecee78" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bincode", |  "bincode", | ||||||
|  "prost", |  "prost", | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ tokio-retry = "0.3.0" | |||||||
| detee-sgx = { git = "ssh://git@gitea.detee.cloud/testnet/detee-sgx.git", branch = "hratls", features=["hratls", "qvl"] } | detee-sgx = { git = "ssh://git@gitea.detee.cloud/testnet/detee-sgx.git", branch = "hratls", features=["hratls", "qvl"] } | ||||||
| shadow-rs = { version = "1.1.1", features = ["metadata"] } | shadow-rs = { version = "1.1.1", features = ["metadata"] } | ||||||
| 
 | 
 | ||||||
| detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "surreal_brain" } | detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "surreal_brain_app" } | ||||||
| # detee-shared = { path = "../detee-shared" } | # detee-shared = { path = "../detee-shared" } | ||||||
| 
 | 
 | ||||||
| [build-dependencies] | [build-dependencies] | ||||||
|  | |||||||
| @ -127,9 +127,9 @@ fn clap_cmd() -> Command { | |||||||
|                     .arg( |                     .arg( | ||||||
|                         Arg::new("disk") |                         Arg::new("disk") | ||||||
|                         .long("disk") |                         .long("disk") | ||||||
|                         .default_value("1000") |                         .default_value("2") | ||||||
|                         .value_parser(clap::value_parser!(u32).range(300..8000)) |                         .value_parser(clap::value_parser!(u32).range(1..100)) | ||||||
|                         .help("disk size in MB") |                         .help("disk size in GB") | ||||||
|                     ) |                     ) | ||||||
|                     .arg( |                     .arg( | ||||||
|                         Arg::new("port") |                         Arg::new("port") | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | use crate::constants::{BRAIN_STAGING, BRAIN_TESTING}; | ||||||
| use crate::{general, utils::block_on}; | use crate::{general, utils::block_on}; | ||||||
| use ed25519_dalek::SigningKey; | use ed25519_dalek::SigningKey; | ||||||
| use log::{debug, info, warn}; | use log::{debug, info, warn}; | ||||||
| @ -309,20 +310,30 @@ impl Config { | |||||||
| 
 | 
 | ||||||
|     pub fn get_brain_info() -> (String, String) { |     pub fn get_brain_info() -> (String, String) { | ||||||
|         match Self::init_config().network.as_str() { |         match Self::init_config().network.as_str() { | ||||||
|             "staging" => ("https://184.107.169.199:49092".to_string(), "staging-brain".to_string()), |  | ||||||
|             "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), |             "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), | ||||||
|             _ => ("https://173.234.17.2:39477".to_string(), "testnet-brain".to_string()), |             "staging" => { | ||||||
|  |                 let url = BRAIN_STAGING.to_string(); | ||||||
|  |                 log::info!("Using staging brain URL: {url}"); | ||||||
|  |                 (url, "staging-brain".to_string()) | ||||||
|  |             } | ||||||
|  |             _ => { | ||||||
|  |                 let url = BRAIN_TESTING.to_string(); | ||||||
|  |                 log::info!("Using testnet brain URL: {url}"); | ||||||
|  |                 (url, "testnet-brain".to_string()) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn get_brain_channel() -> Result<tonic::transport::Channel, Error> { |     pub async fn connect_brain_channel( | ||||||
|         let (brain_url, brain_san) = Self::get_brain_info(); |         brain_url: String, | ||||||
| 
 |     ) -> Result<tonic::transport::Channel, Error> { | ||||||
|         use hyper_rustls::HttpsConnectorBuilder; |         use hyper_rustls::HttpsConnectorBuilder; | ||||||
|         use rustls::pki_types::pem::PemObject; |         use rustls::pki_types::pem::PemObject; | ||||||
|         use rustls::pki_types::CertificateDer; |         use rustls::pki_types::CertificateDer; | ||||||
|         use rustls::{ClientConfig, RootCertStore}; |         use rustls::{ClientConfig, RootCertStore}; | ||||||
| 
 | 
 | ||||||
|  |         let brain_san = Config::get_brain_info().1; | ||||||
|  | 
 | ||||||
|         let mut detee_root_ca_store = RootCertStore::empty(); |         let mut detee_root_ca_store = RootCertStore::empty(); | ||||||
|         detee_root_ca_store |         detee_root_ca_store | ||||||
|             .add(CertificateDer::from_pem_file(Config::get_root_ca_path()?).map_err(|e| { |             .add(CertificateDer::from_pem_file(Config::get_root_ca_path()?).map_err(|e| { | ||||||
|  | |||||||
| @ -1 +1,22 @@ | |||||||
|  | use rand::Rng; | ||||||
|  | use std::sync::LazyLock; | ||||||
|  | 
 | ||||||
| pub const HRATLS_APP_PORT: u32 = 34500; | pub const HRATLS_APP_PORT: u32 = 34500; | ||||||
|  | pub const MAX_REDIRECTS: u16 = 3; | ||||||
|  | pub const STAGING_BRAIN_URLS: [&str; 3] = [ | ||||||
|  |     "https://156.146.63.216:31337", // staging brain 1
 | ||||||
|  |     "https://156.146.63.216:31337",     // staging brain 2
 | ||||||
|  |     "https://156.146.63.216:31337",    // staging brain 3
 | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | pub const TESTNET_BRAIN_URLS: [&str; 3] = [ | ||||||
|  |     "https://184.107.169.199:45223", // testnet brain 1
 | ||||||
|  |     "https://149.22.95.1:44522",     // testnet brain 2
 | ||||||
|  |     "https://149.36.48.99:48638",    // testnet brain 3
 | ||||||
|  | ]; | ||||||
|  | 
 | ||||||
|  | pub static BRAIN_STAGING: LazyLock<&str> = | ||||||
|  |     LazyLock::new(|| STAGING_BRAIN_URLS[rand::thread_rng().gen_range(0..STAGING_BRAIN_URLS.len())]); | ||||||
|  | 
 | ||||||
|  | pub static BRAIN_TESTING: LazyLock<&str> = | ||||||
|  |     LazyLock::new(|| TESTNET_BRAIN_URLS[rand::thread_rng().gen_range(0..TESTNET_BRAIN_URLS.len())]); | ||||||
|  | |||||||
| @ -35,7 +35,8 @@ pub enum Error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn client() -> Result<BrainGeneralCliClient<Channel>, Error> { | async fn client() -> Result<BrainGeneralCliClient<Channel>, Error> { | ||||||
|     Ok(BrainGeneralCliClient::new(Config::get_brain_channel().await?)) |     let default_brain_url = Config::get_brain_info().0; | ||||||
|  |     Ok(BrainGeneralCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn get_balance(account: &str) -> Result<AccountBalance, Error> { | pub async fn get_balance(account: &str) -> Result<AccountBalance, Error> { | ||||||
|  | |||||||
| @ -80,9 +80,9 @@ fn handle_deploy( | |||||||
|         // TODO: maybe add launch config on deploy command with --launch-config flag
 |         // TODO: maybe add launch config on deploy command with --launch-config flag
 | ||||||
|         (AppDeployConfig::from_path(file_path).unwrap(), None) |         (AppDeployConfig::from_path(file_path).unwrap(), None) | ||||||
|     } else { |     } else { | ||||||
|         let vcpu = *deploy_match.get_one::<u32>("vcpus").unwrap(); |         let vcpus = *deploy_match.get_one::<u32>("vcpus").unwrap(); | ||||||
|         let memory_mb = *deploy_match.get_one::<u32>("memory").unwrap(); |         let memory_mb = *deploy_match.get_one::<u32>("memory").unwrap(); | ||||||
|         let disk_mb = *deploy_match.get_one::<u32>("disk").unwrap(); |         let disk_size_gb = *deploy_match.get_one::<u32>("disk").unwrap(); | ||||||
|         let port = |         let port = | ||||||
|             deploy_match.get_many::<u32>("port").unwrap_or_default().cloned().collect::<Vec<_>>(); |             deploy_match.get_many::<u32>("port").unwrap_or_default().cloned().collect::<Vec<_>>(); | ||||||
|         let package_name = deploy_match.get_one::<String>("package").unwrap().clone(); |         let package_name = deploy_match.get_one::<String>("package").unwrap().clone(); | ||||||
| @ -98,7 +98,7 @@ fn handle_deploy( | |||||||
| 
 | 
 | ||||||
|         let private_package = false; |         let private_package = false; | ||||||
| 
 | 
 | ||||||
|         let resource = Resource { vcpu, memory_mb, disk_mb, port }; |         let resource = Resource { vcpus, memory_mb, disk_size_gb, port }; | ||||||
|         let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) { |         let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) { | ||||||
|             Ok(node) => node.node_pubkey, |             Ok(node) => node.node_pubkey, | ||||||
|             Err(e) => { |             Err(e) => { | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ use detee_shared::sgx::types::brain::AppDeployConfig; | |||||||
| use tokio_stream::StreamExt; | use tokio_stream::StreamExt; | ||||||
| use tonic::transport::Channel; | use tonic::transport::Channel; | ||||||
| 
 | 
 | ||||||
|  | use crate::call_with_follow_redirect; | ||||||
| use crate::config::Config; | use crate::config::Config; | ||||||
| use crate::sgx::utils::calculate_nanolp_for_app; | use crate::sgx::utils::calculate_nanolp_for_app; | ||||||
| use crate::utils::{self, sign_request}; | use crate::utils::{self, sign_request}; | ||||||
| @ -25,6 +26,10 @@ pub enum Error { | |||||||
|     CorruptedRootCa(#[from] std::io::Error), |     CorruptedRootCa(#[from] std::io::Error), | ||||||
|     #[error("Internal app error: could not parse Brain URL")] |     #[error("Internal app error: could not parse Brain URL")] | ||||||
|     CorruptedBrainUrl, |     CorruptedBrainUrl, | ||||||
|  |     #[error("Max redirects exceeded: {0}")] | ||||||
|  |     MaxRedirectsExceeded(String), | ||||||
|  |     #[error("Redirect error: {0}")] | ||||||
|  |     RedirectError(String), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Result<T> = std::result::Result<T, Error>; | type Result<T> = std::result::Result<T, Error>; | ||||||
| @ -48,7 +53,7 @@ impl crate::HumanOutput for AppContract { | |||||||
|             .mapped_ports |             .mapped_ports | ||||||
|             .clone() |             .clone() | ||||||
|             .iter() |             .iter() | ||||||
|             .map(|p| format!("({},{})", p.host_port, p.app_port)) |             .map(|p| format!("({},{})", p.host_port, p.guest_port)) | ||||||
|             .collect::<Vec<_>>() |             .collect::<Vec<_>>() | ||||||
|             .join(", "); |             .join(", "); | ||||||
|         println!( |         println!( | ||||||
| @ -57,8 +62,8 @@ impl crate::HumanOutput for AppContract { | |||||||
|         ); |         ); | ||||||
|         println!("The app has mapped ports by the node are: {mapped_ports}"); |         println!("The app has mapped ports by the node are: {mapped_ports}"); | ||||||
|         println!( |         println!( | ||||||
|             "The App has {} vCPUS, {}MB of memory and a disk of {} MB.", |             "The App has {} vCPUS, {}MB of memory and a disk of {} GB.", | ||||||
|             app_resource.vcpu, app_resource.memory_mb, app_resource.disk_mb |             app_resource.vcpus, app_resource.memory_mb, app_resource.disk_size_gb | ||||||
|         ); |         ); | ||||||
|         println!("You have locked {} nanoLP in the contract, that get collected at a rate of {} nanoLP per minute.", |         println!("You have locked {} nanoLP in the contract, that get collected at a rate of {} nanoLP per minute.", | ||||||
|             self.locked_nano, self.nano_per_minute); |             self.locked_nano, self.nano_per_minute); | ||||||
| @ -66,7 +71,12 @@ impl crate::HumanOutput for AppContract { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn client() -> Result<BrainAppCliClient<Channel>> { | async fn client() -> Result<BrainAppCliClient<Channel>> { | ||||||
|     Ok(BrainAppCliClient::new(Config::get_brain_channel().await?)) |     let default_brain_url = Config::get_brain_info().0; | ||||||
|  |     Ok(BrainAppCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async fn client_from_endpoint(reconnect_endpoint: String) -> Result<BrainAppCliClient<Channel>> { | ||||||
|  |     Ok(BrainAppCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result<NewAppRes> { | pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result<NewAppRes> { | ||||||
| @ -74,9 +84,9 @@ pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result<NewAppRes> { | |||||||
|     let mut req: NewAppReq = app_deploy_config.clone().into(); |     let mut req: NewAppReq = app_deploy_config.clone().into(); | ||||||
| 
 | 
 | ||||||
|     let locked_nano = calculate_nanolp_for_app( |     let locked_nano = calculate_nanolp_for_app( | ||||||
|         resource.vcpu, |         resource.vcpus, | ||||||
|         resource.memory_mb, |         resource.memory_mb, | ||||||
|         resource.disk_mb, |         resource.disk_size_gb, | ||||||
|         app_deploy_config.hours, |         app_deploy_config.hours, | ||||||
|         req.price_per_unit, |         req.price_per_unit, | ||||||
|     ); |     ); | ||||||
| @ -86,14 +96,21 @@ pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result<NewAppRes> { | |||||||
|     req.admin_pubkey = Config::get_detee_wallet()?; |     req.admin_pubkey = Config::get_detee_wallet()?; | ||||||
|     req.hratls_pubkey = Config::get_hratls_pubkey_hex()?; |     req.hratls_pubkey = Config::get_hratls_pubkey_hex()?; | ||||||
| 
 | 
 | ||||||
|     let res = client().await?.deploy_app(sign_request(req)?).await?; |     let client = client().await?; | ||||||
|     Ok(res.into_inner()) |     match call_with_follow_redirect!(client, req, new_app).await { | ||||||
|  |         Ok(res) => Ok(res.into_inner()), | ||||||
|  |         Err(e) => { | ||||||
|  |             log::error!("Failed to create new app: {}", e); | ||||||
|  |             Err(e.into()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn delete_app(app_uuid: String) -> Result<()> { | pub async fn delete_app(app_uuid: String) -> Result<()> { | ||||||
|     let admin_pubkey = Config::get_detee_wallet()?; |     let admin_pubkey = Config::get_detee_wallet()?; | ||||||
|     let delete_req = DelAppReq { uuid: app_uuid, admin_pubkey }; |     let delete_req = DelAppReq { uuid: app_uuid, admin_pubkey }; | ||||||
|     let _ = client().await?.delete_app(sign_request(delete_req)?).await?; |     let client = client().await?; | ||||||
|  |     let _ = call_with_follow_redirect!(client, delete_req, delete_app).await?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ pub async fn connect_app_dtpm_client(app_uuid: &str) -> Result<DtpmConfigManager | |||||||
|     let private_key_pem = Config::get_hratls_private_key()?; |     let private_key_pem = Config::get_hratls_private_key()?; | ||||||
| 
 | 
 | ||||||
|     let (hratls_uri, package_mr_enclave) = hratls_url_and_mr_enclave_from_app_id(app_uuid).await?; |     let (hratls_uri, package_mr_enclave) = hratls_url_and_mr_enclave_from_app_id(app_uuid).await?; | ||||||
|     log::info!("hratls uri: {}\nmr_enclave: {:?}", &hratls_uri, &package_mr_enclave); |     log::info!("hratls uri: {} mr_enclave: {:?}", &hratls_uri, &package_mr_enclave); | ||||||
| 
 | 
 | ||||||
|     let hratls_config = |     let hratls_config = | ||||||
|         Arc::new(RwLock::new(HRaTlsConfig::new().with_hratls_private_key_pem(private_key_pem))); |         Arc::new(RwLock::new(HRaTlsConfig::new().with_hratls_private_key_pem(private_key_pem))); | ||||||
|  | |||||||
| @ -41,11 +41,11 @@ pub struct AppContract { | |||||||
|     pub uuid: String, |     pub uuid: String, | ||||||
|     pub name: String, |     pub name: String, | ||||||
|     #[tabled(rename = "Cores")] |     #[tabled(rename = "Cores")] | ||||||
|     pub vcpu: u32, |     pub vcpus: u32, | ||||||
|     #[tabled(rename = "Mem (MB)")] |     #[tabled(rename = "Mem (MB)")] | ||||||
|     pub memory_mb: u32, |     pub memory_mb: u32, | ||||||
|     #[tabled(rename = "Disk (MB)")] |     #[tabled(rename = "Disk (GB)")] | ||||||
|     pub disk_mb: u32, |     pub disk_size_gb: u32, | ||||||
|     #[tabled(rename = "LP/h")] |     #[tabled(rename = "LP/h")] | ||||||
|     pub cost_h: String, |     pub cost_h: String, | ||||||
|     #[tabled(rename = "time left", display_with = "display_mins")] |     #[tabled(rename = "time left", display_with = "display_mins")] | ||||||
| @ -137,22 +137,22 @@ impl From<AppContractPB> for AppContract { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let AppResource { vcpu, memory_mb, disk_mb, .. } = |         let AppResource { vcpus, memory_mb, disk_size_gb, .. } = | ||||||
|             brain_app_contract.resource.unwrap_or_default(); |             brain_app_contract.resource.unwrap_or_default(); | ||||||
| 
 | 
 | ||||||
|         let exposed_host_ports = brain_app_contract |         let exposed_host_ports = brain_app_contract | ||||||
|             .mapped_ports |             .mapped_ports | ||||||
|             .iter() |             .iter() | ||||||
|             .map(|port| (port.host_port, port.app_port)) |             .map(|port| (port.host_port, port.guest_port)) | ||||||
|             .collect::<Vec<_>>(); |             .collect::<Vec<_>>(); | ||||||
| 
 | 
 | ||||||
|         Self { |         Self { | ||||||
|             location, |             location, | ||||||
|             uuid: brain_app_contract.uuid, |             uuid: brain_app_contract.uuid, | ||||||
|             name: brain_app_contract.app_name, |             name: brain_app_contract.app_name, | ||||||
|             vcpu, |             vcpus, | ||||||
|             memory_mb, |             memory_mb, | ||||||
|             disk_mb, |             disk_size_gb, | ||||||
|             cost_h: format!( |             cost_h: format!( | ||||||
|                 "{:.4}", |                 "{:.4}", | ||||||
|                 (brain_app_contract.nano_per_minute * 60) as f64 / 1_000_000_000.0 |                 (brain_app_contract.nano_per_minute * 60) as f64 / 1_000_000_000.0 | ||||||
| @ -181,7 +181,6 @@ pub async fn get_one_contract(uuid: &str) -> Result<AppContractPB, Error> { | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Serialize, Deserialize)] | ||||||
| pub struct AppDeployResponse { | pub struct AppDeployResponse { | ||||||
|     pub status: String, |  | ||||||
|     pub uuid: String, |     pub uuid: String, | ||||||
|     pub name: String, |     pub name: String, | ||||||
|     pub node_ip: String, |     pub node_ip: String, | ||||||
| @ -198,14 +197,13 @@ impl crate::HumanOutput for AppDeployResponse { | |||||||
| impl From<(NewAppRes, String)> for AppDeployResponse { | impl From<(NewAppRes, String)> for AppDeployResponse { | ||||||
|     fn from((value, name): (NewAppRes, String)) -> Self { |     fn from((value, name): (NewAppRes, String)) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             status: value.status, |  | ||||||
|             uuid: value.uuid, |             uuid: value.uuid, | ||||||
|             name, |             name, | ||||||
|             node_ip: value.ip_address, |             node_ip: value.ip_address, | ||||||
|             hratls_port: value |             hratls_port: value | ||||||
|                 .mapped_ports |                 .mapped_ports | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .find(|port| port.app_port == HRATLS_APP_PORT) |                 .find(|port| port.guest_port == HRATLS_APP_PORT) | ||||||
|                 .map(|port| port.host_port) |                 .map(|port| port.host_port) | ||||||
|                 .unwrap_or(HRATLS_APP_PORT), |                 .unwrap_or(HRATLS_APP_PORT), | ||||||
|             error: value.error, |             error: value.error, | ||||||
| @ -230,14 +228,15 @@ pub async fn get_app_node( | |||||||
|     location: snp::deploy::Location, |     location: snp::deploy::Location, | ||||||
| ) -> Result<AppNodeListResp, grpc_brain::Error> { | ) -> Result<AppNodeListResp, grpc_brain::Error> { | ||||||
|     let app_node_filter = AppNodeFilters { |     let app_node_filter = AppNodeFilters { | ||||||
|         vcpus: resource.vcpu, |         vcpus: resource.vcpus, | ||||||
|         memory_mb: resource.memory_mb, |         memory_mb: resource.memory_mb, | ||||||
|         storage_mb: resource.disk_mb, |         storage_gb: resource.disk_size_gb, | ||||||
|         country: location.country.clone().unwrap_or_default(), |         country: location.country.clone().unwrap_or_default(), | ||||||
|         region: location.region.clone().unwrap_or_default(), |         region: location.region.clone().unwrap_or_default(), | ||||||
|         city: location.city.clone().unwrap_or_default(), |         city: location.city.clone().unwrap_or_default(), | ||||||
|         ip: location.node_ip.clone().unwrap_or_default(), |         ip: location.node_ip.clone().unwrap_or_default(), | ||||||
|         node_pubkey: String::new(), |         node_pubkey: String::new(), | ||||||
|  |         free_ports: (resource.port.len() + 1) as u32, | ||||||
|     }; |     }; | ||||||
|     get_one_app_node(app_node_filter).await |     get_one_app_node(app_node_filter).await | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ pub async fn hratls_url_and_mr_enclave_from_app_id( | |||||||
|     let dtpm_port = app_contract |     let dtpm_port = app_contract | ||||||
|         .mapped_ports |         .mapped_ports | ||||||
|         .iter() |         .iter() | ||||||
|         .find(|port| port.app_port == HRATLS_APP_PORT) |         .find(|port| port.guest_port == HRATLS_APP_PORT) | ||||||
|         .ok_or(crate::sgx::grpc_dtpm::Error::Dtpm("Could not find DTMP port".to_string()))? |         .ok_or(crate::sgx::grpc_dtpm::Error::Dtpm("Could not find DTMP port".to_string()))? | ||||||
|         .host_port; |         .host_port; | ||||||
| 
 | 
 | ||||||
| @ -71,13 +71,13 @@ pub async fn fetch_config(package_name: &str) -> Result<DtpmConfig, Error> { | |||||||
| pub fn calculate_nanolp_for_app( | pub fn calculate_nanolp_for_app( | ||||||
|     vcpus: u32, |     vcpus: u32, | ||||||
|     memory_mb: u32, |     memory_mb: u32, | ||||||
|     disk_size_mb: u32, |     disk_size_gb: u32, | ||||||
|     hours: u64, |     hours: u64, | ||||||
|     node_price: u64, |     node_price: u64, | ||||||
| ) -> u64 { | ) -> u64 { | ||||||
|     // this calculation needs to match the calculation of the network
 |     // this calculation needs to match the calculation of the network
 | ||||||
|     let total_units = |     let total_units = | ||||||
|         (vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_mb as f64 / 10000f64); |         (vcpus as f64 * 5f64) + (memory_mb as f64 / 200f64) + (disk_size_gb as f64 / 10f64); | ||||||
|     let locked_nano = (hours as f64 * 60f64 * total_units * node_price as f64) as u64; |     let locked_nano = (hours as f64 * 60f64 * total_units * node_price as f64) as u64; | ||||||
|     eprintln!( |     eprintln!( | ||||||
|             "Node price: {}/unit/minute. Total Units for hardware requested: {:.4}. Locking {} LP (offering the App for {} hours).", |             "Node price: {}/unit/minute. Total Units for hardware requested: {:.4}. Locking {} LP (offering the App for {} hours).", | ||||||
|  | |||||||
| @ -3,7 +3,9 @@ pub mod proto { | |||||||
|     pub use detee_shared::vm_proto::*; |     pub use detee_shared::vm_proto::*; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | use crate::call_with_follow_redirect; | ||||||
| use crate::config::Config; | use crate::config::Config; | ||||||
|  | use crate::utils::{self, sign_request}; | ||||||
| use lazy_static::lazy_static; | use lazy_static::lazy_static; | ||||||
| use log::{debug, info, warn}; | use log::{debug, info, warn}; | ||||||
| use proto::{ | use proto::{ | ||||||
| @ -12,9 +14,7 @@ use proto::{ | |||||||
| }; | }; | ||||||
| use tokio_stream::StreamExt; | use tokio_stream::StreamExt; | ||||||
| use tonic::metadata::errors::InvalidMetadataValue; | use tonic::metadata::errors::InvalidMetadataValue; | ||||||
| use tonic::metadata::AsciiMetadataValue; |  | ||||||
| use tonic::transport::Channel; | use tonic::transport::Channel; | ||||||
| use tonic::Request; |  | ||||||
| 
 | 
 | ||||||
| lazy_static! { | lazy_static! { | ||||||
|     static ref SECURE_PUBLIC_KEY: String = use_default_string(); |     static ref SECURE_PUBLIC_KEY: String = use_default_string(); | ||||||
| @ -41,6 +41,12 @@ pub enum Error { | |||||||
|     CorruptedRootCa(#[from] std::io::Error), |     CorruptedRootCa(#[from] std::io::Error), | ||||||
|     #[error("Internal app error: could not parse Brain URL")] |     #[error("Internal app error: could not parse Brain URL")] | ||||||
|     CorruptedBrainUrl, |     CorruptedBrainUrl, | ||||||
|  |     #[error("Max redirects exceeded: {0}")] | ||||||
|  |     MaxRedirectsExceeded(String), | ||||||
|  |     #[error("Redirect error: {0}")] | ||||||
|  |     RedirectError(String), | ||||||
|  |     #[error(transparent)] | ||||||
|  |     InternalError(#[from] utils::Error), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl crate::HumanOutput for VmContract { | impl crate::HumanOutput for VmContract { | ||||||
| @ -84,21 +90,15 @@ impl crate::HumanOutput for VmNodeListResp { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async fn client() -> Result<BrainVmCliClient<Channel>, Error> { | async fn client() -> Result<BrainVmCliClient<Channel>, Error> { | ||||||
|     Ok(BrainVmCliClient::new(Config::get_brain_channel().await?)) |     let default_brain_url = Config::get_brain_info().0; | ||||||
|  |     info!("brain_url: {default_brain_url}"); | ||||||
|  |     Ok(BrainVmCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn sign_request<T: std::fmt::Debug>(req: T) -> Result<Request<T>, Error> { | async fn client_from_endpoint( | ||||||
|     let pubkey = Config::get_detee_wallet()?; |     reconnect_endpoint: String, | ||||||
|     let timestamp = chrono::Utc::now().to_rfc3339(); | ) -> Result<BrainVmCliClient<Channel>, Error> { | ||||||
|     let signature = Config::try_sign_message(&format!("{timestamp}{req:?}"))?; |     Ok(BrainVmCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?)) | ||||||
|     let timestamp: AsciiMetadataValue = timestamp.parse()?; |  | ||||||
|     let pubkey: AsciiMetadataValue = pubkey.parse()?; |  | ||||||
|     let signature: AsciiMetadataValue = signature.parse()?; |  | ||||||
|     let mut req = Request::new(req); |  | ||||||
|     req.metadata_mut().insert("timestamp", timestamp); |  | ||||||
|     req.metadata_mut().insert("pubkey", pubkey); |  | ||||||
|     req.metadata_mut().insert("request-signature", signature); |  | ||||||
|     Ok(req) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn get_node_list(req: VmNodeFilters) -> Result<Vec<VmNodeListResp>, Error> { | pub async fn get_node_list(req: VmNodeFilters) -> Result<Vec<VmNodeListResp>, Error> { | ||||||
| @ -128,9 +128,10 @@ pub async fn get_one_node(req: VmNodeFilters) -> Result<VmNodeListResp, Error> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn create_vm(req: NewVmReq) -> Result<NewVmResp, Error> { | pub async fn create_vm(req: NewVmReq) -> Result<NewVmResp, Error> { | ||||||
|     let mut client = client().await?; |  | ||||||
|     debug!("Sending NewVmReq to brain: {req:?}"); |     debug!("Sending NewVmReq to brain: {req:?}"); | ||||||
|     match client.new_vm(sign_request(req)?).await { | 
 | ||||||
|  |     let client = client().await?; | ||||||
|  |     match call_with_follow_redirect!(client, req, new_vm).await { | ||||||
|         Ok(resp) => Ok(resp.into_inner()), |         Ok(resp) => Ok(resp.into_inner()), | ||||||
|         Err(e) => Err(e.into()), |         Err(e) => Err(e.into()), | ||||||
|     } |     } | ||||||
| @ -157,10 +158,9 @@ pub async fn list_contracts(req: ListVmContractsReq) -> Result<Vec<VmContract>, | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn delete_vm(uuid: &str) -> Result<(), Error> { | pub async fn delete_vm(uuid: &str) -> Result<(), Error> { | ||||||
|     let mut client = client().await?; |     let client = client().await?; | ||||||
|     let req = DeleteVmReq { uuid: uuid.to_string(), admin_pubkey: Config::get_detee_wallet()? }; |     let req = DeleteVmReq { uuid: uuid.to_string(), admin_pubkey: Config::get_detee_wallet()? }; | ||||||
|     let result = client.delete_vm(sign_request(req)?).await; |     match call_with_follow_redirect!(client, req, delete_vm).await { | ||||||
|     match result { |  | ||||||
|         Ok(confirmation) => { |         Ok(confirmation) => { | ||||||
|             log::debug!("VM deletion confirmation: {confirmation:?}"); |             log::debug!("VM deletion confirmation: {confirmation:?}"); | ||||||
|         } |         } | ||||||
| @ -195,9 +195,8 @@ pub async fn extend_vm(uuid: String, admin_pubkey: String, locked_nano: u64) -> | |||||||
| 
 | 
 | ||||||
| pub async fn update_vm(req: UpdateVmReq) -> Result<UpdateVmResp, Error> { | pub async fn update_vm(req: UpdateVmReq) -> Result<UpdateVmResp, Error> { | ||||||
|     info!("Updating VM {req:?}"); |     info!("Updating VM {req:?}"); | ||||||
|     let mut client = client().await?; |     let client = client().await?; | ||||||
|     let result = client.update_vm(sign_request(req)?).await; |     match call_with_follow_redirect!(client, req, update_vm).await { | ||||||
|     match result { |  | ||||||
|         Ok(resp) => { |         Ok(resp) => { | ||||||
|             let resp = resp.into_inner(); |             let resp = resp.into_inner(); | ||||||
|             if resp.error.is_empty() { |             if resp.error.is_empty() { | ||||||
|  | |||||||
							
								
								
									
										50
									
								
								src/utils.rs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										50
									
								
								src/utils.rs
									
									
									
									
									
								
							| @ -42,3 +42,53 @@ pub fn shorten_string(my_string: &String) -> String { | |||||||
|         format!("{}", first_part) |         format!("{}", first_part) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[macro_export] | ||||||
|  | macro_rules! call_with_follow_redirect { | ||||||
|  |     ( | ||||||
|  |         $client:expr, | ||||||
|  |         $req_data:expr, | ||||||
|  |         $method:ident | ||||||
|  |     ) => { | ||||||
|  |         async { | ||||||
|  |             let mut client = $client; | ||||||
|  | 
 | ||||||
|  |             for attempt in 0..crate::constants::MAX_REDIRECTS { | ||||||
|  |                 log::debug!( | ||||||
|  |                     "Attempt #{}: Calling method '{}'...", | ||||||
|  |                     attempt + 1, | ||||||
|  |                     stringify!($method) | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 let req_data_clone = $req_data.clone(); | ||||||
|  |                 let signed_req = crate::utils::sign_request(req_data_clone)?; | ||||||
|  | 
 | ||||||
|  |                 match client.$method(signed_req).await { | ||||||
|  |                     Ok(resp) => return Ok(resp), | ||||||
|  | 
 | ||||||
|  |                     Err(status) | ||||||
|  |                         if status.code() == tonic::Code::Unavailable | ||||||
|  |                             && status.message() == "moved" => | ||||||
|  |                     { | ||||||
|  |                         let redirect_url = status | ||||||
|  |                             .metadata() | ||||||
|  |                             .get("location") | ||||||
|  |                             .and_then(|v| v.to_str().ok()) | ||||||
|  |                             .ok_or_else(|| { | ||||||
|  |                                 Error::RedirectError( | ||||||
|  |                                     "Server indicated a move but provided no location".into(), | ||||||
|  |                                 ) | ||||||
|  |                             })?; | ||||||
|  | 
 | ||||||
|  |                         log::info!("Server moved. Redirecting to {}...", redirect_url); | ||||||
|  | 
 | ||||||
|  |                         client = client_from_endpoint(format!("https://{}", redirect_url)).await?; | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     Err(e) => return Err(Error::ResponseStatus(e)), | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Err(Error::MaxRedirectsExceeded(crate::constants::MAX_REDIRECTS.to_string())) | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user