solana_program/nonce/state/
mod.rs1mod current;
4pub use current::{Data, DurableNonce, State};
5use {
6 crate::{hash::Hash, pubkey::Pubkey},
7 serde_derive::{Deserialize, Serialize},
8 std::collections::HashSet,
9};
10
11#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
12pub enum Versions {
13 Legacy(Box<State>),
14 Current(Box<State>),
16}
17
18#[derive(Debug, Eq, PartialEq)]
19pub enum AuthorizeNonceError {
20 MissingRequiredSignature(Pubkey),
21 Uninitialized,
22}
23
24impl Versions {
25 pub fn new(state: State) -> Self {
26 Self::Current(Box::new(state))
27 }
28
29 pub fn state(&self) -> &State {
30 match self {
31 Self::Legacy(state) => state,
32 Self::Current(state) => state,
33 }
34 }
35
36 pub fn verify_recent_blockhash(
39 &self,
40 recent_blockhash: &Hash, ) -> Option<&Data> {
42 match self {
43 Self::Legacy(_) => None,
46 Self::Current(state) => match **state {
47 State::Uninitialized => None,
48 State::Initialized(ref data) => {
49 (recent_blockhash == &data.blockhash()).then_some(data)
50 }
51 },
52 }
53 }
54
55 pub fn upgrade(self) -> Option<Self> {
57 match self {
58 Self::Legacy(mut state) => {
59 match *state {
60 State::Uninitialized => None,
65 State::Initialized(ref mut data) => {
66 data.durable_nonce = DurableNonce::from_blockhash(&data.blockhash());
67 Some(Self::Current(state))
68 }
69 }
70 }
71 Self::Current(_) => None,
72 }
73 }
74
75 pub fn authorize(
77 self,
78 signers: &HashSet<Pubkey>,
79 authority: Pubkey,
80 ) -> Result<Self, AuthorizeNonceError> {
81 let data = match self.state() {
82 State::Uninitialized => return Err(AuthorizeNonceError::Uninitialized),
83 State::Initialized(data) => data,
84 };
85 if !signers.contains(&data.authority) {
86 return Err(AuthorizeNonceError::MissingRequiredSignature(
87 data.authority,
88 ));
89 }
90 let data = Data::new(
91 authority,
92 data.durable_nonce,
93 data.get_lamports_per_signature(),
94 );
95 let state = Box::new(State::Initialized(data));
96 Ok(match self {
99 Self::Legacy(_) => Self::Legacy,
100 Self::Current(_) => Self::Current,
101 }(state))
102 }
103}
104
105impl From<Versions> for State {
106 fn from(versions: Versions) -> Self {
107 match versions {
108 Versions::Legacy(state) => *state,
109 Versions::Current(state) => *state,
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use {
117 super::*,
118 crate::{fee_calculator::FeeCalculator, pubkey::Pubkey},
119 std::iter::repeat_with,
120 };
121
122 #[test]
123 fn test_verify_recent_blockhash() {
124 let blockhash = Hash::from([171; 32]);
125 let versions = Versions::Legacy(Box::new(State::Uninitialized));
126 assert_eq!(versions.verify_recent_blockhash(&blockhash), None);
127 assert_eq!(versions.verify_recent_blockhash(&Hash::default()), None);
128 let versions = Versions::Current(Box::new(State::Uninitialized));
129 assert_eq!(versions.verify_recent_blockhash(&blockhash), None);
130 assert_eq!(versions.verify_recent_blockhash(&Hash::default()), None);
131 let durable_nonce = DurableNonce::from_blockhash(&blockhash);
132 let data = Data {
133 authority: Pubkey::new_unique(),
134 durable_nonce,
135 fee_calculator: FeeCalculator {
136 lamports_per_signature: 2718,
137 },
138 };
139 let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
140 assert_eq!(versions.verify_recent_blockhash(&Hash::default()), None);
141 assert_eq!(versions.verify_recent_blockhash(&blockhash), None);
142 assert_eq!(versions.verify_recent_blockhash(&data.blockhash()), None);
143 assert_eq!(
144 versions.verify_recent_blockhash(durable_nonce.as_hash()),
145 None
146 );
147 let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
148 assert_ne!(data.durable_nonce, durable_nonce);
149 let data = Data {
150 durable_nonce,
151 ..data
152 };
153 let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
154 assert_eq!(versions.verify_recent_blockhash(&blockhash), None);
155 assert_eq!(versions.verify_recent_blockhash(&Hash::default()), None);
156 assert_eq!(
157 versions.verify_recent_blockhash(&data.blockhash()),
158 Some(&data)
159 );
160 assert_eq!(
161 versions.verify_recent_blockhash(durable_nonce.as_hash()),
162 Some(&data)
163 );
164 }
165
166 #[test]
167 fn test_nonce_versions_upgrade() {
168 let versions = Versions::Legacy(Box::new(State::Uninitialized));
170 assert_eq!(versions.upgrade(), None);
171 let blockhash = Hash::from([171; 32]);
173 let durable_nonce = DurableNonce::from_blockhash(&blockhash);
174 let data = Data {
175 authority: Pubkey::new_unique(),
176 durable_nonce,
177 fee_calculator: FeeCalculator {
178 lamports_per_signature: 2718,
179 },
180 };
181 let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
182 let durable_nonce = DurableNonce::from_blockhash(durable_nonce.as_hash());
183 assert_ne!(data.durable_nonce, durable_nonce);
184 let data = Data {
185 durable_nonce,
186 ..data
187 };
188 let versions = versions.upgrade().unwrap();
189 assert_eq!(
190 versions,
191 Versions::Current(Box::new(State::Initialized(data)))
192 );
193 assert_eq!(versions.upgrade(), None);
194 }
195
196 #[test]
197 fn test_nonce_versions_authorize() {
198 let mut signers = repeat_with(Pubkey::new_unique).take(16).collect();
200 let versions = Versions::Legacy(Box::new(State::Uninitialized));
201 assert_eq!(
202 versions.authorize(&signers, Pubkey::new_unique()),
203 Err(AuthorizeNonceError::Uninitialized)
204 );
205 let versions = Versions::Current(Box::new(State::Uninitialized));
206 assert_eq!(
207 versions.authorize(&signers, Pubkey::new_unique()),
208 Err(AuthorizeNonceError::Uninitialized)
209 );
210 let blockhash = Hash::from([171; 32]);
212 let durable_nonce = DurableNonce::from_blockhash(&blockhash);
213 let data = Data {
214 authority: Pubkey::new_unique(),
215 durable_nonce,
216 fee_calculator: FeeCalculator {
217 lamports_per_signature: 2718,
218 },
219 };
220 let account_authority = data.authority;
221 let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
222 let authority = Pubkey::new_unique();
223 assert_ne!(authority, account_authority);
224 let data = Data { authority, ..data };
225 assert_eq!(
226 versions.clone().authorize(&signers, authority),
227 Err(AuthorizeNonceError::MissingRequiredSignature(
228 account_authority
229 )),
230 );
231 assert!(signers.insert(account_authority));
232 assert_eq!(
233 versions.authorize(&signers, authority),
234 Ok(Versions::Legacy(Box::new(State::Initialized(data.clone()))))
235 );
236 let account_authority = data.authority;
238 let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
239 let authority = Pubkey::new_unique();
240 assert_ne!(authority, account_authority);
241 let data = Data { authority, ..data };
242 assert_eq!(
243 versions.clone().authorize(&signers, authority),
244 Err(AuthorizeNonceError::MissingRequiredSignature(
245 account_authority
246 )),
247 );
248 assert!(signers.insert(account_authority));
249 assert_eq!(
250 versions.authorize(&signers, authority),
251 Ok(Versions::Current(Box::new(State::Initialized(data))))
252 );
253 }
254}