solana_block_decoder/message/
message.rs1use {
2 crate::{
3 errors::{
4 decode_error::DecodeError,
5 },
6 instruction::{
7 CompiledInstruction,
8 },
9 decodable::{
10 Decodable,
11 },
12 },
13 serde_derive::{Deserialize, Serialize},
14 solana_short_vec as short_vec,
15 solana_hash::{
16 Hash,
17 },
18 solana_message::{
19 Message as SolanaMessage,
20 MessageHeader,
21 },
22 solana_pubkey::Pubkey,
23 solana_transaction_status_client_types::{
24 UiMessage,
25 },
26 std::{
27 str::FromStr,
28 },
29};
30
31#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
32#[serde(rename_all = "camelCase")]
33pub struct Message {
34 pub header: MessageHeader,
37
38 #[serde(with = "short_vec")]
40 pub account_keys: Vec<Pubkey>,
41
42 pub recent_blockhash: Hash,
44
45 #[serde(with = "short_vec")]
48 pub instructions: Vec<CompiledInstruction>,
49}
50
51impl Decodable for Message {
52 type Encoded = UiMessage;
53 type Decoded = Message;
54
55 fn decode(encoded: &Self::Encoded) -> Result<Self::Decoded, DecodeError> {
56 match encoded {
57 UiMessage::Raw(raw_message) => {
58 let header = raw_message.header;
59 let account_keys: Result<Vec<Pubkey>, _> = raw_message
60 .account_keys
61 .iter()
62 .map(|key_str| key_str.parse())
63 .collect();
64 let account_keys = account_keys?;
65 let recent_blockhash = Hash::from_str(&raw_message.recent_blockhash)
66 .map_err(|err| DecodeError::ParseHashFailed(err))?;
67 let instructions: Vec<CompiledInstruction> = raw_message
68 .instructions
69 .iter()
70 .map(|ui_instruction| ui_instruction.clone().into() )
72 .collect();
73
74 Ok(Message {
75 header,
76 account_keys,
77 recent_blockhash,
78 instructions,
79 })
80 }
81 UiMessage::Parsed(_) => {
82 Err(DecodeError::UnsupportedEncoding)
83 }
84 }
85 }
86}
87
88
89impl From<Message> for SolanaMessage {
90 fn from(msg: Message) -> Self {
91 Self {
92 header: msg.header,
93 account_keys: msg.account_keys,
94 recent_blockhash: msg.recent_blockhash,
95 instructions: msg.instructions.into_iter().map(Into::into).collect(),
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use solana_transaction_status_client_types::{UiCompiledInstruction, UiMessage, UiRawMessage, UiParsedMessage};
104 use solana_hash::Hash;
105 use solana_pubkey::Pubkey;
106 use std::str::FromStr;
107 use serde_json;
108 use bs58;
109
110 fn sample_ui_message() -> UiMessage {
111 UiMessage::Raw(UiRawMessage {
112 header: MessageHeader {
113 num_required_signatures: 1,
114 num_readonly_signed_accounts: 0,
115 num_readonly_unsigned_accounts: 1,
116 },
117 account_keys: vec![Pubkey::new_unique().to_string()],
118 recent_blockhash: Hash::new_unique().to_string(),
119 instructions: vec![UiCompiledInstruction {
120 program_id_index: 0,
121 accounts: vec![0],
122 data: bs58::encode(b"test_data").into_string(),
123 stack_height: None,
124 }],
125 address_table_lookups: None,
126 })
127 }
128
129 #[test]
130 fn test_message_decoding_valid() {
131 let ui_message = sample_ui_message();
132 let decoded_message = Message::decode(&ui_message);
133 assert!(decoded_message.is_ok(), "Expected decoding to succeed");
134 }
135
136 #[test]
137 fn test_message_decoding_invalid_pubkey() {
138 let mut ui_message = sample_ui_message();
139 if let UiMessage::Raw(ref mut raw_message) = ui_message {
140 raw_message.account_keys[0] = "invalid_pubkey".to_string();
141 }
142
143 let decoded_message = Message::decode(&ui_message);
144 assert!(decoded_message.is_err(), "Expected decoding to fail due to invalid pubkey");
145 }
146
147 #[test]
148 fn test_message_decoding_invalid_hash() {
149 let mut ui_message = sample_ui_message();
150 if let UiMessage::Raw(ref mut raw_message) = ui_message {
151 raw_message.recent_blockhash = "invalid_hash".to_string();
152 }
153
154 let decoded_message = Message::decode(&ui_message);
155 assert!(decoded_message.is_err(), "Expected decoding to fail due to invalid hash");
156 }
157
158 #[test]
181 fn test_message_decoding_unsupported_encoding() {
182 let ui_message = UiMessage::Parsed(UiParsedMessage {
183 account_keys: vec![],
184 recent_blockhash: "".to_string(),
185 instructions: vec![],
186 address_table_lookups: None,
187 });
188 let decoded_message = Message::decode(&ui_message);
189 assert!(matches!(decoded_message, Err(DecodeError::UnsupportedEncoding)));
190 }
191
192 #[test]
193 fn test_message_conversion_to_solana_message() {
194 let ui_message = sample_ui_message();
195 let decoded_message = Message::decode(&ui_message).expect("Decoding failed");
196 let solana_message: SolanaMessage = decoded_message.clone().into();
197
198 assert_eq!(decoded_message.header, solana_message.header);
199 assert_eq!(decoded_message.account_keys, solana_message.account_keys);
200 assert_eq!(decoded_message.recent_blockhash, solana_message.recent_blockhash);
201 assert_eq!(decoded_message.instructions.len(), solana_message.instructions.len());
202 }
203
204 #[test]
205 fn test_message_with_multiple_account_keys() {
206 let mut ui_message = sample_ui_message();
207 if let UiMessage::Raw(ref mut raw_message) = ui_message {
208 raw_message.account_keys.push(Pubkey::new_unique().to_string());
209 }
210 let decoded_message = Message::decode(&ui_message);
211 assert!(decoded_message.is_ok(), "Expected decoding to succeed with multiple account keys");
212 assert_eq!(decoded_message.unwrap().account_keys.len(), 2);
213 }
214}