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 return None;
63 }
64
65 let data = account.data();
66
67 if data.is_empty() {
68 Some(SystemAccountKind::System)
69 } else if data.len() == State::size() {
70 const NONCE_VERSIONS_LEGACY: u32 = 0;
71 const NONCE_VERSIONS_CURRENT: u32 = 1;
72 const NONCE_STATE_INITIALIZED: u32 = 1;
73
74 let versions_tag = u32::from_le_bytes(data.get(..4)?.try_into().ok()?);
75 let state_tag = u32::from_le_bytes(data.get(4..8)?.try_into().ok()?);
76
77 match (versions_tag, state_tag) {
78 (NONCE_VERSIONS_LEGACY, NONCE_STATE_INITIALIZED) => Some(SystemAccountKind::Nonce),
79 (NONCE_VERSIONS_CURRENT, NONCE_STATE_INITIALIZED) => Some(SystemAccountKind::Nonce),
80 _ => None,
81 }
82 } else {
83 None
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use {
90 super::*,
91 solana_fee_calculator::FeeCalculator,
92 solana_nonce::state::{Data, DurableNonce},
93 solana_pubkey::Pubkey,
94 };
95
96 #[test]
97 fn test_verify_bad_account_owner_fails() {
98 let program_id = Pubkey::new_unique();
99 assert_ne!(program_id, system_program::id());
100 let account = AccountSharedData::new_data_with_space(
101 42,
102 &Versions::new(State::Uninitialized),
103 State::size(),
104 &program_id,
105 )
106 .expect("nonce_account");
107 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
108 }
109
110 fn new_nonce_account(versions: Versions) -> AccountSharedData {
111 AccountSharedData::new_data(
112 1_000_000, &versions, &system_program::id(), )
116 .unwrap()
117 }
118
119 #[test]
120 fn test_verify_nonce_account() {
121 let blockhash = Hash::from([171; 32]);
122 let versions = Versions::Legacy(Box::new(State::Uninitialized));
123 let account = new_nonce_account(versions);
124 assert_eq!(verify_nonce_account(&account, &blockhash), None);
125 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
126 let versions = Versions::Current(Box::new(State::Uninitialized));
127 let account = new_nonce_account(versions);
128 assert_eq!(verify_nonce_account(&account, &blockhash), None);
129 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
130 let durable_nonce = DurableNonce::from_blockhash(&blockhash);
131 let data = Data {
132 authority: Pubkey::new_unique(),
133 durable_nonce,
134 fee_calculator: FeeCalculator {
135 lamports_per_signature: 2718,
136 },
137 };
138 let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
139 let account = new_nonce_account(versions);
140 assert_eq!(verify_nonce_account(&account, &blockhash), None);
141 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
142 assert_eq!(verify_nonce_account(&account, &data.blockhash()), None);
143 assert_eq!(
144 verify_nonce_account(&account, durable_nonce.as_hash()),
145 None
146 );
147 let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
148 assert_ne!(data.durable_nonce, durable_nonce);
149 let data = Data {
150 durable_nonce,
151 ..data
152 };
153 let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
154 let account = new_nonce_account(versions);
155 assert_eq!(verify_nonce_account(&account, &blockhash), None);
156 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
157 assert_eq!(
158 verify_nonce_account(&account, &data.blockhash()),
159 Some(data.clone())
160 );
161 assert_eq!(
162 verify_nonce_account(&account, durable_nonce.as_hash()),
163 Some(data)
164 );
165 }
166
167 #[test]
168 fn test_get_system_account_kind() {
169 fn _assert_nonce_versions(v: Versions, s: State) {
172 match v {
173 Versions::Legacy(..) => {}
174 Versions::Current(..) => {}
175 }
176 match s {
177 State::Uninitialized => {}
178 State::Initialized(..) => {}
179 }
180 }
181
182 let assert_correct = |bytes: &[u8], kind: Option<SystemAccountKind>| {
184 let mut account = AccountSharedData::new(0, 0, &system_program::id());
185 account.set_data_from_slice(bytes);
186 assert_eq!(get_system_account_kind(&account), kind);
187 };
188
189 let system_bytes = vec![];
191 let legacy_nonce_bytes = bincode::serialize(&Versions::Legacy(Box::new(
192 State::Initialized(Data::default()),
193 )))
194 .unwrap();
195 let current_nonce_bytes = bincode::serialize(&Versions::Current(Box::new(
196 State::Initialized(Data::default()),
197 )))
198 .unwrap();
199
200 assert_correct(&system_bytes, Some(SystemAccountKind::System));
202 assert_correct(&legacy_nonce_bytes, Some(SystemAccountKind::Nonce));
203 assert_correct(¤t_nonce_bytes, Some(SystemAccountKind::Nonce));
204
205 for bytes in [&system_bytes, &legacy_nonce_bytes, ¤t_nonce_bytes] {
207 let mut non_system = AccountSharedData::new(0, 0, &Pubkey::new_unique());
208 non_system.set_data_from_slice(bytes);
209 assert_eq!(get_system_account_kind(&non_system), None);
210 }
211
212 for nonce in &[Versions::Legacy, Versions::Current] {
214 let mut bytes = bincode::serialize(&nonce(Box::new(State::Uninitialized))).unwrap();
215 bytes.resize(State::size(), 0);
216 assert_correct(&bytes, None);
217 }
218
219 for bytes in [&legacy_nonce_bytes, ¤t_nonce_bytes] {
220 for len in 1..bytes.len() {
222 assert_correct(&bytes[..len], None);
223 }
224
225 let mut extended = bytes.clone();
227 extended.push(0);
228 assert_correct(&extended, None);
229
230 for byte in 0..=255 {
232 for i in 0..=7 {
233 if bytes[i] == byte {
235 continue;
236 }
237
238 let mut corrupted = bytes.clone();
239 corrupted[i] = byte;
240
241 if corrupted == legacy_nonce_bytes || corrupted == current_nonce_bytes {
243 continue;
244 }
245
246 assert_correct(&corrupted, None);
247 }
248 }
249
250 for i in 8..bytes.len() {
252 let mut with_data = bytes.clone();
253 with_data[i] = 255;
254 assert_correct(&with_data, Some(SystemAccountKind::Nonce));
255 }
256 }
257 }
258}