clone_solana_nonce_account/
lib.rs

1//! Functions related to nonce accounts.
2
3use {
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
26/// Checks if the recent_blockhash field in Transaction verifies, and returns
27/// nonce account data if so.
28pub fn verify_nonce_account(
29    account: &AccountSharedData,
30    recent_blockhash: &Hash, // Transaction.message.recent_blockhash
31) -> 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,             // lamports
99            &versions,             // state
100            &system_program::id(), // owner
101        )
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}