solana_stable_layout/
stable_instruction.rs

1//! `Instruction`, with a stable memory layout
2
3use {
4    crate::stable_vec::StableVec,
5    solana_instruction::{AccountMeta, Instruction},
6    solana_pubkey::Pubkey,
7    std::fmt::Debug,
8};
9
10/// `Instruction`, with a stable memory layout
11///
12/// This is used within the runtime to ensure memory mapping and memory accesses are valid.  We
13/// rely on known addresses and offsets within the runtime, and since `Instruction`'s layout is
14/// allowed to change, we must provide a way to lock down the memory layout.  `StableInstruction`
15/// reimplements the bare minimum of `Instruction`'s API sufficient only for the runtime's needs.
16///
17/// # Examples
18///
19/// Creating a `StableInstruction` from an `Instruction`
20///
21/// ```
22/// # use solana_instruction::Instruction;
23/// # use solana_pubkey::Pubkey;
24/// # use solana_stable_layout::stable_instruction::StableInstruction;
25/// # let program_id = Pubkey::default();
26/// # let accounts = Vec::default();
27/// # let data = Vec::default();
28/// let instruction = Instruction { program_id, accounts, data };
29/// let instruction = StableInstruction::from(instruction);
30/// ```
31#[derive(Debug, PartialEq)]
32#[repr(C)]
33pub struct StableInstruction {
34    pub accounts: StableVec<AccountMeta>,
35    pub data: StableVec<u8>,
36    pub program_id: Pubkey,
37}
38
39impl From<Instruction> for StableInstruction {
40    fn from(other: Instruction) -> Self {
41        Self {
42            accounts: other.accounts.into(),
43            data: other.data.into(),
44            program_id: other.program_id,
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use {
52        super::*,
53        memoffset::offset_of,
54        std::mem::{align_of, size_of},
55    };
56
57    #[test]
58    fn test_memory_layout() {
59        assert_eq!(offset_of!(StableInstruction, accounts), 0);
60        assert_eq!(offset_of!(StableInstruction, data), 24);
61        assert_eq!(offset_of!(StableInstruction, program_id), 48);
62        assert_eq!(align_of::<StableInstruction>(), 8);
63        assert_eq!(size_of::<StableInstruction>(), 24 + 24 + 32);
64
65        let program_id = Pubkey::new_unique();
66        let account_meta1 = AccountMeta {
67            pubkey: Pubkey::new_unique(),
68            is_signer: true,
69            is_writable: false,
70        };
71        let account_meta2 = AccountMeta {
72            pubkey: Pubkey::new_unique(),
73            is_signer: false,
74            is_writable: true,
75        };
76        let accounts = vec![account_meta1, account_meta2];
77        let data = vec![1, 2, 3, 4, 5];
78        let instruction = Instruction {
79            program_id,
80            accounts: accounts.clone(),
81            data: data.clone(),
82        };
83        let instruction = StableInstruction::from(instruction);
84
85        let instruction_addr = &instruction as *const _ as u64;
86
87        let accounts_ptr = instruction_addr as *const StableVec<AccountMeta>;
88        assert_eq!(unsafe { &*accounts_ptr }, &accounts);
89
90        let data_ptr = (instruction_addr + 24) as *const StableVec<u8>;
91        assert_eq!(unsafe { &*data_ptr }, &data);
92
93        let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
94        assert_eq!(unsafe { *pubkey_ptr }, program_id);
95    }
96}