1#![cfg_attr(docsrs, feature(doc_cfg))]
3
4use {
5 solana_account::{state_traits::StateMut, AccountSharedData, ReadableAccount},
6 solana_hash::Hash,
7 solana_nonce::{
8 state::{Data, State},
9 versions::Versions,
10 },
11 solana_sdk_ids::system_program,
12 std::cell::RefCell,
13};
14
15pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> {
16 RefCell::new(
17 AccountSharedData::new_data_with_space(
18 lamports,
19 &Versions::new(State::Uninitialized),
20 State::size(),
21 &system_program::id(),
22 )
23 .expect("nonce_account"),
24 )
25}
26
27pub fn verify_nonce_account(
30 account: &AccountSharedData,
31 recent_blockhash: &Hash, ) -> Option<Data> {
33 (account.owner() == &system_program::id())
34 .then(|| {
35 #[cfg(feature = "wincode")]
36 let versions = wincode::deserialize::<Versions>(account.data());
37 #[cfg(not(feature = "wincode"))]
38 let versions = StateMut::<Versions>::state(account);
39 versions
40 .ok()?
41 .verify_recent_blockhash(recent_blockhash)
42 .cloned()
43 })
44 .flatten()
45}
46
47pub fn lamports_per_signature_of(account: &AccountSharedData) -> Option<u64> {
48 match StateMut::<Versions>::state(account).ok()?.state() {
49 State::Initialized(data) => Some(data.fee_calculator.lamports_per_signature),
50 State::Uninitialized => None,
51 }
52}
53
54#[derive(Copy, Clone, Debug, Eq, PartialEq)]
55pub enum SystemAccountKind {
56 System,
57 Nonce,
58}
59
60pub fn get_system_account_kind(account: &AccountSharedData) -> Option<SystemAccountKind> {
61 if system_program::check_id(account.owner()) {
62 if account.data().is_empty() {
63 Some(SystemAccountKind::System)
64 } else if account.data().len() == State::size() {
65 let nonce_versions: Versions = account.state().ok()?;
66 match nonce_versions.state() {
67 State::Uninitialized => None,
68 State::Initialized(_) => Some(SystemAccountKind::Nonce),
69 }
70 } else {
71 None
72 }
73 } else {
74 None
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use {
81 super::*,
82 solana_fee_calculator::FeeCalculator,
83 solana_nonce::state::{Data, DurableNonce},
84 solana_pubkey::Pubkey,
85 };
86
87 #[test]
88 fn test_verify_bad_account_owner_fails() {
89 let program_id = Pubkey::new_unique();
90 assert_ne!(program_id, system_program::id());
91 let account = AccountSharedData::new_data_with_space(
92 42,
93 &Versions::new(State::Uninitialized),
94 State::size(),
95 &program_id,
96 )
97 .expect("nonce_account");
98 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
99 }
100
101 fn new_nonce_account(versions: Versions) -> AccountSharedData {
102 AccountSharedData::new_data(
103 1_000_000, &versions, &system_program::id(), )
107 .unwrap()
108 }
109
110 #[test]
111 fn test_verify_nonce_account() {
112 let blockhash = Hash::from([171; 32]);
113 let versions = Versions::Legacy(Box::new(State::Uninitialized));
114 let account = new_nonce_account(versions);
115 assert_eq!(verify_nonce_account(&account, &blockhash), None);
116 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
117 let versions = Versions::Current(Box::new(State::Uninitialized));
118 let account = new_nonce_account(versions);
119 assert_eq!(verify_nonce_account(&account, &blockhash), None);
120 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
121 let durable_nonce = DurableNonce::from_blockhash(&blockhash);
122 let data = Data {
123 authority: Pubkey::new_unique(),
124 durable_nonce,
125 fee_calculator: FeeCalculator {
126 lamports_per_signature: 2718,
127 },
128 };
129 let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
130 let account = new_nonce_account(versions);
131 assert_eq!(verify_nonce_account(&account, &blockhash), None);
132 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
133 assert_eq!(verify_nonce_account(&account, &data.blockhash()), None);
134 assert_eq!(
135 verify_nonce_account(&account, durable_nonce.as_hash()),
136 None
137 );
138 let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
139 assert_ne!(data.durable_nonce, durable_nonce);
140 let data = Data {
141 durable_nonce,
142 ..data
143 };
144 let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
145 let account = new_nonce_account(versions);
146 assert_eq!(verify_nonce_account(&account, &blockhash), None);
147 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
148 assert_eq!(
149 verify_nonce_account(&account, &data.blockhash()),
150 Some(data.clone())
151 );
152 assert_eq!(
153 verify_nonce_account(&account, durable_nonce.as_hash()),
154 Some(data)
155 );
156 }
157
158 #[test]
159 fn test_get_system_account_kind_system_ok() {
160 let system_account = AccountSharedData::default();
161 assert_eq!(
162 get_system_account_kind(&system_account),
163 Some(SystemAccountKind::System)
164 );
165 }
166
167 #[test]
168 fn test_get_system_account_kind_nonce_ok() {
169 let nonce_account = AccountSharedData::new_data(
170 42,
171 &Versions::new(State::Initialized(Data::default())),
172 &system_program::id(),
173 )
174 .unwrap();
175 assert_eq!(
176 get_system_account_kind(&nonce_account),
177 Some(SystemAccountKind::Nonce)
178 );
179 }
180
181 #[test]
182 fn test_get_system_account_kind_uninitialized_nonce_account_fail() {
183 assert_eq!(
184 get_system_account_kind(&crate::create_account(42).borrow()),
185 None
186 );
187 }
188
189 #[test]
190 fn test_get_system_account_kind_system_owner_nonzero_nonnonce_data_fail() {
191 let other_data_account =
192 AccountSharedData::new_data(42, b"other", &Pubkey::default()).unwrap();
193 assert_eq!(get_system_account_kind(&other_data_account), None);
194 }
195
196 #[test]
197 fn test_get_system_account_kind_nonsystem_owner_with_nonce_data_fail() {
198 let nonce_account = AccountSharedData::new_data(
199 42,
200 &Versions::new(State::Initialized(Data::default())),
201 &Pubkey::new_unique(),
202 )
203 .unwrap();
204 assert_eq!(get_system_account_kind(&nonce_account), None);
205 }
206}