1#![allow(clippy::arithmetic_side_effects)]
13
14use {
15 crate::{
16 bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
17 hash::Hash,
18 instruction::{CompiledInstruction, Instruction},
19 message::{compiled_keys::CompiledKeys, MessageHeader},
20 pubkey::Pubkey,
21 sanitize::{Sanitize, SanitizeError},
22 short_vec, system_instruction, system_program, sysvar, wasm_bindgen,
23 },
24 lazy_static::lazy_static,
25 std::{convert::TryFrom, str::FromStr},
26};
27
28lazy_static! {
29 pub static ref BUILTIN_PROGRAMS_KEYS: [Pubkey; 10] = {
31 let parse = |s| Pubkey::from_str(s).unwrap();
32 [
33 parse("Config1111111111111111111111111111111111111"),
34 parse("Feature111111111111111111111111111111111111"),
35 parse("NativeLoader1111111111111111111111111111111"),
36 parse("Stake11111111111111111111111111111111111111"),
37 parse("StakeConfig11111111111111111111111111111111"),
38 parse("Vote111111111111111111111111111111111111111"),
39 system_program::id(),
40 bpf_loader::id(),
41 bpf_loader_deprecated::id(),
42 bpf_loader_upgradeable::id(),
43 ]
44 };
45}
46
47lazy_static! {
48 pub static ref MAYBE_BUILTIN_KEY_OR_SYSVAR: [bool; 256] = {
54 let mut temp_table: [bool; 256] = [false; 256];
55 BUILTIN_PROGRAMS_KEYS.iter().for_each(|key| temp_table[key.0[0] as usize] = true);
56 sysvar::ALL_IDS.iter().for_each(|key| temp_table[key.0[0] as usize] = true);
57 temp_table
58 };
59}
60
61pub fn is_builtin_key_or_sysvar(key: &Pubkey) -> bool {
62 if MAYBE_BUILTIN_KEY_OR_SYSVAR[key.0[0] as usize] {
63 return sysvar::is_sysvar_id(key) || BUILTIN_PROGRAMS_KEYS.contains(key);
64 }
65 false
66}
67
68fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
69 keys.iter().position(|k| k == key).unwrap() as u8
70}
71
72fn compile_instruction(ix: &Instruction, keys: &[Pubkey]) -> CompiledInstruction {
73 let accounts: Vec<_> = ix
74 .accounts
75 .iter()
76 .map(|account_meta| position(keys, &account_meta.pubkey))
77 .collect();
78
79 CompiledInstruction {
80 program_id_index: position(keys, &ix.program_id),
81 data: ix.data.clone(),
82 accounts,
83 }
84}
85
86fn compile_instructions(ixs: &[Instruction], keys: &[Pubkey]) -> Vec<CompiledInstruction> {
87 ixs.iter().map(|ix| compile_instruction(ix, keys)).collect()
88}
89
90#[wasm_bindgen]
107#[frozen_abi(digest = "2KnLEqfLcTBQqitE22Pp8JYkaqVVbAkGbCfdeHoyxcAU")]
108#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)]
109#[serde(rename_all = "camelCase")]
110pub struct Message {
111 #[wasm_bindgen(skip)]
114 pub header: MessageHeader,
115
116 #[wasm_bindgen(skip)]
118 #[serde(with = "short_vec")]
119 pub account_keys: Vec<Pubkey>,
120
121 pub recent_blockhash: Hash,
123
124 #[wasm_bindgen(skip)]
127 #[serde(with = "short_vec")]
128 pub instructions: Vec<CompiledInstruction>,
129}
130
131impl Sanitize for Message {
132 fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
133 if self.header.num_required_signatures as usize
135 + self.header.num_readonly_unsigned_accounts as usize
136 > self.account_keys.len()
137 {
138 return Err(SanitizeError::IndexOutOfBounds);
139 }
140
141 if self.header.num_readonly_signed_accounts >= self.header.num_required_signatures {
143 return Err(SanitizeError::IndexOutOfBounds);
144 }
145
146 for ci in &self.instructions {
147 if ci.program_id_index as usize >= self.account_keys.len() {
148 return Err(SanitizeError::IndexOutOfBounds);
149 }
150 if ci.program_id_index == 0 {
152 return Err(SanitizeError::IndexOutOfBounds);
153 }
154 for ai in &ci.accounts {
155 if *ai as usize >= self.account_keys.len() {
156 return Err(SanitizeError::IndexOutOfBounds);
157 }
158 }
159 }
160 self.account_keys.sanitize()?;
161 self.recent_blockhash.sanitize()?;
162 self.instructions.sanitize()?;
163 Ok(())
164 }
165}
166
167impl Message {
168 pub fn new(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
237 Self::new_with_blockhash(instructions, payer, &Hash::default())
238 }
239
240 pub fn new_with_blockhash(
312 instructions: &[Instruction],
313 payer: Option<&Pubkey>,
314 blockhash: &Hash,
315 ) -> Self {
316 let compiled_keys = CompiledKeys::compile(instructions, payer.cloned());
317 let (header, account_keys) = compiled_keys
318 .try_into_message_components()
319 .expect("overflow when compiling message keys");
320 let instructions = compile_instructions(instructions, &account_keys);
321 Self::new_with_compiled_instructions(
322 header.num_required_signatures,
323 header.num_readonly_signed_accounts,
324 header.num_readonly_unsigned_accounts,
325 account_keys,
326 *blockhash,
327 instructions,
328 )
329 }
330
331 pub fn new_with_nonce(
439 mut instructions: Vec<Instruction>,
440 payer: Option<&Pubkey>,
441 nonce_account_pubkey: &Pubkey,
442 nonce_authority_pubkey: &Pubkey,
443 ) -> Self {
444 let nonce_ix =
445 system_instruction::advance_nonce_account(nonce_account_pubkey, nonce_authority_pubkey);
446 instructions.insert(0, nonce_ix);
447 Self::new(&instructions, payer)
448 }
449
450 pub fn new_with_compiled_instructions(
451 num_required_signatures: u8,
452 num_readonly_signed_accounts: u8,
453 num_readonly_unsigned_accounts: u8,
454 account_keys: Vec<Pubkey>,
455 recent_blockhash: Hash,
456 instructions: Vec<CompiledInstruction>,
457 ) -> Self {
458 Self {
459 header: MessageHeader {
460 num_required_signatures,
461 num_readonly_signed_accounts,
462 num_readonly_unsigned_accounts,
463 },
464 account_keys,
465 recent_blockhash,
466 instructions,
467 }
468 }
469
470 #[cfg(not(target_os = "solana"))]
472 pub fn hash(&self) -> Hash {
473 let message_bytes = self.serialize();
474 Self::hash_raw_message(&message_bytes)
475 }
476
477 #[cfg(not(target_os = "solana"))]
479 pub fn hash_raw_message(message_bytes: &[u8]) -> Hash {
480 use blake3::traits::digest::Digest;
481 let mut hasher = blake3::Hasher::new();
482 hasher.update(b"miraland-tx-message-v1");
483 hasher.update(message_bytes);
484 Hash(hasher.finalize().into())
485 }
486
487 pub fn compile_instruction(&self, ix: &Instruction) -> CompiledInstruction {
488 compile_instruction(ix, &self.account_keys)
489 }
490
491 pub fn serialize(&self) -> Vec<u8> {
492 bincode::serialize(self).unwrap()
493 }
494
495 pub fn program_id(&self, instruction_index: usize) -> Option<&Pubkey> {
496 Some(
497 &self.account_keys[self.instructions.get(instruction_index)?.program_id_index as usize],
498 )
499 }
500
501 pub fn program_index(&self, instruction_index: usize) -> Option<usize> {
502 Some(self.instructions.get(instruction_index)?.program_id_index as usize)
503 }
504
505 pub fn program_ids(&self) -> Vec<&Pubkey> {
506 self.instructions
507 .iter()
508 .map(|ix| &self.account_keys[ix.program_id_index as usize])
509 .collect()
510 }
511
512 pub fn is_key_passed_to_program(&self, key_index: usize) -> bool {
513 if let Ok(key_index) = u8::try_from(key_index) {
514 self.instructions
515 .iter()
516 .any(|ix| ix.accounts.contains(&key_index))
517 } else {
518 false
519 }
520 }
521
522 pub fn is_key_called_as_program(&self, key_index: usize) -> bool {
523 if let Ok(key_index) = u8::try_from(key_index) {
524 self.instructions
525 .iter()
526 .any(|ix| ix.program_id_index == key_index)
527 } else {
528 false
529 }
530 }
531
532 pub fn is_non_loader_key(&self, key_index: usize) -> bool {
533 !self.is_key_called_as_program(key_index) || self.is_key_passed_to_program(key_index)
534 }
535
536 pub fn program_position(&self, index: usize) -> Option<usize> {
537 let program_ids = self.program_ids();
538 program_ids
539 .iter()
540 .position(|&&pubkey| pubkey == self.account_keys[index])
541 }
542
543 pub fn maybe_executable(&self, i: usize) -> bool {
544 self.program_position(i).is_some()
545 }
546
547 pub fn demote_program_id(&self, i: usize) -> bool {
548 self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present()
549 }
550
551 pub fn is_writable(&self, i: usize) -> bool {
552 (i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
553 as usize
554 || (i >= self.header.num_required_signatures as usize
555 && i < self.account_keys.len()
556 - self.header.num_readonly_unsigned_accounts as usize))
557 && !is_builtin_key_or_sysvar(&self.account_keys[i])
558 && !self.demote_program_id(i)
559 }
560
561 pub fn is_signer(&self, i: usize) -> bool {
562 i < self.header.num_required_signatures as usize
563 }
564
565 #[deprecated]
566 pub fn get_account_keys_by_lock_type(&self) -> (Vec<&Pubkey>, Vec<&Pubkey>) {
567 let mut writable_keys = vec![];
568 let mut readonly_keys = vec![];
569 for (i, key) in self.account_keys.iter().enumerate() {
570 if self.is_writable(i) {
571 writable_keys.push(key);
572 } else {
573 readonly_keys.push(key);
574 }
575 }
576 (writable_keys, readonly_keys)
577 }
578
579 #[deprecated]
580 pub fn deserialize_instruction(
581 index: usize,
582 data: &[u8],
583 ) -> Result<Instruction, SanitizeError> {
584 #[allow(deprecated)]
585 sysvar::instructions::load_instruction_at(index, data)
586 }
587
588 pub fn signer_keys(&self) -> Vec<&Pubkey> {
589 let last_key = self
591 .account_keys
592 .len()
593 .min(self.header.num_required_signatures as usize);
594 self.account_keys[..last_key].iter().collect()
595 }
596
597 pub fn has_duplicates(&self) -> bool {
599 for i in 1..self.account_keys.len() {
603 #[allow(clippy::arithmetic_side_effects)]
604 if self.account_keys[i..].contains(&self.account_keys[i - 1]) {
605 return true;
606 }
607 }
608 false
609 }
610
611 pub fn is_upgradeable_loader_present(&self) -> bool {
613 self.account_keys
614 .iter()
615 .any(|&key| key == bpf_loader_upgradeable::id())
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 #![allow(deprecated)]
622 use {
623 super::*,
624 crate::{hash, instruction::AccountMeta, message::MESSAGE_HEADER_LENGTH},
625 std::collections::HashSet,
626 };
627
628 #[test]
629 fn test_builtin_program_keys() {
630 let keys: HashSet<Pubkey> = BUILTIN_PROGRAMS_KEYS.iter().copied().collect();
631 assert_eq!(keys.len(), 10);
632 for k in keys {
633 let k = format!("{k}");
634 assert!(k.ends_with("11111111111111111111111"));
635 }
636 }
637
638 #[test]
639 fn test_builtin_program_keys_abi_freeze() {
640 let builtins = format!("{:?}", *BUILTIN_PROGRAMS_KEYS);
643 assert_eq!(
644 format!("{}", hash::hash(builtins.as_bytes())),
645 "ACqmMkYbo9eqK6QrRSrB3HLyR6uHhLf31SCfGUAJjiWj"
646 );
647 }
648
649 #[test]
650 fn test_message_signed_keys_len() {
652 let program_id = Pubkey::default();
653 let id0 = Pubkey::default();
654 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
655 let message = Message::new(&[ix], None);
656 assert_eq!(message.header.num_required_signatures, 0);
657
658 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
659 let message = Message::new(&[ix], Some(&id0));
660 assert_eq!(message.header.num_required_signatures, 1);
661 }
662
663 #[test]
664 fn test_message_kitchen_sink() {
665 let program_id0 = Pubkey::new_unique();
666 let program_id1 = Pubkey::new_unique();
667 let id0 = Pubkey::default();
668 let id1 = Pubkey::new_unique();
669 let message = Message::new(
670 &[
671 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
672 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id1, true)]),
673 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, false)]),
674 ],
675 Some(&id1),
676 );
677 assert_eq!(
678 message.instructions[0],
679 CompiledInstruction::new(2, &0, vec![1])
680 );
681 assert_eq!(
682 message.instructions[1],
683 CompiledInstruction::new(3, &0, vec![0])
684 );
685 assert_eq!(
686 message.instructions[2],
687 CompiledInstruction::new(2, &0, vec![0])
688 );
689 }
690
691 #[test]
692 fn test_message_payer_first() {
693 let program_id = Pubkey::default();
694 let payer = Pubkey::new_unique();
695 let id0 = Pubkey::default();
696
697 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
698 let message = Message::new(&[ix], Some(&payer));
699 assert_eq!(message.header.num_required_signatures, 1);
700
701 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
702 let message = Message::new(&[ix], Some(&payer));
703 assert_eq!(message.header.num_required_signatures, 2);
704
705 let ix = Instruction::new_with_bincode(
706 program_id,
707 &0,
708 vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
709 );
710 let message = Message::new(&[ix], Some(&payer));
711 assert_eq!(message.header.num_required_signatures, 2);
712 }
713
714 #[test]
715 fn test_program_position() {
716 let program_id0 = Pubkey::default();
717 let program_id1 = Pubkey::new_unique();
718 let id = Pubkey::new_unique();
719 let message = Message::new(
720 &[
721 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id, false)]),
722 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id, true)]),
723 ],
724 Some(&id),
725 );
726 assert_eq!(message.program_position(0), None);
727 assert_eq!(message.program_position(1), Some(0));
728 assert_eq!(message.program_position(2), Some(1));
729 }
730
731 #[test]
732 fn test_is_writable() {
733 let key0 = Pubkey::new_unique();
734 let key1 = Pubkey::new_unique();
735 let key2 = Pubkey::new_unique();
736 let key3 = Pubkey::new_unique();
737 let key4 = Pubkey::new_unique();
738 let key5 = Pubkey::new_unique();
739
740 let message = Message {
741 header: MessageHeader {
742 num_required_signatures: 3,
743 num_readonly_signed_accounts: 2,
744 num_readonly_unsigned_accounts: 1,
745 },
746 account_keys: vec![key0, key1, key2, key3, key4, key5],
747 recent_blockhash: Hash::default(),
748 instructions: vec![],
749 };
750 assert!(message.is_writable(0));
751 assert!(!message.is_writable(1));
752 assert!(!message.is_writable(2));
753 assert!(message.is_writable(3));
754 assert!(message.is_writable(4));
755 assert!(!message.is_writable(5));
756 }
757
758 #[test]
759 fn test_get_account_keys_by_lock_type() {
760 let program_id = Pubkey::default();
761 let id0 = Pubkey::new_unique();
762 let id1 = Pubkey::new_unique();
763 let id2 = Pubkey::new_unique();
764 let id3 = Pubkey::new_unique();
765 let message = Message::new(
766 &[
767 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
768 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, true)]),
769 Instruction::new_with_bincode(
770 program_id,
771 &0,
772 vec![AccountMeta::new_readonly(id2, false)],
773 ),
774 Instruction::new_with_bincode(
775 program_id,
776 &0,
777 vec![AccountMeta::new_readonly(id3, true)],
778 ),
779 ],
780 Some(&id1),
781 );
782 assert_eq!(
783 message.get_account_keys_by_lock_type(),
784 (vec![&id1, &id0], vec![&id3, &program_id, &id2])
785 );
786 }
787
788 #[test]
789 fn test_program_ids() {
790 let key0 = Pubkey::new_unique();
791 let key1 = Pubkey::new_unique();
792 let loader2 = Pubkey::new_unique();
793 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
794 let message = Message::new_with_compiled_instructions(
795 1,
796 0,
797 2,
798 vec![key0, key1, loader2],
799 Hash::default(),
800 instructions,
801 );
802 assert_eq!(message.program_ids(), vec![&loader2]);
803 }
804
805 #[test]
806 fn test_is_key_passed_to_program() {
807 let key0 = Pubkey::new_unique();
808 let key1 = Pubkey::new_unique();
809 let loader2 = Pubkey::new_unique();
810 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
811 let message = Message::new_with_compiled_instructions(
812 1,
813 0,
814 2,
815 vec![key0, key1, loader2],
816 Hash::default(),
817 instructions,
818 );
819
820 assert!(message.is_key_passed_to_program(0));
821 assert!(message.is_key_passed_to_program(1));
822 assert!(!message.is_key_passed_to_program(2));
823 }
824
825 #[test]
826 fn test_is_non_loader_key() {
827 let key0 = Pubkey::new_unique();
828 let key1 = Pubkey::new_unique();
829 let loader2 = Pubkey::new_unique();
830 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
831 let message = Message::new_with_compiled_instructions(
832 1,
833 0,
834 2,
835 vec![key0, key1, loader2],
836 Hash::default(),
837 instructions,
838 );
839 assert!(message.is_non_loader_key(0));
840 assert!(message.is_non_loader_key(1));
841 assert!(!message.is_non_loader_key(2));
842 }
843
844 #[test]
845 fn test_message_header_len_constant() {
846 assert_eq!(
847 bincode::serialized_size(&MessageHeader::default()).unwrap() as usize,
848 MESSAGE_HEADER_LENGTH
849 );
850 }
851
852 #[test]
853 fn test_message_hash() {
854 let program_id0 = Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap();
857 let program_id1 = Pubkey::from_str("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh").unwrap();
858 let id0 = Pubkey::from_str("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3").unwrap();
859 let id1 = Pubkey::from_str("GcdayuLaLyrdmUu324nahyv33G5poQdLUEZ1nEytDeP").unwrap();
860 let id2 = Pubkey::from_str("LX3EUdRUBUa3TbsYXLEUdj9J3prXkWXvLYSWyYyc2Jj").unwrap();
861 let id3 = Pubkey::from_str("QRSsyMWN1yHT9ir42bgNZUNZ4PdEhcSWCrL2AryKpy5").unwrap();
862 let instructions = vec![
863 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
864 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
865 Instruction::new_with_bincode(
866 program_id1,
867 &0,
868 vec![AccountMeta::new_readonly(id2, false)],
869 ),
870 Instruction::new_with_bincode(
871 program_id1,
872 &0,
873 vec![AccountMeta::new_readonly(id3, true)],
874 ),
875 ];
876
877 let message = Message::new(&instructions, Some(&id1));
878 assert_eq!(
879 message.hash(),
880 Hash::from_str("7VWCF4quo2CcWQFNUayZiorxpiR5ix8YzLebrXKf3fMF").unwrap()
881 )
882 }
883}