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