Skip to main content

solana_nonce_account/
lib.rs

1//! Functions related to nonce accounts.
2#![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
27/// Checks if the recent_blockhash field in Transaction verifies, and returns
28/// nonce account data if so.
29pub fn verify_nonce_account(
30    account: &AccountSharedData,
31    recent_blockhash: &Hash, // Transaction.message.recent_blockhash
32) -> 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        if account.data().is_empty() {
63            Some(SystemAccountKind::System)
64        } else if account.data().len() == State::size() {
65            let nonce_versions: Versions = account.state().ok()?;
66            match nonce_versions.state() {
67                State::Uninitialized => None,
68                State::Initialized(_) => Some(SystemAccountKind::Nonce),
69            }
70        } else {
71            None
72        }
73    } else {
74        None
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use {
81        super::*,
82        solana_fee_calculator::FeeCalculator,
83        solana_nonce::state::{Data, DurableNonce},
84        solana_pubkey::Pubkey,
85    };
86
87    #[test]
88    fn test_verify_bad_account_owner_fails() {
89        let program_id = Pubkey::new_unique();
90        assert_ne!(program_id, system_program::id());
91        let account = AccountSharedData::new_data_with_space(
92            42,
93            &Versions::new(State::Uninitialized),
94            State::size(),
95            &program_id,
96        )
97        .expect("nonce_account");
98        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
99    }
100
101    fn new_nonce_account(versions: Versions) -> AccountSharedData {
102        AccountSharedData::new_data(
103            1_000_000,             // lamports
104            &versions,             // state
105            &system_program::id(), // owner
106        )
107        .unwrap()
108    }
109
110    #[test]
111    fn test_verify_nonce_account() {
112        let blockhash = Hash::from([171; 32]);
113        let versions = Versions::Legacy(Box::new(State::Uninitialized));
114        let account = new_nonce_account(versions);
115        assert_eq!(verify_nonce_account(&account, &blockhash), None);
116        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
117        let versions = Versions::Current(Box::new(State::Uninitialized));
118        let account = new_nonce_account(versions);
119        assert_eq!(verify_nonce_account(&account, &blockhash), None);
120        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
121        let durable_nonce = DurableNonce::from_blockhash(&blockhash);
122        let data = Data {
123            authority: Pubkey::new_unique(),
124            durable_nonce,
125            fee_calculator: FeeCalculator {
126                lamports_per_signature: 2718,
127            },
128        };
129        let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
130        let account = new_nonce_account(versions);
131        assert_eq!(verify_nonce_account(&account, &blockhash), None);
132        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
133        assert_eq!(verify_nonce_account(&account, &data.blockhash()), None);
134        assert_eq!(
135            verify_nonce_account(&account, durable_nonce.as_hash()),
136            None
137        );
138        let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
139        assert_ne!(data.durable_nonce, durable_nonce);
140        let data = Data {
141            durable_nonce,
142            ..data
143        };
144        let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
145        let account = new_nonce_account(versions);
146        assert_eq!(verify_nonce_account(&account, &blockhash), None);
147        assert_eq!(verify_nonce_account(&account, &Hash::default()), None);
148        assert_eq!(
149            verify_nonce_account(&account, &data.blockhash()),
150            Some(data.clone())
151        );
152        assert_eq!(
153            verify_nonce_account(&account, durable_nonce.as_hash()),
154            Some(data)
155        );
156    }
157
158    #[test]
159    fn test_get_system_account_kind_system_ok() {
160        let system_account = AccountSharedData::default();
161        assert_eq!(
162            get_system_account_kind(&system_account),
163            Some(SystemAccountKind::System)
164        );
165    }
166
167    #[test]
168    fn test_get_system_account_kind_nonce_ok() {
169        let nonce_account = AccountSharedData::new_data(
170            42,
171            &Versions::new(State::Initialized(Data::default())),
172            &system_program::id(),
173        )
174        .unwrap();
175        assert_eq!(
176            get_system_account_kind(&nonce_account),
177            Some(SystemAccountKind::Nonce)
178        );
179    }
180
181    #[test]
182    fn test_get_system_account_kind_uninitialized_nonce_account_fail() {
183        assert_eq!(
184            get_system_account_kind(&crate::create_account(42).borrow()),
185            None
186        );
187    }
188
189    #[test]
190    fn test_get_system_account_kind_system_owner_nonzero_nonnonce_data_fail() {
191        let other_data_account =
192            AccountSharedData::new_data(42, b"other", &Pubkey::default()).unwrap();
193        assert_eq!(get_system_account_kind(&other_data_account), None);
194    }
195
196    #[test]
197    fn test_get_system_account_kind_nonsystem_owner_with_nonce_data_fail() {
198        let nonce_account = AccountSharedData::new_data(
199            42,
200            &Versions::new(State::Initialized(Data::default())),
201            &Pubkey::new_unique(),
202        )
203        .unwrap();
204        assert_eq!(get_system_account_kind(&nonce_account), None);
205    }
206}