solana_sdk/
nonce_account.rs

1//! Functions related to nonce accounts.
2
3use {
4    crate::{
5        account_utils::StateMut,
6        hash::Hash,
7        nonce::{
8            state::{Data, Versions},
9            State,
10        },
11    },
12    solana_account::{AccountSharedData, ReadableAccount},
13    std::cell::RefCell,
14};
15
16pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> {
17    RefCell::new(
18        AccountSharedData::new_data_with_space(
19            lamports,
20            &Versions::new(State::Uninitialized),
21            State::size(),
22            &crate::system_program::id(),
23        )
24        .expect("nonce_account"),
25    )
26}
27
28/// Checks if the recent_blockhash field in Transaction verifies, and returns
29/// nonce account data if so.
30pub fn verify_nonce_account(
31    account: &AccountSharedData,
32    recent_blockhash: &Hash, // Transaction.message.recent_blockhash
33) -> Option<Data> {
34    (account.owner() == &crate::system_program::id())
35        .then(|| {
36            StateMut::<Versions>::state(account)
37                .ok()?
38                .verify_recent_blockhash(recent_blockhash)
39                .cloned()
40        })
41        .flatten()
42}
43
44pub fn lamports_per_signature_of(account: &AccountSharedData) -> Option<u64> {
45    match StateMut::<Versions>::state(account).ok()?.state() {
46        State::Initialized(data) => Some(data.fee_calculator.lamports_per_signature),
47        State::Uninitialized => None,
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use {
54        super::*,
55        crate::{
56            fee_calculator::FeeCalculator,
57            nonce::state::{Data, DurableNonce},
58            pubkey::Pubkey,
59            system_program,
60        },
61    };
62
63    #[test]
64    fn test_verify_bad_account_owner_fails() {
65        let program_id = Pubkey::new_unique();
66        assert_ne!(program_id, crate::system_program::id());
67        let account = AccountSharedData::new_data_with_space(
68            42,
69            &Versions::new(State::Uninitialized),
70            State::size(),
71            &program_id,
72        )
73        .expect("nonce_account");
74        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
75    }
76
77    fn new_nonce_account(versions: Versions) -> AccountSharedData {
78        AccountSharedData::new_data(
79            1_000_000,             // lamports
80            &versions,             // state
81            &system_program::id(), // owner
82        )
83        .unwrap()
84    }
85
86    #[test]
87    fn test_verify_nonce_account() {
88        let blockhash = Hash::from([171; 32]);
89        let versions = Versions::Legacy(Box::new(State::Uninitialized));
90        let account = new_nonce_account(versions);
91        assert_eq!(verify_nonce_account(&account, &blockhash), None);
92        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
93        let versions = Versions::Current(Box::new(State::Uninitialized));
94        let account = new_nonce_account(versions);
95        assert_eq!(verify_nonce_account(&account, &blockhash), None);
96        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
97        let durable_nonce = DurableNonce::from_blockhash(&blockhash);
98        let data = Data {
99            authority: Pubkey::new_unique(),
100            durable_nonce,
101            fee_calculator: FeeCalculator {
102                lamports_per_signature: 2718,
103            },
104        };
105        let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
106        let account = new_nonce_account(versions);
107        assert_eq!(verify_nonce_account(&account, &blockhash), None);
108        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
109        assert_eq!(verify_nonce_account(&account, &data.blockhash()), None);
110        assert_eq!(
111            verify_nonce_account(&account, durable_nonce.as_hash()),
112            None
113        );
114        let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
115        assert_ne!(data.durable_nonce, durable_nonce);
116        let data = Data {
117            durable_nonce,
118            ..data
119        };
120        let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
121        let account = new_nonce_account(versions);
122        assert_eq!(verify_nonce_account(&account, &blockhash), None);
123        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
124        assert_eq!(
125            verify_nonce_account(&account, &data.blockhash()),
126            Some(data.clone())
127        );
128        assert_eq!(
129            verify_nonce_account(&account, durable_nonce.as_hash()),
130            Some(data)
131        );
132    }
133}