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
195
196
197
198
use crate::anchor_traits::*;
use crate::cfg_client;
#[allow(unused_imports)]
use crate::impl_account_deserialize;
use crate::OnDemandError;
#[allow(unused_imports)]
use crate::OracleAccountData;
use crate::get_sb_program_id;
use bytemuck::{Pod, Zeroable};
use solana_program::account_info::AccountInfo;
use solana_program::pubkey::Pubkey;
use std::cell::Ref;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct QueueAccountData {
    /// The address of the authority which is permitted to add/remove allowed enclave measurements.
    pub authority: Pubkey,
    /// Allowed enclave measurements.
    pub mr_enclaves: [[u8; 32]; 32],
    /// The addresses of the quote oracles who have a valid
    /// verification status and have heartbeated on-chain recently.
    pub oracle_keys: [Pubkey; 128],
    /// The maximum allowable time until a EnclaveAccount needs to be re-verified on-chain.
    pub max_quote_verification_age: i64,
    /// The unix timestamp when the last quote oracle heartbeated on-chain.
    pub last_heartbeat: i64,
    pub node_timeout: i64,
    /// The minimum number of lamports a quote oracle needs to lock-up in order to heartbeat and verify other quotes.
    pub oracle_min_stake: u64,
    pub allow_authority_override_after: i64,

    /// The number of allowed enclave measurements.
    pub mr_enclaves_len: u32,
    /// The length of valid quote oracles for the given attestation queue.
    pub oracle_keys_len: u32,
    /// The reward paid to quote oracles for attesting on-chain.
    pub reward: u32, //TODO
    /// Incrementer used to track the current quote oracle permitted to run any available functions.
    pub curr_idx: u32,
    /// Incrementer used to garbage collect and remove stale quote oracles.
    pub gc_idx: u32,

    pub require_authority_heartbeat_permission: u8,
    pub require_authority_verify_permission: u8,
    pub require_usage_permissions: u8,
    padding1: [u8; 1],

    pub mint: Pubkey,

    /// Reserved.
    pub _ebuf: [u8; 1024],
}
unsafe impl Pod for QueueAccountData {}
unsafe impl Zeroable for QueueAccountData {}

cfg_client! {
    impl_account_deserialize!(QueueAccountData);
}

impl Discriminator for QueueAccountData {
    const DISCRIMINATOR: [u8; 8] = [217, 194, 55, 127, 184, 83, 138, 1];
}

impl Owner for QueueAccountData {
    fn owner() -> Pubkey {
        let cluster = std::env::var("CLUSTER").unwrap_or("mainnet".to_string());
        get_sb_program_id(&cluster)
    }
}

impl QueueAccountData {
    pub fn size() -> usize {
        8 + std::mem::size_of::<QueueAccountData>()
    }

    /// Returns the deserialized Switchboard AttestationQueue account
    ///
    /// # Arguments
    ///
    /// * `attestation_queue_account_info` - A Solana AccountInfo referencing an existing Switchboard AttestationQueue
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use switchboard_solana::QueueAccountData;
    ///
    /// let attestation_queue = QueueAccountData::new(attestation_queue_account_info)?;
    /// ```
    pub fn new<'info>(
        attestation_queue_account_info: &'info AccountInfo<'info>,
    ) -> Result<Ref<'info, QueueAccountData>, OnDemandError> {
        let data = attestation_queue_account_info
            .try_borrow_data()
            .map_err(|_| OnDemandError::AccountBorrowError)?;
        if data.len() < QueueAccountData::discriminator().len() {
            return Err(OnDemandError::InvalidDiscriminator);
        }

        let mut disc_bytes = [0u8; 8];
        disc_bytes.copy_from_slice(&data[..8]);
        if disc_bytes != QueueAccountData::discriminator() {
            return Err(OnDemandError::InvalidDiscriminator);
        }

        Ok(Ref::map(data, |data| {
            bytemuck::from_bytes(&data[8..std::mem::size_of::<QueueAccountData>() + 8])
        }))
    }

    /// Returns the deserialized Switchboard AttestationQueue account
    ///
    /// # Arguments
    ///
    /// * `data` - A Solana AccountInfo's data buffer
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use switchboard_solana::QueueAccountData;
    ///
    /// let attestation_queue = QueueAccountData::new(attestation_queue_account_info.try_borrow_data()?)?;
    /// ```
    pub fn new_from_bytes(data: &[u8]) -> Result<&QueueAccountData, OnDemandError> {
        if data.len() < QueueAccountData::discriminator().len() {
            return Err(OnDemandError::InvalidDiscriminator);
        }

        let mut disc_bytes = [0u8; 8];
        disc_bytes.copy_from_slice(&data[..8]);
        if disc_bytes != QueueAccountData::discriminator() {
            return Err(OnDemandError::InvalidDiscriminator);
        }

        Ok(bytemuck::from_bytes(
            &data[8..std::mem::size_of::<QueueAccountData>() + 8],
        ))
    }

    pub fn has_mr_enclave(&self, mr_enclave: &[u8]) -> bool {
        self.mr_enclaves[..self.mr_enclaves_len as usize]
            .iter()
            .any(|x| x.to_vec() == mr_enclave.to_vec())
    }

    pub fn permitted_enclaves(&self) -> Vec<[u8; 32]> {
        self.mr_enclaves[..self.mr_enclaves_len as usize].to_vec()
    }

    pub fn garbage_collection_node(&self) -> Option<Pubkey> {
        let gc_node = self.oracle_keys[self.gc_idx as usize];
        if gc_node != Pubkey::default() {
            Some(gc_node)
        } else {
            None
        }
    }

    pub fn idx_of_oracle(&self, oracle: &Pubkey) -> Option<usize> {
        self.oracle_keys[..self.oracle_keys_len as usize]
            .iter()
            .position(|x| x == oracle)
    }

    pub fn oracle_keys(&self) -> Vec<Pubkey> {
        self.oracle_keys[..self.oracle_keys_len as usize].to_vec()
    }

    cfg_client! {
        pub async fn fetch_async(
            client: &solana_client::nonblocking::rpc_client::RpcClient,
            pubkey: Pubkey,
        ) -> std::result::Result<Self, crate::OnDemandError> {
            crate::client::fetch_zerocopy_account_async(client, pubkey).await
        }

        pub async fn fetch_oracles(
            &self,
            client: &solana_client::nonblocking::rpc_client::RpcClient,
        ) -> std::result::Result<Vec<(Pubkey, OracleAccountData)>, crate::OnDemandError> {
            let oracles = &self.oracle_keys[..self.oracle_keys_len as usize];
            let datas: Vec<_> = client
                .get_multiple_accounts(&oracles)
                .await
                .map_err(|_e| crate::OnDemandError::NetworkError)?
                .into_iter()
                .filter_map(|x| x)
                .map(|x| x.data.clone())
                .collect::<Vec<_>>()
                .iter()
                .map(|x| OracleAccountData::new_from_bytes(x))
                .filter_map(|x| x.ok())
                .map(|x| x.clone())
                .collect();
            Ok(oracles.iter().cloned().zip(datas).collect())
        }
    }
}