Skip to content

Commit

Permalink
Add negative tests for backward verification
Browse files Browse the repository at this point in the history
  • Loading branch information
romac committed Jan 9, 2021
1 parent 2e725fa commit a4e2edb
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 22 deletions.
124 changes: 103 additions & 21 deletions light-client/tests/backward.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, time::Duration};

use tendermint::Time;
use tendermint::{hash::Algorithm, Hash, Time};

use tendermint_light_client::{
components::{
Expand All @@ -22,7 +22,7 @@ use tendermint_testgen::{
Generator, LightChain,
};

use proptest::prelude::*;
use proptest::{prelude::*, test_runner::TestRng};

fn testgen_to_lb(tm_lb: TGLightBlock) -> LightBlock {
LightBlock {
Expand Down Expand Up @@ -105,13 +105,11 @@ fn ok_test(tc: TestCase) -> Result<(), TestCaseError> {
Ok(())
}

// fn bad_test(tc: TestCase) -> Result<(), TestCaseError> {
// let result = verify(tc);

// prop_assert!(result.is_err());

// Ok(())
// }
fn bad_test(tc: TestCase) -> Result<(), TestCaseError> {
let result = verify(tc);
prop_assert!(result.is_err());
Ok(())
}

fn testcase(max: u32) -> impl Strategy<Value = TestCase> {
(1..=max).prop_flat_map(move |length| {
Expand All @@ -126,15 +124,90 @@ fn testcase(max: u32) -> impl Strategy<Value = TestCase> {
})
}

// fn mutate(tc: &mut TestCase) {
// let trusted = &mut tc.chain.light_blocks[tc.trusted_height.value() as usize - 1];
// if let Some(header) = trusted.header.as_mut() {
// header.last_block_id_hash = None;
// }
// }
fn remove_last_block_id_hash(mut tc: TestCase, mut rng: TestRng) -> TestCase {
let from = tc.target_height.value() + 1;
let to = tc.trusted_height.value() + 1;
let height = rng.gen_range(from, to);

dbg!(tc.target_height, tc.trusted_height, height);

let block = tc.chain.block_mut(height).unwrap();

if let Some(header) = block.header.as_mut() {
header.last_block_id_hash = None;
}

tc
}

fn corrupt_hash(mut tc: TestCase, mut rng: TestRng) -> TestCase {
let from = tc.target_height.value();
let to = tc.trusted_height.value();
let height = rng.gen_range(from, to);

dbg!(tc.target_height, tc.trusted_height, height);

let block = tc.chain.block_mut(height).unwrap();

if let Some(header) = block.header.as_mut() {
header.time = Some(1610105021);
}

tc
}

fn corrupt_last_block_id_hash(mut tc: TestCase, mut rng: TestRng) -> TestCase {
let from = tc.target_height.value() + 1;
let to = tc.trusted_height.value() + 1;
let height = rng.gen_range(from, to);

dbg!(tc.target_height, tc.trusted_height, height);

let block = tc.chain.block_mut(height).unwrap();

if let Some(header) = block.header.as_mut() {
let hash = Hash::from_hex_upper(
Algorithm::Sha256,
"C68B4CFC7F9AA239F9E0DF7CDEF264DD1CDFE8B73EF04B5600A20111144F42BF",
)
.unwrap();

header.last_block_id_hash = Some(hash);
}

tc
}

fn tc_missing_last_block_id_hash(max: u32) -> impl Strategy<Value = TestCase> {
testcase(max)
.prop_filter("target == trusted", |tc| {
tc.target_height != tc.trusted_height
})
.prop_perturb(remove_last_block_id_hash)
}

fn tc_corrupted_last_block_id_hash(max: u32) -> impl Strategy<Value = TestCase> {
testcase(max)
.prop_filter("target == trusted", |tc| {
tc.target_height != tc.trusted_height
})
.prop_perturb(corrupt_last_block_id_hash)
}

fn tc_corrupted_hash(max: u32) -> impl Strategy<Value = TestCase> {
testcase(max)
.prop_filter("target == trusted", |tc| {
tc.target_height != tc.trusted_height
})
.prop_perturb(corrupt_hash)
}

proptest! {
#![proptest_config(ProptestConfig::with_cases(5))]
#![proptest_config(ProptestConfig {
cases: 20,
max_shrink_iters: 0,
..Default::default()
})]

#[test]
fn prop_target_equal_trusted_first_block(mut tc in testcase(100)) {
Expand Down Expand Up @@ -168,9 +241,18 @@ proptest! {
ok_test(tc)?;
}

// #[test]
// fn bad(mut tc in testcase(100)) {
// mutate(&mut tc);
// bad_test(tc)?;
// }
#[test]
fn missing_last_block_id_hash(tc in tc_missing_last_block_id_hash(100)) {
bad_test(tc)?;
}

#[test]
fn corrupted_last_block_id_hash(tc in tc_corrupted_last_block_id_hash(100)) {
bad_test(tc)?;
}

#[test]
fn corrupted_hash(tc in tc_corrupted_hash(100)) {
bad_test(tc)?;
}
}
2 changes: 1 addition & 1 deletion light-client/tests/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,4 @@ fn run_tests() {
tester.add_test("forward verification with bisection", forward_test);
tester.run_foreach_in_dir("bisection/single_peer");
tester.finalize();
}
}
8 changes: 8 additions & 0 deletions testgen/src/light_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ impl LightChain {
.find(|lb| lb.height() == target_height)
}

/// fetches a mutable block from LightChain at a certain height
/// it returns None if a block does not exist for the target_height
pub fn block_mut(&mut self, target_height: u64) -> Option<&mut LightBlock> {
self.light_blocks
.iter_mut()
.find(|lb| lb.height() == target_height)
}

/// fetches the latest block from LightChain
pub fn latest_block(&self) -> &LightBlock {
self.light_blocks
Expand Down

0 comments on commit a4e2edb

Please sign in to comment.