spl_slashing/
processor.rs1use {
4 crate::{
5 duplicate_block_proof::DuplicateBlockProofData,
6 error::SlashingError,
7 instruction::{
8 decode_instruction_data, decode_instruction_type, DuplicateBlockProofInstructionData,
9 SlashingInstruction,
10 },
11 state::SlashingProofData,
12 },
13 solana_program::{
14 account_info::{next_account_info, AccountInfo},
15 clock::Slot,
16 entrypoint::ProgramResult,
17 msg,
18 program_error::ProgramError,
19 pubkey::Pubkey,
20 sysvar::{clock::Clock, epoch_schedule::EpochSchedule, Sysvar},
21 },
22};
23
24fn verify_proof_data<'a, T>(slot: Slot, pubkey: &Pubkey, proof_data: &'a [u8]) -> ProgramResult
25where
26 T: SlashingProofData<'a>,
27{
28 let clock = Clock::get()?;
30 let Some(elapsed) = clock.slot.checked_sub(slot) else {
31 return Err(ProgramError::ArithmeticOverflow);
32 };
33 let epoch_schedule = EpochSchedule::get()?;
34 if elapsed > epoch_schedule.slots_per_epoch {
35 return Err(SlashingError::ExceedsStatueOfLimitations.into());
36 }
37
38 let proof_data: T =
39 T::unpack(proof_data).map_err(|_| SlashingError::ShredDeserializationError)?;
40
41 SlashingProofData::verify_proof(proof_data, slot, pubkey)?;
42
43 msg!(
46 "{} violation verified in slot {}. This incident will be recorded",
47 T::PROOF_TYPE.violation_str(),
48 slot
49 );
50 Ok(())
51}
52
53pub fn process_instruction(
55 _program_id: &Pubkey,
56 accounts: &[AccountInfo],
57 input: &[u8],
58) -> ProgramResult {
59 let instruction_type = decode_instruction_type(input)?;
60 let account_info_iter = &mut accounts.iter();
61 let proof_data_info = next_account_info(account_info_iter);
62
63 match instruction_type {
64 SlashingInstruction::DuplicateBlockProof => {
65 let data = decode_instruction_data::<DuplicateBlockProofInstructionData>(input)?;
66 let proof_data = &proof_data_info?.data.borrow()[u64::from(data.offset) as usize..];
67 verify_proof_data::<DuplicateBlockProofData>(
68 data.slot.into(),
69 &data.node_pubkey,
70 proof_data,
71 )?;
72 Ok(())
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use {
80 super::verify_proof_data,
81 crate::{
82 duplicate_block_proof::DuplicateBlockProofData, error::SlashingError,
83 shred::tests::new_rand_data_shred,
84 },
85 rand::Rng,
86 solana_ledger::shred::Shredder,
87 solana_sdk::{
88 clock::{Clock, Slot, DEFAULT_SLOTS_PER_EPOCH},
89 epoch_schedule::EpochSchedule,
90 program_error::ProgramError,
91 signature::Keypair,
92 signer::Signer,
93 },
94 std::sync::{Arc, RwLock},
95 };
96
97 const SLOT: Slot = 53084024;
98 lazy_static::lazy_static! {
99 static ref CLOCK_SLOT: Arc<RwLock<Slot>> = Arc::new(RwLock::new(SLOT));
100 }
101
102 fn generate_proof_data(leader: Arc<Keypair>) -> Vec<u8> {
103 let mut rng = rand::thread_rng();
104 let (slot, parent_slot, reference_tick, version) = (SLOT, SLOT - 1, 0, 0);
105 let shredder = Shredder::new(slot, parent_slot, reference_tick, version).unwrap();
106 let next_shred_index = rng.gen_range(0..32_000);
107 let shred1 =
108 new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true);
109 let shred2 =
110 new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true);
111 let proof = DuplicateBlockProofData {
112 shred1: shred1.payload().as_slice(),
113 shred2: shred2.payload().as_slice(),
114 };
115 proof.pack()
116 }
117
118 #[test]
119 fn test_statue_of_limitations() {
120 *CLOCK_SLOT.write().unwrap() = SLOT + 5;
121 verify_with_clock().unwrap();
122
123 *CLOCK_SLOT.write().unwrap() = SLOT - 1;
124 assert_eq!(
125 verify_with_clock().unwrap_err(),
126 ProgramError::ArithmeticOverflow
127 );
128
129 *CLOCK_SLOT.write().unwrap() = SLOT + DEFAULT_SLOTS_PER_EPOCH + 1;
130 assert_eq!(
131 verify_with_clock().unwrap_err(),
132 SlashingError::ExceedsStatueOfLimitations.into()
133 );
134 }
135
136 fn verify_with_clock() -> Result<(), ProgramError> {
137 struct SyscallStubs {}
138 impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
139 fn sol_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 {
140 unsafe {
141 let clock = Clock {
142 slot: *CLOCK_SLOT.read().unwrap(),
143 ..Clock::default()
144 };
145 *(var_addr as *mut _ as *mut Clock) = clock;
146 }
147 solana_program::entrypoint::SUCCESS
148 }
149
150 fn sol_get_epoch_schedule_sysvar(&self, var_addr: *mut u8) -> u64 {
151 unsafe {
152 *(var_addr as *mut _ as *mut EpochSchedule) = EpochSchedule::default();
153 }
154 solana_program::entrypoint::SUCCESS
155 }
156 }
157
158 solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {}));
159 let leader = Arc::new(Keypair::new());
160 verify_proof_data::<DuplicateBlockProofData>(
161 SLOT,
162 &leader.pubkey(),
163 &generate_proof_data(leader),
164 )
165 }
166}