gemachain_program/message/
versions.rs1use {
2 crate::{
3 hash::Hash,
4 instruction::CompiledInstruction,
5 message::{v0, Message, MessageHeader},
6 pubkey::Pubkey,
7 sanitize::{Sanitize, SanitizeError},
8 short_vec,
9 },
10 serde::{
11 de::{self, Deserializer, SeqAccess, Visitor},
12 ser::{SerializeTuple, Serializer},
13 {Deserialize, Serialize},
14 },
15 std::fmt,
16};
17
18pub const MESSAGE_VERSION_PREFIX: u8 = 0x80;
20
21#[frozen_abi(digest = "qKNCqQpsBZYQxS9P3hVcFr8hAF4VnqV6ZBdC6KoUvHJ")]
30#[derive(Debug, PartialEq, Eq, Clone, AbiEnumVisitor, AbiExample)]
31pub enum VersionedMessage {
32 Legacy(Message),
33 V0(v0::Message),
34}
35
36impl VersionedMessage {
37 pub fn header(&self) -> &MessageHeader {
38 match self {
39 Self::Legacy(message) => &message.header,
40 Self::V0(message) => &message.header,
41 }
42 }
43
44 pub fn unmapped_keys(self) -> Vec<Pubkey> {
45 match self {
46 Self::Legacy(message) => message.account_keys,
47 Self::V0(message) => message.account_keys,
48 }
49 }
50
51 pub fn unmapped_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
52 match self {
53 Self::Legacy(message) => message.account_keys.iter(),
54 Self::V0(message) => message.account_keys.iter(),
55 }
56 }
57
58 pub fn unmapped_keys_len(&self) -> usize {
59 match self {
60 Self::Legacy(message) => message.account_keys.len(),
61 Self::V0(message) => message.account_keys.len(),
62 }
63 }
64
65 pub fn recent_blockhash(&self) -> &Hash {
66 match self {
67 Self::Legacy(message) => &message.recent_blockhash,
68 Self::V0(message) => &message.recent_blockhash,
69 }
70 }
71
72 pub fn set_recent_blockhash(&mut self, recent_blockhash: Hash) {
73 match self {
74 Self::Legacy(message) => message.recent_blockhash = recent_blockhash,
75 Self::V0(message) => message.recent_blockhash = recent_blockhash,
76 }
77 }
78
79 pub fn serialize(&self) -> Vec<u8> {
80 bincode::serialize(self).unwrap()
81 }
82
83 pub fn hash(&self) -> Hash {
85 let message_bytes = self.serialize();
86 Self::hash_raw_message(&message_bytes)
87 }
88
89 pub fn hash_raw_message(message_bytes: &[u8]) -> Hash {
91 use blake3::traits::digest::Digest;
92 use std::convert::TryFrom;
93 let mut hasher = blake3::Hasher::new();
94 hasher.update(b"gemachain-tx-message-v1");
95 hasher.update(message_bytes);
96 Hash(<[u8; crate::hash::HASH_BYTES]>::try_from(hasher.finalize().as_slice()).unwrap())
97 }
98}
99
100impl Default for VersionedMessage {
101 fn default() -> Self {
102 Self::Legacy(Message::default())
103 }
104}
105
106impl Sanitize for VersionedMessage {
107 fn sanitize(&self) -> Result<(), SanitizeError> {
108 match self {
109 Self::Legacy(message) => message.sanitize(),
110 Self::V0(message) => message.sanitize(),
111 }
112 }
113}
114
115impl Serialize for VersionedMessage {
116 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117 where
118 S: Serializer,
119 {
120 match self {
121 Self::Legacy(message) => {
122 let mut seq = serializer.serialize_tuple(1)?;
123 seq.serialize_element(message)?;
124 seq.end()
125 }
126 Self::V0(message) => {
127 let mut seq = serializer.serialize_tuple(2)?;
128 seq.serialize_element(&MESSAGE_VERSION_PREFIX)?;
129 seq.serialize_element(message)?;
130 seq.end()
131 }
132 }
133 }
134}
135
136enum MessagePrefix {
137 Legacy(u8),
138 Versioned(u8),
139}
140
141impl<'de> Deserialize<'de> for MessagePrefix {
142 fn deserialize<D>(deserializer: D) -> Result<MessagePrefix, D::Error>
143 where
144 D: Deserializer<'de>,
145 {
146 struct PrefixVisitor;
147
148 impl<'de> Visitor<'de> for PrefixVisitor {
149 type Value = MessagePrefix;
150
151 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
152 formatter.write_str("message prefix byte")
153 }
154
155 fn visit_u8<E>(self, byte: u8) -> Result<MessagePrefix, E> {
156 if byte & MESSAGE_VERSION_PREFIX != 0 {
157 Ok(MessagePrefix::Versioned(byte & !MESSAGE_VERSION_PREFIX))
158 } else {
159 Ok(MessagePrefix::Legacy(byte))
160 }
161 }
162 }
163
164 deserializer.deserialize_u8(PrefixVisitor)
165 }
166}
167
168impl<'de> Deserialize<'de> for VersionedMessage {
169 fn deserialize<D>(deserializer: D) -> Result<VersionedMessage, D::Error>
170 where
171 D: Deserializer<'de>,
172 {
173 struct MessageVisitor;
174
175 impl<'de> Visitor<'de> for MessageVisitor {
176 type Value = VersionedMessage;
177
178 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
179 formatter.write_str("message bytes")
180 }
181
182 fn visit_seq<A>(self, mut seq: A) -> Result<VersionedMessage, A::Error>
183 where
184 A: SeqAccess<'de>,
185 {
186 let prefix: MessagePrefix = seq
187 .next_element()?
188 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
189
190 match prefix {
191 MessagePrefix::Legacy(num_required_signatures) => {
192 #[derive(Serialize, Deserialize)]
194 struct RemainingLegacyMessage {
195 pub num_readonly_signed_accounts: u8,
196 pub num_readonly_unsigned_accounts: u8,
197 #[serde(with = "short_vec")]
198 pub account_keys: Vec<Pubkey>,
199 pub recent_blockhash: Hash,
200 #[serde(with = "short_vec")]
201 pub instructions: Vec<CompiledInstruction>,
202 }
203
204 let message: RemainingLegacyMessage =
205 seq.next_element()?.ok_or_else(|| {
206 de::Error::invalid_length(1, &self)
208 })?;
209
210 Ok(VersionedMessage::Legacy(Message {
211 header: MessageHeader {
212 num_required_signatures,
213 num_readonly_signed_accounts: message.num_readonly_signed_accounts,
214 num_readonly_unsigned_accounts: message
215 .num_readonly_unsigned_accounts,
216 },
217 account_keys: message.account_keys,
218 recent_blockhash: message.recent_blockhash,
219 instructions: message.instructions,
220 }))
221 }
222 MessagePrefix::Versioned(version) => {
223 if version == 0 {
224 Ok(VersionedMessage::V0(seq.next_element()?.ok_or_else(
225 || {
226 de::Error::invalid_length(1, &self)
228 },
229 )?))
230 } else {
231 Err(de::Error::invalid_value(
232 de::Unexpected::Unsigned(version as u64),
233 &"supported versions: [0]",
234 ))
235 }
236 }
237 }
238 }
239 }
240
241 deserializer.deserialize_tuple(2, MessageVisitor)
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use crate::{
249 instruction::{AccountMeta, Instruction},
250 message::v0::AddressMapIndexes,
251 };
252
253 #[test]
254 fn test_legacy_message_serialization() {
255 let program_id0 = Pubkey::new_unique();
256 let program_id1 = Pubkey::new_unique();
257 let id0 = Pubkey::new_unique();
258 let id1 = Pubkey::new_unique();
259 let id2 = Pubkey::new_unique();
260 let id3 = Pubkey::new_unique();
261 let instructions = vec![
262 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
263 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
264 Instruction::new_with_bincode(
265 program_id1,
266 &0,
267 vec![AccountMeta::new_readonly(id2, false)],
268 ),
269 Instruction::new_with_bincode(
270 program_id1,
271 &0,
272 vec![AccountMeta::new_readonly(id3, true)],
273 ),
274 ];
275
276 let mut message = Message::new(&instructions, Some(&id1));
277 message.recent_blockhash = Hash::new_unique();
278
279 let bytes1 = bincode::serialize(&message).unwrap();
280 let bytes2 = bincode::serialize(&VersionedMessage::Legacy(message.clone())).unwrap();
281
282 assert_eq!(bytes1, bytes2);
283
284 let message1: Message = bincode::deserialize(&bytes1).unwrap();
285 let message2: VersionedMessage = bincode::deserialize(&bytes2).unwrap();
286
287 if let VersionedMessage::Legacy(message2) = message2 {
288 assert_eq!(message, message1);
289 assert_eq!(message1, message2);
290 } else {
291 panic!("should deserialize to legacy message");
292 }
293 }
294
295 #[test]
296 fn test_versioned_message_serialization() {
297 let message = v0::Message {
298 header: MessageHeader {
299 num_required_signatures: 1,
300 num_readonly_signed_accounts: 0,
301 num_readonly_unsigned_accounts: 2,
302 },
303 recent_blockhash: Hash::new_unique(),
304 account_keys: vec![
305 Pubkey::new_unique(),
306 Pubkey::new_unique(),
307 Pubkey::new_unique(),
308 ],
309 address_map_indexes: vec![
310 AddressMapIndexes {
311 writable: vec![1],
312 readonly: vec![0],
313 },
314 AddressMapIndexes {
315 writable: vec![0],
316 readonly: vec![1],
317 },
318 ],
319 instructions: vec![CompiledInstruction {
320 program_id_index: 1,
321 accounts: vec![0],
322 data: vec![],
323 }],
324 };
325
326 let bytes = bincode::serialize(&VersionedMessage::V0(message.clone())).unwrap();
327 let message_from_bytes: VersionedMessage = bincode::deserialize(&bytes).unwrap();
328
329 if let VersionedMessage::V0(message_from_bytes) = message_from_bytes {
330 assert_eq!(message, message_from_bytes);
331 } else {
332 panic!("should deserialize to versioned message");
333 }
334 }
335}