1#![cfg_attr(docsrs, feature(doc_cfg))]
3
4#[cfg(all(feature = "bincode", not(feature = "wincode")))]
7use solana_account::state_traits::StateMut as _;
8#[cfg(feature = "wincode")]
9use solana_account::state_traits::StateMutWincode as _;
10use {
11 solana_account::{AccountSharedData, ReadableAccount},
12 solana_nonce::state::State,
13 solana_sdk_ids::system_program,
14};
15#[cfg(any(feature = "bincode", feature = "wincode"))]
16use {
17 solana_hash::Hash,
18 solana_nonce::{state::Data, versions::Versions},
19 std::cell::RefCell,
20};
21
22#[cfg(any(feature = "bincode", feature = "wincode"))]
23pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> {
24 RefCell::new(
27 AccountSharedData::new_data_with_space(
28 lamports,
29 &Versions::new(State::Uninitialized),
30 State::size(),
31 &system_program::id(),
32 )
33 .expect("nonce_account"),
34 )
35}
36
37#[cfg(any(feature = "bincode", feature = "wincode"))]
40pub fn verify_nonce_account(
41 account: &AccountSharedData,
42 recent_blockhash: &Hash, ) -> Option<Data> {
44 (account.owner() == &system_program::id())
45 .then(|| {
46 let versions: Versions = account.state().ok()?;
47 versions.verify_recent_blockhash(recent_blockhash).cloned()
48 })
49 .flatten()
50}
51
52#[cfg(any(feature = "bincode", feature = "wincode"))]
53pub fn lamports_per_signature_of(account: &AccountSharedData) -> Option<u64> {
54 let versions: Versions = account.state().ok()?;
55 match versions.state() {
56 State::Initialized(data) => Some(data.fee_calculator.lamports_per_signature),
57 State::Uninitialized => None,
58 }
59}
60
61#[derive(Copy, Clone, Debug, Eq, PartialEq)]
62pub enum SystemAccountKind {
63 System,
64 Nonce,
65}
66
67pub fn get_system_account_kind(account: &AccountSharedData) -> Option<SystemAccountKind> {
68 if !system_program::check_id(account.owner()) {
69 return None;
70 }
71
72 let data = account.data();
73
74 if data.is_empty() {
75 Some(SystemAccountKind::System)
76 } else if data.len() == State::size() {
77 const NONCE_VERSIONS_LEGACY: u32 = 0;
78 const NONCE_VERSIONS_CURRENT: u32 = 1;
79 const NONCE_STATE_INITIALIZED: u32 = 1;
80
81 let versions_tag = u32::from_le_bytes(data.get(..4)?.try_into().ok()?);
82 let state_tag = u32::from_le_bytes(data.get(4..8)?.try_into().ok()?);
83
84 match (versions_tag, state_tag) {
85 (NONCE_VERSIONS_LEGACY, NONCE_STATE_INITIALIZED) => Some(SystemAccountKind::Nonce),
86 (NONCE_VERSIONS_CURRENT, NONCE_STATE_INITIALIZED) => Some(SystemAccountKind::Nonce),
87 _ => None,
88 }
89 } else {
90 None
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use {
97 super::*,
98 solana_nonce::{state::Data, versions::Versions},
99 solana_pubkey::Pubkey,
100 };
101 #[cfg(any(feature = "bincode", feature = "wincode"))]
102 use {solana_fee_calculator::FeeCalculator, solana_nonce::state::DurableNonce};
103
104 #[cfg(any(feature = "bincode", feature = "wincode"))]
106 #[test]
107 fn test_create_account() {
108 let account = create_account(42);
109 let account = account.borrow();
110 assert_eq!(account.lamports(), 42);
111 assert_eq!(account.owner(), &system_program::id());
112 assert_eq!(account.data().len(), State::size());
113 let versions: Versions = account.state().unwrap();
114 assert_eq!(versions.state(), &State::Uninitialized);
115 }
116
117 #[cfg(any(feature = "bincode", feature = "wincode"))]
118 #[test]
119 fn test_verify_bad_account_owner_fails() {
120 let program_id = Pubkey::new_unique();
121 assert_ne!(program_id, system_program::id());
122 let account = AccountSharedData::new_data_with_space(
123 42,
124 &Versions::new(State::Uninitialized),
125 State::size(),
126 &program_id,
127 )
128 .expect("nonce_account");
129 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
130 }
131
132 #[cfg(any(feature = "bincode", feature = "wincode"))]
133 fn new_nonce_account(versions: Versions) -> AccountSharedData {
134 AccountSharedData::new_data(
135 1_000_000, &versions, &system_program::id(), )
139 .unwrap()
140 }
141
142 #[cfg(any(feature = "bincode", feature = "wincode"))]
143 #[test]
144 fn test_verify_nonce_account() {
145 let blockhash = Hash::from([171; 32]);
146 let versions = Versions::Legacy(Box::new(State::Uninitialized));
147 let account = new_nonce_account(versions);
148 assert_eq!(verify_nonce_account(&account, &blockhash), None);
149 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
150 let versions = Versions::Current(Box::new(State::Uninitialized));
151 let account = new_nonce_account(versions);
152 assert_eq!(verify_nonce_account(&account, &blockhash), None);
153 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
154 let durable_nonce = DurableNonce::from_blockhash(&blockhash);
155 let data = Data {
156 authority: Pubkey::new_unique(),
157 durable_nonce,
158 fee_calculator: FeeCalculator {
159 lamports_per_signature: 2718,
160 },
161 };
162 let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
163 let account = new_nonce_account(versions);
164 assert_eq!(verify_nonce_account(&account, &blockhash), None);
165 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
166 assert_eq!(verify_nonce_account(&account, &data.blockhash()), None);
167 assert_eq!(
168 verify_nonce_account(&account, durable_nonce.as_hash()),
169 None
170 );
171 let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
172 assert_ne!(data.durable_nonce, durable_nonce);
173 let data = Data {
174 durable_nonce,
175 ..data
176 };
177 let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
178 let account = new_nonce_account(versions);
179 assert_eq!(verify_nonce_account(&account, &blockhash), None);
180 assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
181 assert_eq!(
182 verify_nonce_account(&account, &data.blockhash()),
183 Some(data.clone())
184 );
185 assert_eq!(
186 verify_nonce_account(&account, durable_nonce.as_hash()),
187 Some(data)
188 );
189 }
190
191 #[test]
192 fn test_get_system_account_kind() {
193 fn _assert_nonce_versions(v: Versions, s: State) {
196 match v {
197 Versions::Legacy(..) => {}
198 Versions::Current(..) => {}
199 }
200 match s {
201 State::Uninitialized => {}
202 State::Initialized(..) => {}
203 }
204 }
205
206 let assert_correct = |bytes: &[u8], kind: Option<SystemAccountKind>| {
208 let mut account = AccountSharedData::new(0, 0, &system_program::id());
209 account.set_data_from_slice(bytes);
210 assert_eq!(get_system_account_kind(&account), kind);
211 };
212
213 let system_bytes = vec![];
215 let legacy_nonce_bytes = bincode::serialize(&Versions::Legacy(Box::new(
216 State::Initialized(Data::default()),
217 )))
218 .unwrap();
219 let current_nonce_bytes = bincode::serialize(&Versions::Current(Box::new(
220 State::Initialized(Data::default()),
221 )))
222 .unwrap();
223
224 assert_correct(&system_bytes, Some(SystemAccountKind::System));
226 assert_correct(&legacy_nonce_bytes, Some(SystemAccountKind::Nonce));
227 assert_correct(¤t_nonce_bytes, Some(SystemAccountKind::Nonce));
228
229 for bytes in [&system_bytes, &legacy_nonce_bytes, ¤t_nonce_bytes] {
231 let mut non_system = AccountSharedData::new(0, 0, &Pubkey::new_unique());
232 non_system.set_data_from_slice(bytes);
233 assert_eq!(get_system_account_kind(&non_system), None);
234 }
235
236 for nonce in &[Versions::Legacy, Versions::Current] {
238 let mut bytes = bincode::serialize(&nonce(Box::new(State::Uninitialized))).unwrap();
239 bytes.resize(State::size(), 0);
240 assert_correct(&bytes, None);
241 }
242
243 for bytes in [&legacy_nonce_bytes, ¤t_nonce_bytes] {
244 for len in 1..bytes.len() {
246 assert_correct(&bytes[..len], None);
247 }
248
249 let mut extended = bytes.clone();
251 extended.push(0);
252 assert_correct(&extended, None);
253
254 for byte in 0..=255 {
256 for i in 0..=7 {
257 if bytes[i] == byte {
259 continue;
260 }
261
262 let mut corrupted = bytes.clone();
263 corrupted[i] = byte;
264
265 if corrupted == legacy_nonce_bytes || corrupted == current_nonce_bytes {
267 continue;
268 }
269
270 assert_correct(&corrupted, None);
271 }
272 }
273
274 for i in 8..bytes.len() {
276 let mut with_data = bytes.clone();
277 with_data[i] = 255;
278 assert_correct(&with_data, Some(SystemAccountKind::Nonce));
279 }
280 }
281 }
282}