1#![allow(clippy::arithmetic_side_effects)]
13
14#[cfg(target_arch = "wasm32")]
15use crate::wasm_bindgen;
16#[allow(deprecated)]
17pub use builtins::{BUILTIN_PROGRAMS_KEYS, MAYBE_BUILTIN_KEY_OR_SYSVAR};
18use {
19 crate::{
20 bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
21 hash::Hash,
22 instruction::{CompiledInstruction, Instruction},
23 message::{compiled_keys::CompiledKeys, MessageHeader},
24 pubkey::Pubkey,
25 system_instruction, system_program, sysvar,
26 },
27 solana_sanitize::{Sanitize, SanitizeError},
28 solana_short_vec as short_vec,
29 std::{collections::HashSet, convert::TryFrom, str::FromStr},
30};
31
32#[deprecated(
33 since = "2.0.0",
34 note = "please use `solana_sdk::reserved_account_keys::ReservedAccountKeys` instead"
35)]
36#[allow(deprecated)]
37mod builtins {
38 use {super::*, lazy_static::lazy_static};
39
40 lazy_static! {
41 pub static ref BUILTIN_PROGRAMS_KEYS: [Pubkey; 10] = {
42 let parse = |s| Pubkey::from_str(s).unwrap();
43 [
44 parse("Config1111111111111111111111111111111111111"),
45 parse("Feature111111111111111111111111111111111111"),
46 parse("NativeLoader1111111111111111111111111111111"),
47 parse("Stake11111111111111111111111111111111111111"),
48 parse("StakeConfig11111111111111111111111111111111"),
49 parse("Vote111111111111111111111111111111111111111"),
50 system_program::id(),
51 bpf_loader::id(),
52 bpf_loader_deprecated::id(),
53 bpf_loader_upgradeable::id(),
54 ]
55 };
56 }
57
58 lazy_static! {
59 pub static ref MAYBE_BUILTIN_KEY_OR_SYSVAR: [bool; 256] = {
65 let mut temp_table: [bool; 256] = [false; 256];
66 BUILTIN_PROGRAMS_KEYS.iter().for_each(|key| temp_table[key.as_ref()[0] as usize] = true);
67 sysvar::ALL_IDS.iter().for_each(|key| temp_table[key.as_ref()[0] as usize] = true);
68 temp_table
69 };
70 }
71}
72
73#[deprecated(
74 since = "2.0.0",
75 note = "please use `solana_sdk::reserved_account_keys::ReservedAccountKeys::is_reserved` instead"
76)]
77#[allow(deprecated)]
78pub fn is_builtin_key_or_sysvar(key: &Pubkey) -> bool {
79 if MAYBE_BUILTIN_KEY_OR_SYSVAR[key.as_ref()[0] as usize] {
80 return sysvar::is_sysvar_id(key) || BUILTIN_PROGRAMS_KEYS.contains(key);
81 }
82 false
83}
84
85fn position(keys: &[Pubkey], key: &Pubkey) -> u8 {
86 keys.iter().position(|k| k == key).unwrap() as u8
87}
88
89fn compile_instruction(ix: &Instruction, keys: &[Pubkey]) -> CompiledInstruction {
90 let accounts: Vec<_> = ix
91 .accounts
92 .iter()
93 .map(|account_meta| position(keys, &account_meta.pubkey))
94 .collect();
95
96 CompiledInstruction {
97 program_id_index: position(keys, &ix.program_id),
98 data: ix.data.clone(),
99 accounts,
100 }
101}
102
103fn compile_instructions(ixs: &[Instruction], keys: &[Pubkey]) -> Vec<CompiledInstruction> {
104 ixs.iter().map(|ix| compile_instruction(ix, keys)).collect()
105}
106
107#[cfg(not(target_arch = "wasm32"))]
124#[cfg_attr(
125 feature = "frozen-abi",
126 frozen_abi(digest = "4kL6EbLGU25m5eMk4H1cW9YGhA5LejHSgj2w2fhY1NGp"),
127 derive(AbiExample)
128)]
129#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
130#[serde(rename_all = "camelCase")]
131pub struct Message {
132 pub header: MessageHeader,
135
136 #[serde(with = "short_vec")]
138 pub account_keys: Vec<Pubkey>,
139
140 pub recent_blockhash: Hash,
142
143 #[serde(with = "short_vec")]
146 pub instructions: Vec<CompiledInstruction>,
147}
148
149#[cfg(target_arch = "wasm32")]
153#[wasm_bindgen]
154#[cfg_attr(
155 feature = "frozen-abi",
156 frozen_abi(digest = "4kL6EbLGU25m5eMk4H1cW9YGhA5LejHSgj2w2fhY1NGp"),
157 derive(AbiExample)
158)]
159#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
160#[serde(rename_all = "camelCase")]
161pub struct Message {
162 #[wasm_bindgen(skip)]
163 pub header: MessageHeader,
164
165 #[wasm_bindgen(skip)]
166 #[serde(with = "short_vec")]
167 pub account_keys: Vec<Pubkey>,
168
169 pub recent_blockhash: Hash,
171
172 #[wasm_bindgen(skip)]
173 #[serde(with = "short_vec")]
174 pub instructions: Vec<CompiledInstruction>,
175}
176
177impl Sanitize for Message {
178 fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
179 if self.header.num_required_signatures as usize
181 + self.header.num_readonly_unsigned_accounts as usize
182 > self.account_keys.len()
183 {
184 return Err(SanitizeError::IndexOutOfBounds);
185 }
186
187 if self.header.num_readonly_signed_accounts >= self.header.num_required_signatures {
189 return Err(SanitizeError::IndexOutOfBounds);
190 }
191
192 for ci in &self.instructions {
193 if ci.program_id_index as usize >= self.account_keys.len() {
194 return Err(SanitizeError::IndexOutOfBounds);
195 }
196 if ci.program_id_index == 0 {
198 return Err(SanitizeError::IndexOutOfBounds);
199 }
200 for ai in &ci.accounts {
201 if *ai as usize >= self.account_keys.len() {
202 return Err(SanitizeError::IndexOutOfBounds);
203 }
204 }
205 }
206 self.account_keys.sanitize()?;
207 self.recent_blockhash.sanitize()?;
208 self.instructions.sanitize()?;
209 Ok(())
210 }
211}
212
213impl Message {
214 pub fn new(instructions: &[Instruction], payer: Option<&Pubkey>) -> Self {
283 Self::new_with_blockhash(instructions, payer, &Hash::default())
284 }
285
286 pub fn new_with_blockhash(
358 instructions: &[Instruction],
359 payer: Option<&Pubkey>,
360 blockhash: &Hash,
361 ) -> Self {
362 let compiled_keys = CompiledKeys::compile(instructions, payer.cloned());
363 let (header, account_keys) = compiled_keys
364 .try_into_message_components()
365 .expect("overflow when compiling message keys");
366 let instructions = compile_instructions(instructions, &account_keys);
367 Self::new_with_compiled_instructions(
368 header.num_required_signatures,
369 header.num_readonly_signed_accounts,
370 header.num_readonly_unsigned_accounts,
371 account_keys,
372 *blockhash,
373 instructions,
374 )
375 }
376
377 pub fn new_with_nonce(
485 mut instructions: Vec<Instruction>,
486 payer: Option<&Pubkey>,
487 nonce_account_pubkey: &Pubkey,
488 nonce_authority_pubkey: &Pubkey,
489 ) -> Self {
490 let nonce_ix =
491 system_instruction::advance_nonce_account(nonce_account_pubkey, nonce_authority_pubkey);
492 instructions.insert(0, nonce_ix);
493 Self::new(&instructions, payer)
494 }
495
496 pub fn new_with_compiled_instructions(
497 num_required_signatures: u8,
498 num_readonly_signed_accounts: u8,
499 num_readonly_unsigned_accounts: u8,
500 account_keys: Vec<Pubkey>,
501 recent_blockhash: Hash,
502 instructions: Vec<CompiledInstruction>,
503 ) -> Self {
504 Self {
505 header: MessageHeader {
506 num_required_signatures,
507 num_readonly_signed_accounts,
508 num_readonly_unsigned_accounts,
509 },
510 account_keys,
511 recent_blockhash,
512 instructions,
513 }
514 }
515
516 #[cfg(not(target_os = "solana"))]
518 pub fn hash(&self) -> Hash {
519 let message_bytes = self.serialize();
520 Self::hash_raw_message(&message_bytes)
521 }
522
523 #[cfg(not(target_os = "solana"))]
525 pub fn hash_raw_message(message_bytes: &[u8]) -> Hash {
526 use {blake3::traits::digest::Digest, solana_hash::HASH_BYTES};
527 let mut hasher = blake3::Hasher::new();
528 hasher.update(b"solana-tx-message-v1");
529 hasher.update(message_bytes);
530 let hash_bytes: [u8; HASH_BYTES] = hasher.finalize().into();
531 hash_bytes.into()
532 }
533
534 pub fn compile_instruction(&self, ix: &Instruction) -> CompiledInstruction {
535 compile_instruction(ix, &self.account_keys)
536 }
537
538 pub fn serialize(&self) -> Vec<u8> {
539 bincode::serialize(self).unwrap()
540 }
541
542 pub fn program_id(&self, instruction_index: usize) -> Option<&Pubkey> {
543 Some(
544 &self.account_keys[self.instructions.get(instruction_index)?.program_id_index as usize],
545 )
546 }
547
548 pub fn program_index(&self, instruction_index: usize) -> Option<usize> {
549 Some(self.instructions.get(instruction_index)?.program_id_index as usize)
550 }
551
552 pub fn program_ids(&self) -> Vec<&Pubkey> {
553 self.instructions
554 .iter()
555 .map(|ix| &self.account_keys[ix.program_id_index as usize])
556 .collect()
557 }
558
559 #[deprecated(since = "2.0.0", note = "Please use `is_instruction_account` instead")]
560 pub fn is_key_passed_to_program(&self, key_index: usize) -> bool {
561 self.is_instruction_account(key_index)
562 }
563
564 pub fn is_instruction_account(&self, key_index: usize) -> bool {
567 if let Ok(key_index) = u8::try_from(key_index) {
568 self.instructions
569 .iter()
570 .any(|ix| ix.accounts.contains(&key_index))
571 } else {
572 false
573 }
574 }
575
576 pub fn is_key_called_as_program(&self, key_index: usize) -> bool {
577 if let Ok(key_index) = u8::try_from(key_index) {
578 self.instructions
579 .iter()
580 .any(|ix| ix.program_id_index == key_index)
581 } else {
582 false
583 }
584 }
585
586 #[deprecated(
587 since = "2.0.0",
588 note = "Please use `is_key_called_as_program` and `is_instruction_account` directly"
589 )]
590 pub fn is_non_loader_key(&self, key_index: usize) -> bool {
591 !self.is_key_called_as_program(key_index) || self.is_instruction_account(key_index)
592 }
593
594 pub fn program_position(&self, index: usize) -> Option<usize> {
595 let program_ids = self.program_ids();
596 program_ids
597 .iter()
598 .position(|&&pubkey| pubkey == self.account_keys[index])
599 }
600
601 pub fn maybe_executable(&self, i: usize) -> bool {
602 self.program_position(i).is_some()
603 }
604
605 pub fn demote_program_id(&self, i: usize) -> bool {
606 self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present()
607 }
608
609 pub(super) fn is_writable_index(&self, i: usize) -> bool {
612 i < (self.header.num_required_signatures - self.header.num_readonly_signed_accounts)
613 as usize
614 || (i >= self.header.num_required_signatures as usize
615 && i < self.account_keys.len()
616 - self.header.num_readonly_unsigned_accounts as usize)
617 }
618
619 #[deprecated(since = "2.0.0", note = "Please use `is_maybe_writable` instead")]
624 #[allow(deprecated)]
625 pub fn is_writable(&self, i: usize) -> bool {
626 (self.is_writable_index(i))
627 && !is_builtin_key_or_sysvar(&self.account_keys[i])
628 && !self.demote_program_id(i)
629 }
630
631 pub fn is_maybe_writable(
638 &self,
639 i: usize,
640 reserved_account_keys: Option<&HashSet<Pubkey>>,
641 ) -> bool {
642 (self.is_writable_index(i))
643 && !self.is_account_maybe_reserved(i, reserved_account_keys)
644 && !self.demote_program_id(i)
645 }
646
647 fn is_account_maybe_reserved(
650 &self,
651 key_index: usize,
652 reserved_account_keys: Option<&HashSet<Pubkey>>,
653 ) -> bool {
654 let mut is_maybe_reserved = false;
655 if let Some(reserved_account_keys) = reserved_account_keys {
656 if let Some(key) = self.account_keys.get(key_index) {
657 is_maybe_reserved = reserved_account_keys.contains(key);
658 }
659 }
660 is_maybe_reserved
661 }
662
663 pub fn is_signer(&self, i: usize) -> bool {
664 i < self.header.num_required_signatures as usize
665 }
666
667 pub fn signer_keys(&self) -> Vec<&Pubkey> {
668 let last_key = self
670 .account_keys
671 .len()
672 .min(self.header.num_required_signatures as usize);
673 self.account_keys[..last_key].iter().collect()
674 }
675
676 pub fn has_duplicates(&self) -> bool {
678 for i in 1..self.account_keys.len() {
682 #[allow(clippy::arithmetic_side_effects)]
683 if self.account_keys[i..].contains(&self.account_keys[i - 1]) {
684 return true;
685 }
686 }
687 false
688 }
689
690 pub fn is_upgradeable_loader_present(&self) -> bool {
692 self.account_keys
693 .iter()
694 .any(|&key| key == bpf_loader_upgradeable::id())
695 }
696}
697
698#[cfg(test)]
699mod tests {
700 #![allow(deprecated)]
701 use {
702 super::*,
703 crate::{hash, instruction::AccountMeta, message::MESSAGE_HEADER_LENGTH},
704 std::collections::HashSet,
705 };
706
707 #[test]
708 fn test_builtin_program_keys() {
709 let keys: HashSet<Pubkey> = BUILTIN_PROGRAMS_KEYS.iter().copied().collect();
710 assert_eq!(keys.len(), 10);
711 for k in keys {
712 let k = format!("{k}");
713 assert!(k.ends_with("11111111111111111111111"));
714 }
715 }
716
717 #[test]
718 fn test_builtin_program_keys_abi_freeze() {
719 let builtins = format!("{:?}", *BUILTIN_PROGRAMS_KEYS);
722 assert_eq!(
723 format!("{}", hash::hash(builtins.as_bytes())),
724 "ACqmMkYbo9eqK6QrRSrB3HLyR6uHhLf31SCfGUAJjiWj"
725 );
726 }
727
728 #[test]
729 fn test_message_signed_keys_len() {
731 let program_id = Pubkey::default();
732 let id0 = Pubkey::default();
733 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
734 let message = Message::new(&[ix], None);
735 assert_eq!(message.header.num_required_signatures, 0);
736
737 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
738 let message = Message::new(&[ix], Some(&id0));
739 assert_eq!(message.header.num_required_signatures, 1);
740 }
741
742 #[test]
743 fn test_message_kitchen_sink() {
744 let program_id0 = Pubkey::new_unique();
745 let program_id1 = Pubkey::new_unique();
746 let id0 = Pubkey::default();
747 let id1 = Pubkey::new_unique();
748 let message = Message::new(
749 &[
750 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
751 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id1, true)]),
752 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, false)]),
753 ],
754 Some(&id1),
755 );
756 assert_eq!(
757 message.instructions[0],
758 CompiledInstruction::new(2, &0, vec![1])
759 );
760 assert_eq!(
761 message.instructions[1],
762 CompiledInstruction::new(3, &0, vec![0])
763 );
764 assert_eq!(
765 message.instructions[2],
766 CompiledInstruction::new(2, &0, vec![0])
767 );
768 }
769
770 #[test]
771 fn test_message_payer_first() {
772 let program_id = Pubkey::default();
773 let payer = Pubkey::new_unique();
774 let id0 = Pubkey::default();
775
776 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]);
777 let message = Message::new(&[ix], Some(&payer));
778 assert_eq!(message.header.num_required_signatures, 1);
779
780 let ix = Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, true)]);
781 let message = Message::new(&[ix], Some(&payer));
782 assert_eq!(message.header.num_required_signatures, 2);
783
784 let ix = Instruction::new_with_bincode(
785 program_id,
786 &0,
787 vec![AccountMeta::new(payer, true), AccountMeta::new(id0, true)],
788 );
789 let message = Message::new(&[ix], Some(&payer));
790 assert_eq!(message.header.num_required_signatures, 2);
791 }
792
793 #[test]
794 fn test_program_position() {
795 let program_id0 = Pubkey::default();
796 let program_id1 = Pubkey::new_unique();
797 let id = Pubkey::new_unique();
798 let message = Message::new(
799 &[
800 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id, false)]),
801 Instruction::new_with_bincode(program_id1, &0, vec![AccountMeta::new(id, true)]),
802 ],
803 Some(&id),
804 );
805 assert_eq!(message.program_position(0), None);
806 assert_eq!(message.program_position(1), Some(0));
807 assert_eq!(message.program_position(2), Some(1));
808 }
809
810 #[test]
811 fn test_is_writable() {
812 let key0 = Pubkey::new_unique();
813 let key1 = Pubkey::new_unique();
814 let key2 = Pubkey::new_unique();
815 let key3 = Pubkey::new_unique();
816 let key4 = Pubkey::new_unique();
817 let key5 = Pubkey::new_unique();
818
819 let message = Message {
820 header: MessageHeader {
821 num_required_signatures: 3,
822 num_readonly_signed_accounts: 2,
823 num_readonly_unsigned_accounts: 1,
824 },
825 account_keys: vec![key0, key1, key2, key3, key4, key5],
826 recent_blockhash: Hash::default(),
827 instructions: vec![],
828 };
829 assert!(message.is_writable(0));
830 assert!(!message.is_writable(1));
831 assert!(!message.is_writable(2));
832 assert!(message.is_writable(3));
833 assert!(message.is_writable(4));
834 assert!(!message.is_writable(5));
835 }
836
837 #[test]
838 fn test_is_maybe_writable() {
839 let key0 = Pubkey::new_unique();
840 let key1 = Pubkey::new_unique();
841 let key2 = Pubkey::new_unique();
842 let key3 = Pubkey::new_unique();
843 let key4 = Pubkey::new_unique();
844 let key5 = Pubkey::new_unique();
845
846 let message = Message {
847 header: MessageHeader {
848 num_required_signatures: 3,
849 num_readonly_signed_accounts: 2,
850 num_readonly_unsigned_accounts: 1,
851 },
852 account_keys: vec![key0, key1, key2, key3, key4, key5],
853 recent_blockhash: Hash::default(),
854 instructions: vec![],
855 };
856
857 let reserved_account_keys = HashSet::from([key3]);
858
859 assert!(message.is_maybe_writable(0, Some(&reserved_account_keys)));
860 assert!(!message.is_maybe_writable(1, Some(&reserved_account_keys)));
861 assert!(!message.is_maybe_writable(2, Some(&reserved_account_keys)));
862 assert!(!message.is_maybe_writable(3, Some(&reserved_account_keys)));
863 assert!(message.is_maybe_writable(3, None));
864 assert!(message.is_maybe_writable(4, Some(&reserved_account_keys)));
865 assert!(!message.is_maybe_writable(5, Some(&reserved_account_keys)));
866 assert!(!message.is_maybe_writable(6, Some(&reserved_account_keys)));
867 }
868
869 #[test]
870 fn test_is_account_maybe_reserved() {
871 let key0 = Pubkey::new_unique();
872 let key1 = Pubkey::new_unique();
873
874 let message = Message {
875 account_keys: vec![key0, key1],
876 ..Message::default()
877 };
878
879 let reserved_account_keys = HashSet::from([key1]);
880
881 assert!(!message.is_account_maybe_reserved(0, Some(&reserved_account_keys)));
882 assert!(message.is_account_maybe_reserved(1, Some(&reserved_account_keys)));
883 assert!(!message.is_account_maybe_reserved(2, Some(&reserved_account_keys)));
884 assert!(!message.is_account_maybe_reserved(0, None));
885 assert!(!message.is_account_maybe_reserved(1, None));
886 assert!(!message.is_account_maybe_reserved(2, None));
887 }
888
889 #[test]
890 fn test_program_ids() {
891 let key0 = Pubkey::new_unique();
892 let key1 = Pubkey::new_unique();
893 let loader2 = Pubkey::new_unique();
894 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
895 let message = Message::new_with_compiled_instructions(
896 1,
897 0,
898 2,
899 vec![key0, key1, loader2],
900 Hash::default(),
901 instructions,
902 );
903 assert_eq!(message.program_ids(), vec![&loader2]);
904 }
905
906 #[test]
907 fn test_is_instruction_account() {
908 let key0 = Pubkey::new_unique();
909 let key1 = Pubkey::new_unique();
910 let loader2 = Pubkey::new_unique();
911 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
912 let message = Message::new_with_compiled_instructions(
913 1,
914 0,
915 2,
916 vec![key0, key1, loader2],
917 Hash::default(),
918 instructions,
919 );
920
921 assert!(message.is_instruction_account(0));
922 assert!(message.is_instruction_account(1));
923 assert!(!message.is_instruction_account(2));
924 }
925
926 #[test]
927 fn test_is_non_loader_key() {
928 #![allow(deprecated)]
929 let key0 = Pubkey::new_unique();
930 let key1 = Pubkey::new_unique();
931 let loader2 = Pubkey::new_unique();
932 let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
933 let message = Message::new_with_compiled_instructions(
934 1,
935 0,
936 2,
937 vec![key0, key1, loader2],
938 Hash::default(),
939 instructions,
940 );
941 assert!(message.is_non_loader_key(0));
942 assert!(message.is_non_loader_key(1));
943 assert!(!message.is_non_loader_key(2));
944 }
945
946 #[test]
947 fn test_message_header_len_constant() {
948 assert_eq!(
949 bincode::serialized_size(&MessageHeader::default()).unwrap() as usize,
950 MESSAGE_HEADER_LENGTH
951 );
952 }
953
954 #[test]
955 fn test_message_hash() {
956 let program_id0 = Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap();
959 let program_id1 = Pubkey::from_str("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh").unwrap();
960 let id0 = Pubkey::from_str("CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3").unwrap();
961 let id1 = Pubkey::from_str("GcdayuLaLyrdmUu324nahyv33G5poQdLUEZ1nEytDeP").unwrap();
962 let id2 = Pubkey::from_str("LX3EUdRUBUa3TbsYXLEUdj9J3prXkWXvLYSWyYyc2Jj").unwrap();
963 let id3 = Pubkey::from_str("QRSsyMWN1yHT9ir42bgNZUNZ4PdEhcSWCrL2AryKpy5").unwrap();
964 let instructions = vec![
965 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id0, false)]),
966 Instruction::new_with_bincode(program_id0, &0, vec![AccountMeta::new(id1, true)]),
967 Instruction::new_with_bincode(
968 program_id1,
969 &0,
970 vec![AccountMeta::new_readonly(id2, false)],
971 ),
972 Instruction::new_with_bincode(
973 program_id1,
974 &0,
975 vec![AccountMeta::new_readonly(id3, true)],
976 ),
977 ];
978
979 let message = Message::new(&instructions, Some(&id1));
980 assert_eq!(
981 message.hash(),
982 Hash::from_str("7VWCF4quo2CcWQFNUayZiorxpiR5ix8YzLebrXKf3fMF").unwrap()
983 )
984 }
985}