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
//! State transition types

use {
    crate::{error::SinglePoolError, find_pool_address},
    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
    solana_program::{
        account_info::AccountInfo, borsh0_10::try_from_slice_unchecked,
        program_error::ProgramError, pubkey::Pubkey,
    },
};

/// Single-Validator Stake Pool account type
#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum SinglePoolAccountType {
    /// Uninitialized account
    #[default]
    Uninitialized,
    /// Main pool account
    Pool,
}

/// Single-Validator Stake Pool account, used to derive all PDAs
#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct SinglePool {
    /// Pool account type, reserved for future compat
    pub account_type: SinglePoolAccountType,
    /// The vote account this pool is mapped to
    pub vote_account_address: Pubkey,
}
impl SinglePool {
    /// Create a SinglePool struct from its account info
    pub fn from_account_info(
        account_info: &AccountInfo,
        program_id: &Pubkey,
    ) -> Result<Self, ProgramError> {
        // pool is allocated and owned by this program
        if account_info.data_len() == 0 || account_info.owner != program_id {
            return Err(SinglePoolError::InvalidPoolAccount.into());
        }

        let pool = try_from_slice_unchecked::<SinglePool>(&account_info.data.borrow())?;

        // pool is well-typed
        if pool.account_type != SinglePoolAccountType::Pool {
            return Err(SinglePoolError::InvalidPoolAccount.into());
        }

        // pool vote account address is properly configured. in practice this is irrefutable
        // because the pool is initialized from the addresss that derives it, and never modified
        if *account_info.key != find_pool_address(program_id, &pool.vote_account_address) {
            return Err(SinglePoolError::InvalidPoolAccount.into());
        }

        Ok(pool)
    }
}