Skip to main content

solana_instructions_sysvar/
lib.rs

1//! The serialized instructions of the current transaction.
2//!
3//! The _instructions sysvar_ provides access to the serialized instruction data
4//! for the currently-running transaction. This allows for [instruction
5//! introspection][in], which is required for correctly interoperating with
6//! native programs like the [secp256k1] and [ed25519] programs.
7//!
8//! [in]: https://docs.solanalabs.com/implemented-proposals/instruction_introspection
9//! [secp256k1]: https://docs.rs/solana-secp256k1-program/latest/solana_secp256k1_program/
10//! [ed25519]: https://docs.rs/solana-ed25519-program/latest/solana_ed25519_program/
11//!
12//! Unlike other sysvars, the data in the instructions sysvar is not accessed
13//! through a type that implements the [`Sysvar`] trait. Instead, the
14//! instruction sysvar is accessed through several free functions within this
15//! module.
16//!
17//! [`Sysvar`]: https://docs.rs/solana-sysvar/latest/solana_sysvar/trait.Sysvar.html
18//!
19//! See also the Solana [documentation on the instructions sysvar][sdoc].
20//!
21//! [sdoc]: https://docs.solanalabs.com/runtime/sysvars#instructions
22//!
23//! # Examples
24//!
25//! For a complete example of how the instructions sysvar is used see the
26//! documentation for [`secp256k1_instruction`] in the `solana-sdk` crate.
27//!
28//! [`secp256k1_instruction`]: https://docs.rs/solana-sdk/latest/solana_sdk/secp256k1_instruction/index.html
29
30#![cfg_attr(docsrs, feature(doc_cfg))]
31#![allow(clippy::arithmetic_side_effects)]
32#![no_std]
33
34extern crate alloc;
35
36#[cfg(feature = "dev-context-only-utils")]
37use qualifier_attr::qualifiers;
38pub use solana_sdk_ids::sysvar::instructions::{check_id, id, ID};
39use {
40    alloc::vec::Vec,
41    solana_account_info::AccountInfo,
42    solana_instruction::{AccountMeta, Instruction},
43    solana_instruction_error::InstructionError,
44    solana_program_error::ProgramError,
45    solana_sanitize::SanitizeError,
46    solana_serialize_utils::{read_pubkey, read_slice, read_u16, read_u8},
47};
48#[cfg(not(target_os = "solana"))]
49use {
50    bitflags::bitflags,
51    solana_instruction::BorrowedInstruction,
52    solana_serialize_utils::{append_slice, append_u16, append_u8},
53};
54
55/// Instructions sysvar, dummy type.
56///
57/// This type exists for consistency with other sysvar modules, but is a dummy
58/// type that does not contain sysvar data. It implements the [`SysvarId`] trait
59/// but does not implement the [`Sysvar`] trait.
60///
61/// [`SysvarId`]: https://docs.rs/solana-sysvar-id/latest/solana_sysvar_id/trait.SysvarId.html
62/// [`Sysvar`]: https://docs.rs/solana-sysvar/latest/solana_sysvar/trait.Sysvar.html
63///
64/// Use the free functions in this module to access the instructions sysvar.
65pub struct Instructions();
66
67solana_sysvar_id::impl_sysvar_id!(Instructions);
68
69/// Construct the account data for the instructions sysvar.
70///
71/// This function is used by the runtime and not available to Solana programs.
72#[cfg(not(target_os = "solana"))]
73pub fn construct_instructions_data(
74    instructions: &[BorrowedInstruction],
75) -> Result<Vec<u8>, InstructionsSysvarError> {
76    let mut data = serialize_instructions(instructions)?;
77    // add room for current instruction index.
78    data.resize(data.len() + 2, 0);
79
80    Ok(data)
81}
82
83#[cfg(not(target_os = "solana"))]
84bitflags! {
85    struct InstructionsSysvarAccountMeta: u8 {
86        const IS_SIGNER = 0b00000001;
87        const IS_WRITABLE = 0b00000010;
88    }
89}
90
91#[derive(Debug, PartialEq, Eq)]
92pub enum InstructionsSysvarError {
93    /// The instruction index stored in the instructions sysvar account data is out of bounds.
94    InstructionIndexOutOfBounds,
95}
96
97// Instructions memory layout
98//
99// Header layout:
100//   [0..2]                      num_instructions (u16)
101//   [2..2 + 2*N]                instruction_offsets ([u16; N])
102//
103// Each instruction starts at an offset specified in `instruction_offsets`.
104// The layout of each instruction is relative to its start offset.
105//
106// Instruction layout:
107//   [0..2]                      num_accounts (u16)
108//   [2..2 + 33*A]               accounts ([AccountMeta; A])
109//   [2 + 33*A..34 + 33*A]       program_id (Address)
110//   [34 + 33*A..36 + 33*A]      data_len (u16)
111//   [36 + 33*A..36 + 33*A + D]  data (&[u8])
112//
113// AccountMeta layout:
114//   [0..1]                      meta (u8: bit 0: is_signer, bit 1: is_writable)
115//   [1..33]                     pubkey (Address)
116//
117// Where:
118// - N = num_instructions
119// - A = number of accounts in a particular instruction
120// - D = data_len
121#[cfg(not(target_os = "solana"))]
122#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
123fn serialize_instructions(
124    instructions: &[BorrowedInstruction],
125) -> Result<Vec<u8>, InstructionsSysvarError> {
126    // 64 bytes is a reasonable guess, calculating exactly is slower in benchmarks
127    let mut data = Vec::with_capacity(instructions.len() * (32 * 2));
128    append_u16(&mut data, instructions.len() as u16);
129    for _ in 0..instructions.len() {
130        append_u16(&mut data, 0);
131    }
132
133    for (i, instruction) in instructions.iter().enumerate() {
134        let start_instruction_offset = u16::try_from(data.len())
135            .map_err(|_| InstructionsSysvarError::InstructionIndexOutOfBounds)?;
136        let start = 2 + (2 * i);
137        data[start..start + 2].copy_from_slice(&start_instruction_offset.to_le_bytes());
138        append_u16(&mut data, instruction.accounts.len() as u16);
139        for account_meta in &instruction.accounts {
140            let mut account_meta_flags = InstructionsSysvarAccountMeta::empty();
141            if account_meta.is_signer {
142                account_meta_flags |= InstructionsSysvarAccountMeta::IS_SIGNER;
143            }
144            if account_meta.is_writable {
145                account_meta_flags |= InstructionsSysvarAccountMeta::IS_WRITABLE;
146            }
147            append_u8(&mut data, account_meta_flags.bits());
148            append_slice(&mut data, account_meta.pubkey.as_ref());
149        }
150
151        append_slice(&mut data, instruction.program_id.as_ref());
152        append_u16(&mut data, instruction.data.len() as u16);
153        append_slice(&mut data, instruction.data);
154    }
155    Ok(data)
156}
157
158/// Load the current `Instruction`'s index in the currently executing
159/// `Transaction`.
160///
161/// `data` is the instructions sysvar account data.
162///
163/// Unsafe because the sysvar accounts address is not checked; only used
164/// internally after such a check.
165fn load_current_index(data: &[u8]) -> u16 {
166    let mut instr_fixed_data = [0u8; 2];
167    let len = data.len();
168    instr_fixed_data.copy_from_slice(&data[len - 2..len]);
169    u16::from_le_bytes(instr_fixed_data)
170}
171
172/// Load the current `Instruction`'s index in the currently executing
173/// `Transaction`.
174///
175/// # Errors
176///
177/// Returns [`ProgramError::UnsupportedSysvar`] if the given account's ID is not equal to [`ID`].
178pub fn load_current_index_checked(
179    instruction_sysvar_account_info: &AccountInfo,
180) -> Result<u16, ProgramError> {
181    if !check_id(instruction_sysvar_account_info.key) {
182        return Err(ProgramError::UnsupportedSysvar);
183    }
184
185    let instruction_sysvar = instruction_sysvar_account_info.try_borrow_data()?;
186    let index = load_current_index(&instruction_sysvar);
187    Ok(index)
188}
189
190/// Store the current `Instruction`'s index in the instructions sysvar data.
191pub fn store_current_index_checked(
192    data: &mut [u8],
193    instruction_index: u16,
194) -> Result<(), InstructionError> {
195    if data.len() < 2 {
196        return Err(InstructionError::AccountDataTooSmall);
197    }
198    let last_index = data.len() - 2;
199    data[last_index..last_index + 2].copy_from_slice(&instruction_index.to_le_bytes());
200    Ok(())
201}
202
203#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
204fn deserialize_instruction(index: usize, data: &[u8]) -> Result<Instruction, SanitizeError> {
205    const IS_SIGNER_BIT: usize = 0;
206    const IS_WRITABLE_BIT: usize = 1;
207
208    let mut current = 0;
209    let num_instructions = read_u16(&mut current, data)?;
210    if index >= num_instructions as usize {
211        return Err(SanitizeError::IndexOutOfBounds);
212    }
213
214    // index into the instruction byte-offset table.
215    current += index * 2;
216    let start = read_u16(&mut current, data)?;
217
218    current = start as usize;
219    let num_accounts = read_u16(&mut current, data)?;
220    let mut accounts = Vec::with_capacity(num_accounts as usize);
221    for _ in 0..num_accounts {
222        let meta_byte = read_u8(&mut current, data)?;
223        let mut is_signer = false;
224        let mut is_writable = false;
225        if meta_byte & (1 << IS_SIGNER_BIT) != 0 {
226            is_signer = true;
227        }
228        if meta_byte & (1 << IS_WRITABLE_BIT) != 0 {
229            is_writable = true;
230        }
231        let pubkey = read_pubkey(&mut current, data)?;
232        accounts.push(AccountMeta {
233            pubkey,
234            is_signer,
235            is_writable,
236        });
237    }
238    let program_id = read_pubkey(&mut current, data)?;
239    let data_len = read_u16(&mut current, data)?;
240    let data = read_slice(&mut current, data, data_len as usize)?;
241    Ok(Instruction {
242        program_id,
243        accounts,
244        data,
245    })
246}
247
248/// Load an `Instruction` in the currently executing `Transaction` at the
249/// specified index.
250///
251/// `data` is the instructions sysvar account data.
252///
253/// Unsafe because the sysvar accounts address is not checked; only used
254/// internally after such a check.
255#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
256fn load_instruction_at(index: usize, data: &[u8]) -> Result<Instruction, SanitizeError> {
257    deserialize_instruction(index, data)
258}
259
260/// Load an `Instruction` in the currently executing `Transaction` at the
261/// specified index.
262///
263/// # Errors
264///
265/// Returns [`ProgramError::UnsupportedSysvar`] if the given account's ID is not equal to [`ID`].
266pub fn load_instruction_at_checked(
267    index: usize,
268    instruction_sysvar_account_info: &AccountInfo,
269) -> Result<Instruction, ProgramError> {
270    if !check_id(instruction_sysvar_account_info.key) {
271        return Err(ProgramError::UnsupportedSysvar);
272    }
273
274    let instruction_sysvar = instruction_sysvar_account_info.try_borrow_data()?;
275    load_instruction_at(index, &instruction_sysvar).map_err(|err| match err {
276        SanitizeError::IndexOutOfBounds => ProgramError::InvalidArgument,
277        _ => ProgramError::InvalidInstructionData,
278    })
279}
280
281/// Returns the `Instruction` relative to the current `Instruction` in the
282/// currently executing `Transaction`.
283///
284/// # Errors
285///
286/// Returns [`ProgramError::UnsupportedSysvar`] if the given account's ID is not equal to [`ID`].
287pub fn get_instruction_relative(
288    index_relative_to_current: i64,
289    instruction_sysvar_account_info: &AccountInfo,
290) -> Result<Instruction, ProgramError> {
291    if !check_id(instruction_sysvar_account_info.key) {
292        return Err(ProgramError::UnsupportedSysvar);
293    }
294
295    let instruction_sysvar = instruction_sysvar_account_info.data.borrow();
296    let current_index = load_current_index(&instruction_sysvar) as i64;
297    let index = current_index.saturating_add(index_relative_to_current);
298    if index < 0 {
299        return Err(ProgramError::InvalidArgument);
300    }
301    load_instruction_at(
302        current_index.saturating_add(index_relative_to_current) as usize,
303        &instruction_sysvar,
304    )
305    .map_err(|err| match err {
306        SanitizeError::IndexOutOfBounds => ProgramError::InvalidArgument,
307        _ => ProgramError::InvalidInstructionData,
308    })
309}
310
311#[cfg(test)]
312mod tests {
313    use {
314        super::*,
315        alloc::vec,
316        solana_account_info::AccountInfo,
317        solana_address::Address,
318        solana_instruction::{AccountMeta, BorrowedAccountMeta, BorrowedInstruction, Instruction},
319        solana_program_error::ProgramError,
320        solana_sanitize::SanitizeError,
321        solana_sdk_ids::sysvar::instructions::id,
322    };
323
324    #[test]
325    fn test_load_store_instruction() {
326        let mut data = [4u8; 10];
327        let res = store_current_index_checked(&mut data, 3);
328        assert!(res.is_ok());
329        let index = load_current_index(&data);
330        assert_eq!(index, 3);
331        assert_eq!([4u8; 8], data[0..8]);
332    }
333
334    #[test]
335    fn test_store_instruction_too_small_data() {
336        let mut data = [4u8; 1];
337        let res = store_current_index_checked(&mut data, 3);
338        assert!(res.is_err());
339    }
340
341    #[derive(Copy, Clone)]
342    struct MakeInstructionParams {
343        program_id: Address,
344        account_key: Address,
345        is_signer: bool,
346        is_writable: bool,
347    }
348
349    fn make_borrowed_instruction(params: &MakeInstructionParams) -> BorrowedInstruction<'_> {
350        let MakeInstructionParams {
351            program_id,
352            account_key,
353            is_signer,
354            is_writable,
355        } = params;
356        BorrowedInstruction {
357            program_id,
358            accounts: vec![BorrowedAccountMeta {
359                pubkey: account_key,
360                is_signer: *is_signer,
361                is_writable: *is_writable,
362            }],
363            data: &[0],
364        }
365    }
366
367    fn make_instruction(params: MakeInstructionParams) -> Instruction {
368        let MakeInstructionParams {
369            program_id,
370            account_key,
371            is_signer,
372            is_writable,
373        } = params;
374        Instruction {
375            program_id,
376            accounts: vec![AccountMeta {
377                pubkey: account_key,
378                is_signer,
379                is_writable,
380            }],
381            data: vec![0],
382        }
383    }
384
385    #[test]
386    fn test_load_instruction_at_checked() {
387        let program_id0 = Address::new_unique();
388        let program_id1 = Address::new_unique();
389        let account_key0 = Address::new_unique();
390        let account_key1 = Address::new_unique();
391        let params0 = MakeInstructionParams {
392            program_id: program_id0,
393            account_key: account_key0,
394            is_signer: false,
395            is_writable: false,
396        };
397        let params1 = MakeInstructionParams {
398            program_id: program_id1,
399            account_key: account_key1,
400            is_signer: false,
401            is_writable: false,
402        };
403        let instruction0 = make_instruction(params0);
404        let instruction1 = make_instruction(params1);
405        let borrowed_instruction0 = make_borrowed_instruction(&params0);
406        let borrowed_instruction1 = make_borrowed_instruction(&params1);
407        let key = id();
408        let mut lamports = 0;
409        let mut data =
410            construct_instructions_data(&[borrowed_instruction0, borrowed_instruction1]).unwrap();
411        let owner = solana_sdk_ids::sysvar::id();
412        let mut account_info =
413            AccountInfo::new(&key, false, false, &mut lamports, &mut data, &owner, false);
414
415        assert_eq!(
416            instruction0,
417            load_instruction_at_checked(0, &account_info).unwrap()
418        );
419        assert_eq!(
420            instruction1,
421            load_instruction_at_checked(1, &account_info).unwrap()
422        );
423        assert_eq!(
424            Err(ProgramError::InvalidArgument),
425            load_instruction_at_checked(2, &account_info)
426        );
427
428        let key = Address::new_unique();
429        account_info.key = &key;
430        assert_eq!(
431            Err(ProgramError::UnsupportedSysvar),
432            load_instruction_at_checked(2, &account_info)
433        );
434    }
435
436    #[test]
437    fn test_load_current_index_checked() {
438        let program_id0 = Address::new_unique();
439        let program_id1 = Address::new_unique();
440        let account_key0 = Address::new_unique();
441        let account_key1 = Address::new_unique();
442        let params0 = MakeInstructionParams {
443            program_id: program_id0,
444            account_key: account_key0,
445            is_signer: false,
446            is_writable: false,
447        };
448        let params1 = MakeInstructionParams {
449            program_id: program_id1,
450            account_key: account_key1,
451            is_signer: false,
452            is_writable: false,
453        };
454        let borrowed_instruction0 = make_borrowed_instruction(&params0);
455        let borrowed_instruction1 = make_borrowed_instruction(&params1);
456
457        let key = id();
458        let mut lamports = 0;
459        let mut data =
460            construct_instructions_data(&[borrowed_instruction0, borrowed_instruction1]).unwrap();
461        let res = store_current_index_checked(&mut data, 1);
462        assert!(res.is_ok());
463        let owner = solana_sdk_ids::sysvar::id();
464        let mut account_info =
465            AccountInfo::new(&key, false, false, &mut lamports, &mut data, &owner, false);
466
467        assert_eq!(1, load_current_index_checked(&account_info).unwrap());
468        {
469            let mut data = account_info.try_borrow_mut_data().unwrap();
470            let res = store_current_index_checked(&mut data, 0);
471            assert!(res.is_ok());
472        }
473        assert_eq!(0, load_current_index_checked(&account_info).unwrap());
474
475        let key = Address::new_unique();
476        account_info.key = &key;
477        assert_eq!(
478            Err(ProgramError::UnsupportedSysvar),
479            load_current_index_checked(&account_info)
480        );
481    }
482
483    #[test]
484    fn test_get_instruction_relative() {
485        let program_id0 = Address::new_unique();
486        let program_id1 = Address::new_unique();
487        let program_id2 = Address::new_unique();
488        let account_key0 = Address::new_unique();
489        let account_key1 = Address::new_unique();
490        let account_key2 = Address::new_unique();
491        let params0 = MakeInstructionParams {
492            program_id: program_id0,
493            account_key: account_key0,
494            is_signer: false,
495            is_writable: false,
496        };
497        let params1 = MakeInstructionParams {
498            program_id: program_id1,
499            account_key: account_key1,
500            is_signer: false,
501            is_writable: false,
502        };
503        let params2 = MakeInstructionParams {
504            program_id: program_id2,
505            account_key: account_key2,
506            is_signer: false,
507            is_writable: false,
508        };
509        let instruction0 = make_instruction(params0);
510        let instruction1 = make_instruction(params1);
511        let instruction2 = make_instruction(params2);
512        let borrowed_instruction0 = make_borrowed_instruction(&params0);
513        let borrowed_instruction1 = make_borrowed_instruction(&params1);
514        let borrowed_instruction2 = make_borrowed_instruction(&params2);
515
516        let key = id();
517        let mut lamports = 0;
518        let mut data = construct_instructions_data(&[
519            borrowed_instruction0,
520            borrowed_instruction1,
521            borrowed_instruction2,
522        ])
523        .unwrap();
524        let res = store_current_index_checked(&mut data, 1);
525        assert!(res.is_ok());
526        let owner = solana_sdk_ids::sysvar::id();
527        let mut account_info =
528            AccountInfo::new(&key, false, false, &mut lamports, &mut data, &owner, false);
529
530        assert_eq!(
531            Err(ProgramError::InvalidArgument),
532            get_instruction_relative(-2, &account_info)
533        );
534        assert_eq!(
535            instruction0,
536            get_instruction_relative(-1, &account_info).unwrap()
537        );
538        assert_eq!(
539            instruction1,
540            get_instruction_relative(0, &account_info).unwrap()
541        );
542        assert_eq!(
543            instruction2,
544            get_instruction_relative(1, &account_info).unwrap()
545        );
546        assert_eq!(
547            Err(ProgramError::InvalidArgument),
548            get_instruction_relative(2, &account_info)
549        );
550        {
551            let mut data = account_info.try_borrow_mut_data().unwrap();
552            let res = store_current_index_checked(&mut data, 0);
553            assert!(res.is_ok());
554        }
555        assert_eq!(
556            Err(ProgramError::InvalidArgument),
557            get_instruction_relative(-1, &account_info)
558        );
559        assert_eq!(
560            instruction0,
561            get_instruction_relative(0, &account_info).unwrap()
562        );
563        assert_eq!(
564            instruction1,
565            get_instruction_relative(1, &account_info).unwrap()
566        );
567        assert_eq!(
568            instruction2,
569            get_instruction_relative(2, &account_info).unwrap()
570        );
571        assert_eq!(
572            Err(ProgramError::InvalidArgument),
573            get_instruction_relative(3, &account_info)
574        );
575
576        let key = Address::new_unique();
577        account_info.key = &key;
578        assert_eq!(
579            Err(ProgramError::UnsupportedSysvar),
580            get_instruction_relative(0, &account_info)
581        );
582    }
583
584    #[test]
585    fn test_serialize_instructions() {
586        let program_id0 = Address::new_unique();
587        let program_id1 = Address::new_unique();
588        let id0 = Address::new_unique();
589        let id1 = Address::new_unique();
590        let id2 = Address::new_unique();
591        let id3 = Address::new_unique();
592        let params = vec![
593            MakeInstructionParams {
594                program_id: program_id0,
595                account_key: id0,
596                is_signer: false,
597                is_writable: true,
598            },
599            MakeInstructionParams {
600                program_id: program_id0,
601                account_key: id1,
602                is_signer: true,
603                is_writable: true,
604            },
605            MakeInstructionParams {
606                program_id: program_id1,
607                account_key: id2,
608                is_signer: false,
609                is_writable: false,
610            },
611            MakeInstructionParams {
612                program_id: program_id1,
613                account_key: id3,
614                is_signer: true,
615                is_writable: false,
616            },
617        ];
618        let instructions: Vec<Instruction> =
619            params.clone().into_iter().map(make_instruction).collect();
620        let borrowed_instructions: Vec<BorrowedInstruction> =
621            params.iter().map(make_borrowed_instruction).collect();
622
623        let serialized = serialize_instructions(&borrowed_instructions).unwrap();
624
625        // assert that deserialize_instruction is compatible with SanitizedMessage::serialize_instructions
626        for (i, instruction) in instructions.iter().enumerate() {
627            assert_eq!(
628                deserialize_instruction(i, &serialized).unwrap(),
629                *instruction
630            );
631        }
632    }
633
634    #[test]
635    fn test_serialize_instructions_rejects_instruction_offset_overflow() {
636        fn make_borrowed_instruction_with_accounts<'a>(
637            program_id: &'a Address,
638            account_key: &'a Address,
639            num_account_refs: usize,
640            data: &'a [u8],
641        ) -> BorrowedInstruction<'a> {
642            BorrowedInstruction {
643                program_id,
644                accounts: (0..num_account_refs)
645                    .map(|_| BorrowedAccountMeta {
646                        pubkey: account_key,
647                        is_signer: false,
648                        is_writable: false,
649                    })
650                    .collect(),
651                data,
652            }
653        }
654
655        fn make_instructions<'a>(
656            program_id: &'a Address,
657            account_key: &'a Address,
658            padding_data: &'a [u8],
659        ) -> Vec<BorrowedInstruction<'a>> {
660            let mut instructions = (0..7)
661                .map(|_| make_borrowed_instruction_with_accounts(program_id, account_key, 255, &[]))
662                .collect::<Vec<_>>();
663            instructions.push(make_borrowed_instruction_with_accounts(
664                program_id,
665                account_key,
666                191,
667                padding_data,
668            ));
669            instructions.push(make_borrowed_instruction_with_accounts(
670                program_id,
671                account_key,
672                255,
673                &[],
674            ));
675            instructions
676        }
677
678        let program_id = Address::new_unique();
679        let account_key = Address::new_unique();
680
681        let boundary_padding_data = [0; 19];
682        let instructions = make_instructions(&program_id, &account_key, &boundary_padding_data);
683        assert!(serialize_instructions(&instructions).is_ok());
684
685        let overflow_padding_data = [0; 20];
686        let instructions = make_instructions(&program_id, &account_key, &overflow_padding_data);
687        assert_eq!(
688            serialize_instructions(&instructions),
689            Err(InstructionsSysvarError::InstructionIndexOutOfBounds)
690        );
691        assert_eq!(
692            construct_instructions_data(&instructions),
693            Err(InstructionsSysvarError::InstructionIndexOutOfBounds)
694        );
695    }
696
697    #[test]
698    fn test_decompile_instructions_out_of_bounds() {
699        let program_id0 = Address::new_unique();
700        let id0 = Address::new_unique();
701        let id1 = Address::new_unique();
702        let params = vec![
703            MakeInstructionParams {
704                program_id: program_id0,
705                account_key: id0,
706                is_signer: false,
707                is_writable: true,
708            },
709            MakeInstructionParams {
710                program_id: program_id0,
711                account_key: id1,
712                is_signer: true,
713                is_writable: true,
714            },
715        ];
716        let instructions: Vec<Instruction> =
717            params.clone().into_iter().map(make_instruction).collect();
718        let borrowed_instructions: Vec<BorrowedInstruction> =
719            params.iter().map(make_borrowed_instruction).collect();
720
721        let serialized = serialize_instructions(&borrowed_instructions).unwrap();
722        assert_eq!(
723            deserialize_instruction(instructions.len(), &serialized).unwrap_err(),
724            SanitizeError::IndexOutOfBounds,
725        );
726    }
727}