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))
    }
}