From ce23dda6cb6c106493322921ae140e00e1874041 Mon Sep 17 00:00:00 2001 From: Ignotus Peverell Date: Mon, 26 Dec 2016 15:39:31 -0800 Subject: [PATCH] Block headers maintain total difficulty up to previous. Changed all references of target to difficulty to stay consistent. Ported retargeting algo to use difficulty as well. --- chain/src/pipe.rs | 28 ++- chain/tests/mine_simple_chain.rs | 15 +- core/Cargo.toml | 1 + core/src/consensus.rs | 123 ++++++------ core/src/core/block.rs | 37 +++- core/src/core/mod.rs | 4 +- core/src/core/target.rs | 325 +++++-------------------------- core/src/genesis.rs | 6 +- core/src/lib.rs | 1 + core/src/pow/mod.rs | 30 +-- grin/src/miner.rs | 10 +- 11 files changed, 191 insertions(+), 389 deletions(-) diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index 631c21343..92bbf8201 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -20,7 +20,8 @@ use secp; use time; use core::consensus; -use core::core::hash::Hash; +use core::core::hash::{Hash, Hashed}; +use core::core::target::Difficulty; use core::core::{BlockHeader, Block, Proof}; use core::pow; use types; @@ -50,8 +51,10 @@ pub struct BlockContext { pub enum Error { /// The block doesn't fit anywhere in our chain Unfit(String), - /// Target is too high either compared to ours or the block PoW hash - TargetTooHigh, + /// Difficulty is too low either compared to ours or the block PoW hash + DifficultyTooLow, + /// Addition of difficulties on all previous block is wrong + WrongTotalDifficulty, /// Size of the Cuckoo graph in block header doesn't match PoW requirements WrongCuckooSize, /// The proof of work is invalid @@ -137,20 +140,25 @@ fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> { return Err(Error::InvalidBlockTime); } + if b.header.total_difficulty != + prev.total_difficulty.clone() + Difficulty::from_hash(&prev.hash()) { + return Err(Error::WrongTotalDifficulty); + } + // verify the proof of work and related parameters - let (diff_target, cuckoo_sz) = consensus::next_target(header.timestamp.to_timespec().sec, - prev.timestamp.to_timespec().sec, - prev.target, - prev.cuckoo_len); - if header.target > diff_target { - return Err(Error::TargetTooHigh); + let (difficulty, cuckoo_sz) = consensus::next_target(header.timestamp.to_timespec().sec, + prev.timestamp.to_timespec().sec, + prev.difficulty, + prev.cuckoo_len); + if header.difficulty < difficulty { + return Err(Error::DifficultyTooLow); } if header.cuckoo_len != cuckoo_sz && !ctx.opts.intersects(EASY_POW) { return Err(Error::WrongCuckooSize); } if ctx.opts.intersects(EASY_POW) { - if !pow::verify_size(b, 15) { + if !pow::verify_size(b, 16) { return Err(Error::InvalidPow); } } else if !pow::verify(b) { diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 00c04bcc8..596d178f1 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -15,12 +15,15 @@ extern crate grin_core; extern crate grin_chain; extern crate rand; +extern crate time; extern crate secp256k1zkp as secp; use std::sync::Arc; use rand::os::OsRng; use grin_chain::types::*; +use grin_core::core::hash::Hashed; +use grin_core::core::target::Difficulty; use grin_core::pow; use grin_core::core; use grin_core::consensus; @@ -31,7 +34,8 @@ fn mine_empty_chain() { let store = grin_chain::store::ChainKVStore::new(".grin".to_string()).unwrap(); // save a genesis block - let gen = grin_core::genesis::genesis(); + let mut gen = grin_core::genesis::genesis(); + gen.header.cuckoo_len = 16; store.save_block(&gen).unwrap(); // setup a new head tip @@ -47,16 +51,17 @@ fn mine_empty_chain() { for n in 1..4 { let mut b = core::Block::new(&prev.header, vec![], reward_key).unwrap(); + b.header.timestamp = prev.header.timestamp + time::Duration::seconds(60); - let (diff_target, _) = consensus::next_target(b.header.timestamp.to_timespec().sec, + let (difficulty, _) = consensus::next_target(b.header.timestamp.to_timespec().sec, prev.header.timestamp.to_timespec().sec, - prev.header.target, + prev.header.difficulty.clone(), prev.header.cuckoo_len); - let (proof, nonce) = pow::pow_size(&b, diff_target, 15).unwrap(); + let (proof, nonce) = pow::pow_size(&b, difficulty.clone(), prev.header.cuckoo_len as u32).unwrap(); b.header.pow = proof; b.header.nonce = nonce; - b.header.target = diff_target; + b.header.difficulty = difficulty; grin_chain::pipe::process_block(&b, arc_store.clone(), adapter.clone(), grin_chain::pipe::EASY_POW).unwrap(); // checking our new head diff --git a/core/Cargo.toml b/core/Cargo.toml index 0e862ee0b..2ece352d8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Ignotus Peverell "] [dependencies] byteorder = "^0.5" +num-bigint = "^0.1.35" rust-crypto = "^0.2" rand = "^0.3" time = "^0.1" diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 4803b4392..199631e23 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -21,7 +21,9 @@ use std::cmp; -use core::target::Target; +use bigint::{BigInt, Sign, BigUint}; + +use core::target::Difficulty; /// The block subsidy amount pub const REWARD: u64 = 1_000_000_000; @@ -55,47 +57,42 @@ pub const EASINESS: u32 = 50; /// remove 1/1024th of the target for each 10 seconds of deviation from the 30 /// seconds block time. Increases Cuckoo size shift by one when next_target /// reaches soft max. -pub fn next_target(ts: i64, prev_ts: i64, prev_target: Target, prev_cuckoo_sz: u8) -> (Target, u8) { +pub fn next_target(ts: i64, + prev_ts: i64, + prev_diff: Difficulty, + prev_cuckoo_sz: u8) + -> (Difficulty, u8) { + let one = BigInt::new(Sign::Plus, vec![1]); + let two = BigInt::new(Sign::Plus, vec![2]); + let ten = BigInt::new(Sign::Plus, vec![10]); + // increase the cuckoo size when the target gets lower than the soft min as // long as we're not at the max size already; target gets 2x to compensate for // increased next_target - let soft_min = SOFT_MIN_TARGET >> - (((prev_cuckoo_sz - cmp::min(DEFAULT_SIZESHIFT, prev_cuckoo_sz)) * 8) as usize); - let (ptarget, clen) = if prev_target < soft_min && prev_cuckoo_sz < MAX_SIZESHIFT { - (prev_target << 1, prev_cuckoo_sz + 1) + let soft_min = one.clone() << + (((prev_cuckoo_sz - cmp::min(DEFAULT_SIZESHIFT, prev_cuckoo_sz)) * + 8 + 16) as usize); + let prev_diff = BigInt::from_biguint(Sign::Plus, prev_diff.num); + let (pdiff, clen) = if prev_diff > soft_min && prev_cuckoo_sz < MAX_SIZESHIFT { + (prev_diff / two, prev_cuckoo_sz + 1) } else { - (prev_target, prev_cuckoo_sz) + (prev_diff, prev_cuckoo_sz) }; - // target is increased/decreased by multiples of 1/1024th of itself - let incdec = ptarget >> 10; - // signed deviation from desired value divided by ten and bounded in [-3, 3] - let delta = cmp::max(cmp::min((ts - prev_ts - (BLOCK_TIME_SEC as i64)) / 10, 3), - -3); - // increase or decrease the target based on the sign of delta by a shift of - // |delta|-1; keep as-is for delta of zero - let new_target = match delta { - 1...3 => ptarget + (incdec << ((delta - 1) as usize)), - -3...-1 => ptarget - (incdec << ((-delta - 1) as usize)), - _ => ptarget, - }; + // signed deviation from desired value divided by ten and bounded in [-6, 6] + let delta = cmp::max(cmp::min((ts - prev_ts - (BLOCK_TIME_SEC as i64)), 60), -60); + let delta_bigi = BigInt::new(if delta >= 0 { Sign::Plus } else { Sign::Minus }, + vec![delta.abs() as u32]); + let new_diff = pdiff.clone() - ((pdiff >> 10) + one.clone()) * delta_bigi / ten; - // cannot exceed the maximum target - if new_target > MAX_TARGET { - (MAX_TARGET, clen) + // cannot be lower than one + if new_diff < one { + (Difficulty::one(), clen) } else { - (new_target.truncate(), clen) + (Difficulty { num: new_diff.to_biguint().unwrap() }, clen) } } -/// Max target hash, lowest next_target -pub const MAX_TARGET: Target = Target([0xf, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - -/// Target limit under which we start increasing the size shift on Cuckoo cycle. -pub const SOFT_MIN_TARGET: Target = Target([0, 0, 0xf, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - /// Default number of blocks in the past when cross-block cut-through will start /// happening. Needs to be long enough to not overlap with a long reorg. /// Rational @@ -110,52 +107,46 @@ pub const MAX_MSG_LEN: u64 = 20_000_000; #[cfg(test)] mod test { + use core::target::Difficulty; + use super::*; #[test] /// Checks different next_target adjustments and difficulty boundaries fn next_target_adjustment() { // can't do lower than min - assert_eq!(next_target(60, 0, MAX_TARGET, 26), (MAX_TARGET, 26)); - assert_eq!(next_target(90, 30, MAX_TARGET, 26), (MAX_TARGET, 26)); - assert_eq!(next_target(60, 0, MAX_TARGET, 26), (MAX_TARGET, 26)); + assert_eq!(next_target(60, 0, Difficulty::one(), 26), + (Difficulty::one(), 26)); + assert_eq!(next_target(90, 30, Difficulty::one(), 26), + (Difficulty::one(), 26)); + assert_eq!(next_target(60, 0, Difficulty::one(), 26), + (Difficulty::one(), 26)); - // lower next_target if gap too short, even negative - assert_eq!(next_target(50, 0, MAX_TARGET, 26).0, - (MAX_TARGET - (MAX_TARGET >> 10)).truncate()); - assert_eq!(next_target(40, 0, MAX_TARGET, 26).0, - (MAX_TARGET - ((MAX_TARGET >> 10) << 1)).truncate()); - assert_eq!(next_target(0, 20, MAX_TARGET, 26).0, - (MAX_TARGET - ((MAX_TARGET >> 10) << 2)).truncate()); + // lower next_target if gap too short + assert_eq!(next_target(30, 0, Difficulty::one(), 26).0, + Difficulty::from_num(4)); + assert_eq!(next_target(50, 0, Difficulty::one(), 26).0, + Difficulty::from_num(2)); + assert_eq!(next_target(40, 0, Difficulty::from_num(1024 * 8), 26).0, + Difficulty::from_num(1024 * 8 + 18)); - // raise next_target if gap too wide - let lower_target = MAX_TARGET >> 8; - assert_eq!(next_target(70, 0, lower_target, 26).0, - (lower_target + (lower_target >> 10)).truncate()); - assert_eq!(next_target(80, 0, lower_target, 26).0, - (lower_target + ((lower_target >> 10) << 1)).truncate()); - assert_eq!(next_target(200, 0, lower_target, 26).0, - (lower_target + ((lower_target >> 10) << 2)).truncate()); + // lower difficulty if gap too wide + assert_eq!(next_target(70, 0, Difficulty::from_num(10), 26).0, + Difficulty::from_num(9)); + assert_eq!(next_target(90, 0, Difficulty::from_num(1024 * 8), 26).0, + Difficulty::from_num(1024 * 8 - 9 * 3)); - // close enough, no adjustment - assert_eq!(next_target(65, 0, lower_target, 26).0, lower_target); - assert_eq!(next_target(55, 0, lower_target, 26).0, lower_target); + // identical, no adjustment + assert_eq!(next_target(60, 0, Difficulty::from_num(1024 * 8), 26).0, + Difficulty::from_num(1024 * 8)); // increase cuckoo size if next_target goes above soft max, target is doubled, // up to 29 - assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 1, 25), - ((SOFT_MIN_TARGET >> 1) << 1, 26)); - assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 9, 26), - ((SOFT_MIN_TARGET >> 9) << 1, 27)); - assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 17, 27), - ((SOFT_MIN_TARGET >> 17) << 1, 28)); - assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 25, 28), - ((SOFT_MIN_TARGET >> 25) << 1, 29)); - assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 33, 29), - (SOFT_MIN_TARGET >> 33, 29)); - - // should only increase on the according previous size - assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 1, 26), - (SOFT_MIN_TARGET >> 1, 26)); + assert_eq!(next_target(60, 0, Difficulty::from_num(1 << 16), 25), + (Difficulty::from_num(1 << 16), 25)); + assert_eq!(next_target(60, 0, Difficulty::from_num((1 << 16) + 1), 25), + (Difficulty::from_num(1 << 15), 26)); + assert_eq!(next_target(60, 0, Difficulty::from_num((1 << 24) + 1), 26), + (Difficulty::from_num(1 << 23), 27)); } } diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 610235951..4f75af2d2 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -23,9 +23,9 @@ use std::collections::HashSet; use core::Committed; use core::{Input, Output, Proof, TxProof, Transaction}; use core::transaction::merkle_inputs_outputs; -use consensus::{REWARD, DEFAULT_SIZESHIFT, MAX_TARGET}; +use consensus::{REWARD, DEFAULT_SIZESHIFT}; use core::hash::{Hash, Hashed, ZERO_HASH}; -use core::target::Target; +use core::target::Difficulty; use ser::{self, Readable, Reader, Writeable, Writer}; /// Block header, fairly standard compared to other blockchains. @@ -38,14 +38,16 @@ pub struct BlockHeader { pub timestamp: time::Tm, /// Length of the cuckoo cycle used to mine this block. pub cuckoo_len: u8, - /// Difficulty used to mine the block. - pub target: Target, pub utxo_merkle: Hash, pub tx_merkle: Hash, /// Nonce increment used to mine this block. pub nonce: u64, /// Proof of work data. pub pow: Proof, + /// Difficulty used to mine the block. + pub difficulty: Difficulty, + /// Total accumulated difficulty since genesis block + pub total_difficulty: Difficulty, } impl Default for BlockHeader { @@ -55,7 +57,8 @@ impl Default for BlockHeader { previous: ZERO_HASH, timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }), cuckoo_len: 20, // only for tests - target: MAX_TARGET, + difficulty: Difficulty::one(), + total_difficulty: Difficulty::one(), utxo_merkle: ZERO_HASH, tx_merkle: ZERO_HASH, nonce: 0, @@ -72,7 +75,6 @@ impl Writeable for BlockHeader { [write_fixed_bytes, &self.previous], [write_i64, self.timestamp.to_timespec().sec], [write_u8, self.cuckoo_len]); - try!(self.target.write(writer)); ser_multiwrite!(writer, [write_fixed_bytes, &self.utxo_merkle], [write_fixed_bytes, &self.tx_merkle]); @@ -80,7 +82,10 @@ impl Writeable for BlockHeader { // avoid complicating PoW try!(writer.write_u64(self.nonce)); // proof - self.pow.write(writer) + try!(self.pow.write(writer)); + // block and total difficulty + try!(self.difficulty.write(writer)); + self.total_difficulty.write(writer) } } @@ -90,11 +95,12 @@ impl Readable for BlockHeader { let height = try!(reader.read_u64()); let previous = try!(Hash::read(reader)); let (timestamp, cuckoo_len) = ser_multiread!(reader, read_i64, read_u8); - let target = try!(Target::read(reader)); let utxo_merkle = try!(Hash::read(reader)); let tx_merkle = try!(Hash::read(reader)); let nonce = try!(reader.read_u64()); let pow = try!(Proof::read(reader)); + let difficulty = try!(Difficulty::read(reader)); + let total_difficulty = try!(Difficulty::read(reader)); Ok(BlockHeader { height: height, @@ -104,11 +110,12 @@ impl Readable for BlockHeader { nsec: 0, }), cuckoo_len: cuckoo_len, - target: target, utxo_merkle: utxo_merkle, tx_merkle: tx_merkle, pow: pow, nonce: nonce, + difficulty: difficulty, + total_difficulty: total_difficulty, }) } } @@ -243,6 +250,9 @@ impl Block { height: prev.height + 1, timestamp: time::now(), previous: prev.hash(), + total_difficulty: Difficulty::from_hash(&prev.hash()) + + prev.total_difficulty.clone(), + cuckoo_len: prev.cuckoo_len, ..Default::default() }, inputs: inputs, @@ -292,6 +302,8 @@ impl Block { header: BlockHeader { tx_merkle: tx_merkle, pow: self.header.pow.clone(), + difficulty: self.header.difficulty.clone(), + total_difficulty: self.header.total_difficulty.clone(), ..self.header }, inputs: new_inputs, @@ -317,7 +329,12 @@ impl Block { Block { // compact will fix the merkle tree - header: BlockHeader { pow: self.header.pow.clone(), ..self.header }, + header: BlockHeader { + pow: self.header.pow.clone(), + difficulty: self.header.difficulty.clone(), + total_difficulty: self.header.total_difficulty.clone(), + ..self.header + }, inputs: all_inputs, outputs: all_outputs, proofs: all_proofs, diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index e3244f714..2fb994b2f 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -131,8 +131,8 @@ impl Proof { /// Converts the proof to a proof-of-work Target so they can be compared. /// Hashes the Cuckoo Proof data. - pub fn to_target(self) -> target::Target { - target::Target(self.hash().0) + pub fn to_difficulty(self) -> target::Difficulty { + target::Difficulty::from_hash(&self.hash()) } } diff --git a/core/src/core/target.rs b/core/src/core/target.rs index 7dcb1bd77..31197e91b 100644 --- a/core/src/core/target.rs +++ b/core/src/core/target.rs @@ -12,292 +12,69 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Primary hash function used in the protocol -//! +//! Definition of the maximum target value a proof-of-work block hash can have +//! and +//! the related difficulty, defined as the maximum target divided by the hash. use byteorder::{ByteOrder, BigEndian}; -use std::fmt; -use std::ops::{Add, Sub, Shl, Shr, Index, IndexMut}; -use tiny_keccak::Keccak; +use std::ops::Add; +use bigint::BigUint; + +use core::hash::Hash; use ser::{self, Reader, Writer, Writeable, Readable}; -/// A Bitcoin-style target, implemented as a 32 bytes positive big number that -/// can be compared against a proof of work. Serializes in a compact format -/// composed of a one-byte exponent and a 4 bytes mantissa. Hence a target will -/// only ever have a u32 worth of significant numbers. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct Target(pub [u8; 32]); +/// The target is the 32-bytes hash block hashes must be lower than. +pub const MAX_TARGET: [u8; 32] = [0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; -impl fmt::Display for Target { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for i in self.0[..].iter().cloned() { - try!(write!(f, "{:02x}", i)); - } - Ok(()) +/// The difficulty is defined as the maximum target divided by the block hash. +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] +pub struct Difficulty { + pub num: BigUint, +} + +impl Difficulty { + /// Difficulty of one, which is the minumum difficulty (when the hash + /// equals the + /// max target) + pub fn one() -> Difficulty { + Difficulty { num: BigUint::new(vec![1]) } + } + + pub fn from_num(num: u32) -> Difficulty { + Difficulty { num: BigUint::new(vec![num]) } + } + + /// Computes the difficulty from a hash. Divides the maximum target by the + /// provided hash. + pub fn from_hash(h: &Hash) -> Difficulty { + let max_target = BigUint::from_bytes_be(&MAX_TARGET); + let h_num = BigUint::from_bytes_be(h.to_slice()); + Difficulty { num: max_target / h_num } } } -impl Writeable for Target { +impl Add for Difficulty { + type Output = Difficulty; + fn add(self, other: Difficulty) -> Difficulty { + Difficulty { num: self.num + other.num } + } +} + +impl Writeable for Difficulty { fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { - let (exp, mantissa) = self.split(); - try!(writer.write_u8(exp)); - writer.write_u32(mantissa) + let data = self.num.to_bytes_be(); + try!(writer.write_u8(data.len() as u8)); + writer.write_fixed_bytes(&data) } } -impl Readable for Target { - fn read(reader: &mut Reader) -> Result { - let (exp, mantissa) = ser_multiread!(reader, read_u8, read_u32); - Target::join(exp, mantissa) - } -} - -impl Target { - /// Takes a u32 mantissa, bringing it to the provided exponent to build a - /// new target. - fn join(exp: u8, mantissa: u32) -> Result { - if exp > 255 { - return Err(ser::Error::CorruptedData); - } - let mut t = [0; 32]; - t[31] = (mantissa & 0xff) as u8; - t[30] = (mantissa >> 8) as u8; - t[29] = (mantissa >> 16) as u8; - t[28] = (mantissa >> 24) as u8; - Ok(Target(t) << (exp as usize)) - } - - /// Splits the target into an exponent and a mantissa with most significant - /// numbers. The precision is one of a u32. - fn split(&self) -> (u8, u32) { - let mut exp = 0; - let mut mantissa = self.clone(); - let max_target = Target::join(32, 1).unwrap(); - while mantissa > max_target { - exp += 1; - mantissa = mantissa >> 1; - } - let mut res = mantissa[31] as u32; - res += (mantissa[30] as u32) << 8; - res += (mantissa[29] as u32) << 16; - res += (mantissa[28] as u32) << 24; - (exp, res) - } - - /// Truncates the target to its maximum precision. - pub fn truncate(&self) -> Target { - let (e, m) = self.split(); - Target::join(e, m).unwrap() - } -} - -impl Index for Target { - type Output = u8; - fn index(&self, idx: usize) -> &u8 { - &self.0[idx] - } -} - -impl IndexMut for Target { - fn index_mut(&mut self, idx: usize) -> &mut u8 { - &mut self.0[idx] - } -} - -/// Implements shift left to break the target down into an exponent and a -/// mantissa and provide simple multiplication by a power of 2. -impl Shl for Target { - type Output = Target; - - fn shl(self, shift: usize) -> Target { - let Target(ref t) = self; - let mut ret = [0; 32]; - let byte_shift = shift / 8; - let bit_shift = shift % 8; - - // shift - for i in byte_shift..32 { - ret[i - byte_shift] = t[i] << bit_shift; - } - // carry - if bit_shift > 0 { - for i in byte_shift + 1..32 { - let s = t[i] >> (8 - bit_shift); - ret[i - 1 - byte_shift] += s; - } - } - Target(ret) - } -} - -/// Implements shift right to build a target from an exponent and a mantissa -/// and provide simple division by a power of 2. -impl Shr for Target { - type Output = Target; - - fn shr(self, shift: usize) -> Target { - let Target(ref t) = self; - let mut ret = [0; 32]; - let byte_shift = shift / 8; - let bit_shift = shift % 8; - - // shift - for i in byte_shift..32 { - let (s, _) = t[i - byte_shift].overflowing_shr(bit_shift as u32); - ret[i] = s; - } - // Carry - if bit_shift > 0 { - for i in byte_shift + 1..32 { - ret[i] += t[i - byte_shift - 1] << (8 - bit_shift); - } - } - Target(ret) - } -} - -/// Implement addition between targets. Overflow is truncated. -impl Add for Target { - type Output = Target; - - fn add(self, other: Target) -> Target { - let mut sum = [0; 32]; - let mut carry = 0; - for i in (0..32).rev() { - let (sum_i, carry_i) = add_with_carryover(self[i], other[i], carry); - sum[i] = sum_i; - carry = carry_i; - } - Target(sum) - } -} - -fn add_with_carryover(a: u8, b: u8, carry: u8) -> (u8, u8) { - let mut new_carry = 0; - let (a_carry, over) = a.overflowing_add(carry); - if over { - new_carry += 1; - } - let (sum, over) = a_carry.overflowing_add(b); - if over { - new_carry += 1; - } - (sum, new_carry) -} - -/// Implement subtractions between targets. Underflow is truncated. -impl Sub for Target { - type Output = Target; - - fn sub(self, other: Target) -> Target { - let mut diff = [0; 32]; - let mut carry = 0; - for i in (0..32).rev() { - let (diff_i, carry_i) = sub_with_carryover(self[i], other[i], carry); - diff[i] = diff_i; - carry = carry_i; - } - Target(diff) - } -} - -fn sub_with_carryover(a: u8, b: u8, carry: u8) -> (u8, u8) { - let mut new_carry = 0; - let (a_carry, under) = a.overflowing_sub(carry); - if under { - new_carry += 1; - } - let (diff, under) = a_carry.overflowing_sub(b); - if under { - new_carry += 1; - } - (diff, new_carry) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn small_shift_left_right() { - let mut arr = [0; 32]; - arr[31] = 128; - let t = Target(arr); - let tsl = t << 2; - assert_eq!(tsl[31], 0); - assert_eq!(tsl[30], 2); - assert_eq!(tsl[29], 0); - let rsl = tsl >> 2; - assert_eq!(rsl[31], 128); - assert_eq!(rsl[30], 0); - assert_eq!(rsl[0], 0); - } - - #[test] - fn shift_byte_left_right() { - let mut arr = [0; 32]; - arr[31] = 64; - let t = Target(arr); - let tsl = t << 7 * 8 + 1; - assert_eq!(tsl[31], 0); - assert_eq!(tsl[24], 128); - let rsl = tsl >> 5 * 8 + 1; - assert_eq!(rsl[29], 64); - assert_eq!(rsl[24], 0); - assert_eq!(rsl[31], 0); - } - - #[test] - fn shift_truncate() { - assert_eq!((Target::join(0, 0xffff).unwrap() >> 8) << 8, - Target::join(0, 0xff00).unwrap()); - } - - #[test] - fn split_fit() { - let t = Target::join(10 * 8, ::std::u32::MAX).unwrap(); - let (exp, mant) = t.split(); - assert_eq!(exp, 10 * 8); - assert_eq!(mant, ::std::u32::MAX); - } - - #[test] - fn split_nofit() { - let mut t = Target::join(10 * 8, 255).unwrap(); - t[0] = 10; - t[25] = 17; - let (exp, mant) = t.split(); - assert_eq!(exp, 220); - assert_eq!(mant, 10 << 28); - } - - #[test] - fn addition() { - assert_eq!(Target::join(0, 10).unwrap() + Target::join(0, 20).unwrap(), - Target::join(0, 30).unwrap()); - // single overflow - assert_eq!(Target::join(0, 250).unwrap() + Target::join(0, 250).unwrap(), - Target::join(0, 500).unwrap()); - // multiple overflows - assert_eq!(Target::join(0, 300).unwrap() + Target::join(0, 300).unwrap(), - Target::join(0, 600).unwrap()); - assert_eq!(Target::join(10, 300).unwrap() + Target::join(10, 300).unwrap(), - Target::join(10, 600).unwrap()); - // cascading overflows - assert_eq!(Target::join(8, 0xffff).unwrap() + Target::join(8, 0xffff).unwrap(), - Target::join(8, 0x1fffe).unwrap()); - } - - #[test] - fn subtraction() { - assert_eq!(Target::join(0, 40).unwrap() - Target::join(0, 10).unwrap(), - Target::join(0, 30).unwrap()); - assert_eq!(Target::join(0, 300).unwrap() - Target::join(0, 100).unwrap(), - Target::join(0, 200).unwrap()); - assert_eq!(Target::join(0, 0xffff).unwrap() - Target::join(0, 0xffff).unwrap(), - Target::join(0, 0).unwrap()); - assert_eq!(Target::join(0, 0xffff).unwrap() - Target::join(0, 0xff01).unwrap(), - Target::join(0, 0xfe).unwrap()); +impl Readable for Difficulty { + fn read(reader: &mut Reader) -> Result { + let dlen = try!(reader.read_u8()); + let data = try!(reader.read_fixed_bytes(dlen as usize)); + Ok(Difficulty { num: BigUint::from_bytes_be(&data[..]) }) } } diff --git a/core/src/genesis.rs b/core/src/genesis.rs index a43399d6d..e62016c51 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -17,8 +17,9 @@ use time; use core; -use consensus::{DEFAULT_SIZESHIFT, MAX_TARGET}; +use consensus::DEFAULT_SIZESHIFT; use core::hash::Hashed; +use core::target::Difficulty; // Genesis block definition. It has no rewards, no inputs, no outputs, no // fees and a height of zero. @@ -34,7 +35,8 @@ pub fn genesis() -> core::Block { ..time::empty_tm() }, cuckoo_len: DEFAULT_SIZESHIFT, - target: MAX_TARGET, + difficulty: Difficulty::one(), + total_difficulty: Difficulty::one(), utxo_merkle: [].hash(), tx_merkle: [].hash(), nonce: 0, diff --git a/core/src/lib.rs b/core/src/lib.rs index ae4ccdaf5..a7f2f3307 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -23,6 +23,7 @@ extern crate byteorder; extern crate crypto; +extern crate num_bigint as bigint; extern crate rand; extern crate secp256k1zkp as secp; extern crate time; diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 5867b064d..5817e71a3 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -30,7 +30,7 @@ use time; use consensus::EASINESS; use core::{Block, Proof}; use core::hash::{Hash, Hashed}; -use core::target::Target; +use core::target::Difficulty; use pow::cuckoo::{Cuckoo, Miner, Error}; use ser; @@ -94,9 +94,9 @@ pub fn verify(b: &Block) -> bool { pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool { let hash = PowHeader::from_block(b).hash(); - // make sure the hash is smaller than our target before going into more - // expensive validation - if b.header.target < b.header.pow.to_target() { + // make sure the pow hash shows a difficulty at least as large as the target + // difficulty + if b.header.difficulty > b.header.pow.to_difficulty() { return false; } Cuckoo::new(hash.to_slice(), cuckoo_sz).verify(b.header.pow, EASINESS as u64) @@ -105,17 +105,17 @@ pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool { /// Runs a naive single-threaded proof of work computation over the provided /// block, until the required difficulty target is reached. May take a /// while for a low target... -pub fn pow(b: &Block, target: Target) -> Result<(Proof, u64), Error> { - pow_size(b, target, b.header.cuckoo_len as u32) +pub fn pow(b: &Block, diff: Difficulty) -> Result<(Proof, u64), Error> { + pow_size(b, diff, b.header.cuckoo_len as u32) } /// Same as default pow function but uses the much easier Cuckoo20 (mostly for /// tests). -pub fn pow20(b: &Block, target: Target) -> Result<(Proof, u64), Error> { - pow_size(b, target, 20) +pub fn pow20(b: &Block, diff: Difficulty) -> Result<(Proof, u64), Error> { + pow_size(b, diff, 20) } -pub fn pow_size(b: &Block, target: Target, sizeshift: u32) -> Result<(Proof, u64), Error> { +pub fn pow_size(b: &Block, diff: Difficulty, sizeshift: u32) -> Result<(Proof, u64), Error> { let mut pow_header = PowHeader::from_block(b); let start_nonce = pow_header.nonce; @@ -125,10 +125,10 @@ pub fn pow_size(b: &Block, target: Target, sizeshift: u32) -> Result<(Proof, u64 // is not meant as a fast miner implementation let pow_hash = pow_header.hash(); - // if we found a cycle (not guaranteed) and the proof is lower that the target, - // we're all good + // if we found a cycle (not guaranteed) and the proof hash is higher that the + // diff, we're all good if let Ok(proof) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() { - if proof.to_target() <= target { + if proof.to_difficulty() >= diff { return Ok((proof, pow_header.nonce)); } } @@ -147,16 +147,16 @@ pub fn pow_size(b: &Block, target: Target, sizeshift: u32) -> Result<(Proof, u64 #[cfg(test)] mod test { use super::*; - use consensus::MAX_TARGET; use core::Proof; + use core::target::Difficulty; use genesis; #[test] fn genesis_pow() { let mut b = genesis::genesis(); - let (proof, nonce) = pow20(&b, MAX_TARGET).unwrap(); + let (proof, nonce) = pow20(&b, Difficulty::one()).unwrap(); assert!(nonce > 0); - assert!(proof.to_target() < MAX_TARGET); + assert!(proof.to_difficulty() >= Difficulty::one()); b.header.pow = proof; b.header.nonce = nonce; b.header.cuckoo_len = 20; diff --git a/grin/src/miner.rs b/grin/src/miner.rs index 60ada2386..30840f213 100644 --- a/grin/src/miner.rs +++ b/grin/src/miner.rs @@ -17,13 +17,13 @@ use rand::{self, Rng}; use std::sync::{Arc, Mutex}; -use std::ops::Deref; use time; use adapters::ChainToNetAdapter; use core::consensus; use core::core; use core::core::hash::{Hash, Hashed}; +use core::core::target::Difficulty; use core::pow; use core::pow::cuckoo; use chain; @@ -79,7 +79,7 @@ impl Miner { consensus::EASINESS, b.header.cuckoo_len as u32); if let Ok(proof) = miner.mine() { - if proof.to_target() <= b.header.target { + if proof.to_difficulty() >= b.header.difficulty { sol = Some(proof); break; } @@ -122,8 +122,8 @@ impl Miner { if now_sec == head_sec { now_sec += 1; } - let (target, cuckoo_len) = - consensus::next_target(now_sec, head_sec, head.target, head.cuckoo_len); + let (difficulty, cuckoo_len) = + consensus::next_target(now_sec, head_sec, head.difficulty.clone(), head.cuckoo_len); let mut rng = rand::OsRng::new().unwrap(); let secp_inst = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); @@ -133,8 +133,8 @@ impl Miner { // TODO populate inputs and outputs from pool transactions let mut b = core::Block::new(head, vec![], skey).unwrap(); b.header.nonce = rng.gen(); - b.header.target = target; b.header.cuckoo_len = cuckoo_len; + b.header.difficulty = difficulty; b.header.timestamp = time::at(time::Timespec::new(now_sec, 0)); b }