median timestamp

This commit is contained in:
debris 2016-11-25 01:07:38 +01:00
parent 49dcd81f93
commit 98b514b256
1 changed files with 32 additions and 3 deletions

View File

@ -1,5 +1,7 @@
//! Bitcoin chain verifier
use std::cmp;
use std::collections::HashSet;
use db::{self, BlockRef, BlockLocation};
use super::{Verify, VerificationResult, Chain, Error, TransactionError, ContinueVerify};
use {chain, utils};
@ -57,12 +59,18 @@ impl ChainVerifier {
if let Some(work) = self.work_required(block, at_height) {
if !self.skip_pow && work != block.header().nbits {
trace!(target: "verification", "pow verification error at height: {}", at_height);
trace!(target: "verification", "block: {:?}", block);
trace!(target: "verification", "expected work: {}, got {}", work, block.header().nbits);
return Err(Error::Difficulty);
}
}
if let Some(median_timestamp) = self.ordered_median_timestamp(block, at_height) {
if median_timestamp >= block.block_header.time {
trace!(target: "verification", "median timestamp verification failed, median: {}, current: {}", median_timestamp, block.block_header.time);
return Err(Error::Timestamp);
}
}
let coinbase_spends = block.transactions()[0].total_spends();
let mut total_unspent = 0u64;
@ -267,13 +275,34 @@ impl ChainVerifier {
}
}
fn ordered_median_timestamp(&self, block: &chain::Block, height: u32) -> Option<u32> {
if height == 0 {
return None;
}
// TODO: make 11 a const
let max = cmp::min(height, 11);
let mut timestamps = HashSet::new();
let mut block_ref = block.block_header.previous_header_hash.clone().into();
// TODO: optimize it, so it does not make 11 redundant queries each time
for _ in 0..max {
let previous_header = self.store.block_header(block_ref).expect("block_ref < height; qed");
timestamps.insert(previous_header.time);
block_ref = previous_header.previous_header_hash.into();
}
let timestamps: Vec<_> = timestamps.into_iter().collect();
Some(timestamps[timestamps.len() / 2])
}
fn work_required(&self, block: &chain::Block, height: u32) -> Option<u32> {
if height == 0 {
return None;
}
let parent_ref = block.block_header.previous_header_hash.clone().into();
let previous_header = self.store.block_header(parent_ref).expect("expected to find parent header in database");
// should this be best_header or parent header?
// regtest do not pass with previous header, but, imo checking with best is a bit weird, mk
let previous_header = self.store.best_header().expect("self.height != 0; qed");
if utils::is_retarget_height(height) {
let retarget_ref = (height - utils::RETARGETING_INTERVAL).into();