gemachain_program/message/
v0.rs1use crate::{
2 hash::Hash,
3 instruction::CompiledInstruction,
4 message::{MessageHeader, MESSAGE_VERSION_PREFIX},
5 pubkey::Pubkey,
6 sanitize::{Sanitize, SanitizeError},
7 short_vec,
8};
9
10#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)]
13#[serde(rename_all = "camelCase")]
14pub struct AddressMapIndexes {
15 #[serde(with = "short_vec")]
16 pub writable: Vec<u8>,
17 #[serde(with = "short_vec")]
18 pub readonly: Vec<u8>,
19}
20
21#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)]
24#[serde(rename_all = "camelCase")]
25pub struct Message {
26 pub header: MessageHeader,
28
29 #[serde(with = "short_vec")]
31 pub account_keys: Vec<Pubkey>,
32
33 #[serde(with = "short_vec")]
41 pub address_map_indexes: Vec<AddressMapIndexes>,
42
43 pub recent_blockhash: Hash,
45
46 #[serde(with = "short_vec")]
56 pub instructions: Vec<CompiledInstruction>,
57}
58
59impl Sanitize for Message {
60 fn sanitize(&self) -> Result<(), SanitizeError> {
61 if usize::from(self.header.num_required_signatures)
64 .saturating_add(usize::from(self.header.num_readonly_unsigned_accounts))
65 > self.account_keys.len()
66 {
67 return Err(SanitizeError::IndexOutOfBounds);
68 }
69
70 if self.header.num_readonly_signed_accounts >= self.header.num_required_signatures {
72 return Err(SanitizeError::IndexOutOfBounds);
73 }
74
75 let num_address_map_indexes = self.address_map_indexes.len();
77 if num_address_map_indexes > usize::from(self.header.num_readonly_unsigned_accounts) {
78 return Err(SanitizeError::IndexOutOfBounds);
79 }
80
81 let mut num_loaded_accounts = self.account_keys.len();
83 for indexes in &self.address_map_indexes {
84 let num_loaded_map_entries = indexes
85 .writable
86 .len()
87 .saturating_add(indexes.readonly.len());
88
89 if num_loaded_map_entries == 0 {
90 return Err(SanitizeError::InvalidValue);
91 }
92
93 num_loaded_accounts = num_loaded_accounts.saturating_add(num_loaded_map_entries);
94 }
95
96 if num_loaded_accounts > 256 {
99 return Err(SanitizeError::IndexOutOfBounds);
100 }
101
102 for ci in &self.instructions {
103 if usize::from(ci.program_id_index) >= num_loaded_accounts {
104 return Err(SanitizeError::IndexOutOfBounds);
105 }
106 if ci.program_id_index == 0 {
108 return Err(SanitizeError::IndexOutOfBounds);
109 }
110 for ai in &ci.accounts {
111 if usize::from(*ai) >= num_loaded_accounts {
112 return Err(SanitizeError::IndexOutOfBounds);
113 }
114 }
115 }
116
117 Ok(())
118 }
119}
120
121impl Message {
122 pub fn serialize(&self) -> Vec<u8> {
124 bincode::serialize(&(MESSAGE_VERSION_PREFIX, self)).unwrap()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::message::VersionedMessage;
132
133 fn simple_message() -> Message {
134 Message {
135 header: MessageHeader {
136 num_required_signatures: 1,
137 num_readonly_signed_accounts: 0,
138 num_readonly_unsigned_accounts: 1,
139 },
140 account_keys: vec![Pubkey::new_unique(), Pubkey::new_unique()],
141 address_map_indexes: vec![AddressMapIndexes {
142 writable: vec![],
143 readonly: vec![0],
144 }],
145 ..Message::default()
146 }
147 }
148
149 fn two_map_message() -> Message {
150 Message {
151 header: MessageHeader {
152 num_required_signatures: 1,
153 num_readonly_signed_accounts: 0,
154 num_readonly_unsigned_accounts: 2,
155 },
156 account_keys: vec![
157 Pubkey::new_unique(),
158 Pubkey::new_unique(),
159 Pubkey::new_unique(),
160 ],
161 address_map_indexes: vec![
162 AddressMapIndexes {
163 writable: vec![1],
164 readonly: vec![0],
165 },
166 AddressMapIndexes {
167 writable: vec![0],
168 readonly: vec![1],
169 },
170 ],
171 ..Message::default()
172 }
173 }
174
175 #[test]
176 fn test_sanitize_account_indices() {
177 assert!(Message {
178 account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(),
179 address_map_indexes: vec![],
180 instructions: vec![CompiledInstruction {
181 program_id_index: 1,
182 accounts: vec![u8::MAX],
183 data: vec![],
184 }],
185 ..simple_message()
186 }
187 .sanitize()
188 .is_ok());
189
190 assert!(Message {
191 account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(),
192 address_map_indexes: vec![],
193 instructions: vec![CompiledInstruction {
194 program_id_index: 1,
195 accounts: vec![u8::MAX],
196 data: vec![],
197 }],
198 ..simple_message()
199 }
200 .sanitize()
201 .is_err());
202
203 assert!(Message {
204 account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(),
205 instructions: vec![CompiledInstruction {
206 program_id_index: 1,
207 accounts: vec![u8::MAX],
208 data: vec![],
209 }],
210 ..simple_message()
211 }
212 .sanitize()
213 .is_ok());
214
215 assert!(Message {
216 account_keys: (0..u8::MAX - 1).map(|_| Pubkey::new_unique()).collect(),
217 instructions: vec![CompiledInstruction {
218 program_id_index: 1,
219 accounts: vec![u8::MAX],
220 data: vec![],
221 }],
222 ..simple_message()
223 }
224 .sanitize()
225 .is_err());
226
227 assert!(Message {
228 address_map_indexes: vec![
229 AddressMapIndexes {
230 writable: (0..200).step_by(2).collect(),
231 readonly: (1..200).step_by(2).collect(),
232 },
233 AddressMapIndexes {
234 writable: (0..53).step_by(2).collect(),
235 readonly: (1..53).step_by(2).collect(),
236 },
237 ],
238 instructions: vec![CompiledInstruction {
239 program_id_index: 1,
240 accounts: vec![u8::MAX],
241 data: vec![],
242 }],
243 ..two_map_message()
244 }
245 .sanitize()
246 .is_ok());
247
248 assert!(Message {
249 address_map_indexes: vec![
250 AddressMapIndexes {
251 writable: (0..200).step_by(2).collect(),
252 readonly: (1..200).step_by(2).collect(),
253 },
254 AddressMapIndexes {
255 writable: (0..52).step_by(2).collect(),
256 readonly: (1..52).step_by(2).collect(),
257 },
258 ],
259 instructions: vec![CompiledInstruction {
260 program_id_index: 1,
261 accounts: vec![u8::MAX],
262 data: vec![],
263 }],
264 ..two_map_message()
265 }
266 .sanitize()
267 .is_err());
268 }
269
270 #[test]
271 fn test_sanitize_excessive_loaded_accounts() {
272 assert!(Message {
273 account_keys: (0..=u8::MAX).map(|_| Pubkey::new_unique()).collect(),
274 address_map_indexes: vec![],
275 ..simple_message()
276 }
277 .sanitize()
278 .is_ok());
279
280 assert!(Message {
281 account_keys: (0..257).map(|_| Pubkey::new_unique()).collect(),
282 address_map_indexes: vec![],
283 ..simple_message()
284 }
285 .sanitize()
286 .is_err());
287
288 assert!(Message {
289 account_keys: (0..u8::MAX).map(|_| Pubkey::new_unique()).collect(),
290 ..simple_message()
291 }
292 .sanitize()
293 .is_ok());
294
295 assert!(Message {
296 account_keys: (0..256).map(|_| Pubkey::new_unique()).collect(),
297 ..simple_message()
298 }
299 .sanitize()
300 .is_err());
301
302 assert!(Message {
303 address_map_indexes: vec![
304 AddressMapIndexes {
305 writable: (0..200).step_by(2).collect(),
306 readonly: (1..200).step_by(2).collect(),
307 },
308 AddressMapIndexes {
309 writable: (0..53).step_by(2).collect(),
310 readonly: (1..53).step_by(2).collect(),
311 }
312 ],
313 ..two_map_message()
314 }
315 .sanitize()
316 .is_ok());
317
318 assert!(Message {
319 address_map_indexes: vec![
320 AddressMapIndexes {
321 writable: (0..200).step_by(2).collect(),
322 readonly: (1..200).step_by(2).collect(),
323 },
324 AddressMapIndexes {
325 writable: (0..200).step_by(2).collect(),
326 readonly: (1..200).step_by(2).collect(),
327 }
328 ],
329 ..two_map_message()
330 }
331 .sanitize()
332 .is_err());
333 }
334
335 #[test]
336 fn test_sanitize_excessive_maps() {
337 assert!(Message {
338 header: MessageHeader {
339 num_readonly_unsigned_accounts: 1,
340 ..simple_message().header
341 },
342 ..simple_message()
343 }
344 .sanitize()
345 .is_ok());
346
347 assert!(Message {
348 header: MessageHeader {
349 num_readonly_unsigned_accounts: 0,
350 ..simple_message().header
351 },
352 ..simple_message()
353 }
354 .sanitize()
355 .is_err());
356 }
357
358 #[test]
359 fn test_sanitize_address_map() {
360 assert!(Message {
361 address_map_indexes: vec![AddressMapIndexes {
362 writable: vec![0],
363 readonly: vec![],
364 }],
365 ..simple_message()
366 }
367 .sanitize()
368 .is_ok());
369
370 assert!(Message {
371 address_map_indexes: vec![AddressMapIndexes {
372 writable: vec![],
373 readonly: vec![0],
374 }],
375 ..simple_message()
376 }
377 .sanitize()
378 .is_ok());
379
380 assert!(Message {
381 address_map_indexes: vec![AddressMapIndexes {
382 writable: vec![],
383 readonly: vec![],
384 }],
385 ..simple_message()
386 }
387 .sanitize()
388 .is_err());
389 }
390
391 #[test]
392 fn test_serialize() {
393 let message = simple_message();
394 let versioned_msg = VersionedMessage::V0(message.clone());
395 assert_eq!(message.serialize(), versioned_msg.serialize());
396 }
397}