1use crate::types::{CompiledInstruction, MessageAddressTableLookup, Pubkey};
2use borsh::{BorshDeserialize, BorshSerialize};
3
4use serde::{Deserialize, Serialize};
5
6fn serialize_message_body(
9 header: &MessageHeader,
10 account_keys: &[Pubkey],
11 recent_blockhash: &[u8; 32],
12 instructions: &[CompiledInstruction],
13) -> Result<Vec<u8>, String> {
14 let mut bytes = Vec::new();
15
16 bytes.push(header.num_required_signatures);
18 bytes.push(header.num_readonly_signed_accounts);
19 bytes.push(header.num_readonly_unsigned_accounts);
20
21 let len = crate::encode_length_to_compact_u16_bytes(account_keys.len())?;
23 bytes.extend_from_slice(&len);
24 for pubkey in account_keys {
25 bytes.extend_from_slice(pubkey.as_bytes());
26 }
27
28 bytes.extend_from_slice(recent_blockhash);
30
31 let len = crate::encode_length_to_compact_u16_bytes(instructions.len())?;
33 bytes.extend_from_slice(&len);
34 for ix in instructions {
35 bytes.push(ix.program_id_index);
36
37 let len = crate::encode_length_to_compact_u16_bytes(ix.accounts.len())?;
38 bytes.extend_from_slice(&len);
39 bytes.extend_from_slice(&ix.accounts);
40
41 let len = crate::encode_length_to_compact_u16_bytes(ix.data.len())?;
42 bytes.extend_from_slice(&len);
43 bytes.extend_from_slice(&ix.data);
44 }
45
46 Ok(bytes)
47}
48
49#[derive(Debug, Clone, PartialEq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
51pub struct MessageHeader {
52 pub num_required_signatures: u8,
54 pub num_readonly_signed_accounts: u8,
56 pub num_readonly_unsigned_accounts: u8,
58}
59
60#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
62pub struct LegacyMessage {
63 pub header: MessageHeader,
65 pub account_keys: Vec<Pubkey>,
67 pub recent_blockhash: [u8; 32],
69 pub instructions: Vec<CompiledInstruction>,
71}
72
73impl LegacyMessage {
74 pub fn serialize_for_signing(&self) -> Result<Vec<u8>, String> {
75 serialize_message_body(
76 &self.header,
77 &self.account_keys,
78 &self.recent_blockhash,
79 &self.instructions,
80 )
81 }
82}
83
84#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
86pub struct VersionedMessageV0 {
87 pub header: MessageHeader,
89 pub account_keys: Vec<Pubkey>,
91 pub recent_blockhash: [u8; 32],
93 pub instructions: Vec<CompiledInstruction>,
95 pub address_table_lookups: Vec<MessageAddressTableLookup>,
97}
98
99impl VersionedMessageV0 {
100 pub fn serialize_for_signing(&self) -> Result<Vec<u8>, String> {
104 let mut bytes = Vec::new();
105
106 bytes.push(0x80);
108
109 let body = serialize_message_body(
111 &self.header,
112 &self.account_keys,
113 &self.recent_blockhash,
114 &self.instructions,
115 )?;
116 bytes.extend_from_slice(&body);
117
118 let lookup_len =
120 crate::encode_length_to_compact_u16_bytes(self.address_table_lookups.len())?;
121 bytes.extend_from_slice(&lookup_len);
122
123 for lookup in &self.address_table_lookups {
124 bytes.extend_from_slice(lookup.account_key.as_bytes());
125
126 let writable_len =
127 crate::encode_length_to_compact_u16_bytes(lookup.writable_indexes.len())?;
128 bytes.extend_from_slice(&writable_len);
129 bytes.extend_from_slice(&lookup.writable_indexes);
130
131 let readonly_len =
132 crate::encode_length_to_compact_u16_bytes(lookup.readonly_indexes.len())?;
133 bytes.extend_from_slice(&readonly_len);
134 bytes.extend_from_slice(&lookup.readonly_indexes);
135 }
136
137 Ok(bytes)
138 }
139}
140
141#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
143pub enum VersionedMessage {
144 Legacy(LegacyMessage),
146 V0(VersionedMessageV0),
148}
149
150#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
152pub struct Message {
153 pub header: MessageHeader,
155 pub account_keys: Vec<Pubkey>,
157 pub recent_blockhash: [u8; 32],
159 pub instructions: Vec<CompiledInstruction>,
161}
162
163impl Message {
164 pub fn new(
166 header: MessageHeader,
167 account_keys: Vec<Pubkey>,
168 recent_blockhash: [u8; 32],
169 instructions: Vec<CompiledInstruction>,
170 ) -> Self {
171 Self {
172 header,
173 account_keys,
174 recent_blockhash,
175 instructions,
176 }
177 }
178
179 pub fn num_required_signatures(&self) -> u8 {
181 self.header.num_required_signatures
182 }
183
184 pub fn num_readonly_signed_accounts(&self) -> u8 {
186 self.header.num_readonly_signed_accounts
187 }
188
189 pub fn num_readonly_unsigned_accounts(&self) -> u8 {
191 self.header.num_readonly_unsigned_accounts
192 }
193
194 pub fn serialize_for_signing(&self) -> Result<Vec<u8>, String> {
197 serialize_message_body(
198 &self.header,
199 &self.account_keys,
200 &self.recent_blockhash,
201 &self.instructions,
202 )
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::types::{CompiledInstruction, Pubkey};
210
211 #[test]
212 fn test_message() {
213 let header = MessageHeader {
214 num_required_signatures: 1,
215 num_readonly_signed_accounts: 0,
216 num_readonly_unsigned_accounts: 1,
217 };
218 let account_keys = vec![Pubkey::new([0; 32]), Pubkey::new([1; 32])];
219 let recent_blockhash = [0u8; 32];
220 let instructions = vec![CompiledInstruction {
221 program_id_index: 1,
222 accounts: vec![0],
223 data: vec![],
224 }];
225
226 let message = Message::new(header, account_keys, recent_blockhash, instructions);
227
228 assert_eq!(message.num_required_signatures(), 1);
229 assert_eq!(message.num_readonly_signed_accounts(), 0);
230 assert_eq!(message.num_readonly_unsigned_accounts(), 1);
231 }
232
233 #[test]
234 fn test_versioned_message() {
235 let header = MessageHeader {
236 num_required_signatures: 1,
237 num_readonly_signed_accounts: 0,
238 num_readonly_unsigned_accounts: 1,
239 };
240 let account_keys = vec![Pubkey::new([0; 32]), Pubkey::new([1; 32])];
241 let recent_blockhash = [0u8; 32];
242 let instructions = vec![CompiledInstruction {
243 program_id_index: 1,
244 accounts: vec![0],
245 data: vec![],
246 }];
247
248 let address_table_lookups = vec![MessageAddressTableLookup::new(
250 Pubkey::new([2; 32]),
251 vec![0, 1], vec![2], )];
254
255 let v0_message = VersionedMessageV0 {
256 header: header.clone(),
257 account_keys: account_keys.clone(),
258 recent_blockhash,
259 instructions: instructions.clone(),
260 address_table_lookups,
261 };
262
263 let versioned_message = VersionedMessage::V0(v0_message);
265
266 match versioned_message {
268 VersionedMessage::Legacy(_) => panic!("Expected V0 message"),
269 VersionedMessage::V0(msg) => {
270 assert_eq!(msg.header.num_required_signatures, 1);
271 assert_eq!(msg.account_keys.len(), 2);
272 assert_eq!(msg.instructions.len(), 1);
273 assert_eq!(msg.address_table_lookups.len(), 1);
274 assert_eq!(
275 msg.address_table_lookups[0].account_key,
276 Pubkey::new([2; 32])
277 );
278 assert_eq!(msg.address_table_lookups[0].writable_indexes, vec![0, 1]);
279 assert_eq!(msg.address_table_lookups[0].readonly_indexes, vec![2]);
280 }
281 }
282 }
283}