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