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