Compare commits
1 Commits
e9f5ae01fb
...
af3c3103e2
Author | SHA1 | Date | |
---|---|---|---|
af3c3103e2 |
95
src/data.rs
95
src/data.rs
@ -18,6 +18,10 @@ pub enum Error {
|
|||||||
InsufficientFunds,
|
InsufficientFunds,
|
||||||
#[error("Could not find contract {0}")]
|
#[error("Could not find contract {0}")]
|
||||||
VmContractNotFound(String),
|
VmContractNotFound(String),
|
||||||
|
#[error("This error should never happen.")]
|
||||||
|
ImpossibleError,
|
||||||
|
#[error("You don't have the required permissions for this operation.")]
|
||||||
|
AccessDenied,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
@ -26,6 +30,7 @@ pub struct AccountData {
|
|||||||
pub tmp_locked: u64,
|
pub tmp_locked: u64,
|
||||||
// holds reasons why VMs of this account got kicked
|
// holds reasons why VMs of this account got kicked
|
||||||
pub kicked_for: Vec<String>,
|
pub kicked_for: Vec<String>,
|
||||||
|
pub last_kick: chrono::DateTime<Utc>,
|
||||||
// holds accounts that banned this account
|
// holds accounts that banned this account
|
||||||
pub banned_by: HashSet<String>,
|
pub banned_by: HashSet<String>,
|
||||||
}
|
}
|
||||||
@ -99,14 +104,15 @@ pub struct VmContract {
|
|||||||
pub dtrfs_sha: String,
|
pub dtrfs_sha: String,
|
||||||
pub created_at: chrono::DateTime<Utc>,
|
pub created_at: chrono::DateTime<Utc>,
|
||||||
pub updated_at: chrono::DateTime<Utc>,
|
pub updated_at: chrono::DateTime<Utc>,
|
||||||
// price per unit per minute
|
|
||||||
// recommended value is 20000
|
// recommended value is 20000
|
||||||
|
/// price per unit per minute
|
||||||
pub price_per_unit: u64,
|
pub price_per_unit: u64,
|
||||||
pub locked_nano: u64,
|
pub locked_nano: u64,
|
||||||
pub collected_at: chrono::DateTime<Utc>,
|
pub collected_at: chrono::DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmContract {
|
impl VmContract {
|
||||||
|
/// total hardware units of this VM
|
||||||
fn total_units(&self) -> u64 {
|
fn total_units(&self) -> u64 {
|
||||||
// TODO: Optimize this based on price of hardware.
|
// TODO: Optimize this based on price of hardware.
|
||||||
// I tried, but this can be done better.
|
// I tried, but this can be done better.
|
||||||
@ -117,7 +123,7 @@ impl VmContract {
|
|||||||
+ (!self.public_ipv4.is_empty() as u64 * 10)
|
+ (!self.public_ipv4.is_empty() as u64 * 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns price per minute in nanoLP
|
/// Returns price per minute in nanoLP
|
||||||
fn price_per_minute(&self) -> u64 {
|
fn price_per_minute(&self) -> u64 {
|
||||||
self.total_units() * self.price_per_unit
|
self.total_units() * self.price_per_unit
|
||||||
}
|
}
|
||||||
@ -182,6 +188,7 @@ impl BrainData {
|
|||||||
tmp_locked: 0,
|
tmp_locked: 0,
|
||||||
kicked_for: Vec::new(),
|
kicked_for: Vec::new(),
|
||||||
banned_by: HashSet::new(),
|
banned_by: HashSet::new(),
|
||||||
|
last_kick: chrono::Utc::now(),
|
||||||
};
|
};
|
||||||
return balance;
|
return balance;
|
||||||
}
|
}
|
||||||
@ -271,6 +278,63 @@ impl BrainData {
|
|||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: this should also support Apps
|
||||||
|
/// Receives: operator, contract uuid, reason of kick
|
||||||
|
pub async fn kick_contract(
|
||||||
|
&self,
|
||||||
|
operator: &str,
|
||||||
|
uuid: &str,
|
||||||
|
reason: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let contract = self.find_contract_by_uuid(uuid)?;
|
||||||
|
let mut operator_data = self
|
||||||
|
.operators
|
||||||
|
.get_mut(operator)
|
||||||
|
.ok_or(Error::AccessDenied)?;
|
||||||
|
if !operator_data.vm_nodes.contains(&contract.node_pubkey) {
|
||||||
|
return Err(Error::AccessDenied);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut minutes_to_refund = chrono::Utc::now()
|
||||||
|
.signed_duration_since(contract.updated_at)
|
||||||
|
.num_minutes()
|
||||||
|
.abs() as u64;
|
||||||
|
// cap refund at 1 week
|
||||||
|
if minutes_to_refund > 10080 {
|
||||||
|
minutes_to_refund = 10080;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut refund_ammount = minutes_to_refund * contract.price_per_minute();
|
||||||
|
let mut user = self
|
||||||
|
.accounts
|
||||||
|
.get_mut(&contract.admin_pubkey)
|
||||||
|
.ok_or(Error::ImpossibleError)?;
|
||||||
|
|
||||||
|
// check if he got kicked within the last day
|
||||||
|
if !chrono::Utc::now()
|
||||||
|
.signed_duration_since(user.last_kick)
|
||||||
|
.gt(&chrono::Duration::days(1))
|
||||||
|
{
|
||||||
|
refund_ammount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if operator_data.escrow < refund_ammount {
|
||||||
|
refund_ammount = operator_data.escrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.balance += refund_ammount;
|
||||||
|
user.kicked_for.push(reason.to_string());
|
||||||
|
operator_data.escrow -= refund_ammount;
|
||||||
|
|
||||||
|
self.delete_vm(grpc::DeleteVmReq {
|
||||||
|
uuid: contract.uuid,
|
||||||
|
admin_pubkey: contract.admin_pubkey,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn report_node(&self, admin_pubkey: String, node: &str, report: String) {
|
pub fn report_node(&self, admin_pubkey: String, node: &str, report: String) {
|
||||||
let mut nodes = self.vm_nodes.write().unwrap();
|
let mut nodes = self.vm_nodes.write().unwrap();
|
||||||
if let Some(node) = nodes.iter_mut().find(|n| n.public_key == node) {
|
if let Some(node) = nodes.iter_mut().find(|n| n.public_key == node) {
|
||||||
@ -363,17 +427,10 @@ impl BrainData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_vm(&self, delete_vm: grpc::DeleteVmReq) -> Result<(), Error> {
|
pub async fn delete_vm(&self, delete_vm: grpc::DeleteVmReq) -> Result<(), Error> {
|
||||||
let contract = match self.find_contract_by_uuid(&delete_vm.uuid) {
|
let contract = self.find_contract_by_uuid(&delete_vm.uuid)?;
|
||||||
Some(contract) => {
|
if contract.admin_pubkey != delete_vm.admin_pubkey {
|
||||||
if contract.admin_pubkey != delete_vm.admin_pubkey {
|
return Err(Error::AccessDenied);
|
||||||
return Err(Error::VmContractNotFound(delete_vm.uuid));
|
}
|
||||||
}
|
|
||||||
contract
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(Error::VmContractNotFound(delete_vm.uuid));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
info!("Found vm {}. Deleting...", delete_vm.uuid);
|
info!("Found vm {}. Deleting...", delete_vm.uuid);
|
||||||
if let Some(daemon_tx) = self.daemon_tx.get(&contract.node_pubkey) {
|
if let Some(daemon_tx) = self.daemon_tx.get(&contract.node_pubkey) {
|
||||||
debug!(
|
debug!(
|
||||||
@ -582,7 +639,7 @@ impl BrainData {
|
|||||||
let uuid = req.uuid.clone();
|
let uuid = req.uuid.clone();
|
||||||
info!("Inserting new vm update request in memory: {req:?}");
|
info!("Inserting new vm update request in memory: {req:?}");
|
||||||
let node_pubkey = match self.find_contract_by_uuid(&req.uuid) {
|
let node_pubkey = match self.find_contract_by_uuid(&req.uuid) {
|
||||||
Some(contract) => {
|
Ok(contract) => {
|
||||||
if contract.admin_pubkey != req.admin_pubkey {
|
if contract.admin_pubkey != req.admin_pubkey {
|
||||||
let _ = tx.send(grpc::UpdateVmResp {
|
let _ = tx.send(grpc::UpdateVmResp {
|
||||||
uuid,
|
uuid,
|
||||||
@ -593,7 +650,7 @@ impl BrainData {
|
|||||||
}
|
}
|
||||||
contract.node_pubkey
|
contract.node_pubkey
|
||||||
}
|
}
|
||||||
None => {
|
Err(_) => {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"Received UpdateVMReq for a contract that does not exist: {}",
|
"Received UpdateVMReq for a contract that does not exist: {}",
|
||||||
req.uuid
|
req.uuid
|
||||||
@ -753,9 +810,13 @@ impl BrainData {
|
|||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_contract_by_uuid(&self, uuid: &str) -> Option<VmContract> {
|
pub fn find_contract_by_uuid(&self, uuid: &str) -> Result<VmContract, Error> {
|
||||||
let contracts = self.vm_contracts.read().unwrap();
|
let contracts = self.vm_contracts.read().unwrap();
|
||||||
contracts.iter().cloned().find(|c| c.uuid == uuid)
|
contracts
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.find(|c| c.uuid == uuid)
|
||||||
|
.ok_or(Error::VmContractNotFound(uuid.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_all_contracts(&self) -> Vec<VmContract> {
|
pub fn list_all_contracts(&self) -> Vec<VmContract> {
|
||||||
|
23
src/grpc.rs
23
src/grpc.rs
@ -216,7 +216,7 @@ impl BrainCli for BrainCliMock {
|
|||||||
async fn report_node(&self, req: Request<ReportNodeReq>) -> Result<Response<Empty>, Status> {
|
async fn report_node(&self, req: Request<ReportNodeReq>) -> Result<Response<Empty>, Status> {
|
||||||
let req = check_sig_from_req(req)?;
|
let req = check_sig_from_req(req)?;
|
||||||
match self.data.find_contract_by_uuid(&req.contract) {
|
match self.data.find_contract_by_uuid(&req.contract) {
|
||||||
Some(contract)
|
Ok(contract)
|
||||||
if contract.admin_pubkey == req.admin_pubkey
|
if contract.admin_pubkey == req.admin_pubkey
|
||||||
&& contract.node_pubkey == req.node_pubkey =>
|
&& contract.node_pubkey == req.node_pubkey =>
|
||||||
{
|
{
|
||||||
@ -238,8 +238,8 @@ impl BrainCli for BrainCliMock {
|
|||||||
info!("CLI {} requested ListVMVmContractsStream", req.admin_pubkey);
|
info!("CLI {} requested ListVMVmContractsStream", req.admin_pubkey);
|
||||||
let contracts = match req.uuid.is_empty() {
|
let contracts = match req.uuid.is_empty() {
|
||||||
false => match self.data.find_contract_by_uuid(&req.uuid) {
|
false => match self.data.find_contract_by_uuid(&req.uuid) {
|
||||||
Some(contract) => vec![contract],
|
Ok(contract) => vec![contract],
|
||||||
None => Vec::new(),
|
_ => Vec::new(),
|
||||||
},
|
},
|
||||||
true => self.data.find_vm_contracts_by_admin(&req.admin_pubkey),
|
true => self.data.find_vm_contracts_by_admin(&req.admin_pubkey),
|
||||||
};
|
};
|
||||||
@ -291,8 +291,8 @@ impl BrainCli for BrainCliMock {
|
|||||||
|
|
||||||
async fn register_operator(
|
async fn register_operator(
|
||||||
&self,
|
&self,
|
||||||
req: tonic::Request<RegOperatorReq>,
|
req: Request<RegOperatorReq>,
|
||||||
) -> std::result::Result<tonic::Response<Empty>, tonic::Status> {
|
) -> Result<Response<Empty>, Status> {
|
||||||
let req = check_sig_from_req(req)?;
|
let req = check_sig_from_req(req)?;
|
||||||
info!("Regitering new operator: {req:?}");
|
info!("Regitering new operator: {req:?}");
|
||||||
match self.data.register_operator(req) {
|
match self.data.register_operator(req) {
|
||||||
@ -301,6 +301,18 @@ impl BrainCli for BrainCliMock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn kick_contract(&self, req: Request<KickReq>) -> Result<Response<Empty>, Status> {
|
||||||
|
let req = check_sig_from_req(req)?;
|
||||||
|
match self
|
||||||
|
.data
|
||||||
|
.kick_contract(&req.operator_wallet, &req.contract_uuid, &req.reason)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => Ok(Response::new(Empty {})),
|
||||||
|
Err(e) => Err(Status::permission_denied(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ListOperatorsStream =
|
type ListOperatorsStream =
|
||||||
Pin<Box<dyn Stream<Item = Result<ListOperatorsResp, Status>> + Send>>;
|
Pin<Box<dyn Stream<Item = Result<ListOperatorsResp, Status>> + Send>>;
|
||||||
async fn list_operators(
|
async fn list_operators(
|
||||||
@ -419,6 +431,7 @@ impl_pubkey_getter!(ReportNodeReq, admin_pubkey);
|
|||||||
impl_pubkey_getter!(ListVmContractsReq, admin_pubkey);
|
impl_pubkey_getter!(ListVmContractsReq, admin_pubkey);
|
||||||
impl_pubkey_getter!(RegisterVmNodeReq, node_pubkey);
|
impl_pubkey_getter!(RegisterVmNodeReq, node_pubkey);
|
||||||
impl_pubkey_getter!(RegOperatorReq, pubkey);
|
impl_pubkey_getter!(RegOperatorReq, pubkey);
|
||||||
|
impl_pubkey_getter!(KickReq, operator_wallet);
|
||||||
|
|
||||||
impl_pubkey_getter!(VmNodeFilters);
|
impl_pubkey_getter!(VmNodeFilters);
|
||||||
impl_pubkey_getter!(Empty);
|
impl_pubkey_getter!(Empty);
|
||||||
|
5
vm.proto
5
vm.proto
@ -234,8 +234,9 @@ message ReportNodeReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message KickReq {
|
message KickReq {
|
||||||
string uuid = 1;
|
string operator_wallet = 1;
|
||||||
string reason = 2;
|
string contract_uuid = 2;
|
||||||
|
string reason = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
service BrainCli {
|
service BrainCli {
|
||||||
|
Loading…
Reference in New Issue
Block a user