HeaderAcceptor finished

This commit is contained in:
debris 2016-12-11 19:10:00 +01:00
parent 0df90a85bd
commit 95c2fa7d8d
5 changed files with 133 additions and 43 deletions

View File

@ -1,18 +1,17 @@
use network::{Magic, ConsensusParams};
use db::{SharedStore, PreviousTransactionOutputProvider, BlockHeaderProvider};
use db::{SharedStore, PreviousTransactionOutputProvider};
use sigops::{StoreWithUnretainedOutputs, transaction_sigops};
use utils::{work_required, block_reward_satoshi};
use utils::block_reward_satoshi;
use canon::CanonBlock;
use constants::MAX_BLOCK_SIGOPS;
use error::Error;
const EXPECT_ORDERED: &'static str = "Block ancestors expected to be found in database";
const EXPECT_CANON: &'static str = "Block ancestors expected to be found in canon chain";
/// Flexible verification of ordered block
pub struct BlockAcceptor<'a> {
pub finality: BlockFinality<'a>,
pub sigops: BlockSigops<'a>,
pub work: BlockWork<'a>,
pub coinbase_claim: BlockCoinbaseClaim<'a>,
}
@ -22,7 +21,6 @@ impl<'a> BlockAcceptor<'a> {
BlockAcceptor {
finality: BlockFinality::new(block, height),
sigops: BlockSigops::new(block, store.as_previous_transaction_output_provider(), params, MAX_BLOCK_SIGOPS),
work: BlockWork::new(block, store.as_block_header_provider(), height, network),
coinbase_claim: BlockCoinbaseClaim::new(block, store.as_previous_transaction_output_provider(), height),
}
}
@ -30,7 +28,6 @@ impl<'a> BlockAcceptor<'a> {
pub fn check(&self) -> Result<(), Error> {
try!(self.finality.check());
try!(self.sigops.check());
try!(self.work.check());
try!(self.coinbase_claim.check());
Ok(())
}
@ -88,7 +85,7 @@ impl<'a> BlockRule for BlockSigops<'a> {
let store = StoreWithUnretainedOutputs::new(self.store, &*self.block);
let bip16_active = self.block.header.raw.time >= self.consensus_params.bip16_time;
let sigops = self.block.transactions.iter()
.map(|tx| transaction_sigops(&tx.raw, &store, bip16_active).expect(EXPECT_ORDERED))
.map(|tx| transaction_sigops(&tx.raw, &store, bip16_active).expect(EXPECT_CANON))
.sum::<usize>();
if sigops > self.max_sigops {
@ -99,37 +96,6 @@ impl<'a> BlockRule for BlockSigops<'a> {
}
}
pub struct BlockWork<'a> {
block: CanonBlock<'a>,
store: &'a BlockHeaderProvider,
height: u32,
network: Magic,
}
impl<'a> BlockWork<'a> {
fn new(block: CanonBlock<'a>, store: &'a BlockHeaderProvider, height: u32, network: Magic) -> Self {
BlockWork {
block: block,
store: store,
height: height,
network: network,
}
}
}
impl<'a> BlockRule for BlockWork<'a> {
fn check(&self) -> Result<(), Error> {
let previous_header_hash = self.block.header.raw.previous_header_hash.clone();
let time = self.block.header.raw.time;
let work = work_required(previous_header_hash, time, self.height, self.store, self.network);
if work == self.block.header.raw.bits {
Ok(())
} else {
Err(Error::Difficulty)
}
}
}
pub struct BlockCoinbaseClaim<'a> {
block: CanonBlock<'a>,
store: &'a PreviousTransactionOutputProvider,
@ -152,7 +118,7 @@ impl<'a> BlockRule for BlockCoinbaseClaim<'a> {
let total_outputs = self.block.transactions.iter()
.skip(1)
.flat_map(|tx| tx.raw.inputs.iter())
.map(|input| store.previous_transaction_output(&input.previous_output).expect(EXPECT_ORDERED))
.map(|input| store.previous_transaction_output(&input.previous_output).expect(EXPECT_CANON))
.map(|output| output.value)
.sum::<u64>();

View File

@ -17,7 +17,7 @@ impl<'a> ChainAcceptor<'a> {
pub fn new(store: &'a SharedStore, network: Magic, block: CanonBlock<'a>, height: u32) -> Self {
ChainAcceptor {
block: BlockAcceptor::new(store, network, block, height),
header: HeaderAcceptor::new(block.header()),
header: HeaderAcceptor::new(store, network, block.header(), height),
transactions: block.transactions().into_iter().map(TransactionAcceptor::new).collect(),
}
}

View File

@ -1,18 +1,139 @@
use std::cmp;
use std::collections::BTreeSet;
use network::Magic;
use db::{SharedStore, BlockHeaderProvider};
use canon::CanonHeader;
use constants::MIN_BLOCK_VERSION;
use error::Error;
use utils::work_required;
const EXPECT_CANON: &'static str = "Block ancestors expected to be found in canon chain";
pub struct HeaderAcceptor<'a> {
_tmp: CanonHeader<'a>,
pub version: HeaderVersion<'a>,
pub work: HeaderWork<'a>,
pub median_timestamp: HeaderMedianTimestamp<'a>,
}
impl<'a> HeaderAcceptor<'a> {
pub fn new(header: CanonHeader<'a>) -> Self {
pub fn new(store: &'a SharedStore, network: Magic, header: CanonHeader<'a>, height: u32) -> Self {
HeaderAcceptor {
_tmp: header,
// TODO: check last 1000 blocks instead of hardcoding the value
version: HeaderVersion::new(header, MIN_BLOCK_VERSION),
work: HeaderWork::new(header, store.as_block_header_provider(), height, network),
median_timestamp: HeaderMedianTimestamp::new(header, store.as_block_header_provider(), height, network),
}
}
pub fn check(&self) -> Result<(), Error> {
try!(self.version.check());
try!(self.work.check());
try!(self.median_timestamp.check());
Ok(())
}
}
pub trait HeaderRule {
fn check(&self) -> Result<(), Error>;
}
pub struct HeaderVersion<'a> {
header: CanonHeader<'a>,
min_version: u32,
}
impl<'a> HeaderVersion<'a> {
fn new(header: CanonHeader<'a>, min_version: u32) -> Self {
HeaderVersion {
header: header,
min_version: min_version,
}
}
}
impl<'a> HeaderRule for HeaderVersion<'a> {
fn check(&self) -> Result<(), Error> {
if self.header.raw.version < self.min_version {
Err(Error::OldVersionBlock)
} else {
Ok(())
}
}
}
pub struct HeaderWork<'a> {
header: CanonHeader<'a>,
store: &'a BlockHeaderProvider,
height: u32,
network: Magic,
}
impl<'a> HeaderWork<'a> {
fn new(header: CanonHeader<'a>, store: &'a BlockHeaderProvider, height: u32, network: Magic) -> Self {
HeaderWork {
header: header,
store: store,
height: height,
network: network,
}
}
}
impl<'a> HeaderRule for HeaderWork<'a> {
fn check(&self) -> Result<(), Error> {
let previous_header_hash = self.header.raw.previous_header_hash.clone();
let time = self.header.raw.time;
let work = work_required(previous_header_hash, time, self.height, self.store, self.network);
if work == self.header.raw.bits {
Ok(())
} else {
Err(Error::Difficulty)
}
}
}
pub struct HeaderMedianTimestamp<'a> {
header: CanonHeader<'a>,
store: &'a BlockHeaderProvider,
height: u32,
network: Magic,
}
impl<'a> HeaderMedianTimestamp<'a> {
fn new(header: CanonHeader<'a>, store: &'a BlockHeaderProvider, height: u32, network: Magic) -> Self {
HeaderMedianTimestamp {
header: header,
store: store,
height: height,
network: network,
}
}
}
impl<'a> HeaderRule for HeaderMedianTimestamp<'a> {
fn check(&self) -> Result<(), Error> {
// TODO: timestamp validation on testnet is broken
if self.height == 0 || self.network == Magic::Testnet {
return Ok(());
}
let ancestors = cmp::min(11, self.height);
let mut timestamps = BTreeSet::new();
let mut block_ref = self.header.raw.previous_header_hash.clone().into();
for _ in 0..ancestors {
let previous_header = self.store.block_header(block_ref).expect(EXPECT_CANON);
timestamps.insert(previous_header.time);
block_ref = previous_header.previous_header_hash.into();
}
let timestamps = timestamps.into_iter().collect::<Vec<_>>();
let median = timestamps[timestamps.len() / 2];
if self.header.raw.time <= median {
Err(Error::Timestamp)
} else {
Ok(())
}
}
}

View File

@ -6,3 +6,4 @@ pub const MAX_BLOCK_SIZE: usize = 1_000_000;
pub const MAX_BLOCK_SIGOPS: usize = 20_000;
pub const MIN_COINBASE_SIZE: usize = 2;
pub const MAX_COINBASE_SIZE: usize = 100;
pub const MIN_BLOCK_VERSION: u32 = 0;

View File

@ -34,6 +34,8 @@ pub enum Error {
Size(usize),
/// Block transactions are not final.
NonFinalBlock,
/// Old version block.
OldVersionBlock,
}
#[derive(Debug, PartialEq)]