spl_slashing/
instruction.rs1use {
4 crate::{error::SlashingError, id},
5 bytemuck::{Pod, Zeroable},
6 num_enum::{IntoPrimitive, TryFromPrimitive},
7 solana_program::{
8 clock::Slot,
9 instruction::{AccountMeta, Instruction},
10 program_error::ProgramError,
11 pubkey::Pubkey,
12 },
13 spl_pod::{
14 bytemuck::{pod_from_bytes, pod_get_packed_len},
15 primitives::PodU64,
16 },
17};
18
19#[repr(u8)]
21#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
22pub enum SlashingInstruction {
23 DuplicateBlockProof,
40}
41
42#[repr(C)]
45#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
46pub struct DuplicateBlockProofInstructionData {
47 pub(crate) offset: PodU64,
49 pub(crate) slot: PodU64,
51 pub(crate) node_pubkey: Pubkey,
53}
54
55pub(crate) fn encode_instruction<D: Pod>(
57 accounts: Vec<AccountMeta>,
58 instruction: SlashingInstruction,
59 instruction_data: &D,
60) -> Instruction {
61 let mut data = vec![u8::from(instruction)];
62 data.extend_from_slice(bytemuck::bytes_of(instruction_data));
63 Instruction {
64 program_id: id(),
65 accounts,
66 data,
67 }
68}
69
70pub(crate) fn decode_instruction_type(input: &[u8]) -> Result<SlashingInstruction, ProgramError> {
72 if input.is_empty() {
73 Err(ProgramError::InvalidInstructionData)
74 } else {
75 SlashingInstruction::try_from(input[0])
76 .map_err(|_| SlashingError::InvalidInstruction.into())
77 }
78}
79
80pub(crate) fn decode_instruction_data<T: Pod>(input_with_type: &[u8]) -> Result<&T, ProgramError> {
82 if input_with_type.len() != pod_get_packed_len::<T>().saturating_add(1) {
83 Err(ProgramError::InvalidInstructionData)
84 } else {
85 pod_from_bytes(&input_with_type[1..])
86 }
87}
88
89pub fn duplicate_block_proof(
91 proof_account: &Pubkey,
92 offset: u64,
93 slot: Slot,
94 node_pubkey: Pubkey,
95) -> Instruction {
96 encode_instruction(
97 vec![AccountMeta::new_readonly(*proof_account, false)],
98 SlashingInstruction::DuplicateBlockProof,
99 &DuplicateBlockProofInstructionData {
100 offset: PodU64::from(offset),
101 slot: PodU64::from(slot),
102 node_pubkey,
103 },
104 )
105}
106
107#[cfg(test)]
108mod tests {
109 use {super::*, solana_program::program_error::ProgramError};
110
111 const TEST_BYTES: [u8; 8] = [42; 8];
112
113 #[test]
114 fn serialize_duplicate_block_proof() {
115 let offset = 34;
116 let slot = 42;
117 let node_pubkey = Pubkey::new_unique();
118 let instruction = duplicate_block_proof(&Pubkey::new_unique(), offset, slot, node_pubkey);
119 let mut expected = vec![0];
120 expected.extend_from_slice(&offset.to_le_bytes());
121 expected.extend_from_slice(&slot.to_le_bytes());
122 expected.extend_from_slice(&node_pubkey.to_bytes());
123 assert_eq!(instruction.data, expected);
124
125 assert_eq!(
126 SlashingInstruction::DuplicateBlockProof,
127 decode_instruction_type(&instruction.data).unwrap()
128 );
129 let instruction_data: &DuplicateBlockProofInstructionData =
130 decode_instruction_data(&instruction.data).unwrap();
131
132 assert_eq!(instruction_data.offset, offset.into());
133 assert_eq!(instruction_data.slot, slot.into());
134 assert_eq!(instruction_data.node_pubkey, node_pubkey);
135 }
136
137 #[test]
138 fn deserialize_invalid_instruction() {
139 let mut expected = vec![12];
140 expected.extend_from_slice(&TEST_BYTES);
141 let err: ProgramError = decode_instruction_type(&expected).unwrap_err();
142 assert_eq!(err, SlashingError::InvalidInstruction.into());
143 }
144}