gemachain_program/message/
legacy.rs

1#![allow(clippy::integer_arithmetic)]
2//! A library for generating a message from a sequence of instructions
3
4use 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    // Copied keys over since direct references create cyclical dependency.
22    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/// A helper struct to collect pubkeys referenced by a set of instructions and read-only counts
62#[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
86/// Return pubkeys referenced by all instructions, with the ones needing signatures first. If the
87/// payer key is provided, it is always placed first in the list of signed keys. Read-only signed
88/// accounts are placed last in the set of signed accounts. Read-only unsigned accounts,
89/// including program ids, are placed last in the set. No duplicates and order is preserved.
90fn 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        // Promote to writable if a later AccountMeta requires it
123        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
158/// Return program ids referenced by all instructions.  No duplicates and order is preserved.
159fn get_program_ids(instructions: &[Instruction]) -> Vec<Pubkey> {
160    instructions
161        .iter()
162        .map(|ix| ix.program_id)
163        .unique()
164        .collect()
165}
166
167// NOTE: Serialization-related changes must be paired with the custom serialization
168// for versioned messages in the `RemainingLegacyMessage` struct.
169#[frozen_abi(digest = "2KnLEqfLcTBQqitE22Pp8JYkaqVVbAkGbCfdeHoyxcAU")]
170#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, AbiExample)]
171#[serde(rename_all = "camelCase")]
172pub struct Message {
173    /// The message header, identifying signed and read-only `account_keys`
174    /// NOTE: Serialization-related changes must be paired with the direct read at sigverify.
175    pub header: MessageHeader,
176
177    /// All the account keys used by this transaction
178    #[serde(with = "short_vec")]
179    pub account_keys: Vec<Pubkey>,
180
181    /// The id of a recent ledger entry.
182    pub recent_blockhash: Hash,
183
184    /// Programs that will be executed in sequence and committed in one atomic transaction if all
185    /// succeed.
186    #[serde(with = "short_vec")]
187    pub instructions: Vec<CompiledInstruction>,
188}
189
190impl Sanitize for Message {
191    fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
192        // signing area and read-only non-signing area should not overlap
193        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        // there should be at least 1 RW fee-payer account.
201        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            // A program cannot be a payer.
210            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    /// Compute the blake3 hash of this transaction's message
280    #[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    /// Compute the blake3 hash of a raw transaction message
287    #[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, /*demote_program_write_locks=*/ true) {
379                writable_keys.push(key);
380            } else {
381                readonly_keys.push(key);
382            }
383        }
384        (writable_keys, readonly_keys)
385    }
386
387    // First encode the number of instructions:
388    // [0..2 - num_instructions
389    //
390    // Then a table of offsets of where to find them in the data
391    //  3..2 * num_instructions table of instruction offsets
392    //
393    // Each instruction is then encoded as:
394    //   0..2 - num_accounts
395    //   2 - meta_byte -> (bit 0 signer, bit 1 is_writable)
396    //   3..35 - pubkey - 32 bytes
397    //   35..67 - program_id
398    //   67..69 - data len - u16
399    //   69..data_len - data
400    #[deprecated]
401    pub fn serialize_instructions(&self) -> Vec<u8> {
402        // 64 bytes is a reasonable guess, calculating exactly is slower in benchmarks
403        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, /*demote_program_write_locks=*/ 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        // index into the instruction byte-offset table.
451        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        // Clamp in case we're working on un-`sanitize()`ed input
486        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    /// Return true if account_keys has any duplicate keys
494    pub fn has_duplicates(&self) -> bool {
495        // Note: This is an O(n^2) algorithm, but requires no heap allocations. The benchmark
496        // `bench_has_duplicates` in benches/message_processor.rs shows that this implementation is
497        // ~50 times faster than using HashSet for very short slices.
498        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        // Once the feature is flipped on, we can't further modify
538        // BUILTIN_PROGRAMS_KEYS without the risk of breaking consensus.
539        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(); // Key less than program_id0
562        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        // Ensure the key is no longer readonly
645        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        // Ensure the key is no longer readonly
665        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(); // Key less than id0
673        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    // Ensure there's a way to calculate the number of required signatures.
716    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(); // Identical key/program_id should be de-duped
732        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        // when this test fails, it's most likely due to a new serialized format of a message.
1030        // in this case, the domain prefix `gemachain-tx-message-v1` should be updated.
1031        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}