cbe_program/message/versions/
mod.rs

1use {
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
23/// Bit mask that indicates whether a serialized message is versioned.
24pub const MESSAGE_VERSION_PREFIX: u8 = 0x80;
25
26/// Either a legacy message or a v0 message.
27///
28/// # Serialization
29///
30/// If the first bit is set, the remaining 7 bits will be used to determine
31/// which message version is serialized starting from version `0`. If the first
32/// is bit is not set, all bytes are used to encode the legacy `Message`
33/// format.
34#[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    /// Returns true if the account at the specified index signed this
71    /// message.
72    pub fn is_signer(&self, index: usize) -> bool {
73        index < usize::from(self.header().num_required_signatures)
74    }
75
76    /// Returns true if the account at the specified index is writable by the
77    /// instructions in this message. Since dynamically loaded addresses can't
78    /// have write locks demoted without loading addresses, this shouldn't be
79    /// used in the runtime.
80    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    /// Returns true if the account at the specified index is an input to some
88    /// program instruction in this message.
89    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    /// Returns true if the account at the specified index is not invoked as a
107    /// program or, if invoked, is passed to a program.
108    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    /// Program instructions that will be executed in sequence and committed in
127    /// one atomic transaction if all succeed.
128    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    /// Compute the blake3 hash of this transaction's message
140    pub fn hash(&self) -> Hash {
141        let message_bytes = self.serialize();
142        Self::hash_raw_message(&message_bytes)
143    }
144
145    /// Compute the blake3 hash of a raw transaction message
146    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                        // The remaining fields of the legacy Message struct after the first byte.
239                        #[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                                // will never happen since tuple length is always 2
253                                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                                    // will never happen since tuple length is always 2
273                                    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}