solana_feature_gate_interface/
lib.rs1pub use solana_sdk_ids::feature::{check_id, id, ID};
15#[cfg(feature = "bincode")]
16use {
17 solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
18 solana_account_info::AccountInfo,
19 solana_instruction::Instruction,
20 solana_program_error::ProgramError,
21 solana_pubkey::Pubkey,
22 solana_rent::Rent,
23 solana_system_interface::instruction as system_instruction,
24};
25
26#[cfg_attr(
27 feature = "serde",
28 derive(serde_derive::Deserialize, serde_derive::Serialize)
29)]
30#[derive(Default, Debug, PartialEq, Eq)]
31pub struct Feature {
32 pub activated_at: Option<u64>,
33}
34
35impl Feature {
36 pub const fn size_of() -> usize {
37 9 }
39
40 #[cfg(feature = "bincode")]
41 pub fn from_account_info(account_info: &AccountInfo) -> Result<Self, ProgramError> {
42 if *account_info.owner != id() {
43 return Err(ProgramError::InvalidAccountOwner);
44 }
45 if account_info.data_len() < Feature::size_of() {
46 return Err(ProgramError::InvalidAccountData);
47 }
48 bincode::deserialize(&account_info.data.borrow())
49 .map_err(|_| ProgramError::InvalidAccountData)
50 }
51}
52
53#[cfg(feature = "bincode")]
54pub fn activate(feature_id: &Pubkey, funding_address: &Pubkey, rent: &Rent) -> Vec<Instruction> {
56 activate_with_lamports(
57 feature_id,
58 funding_address,
59 rent.minimum_balance(Feature::size_of()),
60 )
61}
62
63#[cfg(feature = "bincode")]
64pub fn activate_with_lamports(
65 feature_id: &Pubkey,
66 funding_address: &Pubkey,
67 lamports: u64,
68) -> Vec<Instruction> {
69 vec![
70 system_instruction::transfer(funding_address, feature_id, lamports),
71 system_instruction::allocate(feature_id, Feature::size_of() as u64),
72 system_instruction::assign(feature_id, &id()),
73 ]
74}
75
76#[cfg(feature = "bincode")]
77pub fn from_account<T: ReadableAccount>(account: &T) -> Option<Feature> {
78 if account.owner() != &id() || account.data().len() < Feature::size_of() {
79 None
80 } else {
81 bincode::deserialize(account.data()).ok()
82 }
83}
84
85#[cfg(feature = "bincode")]
86pub fn to_account(feature: &Feature, account: &mut AccountSharedData) -> Option<()> {
87 bincode::serialize_into(account.data_as_mut_slice(), feature).ok()
88}
89
90#[cfg(feature = "bincode")]
91pub fn create_account(feature: &Feature, lamports: u64) -> AccountSharedData {
92 let data_len = Feature::size_of().max(bincode::serialized_size(feature).unwrap() as usize);
93 let mut account = AccountSharedData::new(lamports, data_len, &id());
94 to_account(feature, &mut account).unwrap();
95 account
96}
97
98#[cfg(test)]
99mod test {
100 use super::*;
101
102 #[test]
103 fn test_feature_size_of() {
104 assert_eq!(Feature::size_of() as u64, {
105 let feature = Feature {
106 activated_at: Some(0),
107 };
108 bincode::serialized_size(&feature).unwrap()
109 });
110 assert!(
111 Feature::size_of() >= bincode::serialized_size(&Feature::default()).unwrap() as usize
112 );
113 assert_eq!(Feature::default(), Feature { activated_at: None });
114
115 let features = [
116 Feature {
117 activated_at: Some(0),
118 },
119 Feature {
120 activated_at: Some(u64::MAX),
121 },
122 ];
123 for feature in &features {
124 assert_eq!(
125 Feature::size_of(),
126 bincode::serialized_size(feature).unwrap() as usize
127 );
128 }
129 }
130
131 #[test]
132 fn feature_from_account_info_none() {
133 let key = Pubkey::new_unique();
134 let mut lamports = 42;
135
136 let mut good_data = vec![0; Feature::size_of()];
137 let mut small_data = vec![0; Feature::size_of() - 1]; assert_eq!(
140 Feature::from_account_info(&AccountInfo::new(
141 &key,
142 false,
143 false,
144 &mut lamports,
145 &mut good_data,
146 &id(),
147 false,
148 u64::MAX,
149 )),
150 Ok(Feature { activated_at: None })
151 );
152 assert_eq!(
153 Feature::from_account_info(&AccountInfo::new(
154 &key,
155 false,
156 false,
157 &mut lamports,
158 &mut small_data, &id(),
160 false,
161 u64::MAX,
162 )),
163 Err(ProgramError::InvalidAccountData),
164 );
165 assert_eq!(
166 Feature::from_account_info(&AccountInfo::new(
167 &key,
168 false,
169 false,
170 &mut lamports,
171 &mut good_data,
172 &Pubkey::new_unique(), false,
174 u64::MAX,
175 )),
176 Err(ProgramError::InvalidAccountOwner),
177 );
178 }
179
180 #[test]
181 fn feature_deserialize_none() {
182 assert_eq!(
183 from_account(&AccountSharedData::new(42, Feature::size_of(), &id())),
184 Some(Feature { activated_at: None })
185 );
186 assert_eq!(
187 from_account(&AccountSharedData::new(
188 42,
189 Feature::size_of() - 1, &id()
191 )),
192 None,
193 );
194 assert_eq!(
195 from_account(&AccountSharedData::new(
196 42,
197 Feature::size_of(),
198 &Pubkey::new_unique(), )),
200 None,
201 );
202 }
203}