fix(add helper for timestamp overflows) (#10330)

* fix(add helper timestamp overflows)

* fix(simplify code)

* fix(make helper private)
This commit is contained in:
Niklas Adolfsson 2019-02-12 15:16:23 +01:00 committed by Kirill Pimenov
parent d89b8d904f
commit d8394bded7
2 changed files with 34 additions and 4 deletions

View File

@ -89,6 +89,8 @@ pub enum BlockError {
InvalidNumber(Mismatch<BlockNumber>),
/// Block number isn't sensible.
RidiculousNumber(OutOfBounds<BlockNumber>),
/// Timestamp header overflowed
TimestampOverflow,
/// Too many transactions from a particular address.
TooManyTransactions(Address),
/// Parent given is unknown.
@ -138,6 +140,7 @@ impl fmt::Display for BlockError {
UnknownParent(ref hash) => format!("Unknown parent: {}", hash),
UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash),
UnknownEpochTransition(ref num) => format!("Unknown transition to epoch number: {}", num),
TimestampOverflow => format!("Timestamp overflow"),
TooManyTransactions(ref address) => format!("Too many transactions from: {}", address),
};

View File

@ -40,6 +40,25 @@ use types::{BlockNumber, header::Header};
use types::transaction::SignedTransaction;
use verification::queue::kind::blocks::Unverified;
/// Returns `Ok<SystemTime>` when the result less or equal to `i32::max_value` to prevent `SystemTime` to panic because
/// it is platform specific, may be i32 or i64.
///
/// `Err<BlockError::TimestampOver` otherwise.
///
// FIXME: @niklasad1 - remove this when and use `SystemTime::checked_add`
// when https://github.com/rust-lang/rust/issues/55940 is stabilized.
fn timestamp_checked_add(sys: SystemTime, d2: Duration) -> Result<SystemTime, BlockError> {
let d1 = sys.duration_since(UNIX_EPOCH).map_err(|_| BlockError::TimestampOverflow)?;
let total_time = d1.checked_add(d2).ok_or(BlockError::TimestampOverflow)?;
if total_time.as_secs() <= i32::max_value() as u64 {
Ok(sys + d2)
} else {
Err(BlockError::TimestampOverflow)
}
}
/// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreverifiedBlock {
/// Populated block header
@ -306,7 +325,7 @@ pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool,
const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15);
let max_time = SystemTime::now() + ACCEPTABLE_DRIFT;
let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9;
let timestamp = UNIX_EPOCH + Duration::from_secs(header.timestamp());
let timestamp = timestamp_checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp()))?;
if timestamp > invalid_threshold {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: timestamp })))
@ -328,8 +347,8 @@ fn verify_parent(header: &Header, parent: &Header, engine: &EthEngine) -> Result
let gas_limit_divisor = engine.params().gas_limit_bound_divisor;
if !engine.is_timestamp_valid(header.timestamp(), parent.timestamp()) {
let min = SystemTime::now() + Duration::from_secs(parent.timestamp() + 1);
let found = SystemTime::now() + Duration::from_secs(header.timestamp());
let min = timestamp_checked_add(SystemTime::now(), Duration::from_secs(parent.timestamp().saturating_add(1)))?;
let found = timestamp_checked_add(SystemTime::now(), Duration::from_secs(header.timestamp()))?;
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found })))
}
if header.number() != parent.number() + 1 {
@ -743,7 +762,8 @@ mod tests {
check_fail_timestamp(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine, &bc), false);
header = good.clone();
header.set_timestamp(2450000000);
// will return `BlockError::TimestampOverflow` when timestamp > `i32::max_value()`
header.set_timestamp(i32::max_value() as u64);
check_fail_timestamp(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine), false);
header = good.clone();
@ -815,4 +835,11 @@ mod tests {
check_fail(unordered_test(&create_test_block_with_data(&header, &bad_transactions, &[]), &engine), TooManyTransactions(keypair.address()));
unordered_test(&create_test_block_with_data(&header, &good_transactions, &[]), &engine).unwrap();
}
#[test]
fn checked_add_systime_dur() {
assert!(timestamp_checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64 + 1, 0)).is_err());
assert!(timestamp_checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64, 0)).is_ok());
assert!(timestamp_checked_add(UNIX_EPOCH, Duration::new(i32::max_value() as u64 - 1, 1_000_000_000)).is_ok());
}
}