From 961e4361cf88994a34da0acb9211b514a478436c Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 23 Nov 2016 04:10:11 +0100 Subject: [PATCH] pow validation in progress --- Cargo.lock | 1 + chain/src/block_header.rs | 40 ++++++++++++++++++++++ chain/src/lib.rs | 3 +- chain/src/nbits.rs | 55 ++++++++++++++++++++++++++++++ primitives/Cargo.toml | 1 + primitives/src/hash.rs | 9 +++++ primitives/src/lib.rs | 3 +- primitives/src/uint.rs | 23 +++++++++++++ verification/src/chain_verifier.rs | 3 +- 9 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 chain/src/nbits.rs diff --git a/Cargo.lock b/Cargo.lock index 993b20fd..a64f8600 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -493,6 +493,7 @@ dependencies = [ name = "primitives" version = "0.1.0" dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/chain/src/block_header.rs b/chain/src/block_header.rs index 64aaa07a..98cc44ac 100644 --- a/chain/src/block_header.rs +++ b/chain/src/block_header.rs @@ -5,6 +5,8 @@ use ser::{ }; use crypto::dhash256; use hash::H256; +use uint::U256; +use nbits::{NBits, MAX_NBITS_MAINNET, MAX_NBITS_REGTEST}; #[derive(PartialEq, Clone)] pub struct BlockHeader { @@ -20,6 +22,36 @@ impl BlockHeader { pub fn hash(&self) -> H256 { dhash256(&serialize(self)) } + + /// Returns the total work of the block + //pub fn work(&self) -> U256 { + //// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind) + //let mut ret = !self.target(); + //let mut ret1 = self.target(); + //ret1 = ret1 + 1.into(); + //ret = ret / ret1; + //ret = ret + 1.into(); + //ret + //} + + pub fn is_valid_proof_of_work(&self) -> bool { + let max = match NBits::new(MAX_NBITS_REGTEST).target() { + Some(max) => max, + None => return false, + }; + + let target = match NBits::new(self.nbits).target() { + Some(target) => target, + None => return false, + }; + + if target > max { + return false; + } + + let target = H256::from(target.to_little_endian()); + self.hash() <= target + } } impl fmt::Debug for BlockHeader { @@ -66,6 +98,7 @@ impl Deserializable for BlockHeader { mod tests { use ser::{Reader, Error as ReaderError, Stream}; use super::BlockHeader; + use nbits::MAX_NBITS_REGTEST; #[test] fn test_block_header_stream() { @@ -118,4 +151,11 @@ mod tests { assert_eq!(expected, reader.read().unwrap()); assert_eq!(ReaderError::UnexpectedEnd, reader.read::().unwrap_err()); } + + #[test] + fn test_is_valid_proof_of_work() { + let mut header = BlockHeader::default(); + header.nbits = MAX_NBITS_REGTEST; + assert!(header.is_valid_proof_of_work()); + } } diff --git a/chain/src/lib.rs b/chain/src/lib.rs index d0bd9799..cc14aba3 100644 --- a/chain/src/lib.rs +++ b/chain/src/lib.rs @@ -7,6 +7,7 @@ extern crate serialization as ser; mod block; mod block_header; mod merkle_root; +mod nbits; mod transaction; pub trait RepresentH256 { @@ -14,7 +15,7 @@ pub trait RepresentH256 { } pub use rustc_serialize::hex; -pub use primitives::{hash, bytes}; +pub use primitives::{hash, bytes, uint}; pub use self::block::Block; pub use self::block_header::BlockHeader; diff --git a/chain/src/nbits.rs b/chain/src/nbits.rs new file mode 100644 index 00000000..14ff7580 --- /dev/null +++ b/chain/src/nbits.rs @@ -0,0 +1,55 @@ +use uint::U256; + +pub const MAX_NBITS_MAINNET: u32 = 0x1d00ffff; +pub const MAX_NBITS_TESTNET: u32 = 0x1d00ffff; +pub const MAX_NBITS_REGTEST: u32 = 0x207fffff; + +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct NBits(u32); + +impl NBits { + pub fn new(u: u32) -> Self { + NBits(u) + } + + /// Computes the target [0, T] that a blockhash must land in to be valid + /// Returns None, if there is an overflow or its negative value + pub fn target(&self) -> Option { + let size = self.0 >> 24; + let mut word = self.0 & 0x007fffff; + + let result = if size <= 3 { + word = word >> (8 * (3 - size as usize)); + word.into() + } else { + U256::from(word) << (8 * (size as usize - 3)) + }; + + let is_negative = word != 0 && (self.0 & 0x00800000) != 0; + let is_overflow = (word != 0 && size > 34) || + (word > 0xff && size > 33) || + (word > 0xffff && size > 32); + + if is_negative || is_overflow { + None + } else { + Some(result) + } + } +} + +#[cfg(test)] +mod tests { + use super::NBits; + + #[test] + fn test_basic_nbits_target() { + assert_eq!(NBits::new(0x01003456).target(), Some(0.into())); + assert_eq!(NBits::new(0x01123456).target(), Some(0x12.into())); + assert_eq!(NBits::new(0x02008000).target(), Some(0x80.into())); + assert_eq!(NBits::new(0x05009234).target(), Some(0x92340000u64.into())); + // negative -0x12345600 + assert_eq!(NBits::new(0x04923456).target(), None); + assert_eq!(NBits::new(0x04123456).target(), Some(0x12345600u64.into())); + } +} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index faf0e094..c8f33616 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" [dependencies] heapsize = "0.3" +byteorder = "0.5" rustc-serialize = "0.3" [build-dependencies] diff --git a/primitives/src/hash.rs b/primitives/src/hash.rs index 5a31d685..17999dd2 100644 --- a/primitives/src/hash.rs +++ b/primitives/src/hash.rs @@ -105,6 +105,15 @@ macro_rules! impl_hash { } } + impl cmp::PartialOrd for $name { + fn partial_cmp(&self, other: &Self) -> Option { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref.partial_cmp(other_ref) + } + } + + impl Hash for $name { fn hash(&self, state: &mut H) where H: Hasher { state.write(&self.0); diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index eafddb14..c119fe84 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,7 +1,8 @@ #![cfg_attr(asm_available, feature(asm))] -extern crate rustc_serialize; +extern crate byteorder; #[macro_use] extern crate heapsize; +extern crate rustc_serialize; pub mod bytes; pub mod hash; diff --git a/primitives/src/uint.rs b/primitives/src/uint.rs index 9ae77bc8..ea0730f5 100644 --- a/primitives/src/uint.rs +++ b/primitives/src/uint.rs @@ -8,6 +8,7 @@ use std::{str, fmt}; use std::ops::{Shr, Shl, BitAnd, BitOr, BitXor, Not, Div, Rem, Mul, Add, Sub}; use std::cmp::Ordering; +use byteorder::{WriteBytesExt, LittleEndian, BigEndian}; use hex::{FromHex, FromHexError}; /// Conversion from decimal string error @@ -393,6 +394,28 @@ macro_rules! construct_uint { Ok(res) } + pub fn to_little_endian(&self) -> [u8; $n_words * 8] { + let mut result = [0u8; $n_words * 8]; + { + let mut result_ref: &mut [u8] = &mut result; + for word in self.0.into_iter() { + result_ref.write_u64::(*word).expect("sizeof($n_words * u8 * 8) == sizeof($n_words * u64); qed"); + } + } + result + } + + pub fn to_big_endian(&self) -> [u8; $n_words * 8] { + let mut result = [0u8; $n_words * 8]; + { + let mut result_ref: &mut [u8] = &mut result; + for word in self.0.into_iter().rev() { + result_ref.write_u64::(*word).expect("sizeof($n_words * u8 * 8) == sizeof($n_words * u64); qed"); + } + } + result + } + #[inline] pub fn low_u32(&self) -> u32 { let &$name(ref arr) = self; diff --git a/verification/src/chain_verifier.rs b/verification/src/chain_verifier.rs index 25fb7bc6..d423ae82 100644 --- a/verification/src/chain_verifier.rs +++ b/verification/src/chain_verifier.rs @@ -194,7 +194,8 @@ impl ChainVerifier { } // target difficulty threshold - if !self.skip_pow && !utils::check_nbits(&hash, block.header().nbits) { + //if !self.skip_pow && !utils::check_nbits(&hash, block.header().nbits) { + if !self.skip_pow && !block.header().is_valid_proof_of_work() { return Err(Error::Pow); }