spl_slashing/
duplicate_block_proof.rs

1//! Duplicate block proof data and verification
2use {
3    crate::{
4        error::SlashingError,
5        shred::{Shred, ShredType},
6        state::{ProofType, SlashingProofData},
7    },
8    bytemuck::try_from_bytes,
9    solana_program::{clock::Slot, msg, pubkey::Pubkey},
10    spl_pod::primitives::PodU32,
11};
12
13/// Proof of a duplicate block violation
14pub struct DuplicateBlockProofData<'a> {
15    /// Shred signed by a leader
16    pub shred1: &'a [u8],
17    /// Conflicting shred signed by the same leader
18    pub shred2: &'a [u8],
19}
20
21impl<'a> DuplicateBlockProofData<'a> {
22    const LENGTH_SIZE: usize = std::mem::size_of::<PodU32>();
23
24    /// Packs proof data to write in account for
25    /// `SlashingInstruction::DuplicateBlockProof`
26    pub fn pack(self) -> Vec<u8> {
27        let mut buf = vec![];
28        buf.extend_from_slice(&(self.shred1.len() as u32).to_le_bytes());
29        buf.extend_from_slice(self.shred1);
30        buf.extend_from_slice(&(self.shred2.len() as u32).to_le_bytes());
31        buf.extend_from_slice(self.shred2);
32        buf
33    }
34
35    /// Given the maximum size of a shred as `shred_size` this returns
36    /// the maximum size of the account needed to store a
37    /// `DuplicateBlockProofData`
38    pub const fn size_of(shred_size: usize) -> usize {
39        2usize
40            .wrapping_mul(shred_size)
41            .saturating_add(2 * Self::LENGTH_SIZE)
42    }
43}
44
45impl<'a> SlashingProofData<'a> for DuplicateBlockProofData<'a> {
46    const PROOF_TYPE: ProofType = ProofType::DuplicateBlockProof;
47
48    fn verify_proof(self, slot: Slot, _node_pubkey: &Pubkey) -> Result<(), SlashingError> {
49        // TODO: verify through instruction inspection that the shreds were sigverified
50        // earlier in this transaction.
51        // Ed25519 Singature verification is performed on the merkle root:
52        // node_pubkey.verify_strict(merkle_root, signature).
53        // We will verify that the pubkey merkle root and signature match the shred and
54        // that the verification was successful.
55        let shred1 = Shred::new_from_payload(self.shred1)?;
56        let shred2 = Shred::new_from_payload(self.shred2)?;
57        check_shreds(slot, &shred1, &shred2)
58    }
59
60    fn unpack(data: &'a [u8]) -> Result<Self, SlashingError>
61    where
62        Self: Sized,
63    {
64        if data.len() < Self::LENGTH_SIZE {
65            return Err(SlashingError::ProofBufferTooSmall);
66        }
67        let (length1, data) = data.split_at(Self::LENGTH_SIZE);
68        let shred1_length = try_from_bytes::<PodU32>(length1)
69            .map_err(|_| SlashingError::ProofBufferDeserializationError)?;
70        let shred1_length = u32::from(*shred1_length) as usize;
71
72        if data.len() < shred1_length {
73            return Err(SlashingError::ProofBufferTooSmall);
74        }
75        let (shred1, data) = data.split_at(shred1_length);
76
77        if data.len() < Self::LENGTH_SIZE {
78            return Err(SlashingError::ProofBufferTooSmall);
79        }
80        let (length2, shred2) = data.split_at(Self::LENGTH_SIZE);
81        let shred2_length = try_from_bytes::<PodU32>(length2)
82            .map_err(|_| SlashingError::ProofBufferDeserializationError)?;
83        let shred2_length = u32::from(*shred2_length) as usize;
84
85        if shred2.len() < shred2_length {
86            return Err(SlashingError::ProofBufferTooSmall);
87        }
88
89        Ok(Self { shred1, shred2 })
90    }
91}
92
93/// Check that `shred1` and `shred2` indicate a valid duplicate proof
94///     - Must be for the same slot `slot`
95///     - Must be for the same shred version
96///     - Must have a merkle root conflict, otherwise `shred1` and `shred2` must
97///       have the same `shred_type`
98///     - If `shred1` and `shred2` share the same index they must be not have
99///       equal payloads excluding the retransmitter signature
100///     - If `shred1` and `shred2` do not share the same index and are data
101///       shreds verify that they indicate an index conflict. One of them must
102///       be the `LAST_SHRED_IN_SLOT`, however the other shred must have a
103///       higher index.
104///     - If `shred1` and `shred2` do not share the same index and are coding
105///       shreds verify that they have conflicting erasure metas
106fn check_shreds(slot: Slot, shred1: &Shred, shred2: &Shred) -> Result<(), SlashingError> {
107    if shred1.slot()? != slot {
108        msg!(
109            "Invalid proof for different slots {} vs {}",
110            shred1.slot()?,
111            slot,
112        );
113        return Err(SlashingError::SlotMismatch);
114    }
115
116    if shred2.slot()? != slot {
117        msg!(
118            "Invalid proof for different slots {} vs {}",
119            shred1.slot()?,
120            slot,
121        );
122        return Err(SlashingError::SlotMismatch);
123    }
124
125    if shred1.version()? != shred2.version()? {
126        msg!(
127            "Invalid proof for different shred versions {} vs {}",
128            shred1.version()?,
129            shred2.version()?,
130        );
131        return Err(SlashingError::InvalidShredVersion);
132    }
133
134    // Merkle root conflict check
135    if shred1.fec_set_index()? == shred2.fec_set_index()?
136        && shred1.merkle_root()? != shred2.merkle_root()?
137    {
138        // Legacy shreds are discarded by validators and already filtered out
139        // above during proof deserialization, so any valid proof should have
140        // merkle roots.
141        msg!(
142            "Valid merkle root conflict for fec set {}, {:?} vs {:?}",
143            shred1.fec_set_index()?,
144            shred1.merkle_root()?,
145            shred2.merkle_root()?
146        );
147        return Ok(());
148    }
149
150    // Overlapping fec set check
151    if shred1.shred_type() == ShredType::Code && shred1.fec_set_index()? < shred2.fec_set_index()? {
152        let next_fec_set_index = shred1.next_fec_set_index()?;
153        if next_fec_set_index > shred2.fec_set_index()? {
154            msg!(
155                "Valid overlapping fec set conflict. fec set {}'s next set is {} \
156                however we observed a shred with fec set index {}",
157                shred1.fec_set_index()?,
158                next_fec_set_index,
159                shred2.fec_set_index()?
160            );
161            return Ok(());
162        }
163    }
164
165    if shred2.shred_type() == ShredType::Code && shred1.fec_set_index()? > shred2.fec_set_index()? {
166        let next_fec_set_index = shred2.next_fec_set_index()?;
167        if next_fec_set_index > shred1.fec_set_index()? {
168            msg!(
169                "Valid overlapping fec set conflict. fec set {}'s next set is {} \
170                however we observed a shred with fec set index {}",
171                shred2.fec_set_index()?,
172                next_fec_set_index,
173                shred1.fec_set_index()?
174            );
175            return Ok(());
176        }
177    }
178
179    if shred1.shred_type() != shred2.shred_type() {
180        msg!(
181            "Invalid proof for different shred types {:?} vs {:?}",
182            shred1.shred_type(),
183            shred2.shred_type()
184        );
185        return Err(SlashingError::ShredTypeMismatch);
186    }
187
188    if shred1.index()? == shred2.index()? {
189        if shred1.is_shred_duplicate(shred2) {
190            msg!("Valid payload mismatch for shred index {}", shred1.index()?);
191            return Ok(());
192        }
193        msg!(
194            "Invalid proof, payload matches for index {}",
195            shred1.index()?
196        );
197        return Err(SlashingError::InvalidPayloadProof);
198    }
199
200    if shred1.shred_type() == ShredType::Data {
201        if shred1.last_in_slot()? && shred2.index()? > shred1.index()? {
202            msg!(
203                "Valid last in slot conflict last index {} but shred with index {} is present",
204                shred1.index()?,
205                shred2.index()?
206            );
207            return Ok(());
208        }
209        if shred2.last_in_slot()? && shred1.index()? > shred2.index()? {
210            msg!(
211                "Valid last in slot conflict last index {} but shred with index {} is present",
212                shred2.index()?,
213                shred1.index()?
214            );
215            return Ok(());
216        }
217        msg!(
218            "Invalid proof, no last in shred conflict for data shreds {} and {}",
219            shred1.index()?,
220            shred2.index()?
221        );
222        return Err(SlashingError::InvalidLastIndexConflict);
223    }
224
225    if shred1.fec_set_index() == shred2.fec_set_index()
226        && !shred1.check_erasure_consistency(shred2)?
227    {
228        msg!(
229            "Valid erasure meta conflict in fec set {}, config {:?} vs {:?}",
230            shred1.fec_set_index()?,
231            shred1.erasure_meta()?,
232            shred2.erasure_meta()?,
233        );
234        return Ok(());
235    }
236    msg!(
237        "Invalid proof, no erasure meta conflict for coding shreds set {} idx {} and set {} idx {}",
238        shred1.fec_set_index()?,
239        shred1.index()?,
240        shred2.fec_set_index()?,
241        shred2.index()?,
242    );
243    Err(SlashingError::InvalidErasureMetaConflict)
244}
245
246#[cfg(test)]
247mod tests {
248    use {
249        super::*,
250        crate::shred::{
251            tests::{new_rand_coding_shreds, new_rand_data_shred, new_rand_shreds},
252            SIZE_OF_SIGNATURE,
253        },
254        rand::Rng,
255        solana_ledger::shred::{Shred as SolanaShred, Shredder},
256        solana_sdk::signature::{Keypair, Signature, Signer},
257        std::sync::Arc,
258    };
259
260    const SLOT: Slot = 53084024;
261    const PARENT_SLOT: Slot = SLOT - 1;
262    const REFERENCE_TICK: u8 = 0;
263    const VERSION: u16 = 0;
264
265    fn generate_proof_data<'a>(
266        shred1: &'a SolanaShred,
267        shred2: &'a SolanaShred,
268    ) -> DuplicateBlockProofData<'a> {
269        DuplicateBlockProofData {
270            shred1: shred1.payload().as_slice(),
271            shred2: shred2.payload().as_slice(),
272        }
273    }
274
275    #[test]
276    fn test_legacy_shreds_invalid() {
277        let mut rng = rand::thread_rng();
278        let leader = Arc::new(Keypair::new());
279        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
280        let next_shred_index = rng.gen_range(0..32_000);
281        let legacy_data_shred =
282            new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, false, false);
283        let legacy_coding_shred =
284            new_rand_coding_shreds(&mut rng, next_shred_index, 5, &shredder, &leader, false)[0]
285                .clone();
286        let data_shred =
287            new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, false);
288        let coding_shred =
289            new_rand_coding_shreds(&mut rng, next_shred_index, 5, &shredder, &leader, true)[0]
290                .clone();
291
292        let test_cases = [
293            (legacy_data_shred.clone(), legacy_data_shred.clone()),
294            (legacy_coding_shred.clone(), legacy_coding_shred.clone()),
295            (legacy_data_shred.clone(), legacy_coding_shred.clone()),
296            // Mix of legacy and merkle
297            (legacy_data_shred.clone(), data_shred.clone()),
298            (legacy_coding_shred.clone(), coding_shred.clone()),
299            (legacy_data_shred.clone(), coding_shred.clone()),
300            (data_shred.clone(), legacy_coding_shred.clone()),
301        ];
302        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
303            let proof_data = generate_proof_data(shred1, shred2);
304            assert_eq!(
305                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
306                SlashingError::LegacyShreds,
307            );
308        }
309    }
310
311    #[test]
312    fn test_slot_invalid() {
313        let mut rng = rand::thread_rng();
314        let leader = Arc::new(Keypair::new());
315        let shredder_slot = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
316        let shredder_bad_slot =
317            Shredder::new(SLOT + 1, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
318        let next_shred_index = rng.gen_range(0..32_000);
319        let data_shred = new_rand_data_shred(
320            &mut rng,
321            next_shred_index,
322            &shredder_slot,
323            &leader,
324            true,
325            false,
326        );
327        let data_shred_bad_slot = new_rand_data_shred(
328            &mut rng,
329            next_shred_index,
330            &shredder_bad_slot,
331            &leader,
332            true,
333            false,
334        );
335        let coding_shred =
336            new_rand_coding_shreds(&mut rng, next_shred_index, 5, &shredder_slot, &leader, true)[0]
337                .clone();
338
339        let coding_shred_bad_slot = new_rand_coding_shreds(
340            &mut rng,
341            next_shred_index,
342            5,
343            &shredder_bad_slot,
344            &leader,
345            true,
346        )[0]
347        .clone();
348
349        let test_cases = vec![
350            (data_shred_bad_slot.clone(), data_shred_bad_slot.clone()),
351            (coding_shred_bad_slot.clone(), coding_shred_bad_slot.clone()),
352            (data_shred_bad_slot.clone(), coding_shred_bad_slot.clone()),
353            (data_shred.clone(), data_shred_bad_slot.clone()),
354            (coding_shred.clone(), coding_shred_bad_slot.clone()),
355            (data_shred.clone(), coding_shred_bad_slot.clone()),
356            (data_shred_bad_slot.clone(), coding_shred.clone()),
357        ];
358
359        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
360            let proof_data = generate_proof_data(shred1, shred2);
361            assert_eq!(
362                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
363                SlashingError::SlotMismatch
364            );
365        }
366    }
367
368    #[test]
369    fn test_payload_proof_valid() {
370        let mut rng = rand::thread_rng();
371        let leader = Arc::new(Keypair::new());
372        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
373        let next_shred_index = rng.gen_range(0..32_000);
374        let shred1 =
375            new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true);
376        let shred2 =
377            new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true);
378        let proof_data = generate_proof_data(&shred1, &shred2);
379        proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap();
380    }
381
382    #[test]
383    fn test_payload_proof_invalid() {
384        let mut rng = rand::thread_rng();
385        let leader = Arc::new(Keypair::new());
386        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
387        let next_shred_index = rng.gen_range(0..32_000);
388        let data_shred =
389            new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true);
390        let coding_shreds =
391            new_rand_coding_shreds(&mut rng, next_shred_index, 10, &shredder, &leader, true);
392        let test_cases = vec![
393            // Same data_shred
394            (data_shred.clone(), data_shred),
395            // Same coding_shred
396            (coding_shreds[0].clone(), coding_shreds[0].clone()),
397        ];
398
399        for (shred1, shred2) in test_cases.into_iter() {
400            let proof_data = generate_proof_data(&shred1, &shred2);
401            assert_eq!(
402                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
403                SlashingError::InvalidPayloadProof
404            );
405        }
406    }
407
408    #[test]
409    fn test_merkle_root_proof_valid() {
410        let mut rng = rand::thread_rng();
411        let leader = Arc::new(Keypair::new());
412        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
413        let next_shred_index = rng.gen_range(0..32_000);
414        let (data_shreds, coding_shreds) = new_rand_shreds(
415            &mut rng,
416            next_shred_index,
417            next_shred_index,
418            10,
419            true, /* merkle_variant */
420            &shredder,
421            &leader,
422            false,
423        );
424
425        let (diff_data_shreds, diff_coding_shreds) = new_rand_shreds(
426            &mut rng,
427            next_shred_index,
428            next_shred_index,
429            10,
430            true, /* merkle_variant */
431            &shredder,
432            &leader,
433            false,
434        );
435
436        let test_cases = vec![
437            (data_shreds[0].clone(), diff_data_shreds[1].clone()),
438            (coding_shreds[0].clone(), diff_coding_shreds[1].clone()),
439            (data_shreds[0].clone(), diff_coding_shreds[0].clone()),
440            (coding_shreds[0].clone(), diff_data_shreds[0].clone()),
441        ];
442
443        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
444            let proof_data = generate_proof_data(shred1, shred2);
445            proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap();
446        }
447    }
448
449    #[test]
450    fn test_merkle_root_proof_invalid() {
451        let mut rng = rand::thread_rng();
452        let leader = Arc::new(Keypair::new());
453        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
454        let next_shred_index = rng.gen_range(0..32_000);
455        let (data_shreds, coding_shreds) = new_rand_shreds(
456            &mut rng,
457            next_shred_index,
458            next_shred_index,
459            10,
460            true,
461            &shredder,
462            &leader,
463            true,
464        );
465
466        let (next_data_shreds, next_coding_shreds) = new_rand_shreds(
467            &mut rng,
468            next_shred_index + 33,
469            next_shred_index + 33,
470            10,
471            true,
472            &shredder,
473            &leader,
474            true,
475        );
476
477        let test_cases = vec![
478            // Same fec set same merkle root
479            (coding_shreds[0].clone(), data_shreds[0].clone()),
480            // Different FEC set different merkle root
481            (coding_shreds[0].clone(), next_data_shreds[0].clone()),
482            (next_coding_shreds[0].clone(), data_shreds[0].clone()),
483        ];
484
485        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
486            let proof_data = generate_proof_data(shred1, shred2);
487            assert_eq!(
488                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
489                SlashingError::ShredTypeMismatch
490            );
491        }
492    }
493
494    #[test]
495    fn test_last_index_conflict_valid() {
496        let mut rng = rand::thread_rng();
497        let leader = Arc::new(Keypair::new());
498        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
499        let next_shred_index = rng.gen_range(0..32_000);
500        let test_cases = vec![
501            (
502                new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true),
503                new_rand_data_shred(
504                    &mut rng,
505                    // With Merkle shreds, last erasure batch is padded with
506                    // empty data shreds.
507                    next_shred_index + 30,
508                    &shredder,
509                    &leader,
510                    true,
511                    false,
512                ),
513            ),
514            (
515                new_rand_data_shred(
516                    &mut rng,
517                    next_shred_index + 100,
518                    &shredder,
519                    &leader,
520                    true,
521                    true,
522                ),
523                new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true),
524            ),
525        ];
526
527        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
528            let proof_data = generate_proof_data(shred1, shred2);
529            proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap();
530        }
531    }
532
533    #[test]
534    fn test_last_index_conflict_invalid() {
535        let mut rng = rand::thread_rng();
536        let leader = Arc::new(Keypair::new());
537        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
538        let next_shred_index = rng.gen_range(0..32_000);
539        let test_cases = vec![
540            (
541                new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, false),
542                new_rand_data_shred(
543                    &mut rng,
544                    next_shred_index + 1,
545                    &shredder,
546                    &leader,
547                    true,
548                    true,
549                ),
550            ),
551            (
552                new_rand_data_shred(
553                    &mut rng,
554                    next_shred_index + 1,
555                    &shredder,
556                    &leader,
557                    true,
558                    true,
559                ),
560                new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, false),
561            ),
562            (
563                new_rand_data_shred(
564                    &mut rng,
565                    next_shred_index + 100,
566                    &shredder,
567                    &leader,
568                    true,
569                    false,
570                ),
571                new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, false),
572            ),
573            (
574                new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, false),
575                new_rand_data_shred(
576                    &mut rng,
577                    next_shred_index + 100,
578                    &shredder,
579                    &leader,
580                    true,
581                    false,
582                ),
583            ),
584        ];
585
586        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
587            let proof_data = generate_proof_data(shred1, shred2);
588            assert_eq!(
589                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
590                SlashingError::InvalidLastIndexConflict
591            );
592        }
593    }
594
595    #[test]
596    fn test_erasure_meta_conflict_valid() {
597        let mut rng = rand::thread_rng();
598        let leader = Arc::new(Keypair::new());
599        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
600        let next_shred_index = rng.gen_range(0..32_000);
601        let coding_shreds =
602            new_rand_coding_shreds(&mut rng, next_shred_index, 10, &shredder, &leader, true);
603        let coding_shreds_bigger =
604            new_rand_coding_shreds(&mut rng, next_shred_index, 13, &shredder, &leader, true);
605        let coding_shreds_smaller =
606            new_rand_coding_shreds(&mut rng, next_shred_index, 7, &shredder, &leader, true);
607
608        // Same fec-set, different index, different erasure meta
609        let test_cases = vec![
610            (coding_shreds[0].clone(), coding_shreds_bigger[1].clone()),
611            (coding_shreds[0].clone(), coding_shreds_smaller[1].clone()),
612        ];
613        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
614            let proof_data = generate_proof_data(shred1, shred2);
615            proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap();
616        }
617    }
618
619    #[test]
620    fn test_erasure_meta_conflict_invalid() {
621        let mut rng = rand::thread_rng();
622        let leader = Arc::new(Keypair::new());
623        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
624        let next_shred_index = rng.gen_range(0..32_000);
625        let coding_shreds =
626            new_rand_coding_shreds(&mut rng, next_shred_index, 10, &shredder, &leader, true);
627        let coding_shreds_different_fec = new_rand_coding_shreds(
628            &mut rng,
629            next_shred_index + 100,
630            10,
631            &shredder,
632            &leader,
633            true,
634        );
635        let coding_shreds_different_fec_and_size = new_rand_coding_shreds(
636            &mut rng,
637            next_shred_index + 100,
638            13,
639            &shredder,
640            &leader,
641            true,
642        );
643
644        let test_cases = vec![
645            // Different index, different fec set, same erasure meta
646            (
647                coding_shreds[0].clone(),
648                coding_shreds_different_fec[1].clone(),
649            ),
650            // Different index, different fec set, different erasure meta
651            (
652                coding_shreds[0].clone(),
653                coding_shreds_different_fec_and_size[1].clone(),
654            ),
655            // Different index, same fec set, same erasure meta
656            (coding_shreds[0].clone(), coding_shreds[1].clone()),
657            (
658                coding_shreds_different_fec[0].clone(),
659                coding_shreds_different_fec[1].clone(),
660            ),
661            (
662                coding_shreds_different_fec_and_size[0].clone(),
663                coding_shreds_different_fec_and_size[1].clone(),
664            ),
665        ];
666
667        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
668            let proof_data = generate_proof_data(shred1, shred2);
669            assert_eq!(
670                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
671                SlashingError::InvalidErasureMetaConflict
672            );
673        }
674    }
675
676    #[test]
677    fn test_shred_version_invalid() {
678        let mut rng = rand::thread_rng();
679        let leader = Arc::new(Keypair::new());
680        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
681        let next_shred_index = rng.gen_range(0..32_000);
682        let (data_shreds, coding_shreds) = new_rand_shreds(
683            &mut rng,
684            next_shred_index,
685            next_shred_index,
686            10,
687            true,
688            &shredder,
689            &leader,
690            true,
691        );
692
693        // Wrong shred VERSION
694        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION + 1).unwrap();
695        let (wrong_data_shreds, wrong_coding_shreds) = new_rand_shreds(
696            &mut rng,
697            next_shred_index,
698            next_shred_index,
699            10,
700            true,
701            &shredder,
702            &leader,
703            true,
704        );
705        let test_cases = vec![
706            // One correct shred VERSION, one wrong
707            (coding_shreds[0].clone(), wrong_coding_shreds[0].clone()),
708            (coding_shreds[0].clone(), wrong_data_shreds[0].clone()),
709            (data_shreds[0].clone(), wrong_coding_shreds[0].clone()),
710            (data_shreds[0].clone(), wrong_data_shreds[0].clone()),
711        ];
712
713        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
714            let proof_data = generate_proof_data(shred1, shred2);
715            assert_eq!(
716                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
717                SlashingError::InvalidShredVersion
718            );
719        }
720    }
721
722    #[test]
723    fn test_retransmitter_signature_payload_proof_invalid() {
724        // TODO: change visbility of shred::layout::set_retransmitter_signature.
725        // Hardcode offsets for now;
726        const DATA_SHRED_OFFSET: usize = 1139;
727        const CODING_SHRED_OFFSET: usize = 1164;
728
729        let mut rng = rand::thread_rng();
730        let leader = Arc::new(Keypair::new());
731        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
732        let next_shred_index = rng.gen_range(0..32_000);
733        let data_shred =
734            new_rand_data_shred(&mut rng, next_shred_index, &shredder, &leader, true, true);
735        let coding_shred =
736            new_rand_coding_shreds(&mut rng, next_shred_index, 10, &shredder, &leader, true)[0]
737                .clone();
738
739        let mut data_shred_different_retransmitter_payload = data_shred.clone().into_payload();
740        let buffer = data_shred_different_retransmitter_payload
741            .get_mut(DATA_SHRED_OFFSET..DATA_SHRED_OFFSET + SIZE_OF_SIGNATURE)
742            .unwrap();
743        buffer.copy_from_slice(Signature::new_unique().as_ref());
744        let data_shred_different_retransmitter =
745            SolanaShred::new_from_serialized_shred(data_shred_different_retransmitter_payload)
746                .unwrap();
747
748        let mut coding_shred_different_retransmitter_payload = coding_shred.clone().into_payload();
749        let buffer = coding_shred_different_retransmitter_payload
750            .get_mut(CODING_SHRED_OFFSET..CODING_SHRED_OFFSET + SIZE_OF_SIGNATURE)
751            .unwrap();
752        buffer.copy_from_slice(Signature::new_unique().as_ref());
753        let coding_shred_different_retransmitter =
754            SolanaShred::new_from_serialized_shred(coding_shred_different_retransmitter_payload)
755                .unwrap();
756
757        let test_cases = vec![
758            // Same data shred from different retransmitter
759            (data_shred, data_shred_different_retransmitter),
760            // Same coding shred from different retransmitter
761            (coding_shred, coding_shred_different_retransmitter),
762        ];
763        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
764            let proof_data = generate_proof_data(shred1, shred2);
765            assert_eq!(
766                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
767                SlashingError::InvalidPayloadProof
768            );
769        }
770    }
771
772    #[test]
773    fn test_overlapping_erasure_meta_proof_valid() {
774        let mut rng = rand::thread_rng();
775        let leader = Arc::new(Keypair::new());
776        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
777        let next_shred_index = rng.gen_range(0..32_000);
778        let coding_shreds =
779            new_rand_coding_shreds(&mut rng, next_shred_index, 10, &shredder, &leader, true);
780        let (data_shred_next, coding_shred_next) = new_rand_shreds(
781            &mut rng,
782            next_shred_index + 1,
783            next_shred_index + 33,
784            10,
785            true,
786            &shredder,
787            &leader,
788            true,
789        );
790
791        // Fec set is overlapping
792        let test_cases = vec![
793            (coding_shreds[0].clone(), coding_shred_next[0].clone()),
794            (coding_shreds[0].clone(), data_shred_next[0].clone()),
795            (
796                coding_shreds[2].clone(),
797                coding_shred_next.last().unwrap().clone(),
798            ),
799            (
800                coding_shreds[2].clone(),
801                data_shred_next.last().unwrap().clone(),
802            ),
803        ];
804        for (shred1, shred2) in test_cases.iter().flat_map(|(a, b)| [(a, b), (b, a)]) {
805            let proof_data = generate_proof_data(shred1, shred2);
806            proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap();
807        }
808    }
809
810    #[test]
811    fn test_overlapping_erasure_meta_proof_invalid() {
812        let mut rng = rand::thread_rng();
813        let leader = Arc::new(Keypair::new());
814        let shredder = Shredder::new(SLOT, PARENT_SLOT, REFERENCE_TICK, VERSION).unwrap();
815        let next_shred_index = rng.gen_range(0..32_000);
816        let (data_shred, coding_shred) = new_rand_shreds(
817            &mut rng,
818            next_shred_index,
819            next_shred_index,
820            10,
821            true,
822            &shredder,
823            &leader,
824            true,
825        );
826        let next_shred_index = next_shred_index + data_shred.len() as u32;
827        let next_code_index = next_shred_index + coding_shred.len() as u32;
828        let (data_shred_next, coding_shred_next) = new_rand_shreds(
829            &mut rng,
830            next_shred_index,
831            next_code_index,
832            10,
833            true,
834            &shredder,
835            &leader,
836            true,
837        );
838        let test_cases = vec![
839            (
840                coding_shred[0].clone(),
841                data_shred_next[0].clone(),
842                SlashingError::ShredTypeMismatch,
843            ),
844            (
845                coding_shred[0].clone(),
846                coding_shred_next[0].clone(),
847                SlashingError::InvalidErasureMetaConflict,
848            ),
849            (
850                coding_shred[0].clone(),
851                data_shred_next.last().unwrap().clone(),
852                SlashingError::ShredTypeMismatch,
853            ),
854            (
855                coding_shred[0].clone(),
856                coding_shred_next.last().unwrap().clone(),
857                SlashingError::InvalidErasureMetaConflict,
858            ),
859        ];
860
861        for (shred1, shred2, expected) in test_cases
862            .iter()
863            .flat_map(|(a, b, c)| [(a, b, c), (b, a, c)])
864        {
865            let proof_data = generate_proof_data(shred1, shred2);
866            assert_eq!(
867                proof_data.verify_proof(SLOT, &leader.pubkey()).unwrap_err(),
868                *expected,
869            );
870        }
871    }
872}