1#[cfg(feature = "dev-context-only-utils")]
2use {
3 qualifier_attr::qualifiers,
4 solana_account::state_traits::StateMut,
5 solana_nonce::{
6 state::{DurableNonce, State as NonceState},
7 versions::Versions as NonceVersions,
8 },
9 thiserror::Error,
10};
11use {solana_account::AccountSharedData, solana_pubkey::Pubkey};
12
13#[derive(Clone, Debug, Default, PartialEq, Eq)]
15pub struct NonceInfo {
16 address: Pubkey,
17 account: AccountSharedData,
18}
19
20#[derive(Error, Debug, PartialEq)]
21#[cfg(feature = "dev-context-only-utils")]
22#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
23enum AdvanceNonceError {
24 #[error("Invalid account")]
25 Invalid,
26 #[error("Uninitialized nonce")]
27 Uninitialized,
28}
29
30impl NonceInfo {
31 pub fn new(address: Pubkey, account: AccountSharedData) -> Self {
32 Self { address, account }
33 }
34
35 #[cfg(feature = "dev-context-only-utils")]
39 #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
40 fn try_advance_nonce(
41 &mut self,
42 durable_nonce: DurableNonce,
43 lamports_per_signature: u64,
44 ) -> Result<(), AdvanceNonceError> {
45 let nonce_versions = StateMut::<NonceVersions>::state(&self.account)
46 .map_err(|_| AdvanceNonceError::Invalid)?;
47 if let NonceState::Initialized(ref data) = nonce_versions.state() {
48 let nonce_state =
49 NonceState::new_initialized(&data.authority, durable_nonce, lamports_per_signature);
50 let nonce_versions = NonceVersions::new(nonce_state);
51 self.account.set_state(&nonce_versions).unwrap();
52 Ok(())
53 } else {
54 Err(AdvanceNonceError::Uninitialized)
55 }
56 }
57
58 pub fn address(&self) -> &Pubkey {
59 &self.address
60 }
61
62 pub fn account(&self) -> &AccountSharedData {
63 &self.account
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use {
70 super::*,
71 solana_hash::Hash,
72 solana_nonce::{
73 state::{Data as NonceData, DurableNonce, State as NonceState},
74 versions::Versions as NonceVersions,
75 },
76 solana_sdk_ids::system_program,
77 };
78
79 fn create_nonce_account(state: NonceState) -> AccountSharedData {
80 AccountSharedData::new_data(1_000_000, &NonceVersions::new(state), &system_program::id())
81 .unwrap()
82 }
83
84 #[test]
85 fn test_nonce_info() {
86 let nonce_address = Pubkey::new_unique();
87 let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
88 let lamports_per_signature = 42;
89 let nonce_account = create_nonce_account(NonceState::Initialized(NonceData::new(
90 Pubkey::default(),
91 durable_nonce,
92 lamports_per_signature,
93 )));
94
95 let nonce_info = NonceInfo::new(nonce_address, nonce_account.clone());
96 assert_eq!(*nonce_info.address(), nonce_address);
97 assert_eq!(*nonce_info.account(), nonce_account);
98 }
99
100 #[test]
101 fn test_try_advance_nonce_success() {
102 let authority = Pubkey::new_unique();
103 let mut nonce_info = NonceInfo::new(
104 Pubkey::new_unique(),
105 create_nonce_account(NonceState::Initialized(NonceData::new(
106 authority,
107 DurableNonce::from_blockhash(&Hash::new_unique()),
108 42,
109 ))),
110 );
111
112 let new_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
113 let new_lamports_per_signature = 100;
114 let result = nonce_info.try_advance_nonce(new_nonce, new_lamports_per_signature);
115 assert_eq!(result, Ok(()));
116
117 let nonce_versions = StateMut::<NonceVersions>::state(&nonce_info.account).unwrap();
118 assert_eq!(
119 &NonceState::Initialized(NonceData::new(
120 authority,
121 new_nonce,
122 new_lamports_per_signature
123 )),
124 nonce_versions.state()
125 );
126 }
127
128 #[test]
129 fn test_try_advance_nonce_invalid() {
130 let mut nonce_info = NonceInfo::new(
131 Pubkey::new_unique(),
132 AccountSharedData::new(1_000_000, 0, &Pubkey::default()),
133 );
134
135 let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
136 let result = nonce_info.try_advance_nonce(durable_nonce, 5000);
137 assert_eq!(result, Err(AdvanceNonceError::Invalid));
138 }
139
140 #[test]
141 fn test_try_advance_nonce_uninitialized() {
142 let mut nonce_info = NonceInfo::new(
143 Pubkey::new_unique(),
144 create_nonce_account(NonceState::Uninitialized),
145 );
146
147 let durable_nonce = DurableNonce::from_blockhash(&Hash::new_unique());
148 let result = nonce_info.try_advance_nonce(durable_nonce, 5000);
149 assert_eq!(result, Err(AdvanceNonceError::Uninitialized));
150 }
151}