1#![allow(clippy::integer_arithmetic)]
2use crate::sanitize::{Sanitize, SanitizeError};
5use crate::serialize_utils::{
6 append_slice, append_u16, append_u8, read_pubkey, read_slice, read_u16, read_u8,
7};
8use crate::{
9 bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
10 hash::Hash,
11 instruction::{AccountMeta, CompiledInstruction, Instruction},
12 message::MessageHeader,
13 pubkey::Pubkey,
14 short_vec, system_instruction, system_program, sysvar,
15};
16use itertools::Itertools;
17use lazy_static::lazy_static;
18use std::{convert::TryFrom, str::FromStr};
19
20lazy_static! {
21 pub static ref BUILTIN_PROGRAMS_KEYS: [Pubkey; 10] = {
23 let parse = |s| Pubkey::from_str(s).unwrap();
24 [
25 parse("Config1111111111111111111111111111111111111"),
26 parse("Feature111111111111111111111111111111111111"),
27 parse("NativeLoader1111111111111111111111111111111"),
28 parse("Stake11111111111111111111111111111111111111"),
29 parse("StakeConfig11111111111111111111111111111111"),
30 parse("Vote111111111111111111111111111111111111111"),
31 system_program::id(),
32 bpf_loader::id(),
33 bpf_loader_deprecated::id(),
34 bpf_loader_upgradeable::id(),
35 ]
36 };
37}
38
39fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
40 keys.iter().position(|k| k == key).unwrap() as u8
41}
42
43fn compile_instruction(ix: &Instruction, keys: &[Pubkey]) -> CompiledInstruction {
44 let accounts: Vec<_> = ix
45 .accounts
46 .iter()
47 .map(|account_meta| position(keys, &account_meta.pubkey))
48 .collect();
49
50 CompiledInstruction {
51 program_id_index: position(keys, &ix.program_id),
52 data: ix.data.clone(),
53 accounts,
54 }
55}
56
57fn compile_instructions(ixs: &[Instruction], keys: &[Pubkey]) -> Vec<CompiledInstruction> {
58 ixs.iter().map(|ix| compile_instruction(ix, keys)).collect()
59}
60
61#[derive(Debug, PartialEq, Eq)]
63struct InstructionKeys {
64 pub signed_keys: Vec<Pubkey>,
65 pub unsigned_keys: Vec<Pubkey>,
66 pub num_readonly_signed_accounts: u8,
67 pub num_readonly_unsigned_accounts: u8,
68}
69
70impl InstructionKeys {
71 fn new(
72 signed_keys: Vec<Pubkey>,
73 unsigned_keys: Vec<Pubkey>,
74 num_readonly_signed_accounts: u8,
75 num_readonly_unsigned_accounts: u8,
76 ) -> Self {
77 Self {
78 signed_keys,
79 unsigned_keys,
80 num_readonly_signed_accounts,
81 num_readonly_unsigned_accounts,
82 }
83 }
84}
85
86fn get_keys(instructions: &[Instruction], payer: Option<&Pubkey>) -> InstructionKeys {
91 let programs: Vec<_> = get_program_ids(instructions)
92 .iter()
93 .map(|program_id| AccountMeta {
94 pubkey: *program_id,
95 is_signer: false,
96 is_writable: false,
97 })
98 .collect();
99 let mut keys_and_signed: Vec<_> = instructions
100 .iter()
101 .flat_map(|ix| ix.accounts.iter())
102 .collect();
103 keys_and_signed.extend(&programs);
104 keys_and_signed.sort_by(|x, y| {
105 y.is_signer
106 .cmp(&x.is_signer)
107 .then(y.is_writable.cmp(&x.is_writable))
108 });
109
110 let payer_account_meta;
111 if let Some(payer) = payer {
112 payer_account_meta = AccountMeta {
113 pubkey: *payer,
114 is_signer: true,
115 is_writable: true,
116 };
117 keys_and_signed.insert(0, &payer_account_meta);
118 }
119
120 let mut unique_metas: Vec<AccountMeta> = vec![];
121 for account_meta in keys_and_signed {
122 if let Some(x) = unique_metas
124 .iter_mut()
125 .find(|x| x.pubkey == account_meta.pubkey)
126 {
127 x.is_writable |= account_meta.is_writable;
128 continue;
129 }
130 unique_metas.push(account_meta.clone());
131 }
132
133 let mut signed_keys = vec![];
134 let mut unsigned_keys = vec![];
135 let mut num_readonly_signed_accounts = 0;
136 let mut num_readonly_unsigned_accounts = 0;
137 for account_meta in unique_metas {
138 if account_meta.is_signer {
139 signed_keys.push(account_meta.pubkey);
140 if !account_meta.is_writable {
141 num_readonly_signed_accounts += 1;
142 }
143 } else {
144 unsigned_keys.push(account_meta.pubkey);
145 if !account_meta.is_writable {
146 num_readonly_unsigned_accounts += 1;
147 }
148 }
149 }
150 InstructionKeys::new(
151 signed_keys,
152 unsigned_keys,
153 num_readonly_signed_accounts,
154 num_readonly_unsigned_accounts,
155 )
156}
157
158fn get_program_ids(instructions: &[Instruction]) -> Vec<Pubkey> {
160 instructions
161 .iter()
162 .map(|ix| ix.program_id)
163 .unique()
164 .collect()
165}
166
167#[frozen_abi(digest = "2KnLEqfLcTBQqitE22Pp8JYkaqVVbAkGbCfdeHoyxcAU")]
170#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)]
171#[serde(rename_all = "camelCase")]
172pub struct Message {
173 pub header: MessageHeader,
176
177 #[serde(with = "short_vec")]
179 pub account_keys: Vec<Pubkey>,
180
181 pub recent_blockhash: Hash,
183
184 #[serde(with = "short_vec")]
187 pub instructions: Vec<CompiledInstruction>,
188}
189
190impl Sanitize for Message {
191 fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
192 if self.header.num_required_signatures as usize
194 + self.header.num_readonly_unsigned_accounts as usize
195 > self.account_keys.len()
196 {
197 return Err(SanitizeError::IndexOutOfBounds);
198 }
199
200 if self.header.num_readonly_signed_accounts >= self.header.num_required_signatures {
202 return Err(SanitizeError::IndexOutOfBounds);
203 }
204
205 for ci in &self.instructions {
206 if ci.program_id_index as usize >= self.account_keys.len() {
207 return Err(SanitizeError::IndexOutOfBounds);
208 }
209 if ci.program_id_index == 0 {
211 return Err(SanitizeError::IndexOutOfBounds);
212 }
213 for ai in &ci.accounts {
214 if *ai as usize >= self.account_keys.len() {
215 return Err(SanitizeError::IndexOutOfBounds);
216 }
217 }
218 }
219 self.account_keys.sanitize()?;
220 self.recent_blockhash.sanitize()?;
221 self.instructions.sanitize()?;
222 Ok(())
223 }
224}
225
226impl Message {
227 pub fn new_with_compiled_instructions(
228 num_required_signatures: u8,
229 num_readonly_signed_accounts: u8,
230 num_readonly_unsigned_accounts: u8,
231 account_keys: Vec<Pubkey>,
232 recent_blockhash: Hash,
233 instructions: Vec<CompiledInstruction>,
234 ) -> Self {
235 Self {
236 header: MessageHeader {
237 num_required_signatures,
238 num_readonly_signed_accounts,
239 num_readonly_unsigned_accounts,
240 },
241 account_keys,
242 recent_blockhash,
243 instructions,
244 }
245 }
246
247 pub fn new(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
248 let InstructionKeys {
249 mut signed_keys,
250 unsigned_keys,
251 num_readonly_signed_accounts,
252 num_readonly_unsigned_accounts,
253 } = get_keys(instructions, payer);
254 let num_required_signatures = signed_keys.len() as u8;
255 signed_keys.extend(&unsigned_keys);
256 let instructions = compile_instructions(instructions, &signed_keys);
257 Self::new_with_compiled_instructions(
258 num_required_signatures,
259 num_readonly_signed_accounts,
260 num_readonly_unsigned_accounts,
261 signed_keys,
262 Hash::default(),
263 instructions,
264 )
265 }
266
267 pub fn new_with_nonce(
268 mut instructions: Vec<Instruction>,
269 payer: Option<&Pubkey>,
270 nonce_account_pubkey: &Pubkey,
271 nonce_authority_pubkey: &Pubkey,
272 ) -> Self {
273 let nonce_ix =
274 system_instruction::advance_nonce_account(nonce_account_pubkey, nonce_authority_pubkey);
275 instructions.insert(0, nonce_ix);
276 Self::new(&instructions, payer)
277 }
278
279 #[cfg(not(target_arch = "bpf"))]
281 pub fn hash(&self) -> Hash {
282 let message_bytes = self.serialize();
283 Self::hash_raw_message(&message_bytes)
284 }
285
286 #[cfg(not(target_arch = "bpf"))]
288 pub fn hash_raw_message(message_bytes: &[u8]) -> Hash {
289 use blake3::traits::digest::Digest;
290 let mut hasher = blake3::Hasher::new();
291 hasher.update(b"gemachain-tx-message-v1");
292 hasher.update(message_bytes);
293 Hash(<[u8; crate::hash::HASH_BYTES]>::try_from(hasher.finalize().as_slice()).unwrap())
294 }
295
296 pub fn compile_instruction(&self, ix: &Instruction) -> CompiledInstruction {
297 compile_instruction(ix, &self.account_keys)
298 }
299
300 pub fn serialize(&self) -> Vec<u8> {
301 bincode::serialize(self).unwrap()
302 }
303
304 pub fn program_id(&self, instruction_index: usize) -> Option<&Pubkey> {
305 Some(
306 &self.account_keys[self.instructions.get(instruction_index)?.program_id_index as usize],
307 )
308 }
309
310 pub fn program_index(&self, instruction_index: usize) -> Option<usize> {
311 Some(self.instructions.get(instruction_index)?.program_id_index as usize)
312 }
313
314 pub fn program_ids(&self) -> Vec<&Pubkey> {
315 self.instructions
316 .iter()
317 .map(|ix| &self.account_keys[ix.program_id_index as usize])
318 .collect()
319 }
320
321 pub fn is_key_passed_to_program(&self, key_index: usize) -> bool {
322 if let Ok(key_index) = u8::try_from(key_index) {
323 self.instructions
324 .iter()
325 .any(|ix| ix.accounts.contains(&key_index))
326 } else {
327 false
328 }
329 }
330
331 pub fn is_key_called_as_program(&self, key_index: usize) -> bool {
332 if let Ok(key_index) = u8::try_from(key_index) {
333 self.instructions
334 .iter()
335 .any(|ix| ix.program_id_index == key_index)
336 } else {
337 false
338 }
339 }
340
341 pub fn is_non_loader_key(&self, key_index: usize) -> bool {
342 !self.is_key_called_as_program(key_index) || self.is_key_passed_to_program(key_index)
343 }
344
345 pub fn program_position(&self, index: usize) -> Option<usize> {
346 let program_ids = self.program_ids();
347 program_ids
348 .iter()
349 .position(|&&pubkey| pubkey == self.account_keys[index])
350 }
351
352 pub fn maybe_executable(&self, i: usize) -> bool {
353 self.program_position(i).is_some()
354 }
355
356 pub fn is_writable(&self, i: usize, demote_program_write_locks: bool) -> bool {
357 (i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
358 as usize
359 || (i >= self.header.num_required_signatures as usize
360 && i < self.account_keys.len()
361 - self.header.num_readonly_unsigned_accounts as usize))
362 && !{
363 let key = self.account_keys[i];
364 sysvar::is_sysvar_id(&key) || BUILTIN_PROGRAMS_KEYS.contains(&key)
365 }
366 && !(demote_program_write_locks && self.is_key_called_as_program(i))
367 }
368
369 pub fn is_signer(&self, i: usize) -> bool {
370 i < self.header.num_required_signatures as usize
371 }
372
373 #[deprecated]
374 pub fn get_account_keys_by_lock_type(&self) -> (Vec<&Pubkey>, Vec<&Pubkey>) {
375 let mut writable_keys = vec![];
376 let mut readonly_keys = vec![];
377 for (i, key) in self.account_keys.iter().enumerate() {
378 if self.is_writable(i, true) {
379 writable_keys.push(key);
380 } else {
381 readonly_keys.push(key);
382 }
383 }
384 (writable_keys, readonly_keys)
385 }
386
387 #[deprecated]
401 pub fn serialize_instructions(&self) -> Vec<u8> {
402 let mut data = Vec::with_capacity(self.instructions.len() * (32 * 2));
404 append_u16(&mut data, self.instructions.len() as u16);
405 for _ in 0..self.instructions.len() {
406 append_u16(&mut data, 0);
407 }
408 for (i, instruction) in self.instructions.iter().enumerate() {
409 let start_instruction_offset = data.len() as u16;
410 let start = 2 + (2 * i);
411 data[start..start + 2].copy_from_slice(&start_instruction_offset.to_le_bytes());
412 append_u16(&mut data, instruction.accounts.len() as u16);
413 for account_index in &instruction.accounts {
414 let account_index = *account_index as usize;
415 let is_signer = self.is_signer(account_index);
416 let is_writable =
417 self.is_writable(account_index, true);
418 let mut meta_byte = 0;
419 if is_signer {
420 meta_byte |= 1 << Self::IS_SIGNER_BIT;
421 }
422 if is_writable {
423 meta_byte |= 1 << Self::IS_WRITABLE_BIT;
424 }
425 append_u8(&mut data, meta_byte);
426 append_slice(&mut data, self.account_keys[account_index].as_ref());
427 }
428
429 let program_id = &self.account_keys[instruction.program_id_index as usize];
430 append_slice(&mut data, program_id.as_ref());
431 append_u16(&mut data, instruction.data.len() as u16);
432 append_slice(&mut data, &instruction.data);
433 }
434 data
435 }
436
437 const IS_SIGNER_BIT: usize = 0;
438 const IS_WRITABLE_BIT: usize = 1;
439
440 pub fn deserialize_instruction(
441 index: usize,
442 data: &[u8],
443 ) -> Result<Instruction, SanitizeError> {
444 let mut current = 0;
445 let num_instructions = read_u16(&mut current, data)?;
446 if index >= num_instructions as usize {
447 return Err(SanitizeError::IndexOutOfBounds);
448 }
449
450 current += index * 2;
452 let start = read_u16(&mut current, data)?;
453
454 current = start as usize;
455 let num_accounts = read_u16(&mut current, data)?;
456 let mut accounts = Vec::with_capacity(num_accounts as usize);
457 for _ in 0..num_accounts {
458 let meta_byte = read_u8(&mut current, data)?;
459 let mut is_signer = false;
460 let mut is_writable = false;
461 if meta_byte & (1 << Self::IS_SIGNER_BIT) != 0 {
462 is_signer = true;
463 }
464 if meta_byte & (1 << Self::IS_WRITABLE_BIT) != 0 {
465 is_writable = true;
466 }
467 let pubkey = read_pubkey(&mut current, data)?;
468 accounts.push(AccountMeta {
469 pubkey,
470 is_signer,
471 is_writable,
472 });
473 }
474 let program_id = read_pubkey(&mut current, data)?;
475 let data_len = read_u16(&mut current, data)?;
476 let data = read_slice(&mut current, data, data_len as usize)?;
477 Ok(Instruction {
478 program_id,
479 accounts,
480 data,
481 })
482 }
483
484 pub fn signer_keys(&self) -> Vec<&Pubkey> {
485 let last_key = self
487 .account_keys
488 .len()
489 .min(self.header.num_required_signatures as usize);
490 self.account_keys[..last_key].iter().collect()
491 }
492
493 pub fn has_duplicates(&self) -> bool {
495 for i in 1..self.account_keys.len() {
499 #[allow(clippy::integer_arithmetic)]
500 if self.account_keys[i..].contains(&self.account_keys[i - 1]) {
501 return true;
502 }
503 }
504 false
505 }
506}
507
508#[cfg(test)]
509mod tests {
510 #![allow(deprecated)]
511 use super::*;
512 use crate::{hash, instruction::AccountMeta, message::MESSAGE_HEADER_LENGTH};
513 use std::collections::HashSet;
514
515 #[test]
516 fn test_message_unique_program_ids() {
517 let program_id0 = Pubkey::default();
518 let program_ids = get_program_ids(&[
519 Instruction::new_with_bincode(program_id0, &0, vec![]),
520 Instruction::new_with_bincode(program_id0, &0, vec![]),
521 ]);
522 assert_eq!(program_ids, vec![program_id0]);
523 }
524
525 #[test]
526 fn test_builtin_program_keys() {
527 let keys: HashSet<Pubkey> = BUILTIN_PROGRAMS_KEYS.iter().copied().collect();
528 assert_eq!(keys.len(), 10);
529 for k in keys {
530 let k = format!("{}", k);
531 assert!(k.ends_with("11111111111111111111111"));
532 }
533 }
534
535 #[test]
536 fn test_builtin_program_keys_abi_freeze() {
537 let builtins = format!("{:?}", *BUILTIN_PROGRAMS_KEYS);
540 assert_eq!(
541 format!("{}", hash::hash(builtins.as_bytes())),
542 "ACqmMkYbo9eqK6QrRSrB3HLyR6uHhLf31SCfGUAJjiWj"
543 );
544 }
545
546 #[test]
547 fn test_message_unique_program_ids_not_adjacent() {
548 let program_id0 = Pubkey::default();
549 let program_id1 = Pubkey::new_unique();
550 let program_ids = get_program_ids(&[
551 Instruction::new_with_bincode(program_id0, &0, vec![]),
552 Instruction::new_with_bincode(program_id1, &0, vec![]),
553 Instruction::new_with_bincode(program_id0, &0, vec![]),
554 ]);
555 assert_eq!(program_ids, vec![program_id0, program_id1]);
556 }
557
558 #[test]
559 fn test_message_unique_program_ids_order_preserved() {
560 let program_id0 = Pubkey::new_unique();
561 let program_id1 = Pubkey::default(); let program_ids = get_program_ids(&[
563 Instruction::new_with_bincode(program_id0, &0, vec![]),
564 Instruction::new_with_bincode(program_id1, &0, vec![]),
565 Instruction::new_with_bincode(program_id0, &0, vec![]),
566 ]);
567 assert_eq!(program_ids, vec![program_id0, program_id1]);
568 }
569
570 #[test]
571 fn test_message_unique_keys_both_signed() {
572 let program_id = Pubkey::default();
573 let id0 = Pubkey::default();
574 let keys = get_keys(
575 &[
576 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]),
577 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]),
578 ],
579 None,
580 );
581 assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0));
582 }
583
584 #[test]
585 fn test_message_unique_keys_signed_and_payer() {
586 let program_id = Pubkey::default();
587 let id0 = Pubkey::default();
588 let keys = get_keys(
589 &[Instruction::new_with_bincode(
590 program_id,
591 &0,
592 vec![AccountMeta::new(id0, true)],
593 )],
594 Some(&id0),
595 );
596 assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0));
597 }
598
599 #[test]
600 fn test_message_unique_keys_unsigned_and_payer() {
601 let program_id = Pubkey::default();
602 let id0 = Pubkey::default();
603 let keys = get_keys(
604 &[Instruction::new_with_bincode(
605 program_id,
606 &0,
607 vec![AccountMeta::new(id0, false)],
608 )],
609 Some(&id0),
610 );
611 assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0));
612 }
613
614 #[test]
615 fn test_message_unique_keys_one_signed() {
616 let program_id = Pubkey::default();
617 let id0 = Pubkey::default();
618 let keys = get_keys(
619 &[
620 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
621 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]),
622 ],
623 None,
624 );
625 assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0));
626 }
627
628 #[test]
629 fn test_message_unique_keys_one_readonly_signed() {
630 let program_id = Pubkey::default();
631 let id0 = Pubkey::default();
632 let keys = get_keys(
633 &[
634 Instruction::new_with_bincode(
635 program_id,
636 &0,
637 vec![AccountMeta::new_readonly(id0, true)],
638 ),
639 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]),
640 ],
641 None,
642 );
643
644 assert_eq!(keys, InstructionKeys::new(vec![id0], vec![], 0, 0));
646 }
647
648 #[test]
649 fn test_message_unique_keys_one_readonly_unsigned() {
650 let program_id = Pubkey::default();
651 let id0 = Pubkey::default();
652 let keys = get_keys(
653 &[
654 Instruction::new_with_bincode(
655 program_id,
656 &0,
657 vec![AccountMeta::new_readonly(id0, false)],
658 ),
659 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
660 ],
661 None,
662 );
663
664 assert_eq!(keys, InstructionKeys::new(vec![], vec![id0], 0, 0));
666 }
667
668 #[test]
669 fn test_message_unique_keys_order_preserved() {
670 let program_id = Pubkey::default();
671 let id0 = Pubkey::new_unique();
672 let id1 = Pubkey::default(); let keys = get_keys(
674 &[
675 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
676 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, false)]),
677 ],
678 None,
679 );
680 assert_eq!(keys, InstructionKeys::new(vec![], vec![id0, id1], 0, 0));
681 }
682
683 #[test]
684 fn test_message_unique_keys_not_adjacent() {
685 let program_id = Pubkey::default();
686 let id0 = Pubkey::default();
687 let id1 = Pubkey::new_unique();
688 let keys = get_keys(
689 &[
690 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
691 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, false)]),
692 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]),
693 ],
694 None,
695 );
696 assert_eq!(keys, InstructionKeys::new(vec![id0], vec![id1], 0, 0));
697 }
698
699 #[test]
700 fn test_message_signed_keys_first() {
701 let program_id = Pubkey::default();
702 let id0 = Pubkey::default();
703 let id1 = Pubkey::new_unique();
704 let keys = get_keys(
705 &[
706 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
707 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, true)]),
708 ],
709 None,
710 );
711 assert_eq!(keys, InstructionKeys::new(vec![id1], vec![id0], 0, 0));
712 }
713
714 #[test]
715 fn test_message_signed_keys_len() {
717 let program_id = Pubkey::default();
718 let id0 = Pubkey::default();
719 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
720 let message = Message::new(&[ix], None);
721 assert_eq!(message.header.num_required_signatures, 0);
722
723 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
724 let message = Message::new(&[ix], Some(&id0));
725 assert_eq!(message.header.num_required_signatures, 1);
726 }
727
728 #[test]
729 fn test_message_readonly_keys_last() {
730 let program_id = Pubkey::default();
731 let id0 = Pubkey::default(); let id1 = Pubkey::new_unique();
733 let id2 = Pubkey::new_unique();
734 let id3 = Pubkey::new_unique();
735 let keys = get_keys(
736 &[
737 Instruction::new_with_bincode(
738 program_id,
739 &0,
740 vec![AccountMeta::new_readonly(id0, false)],
741 ),
742 Instruction::new_with_bincode(
743 program_id,
744 &0,
745 vec![AccountMeta::new_readonly(id1, true)],
746 ),
747 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id2, false)]),
748 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id3, true)]),
749 ],
750 None,
751 );
752 assert_eq!(
753 keys,
754 InstructionKeys::new(vec![id3, id1], vec![id2, id0], 1, 1)
755 );
756 }
757
758 #[test]
759 fn test_message_kitchen_sink() {
760 let program_id0 = Pubkey::new_unique();
761 let program_id1 = Pubkey::new_unique();
762 let id0 = Pubkey::default();
763 let id1 = Pubkey::new_unique();
764 let message = Message::new(
765 &[
766 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
767 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id1, true)]),
768 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, false)]),
769 ],
770 Some(&id1),
771 );
772 assert_eq!(
773 message.instructions[0],
774 CompiledInstruction::new(2, &0, vec![1])
775 );
776 assert_eq!(
777 message.instructions[1],
778 CompiledInstruction::new(3, &0, vec![0])
779 );
780 assert_eq!(
781 message.instructions[2],
782 CompiledInstruction::new(2, &0, vec![0])
783 );
784 }
785
786 #[test]
787 fn test_message_payer_first() {
788 let program_id = Pubkey::default();
789 let payer = Pubkey::new_unique();
790 let id0 = Pubkey::default();
791
792 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
793 let message = Message::new(&[ix], Some(&payer));
794 assert_eq!(message.header.num_required_signatures, 1);
795
796 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
797 let message = Message::new(&[ix], Some(&payer));
798 assert_eq!(message.header.num_required_signatures, 2);
799
800 let ix = Instruction::new_with_bincode(
801 program_id,
802 &0,
803 vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
804 );
805 let message = Message::new(&[ix], Some(&payer));
806 assert_eq!(message.header.num_required_signatures, 2);
807 }
808
809 #[test]
810 fn test_message_program_last() {
811 let program_id = Pubkey::default();
812 let id0 = Pubkey::new_unique();
813 let id1 = Pubkey::new_unique();
814 let keys = get_keys(
815 &[
816 Instruction::new_with_bincode(
817 program_id,
818 &0,
819 vec![AccountMeta::new_readonly(id0, false)],
820 ),
821 Instruction::new_with_bincode(
822 program_id,
823 &0,
824 vec![AccountMeta::new_readonly(id1, true)],
825 ),
826 ],
827 None,
828 );
829 assert_eq!(
830 keys,
831 InstructionKeys::new(vec![id1], vec![id0, program_id], 1, 2)
832 );
833 }
834
835 #[test]
836 fn test_program_position() {
837 let program_id0 = Pubkey::default();
838 let program_id1 = Pubkey::new_unique();
839 let id = Pubkey::new_unique();
840 let message = Message::new(
841 &[
842 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id, false)]),
843 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id, true)]),
844 ],
845 Some(&id),
846 );
847 assert_eq!(message.program_position(0), None);
848 assert_eq!(message.program_position(1), Some(0));
849 assert_eq!(message.program_position(2), Some(1));
850 }
851
852 #[test]
853 fn test_is_writable() {
854 let key0 = Pubkey::new_unique();
855 let key1 = Pubkey::new_unique();
856 let key2 = Pubkey::new_unique();
857 let key3 = Pubkey::new_unique();
858 let key4 = Pubkey::new_unique();
859 let key5 = Pubkey::new_unique();
860
861 let message = Message {
862 header: MessageHeader {
863 num_required_signatures: 3,
864 num_readonly_signed_accounts: 2,
865 num_readonly_unsigned_accounts: 1,
866 },
867 account_keys: vec![key0, key1, key2, key3, key4, key5],
868 recent_blockhash: Hash::default(),
869 instructions: vec![],
870 };
871 let demote_program_write_locks = true;
872 assert!(message.is_writable(0, demote_program_write_locks));
873 assert!(!message.is_writable(1, demote_program_write_locks));
874 assert!(!message.is_writable(2, demote_program_write_locks));
875 assert!(message.is_writable(3, demote_program_write_locks));
876 assert!(message.is_writable(4, demote_program_write_locks));
877 assert!(!message.is_writable(5, demote_program_write_locks));
878 }
879
880 #[test]
881 fn test_get_account_keys_by_lock_type() {
882 let program_id = Pubkey::default();
883 let id0 = Pubkey::new_unique();
884 let id1 = Pubkey::new_unique();
885 let id2 = Pubkey::new_unique();
886 let id3 = Pubkey::new_unique();
887 let message = Message::new(
888 &[
889 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
890 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, true)]),
891 Instruction::new_with_bincode(
892 program_id,
893 &0,
894 vec![AccountMeta::new_readonly(id2, false)],
895 ),
896 Instruction::new_with_bincode(
897 program_id,
898 &0,
899 vec![AccountMeta::new_readonly(id3, true)],
900 ),
901 ],
902 Some(&id1),
903 );
904 assert_eq!(
905 message.get_account_keys_by_lock_type(),
906 (vec![&id1, &id0], vec![&id3, &id2, &program_id])
907 );
908 }
909
910 #[test]
911 fn test_decompile_instructions() {
912 gemachain_logger::setup();
913 let program_id0 = Pubkey::new_unique();
914 let program_id1 = Pubkey::new_unique();
915 let id0 = Pubkey::new_unique();
916 let id1 = Pubkey::new_unique();
917 let id2 = Pubkey::new_unique();
918 let id3 = Pubkey::new_unique();
919 let instructions = vec![
920 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
921 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
922 Instruction::new_with_bincode(
923 program_id1,
924 &0,
925 vec![AccountMeta::new_readonly(id2, false)],
926 ),
927 Instruction::new_with_bincode(
928 program_id1,
929 &0,
930 vec![AccountMeta::new_readonly(id3, true)],
931 ),
932 ];
933
934 let message = Message::new(&instructions, Some(&id1));
935 let serialized = message.serialize_instructions();
936 for (i, instruction) in instructions.iter().enumerate() {
937 assert_eq!(
938 Message::deserialize_instruction(i, &serialized).unwrap(),
939 *instruction
940 );
941 }
942 }
943
944 #[test]
945 fn test_decompile_instructions_out_of_bounds() {
946 gemachain_logger::setup();
947 let program_id0 = Pubkey::new_unique();
948 let id0 = Pubkey::new_unique();
949 let id1 = Pubkey::new_unique();
950 let instructions = vec![
951 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
952 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
953 ];
954
955 let message = Message::new(&instructions, Some(&id1));
956 let serialized = message.serialize_instructions();
957 assert_eq!(
958 Message::deserialize_instruction(instructions.len(), &serialized).unwrap_err(),
959 SanitizeError::IndexOutOfBounds,
960 );
961 }
962
963 #[test]
964 fn test_program_ids() {
965 let key0 = Pubkey::new_unique();
966 let key1 = Pubkey::new_unique();
967 let loader2 = Pubkey::new_unique();
968 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
969 let message = Message::new_with_compiled_instructions(
970 1,
971 0,
972 2,
973 vec![key0, key1, loader2],
974 Hash::default(),
975 instructions,
976 );
977 assert_eq!(message.program_ids(), vec![&loader2]);
978 }
979
980 #[test]
981 fn test_is_key_passed_to_program() {
982 let key0 = Pubkey::new_unique();
983 let key1 = Pubkey::new_unique();
984 let loader2 = Pubkey::new_unique();
985 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
986 let message = Message::new_with_compiled_instructions(
987 1,
988 0,
989 2,
990 vec![key0, key1, loader2],
991 Hash::default(),
992 instructions,
993 );
994
995 assert!(message.is_key_passed_to_program(0));
996 assert!(message.is_key_passed_to_program(1));
997 assert!(!message.is_key_passed_to_program(2));
998 }
999
1000 #[test]
1001 fn test_is_non_loader_key() {
1002 let key0 = Pubkey::new_unique();
1003 let key1 = Pubkey::new_unique();
1004 let loader2 = Pubkey::new_unique();
1005 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
1006 let message = Message::new_with_compiled_instructions(
1007 1,
1008 0,
1009 2,
1010 vec![key0, key1, loader2],
1011 Hash::default(),
1012 instructions,
1013 );
1014 assert!(message.is_non_loader_key(0));
1015 assert!(message.is_non_loader_key(1));
1016 assert!(!message.is_non_loader_key(2));
1017 }
1018
1019 #[test]
1020 fn test_message_header_len_constant() {
1021 assert_eq!(
1022 bincode::serialized_size(&MessageHeader::default()).unwrap() as usize,
1023 MESSAGE_HEADER_LENGTH
1024 );
1025 }
1026
1027 #[test]
1028 fn test_message_hash() {
1029 let program_id0 = Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap();
1032 let program_id1 = Pubkey::from_str("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh").unwrap();
1033 let id0 = Pubkey::from_str("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3").unwrap();
1034 let id1 = Pubkey::from_str("GcdayuLaLyrdmUu324nahyv33G5poQdLUEZ1nEytDeP").unwrap();
1035 let id2 = Pubkey::from_str("LX3EUdRUBUa3TbsYXLEUdj9J3prXkWXvLYSWyYyc2Jj").unwrap();
1036 let id3 = Pubkey::from_str("QRSsyMWN1yHT9ir42bgNZUNZ4PdEhcSWCrL2AryKpy5").unwrap();
1037 let instructions = vec![
1038 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
1039 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
1040 Instruction::new_with_bincode(
1041 program_id1,
1042 &0,
1043 vec![AccountMeta::new_readonly(id2, false)],
1044 ),
1045 Instruction::new_with_bincode(
1046 program_id1,
1047 &0,
1048 vec![AccountMeta::new_readonly(id3, true)],
1049 ),
1050 ];
1051
1052 let message = Message::new(&instructions, Some(&id1));
1053 assert_eq!(
1054 message.hash(),
1055 Hash::from_str("CXRH7GHLieaQZRUjH1mpnNnUZQtU4V4RpJpAFgy77i3z").unwrap()
1056 )
1057 }
1058}