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,
|
||||
#[error("Could not find contract {0}")]
|
||||
VmContractNotFound(String),
|
||||
#[error("This error should never happen.")]
|
||||
ImpossibleError,
|
||||
#[error("You don't have the required permissions for this operation.")]
|
||||
AccessDenied,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@ -26,6 +30,7 @@ pub struct AccountData {
|
||||
pub tmp_locked: u64,
|
||||
// holds reasons why VMs of this account got kicked
|
||||
pub kicked_for: Vec<String>,
|
||||
pub last_kick: chrono::DateTime<Utc>,
|
||||
// holds accounts that banned this account
|
||||
pub banned_by: HashSet<String>,
|
||||
}
|
||||
@ -99,14 +104,15 @@ pub struct VmContract {
|
||||
pub dtrfs_sha: String,
|
||||
pub created_at: chrono::DateTime<Utc>,
|
||||
pub updated_at: chrono::DateTime<Utc>,
|
||||
// price per unit per minute
|
||||
// recommended value is 20000
|
||||
/// price per unit per minute
|
||||
pub price_per_unit: u64,
|
||||
pub locked_nano: u64,
|
||||
pub collected_at: chrono::DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl VmContract {
|
||||
/// total hardware units of this VM
|
||||
fn total_units(&self) -> u64 {
|
||||
// TODO: Optimize this based on price of hardware.
|
||||
// I tried, but this can be done better.
|
||||
@ -117,7 +123,7 @@ impl VmContract {
|
||||
+ (!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 {
|
||||
self.total_units() * self.price_per_unit
|
||||
}
|
||||
@ -182,6 +188,7 @@ impl BrainData {
|
||||
tmp_locked: 0,
|
||||
kicked_for: Vec::new(),
|
||||
banned_by: HashSet::new(),
|
||||
last_kick: chrono::Utc::now(),
|
||||
};
|
||||
return balance;
|
||||
}
|
||||
@ -271,6 +278,63 @@ impl BrainData {
|
||||
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) {
|
||||
let mut nodes = self.vm_nodes.write().unwrap();
|
||||
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> {
|
||||
let contract = match self.find_contract_by_uuid(&delete_vm.uuid) {
|
||||
Some(contract) => {
|
||||
if contract.admin_pubkey != delete_vm.admin_pubkey {
|
||||
return Err(Error::VmContractNotFound(delete_vm.uuid));
|
||||
}
|
||||
contract
|
||||
}
|
||||
None => {
|
||||
return Err(Error::VmContractNotFound(delete_vm.uuid));
|
||||
}
|
||||
};
|
||||
let contract = self.find_contract_by_uuid(&delete_vm.uuid)?;
|
||||
if contract.admin_pubkey != delete_vm.admin_pubkey {
|
||||
return Err(Error::AccessDenied);
|
||||
}
|
||||
info!("Found vm {}. Deleting...", delete_vm.uuid);
|
||||
if let Some(daemon_tx) = self.daemon_tx.get(&contract.node_pubkey) {
|
||||
debug!(
|
||||
@ -582,7 +639,7 @@ impl BrainData {
|
||||
let uuid = req.uuid.clone();
|
||||
info!("Inserting new vm update request in memory: {req:?}");
|
||||
let node_pubkey = match self.find_contract_by_uuid(&req.uuid) {
|
||||
Some(contract) => {
|
||||
Ok(contract) => {
|
||||
if contract.admin_pubkey != req.admin_pubkey {
|
||||
let _ = tx.send(grpc::UpdateVmResp {
|
||||
uuid,
|
||||
@ -593,7 +650,7 @@ impl BrainData {
|
||||
}
|
||||
contract.node_pubkey
|
||||
}
|
||||
None => {
|
||||
Err(_) => {
|
||||
log::warn!(
|
||||
"Received UpdateVMReq for a contract that does not exist: {}",
|
||||
req.uuid
|
||||
@ -753,9 +810,13 @@ impl BrainData {
|
||||
.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();
|
||||
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> {
|
||||
|
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> {
|
||||
let req = check_sig_from_req(req)?;
|
||||
match self.data.find_contract_by_uuid(&req.contract) {
|
||||
Some(contract)
|
||||
Ok(contract)
|
||||
if contract.admin_pubkey == req.admin_pubkey
|
||||
&& contract.node_pubkey == req.node_pubkey =>
|
||||
{
|
||||
@ -238,8 +238,8 @@ impl BrainCli for BrainCliMock {
|
||||
info!("CLI {} requested ListVMVmContractsStream", req.admin_pubkey);
|
||||
let contracts = match req.uuid.is_empty() {
|
||||
false => match self.data.find_contract_by_uuid(&req.uuid) {
|
||||
Some(contract) => vec![contract],
|
||||
None => Vec::new(),
|
||||
Ok(contract) => vec![contract],
|
||||
_ => Vec::new(),
|
||||
},
|
||||
true => self.data.find_vm_contracts_by_admin(&req.admin_pubkey),
|
||||
};
|
||||
@ -291,8 +291,8 @@ impl BrainCli for BrainCliMock {
|
||||
|
||||
async fn register_operator(
|
||||
&self,
|
||||
req: tonic::Request<RegOperatorReq>,
|
||||
) -> std::result::Result<tonic::Response<Empty>, tonic::Status> {
|
||||
req: Request<RegOperatorReq>,
|
||||
) -> Result<Response<Empty>, Status> {
|
||||
let req = check_sig_from_req(req)?;
|
||||
info!("Regitering new 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 =
|
||||
Pin<Box<dyn Stream<Item = Result<ListOperatorsResp, Status>> + Send>>;
|
||||
async fn list_operators(
|
||||
@ -419,6 +431,7 @@ impl_pubkey_getter!(ReportNodeReq, admin_pubkey);
|
||||
impl_pubkey_getter!(ListVmContractsReq, admin_pubkey);
|
||||
impl_pubkey_getter!(RegisterVmNodeReq, node_pubkey);
|
||||
impl_pubkey_getter!(RegOperatorReq, pubkey);
|
||||
impl_pubkey_getter!(KickReq, operator_wallet);
|
||||
|
||||
impl_pubkey_getter!(VmNodeFilters);
|
||||
impl_pubkey_getter!(Empty);
|
||||
|
5
vm.proto
5
vm.proto
@ -234,8 +234,9 @@ message ReportNodeReq {
|
||||
}
|
||||
|
||||
message KickReq {
|
||||
string uuid = 1;
|
||||
string reason = 2;
|
||||
string operator_wallet = 1;
|
||||
string contract_uuid = 2;
|
||||
string reason = 3;
|
||||
}
|
||||
|
||||
service BrainCli {
|
||||
|
Loading…
Reference in New Issue
Block a user