cbe_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, 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, require_static_program_ids: bool) -> Result<(), SanitizeError> {
43 match self {
44 Self::Legacy(message) => message.sanitize(),
45 Self::V0(message) => message.sanitize(require_static_program_ids),
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"cbe-tx-message-v1");
150 hasher.update(message_bytes);
151 Hash(<[u8; crate::hash::HASH_BYTES]>::try_from(hasher.finalize().as_slice()).unwrap())
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_u8<E>(self, byte: u8) -> Result<MessagePrefix, E> {
202 if byte & MESSAGE_VERSION_PREFIX != 0 {
203 Ok(MessagePrefix::Versioned(byte & !MESSAGE_VERSION_PREFIX))
204 } else {
205 Ok(MessagePrefix::Legacy(byte))
206 }
207 }
208 }
209
210 deserializer.deserialize_u8(PrefixVisitor)
211 }
212}
213
214impl<'de> Deserialize<'de> for VersionedMessage {
215 fn deserialize<D>(deserializer: D) -> Result<VersionedMessage, D::Error>
216 where
217 D: Deserializer<'de>,
218 {
219 struct MessageVisitor;
220
221 impl<'de> Visitor<'de> for MessageVisitor {
222 type Value = VersionedMessage;
223
224 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
225 formatter.write_str("message bytes")
226 }
227
228 fn visit_seq<A>(self, mut seq: A) -> Result<VersionedMessage, A::Error>
229 where
230 A: SeqAccess<'de>,
231 {
232 let prefix: MessagePrefix = seq
233 .next_element()?
234 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
235
236 match prefix {
237 MessagePrefix::Legacy(num_required_signatures) => {
238 #[derive(Serialize, Deserialize)]
240 struct RemainingLegacyMessage {
241 pub num_readonly_signed_accounts: u8,
242 pub num_readonly_unsigned_accounts: u8,
243 #[serde(with = "short_vec")]
244 pub account_keys: Vec<Pubkey>,
245 pub recent_blockhash: Hash,
246 #[serde(with = "short_vec")]
247 pub instructions: Vec<CompiledInstruction>,
248 }
249
250 let message: RemainingLegacyMessage =
251 seq.next_element()?.ok_or_else(|| {
252 de::Error::invalid_length(1, &self)
254 })?;
255
256 Ok(VersionedMessage::Legacy(LegacyMessage {
257 header: MessageHeader {
258 num_required_signatures,
259 num_readonly_signed_accounts: message.num_readonly_signed_accounts,
260 num_readonly_unsigned_accounts: message
261 .num_readonly_unsigned_accounts,
262 },
263 account_keys: message.account_keys,
264 recent_blockhash: message.recent_blockhash,
265 instructions: message.instructions,
266 }))
267 }
268 MessagePrefix::Versioned(version) => {
269 if version == 0 {
270 Ok(VersionedMessage::V0(seq.next_element()?.ok_or_else(
271 || {
272 de::Error::invalid_length(1, &self)
274 },
275 )?))
276 } else {
277 Err(de::Error::invalid_value(
278 de::Unexpected::Unsigned(version as u64),
279 &"supported versions: [0]",
280 ))
281 }
282 }
283 }
284 }
285 }
286
287 deserializer.deserialize_tuple(2, MessageVisitor)
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use {
294 super::*,
295 crate::{
296 instruction::{AccountMeta, Instruction},
297 message::v0::MessageAddressTableLookup,
298 },
299 };
300
301 #[test]
302 fn test_legacy_message_serialization() {
303 let program_id0 = Pubkey::new_unique();
304 let program_id1 = Pubkey::new_unique();
305 let id0 = Pubkey::new_unique();
306 let id1 = Pubkey::new_unique();
307 let id2 = Pubkey::new_unique();
308 let id3 = Pubkey::new_unique();
309 let instructions = vec![
310 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
311 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
312 Instruction::new_with_bincode(
313 program_id1,
314 &0,
315 vec![AccountMeta::new_readonly(id2, false)],
316 ),
317 Instruction::new_with_bincode(
318 program_id1,
319 &0,
320 vec![AccountMeta::new_readonly(id3, true)],
321 ),
322 ];
323
324 let mut message = LegacyMessage::new(&instructions, Some(&id1));
325 message.recent_blockhash = Hash::new_unique();
326
327 let bytes1 = bincode::serialize(&message).unwrap();
328 let bytes2 = bincode::serialize(&VersionedMessage::Legacy(message.clone())).unwrap();
329
330 assert_eq!(bytes1, bytes2);
331
332 let message1: LegacyMessage = bincode::deserialize(&bytes1).unwrap();
333 let message2: VersionedMessage = bincode::deserialize(&bytes2).unwrap();
334
335 if let VersionedMessage::Legacy(message2) = message2 {
336 assert_eq!(message, message1);
337 assert_eq!(message1, message2);
338 } else {
339 panic!("should deserialize to legacy message");
340 }
341 }
342
343 #[test]
344 fn test_versioned_message_serialization() {
345 let message = v0::Message {
346 header: MessageHeader {
347 num_required_signatures: 1,
348 num_readonly_signed_accounts: 0,
349 num_readonly_unsigned_accounts: 0,
350 },
351 recent_blockhash: Hash::new_unique(),
352 account_keys: vec![Pubkey::new_unique()],
353 address_table_lookups: vec![
354 MessageAddressTableLookup {
355 account_key: Pubkey::new_unique(),
356 writable_indexes: vec![1],
357 readonly_indexes: vec![0],
358 },
359 MessageAddressTableLookup {
360 account_key: Pubkey::new_unique(),
361 writable_indexes: vec![0],
362 readonly_indexes: vec![1],
363 },
364 ],
365 instructions: vec![CompiledInstruction {
366 program_id_index: 1,
367 accounts: vec![0, 2, 3, 4],
368 data: vec![],
369 }],
370 };
371
372 let bytes = bincode::serialize(&VersionedMessage::V0(message.clone())).unwrap();
373 let message_from_bytes: VersionedMessage = bincode::deserialize(&bytes).unwrap();
374
375 if let VersionedMessage::V0(message_from_bytes) = message_from_bytes {
376 assert_eq!(message, message_from_bytes);
377 } else {
378 panic!("should deserialize to versioned message");
379 }
380 }
381}