solana_runtime/
stake_account.rs1#[cfg(feature = "dev-context-only-utils")]
2use qualifier_attr::qualifiers;
3use {
4 solana_account::{AccountSharedData, ReadableAccount, state_traits::StateMut},
5 solana_instruction::error::InstructionError,
6 solana_pubkey::Pubkey,
7 solana_stake_interface::{
8 program as stake_program,
9 state::{Delegation, Stake, StakeStateV2},
10 },
11 std::marker::PhantomData,
12 thiserror::Error,
13};
14#[cfg(feature = "frozen-abi")]
15use {
16 solana_frozen_abi::{abi_example::AbiExample, stable_abi::StableAbi},
17 solana_stake_interface::{stake_flags::StakeFlags, state::Meta},
18};
19
20#[cfg_attr(feature = "frozen-abi", derive(StableAbi, StableAbiSample))]
25#[derive(Clone, Debug, Default)]
26pub struct StakeAccount<T> {
27 #[cfg_attr(feature = "frozen-abi", stable_abi_sample(with = "Default::default()"))]
29 account: AccountSharedData,
30 #[cfg_attr(
31 feature = "frozen-abi",
32 stable_abi_sample(with = "sample_delegated_stake_state(rng)")
33 )]
34 stake_state: StakeStateV2,
35 _phantom: PhantomData<T>,
36}
37
38#[cfg(feature = "frozen-abi")]
41fn sample_delegated_stake_state(
42 rng: &mut (impl solana_frozen_abi::rand::RngCore + ?Sized),
43) -> StakeStateV2 {
44 StakeStateV2::Stake(
45 Meta::random(rng),
46 Stake::random(rng),
47 StakeFlags::random(rng),
48 )
49}
50
51#[derive(Debug, Error)]
52pub enum Error {
53 #[error(transparent)]
54 InstructionError(#[from] InstructionError),
55 #[error("Invalid delegation: {0:?}")]
56 InvalidDelegation(Box<StakeStateV2>),
57 #[error("Invalid stake account owner: {0}")]
58 InvalidOwner(Pubkey),
59}
60
61impl<T> StakeAccount<T> {
62 #[inline]
63 pub(crate) fn lamports(&self) -> u64 {
64 self.account.lamports()
65 }
66
67 #[inline]
68 pub(crate) fn stake_state(&self) -> &StakeStateV2 {
69 &self.stake_state
70 }
71
72 #[inline]
73 pub(crate) fn data_len(&self) -> usize {
74 self.account.data().len()
75 }
76}
77
78impl StakeAccount<Delegation> {
79 #[inline]
80 #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
81 pub(crate) fn delegation(&self) -> &Delegation {
82 self.stake_state.delegation_ref().unwrap()
85 }
86
87 #[inline]
88 pub(crate) fn stake(&self) -> &Stake {
89 self.stake_state.stake_ref().unwrap()
92 }
93}
94
95impl TryFrom<AccountSharedData> for StakeAccount<Delegation> {
96 type Error = Error;
97 fn try_from(account: AccountSharedData) -> Result<Self, Self::Error> {
98 if account.owner() != &stake_program::id() {
99 return Err(Error::InvalidOwner(*account.owner()));
100 }
101 let stake_state: StakeStateV2 = account.state()?;
102 if stake_state.delegation().is_none() {
103 return Err(Error::InvalidDelegation(Box::new(stake_state)));
104 }
105 Ok(Self {
106 account,
107 stake_state,
108 _phantom: PhantomData,
109 })
110 }
111}
112
113impl<T> From<StakeAccount<T>> for (AccountSharedData, StakeStateV2) {
114 #[inline]
115 fn from(stake_account: StakeAccount<T>) -> Self {
116 (stake_account.account, stake_account.stake_state)
117 }
118}
119
120impl<S, T> PartialEq<StakeAccount<S>> for StakeAccount<T> {
121 fn eq(&self, other: &StakeAccount<S>) -> bool {
122 let StakeAccount {
123 account,
124 stake_state,
125 _phantom,
126 } = other;
127 account == &self.account && stake_state == &self.stake_state
128 }
129}
130
131#[cfg(feature = "frozen-abi")]
132impl AbiExample for StakeAccount<Delegation> {
133 fn example() -> Self {
134 use solana_account::Account;
135 let stake_state =
136 StakeStateV2::Stake(Meta::example(), Stake::example(), StakeFlags::example());
137 let mut account = Account::example();
138 account.data.resize(200, 0u8);
139 account.owner = stake_program::id();
140 account.set_state(&stake_state).unwrap();
141 Self::try_from(AccountSharedData::from(account)).unwrap()
142 }
143}