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