solana_program/message/versions/
mod.rs1use {
2 crate::{
3 hash::Hash,
4 instruction::CompiledInstruction,
5 message::{legacy::Message as LegacyMessage, v0::MessageAddressTableLookup, MessageHeader},
6 pubkey::Pubkey,
7 },
8 serde::{
9 de::{self, Deserializer, SeqAccess, Unexpected, Visitor},
10 ser::{SerializeTuple, Serializer},
11 },
12 serde_derive::{Deserialize, Serialize},
13 solana_hash::HASH_BYTES,
14 solana_sanitize::{Sanitize, SanitizeError},
15 solana_short_vec as short_vec,
16 std::{collections::HashSet, fmt},
17};
18
19mod sanitized;
20pub mod v0;
21
22pub use sanitized::*;
23
24pub const MESSAGE_VERSION_PREFIX: u8 = 0x80;
26
27#[cfg_attr(
36 feature = "frozen-abi",
37 frozen_abi(digest = "EjjHMjAnRrd86DuTgysFXRicMiAQv3vTvzRzcMJCjYfC"),
38 derive(AbiEnumVisitor, AbiExample)
39)]
40#[derive(Debug, PartialEq, Eq, Clone)]
41pub enum VersionedMessage {
42 Legacy(LegacyMessage),
43 V0(v0::Message),
44}
45
46impl VersionedMessage {
47 pub fn sanitize(&self) -> Result<(), SanitizeError> {
48 match self {
49 Self::Legacy(message) => message.sanitize(),
50 Self::V0(message) => message.sanitize(),
51 }
52 }
53
54 pub fn header(&self) -> &MessageHeader {
55 match self {
56 Self::Legacy(message) => &message.header,
57 Self::V0(message) => &message.header,
58 }
59 }
60
61 pub fn static_account_keys(&self) -> &[Pubkey] {
62 match self {
63 Self::Legacy(message) => &message.account_keys,
64 Self::V0(message) => &message.account_keys,
65 }
66 }
67
68 pub fn address_table_lookups(&self) -> Option<&[MessageAddressTableLookup]> {
69 match self {
70 Self::Legacy(_) => None,
71 Self::V0(message) => Some(&message.address_table_lookups),
72 }
73 }
74
75 pub fn is_signer(&self, index: usize) -> bool {
78 index < usize::from(self.header().num_required_signatures)
79 }
80
81 pub fn is_maybe_writable(
86 &self,
87 index: usize,
88 reserved_account_keys: Option<&HashSet<Pubkey>>,
89 ) -> bool {
90 match self {
91 Self::Legacy(message) => message.is_maybe_writable(index, reserved_account_keys),
92 Self::V0(message) => message.is_maybe_writable(index, reserved_account_keys),
93 }
94 }
95
96 #[deprecated(since = "2.0.0", note = "Please use `is_instruction_account` instead")]
97 pub fn is_key_passed_to_program(&self, key_index: usize) -> bool {
98 self.is_instruction_account(key_index)
99 }
100
101 fn is_instruction_account(&self, key_index: usize) -> bool {
104 if let Ok(key_index) = u8::try_from(key_index) {
105 self.instructions()
106 .iter()
107 .any(|ix| ix.accounts.contains(&key_index))
108 } else {
109 false
110 }
111 }
112
113 pub fn is_invoked(&self, key_index: usize) -> bool {
114 match self {
115 Self::Legacy(message) => message.is_key_called_as_program(key_index),
116 Self::V0(message) => message.is_key_called_as_program(key_index),
117 }
118 }
119
120 pub fn is_non_loader_key(&self, key_index: usize) -> bool {
123 !self.is_invoked(key_index) || self.is_instruction_account(key_index)
124 }
125
126 pub fn recent_blockhash(&self) -> &Hash {
127 match self {
128 Self::Legacy(message) => &message.recent_blockhash,
129 Self::V0(message) => &message.recent_blockhash,
130 }
131 }
132
133 pub fn set_recent_blockhash(&mut self, recent_blockhash: Hash) {
134 match self {
135 Self::Legacy(message) => message.recent_blockhash = recent_blockhash,
136 Self::V0(message) => message.recent_blockhash = recent_blockhash,
137 }
138 }
139
140 pub fn instructions(&self) -> &[CompiledInstruction] {
143 match self {
144 Self::Legacy(message) => &message.instructions,
145 Self::V0(message) => &message.instructions,
146 }
147 }
148
149 pub fn serialize(&self) -> Vec<u8> {
150 bincode::serialize(self).unwrap()
151 }
152
153 pub fn hash(&self) -> Hash {
155 let message_bytes = self.serialize();
156 Self::hash_raw_message(&message_bytes)
157 }
158
159 pub fn hash_raw_message(message_bytes: &[u8]) -> Hash {
161 use blake3::traits::digest::Digest;
162 let mut hasher = blake3::Hasher::new();
163 hasher.update(b"solana-tx-message-v1");
164 hasher.update(message_bytes);
165 let hash_bytes: [u8; HASH_BYTES] = hasher.finalize().into();
166 hash_bytes.into()
167 }
168}
169
170impl Default for VersionedMessage {
171 fn default() -> Self {
172 Self::Legacy(LegacyMessage::default())
173 }
174}
175
176impl serde::Serialize for VersionedMessage {
177 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
178 where
179 S: Serializer,
180 {
181 match self {
182 Self::Legacy(message) => {
183 let mut seq = serializer.serialize_tuple(1)?;
184 seq.serialize_element(message)?;
185 seq.end()
186 }
187 Self::V0(message) => {
188 let mut seq = serializer.serialize_tuple(2)?;
189 seq.serialize_element(&MESSAGE_VERSION_PREFIX)?;
190 seq.serialize_element(message)?;
191 seq.end()
192 }
193 }
194 }
195}
196
197enum MessagePrefix {
198 Legacy(u8),
199 Versioned(u8),
200}
201
202impl<'de> serde::Deserialize<'de> for MessagePrefix {
203 fn deserialize<D>(deserializer: D) -> Result<MessagePrefix, D::Error>
204 where
205 D: Deserializer<'de>,
206 {
207 struct PrefixVisitor;
208
209 impl<'de> Visitor<'de> for PrefixVisitor {
210 type Value = MessagePrefix;
211
212 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
213 formatter.write_str("message prefix byte")
214 }
215
216 fn visit_u64<E: de::Error>(self, value: u64) -> Result<MessagePrefix, E> {
221 if value > u8::MAX as u64 {
222 Err(de::Error::invalid_type(Unexpected::Unsigned(value), &self))?;
223 }
224
225 let byte = value as u8;
226 if byte & MESSAGE_VERSION_PREFIX != 0 {
227 Ok(MessagePrefix::Versioned(byte & !MESSAGE_VERSION_PREFIX))
228 } else {
229 Ok(MessagePrefix::Legacy(byte))
230 }
231 }
232 }
233
234 deserializer.deserialize_u8(PrefixVisitor)
235 }
236}
237
238impl<'de> serde::Deserialize<'de> for VersionedMessage {
239 fn deserialize<D>(deserializer: D) -> Result<VersionedMessage, D::Error>
240 where
241 D: Deserializer<'de>,
242 {
243 struct MessageVisitor;
244
245 impl<'de> Visitor<'de> for MessageVisitor {
246 type Value = VersionedMessage;
247
248 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
249 formatter.write_str("message bytes")
250 }
251
252 fn visit_seq<A>(self, mut seq: A) -> Result<VersionedMessage, A::Error>
253 where
254 A: SeqAccess<'de>,
255 {
256 let prefix: MessagePrefix = seq
257 .next_element()?
258 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
259
260 match prefix {
261 MessagePrefix::Legacy(num_required_signatures) => {
262 #[derive(Serialize, Deserialize)]
264 struct RemainingLegacyMessage {
265 pub num_readonly_signed_accounts: u8,
266 pub num_readonly_unsigned_accounts: u8,
267 #[serde(with = "short_vec")]
268 pub account_keys: Vec<Pubkey>,
269 pub recent_blockhash: Hash,
270 #[serde(with = "short_vec")]
271 pub instructions: Vec<CompiledInstruction>,
272 }
273
274 let message: RemainingLegacyMessage =
275 seq.next_element()?.ok_or_else(|| {
276 de::Error::invalid_length(1, &self)
278 })?;
279
280 Ok(VersionedMessage::Legacy(LegacyMessage {
281 header: MessageHeader {
282 num_required_signatures,
283 num_readonly_signed_accounts: message.num_readonly_signed_accounts,
284 num_readonly_unsigned_accounts: message
285 .num_readonly_unsigned_accounts,
286 },
287 account_keys: message.account_keys,
288 recent_blockhash: message.recent_blockhash,
289 instructions: message.instructions,
290 }))
291 }
292 MessagePrefix::Versioned(version) => {
293 match version {
294 0 => {
295 Ok(VersionedMessage::V0(seq.next_element()?.ok_or_else(
296 || {
297 de::Error::invalid_length(1, &self)
299 },
300 )?))
301 }
302 127 => {
303 Err(de::Error::custom("off-chain messages are not accepted"))
308 }
309 _ => Err(de::Error::invalid_value(
310 de::Unexpected::Unsigned(version as u64),
311 &"a valid transaction message version",
312 )),
313 }
314 }
315 }
316 }
317 }
318
319 deserializer.deserialize_tuple(2, MessageVisitor)
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use {
326 super::*,
327 crate::{
328 instruction::{AccountMeta, Instruction},
329 message::v0::MessageAddressTableLookup,
330 },
331 };
332
333 #[test]
334 fn test_legacy_message_serialization() {
335 let program_id0 = Pubkey::new_unique();
336 let program_id1 = Pubkey::new_unique();
337 let id0 = Pubkey::new_unique();
338 let id1 = Pubkey::new_unique();
339 let id2 = Pubkey::new_unique();
340 let id3 = Pubkey::new_unique();
341 let instructions = vec![
342 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
343 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
344 Instruction::new_with_bincode(
345 program_id1,
346 &0,
347 vec![AccountMeta::new_readonly(id2, false)],
348 ),
349 Instruction::new_with_bincode(
350 program_id1,
351 &0,
352 vec![AccountMeta::new_readonly(id3, true)],
353 ),
354 ];
355
356 let mut message = LegacyMessage::new(&instructions, Some(&id1));
357 message.recent_blockhash = Hash::new_unique();
358 let wrapped_message = VersionedMessage::Legacy(message.clone());
359
360 {
362 let bytes = bincode::serialize(&message).unwrap();
363 assert_eq!(bytes, bincode::serialize(&wrapped_message).unwrap());
364
365 let message_from_bytes: LegacyMessage = bincode::deserialize(&bytes).unwrap();
366 let wrapped_message_from_bytes: VersionedMessage =
367 bincode::deserialize(&bytes).unwrap();
368
369 assert_eq!(message, message_from_bytes);
370 assert_eq!(wrapped_message, wrapped_message_from_bytes);
371 }
372
373 {
375 let string = serde_json::to_string(&message).unwrap();
376 let message_from_string: LegacyMessage = serde_json::from_str(&string).unwrap();
377 assert_eq!(message, message_from_string);
378 }
379 }
380
381 #[test]
382 fn test_versioned_message_serialization() {
383 let message = VersionedMessage::V0(v0::Message {
384 header: MessageHeader {
385 num_required_signatures: 1,
386 num_readonly_signed_accounts: 0,
387 num_readonly_unsigned_accounts: 0,
388 },
389 recent_blockhash: Hash::new_unique(),
390 account_keys: vec![Pubkey::new_unique()],
391 address_table_lookups: vec![
392 MessageAddressTableLookup {
393 account_key: Pubkey::new_unique(),
394 writable_indexes: vec![1],
395 readonly_indexes: vec![0],
396 },
397 MessageAddressTableLookup {
398 account_key: Pubkey::new_unique(),
399 writable_indexes: vec![0],
400 readonly_indexes: vec![1],
401 },
402 ],
403 instructions: vec![CompiledInstruction {
404 program_id_index: 1,
405 accounts: vec![0, 2, 3, 4],
406 data: vec![],
407 }],
408 });
409
410 let bytes = bincode::serialize(&message).unwrap();
411 let message_from_bytes: VersionedMessage = bincode::deserialize(&bytes).unwrap();
412 assert_eq!(message, message_from_bytes);
413
414 let string = serde_json::to_string(&message).unwrap();
415 let message_from_string: VersionedMessage = serde_json::from_str(&string).unwrap();
416 assert_eq!(message, message_from_string);
417 }
418}