atlas_feature_gate_interface/
state.rs

1#[cfg(feature = "bincode")]
2use {
3    atlas_account::{AccountSharedData, ReadableAccount, WritableAccount},
4    atlas_account_info::AccountInfo,
5    atlas_program_error::ProgramError,
6    atlas_sdk_ids::feature::id,
7};
8
9#[cfg_attr(
10    feature = "serde",
11    derive(serde_derive::Deserialize, serde_derive::Serialize)
12)]
13#[derive(Default, Debug, PartialEq, Eq)]
14pub struct Feature {
15    pub activated_at: Option<u64>,
16}
17
18impl Feature {
19    pub const fn size_of() -> usize {
20        9 // see test_feature_size_of.
21    }
22
23    #[cfg(feature = "bincode")]
24    pub fn from_account_info(account_info: &AccountInfo) -> Result<Self, ProgramError> {
25        if *account_info.owner != id() {
26            return Err(ProgramError::InvalidAccountOwner);
27        }
28        if account_info.data_len() < Feature::size_of() {
29            return Err(ProgramError::InvalidAccountData);
30        }
31        bincode::deserialize(&account_info.data.borrow())
32            .map_err(|_| ProgramError::InvalidAccountData)
33    }
34}
35
36#[cfg(feature = "bincode")]
37pub fn from_account<T: ReadableAccount>(account: &T) -> Option<Feature> {
38    if account.owner() != &id() || account.data().len() < Feature::size_of() {
39        None
40    } else {
41        bincode::deserialize(account.data()).ok()
42    }
43}
44
45#[cfg(feature = "bincode")]
46pub fn to_account(feature: &Feature, account: &mut AccountSharedData) -> Option<()> {
47    bincode::serialize_into(account.data_as_mut_slice(), feature).ok()
48}
49
50#[cfg(feature = "bincode")]
51pub fn create_account(feature: &Feature, lamports: u64) -> AccountSharedData {
52    let data_len = Feature::size_of().max(bincode::serialized_size(feature).unwrap() as usize);
53    let mut account = AccountSharedData::new(lamports, data_len, &id());
54    to_account(feature, &mut account).unwrap();
55    account
56}
57
58#[cfg(test)]
59mod test {
60    use {super::*, atlas_pubkey::Pubkey};
61
62    #[test]
63    fn test_feature_size_of() {
64        assert_eq!(Feature::size_of() as u64, {
65            let feature = Feature {
66                activated_at: Some(0),
67            };
68            bincode::serialized_size(&feature).unwrap()
69        });
70        assert!(
71            Feature::size_of() >= bincode::serialized_size(&Feature::default()).unwrap() as usize
72        );
73        assert_eq!(Feature::default(), Feature { activated_at: None });
74
75        let features = [
76            Feature {
77                activated_at: Some(0),
78            },
79            Feature {
80                activated_at: Some(u64::MAX),
81            },
82        ];
83        for feature in &features {
84            assert_eq!(
85                Feature::size_of(),
86                bincode::serialized_size(feature).unwrap() as usize
87            );
88        }
89    }
90
91    #[test]
92    fn feature_from_account_info_none() {
93        let key = Pubkey::new_unique();
94        let mut lamports = 42;
95
96        let mut good_data = vec![0; Feature::size_of()];
97        let mut small_data = vec![0; Feature::size_of() - 1]; // Too small
98
99        assert_eq!(
100            Feature::from_account_info(&AccountInfo::new(
101                &key,
102                false,
103                false,
104                &mut lamports,
105                &mut good_data,
106                &id(),
107                false,
108            )),
109            Ok(Feature { activated_at: None })
110        );
111        assert_eq!(
112            Feature::from_account_info(&AccountInfo::new(
113                &key,
114                false,
115                false,
116                &mut lamports,
117                &mut small_data, // Too small
118                &id(),
119                false,
120            )),
121            Err(ProgramError::InvalidAccountData),
122        );
123        assert_eq!(
124            Feature::from_account_info(&AccountInfo::new(
125                &key,
126                false,
127                false,
128                &mut lamports,
129                &mut good_data,
130                &Pubkey::new_unique(), // Wrong owner
131                false,
132            )),
133            Err(ProgramError::InvalidAccountOwner),
134        );
135    }
136
137    #[test]
138    fn feature_deserialize_none() {
139        assert_eq!(
140            from_account(&AccountSharedData::new(42, Feature::size_of(), &id())),
141            Some(Feature { activated_at: None })
142        );
143        assert_eq!(
144            from_account(&AccountSharedData::new(
145                42,
146                Feature::size_of() - 1, // Too small
147                &id()
148            )),
149            None,
150        );
151        assert_eq!(
152            from_account(&AccountSharedData::new(
153                42,
154                Feature::size_of(),
155                &Pubkey::new_unique(), // Wrong owner
156            )),
157            None,
158        );
159    }
160}