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