Merge branch 'verification-target' of github.com:ethcore/parity-bitcoin into pow_validation
This commit is contained in:
commit
43dcd741d1
|
@ -14,6 +14,9 @@ pub trait BlockProvider {
|
||||||
/// resolves header bytes by block reference (number/hash)
|
/// resolves header bytes by block reference (number/hash)
|
||||||
fn block_header_bytes(&self, block_ref: BlockRef) -> Option<Bytes>;
|
fn block_header_bytes(&self, block_ref: BlockRef) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// resolves header bytes by block reference (number/hash)
|
||||||
|
fn block_header(&self, block_ref: BlockRef) -> Option<chain::BlockHeader>;
|
||||||
|
|
||||||
/// resolves deserialized block body by block reference (number/hash)
|
/// resolves deserialized block body by block reference (number/hash)
|
||||||
fn block(&self, block_ref: BlockRef) -> Option<chain::Block>;
|
fn block(&self, block_ref: BlockRef) -> Option<chain::Block>;
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,9 @@ const MAX_FORK_ROUTE_PRESET: usize = 128;
|
||||||
pub trait Store : BlockProvider + BlockStapler + TransactionProvider + TransactionMetaProvider {
|
pub trait Store : BlockProvider + BlockStapler + TransactionProvider + TransactionMetaProvider {
|
||||||
/// get best block
|
/// get best block
|
||||||
fn best_block(&self) -> Option<BestBlock>;
|
fn best_block(&self) -> Option<BestBlock>;
|
||||||
|
|
||||||
|
/// get best header
|
||||||
|
fn best_header(&self) -> Option<chain::BlockHeader>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blockchain storage with rocksdb database
|
/// Blockchain storage with rocksdb database
|
||||||
|
@ -165,7 +168,6 @@ impl Storage {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// update transactions metadata in the specified database transaction
|
/// update transactions metadata in the specified database transaction
|
||||||
fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction])
|
fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction])
|
||||||
-> Result<(), Error>
|
-> Result<(), Error>
|
||||||
|
@ -385,6 +387,12 @@ impl BlockProvider for Storage {
|
||||||
self.resolve_hash(block_ref).and_then(|h| self.get(COL_BLOCK_HEADERS, &*h))
|
self.resolve_hash(block_ref).and_then(|h| self.get(COL_BLOCK_HEADERS, &*h))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, block_ref: BlockRef) -> Option<chain::BlockHeader> {
|
||||||
|
self.block_header_bytes(block_ref).map(
|
||||||
|
|bytes| deserialize::<_, chain::BlockHeader>(bytes.as_ref())
|
||||||
|
.expect("Error deserializing header, possible db corruption"))
|
||||||
|
}
|
||||||
|
|
||||||
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
|
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
|
||||||
self.resolve_hash(block_ref)
|
self.resolve_hash(block_ref)
|
||||||
.map(|h| self.block_transaction_hashes_by_hash(&h))
|
.map(|h| self.block_transaction_hashes_by_hash(&h))
|
||||||
|
@ -596,6 +604,12 @@ impl Store for Storage {
|
||||||
fn best_block(&self) -> Option<BestBlock> {
|
fn best_block(&self) -> Option<BestBlock> {
|
||||||
self.best_block.read().clone()
|
self.best_block.read().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn best_header(&self) -> Option<chain::BlockHeader> {
|
||||||
|
self.best_block.read().as_ref().and_then(
|
||||||
|
|bb| Some(self.block_header_by_hash(&bb.hash).expect("Best block exists but no such header. Race condition?")),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -81,6 +81,13 @@ impl BlockProvider for TestStorage {
|
||||||
.map(|ref block| serialization::serialize(block.header()))
|
.map(|ref block| serialization::serialize(block.header()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, block_ref: BlockRef) -> Option<chain::BlockHeader> {
|
||||||
|
let data = self.data.read();
|
||||||
|
self.resolve_hash(block_ref)
|
||||||
|
.and_then(|ref h| data.blocks.get(h))
|
||||||
|
.map(|ref block| block.header().clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
|
fn block_transaction_hashes(&self, block_ref: BlockRef) -> Vec<H256> {
|
||||||
let data = self.data.read();
|
let data = self.data.read();
|
||||||
self.resolve_hash(block_ref)
|
self.resolve_hash(block_ref)
|
||||||
|
@ -174,5 +181,11 @@ impl Store for TestStorage {
|
||||||
fn best_block(&self) -> Option<BestBlock> {
|
fn best_block(&self) -> Option<BestBlock> {
|
||||||
self.data.read().best_block.clone()
|
self.data.read().best_block.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn best_header(&self) -> Option<chain::BlockHeader> {
|
||||||
|
self.data.read().best_block.as_ref().and_then(
|
||||||
|
|bb| Some(self.block_header(BlockRef::Hash(bb.hash.clone())).expect("Best block exists but no such header. Race condition?"))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,12 @@ impl ChainVerifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> {
|
fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> {
|
||||||
|
// check that difficulty matches the adjusted level
|
||||||
|
if let Some(expected_nbits) = self.expected_nbits() {
|
||||||
|
if !self.skip_pow && expected_nbits != block.header().nbits {
|
||||||
|
return Err(Error::Difficulty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let coinbase_spends = block.transactions()[0].total_spends();
|
let coinbase_spends = block.transactions()[0].total_spends();
|
||||||
|
|
||||||
|
@ -259,6 +265,22 @@ impl ChainVerifier {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expected_nbits(&self) -> Option<u32> {
|
||||||
|
|
||||||
|
let best_header = match self.store.best_header() {
|
||||||
|
Some(bb) => bb,
|
||||||
|
None => { return None; }
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.store.best_block().expect("At least genesis should exist at this point").number < 2016 {
|
||||||
|
return Some(best_header.nbits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: calculate difficulty adjustment
|
||||||
|
|
||||||
|
Some(best_header.nbits)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Verify for ChainVerifier {
|
impl Verify for ChainVerifier {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
//! Verification utilities
|
//! Verification utilities
|
||||||
use primitives::hash::H256;
|
use primitives::{H256, U256};
|
||||||
|
use primitives::uint::Uint;
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
use chain;
|
use chain;
|
||||||
use script::{self, Script};
|
use script::{self, Script};
|
||||||
|
|
||||||
const MAX_NBITS: u32 = 0x207fffff;
|
const MAX_NBITS: u32 = 0x207fffff;
|
||||||
|
|
||||||
|
/// Simple nbits check that does not require 256-bit arithmetic
|
||||||
pub fn check_nbits(hash: &H256, n_bits: u32) -> bool {
|
pub fn check_nbits(hash: &H256, n_bits: u32) -> bool {
|
||||||
if n_bits > MAX_NBITS { return false; }
|
if n_bits > MAX_NBITS { return false; }
|
||||||
|
|
||||||
|
@ -82,11 +84,28 @@ pub fn p2sh_sigops(output: &Script, input_ref: &Script) -> usize {
|
||||||
output.sigop_count_p2sh(input_ref).unwrap_or(0)
|
output.sigop_count_p2sh(input_ref).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts difficulty threshold to the compact representation (nbits)
|
||||||
|
pub fn threshold_to_nbits(val: U256) -> u32 {
|
||||||
|
let mut nb = [0u8; 4];
|
||||||
|
let bits = val.bits() as u8;
|
||||||
|
nb[0] = (bits + 7) / 8;
|
||||||
|
if val.byte(nb[0] as usize - 1) > 0x7f { nb[0] += 1 }
|
||||||
|
|
||||||
|
nb[1] = val.byte((nb[0]-1) as usize);
|
||||||
|
nb[2] = val.byte((nb[0]-2) as usize);
|
||||||
|
if nb[0] > 2 {
|
||||||
|
nb[3] = val.byte((nb[0]-3) as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigEndian::read_u32(&nb)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::{block_reward_satoshi, check_nbits};
|
use super::{block_reward_satoshi, check_nbits, threshold_to_nbits};
|
||||||
use primitives::hash::H256;
|
use primitives::{H256, U256};
|
||||||
|
use primitives::uint::Uint;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reward() {
|
fn reward() {
|
||||||
|
@ -127,4 +146,13 @@ mod tests {
|
||||||
let nbits = 404129525;
|
let nbits = 404129525;
|
||||||
assert!(check_nbits(&hash, nbits));
|
assert!(check_nbits(&hash, nbits));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn threshold() {
|
||||||
|
let test1 = U256::from(1000u64);
|
||||||
|
assert_eq!(0x0203e800, threshold_to_nbits(test1));
|
||||||
|
|
||||||
|
let test2 = U256::from(2).pow(U256::from(256-32))-U256::from(1);
|
||||||
|
assert_eq!(0x1d00ffff, threshold_to_nbits(test2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue