1#![allow(clippy::integer_arithmetic)]
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 {
236 Self::new_with_blockhash(instructions, payer, &Hash::default())
237 }
238
239 pub fn new_with_blockhash(
310 instructions: &[Instruction],
311 payer: Option<&Pubkey>,
312 blockhash: &Hash,
313 ) -> Self {
314 let compiled_keys = CompiledKeys::compile(instructions, payer.cloned());
315 let (header, account_keys) = compiled_keys
316 .try_into_message_components()
317 .expect("overflow when compiling message keys");
318 let instructions = compile_instructions(instructions, &account_keys);
319 Self::new_with_compiled_instructions(
320 header.num_required_signatures,
321 header.num_readonly_signed_accounts,
322 header.num_readonly_unsigned_accounts,
323 account_keys,
324 *blockhash,
325 instructions,
326 )
327 }
328
329 pub fn new_with_nonce(
436 mut instructions: Vec<Instruction>,
437 payer: Option<&Pubkey>,
438 nonce_account_pubkey: &Pubkey,
439 nonce_authority_pubkey: &Pubkey,
440 ) -> Self {
441 let nonce_ix =
442 system_instruction::advance_nonce_account(nonce_account_pubkey, nonce_authority_pubkey);
443 instructions.insert(0, nonce_ix);
444 Self::new(&instructions, payer)
445 }
446
447 pub fn new_with_compiled_instructions(
448 num_required_signatures: u8,
449 num_readonly_signed_accounts: u8,
450 num_readonly_unsigned_accounts: u8,
451 account_keys: Vec<Pubkey>,
452 recent_blockhash: Hash,
453 instructions: Vec<CompiledInstruction>,
454 ) -> Self {
455 Self {
456 header: MessageHeader {
457 num_required_signatures,
458 num_readonly_signed_accounts,
459 num_readonly_unsigned_accounts,
460 },
461 account_keys,
462 recent_blockhash,
463 instructions,
464 }
465 }
466
467 #[cfg(not(target_os = "solana"))]
469 pub fn hash(&self) -> Hash {
470 let message_bytes = self.serialize();
471 Self::hash_raw_message(&message_bytes)
472 }
473
474 #[cfg(not(target_os = "solana"))]
476 pub fn hash_raw_message(message_bytes: &[u8]) -> Hash {
477 use blake3::traits::digest::Digest;
478 let mut hasher = blake3::Hasher::new();
479 hasher.update(b"solana-tx-message-v1");
480 hasher.update(message_bytes);
481 Hash(<[u8; crate::hash::HASH_BYTES]>::try_from(hasher.finalize().as_slice()).unwrap())
482 }
483
484 pub fn compile_instruction(&self, ix: &Instruction) -> CompiledInstruction {
485 compile_instruction(ix, &self.account_keys)
486 }
487
488 pub fn serialize(&self) -> Vec<u8> {
489 bincode::serialize(self).unwrap()
490 }
491
492 pub fn program_id(&self, instruction_index: usize) -> Option<&Pubkey> {
493 Some(
494 &self.account_keys[self.instructions.get(instruction_index)?.program_id_index as usize],
495 )
496 }
497
498 pub fn program_index(&self, instruction_index: usize) -> Option<usize> {
499 Some(self.instructions.get(instruction_index)?.program_id_index as usize)
500 }
501
502 pub fn program_ids(&self) -> Vec<&Pubkey> {
503 self.instructions
504 .iter()
505 .map(|ix| &self.account_keys[ix.program_id_index as usize])
506 .collect()
507 }
508
509 pub fn is_key_passed_to_program(&self, key_index: usize) -> bool {
510 if let Ok(key_index) = u8::try_from(key_index) {
511 self.instructions
512 .iter()
513 .any(|ix| ix.accounts.contains(&key_index))
514 } else {
515 false
516 }
517 }
518
519 pub fn is_key_called_as_program(&self, key_index: usize) -> bool {
520 if let Ok(key_index) = u8::try_from(key_index) {
521 self.instructions
522 .iter()
523 .any(|ix| ix.program_id_index == key_index)
524 } else {
525 false
526 }
527 }
528
529 pub fn is_non_loader_key(&self, key_index: usize) -> bool {
530 !self.is_key_called_as_program(key_index) || self.is_key_passed_to_program(key_index)
531 }
532
533 pub fn program_position(&self, index: usize) -> Option<usize> {
534 let program_ids = self.program_ids();
535 program_ids
536 .iter()
537 .position(|&&pubkey| pubkey == self.account_keys[index])
538 }
539
540 pub fn maybe_executable(&self, i: usize) -> bool {
541 self.program_position(i).is_some()
542 }
543
544 pub fn demote_program_id(&self, i: usize) -> bool {
545 self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present()
546 }
547
548 pub fn is_writable(&self, i: usize) -> bool {
549 (i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
550 as usize
551 || (i >= self.header.num_required_signatures as usize
552 && i < self.account_keys.len()
553 - self.header.num_readonly_unsigned_accounts as usize))
554 && !is_builtin_key_or_sysvar(&self.account_keys[i])
555 && !self.demote_program_id(i)
556 }
557
558 pub fn is_signer(&self, i: usize) -> bool {
559 i < self.header.num_required_signatures as usize
560 }
561
562 #[deprecated]
563 pub fn get_account_keys_by_lock_type(&self) -> (Vec<&Pubkey>, Vec<&Pubkey>) {
564 let mut writable_keys = vec![];
565 let mut readonly_keys = vec![];
566 for (i, key) in self.account_keys.iter().enumerate() {
567 if self.is_writable(i) {
568 writable_keys.push(key);
569 } else {
570 readonly_keys.push(key);
571 }
572 }
573 (writable_keys, readonly_keys)
574 }
575
576 #[deprecated]
577 pub fn deserialize_instruction(
578 index: usize,
579 data: &[u8],
580 ) -> Result<Instruction, SanitizeError> {
581 #[allow(deprecated)]
582 sysvar::instructions::load_instruction_at(index, data)
583 }
584
585 pub fn signer_keys(&self) -> Vec<&Pubkey> {
586 let last_key = self
588 .account_keys
589 .len()
590 .min(self.header.num_required_signatures as usize);
591 self.account_keys[..last_key].iter().collect()
592 }
593
594 pub fn has_duplicates(&self) -> bool {
596 for i in 1..self.account_keys.len() {
600 #[allow(clippy::integer_arithmetic)]
601 if self.account_keys[i..].contains(&self.account_keys[i - 1]) {
602 return true;
603 }
604 }
605 false
606 }
607
608 pub fn is_upgradeable_loader_present(&self) -> bool {
610 self.account_keys
611 .iter()
612 .any(|&key| key == bpf_loader_upgradeable::id())
613 }
614}
615
616#[cfg(test)]
617mod tests {
618 #![allow(deprecated)]
619 use {
620 super::*,
621 crate::{hash, instruction::AccountMeta, message::MESSAGE_HEADER_LENGTH},
622 std::collections::HashSet,
623 };
624
625 #[test]
626 fn test_builtin_program_keys() {
627 let keys: HashSet<Pubkey> = BUILTIN_PROGRAMS_KEYS.iter().copied().collect();
628 assert_eq!(keys.len(), 10);
629 for k in keys {
630 let k = format!("{}", k);
631 assert!(k.ends_with("11111111111111111111111"));
632 }
633 }
634
635 #[test]
636 fn test_builtin_program_keys_abi_freeze() {
637 let builtins = format!("{:?}", *BUILTIN_PROGRAMS_KEYS);
640 assert_eq!(
641 format!("{}", hash::hash(builtins.as_bytes())),
642 "ACqmMkYbo9eqK6QrRSrB3HLyR6uHhLf31SCfGUAJjiWj"
643 );
644 }
645
646 #[test]
647 fn test_message_signed_keys_len() {
649 let program_id = Pubkey::default();
650 let id0 = Pubkey::default();
651 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
652 let message = Message::new(&[ix], None);
653 assert_eq!(message.header.num_required_signatures, 0);
654
655 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
656 let message = Message::new(&[ix], Some(&id0));
657 assert_eq!(message.header.num_required_signatures, 1);
658 }
659
660 #[test]
661 fn test_message_kitchen_sink() {
662 let program_id0 = Pubkey::new_unique();
663 let program_id1 = Pubkey::new_unique();
664 let id0 = Pubkey::default();
665 let id1 = Pubkey::new_unique();
666 let message = Message::new(
667 &[
668 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
669 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id1, true)]),
670 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, false)]),
671 ],
672 Some(&id1),
673 );
674 assert_eq!(
675 message.instructions[0],
676 CompiledInstruction::new(2, &0, vec![1])
677 );
678 assert_eq!(
679 message.instructions[1],
680 CompiledInstruction::new(3, &0, vec![0])
681 );
682 assert_eq!(
683 message.instructions[2],
684 CompiledInstruction::new(2, &0, vec![0])
685 );
686 }
687
688 #[test]
689 fn test_message_payer_first() {
690 let program_id = Pubkey::default();
691 let payer = Pubkey::new_unique();
692 let id0 = Pubkey::default();
693
694 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
695 let message = Message::new(&[ix], Some(&payer));
696 assert_eq!(message.header.num_required_signatures, 1);
697
698 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
699 let message = Message::new(&[ix], Some(&payer));
700 assert_eq!(message.header.num_required_signatures, 2);
701
702 let ix = Instruction::new_with_bincode(
703 program_id,
704 &0,
705 vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
706 );
707 let message = Message::new(&[ix], Some(&payer));
708 assert_eq!(message.header.num_required_signatures, 2);
709 }
710
711 #[test]
712 fn test_program_position() {
713 let program_id0 = Pubkey::default();
714 let program_id1 = Pubkey::new_unique();
715 let id = Pubkey::new_unique();
716 let message = Message::new(
717 &[
718 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id, false)]),
719 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id, true)]),
720 ],
721 Some(&id),
722 );
723 assert_eq!(message.program_position(0), None);
724 assert_eq!(message.program_position(1), Some(0));
725 assert_eq!(message.program_position(2), Some(1));
726 }
727
728 #[test]
729 fn test_is_writable() {
730 let key0 = Pubkey::new_unique();
731 let key1 = Pubkey::new_unique();
732 let key2 = Pubkey::new_unique();
733 let key3 = Pubkey::new_unique();
734 let key4 = Pubkey::new_unique();
735 let key5 = Pubkey::new_unique();
736
737 let message = Message {
738 header: MessageHeader {
739 num_required_signatures: 3,
740 num_readonly_signed_accounts: 2,
741 num_readonly_unsigned_accounts: 1,
742 },
743 account_keys: vec![key0, key1, key2, key3, key4, key5],
744 recent_blockhash: Hash::default(),
745 instructions: vec![],
746 };
747 assert!(message.is_writable(0));
748 assert!(!message.is_writable(1));
749 assert!(!message.is_writable(2));
750 assert!(message.is_writable(3));
751 assert!(message.is_writable(4));
752 assert!(!message.is_writable(5));
753 }
754
755 #[test]
756 fn test_get_account_keys_by_lock_type() {
757 let program_id = Pubkey::default();
758 let id0 = Pubkey::new_unique();
759 let id1 = Pubkey::new_unique();
760 let id2 = Pubkey::new_unique();
761 let id3 = Pubkey::new_unique();
762 let message = Message::new(
763 &[
764 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
765 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id1, true)]),
766 Instruction::new_with_bincode(
767 program_id,
768 &0,
769 vec![AccountMeta::new_readonly(id2, false)],
770 ),
771 Instruction::new_with_bincode(
772 program_id,
773 &0,
774 vec![AccountMeta::new_readonly(id3, true)],
775 ),
776 ],
777 Some(&id1),
778 );
779 assert_eq!(
780 message.get_account_keys_by_lock_type(),
781 (vec![&id1, &id0], vec![&id3, &program_id, &id2])
782 );
783 }
784
785 #[test]
786 fn test_program_ids() {
787 let key0 = Pubkey::new_unique();
788 let key1 = Pubkey::new_unique();
789 let loader2 = Pubkey::new_unique();
790 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
791 let message = Message::new_with_compiled_instructions(
792 1,
793 0,
794 2,
795 vec![key0, key1, loader2],
796 Hash::default(),
797 instructions,
798 );
799 assert_eq!(message.program_ids(), vec![&loader2]);
800 }
801
802 #[test]
803 fn test_is_key_passed_to_program() {
804 let key0 = Pubkey::new_unique();
805 let key1 = Pubkey::new_unique();
806 let loader2 = Pubkey::new_unique();
807 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
808 let message = Message::new_with_compiled_instructions(
809 1,
810 0,
811 2,
812 vec![key0, key1, loader2],
813 Hash::default(),
814 instructions,
815 );
816
817 assert!(message.is_key_passed_to_program(0));
818 assert!(message.is_key_passed_to_program(1));
819 assert!(!message.is_key_passed_to_program(2));
820 }
821
822 #[test]
823 fn test_is_non_loader_key() {
824 let key0 = Pubkey::new_unique();
825 let key1 = Pubkey::new_unique();
826 let loader2 = Pubkey::new_unique();
827 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
828 let message = Message::new_with_compiled_instructions(
829 1,
830 0,
831 2,
832 vec![key0, key1, loader2],
833 Hash::default(),
834 instructions,
835 );
836 assert!(message.is_non_loader_key(0));
837 assert!(message.is_non_loader_key(1));
838 assert!(!message.is_non_loader_key(2));
839 }
840
841 #[test]
842 fn test_message_header_len_constant() {
843 assert_eq!(
844 bincode::serialized_size(&MessageHeader::default()).unwrap() as usize,
845 MESSAGE_HEADER_LENGTH
846 );
847 }
848
849 #[test]
850 fn test_message_hash() {
851 let program_id0 = Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap();
854 let program_id1 = Pubkey::from_str("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh").unwrap();
855 let id0 = Pubkey::from_str("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3").unwrap();
856 let id1 = Pubkey::from_str("GcdayuLaLyrdmUu324nahyv33G5poQdLUEZ1nEytDeP").unwrap();
857 let id2 = Pubkey::from_str("LX3EUdRUBUa3TbsYXLEUdj9J3prXkWXvLYSWyYyc2Jj").unwrap();
858 let id3 = Pubkey::from_str("QRSsyMWN1yHT9ir42bgNZUNZ4PdEhcSWCrL2AryKpy5").unwrap();
859 let instructions = vec![
860 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
861 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
862 Instruction::new_with_bincode(
863 program_id1,
864 &0,
865 vec![AccountMeta::new_readonly(id2, false)],
866 ),
867 Instruction::new_with_bincode(
868 program_id1,
869 &0,
870 vec![AccountMeta::new_readonly(id3, true)],
871 ),
872 ];
873
874 let message = Message::new(&instructions, Some(&id1));
875 assert_eq!(
876 message.hash(),
877 Hash::from_str("7VWCF4quo2CcWQFNUayZiorxpiR5ix8YzLebrXKf3fMF").unwrap()
878 )
879 }
880}