248 lines
8.7 KiB
Rust
248 lines
8.7 KiB
Rust
pub mod proto {
|
|
pub use detee_shared::general_proto::*;
|
|
pub use detee_shared::vm_proto::*;
|
|
}
|
|
|
|
use crate::config::Config;
|
|
use detee_shared::general_proto::brain_general_cli_client::BrainGeneralCliClient;
|
|
use lazy_static::lazy_static;
|
|
use log::{debug, info, warn};
|
|
use proto::{
|
|
brain_vm_cli_client::BrainVmCliClient, DeleteVmReq, ExtendVmReq, ListVmContractsReq, NewVmReq,
|
|
NewVmResp, ReportNodeReq, UpdateVmReq, UpdateVmResp, VmContract, VmNodeFilters, VmNodeListResp,
|
|
};
|
|
use tokio_stream::StreamExt;
|
|
use tonic::metadata::errors::InvalidMetadataValue;
|
|
use tonic::metadata::AsciiMetadataValue;
|
|
use tonic::Request;
|
|
|
|
lazy_static! {
|
|
static ref SECURE_PUBLIC_KEY: String = use_default_string();
|
|
}
|
|
|
|
fn use_default_string() -> String {
|
|
"ThisIsMyEternalClient".to_string()
|
|
}
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum Error {
|
|
#[error("Failed to connect to the brain: {0}")]
|
|
BrainConnection(#[from] tonic::transport::Error),
|
|
#[error("Received error from brain: status: {}, message: {}",
|
|
_0.code().to_string(), _0.message())]
|
|
ResponseStatus(#[from] tonic::Status),
|
|
#[error(transparent)]
|
|
ConfigError(#[from] crate::config::Error),
|
|
#[error("Could not find contract {0}")]
|
|
VmContractNotFound(String),
|
|
#[error(transparent)]
|
|
InternalError(#[from] InvalidMetadataValue),
|
|
}
|
|
|
|
impl crate::HumanOutput for VmContract {
|
|
fn human_cli_print(&self) {
|
|
println!(
|
|
"The VM {} has the UUID {}, and it runs on the node {}",
|
|
self.hostname, self.uuid, self.node_pubkey
|
|
);
|
|
if self.public_ipv4.is_empty() {
|
|
println!(
|
|
"The VM has no public IPv4. The ports published by the VM are: {:?}",
|
|
self.exposed_ports
|
|
);
|
|
} else {
|
|
println!("The Public IPv4 address of the VM is: {}", self.public_ipv4);
|
|
}
|
|
if self.public_ipv6.is_empty() {
|
|
println!("The VM does not have a public IPv6 address.");
|
|
} else {
|
|
println!("The Public IPv6 address of the VM is: {}", self.public_ipv6);
|
|
}
|
|
println!(
|
|
"The VM has {} vCPUS, {}MB of memory and a disk of {} GB.",
|
|
self.vcpus, self.memory_mb, self.disk_size_gb
|
|
);
|
|
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);
|
|
}
|
|
}
|
|
|
|
impl crate::HumanOutput for VmNodeListResp {
|
|
fn human_cli_print(&self) {
|
|
println!("The pubkey of this node is {} and the IP is {}", self.node_pubkey, self.ip);
|
|
println!("It belongs to the operator {}", self.operator);
|
|
println!(
|
|
"This node is located in the city {}, within the region of {}, in {}",
|
|
self.city, self.region, self.country
|
|
);
|
|
println!("The price multiplier for the node is {}.", self.price);
|
|
}
|
|
}
|
|
|
|
fn sign_request<T: std::fmt::Debug>(req: T) -> Result<Request<T>, Error> {
|
|
let pubkey = Config::get_detee_wallet()?;
|
|
let timestamp = chrono::Utc::now().to_rfc3339();
|
|
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> {
|
|
debug!("Getting nodes from brain...");
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
let mut nodes = Vec::new();
|
|
let mut grpc_stream = client.list_vm_nodes(sign_request(req)?).await?.into_inner();
|
|
while let Some(stream_update) = grpc_stream.next().await {
|
|
match stream_update {
|
|
Ok(node) => {
|
|
debug!("Received node from brain: {node:?}");
|
|
nodes.push(node);
|
|
}
|
|
Err(e) => {
|
|
warn!("Received error instead of node list: {e:?}");
|
|
}
|
|
}
|
|
}
|
|
debug!("Brain terminated list_nodes stream.");
|
|
Ok(nodes)
|
|
}
|
|
|
|
pub async fn get_one_node(req: VmNodeFilters) -> Result<VmNodeListResp, Error> {
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
let response = client.get_one_vm_node(sign_request(req)?).await?;
|
|
Ok(response.into_inner())
|
|
}
|
|
|
|
pub async fn create_vm(req: NewVmReq) -> Result<NewVmResp, Error> {
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
debug!("Sending NewVmReq to brain: {req:?}");
|
|
match client.new_vm(sign_request(req)?).await {
|
|
Ok(resp) => Ok(resp.into_inner()),
|
|
Err(e) => Err(e.into()),
|
|
}
|
|
}
|
|
|
|
pub async fn report_node(
|
|
node_pubkey: String,
|
|
contract: String,
|
|
reason: String,
|
|
) -> Result<(), Error> {
|
|
debug!("Getting contracts from brain...");
|
|
let mut client = BrainGeneralCliClient::connect(Config::get_brain_url()).await?;
|
|
client
|
|
.report_node(sign_request(ReportNodeReq {
|
|
admin_pubkey: Config::get_detee_wallet()?,
|
|
node_pubkey,
|
|
contract,
|
|
reason,
|
|
})?)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn list_contracts(req: ListVmContractsReq) -> Result<Vec<VmContract>, Error> {
|
|
debug!("Getting contracts from brain...");
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
let mut contracts = Vec::new();
|
|
let mut grpc_stream = client.list_vm_contracts(sign_request(req)?).await?.into_inner();
|
|
while let Some(stream_update) = grpc_stream.next().await {
|
|
match stream_update {
|
|
Ok(c) => {
|
|
info!("Received contract from brain: {c:?}");
|
|
contracts.push(c);
|
|
}
|
|
Err(e) => {
|
|
warn!("Received error instead of contracts: {e:?}");
|
|
}
|
|
}
|
|
}
|
|
debug!("Brain terminated list_contracts stream.");
|
|
Ok(contracts)
|
|
}
|
|
|
|
pub async fn delete_vm(uuid: &str) -> Result<(), Error> {
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
let req = DeleteVmReq { uuid: uuid.to_string(), admin_pubkey: Config::get_detee_wallet()? };
|
|
let result = client.delete_vm(sign_request(req)?).await;
|
|
match result {
|
|
Ok(confirmation) => {
|
|
log::debug!("VM deletion confirmation: {confirmation:?}");
|
|
}
|
|
Err(e) => {
|
|
log::error!("Could not delete vm: {e:?}");
|
|
return Err(e.into());
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn extend_vm(uuid: String, admin_pubkey: String, locked_nano: u64) -> Result<(), Error> {
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
let req = ExtendVmReq { admin_pubkey, uuid, locked_nano };
|
|
let result = client.extend_vm(sign_request(req)?).await;
|
|
match result {
|
|
Ok(confirmation) => {
|
|
log::debug!("VM contract extension confirmation: {confirmation:?}");
|
|
log::info!(
|
|
"VM contract got updated. It now has {} LP locked for the VM.",
|
|
locked_nano as f64 / 1_000_000_000.0
|
|
);
|
|
}
|
|
Err(e) => {
|
|
log::debug!("Got error from brain: {:?}", e);
|
|
log::error!("Could not extend VM contract: {}", e.message());
|
|
return Err(e.into());
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn update_vm(req: UpdateVmReq) -> Result<UpdateVmResp, Error> {
|
|
info!("Updating VM {req:?}");
|
|
let mut client = BrainVmCliClient::connect(Config::get_brain_url()).await?;
|
|
let result = client.update_vm(sign_request(req)?).await;
|
|
match result {
|
|
Ok(resp) => {
|
|
let resp = resp.into_inner();
|
|
if resp.error.is_empty() {
|
|
info!("Got VM update response: {resp:?}");
|
|
Ok(resp)
|
|
} else {
|
|
debug!("Got VM update error: {:?}", resp);
|
|
Ok(resp)
|
|
}
|
|
}
|
|
Err(e) => {
|
|
log::error!("Could not update vm: {e:?}");
|
|
Err(e.into())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn get_contract_by_uuid(uuid: &str) -> Result<VmContract, Error> {
|
|
let req = ListVmContractsReq {
|
|
wallet: Config::get_detee_wallet()?,
|
|
uuid: uuid.to_string(),
|
|
..Default::default()
|
|
};
|
|
let contracts = list_contracts(req).await?;
|
|
if contracts.is_empty() {
|
|
log::error!("Could not find any contract by ID {uuid}");
|
|
return Err(Error::VmContractNotFound(uuid.to_string()));
|
|
}
|
|
Ok(contracts[0].clone())
|
|
}
|
|
|
|
// pub fn block_on<F>(future: F) -> F::Output
|
|
// where
|
|
// F: std::future::Future,
|
|
// {
|
|
// tokio::runtime::Runtime::new().unwrap().block_on(future)
|
|
// }
|