Compare commits
	
		
			No commits in common. "65ee3231b2ee969cef385535ec9b8b907481e38f" and "51d50ff49612c93e3c10125d4eef90f26f2b003e" have entirely different histories.
		
	
	
		
			65ee3231b2
			...
			51d50ff496
		
	
		
							
								
								
									
										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_app#0b195b4589e4ec689af7ddca27dc051716ecee78"
 | 
					source = "git+ssh://git@gitea.detee.cloud/testnet/proto.git?branch=surreal_brain#fb38352e1b47837b14f32d8df5ae7f6b17202aae"
 | 
				
			||||||
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_app" }
 | 
					detee-shared = { git = "ssh://git@gitea.detee.cloud/testnet/proto.git", branch = "surreal_brain" }
 | 
				
			||||||
# 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("2")
 | 
					                        .default_value("1000")
 | 
				
			||||||
                        .value_parser(clap::value_parser!(u32).range(1..100))
 | 
					                        .value_parser(clap::value_parser!(u32).range(300..8000))
 | 
				
			||||||
                        .help("disk size in GB")
 | 
					                        .help("disk size in MB")
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    .arg(
 | 
					                    .arg(
 | 
				
			||||||
                        Arg::new("port")
 | 
					                        Arg::new("port")
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,3 @@
 | 
				
			|||||||
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};
 | 
				
			||||||
@ -310,30 +309,20 @@ 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()),
 | 
				
			||||||
            "staging" => {
 | 
					            _ => ("https://173.234.17.2:39477".to_string(), "testnet-brain".to_string()),
 | 
				
			||||||
                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 connect_brain_channel(
 | 
					    pub async fn get_brain_channel() -> Result<tonic::transport::Channel, Error> {
 | 
				
			||||||
        brain_url: String,
 | 
					        let (brain_url, brain_san) = Self::get_brain_info();
 | 
				
			||||||
    ) -> 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,22 +1 @@
 | 
				
			|||||||
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,8 +35,7 @@ pub enum Error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn client() -> Result<BrainGeneralCliClient<Channel>, Error> {
 | 
					async fn client() -> Result<BrainGeneralCliClient<Channel>, Error> {
 | 
				
			||||||
    let default_brain_url = Config::get_brain_info().0;
 | 
					    Ok(BrainGeneralCliClient::new(Config::get_brain_channel().await?))
 | 
				
			||||||
    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 vcpus = *deploy_match.get_one::<u32>("vcpus").unwrap();
 | 
					        let vcpu = *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_size_gb = *deploy_match.get_one::<u32>("disk").unwrap();
 | 
					        let disk_mb = *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 { vcpus, memory_mb, disk_size_gb, port };
 | 
					        let resource = Resource { vcpu, memory_mb, disk_mb, 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,7 +7,6 @@ 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};
 | 
				
			||||||
@ -26,10 +25,6 @@ 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>;
 | 
				
			||||||
@ -53,7 +48,7 @@ impl crate::HumanOutput for AppContract {
 | 
				
			|||||||
            .mapped_ports
 | 
					            .mapped_ports
 | 
				
			||||||
            .clone()
 | 
					            .clone()
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .map(|p| format!("({},{})", p.host_port, p.guest_port))
 | 
					            .map(|p| format!("({},{})", p.host_port, p.app_port))
 | 
				
			||||||
            .collect::<Vec<_>>()
 | 
					            .collect::<Vec<_>>()
 | 
				
			||||||
            .join(", ");
 | 
					            .join(", ");
 | 
				
			||||||
        println!(
 | 
					        println!(
 | 
				
			||||||
@ -62,8 +57,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 {} GB.",
 | 
					            "The App has {} vCPUS, {}MB of memory and a disk of {} MB.",
 | 
				
			||||||
            app_resource.vcpus, app_resource.memory_mb, app_resource.disk_size_gb
 | 
					            app_resource.vcpu, app_resource.memory_mb, app_resource.disk_mb
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        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);
 | 
				
			||||||
@ -71,12 +66,7 @@ impl crate::HumanOutput for AppContract {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn client() -> Result<BrainAppCliClient<Channel>> {
 | 
					async fn client() -> Result<BrainAppCliClient<Channel>> {
 | 
				
			||||||
    let default_brain_url = Config::get_brain_info().0;
 | 
					    Ok(BrainAppCliClient::new(Config::get_brain_channel().await?))
 | 
				
			||||||
    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> {
 | 
				
			||||||
@ -84,9 +74,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.vcpus,
 | 
					        resource.vcpu,
 | 
				
			||||||
        resource.memory_mb,
 | 
					        resource.memory_mb,
 | 
				
			||||||
        resource.disk_size_gb,
 | 
					        resource.disk_mb,
 | 
				
			||||||
        app_deploy_config.hours,
 | 
					        app_deploy_config.hours,
 | 
				
			||||||
        req.price_per_unit,
 | 
					        req.price_per_unit,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
@ -96,21 +86,14 @@ 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 client = client().await?;
 | 
					    let res = client().await?.deploy_app(sign_request(req)?).await?;
 | 
				
			||||||
    match call_with_follow_redirect!(client, req, new_app).await {
 | 
					    Ok(res.into_inner())
 | 
				
			||||||
        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 = client().await?;
 | 
					    let _ = client().await?.delete_app(sign_request(delete_req)?).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: {} mr_enclave: {:?}", &hratls_uri, &package_mr_enclave);
 | 
					    log::info!("hratls uri: {}\nmr_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 vcpus: u32,
 | 
					    pub vcpu: u32,
 | 
				
			||||||
    #[tabled(rename = "Mem (MB)")]
 | 
					    #[tabled(rename = "Mem (MB)")]
 | 
				
			||||||
    pub memory_mb: u32,
 | 
					    pub memory_mb: u32,
 | 
				
			||||||
    #[tabled(rename = "Disk (GB)")]
 | 
					    #[tabled(rename = "Disk (MB)")]
 | 
				
			||||||
    pub disk_size_gb: u32,
 | 
					    pub disk_mb: 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 { vcpus, memory_mb, disk_size_gb, .. } =
 | 
					        let AppResource { vcpu, memory_mb, disk_mb, .. } =
 | 
				
			||||||
            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.guest_port))
 | 
					            .map(|port| (port.host_port, port.app_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,
 | 
				
			||||||
            vcpus,
 | 
					            vcpu,
 | 
				
			||||||
            memory_mb,
 | 
					            memory_mb,
 | 
				
			||||||
            disk_size_gb,
 | 
					            disk_mb,
 | 
				
			||||||
            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,6 +181,7 @@ 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,
 | 
				
			||||||
@ -197,13 +198,14 @@ 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.guest_port == HRATLS_APP_PORT)
 | 
					                .find(|port| port.app_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,
 | 
				
			||||||
@ -228,15 +230,14 @@ 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.vcpus,
 | 
					        vcpus: resource.vcpu,
 | 
				
			||||||
        memory_mb: resource.memory_mb,
 | 
					        memory_mb: resource.memory_mb,
 | 
				
			||||||
        storage_gb: resource.disk_size_gb,
 | 
					        storage_mb: resource.disk_mb,
 | 
				
			||||||
        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.guest_port == HRATLS_APP_PORT)
 | 
					        .find(|port| port.app_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_gb: u32,
 | 
					    disk_size_mb: 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_gb as f64 / 10f64);
 | 
					        (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;
 | 
					    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,9 +3,7 @@ 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::{
 | 
				
			||||||
@ -14,7 +12,9 @@ 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,12 +41,6 @@ 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 {
 | 
				
			||||||
@ -90,15 +84,21 @@ impl crate::HumanOutput for VmNodeListResp {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn client() -> Result<BrainVmCliClient<Channel>, Error> {
 | 
					async fn client() -> Result<BrainVmCliClient<Channel>, Error> {
 | 
				
			||||||
    let default_brain_url = Config::get_brain_info().0;
 | 
					    Ok(BrainVmCliClient::new(Config::get_brain_channel().await?))
 | 
				
			||||||
    info!("brain_url: {default_brain_url}");
 | 
					 | 
				
			||||||
    Ok(BrainVmCliClient::new(Config::connect_brain_channel(default_brain_url).await?))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn client_from_endpoint(
 | 
					fn sign_request<T: std::fmt::Debug>(req: T) -> Result<Request<T>, Error> {
 | 
				
			||||||
    reconnect_endpoint: String,
 | 
					    let pubkey = Config::get_detee_wallet()?;
 | 
				
			||||||
) -> Result<BrainVmCliClient<Channel>, Error> {
 | 
					    let timestamp = chrono::Utc::now().to_rfc3339();
 | 
				
			||||||
    Ok(BrainVmCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?))
 | 
					    let signature = Config::try_sign_message(&format!("{timestamp}{req:?}"))?;
 | 
				
			||||||
 | 
					    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,10 +128,9 @@ 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()),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -158,9 +157,10 @@ 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 client = client().await?;
 | 
					    let mut 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()? };
 | 
				
			||||||
    match call_with_follow_redirect!(client, req, delete_vm).await {
 | 
					    let result = client.delete_vm(sign_request(req)?).await;
 | 
				
			||||||
 | 
					    match result {
 | 
				
			||||||
        Ok(confirmation) => {
 | 
					        Ok(confirmation) => {
 | 
				
			||||||
            log::debug!("VM deletion confirmation: {confirmation:?}");
 | 
					            log::debug!("VM deletion confirmation: {confirmation:?}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -195,8 +195,9 @@ 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 client = client().await?;
 | 
					    let mut client = client().await?;
 | 
				
			||||||
    match call_with_follow_redirect!(client, req, update_vm).await {
 | 
					    let result = client.update_vm(sign_request(req)?).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,53 +42,3 @@ 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