solana_block_decoder/transaction/
transaction.rs1use {
2 crate::{
3 errors::{
4 decode_error::DecodeError,
5 },
6 block::{
7 encoded_block::EncodedTransaction,
8 },
9 message::{
10 message::Message,
11 },
12 decodable::{
13 Decodable,
14 },
15 },
16 serde_derive::{Deserialize, Serialize},
17 solana_short_vec as short_vec,
18 solana_signature::{
19 ParseSignatureError,
20 Signature,
21 },
22 solana_transaction_status_client_types::{
23 TransactionBinaryEncoding,
24 },
26 std::{
27 str::FromStr,
28 },
29 base64::{Engine, prelude::BASE64_STANDARD},
30};
31
32#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize)]
33pub struct Transaction {
34 #[serde(with = "short_vec")]
44 pub signatures: Vec<Signature>,
45
46 pub message: Message,
48}
49
50impl Decodable for Transaction {
51 type Encoded = EncodedTransaction;
52 type Decoded = Transaction;
53
54 fn decode(encoded: &Self::Encoded) -> Result<Self::Decoded, DecodeError> {
55 match encoded {
56 EncodedTransaction::LegacyBinary(s) | EncodedTransaction::Binary(s, TransactionBinaryEncoding::Base58) => {
57 let data = bs58::decode(s)
58 .into_vec()
59 .map_err(|_| DecodeError::DeserializeFailed)?;
60 let transaction: Transaction = bincode::deserialize(&data)
61 .map_err(|_| DecodeError::DeserializeFailed)?;
62 Ok(transaction)
63 }
64 EncodedTransaction::Binary(s, TransactionBinaryEncoding::Base64) => {
65 let data = BASE64_STANDARD.decode(s)
66 .map_err(|_| DecodeError::DeserializeFailed)?;
67 let transaction: Transaction = bincode::deserialize(&data)
68 .map_err(|_| DecodeError::DeserializeFailed)?;
69 Ok(transaction)
70 }
71 EncodedTransaction::Json(ui_transaction) => {
72 let message = Message::decode(&ui_transaction.message)?;
73 let signatures: Result<Vec<Signature>, ParseSignatureError> = ui_transaction.signatures.iter()
74 .map(|s| Signature::from_str(s))
75 .collect();
76 let signatures = match signatures {
77 Ok(signatures) => signatures,
78 Err(error) => return Err(DecodeError::ParseSignatureFailed(error)),
79 };
80 Ok(Transaction {
81 signatures,
82 message,
83 })
84 }
85 EncodedTransaction::Accounts(_) => {
86 Err(DecodeError::UnsupportedEncoding)
87 }
88 }
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use bincode;
96 use bs58;
97 use base64::{Engine, prelude::BASE64_STANDARD};
98 use solana_transaction_status::{
99 TransactionBinaryEncoding, UiTransaction, UiMessage, UiRawMessage, UiAccountsList, UiCompiledInstruction, UiAddressTableLookup,
100 };
101 use solana_message::{
103 MessageHeader,
104 };
105 use crate::errors::decode_error::DecodeError;
106 use crate::block::encoded_block::EncodedTransaction;
107 use crate::message::message::Message;
108 use std::str::FromStr;
109 use solana_signature::Signature;
110
111 fn create_sample_message() -> Message {
112 Message::default()
113 }
114
115 fn create_sample_ui_message() -> UiMessage {
116 UiMessage::Raw(UiRawMessage {
117 header: MessageHeader {
118 num_required_signatures: 1,
119 num_readonly_signed_accounts: 0,
120 num_readonly_unsigned_accounts: 0,
121 },
122 account_keys: vec!["11111111111111111111111111111111".to_string()],
123 recent_blockhash: "11111111111111111111111111111111".to_string(),
124 instructions: vec![],
125 address_table_lookups: None,
126 })
127 }
128
129 fn create_sample_transaction() -> Transaction {
130 Transaction {
131 signatures: vec![Signature::default()],
132 message: create_sample_message(),
133 }
134 }
135
136 #[test]
137 fn test_decode_legacy_binary_success() {
138 let transaction = create_sample_transaction();
139 let serialized = bincode::serialize(&transaction).expect("Failed to serialize transaction");
140 let encoded = bs58::encode(&serialized).into_string();
141 let encoded_transaction = EncodedTransaction::LegacyBinary(encoded);
142 let decoded = Transaction::decode(&encoded_transaction);
143 assert!(decoded.is_ok());
144 assert_eq!(decoded.unwrap(), transaction);
145 }
146
147 #[test]
148 fn test_decode_base58_binary_success() {
149 let transaction = create_sample_transaction();
150 let serialized = bincode::serialize(&transaction).expect("Failed to serialize transaction");
151 let encoded = bs58::encode(&serialized).into_string();
152 let encoded_transaction = EncodedTransaction::Binary(encoded, TransactionBinaryEncoding::Base58);
153 let decoded = Transaction::decode(&encoded_transaction);
154 assert!(decoded.is_ok());
155 assert_eq!(decoded.unwrap(), transaction);
156 }
157
158 #[test]
159 fn test_decode_base64_binary_success() {
160 let transaction = create_sample_transaction();
161 let serialized = bincode::serialize(&transaction).expect("Failed to serialize transaction");
162 let encoded = BASE64_STANDARD.encode(&serialized);
163 let encoded_transaction = EncodedTransaction::Binary(encoded, TransactionBinaryEncoding::Base64);
164 let decoded = Transaction::decode(&encoded_transaction);
165 assert!(decoded.is_ok());
166 assert_eq!(decoded.unwrap(), transaction);
167 }
168
169 #[test]
170 fn test_decode_invalid_base58_binary_fails() {
171 let encoded_transaction = EncodedTransaction::Binary("invalidbase58".to_string(), TransactionBinaryEncoding::Base58);
172 let decoded = Transaction::decode(&encoded_transaction);
173 assert!(decoded.is_err());
174 assert_eq!(decoded.unwrap_err(), DecodeError::DeserializeFailed);
175 }
176
177 #[test]
178 fn test_decode_invalid_base64_binary_fails() {
179 let encoded_transaction = EncodedTransaction::Binary("invalidbase64==".to_string(), TransactionBinaryEncoding::Base64);
180 let decoded = Transaction::decode(&encoded_transaction);
181 assert!(decoded.is_err());
182 assert_eq!(decoded.unwrap_err(), DecodeError::DeserializeFailed);
183 }
184
185 #[test]
186 fn test_decode_json_success() {
187 let message = create_sample_ui_message();
188 let ui_transaction = UiTransaction {
189 signatures: vec![Signature::default().to_string()],
190 message: message.clone(),
191 };
192 let encoded_transaction = EncodedTransaction::Json(ui_transaction);
193 let decoded = Transaction::decode(&encoded_transaction);
194 assert!(decoded.is_ok(), "Transaction decoding should succeed, got {:?}", decoded);
195 if let Ok(result) = decoded {
196 assert_eq!(result.signatures.len(), 1);
197 }
198 }
199
200 #[test]
201 fn test_decode_json_invalid_signature_fails() {
202 let message = create_sample_ui_message();
203 let ui_transaction = UiTransaction {
204 signatures: vec!["invalid_signature".to_string()],
205 message,
206 };
207 let encoded_transaction = EncodedTransaction::Json(ui_transaction);
208 let decoded = Transaction::decode(&encoded_transaction);
209 assert!(matches!(decoded, Err(DecodeError::ParseSignatureFailed(_)) | Err(DecodeError::UnsupportedEncoding)),
210 "Expected ParseSignatureFailed or UnsupportedEncoding error, got {:?}", decoded);
211 }
212
213 #[test]
214 fn test_decode_accounts_encoding_fails() {
215 let encoded_transaction = EncodedTransaction::Accounts(UiAccountsList { account_keys: vec![], signatures: vec![] });
216 let decoded = Transaction::decode(&encoded_transaction);
217 assert!(decoded.is_err());
218 assert_eq!(decoded.unwrap_err(), DecodeError::UnsupportedEncoding);
219 }
220
221 #[test]
222 fn test_decode_empty_base58_fails() {
223 let encoded_transaction = EncodedTransaction::Binary("".to_string(), TransactionBinaryEncoding::Base58);
224 let decoded = Transaction::decode(&encoded_transaction);
225 assert!(decoded.is_err());
226 }
227
228 #[test]
229 fn test_decode_empty_base64_fails() {
230 let encoded_transaction = EncodedTransaction::Binary("".to_string(), TransactionBinaryEncoding::Base64);
231 let decoded = Transaction::decode(&encoded_transaction);
232 assert!(decoded.is_err());
233 }
234}