switchboard_solana/attestation_program/accounts/
function.rs

1use crate::prelude::*;
2use crate::*;
3use bytemuck::{Pod, Zeroable};
4use std::cell::Ref;
5
6#[repr(u8)]
7#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, AnchorSerialize, AnchorDeserialize)]
8pub enum FunctionStatus {
9    #[default]
10    None = 0,
11    Active = 1 << 0,
12    NonExecutable = 1 << 1,
13    Expired = 1 << 2,
14    OutOfFunds = 1 << 3,
15    InvalidPermissions = 1 << 4,
16}
17impl From<FunctionStatus> for u8 {
18    fn from(value: FunctionStatus) -> Self {
19        match value {
20            FunctionStatus::Active => 1 << 0,
21            FunctionStatus::NonExecutable => 1 << 1,
22            FunctionStatus::Expired => 1 << 2,
23            FunctionStatus::OutOfFunds => 1 << 3,
24            FunctionStatus::InvalidPermissions => 1 << 4,
25            _ => 0,
26        }
27    }
28}
29impl From<u8> for FunctionStatus {
30    fn from(value: u8) -> Self {
31        match value {
32            1 => FunctionStatus::Active,
33            2 => FunctionStatus::NonExecutable,
34            4 => FunctionStatus::Expired,
35            8 => FunctionStatus::OutOfFunds,
36            16 => FunctionStatus::InvalidPermissions,
37            _ => FunctionStatus::default(),
38        }
39    }
40}
41#[zero_copy(unsafe)]
42#[repr(packed)]
43#[derive(PartialEq, Debug)]
44pub struct FunctionAccountData {
45    // Easy Filtering Config
46    /// Whether the function is invoked on a schedule or by request
47    #[deprecated(
48        since = "0.28.35",
49        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
50    )]
51    pub is_scheduled: u8,
52    /// Whether the function has been manually triggered with the function_trigger instruction
53    #[deprecated(
54        since = "0.28.35",
55        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
56    )]
57    pub is_triggered: u8,
58
59    /// The function permissions granted by the attestation_queue.authority
60    pub permissions: u32,
61    pub status: FunctionStatus,
62
63    // 15
64
65    // Metadata
66    /// PDA bump.
67    pub bump: u8,
68    /// The payer who originally created the function. Cannot change, used to derive PDA.
69    pub creator_seed: [u8; 32],
70    /// The name of the function for easier identification.
71    pub name: [u8; 64],
72    /// The metadata of the function for easier identification.
73    pub metadata: [u8; 256],
74    /// The Solana slot when the function was created. (PDA)
75    pub created_at_slot: u64,
76    /// The unix timestamp when the function was created.
77    pub created_at: i64,
78    /// The unix timestamp when the function config (container, registry, version, or schedule) was changed.
79    pub updated_at: i64,
80
81    // 392
82
83    // Attestation Config
84    /// The enclave quote
85    #[deprecated(
86        since = "0.28.35",
87        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
88    )]
89    pub enclave: Quote,
90    /// An array of permitted mr_enclave measurements for the function.
91    pub mr_enclaves: [[u8; 32]; 32],
92
93    // 1849
94
95    // Container Settings
96    /// The off-chain registry to fetch the function container from.
97    pub container_registry: [u8; 64],
98    /// The identifier of the container in the given container_registry.
99    pub container: [u8; 64],
100    /// The version tag of the container to pull.
101    pub version: [u8; 32],
102    /// The expected schema for the container params.
103    #[deprecated(
104        since = "0.28.35",
105        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
106    )]
107    pub params_schema: [u8; 256],
108    /// The default params passed to the container during scheduled execution.
109    #[deprecated(
110        since = "0.28.35",
111        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
112    )]
113    pub default_container_params: [u8; 256],
114
115    // 2521
116
117    // Accounts Config
118    /// The authority of the function which is authorized to make account changes.
119    pub authority: Pubkey,
120    /// The address of the AttestationQueueAccountData that will be processing function requests and verifying the function measurements.
121    pub attestation_queue: Pubkey,
122    /// An incrementer used to rotate through an AttestationQueue's verifiers.
123    pub queue_idx: u32,
124    /// The address_lookup_table of the function used to increase the number of accounts we can fit into a function result.
125    pub address_lookup_table: Pubkey,
126
127    // 2621
128
129    // Schedule Config
130    /// The cron schedule to run the function on.
131    #[deprecated(
132        since = "0.28.35",
133        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
134    )]
135    pub schedule: [u8; 64],
136    /// The unix timestamp when the function was last run.
137    #[deprecated(
138        since = "0.28.35",
139        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
140    )]
141    pub last_execution_timestamp: i64,
142    /// The unix timestamp when the function is allowed to run next.
143    #[deprecated(
144        since = "0.28.35",
145        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
146    )]
147    pub next_allowed_timestamp: i64,
148    /// The number of times to trigger the function upon the next invocation.
149    #[deprecated(
150        since = "0.28.35",
151        note = "please use a `FunctionRequestAccountData` for all on-demand executions"
152    )]
153    pub trigger_count: u64,
154    /// Time this function has been sitting in an explicitly triggered state
155    #[deprecated(
156        since = "0.28.35",
157        note = "please use a `FunctionRequestAccountData` for all on-demand executions"
158    )]
159    pub triggered_since: i64,
160
161    // Permission Settings
162    /// UNUSED. The unix timestamp when the current permissions expire.
163    pub permission_expiration: i64,
164
165    // Requests Config
166    /// Number of requests created for this function. Used to prevent closing when there are live requests.
167    pub num_requests: u64,
168    /// Whether custom requests have been disabled for this function.
169    pub requests_disabled: u8,
170    /// Whether new requests need to be authorized by the FunctionAccount authority before being initialized.
171    /// Useful if you want to use CPIs to control request account creation.
172    pub requests_require_authorization: u8,
173    /// DEPRECATED.
174    pub reserved1: [u8; 8],
175    /// The dev fee that is paid out from the request's escrow to the function's escrow on each successful invocation.
176    /// This is used to reward the function maintainer for providing the function.
177    /// 0 = No Fee. Sender = requests's escrow_token_wallet. Receiver = function's reward_token_wallet.
178    pub requests_fee: u64,
179
180    // Token Config
181    /// The SwitchboardWallet that will handle pre-funding rewards paid out to function verifiers.
182    pub escrow_wallet: Pubkey,
183    /// The escrow_wallet TokenAccount that handles pre-funding rewards paid out to function runners.
184    pub escrow_token_wallet: Pubkey,
185    /// The SwitchboardWallet that will handle acruing rewards from requests.
186    /// Defaults to the escrow_wallet.
187    pub reward_escrow_wallet: Pubkey,
188    /// The reward_escrow_wallet TokenAccount used to acrue rewards from requests made with custom parameters.
189    pub reward_escrow_token_wallet: Pubkey,
190
191    /// The last reported error code if the most recent response was a failure
192    #[deprecated(
193        since = "0.28.35",
194        note = "please use the error_status field on your function consumer (request or routine)"
195    )]
196    pub error_status: u8,
197
198    // Routines Config
199    /// Number of routines created for this function. Used to prevent closing when there are live routines.
200    pub num_routines: u64,
201    /// Whether custom routines have been disabled for this function.
202    pub routines_disabled: BoolWithLock,
203    /// Whether new routines need to be authorized by the FunctionAccount authority before being initialized.
204    /// Useful if you want to provide AccessControl and only allow certain parties to run routines.
205    pub routines_require_authorization: u8,
206    /// The fee that is paid out from the routine's escrow to the function's escrow on each successful invocation.
207    /// This is used to reward the function maintainer for providing the function.
208    /// 0 = No Fee. Sender = routine's escrow_token_wallet. Receiver = function's reward_token_wallet.
209    pub routines_dev_fee: u64,
210
211    /// The functions MRENCLAVE measurement dictating the contents of the secure enclave.
212    // This represents the last successful execution of a function.
213    pub mr_enclave: [u8; 32],
214    /// The VerificationStatus of the quote.
215    pub verification_status: u8,
216    /// The unix timestamp when the quote was last verified.
217    pub verification_timestamp: i64,
218    /// The unix timestamp when the quotes verification status expires.
219    pub valid_until: i64,
220
221    /// Reserved.
222    pub _ebuf: [u8; 956],
223}
224
225impl Default for FunctionAccountData {
226    fn default() -> Self {
227        unsafe { std::mem::zeroed() }
228    }
229}
230
231impl anchor_lang::AccountDeserialize for FunctionAccountData {
232    fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
233        if buf.len() < FunctionAccountData::discriminator().len() {
234            return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into());
235        }
236        let given_disc = &buf[..8];
237        if FunctionAccountData::discriminator() != given_disc {
238            return Err(
239                anchor_lang::error::Error::from(anchor_lang::error::AnchorError {
240                    error_name: anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.name(),
241                    error_code_number: anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch
242                        .into(),
243                    error_msg: anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch
244                        .to_string(),
245                    error_origin: Some(anchor_lang::error::ErrorOrigin::Source(
246                        anchor_lang::error::Source {
247                            filename: "programs/attestation_program/src/lib.rs",
248                            line: 1u32,
249                        },
250                    )),
251                    compared_values: None,
252                })
253                .with_account_name("FunctionAccountData"),
254            );
255        }
256        Self::try_deserialize_unchecked(buf)
257    }
258
259    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
260        let data: &[u8] = &buf[8..];
261        bytemuck::try_from_bytes(data)
262            .map(|r: &Self| *r)
263            .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into())
264    }
265}
266
267unsafe impl Pod for FunctionAccountData {}
268unsafe impl Zeroable for FunctionAccountData {}
269
270impl Discriminator for FunctionAccountData {
271    const DISCRIMINATOR: [u8; 8] = [76, 139, 47, 44, 240, 182, 148, 200];
272}
273
274impl Owner for FunctionAccountData {
275    fn owner() -> Pubkey {
276        SWITCHBOARD_ATTESTATION_PROGRAM_ID
277    }
278}
279
280impl ZeroCopy for FunctionAccountData {}
281
282impl FunctionAccountData {
283    pub fn size() -> usize {
284        8 + std::mem::size_of::<FunctionAccountData>()
285    }
286
287    /// Returns the deserialized Switchboard Function account
288    ///
289    /// # Arguments
290    ///
291    /// * `function_account_info` - A Solana AccountInfo referencing an existing Switchboard FunctionAccount
292    ///
293    /// # Examples
294    ///
295    /// ```ignore
296    /// use switchboard_solana::FunctionAccountData;
297    ///
298    /// let function_account = FunctionAccountData::new(function_account_info)?;
299    /// ```
300    pub fn new<'info>(
301        function_account_info: &'info AccountInfo<'info>,
302    ) -> anchor_lang::Result<Ref<'info, FunctionAccountData>> {
303        let data = function_account_info.try_borrow_data()?;
304        if data.len() < FunctionAccountData::discriminator().len() {
305            return Err(ErrorCode::AccountDiscriminatorNotFound.into());
306        }
307
308        let mut disc_bytes = [0u8; 8];
309        disc_bytes.copy_from_slice(&data[..8]);
310        if disc_bytes != FunctionAccountData::discriminator() {
311            return Err(ErrorCode::AccountDiscriminatorMismatch.into());
312        }
313
314        Ok(Ref::map(data, |data| {
315            bytemuck::from_bytes(&data[8..std::mem::size_of::<FunctionAccountData>() + 8])
316        }))
317    }
318
319    /// Returns the deserialized Switchboard Function account
320    ///
321    /// # Arguments
322    ///
323    /// * `data` - A Solana AccountInfo's data buffer
324    ///
325    /// # Examples
326    ///
327    /// ```ignore
328    /// use switchboard_solana::FunctionAccountData;
329    ///
330    /// let function_account = FunctionAccountData::new(function_account_info.try_borrow_data()?)?;
331    /// ```
332    pub fn new_from_bytes(data: &[u8]) -> anchor_lang::Result<&FunctionAccountData> {
333        if data.len() < FunctionAccountData::discriminator().len() {
334            return Err(ErrorCode::AccountDiscriminatorNotFound.into());
335        }
336
337        let mut disc_bytes = [0u8; 8];
338        disc_bytes.copy_from_slice(&data[..8]);
339        if disc_bytes != FunctionAccountData::discriminator() {
340            return Err(ErrorCode::AccountDiscriminatorMismatch.into());
341        }
342
343        Ok(bytemuck::from_bytes(
344            &data[8..std::mem::size_of::<FunctionAccountData>() + 8],
345        ))
346    }
347
348    /// Validate that the provided accounts correspond to the expected function accounts
349    ///
350    /// # Arguments
351    ///
352    /// * `signer` - Solana AccountInfo for a signer
353    pub fn validate(&self, signer: &AccountInfo) -> anchor_lang::Result<bool> {
354        // TODO: validate the seeds and bump
355
356        // validate the enclaves enclave is not empty
357        if self.enclave.mr_enclave == [0u8; 32] {
358            return Ok(false);
359        }
360
361        // validate the enclaves delegated signer matches
362        if self.enclave.enclave_signer != signer.key() {
363            return Ok(false);
364        }
365
366        // validate the function was verified and it is not expired
367        Ok(self.enclave.is_verified(&Clock::get()?))
368    }
369
370    /// Validate that the provided accounts correspond to the expected function accounts
371    ///
372    /// # Arguments
373    ///
374    /// * `signer` - Solana AccountInfo for a signer\
375    #[deprecated(
376        since = "0.28.35",
377        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
378    )]
379    pub fn validate_signer<'a>(&self, signer: &AccountInfo<'a>) -> anchor_lang::Result<bool> {
380        // deserialize accounts and verify the owner
381
382        // TODO: validate the seeds and bump
383
384        // validate the enclaves enclave is not empty
385        if self.enclave.mr_enclave == [0u8; 32] {
386            return Ok(false);
387        }
388
389        // validate the enclaves delegated signer matches
390        if self.enclave.enclave_signer != signer.key() {
391            return Ok(false);
392        }
393
394        // validate the function was verified and it is not expired
395        Ok(self.enclave.is_verified(&Clock::get()?))
396    }
397
398    /// Validates that the provided request is assigned to the same `AttestationQueueAccountData` as the function and the
399    /// provided `enclave_signer` matches the `enclave_signer` stored in the request's `active_request` field.
400    ///
401    /// # Arguments
402    ///
403    /// * `request` - The `FunctionRequestAccountData` being validated.
404    /// * `enclave_signer` - The `AccountInfo` of the enclave signer to validate.
405    ///
406    /// # Errors
407    ///
408    /// Returns an error if:
409    /// * the function and request have different attestation queues
410    /// * the request's verified signer does not match the provided `enclave_signer`
411    /// * the `enclave_signer` did not sign the transaction
412    ///
413    /// # Returns
414    ///
415    /// Returns `Ok(true)` if the validation succeeds, `Ok(false)` otherwise.
416    ///
417    /// # Examples
418    ///
419    /// ```ignore
420    /// use switchboard_solana::FunctionRequestAccountData;
421    ///
422    /// #[derive(Accounts)]
423    /// pub struct Settle<'info> {
424    ///     // YOUR PROGRAM ACCOUNTS
425    ///     #[account(
426    ///         mut,
427    ///         has_one = switchboard_request,
428    ///     )]
429    ///     pub user: AccountLoader<'info, UserState>,
430    ///
431    ///     // SWITCHBOARD ACCOUNTS
432    ///     #[account(
433    ///         constraint = function.load()?.validate_request(
434    ///             &request,
435    ///             &enclave_signer.to_account_info()
436    ///         )?
437    ///     )]
438    ///     pub function: AccountLoader<'info, FunctionAccountData>,
439    ///     #[account(
440    ///         has_one = function,
441    ///     )]
442    ///     pub request: Box<Account<'info, FunctionRequestAccountData>>,
443    ///     pub enclave_signer: Signer<'info>,
444    /// }
445    /// ```
446    pub fn validate_request(
447        &self,
448        request: &FunctionRequestAccountData,
449        enclave_signer: &AccountInfo,
450    ) -> anchor_lang::Result<bool> {
451        if request.attestation_queue != self.attestation_queue {
452            msg!(
453                "AttestationQueueMismatch: fn: {}, request: {}",
454                self.attestation_queue,
455                request.attestation_queue
456            );
457            return Ok(false);
458        }
459
460        if request.active_request.enclave_signer != enclave_signer.key() {
461            msg!(
462                "SignerMismatch: expected {}, received {}",
463                request.active_request.enclave_signer,
464                enclave_signer.key()
465            );
466            return Ok(false);
467        }
468
469        // Verify the enclave signer signed the transaction
470        if enclave_signer.signer_key().is_none() {
471            msg!(
472                "enclave_signer ({}) did not sign the transaction",
473                enclave_signer.key()
474            );
475            return Ok(false);
476        }
477
478        Ok(true)
479    }
480
481    /// Validates that the provided routine is assigned to the same `AttestationQueueAccountData` as the function and the
482    /// provided `enclave_signer` matches the `enclave_signer` stored in the routine's `enclave_signer` field.
483    ///
484    /// # Arguments
485    ///
486    /// * `routine` - The `FunctionRoutineAccountData` being validated.
487    /// * `enclave_signer` - The `AccountInfo` of the enclave signer to validate.
488    ///
489    /// # Errors
490    ///
491    /// Returns an error if:
492    /// * the function and routine have different attestation queues
493    /// * the routine's verified signer does not match the provided `enclave_signer`
494    /// * the `enclave_signer` did not sign the transaction
495    ///
496    /// # Returns
497    ///
498    /// Returns `Ok(true)` if the validation succeeds, `Ok(false)` otherwise.
499    ///
500    /// # Examples
501    ///
502    /// ```ignore
503    /// use switchboard_solana::FunctionRoutineAccountData;
504    ///
505    /// #[derive(Accounts)]
506    /// pub struct Settle<'info> {
507    ///     // YOUR PROGRAM ACCOUNTS
508    ///     #[account(
509    ///         mut,
510    ///         has_one = switchboard_routine,
511    ///     )]
512    ///     pub user: AccountLoader<'info, UserState>,
513    ///
514    ///     // SWITCHBOARD ACCOUNTS
515    ///     pub switchboard_function: AccountLoader<'info, FunctionAccountData>,
516    ///     #[account(
517    ///         constraint = switchboard_routine.validate_signer(
518    ///             &switchboard_function.to_account_info(),
519    ///             &enclave_signer.to_account_info()
520    ///         )?
521    ///     )]
522    ///     pub switchboard_routine: Box<Account<'info, FunctionRoutineAccountData>>,
523    ///     pub enclave_signer: Signer<'info>,
524    /// }
525    /// ```
526    pub fn validate_routine(
527        &self,
528        routine: &FunctionRoutineAccountData,
529        enclave_signer: &AccountInfo,
530    ) -> anchor_lang::Result<bool> {
531        if routine.attestation_queue != self.attestation_queue {
532            msg!(
533                "AttestationQueueMismatch: fn: {}, routine: {}",
534                self.attestation_queue,
535                routine.attestation_queue
536            );
537            return Ok(false);
538        }
539
540        // validate the enclaves delegated signer matches
541        if routine.enclave_signer != enclave_signer.key() {
542            msg!(
543                "EnclaveSignerMismatch: expected {}, received {}",
544                routine.enclave_signer,
545                enclave_signer.key()
546            );
547            return Ok(false);
548        }
549
550        // Verify the enclave signer signed the transaction
551        if enclave_signer.signer_key().is_none() {
552            msg!(
553                "enclave_signer ({}) did not sign the transaction",
554                enclave_signer.key()
555            );
556            return Ok(false);
557        }
558
559        Ok(true)
560    }
561
562    #[deprecated(
563        since = "0.28.35",
564        note = "please use a `FunctionRoutineAccountData` for all scheduled executions"
565    )]
566    pub fn is_empty_schedule(&self) -> bool {
567        self.schedule
568            .first()
569            .map(|&byte| byte == 0)
570            .unwrap_or(false)
571    }
572
573    pub fn get_container(&self) -> String {
574        std::str::from_utf8(&self.container)
575            .unwrap_or("")
576            .trim()
577            .to_string()
578    }
579
580    pub fn get_container_registry(&self) -> String {
581        std::str::from_utf8(&self.container_registry)
582            .unwrap_or("")
583            .trim()
584            .to_string()
585    }
586
587    pub fn get_version(&self) -> String {
588        let version = std::str::from_utf8(&self.version)
589            .unwrap_or("latest")
590            .trim()
591            .to_string();
592
593        if version.is_empty() {
594            String::from("latest")
595        } else {
596            version
597        }
598    }
599
600    pub fn get_name(&self) -> String {
601        format!("{}:{}", self.get_container(), self.get_version()).replace('\u{0}', "")
602    }
603
604    pub fn get_function_name(&self) -> String {
605        std::str::from_utf8(&self.name)
606            .unwrap_or("")
607            .trim()
608            .to_string()
609    }
610
611    /// Asserts that the permissions are valid for the given queue's access control level.
612    ///
613    /// # Arguments
614    ///
615    /// * `queue_require_usage_permissions` - A boolean indicating whether queue usage permissions are required.
616    ///
617    /// # Errors
618    ///
619    /// Returns an error if the queue usage permissions are required but not present.
620    pub fn assert_permissions(
621        &self,
622        queue_require_usage_permissions: bool,
623    ) -> anchor_lang::Result<()> {
624        if queue_require_usage_permissions
625            && self.permissions != SwitchboardAttestationPermission::PermitQueueUsage as u32
626        {
627            return Err(error!(SwitchboardError::PermissionDenied));
628        }
629
630        Ok(())
631    }
632
633    /// Asserts that the given `mr_enclave` is valid by checking it against the enclave set.
634    /// If the `mr_enclave` is invalid, returns an `InvalidMrEnclave` error.
635    pub fn assert_mr_enclave(&self, mr_enclave: &[u8; 32]) -> anchor_lang::Result<()> {
636        if !self.is_valid_enclave(mr_enclave) {
637            return Err(error!(SwitchboardError::InvalidMrEnclave));
638        }
639
640        Ok(())
641    }
642
643    /// Checks if the given `mr_enclave` is valid by verifying if it exists in the list of valid
644    /// `mr_enclaves` stored in the current instance of `FunctionImpl`.
645    ///
646    /// # Arguments
647    ///
648    /// * `mr_enclave` - A reference to a 32-byte array representing the `mr_enclave` value of the
649    ///                  enclave to be validated.
650    ///
651    /// # Returns
652    ///
653    /// A boolean value indicating whether the given `mr_enclave` is valid or not.
654    pub fn is_valid_enclave(&self, mr_enclave: &[u8; 32]) -> bool {
655        if *mr_enclave == [0u8; 32] {
656            return false;
657        }
658
659        self.mr_enclaves.contains(mr_enclave)
660    }
661
662    /// Parses the enclave measurements and returns a vector of 32-byte arrays representing the non-empty mr_enclaves.
663    ///
664    /// # Example
665    ///
666    /// ```
667    /// use crate::FunctionAccountData;
668    ///
669    /// let function = FunctionAccountData::default();
670    /// let parsed_enclaves = function.parse_enclaves();
671    /// assert_eq(0, parsed_enclaves.len());
672    /// ```
673    pub fn parse_enclaves(&self) -> Vec<[u8; 32]> {
674        let mut parsed_enclaves: Vec<[u8; 32]> = vec![];
675        for mr_enclave in self.mr_enclaves.iter() {
676            if *mr_enclave != [0u8; 32] {
677                parsed_enclaves.push(*mr_enclave)
678            }
679        }
680        parsed_enclaves
681    }
682
683    /// Asserts that the current instance has enclaves.
684    ///
685    /// # Errors
686    ///
687    /// Returns an error of type `SwitchboardError::MrEnclavesEmpty` if the enclaves are empty.
688    pub fn assert_has_enclaves(&self) -> anchor_lang::Result<()> {
689        if self.parse_enclaves().is_empty() {
690            return Err(error!(SwitchboardError::MrEnclavesEmpty));
691        }
692
693        Ok(())
694    }
695
696    /// Asserts that requests are enabled for the given function.
697    ///
698    /// # Errors
699    ///
700    /// Returns an error of type `SwitchboardError::UserRequestsDisabled` if the function has requests_disabled configured.
701    fn assert_requests_enabled(&self) -> anchor_lang::Result<()> {
702        if self.requests_disabled.to_bool() {
703            return Err(error!(SwitchboardError::UserRequestsDisabled));
704        }
705
706        Ok(())
707    }
708
709    /// Asserts that routines are enabled for the given function.
710    ///
711    /// # Errors
712    ///
713    /// Returns an error of type `SwitchboardError::FunctionRoutinesDisabled` if the function has routines_disabled configured.
714    fn assert_routines_enabled(&self) -> anchor_lang::Result<()> {
715        if self.routines_disabled.is_disabled() {
716            return Err(error!(SwitchboardError::FunctionRoutinesDisabled));
717        }
718
719        Ok(())
720    }
721
722    /// Checks if the function is ready to execute routines.
723    ///
724    /// # Errors
725    ///
726    /// Returns an error if:
727    ///
728    /// - Routines are disabled.
729    /// - The function has 0 valid mr_enclaves.
730    /// - The function status is not `Active`.
731    ///
732    /// # Returns
733    ///
734    /// Returns `Ok(())` if the function is ready to execute routines.
735    pub fn ready_for_routines(&self) -> anchor_lang::Result<()> {
736        self.assert_routines_enabled()?;
737        self.assert_has_enclaves()?;
738
739        if self.status != FunctionStatus::Active {
740            return Err(error!(SwitchboardError::FunctionNotReady));
741        }
742
743        Ok(())
744    }
745
746    /// Checks if the function is ready to execute requests.
747    ///
748    /// # Errors
749    ///
750    /// Returns an error if:
751    ///
752    /// - Requests are disabled.
753    /// - The function has 0 valid mr_enclaves.
754    /// - The function status is not `Active`.
755    ///
756    /// # Returns
757    ///
758    /// Returns `Ok(())` if the function is ready to execute requests.
759    pub fn ready_for_requests(&self) -> anchor_lang::Result<()> {
760        self.assert_requests_enabled()?;
761        self.assert_has_enclaves()?;
762
763        if self.status != FunctionStatus::Active && self.status != FunctionStatus::OutOfFunds {
764            return Err(error!(SwitchboardError::FunctionNotReady));
765        }
766
767        Ok(())
768    }
769
770    /// Returns the public key of the reward token wallet. If the reward escrow token wallet is set,
771    /// it returns the reward escrow token wallet. Otherwise, it returns the escrow token wallet.
772    pub fn get_reward_token_wallet(&self) -> Pubkey {
773        if self.reward_escrow_token_wallet != Pubkey::default() {
774            self.reward_escrow_token_wallet
775        } else {
776            self.escrow_token_wallet
777        }
778    }
779}
780#[cfg(test)]
781mod tests {
782    use super::*;
783
784    use std::str::FromStr;
785
786    const FUNCTION_DATA: [u8; 3903] = [
787        76, 139, 47, 44, 240, 182, 148, 200, 1, 0, 0, 0, 0, 0, 1, 254, 218, 146, 221, 148, 1, 120,
788        215, 193, 125, 114, 207, 207, 22, 231, 179, 145, 88, 164, 177, 30, 89, 129, 230, 41, 12,
789        196, 209, 106, 74, 219, 84, 225, 83, 111, 108, 97, 110, 97, 32, 84, 97, 115, 107, 32, 82,
790        117, 110, 110, 101, 114, 32, 70, 117, 110, 99, 116, 105, 111, 110, 0, 0, 0, 0, 0, 0, 0, 0,
791        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
792        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
793        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
794        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
795        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
796        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
797        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
798        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
799        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
800        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 18, 218, 14, 0, 0, 0, 0, 227, 58, 32,
801        101, 0, 0, 0, 0, 236, 84, 32, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
802        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
803        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
804        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
805        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
806        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
807        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
808        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
809        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
810        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
811        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
812        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
813        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
814        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
815        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35,
816        225, 147, 52, 198, 184, 237, 187, 106, 131, 221, 177, 176, 80, 145, 117, 118, 50, 205, 62,
817        19, 161, 35, 83, 210, 209, 6, 138, 101, 21, 112, 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
818        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
819        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
820        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
821        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
822        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
823        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
824        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
825        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
826        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
827        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
828        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
829        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
830        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
831        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
832        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
833        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
834        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
835        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
836        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
837        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
838        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
839        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
840        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
841        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
842        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
843        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
844        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
845        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
846        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
847        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
848        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
849        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
850        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 111, 99, 107, 101, 114,
851        104, 117, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
852        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115,
853        119, 105, 116, 99, 104, 98, 111, 97, 114, 100, 108, 97, 98, 115, 47, 115, 111, 108, 97,
854        110, 97, 45, 116, 97, 115, 107, 45, 114, 117, 110, 110, 101, 114, 45, 102, 117, 110, 99,
855        116, 105, 111, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 97,
856        116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
857        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
858        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
859        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
860        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
861        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
862        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
863        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
864        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
865        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
866        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
867        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
868        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
869        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
870        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
871        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
872        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
873        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
874        0, 0, 0, 0, 218, 146, 221, 148, 1, 120, 215, 193, 125, 114, 207, 207, 22, 231, 179, 145,
875        88, 164, 177, 30, 89, 129, 230, 41, 12, 196, 209, 106, 74, 219, 84, 225, 174, 177, 70, 231,
876        73, 196, 214, 194, 190, 219, 159, 24, 162, 119, 159, 16, 120, 53, 239, 102, 225, 241, 66,
877        97, 108, 144, 152, 47, 53, 76, 242, 215, 0, 0, 0, 0, 70, 65, 33, 246, 149, 218, 240, 26,
878        241, 158, 100, 187, 243, 161, 203, 30, 151, 239, 214, 115, 213, 239, 24, 92, 137, 228, 4,
879        197, 143, 13, 48, 131, 42, 32, 42, 47, 53, 32, 42, 32, 42, 32, 42, 32, 42, 0, 0, 0, 0, 0,
880        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
881        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 120, 45, 101, 0, 0, 0, 0, 149, 120,
882        45, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
883        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 249,
884        132, 49, 100, 114, 123, 49, 150, 198, 234, 193, 130, 176, 237, 177, 72, 2, 22, 174, 106,
885        202, 197, 89, 28, 119, 35, 166, 73, 57, 38, 23, 116, 180, 233, 161, 252, 164, 99, 206, 179,
886        104, 122, 218, 86, 162, 24, 43, 248, 82, 63, 111, 64, 70, 179, 213, 70, 253, 4, 4, 203, 25,
887        32, 254, 160, 249, 132, 49, 100, 114, 123, 49, 150, 198, 234, 193, 130, 176, 237, 177, 72,
888        2, 22, 174, 106, 202, 197, 89, 28, 119, 35, 166, 73, 57, 38, 23, 116, 180, 233, 161, 252,
889        164, 99, 206, 179, 104, 122, 218, 86, 162, 24, 43, 248, 82, 63, 111, 64, 70, 179, 213, 70,
890        253, 4, 4, 203, 25, 32, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
891        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
892        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
893        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
894        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
895        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
896        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
897        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
898        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
899        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
900        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
901        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
902        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
903        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
904        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
905        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
906        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
907        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
908        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
909        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
910        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
911        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
912        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
913        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
914        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
915        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
916        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
917        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
918        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
919        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
920        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
921        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
922        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
923        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
924        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
925    ];
926
927    const FUNCTION_DATA_HEX: &str = "4c8b2f2cf0b694c801000000000001feda92dd940178d7c17d72cfcf16e7b39158a4b11e5981e6290cc4d16a4adb54e1536f6c616e61205461736b2052756e6e65722046756e6374696f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c712da0e00000000e33a206500000000ec542065000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023e19334c6b8edbb6a83ddb1b05091757632cd3e13a12353d2d1068a6515707d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000646f636b657268756200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000737769746368626f6172646c6162732f736f6c616e612d7461736b2d72756e6e65722d66756e6374696f6e0000000000000000000000000000000000000000006c617465737400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da92dd940178d7c17d72cfcf16e7b39158a4b11e5981e6290cc4d16a4adb54e1aeb146e749c4d6c2bedb9f18a2779f107835ef66e1f142616c90982f354cf2d700000000464121f695daf01af19e64bbf3a1cb1e97efd673d5ef185c89e404c58f0d30832a202a2f35202a202a202a202a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076782d650000000095782d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0f9843164727b3196c6eac182b0edb1480216ae6acac5591c7723a64939261774b4e9a1fca463ceb3687ada56a2182bf8523f6f4046b3d546fd0404cb1920fea0f9843164727b3196c6eac182b0edb1480216ae6acac5591c7723a64939261774b4e9a1fca463ceb3687ada56a2182bf8523f6f4046b3d546fd0404cb1920feff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
928
929    #[test]
930    fn test_function_deserialization() {
931        let function =
932            FunctionAccountData::try_deserialize_unchecked(&mut FUNCTION_DATA.as_slice()).unwrap();
933
934        assert_eq!(
935            std::str::from_utf8(&function.container)
936                .unwrap()
937                .to_string()
938                .trim_matches(char::from(0)),
939            "switchboardlabs/solana-task-runner-function"
940        );
941
942        assert_eq!(
943            std::str::from_utf8(&function.container_registry)
944                .unwrap()
945                .to_string()
946                .trim_matches(char::from(0)),
947            "dockerhub"
948        );
949
950        assert_eq!(
951            std::str::from_utf8(&function.version)
952                .unwrap()
953                .to_string()
954                .trim_matches(char::from(0)),
955            "latest"
956        );
957
958        assert_eq!(
959            function.attestation_queue,
960            Pubkey::from_str("CkvizjVnm2zA5Wuwan34NhVT3zFc7vqUyGnA6tuEF5aE").unwrap()
961        );
962
963        assert_eq!(
964            function.authority,
965            Pubkey::from_str("FiDmUK83DTc1ijEyVnwMoQwJ6W4gC2S8JhncKsheDQTJ").unwrap()
966        );
967    }
968
969    #[test]
970    fn test_hex_decode() {
971        let account_bytes = hex::decode(FUNCTION_DATA_HEX).unwrap();
972        let function = FunctionAccountData::try_deserialize(&mut account_bytes.as_slice()).unwrap();
973
974        assert_eq!(
975            std::str::from_utf8(&function.container)
976                .unwrap()
977                .to_string()
978                .trim_matches(char::from(0)),
979            "switchboardlabs/solana-task-runner-function"
980        );
981
982        assert_eq!(
983            std::str::from_utf8(&function.container_registry)
984                .unwrap()
985                .to_string()
986                .trim_matches(char::from(0)),
987            "dockerhub"
988        );
989
990        assert_eq!(
991            std::str::from_utf8(&function.version)
992                .unwrap()
993                .to_string()
994                .trim_matches(char::from(0)),
995            "latest"
996        );
997
998        assert_eq!(
999            function.attestation_queue,
1000            Pubkey::from_str("CkvizjVnm2zA5Wuwan34NhVT3zFc7vqUyGnA6tuEF5aE").unwrap()
1001        );
1002
1003        assert_eq!(
1004            function.authority,
1005            Pubkey::from_str("FiDmUK83DTc1ijEyVnwMoQwJ6W4gC2S8JhncKsheDQTJ").unwrap()
1006        );
1007    }
1008}