1use {
4 crate::Transaction, clone_solana_message::VersionedMessage,
5 clone_solana_sanitize::SanitizeError, clone_solana_signature::Signature, std::cmp::Ordering,
6};
7#[cfg(feature = "bincode")]
8use {
9 clone_solana_bincode::limited_deserialize,
10 clone_solana_sdk_ids::system_program,
11 clone_solana_signer::{signers::Signers, SignerError},
12 clone_solana_system_interface::instruction::SystemInstruction,
13};
14#[cfg(feature = "serde")]
15use {
16 clone_solana_short_vec as short_vec,
17 serde_derive::{Deserialize, Serialize},
18};
19
20pub mod sanitized;
21
22#[cfg_attr(
24 feature = "serde",
25 derive(Deserialize, Serialize),
26 serde(rename_all = "camelCase")
27)]
28#[derive(Clone, Debug, PartialEq, Eq)]
29pub enum Legacy {
30 Legacy,
31}
32
33#[cfg_attr(
34 feature = "serde",
35 derive(Deserialize, Serialize),
36 serde(rename_all = "camelCase", untagged)
37)]
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub enum TransactionVersion {
40 Legacy(Legacy),
41 Number(u8),
42}
43
44impl TransactionVersion {
45 pub const LEGACY: Self = Self::Legacy(Legacy::Legacy);
46}
47
48#[cfg_attr(
51 feature = "frozen-abi",
52 derive(clone_solana_frozen_abi_macro::AbiExample)
53)]
54#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
55#[derive(Debug, PartialEq, Default, Eq, Clone)]
56pub struct VersionedTransaction {
57 #[cfg_attr(feature = "serde", serde(with = "short_vec"))]
59 pub signatures: Vec<Signature>,
60 pub message: VersionedMessage,
62}
63
64impl From<Transaction> for VersionedTransaction {
65 fn from(transaction: Transaction) -> Self {
66 Self {
67 signatures: transaction.signatures,
68 message: VersionedMessage::Legacy(transaction.message),
69 }
70 }
71}
72
73impl VersionedTransaction {
74 #[cfg(feature = "bincode")]
77 pub fn try_new<T: Signers + ?Sized>(
78 message: VersionedMessage,
79 keypairs: &T,
80 ) -> std::result::Result<Self, SignerError> {
81 let static_account_keys = message.static_account_keys();
82 if static_account_keys.len() < message.header().num_required_signatures as usize {
83 return Err(SignerError::InvalidInput("invalid message".to_string()));
84 }
85
86 let signer_keys = keypairs.try_pubkeys()?;
87 let expected_signer_keys =
88 &static_account_keys[0..message.header().num_required_signatures as usize];
89
90 match signer_keys.len().cmp(&expected_signer_keys.len()) {
91 Ordering::Greater => Err(SignerError::TooManySigners),
92 Ordering::Less => Err(SignerError::NotEnoughSigners),
93 Ordering::Equal => Ok(()),
94 }?;
95
96 let message_data = message.serialize();
97 let signature_indexes: Vec<usize> = expected_signer_keys
98 .iter()
99 .map(|signer_key| {
100 signer_keys
101 .iter()
102 .position(|key| key == signer_key)
103 .ok_or(SignerError::KeypairPubkeyMismatch)
104 })
105 .collect::<std::result::Result<_, SignerError>>()?;
106
107 let unordered_signatures = keypairs.try_sign_message(&message_data)?;
108 let signatures: Vec<Signature> = signature_indexes
109 .into_iter()
110 .map(|index| {
111 unordered_signatures
112 .get(index)
113 .copied()
114 .ok_or_else(|| SignerError::InvalidInput("invalid keypairs".to_string()))
115 })
116 .collect::<std::result::Result<_, SignerError>>()?;
117
118 Ok(Self {
119 signatures,
120 message,
121 })
122 }
123
124 pub fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
125 self.message.sanitize()?;
126 self.sanitize_signatures()?;
127 Ok(())
128 }
129
130 pub(crate) fn sanitize_signatures(&self) -> std::result::Result<(), SanitizeError> {
131 Self::sanitize_signatures_inner(
132 usize::from(self.message.header().num_required_signatures),
133 self.message.static_account_keys().len(),
134 self.signatures.len(),
135 )
136 }
137
138 pub(crate) fn sanitize_signatures_inner(
139 num_required_signatures: usize,
140 num_static_account_keys: usize,
141 num_signatures: usize,
142 ) -> std::result::Result<(), SanitizeError> {
143 match num_required_signatures.cmp(&num_signatures) {
144 Ordering::Greater => Err(SanitizeError::IndexOutOfBounds),
145 Ordering::Less => Err(SanitizeError::InvalidValue),
146 Ordering::Equal => Ok(()),
147 }?;
148
149 if num_signatures > num_static_account_keys {
152 return Err(SanitizeError::IndexOutOfBounds);
153 }
154
155 Ok(())
156 }
157
158 pub fn version(&self) -> TransactionVersion {
160 match self.message {
161 VersionedMessage::Legacy(_) => TransactionVersion::LEGACY,
162 VersionedMessage::V0(_) => TransactionVersion::Number(0),
163 }
164 }
165
166 pub fn into_legacy_transaction(self) -> Option<Transaction> {
168 match self.message {
169 VersionedMessage::Legacy(message) => Some(Transaction {
170 signatures: self.signatures,
171 message,
172 }),
173 _ => None,
174 }
175 }
176
177 #[cfg(feature = "verify")]
178 pub fn verify_and_hash_message(
180 &self,
181 ) -> clone_solana_transaction_error::TransactionResult<clone_solana_hash::Hash> {
182 let message_bytes = self.message.serialize();
183 if !self
184 ._verify_with_results(&message_bytes)
185 .iter()
186 .all(|verify_result| *verify_result)
187 {
188 Err(clone_solana_transaction_error::TransactionError::SignatureFailure)
189 } else {
190 Ok(VersionedMessage::hash_raw_message(&message_bytes))
191 }
192 }
193
194 #[cfg(feature = "verify")]
195 pub fn verify_with_results(&self) -> Vec<bool> {
197 let message_bytes = self.message.serialize();
198 self._verify_with_results(&message_bytes)
199 }
200
201 #[cfg(feature = "verify")]
202 fn _verify_with_results(&self, message_bytes: &[u8]) -> Vec<bool> {
203 self.signatures
204 .iter()
205 .zip(self.message.static_account_keys().iter())
206 .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
207 .collect()
208 }
209
210 #[cfg(feature = "bincode")]
211 pub fn uses_durable_nonce(&self) -> bool {
213 let message = &self.message;
214 message
215 .instructions()
216 .get(crate::NONCED_TX_MARKER_IX_INDEX as usize)
217 .filter(|instruction| {
218 matches!(
220 message.static_account_keys().get(instruction.program_id_index as usize),
221 Some(program_id) if system_program::check_id(program_id)
222 )
223 && matches!(
225 limited_deserialize(&instruction.data, crate::PACKET_DATA_SIZE as u64,),
226 Ok(SystemInstruction::AdvanceNonceAccount)
227 )
228 })
229 .is_some()
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use {
236 super::*,
237 clone_solana_hash::Hash,
238 clone_solana_instruction::{AccountMeta, Instruction},
239 clone_solana_keypair::Keypair,
240 clone_solana_message::Message as LegacyMessage,
241 clone_solana_pubkey::Pubkey,
242 clone_solana_signer::Signer,
243 clone_solana_system_interface::instruction as system_instruction,
244 };
245
246 #[test]
247 fn test_try_new() {
248 let keypair0 = Keypair::new();
249 let keypair1 = Keypair::new();
250 let keypair2 = Keypair::new();
251
252 let message = VersionedMessage::Legacy(LegacyMessage::new(
253 &[Instruction::new_with_bytes(
254 Pubkey::new_unique(),
255 &[],
256 vec![
257 AccountMeta::new_readonly(keypair1.pubkey(), true),
258 AccountMeta::new_readonly(keypair2.pubkey(), false),
259 ],
260 )],
261 Some(&keypair0.pubkey()),
262 ));
263
264 assert_eq!(
265 VersionedTransaction::try_new(message.clone(), &[&keypair0]),
266 Err(SignerError::NotEnoughSigners)
267 );
268
269 assert_eq!(
270 VersionedTransaction::try_new(message.clone(), &[&keypair0, &keypair0]),
271 Err(SignerError::KeypairPubkeyMismatch)
272 );
273
274 assert_eq!(
275 VersionedTransaction::try_new(message.clone(), &[&keypair1, &keypair2]),
276 Err(SignerError::KeypairPubkeyMismatch)
277 );
278
279 match VersionedTransaction::try_new(message.clone(), &[&keypair0, &keypair1]) {
280 Ok(tx) => assert_eq!(tx.verify_with_results(), vec![true; 2]),
281 Err(err) => assert_eq!(Some(err), None),
282 }
283
284 match VersionedTransaction::try_new(message, &[&keypair1, &keypair0]) {
285 Ok(tx) => assert_eq!(tx.verify_with_results(), vec![true; 2]),
286 Err(err) => assert_eq!(Some(err), None),
287 }
288 }
289
290 fn nonced_transfer_tx() -> (Pubkey, Pubkey, VersionedTransaction) {
291 let from_keypair = Keypair::new();
292 let from_pubkey = from_keypair.pubkey();
293 let nonce_keypair = Keypair::new();
294 let nonce_pubkey = nonce_keypair.pubkey();
295 let instructions = [
296 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
297 system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
298 ];
299 let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey));
300 let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
301 (from_pubkey, nonce_pubkey, tx.into())
302 }
303
304 #[test]
305 fn tx_uses_nonce_ok() {
306 let (_, _, tx) = nonced_transfer_tx();
307 assert!(tx.uses_durable_nonce());
308 }
309
310 #[test]
311 fn tx_uses_nonce_empty_ix_fail() {
312 assert!(!VersionedTransaction::default().uses_durable_nonce());
313 }
314
315 #[test]
316 fn tx_uses_nonce_bad_prog_id_idx_fail() {
317 let (_, _, mut tx) = nonced_transfer_tx();
318 match &mut tx.message {
319 VersionedMessage::Legacy(message) => {
320 message.instructions.get_mut(0).unwrap().program_id_index = 255u8;
321 }
322 VersionedMessage::V0(_) => unreachable!(),
323 };
324 assert!(!tx.uses_durable_nonce());
325 }
326
327 #[test]
328 fn tx_uses_nonce_first_prog_id_not_nonce_fail() {
329 let from_keypair = Keypair::new();
330 let from_pubkey = from_keypair.pubkey();
331 let nonce_keypair = Keypair::new();
332 let nonce_pubkey = nonce_keypair.pubkey();
333 let instructions = [
334 system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
335 system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey),
336 ];
337 let message = LegacyMessage::new(&instructions, Some(&from_pubkey));
338 let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
339 let tx = VersionedTransaction::from(tx);
340 assert!(!tx.uses_durable_nonce());
341 }
342
343 #[test]
344 fn tx_uses_nonce_wrong_first_nonce_ix_fail() {
345 let from_keypair = Keypair::new();
346 let from_pubkey = from_keypair.pubkey();
347 let nonce_keypair = Keypair::new();
348 let nonce_pubkey = nonce_keypair.pubkey();
349 let instructions = [
350 system_instruction::withdraw_nonce_account(
351 &nonce_pubkey,
352 &nonce_pubkey,
353 &from_pubkey,
354 42,
355 ),
356 system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42),
357 ];
358 let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey));
359 let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default());
360 let tx = VersionedTransaction::from(tx);
361 assert!(!tx.uses_durable_nonce());
362 }
363
364 #[test]
365 fn test_sanitize_signatures_inner() {
366 assert_eq!(
367 VersionedTransaction::sanitize_signatures_inner(1, 1, 0),
368 Err(SanitizeError::IndexOutOfBounds)
369 );
370 assert_eq!(
371 VersionedTransaction::sanitize_signatures_inner(1, 1, 2),
372 Err(SanitizeError::InvalidValue)
373 );
374 assert_eq!(
375 VersionedTransaction::sanitize_signatures_inner(2, 1, 2),
376 Err(SanitizeError::IndexOutOfBounds)
377 );
378 assert_eq!(
379 VersionedTransaction::sanitize_signatures_inner(1, 1, 1),
380 Ok(())
381 );
382 }
383}