consensus: Add tests for block node time checks

Part of #477.
This commit is contained in:
teor 2020-06-17 21:19:23 +10:00
parent a41f4f68cf
commit 8fde8c1a6b
1 changed files with 136 additions and 0 deletions

View File

@ -40,3 +40,139 @@ fn node_time_check_helper(
pub(super) fn node_time_check(block: Arc<Block>) -> Result<(), Error> {
node_time_check_helper(block.header.time, Utc::now())
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::offset::{LocalResult, TimeZone};
use zebra_chain::serialization::ZcashDeserialize;
#[test]
fn time_check_past_block() {
// This block is also verified as part of the BlockVerifier service
// tests.
let block =
Arc::<Block>::zcash_deserialize(&zebra_test::vectors::BLOCK_MAINNET_415000_BYTES[..])
.expect("block should deserialize");
// This check is non-deterministic, but BLOCK_MAINNET_415000 is
// a long time in the past. So it's unlikely that the test machine
// will have a clock that's far enough in the past for the test to
// fail.
node_time_check(block).expect("the header time from a mainnet block should be valid");
}
#[test]
fn time_check_now() {
// These checks are deteministic, because all the times are offset
// from the current time.
let now = Utc::now();
let three_hours_in_the_past = now - Duration::hours(3);
let two_hours_in_the_future = now + Duration::hours(2);
let two_hours_and_one_second_in_the_future =
now + Duration::hours(2) + Duration::seconds(1);
node_time_check_helper(now, now)
.expect("the current time should be valid as a block header time");
node_time_check_helper(three_hours_in_the_past, now)
.expect("a past time should be valid as a block header time");
node_time_check_helper(two_hours_in_the_future, now)
.expect("2 hours in the future should be valid as a block header time");
node_time_check_helper(two_hours_and_one_second_in_the_future, now).expect_err(
"2 hours and 1 second in the future should be invalid as a block header time",
);
// Now invert the tests
// 3 hours in the future should fail
node_time_check_helper(now, three_hours_in_the_past)
.expect_err("3 hours in the future should be invalid as a block header time");
// The past should succeed
node_time_check_helper(now, two_hours_in_the_future)
.expect("2 hours in the past should be valid as a block header time");
node_time_check_helper(now, two_hours_and_one_second_in_the_future)
.expect("2 hours and 1 second in the past should be valid as a block header time");
}
/// Valid unix epoch timestamps for blocks, in seconds
static BLOCK_HEADER_VALID_TIMESTAMPS: &[i64] = &[
// These times are currently invalid DateTimes, but they could
// become valid in future chrono versions
i64::MIN,
i64::MIN + 1,
// These times are valid DateTimes
(u32::MIN as i64) - 1,
(u32::MIN as i64),
(u32::MIN as i64) + 1,
(i32::MIN as i64) - 1,
(i32::MIN as i64),
(i32::MIN as i64) + 1,
-1,
0,
1,
// maximum nExpiryHeight or lock_time, in blocks
499_999_999,
// minimum lock_time, in seconds
500_000_000,
500_000_001,
];
/// Invalid unix epoch timestamps for blocks, in seconds
static BLOCK_HEADER_INVALID_TIMESTAMPS: &[i64] = &[
(i32::MAX as i64) - 1,
(i32::MAX as i64),
(i32::MAX as i64) + 1,
(u32::MAX as i64) - 1,
(u32::MAX as i64),
(u32::MAX as i64) + 1,
// These times are currently invalid DateTimes, but they could
// become valid in future chrono versions
i64::MAX - 1,
i64::MAX,
];
#[test]
fn time_check_fixed() {
// These checks are non-deterministic, but the times are all in the
// distant past or far future. So it's unlikely that the test
// machine will have a clock that makes these tests fail.
let now = Utc::now();
for valid_timestamp in BLOCK_HEADER_VALID_TIMESTAMPS {
let block_header_time = match Utc.timestamp_opt(*valid_timestamp, 0) {
LocalResult::Single(time) => time,
LocalResult::None => {
// Skip the test if the timestamp is invalid
continue;
}
LocalResult::Ambiguous(_, _) => {
// Utc doesn't have ambiguous times
unreachable!();
}
};
node_time_check_helper(block_header_time, now)
.expect("the time should be valid as a block header time");
// Invert the check, leading to an invalid time
node_time_check_helper(now, block_header_time)
.expect_err("the inverse comparison should be invalid");
}
for invalid_timestamp in BLOCK_HEADER_INVALID_TIMESTAMPS {
let block_header_time = match Utc.timestamp_opt(*invalid_timestamp, 0) {
LocalResult::Single(time) => time,
LocalResult::None => {
// Skip the test if the timestamp is invalid
continue;
}
LocalResult::Ambiguous(_, _) => {
// Utc doesn't have ambiguous times
unreachable!();
}
};
node_time_check_helper(block_header_time, now)
.expect_err("the time should be invalid as a block header time");
// Invert the check, leading to a valid time
node_time_check_helper(now, block_header_time)
.expect("the inverse comparison should be valid");
}
}
}