1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
use crate::attestation_program::error::SwitchboardError;
use crate::*;
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
pub struct ServiceRow {
pub service: Pubkey,
pub function: Pubkey,
}
/// The region a given server is deployed for services to find a suitable worker.
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, AnchorSerialize, AnchorDeserialize)]
pub enum ServerRegion {
UnitedStates,
Canada,
UnitedKingdom,
Europe,
MiddleEast,
Asia,
Russia,
SouthAmerica,
LatinAmerica,
// TODO: get list from gcp
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, AnchorSerialize, AnchorDeserialize)]
pub enum ServerZone {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
}
/// A [`ServiceWorkerAccountData`] represents a given server or kubernetes cluster that is ready to spin up new workloads.
#[account(zero_copy(unsafe))]
#[repr(packed)]
pub struct ServiceWorkerAccountData {
pub status: u8,
/// Trusted. The region the server will be run within.
pub region: ServerRegion,
/// Trusted. The zone where the server will be run within.
pub zone: ServerZone,
// Accounts
/// Signer allowed to configure the service worker..
pub authority: Pubkey,
/// The Attestation Queue for this service worker, responsible for verifying any SGX quotes.
pub attestation_queue: Pubkey,
// Metadata Config
/// The unix timestamp when the quote was created.
pub created_at: i64,
/// The unix timestamp when the service was last updated.
pub updated_at: i64,
// Access Control
/// Whether the service worker authority needs to approve new services to use the worker.
pub permissions_required: BoolWithLock,
// Resource Availability
/// The available amount of memory on the machine. Trusted to be set correctly by the service worker authority.
pub available_enclave_size: u64,
/// The maximum amount of RAM available to run Trusted Execution Environments (TEEs).
pub max_enclave_size: u64,
/// The maximum CPU that can be given to a service.
pub max_cpu: u64, // maybe f64?
// Token Config
/// The cost to use this service worker. Should this be cost per epoch?
pub enclave_cost: u64,
/// The SwitchboardWallet account containing the reward escrow for verifying quotes on-chain.
/// We should set this whenever the operator changes so we dont need to pass another account and can verify with has_one.
pub reward_escrow: Pubkey,
// Worker Params
/// The addresses of the services who are being executed by the service worker.
pub services: [ServiceRow; 128],
/// The length of valid services for the service worker.
pub services_len: u32,
/// The maximum number of services allowed to run the service.
pub max_services_len: u32,
/// Reserved.
pub _ebuf: [u8; 1024],
}
impl ServiceWorkerAccountData {
pub fn size() -> usize {
8 + std::mem::size_of::<ServiceWorkerAccountData>()
}
pub fn add_service(
&mut self,
service: &mut Account<FunctionServiceAccountData>,
) -> anchor_lang::Result<()> {
if self.services_len == 128 {
// TODO: better error
return Err(error!(SwitchboardError::IllegalExecuteAttempt));
}
if self.available_enclave_size < service.enclave_size {
// TODO: better error
return Err(error!(SwitchboardError::ServiceWorkerEnclaveFull));
}
self.available_enclave_size = self
.available_enclave_size
.checked_sub(service.enclave_size)
.unwrap();
self.services_len += 1;
self.updated_at = Clock::get()?.unix_timestamp;
service.status = ServiceStatus::Active;
service.updated_at = Clock::get()?.unix_timestamp;
Ok(())
}
pub fn remove_service(
&mut self,
service: &mut Account<FunctionServiceAccountData>,
idx: usize,
) -> anchor_lang::Result<()> {
if self.services_len == 0 {
// TOOD: better error
return Err(error!(SwitchboardError::IllegalExecuteAttempt));
}
// TODO: Test this
// Check if idx is within bounds
if idx >= self.services.len() {
// Handle the error or return early
// TOOD: better error
return Err(error!(SwitchboardError::IllegalExecuteAttempt));
}
// Create a temporary vector containing the elements to shift
let temp = self.services[idx + 1..].to_vec();
// Update the elements in `self.services`
self.services[idx] = ServiceRow::default();
if idx < self.services.len() - 1 {
self.services[idx + 1..].copy_from_slice(&temp);
}
// Set the last element to default
self.services[self.services.len() - 1] = ServiceRow::default();
self.services_len -= 1;
// Update other fields
service.service_worker = Pubkey::default();
self.updated_at = Clock::get()?.unix_timestamp;
Ok(())
}
pub fn find_service_idx(
&self,
service: Pubkey,
function: Pubkey,
idx: Option<u32>,
) -> anchor_lang::Result<usize> {
let expected_row = ServiceRow { service, function };
if let Some(idx) = idx {
let row = self.services[idx as usize];
if row != expected_row {
//TODO: better error
return Err(error!(SwitchboardError::IllegalExecuteAttempt));
}
return Ok(idx as usize);
}
if let Some((i, _)) = self
.services
.iter()
.enumerate()
.find(|(_, row)| **row == expected_row)
{
return Ok(i);
}
Err(error!(SwitchboardError::IllegalExecuteAttempt))
}
}