use {
crate::{error::SlashingError, id},
bytemuck::{Pod, Zeroable},
num_enum::{IntoPrimitive, TryFromPrimitive},
solana_program::{
clock::Slot,
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
pubkey::Pubkey,
},
spl_pod::{
bytemuck::{pod_from_bytes, pod_get_packed_len},
primitives::PodU64,
},
};
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
pub enum SlashingInstruction {
DuplicateBlockProof,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct DuplicateBlockProofInstructionData {
pub(crate) offset: PodU64,
pub(crate) slot: PodU64,
pub(crate) node_pubkey: Pubkey,
}
pub(crate) fn encode_instruction<D: Pod>(
accounts: Vec<AccountMeta>,
instruction: SlashingInstruction,
instruction_data: &D,
) -> Instruction {
let mut data = vec![u8::from(instruction)];
data.extend_from_slice(bytemuck::bytes_of(instruction_data));
Instruction {
program_id: id(),
accounts,
data,
}
}
pub(crate) fn decode_instruction_type(input: &[u8]) -> Result<SlashingInstruction, ProgramError> {
if input.is_empty() {
Err(ProgramError::InvalidInstructionData)
} else {
SlashingInstruction::try_from(input[0])
.map_err(|_| SlashingError::InvalidInstruction.into())
}
}
pub(crate) fn decode_instruction_data<T: Pod>(input_with_type: &[u8]) -> Result<&T, ProgramError> {
if input_with_type.len() != pod_get_packed_len::<T>().saturating_add(1) {
Err(ProgramError::InvalidInstructionData)
} else {
pod_from_bytes(&input_with_type[1..])
}
}
pub fn duplicate_block_proof(
proof_account: &Pubkey,
offset: u64,
slot: Slot,
node_pubkey: Pubkey,
) -> Instruction {
encode_instruction(
vec![AccountMeta::new_readonly(*proof_account, false)],
SlashingInstruction::DuplicateBlockProof,
&DuplicateBlockProofInstructionData {
offset: PodU64::from(offset),
slot: PodU64::from(slot),
node_pubkey,
},
)
}
#[cfg(test)]
mod tests {
use {super::*, solana_program::program_error::ProgramError};
const TEST_BYTES: [u8; 8] = [42; 8];
#[test]
fn serialize_duplicate_block_proof() {
let offset = 34;
let slot = 42;
let node_pubkey = Pubkey::new_unique();
let instruction = duplicate_block_proof(&Pubkey::new_unique(), offset, slot, node_pubkey);
let mut expected = vec![0];
expected.extend_from_slice(&offset.to_le_bytes());
expected.extend_from_slice(&slot.to_le_bytes());
expected.extend_from_slice(&node_pubkey.to_bytes());
assert_eq!(instruction.data, expected);
assert_eq!(
SlashingInstruction::DuplicateBlockProof,
decode_instruction_type(&instruction.data).unwrap()
);
let instruction_data: &DuplicateBlockProofInstructionData =
decode_instruction_data(&instruction.data).unwrap();
assert_eq!(instruction_data.offset, offset.into());
assert_eq!(instruction_data.slot, slot.into());
assert_eq!(instruction_data.node_pubkey, node_pubkey);
}
#[test]
fn deserialize_invalid_instruction() {
let mut expected = vec![12];
expected.extend_from_slice(&TEST_BYTES);
let err: ProgramError = decode_instruction_type(&expected).unwrap_err();
assert_eq!(err, SlashingError::InvalidInstruction.into());
}
}