mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
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.
This commit is contained in:
parent
0cc786a1e5
commit
ce23dda6cb
11 changed files with 191 additions and 389 deletions
|
@ -20,7 +20,8 @@ use secp;
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
use core::consensus;
|
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::core::{BlockHeader, Block, Proof};
|
||||||
use core::pow;
|
use core::pow;
|
||||||
use types;
|
use types;
|
||||||
|
@ -50,8 +51,10 @@ pub struct BlockContext {
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// The block doesn't fit anywhere in our chain
|
/// The block doesn't fit anywhere in our chain
|
||||||
Unfit(String),
|
Unfit(String),
|
||||||
/// Target is too high either compared to ours or the block PoW hash
|
/// Difficulty is too low either compared to ours or the block PoW hash
|
||||||
TargetTooHigh,
|
DifficultyTooLow,
|
||||||
|
/// Addition of difficulties on all previous block is wrong
|
||||||
|
WrongTotalDifficulty,
|
||||||
/// Size of the Cuckoo graph in block header doesn't match PoW requirements
|
/// Size of the Cuckoo graph in block header doesn't match PoW requirements
|
||||||
WrongCuckooSize,
|
WrongCuckooSize,
|
||||||
/// The proof of work is invalid
|
/// The proof of work is invalid
|
||||||
|
@ -137,20 +140,25 @@ fn validate_header(b: &Block, ctx: &mut BlockContext) -> Result<(), Error> {
|
||||||
return Err(Error::InvalidBlockTime);
|
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
|
// verify the proof of work and related parameters
|
||||||
let (diff_target, cuckoo_sz) = consensus::next_target(header.timestamp.to_timespec().sec,
|
let (difficulty, cuckoo_sz) = consensus::next_target(header.timestamp.to_timespec().sec,
|
||||||
prev.timestamp.to_timespec().sec,
|
prev.timestamp.to_timespec().sec,
|
||||||
prev.target,
|
prev.difficulty,
|
||||||
prev.cuckoo_len);
|
prev.cuckoo_len);
|
||||||
if header.target > diff_target {
|
if header.difficulty < difficulty {
|
||||||
return Err(Error::TargetTooHigh);
|
return Err(Error::DifficultyTooLow);
|
||||||
}
|
}
|
||||||
if header.cuckoo_len != cuckoo_sz && !ctx.opts.intersects(EASY_POW) {
|
if header.cuckoo_len != cuckoo_sz && !ctx.opts.intersects(EASY_POW) {
|
||||||
return Err(Error::WrongCuckooSize);
|
return Err(Error::WrongCuckooSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.opts.intersects(EASY_POW) {
|
if ctx.opts.intersects(EASY_POW) {
|
||||||
if !pow::verify_size(b, 15) {
|
if !pow::verify_size(b, 16) {
|
||||||
return Err(Error::InvalidPow);
|
return Err(Error::InvalidPow);
|
||||||
}
|
}
|
||||||
} else if !pow::verify(b) {
|
} else if !pow::verify(b) {
|
||||||
|
|
|
@ -15,12 +15,15 @@
|
||||||
extern crate grin_core;
|
extern crate grin_core;
|
||||||
extern crate grin_chain;
|
extern crate grin_chain;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
extern crate time;
|
||||||
extern crate secp256k1zkp as secp;
|
extern crate secp256k1zkp as secp;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use rand::os::OsRng;
|
use rand::os::OsRng;
|
||||||
|
|
||||||
use grin_chain::types::*;
|
use grin_chain::types::*;
|
||||||
|
use grin_core::core::hash::Hashed;
|
||||||
|
use grin_core::core::target::Difficulty;
|
||||||
use grin_core::pow;
|
use grin_core::pow;
|
||||||
use grin_core::core;
|
use grin_core::core;
|
||||||
use grin_core::consensus;
|
use grin_core::consensus;
|
||||||
|
@ -31,7 +34,8 @@ fn mine_empty_chain() {
|
||||||
let store = grin_chain::store::ChainKVStore::new(".grin".to_string()).unwrap();
|
let store = grin_chain::store::ChainKVStore::new(".grin".to_string()).unwrap();
|
||||||
|
|
||||||
// save a genesis block
|
// 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();
|
store.save_block(&gen).unwrap();
|
||||||
|
|
||||||
// setup a new head tip
|
// setup a new head tip
|
||||||
|
@ -47,16 +51,17 @@ fn mine_empty_chain() {
|
||||||
|
|
||||||
for n in 1..4 {
|
for n in 1..4 {
|
||||||
let mut b = core::Block::new(&prev.header, vec![], reward_key).unwrap();
|
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.timestamp.to_timespec().sec,
|
||||||
prev.header.target,
|
prev.header.difficulty.clone(),
|
||||||
prev.header.cuckoo_len);
|
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.pow = proof;
|
||||||
b.header.nonce = nonce;
|
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();
|
grin_chain::pipe::process_block(&b, arc_store.clone(), adapter.clone(), grin_chain::pipe::EASY_POW).unwrap();
|
||||||
|
|
||||||
// checking our new head
|
// checking our new head
|
||||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "^0.5"
|
byteorder = "^0.5"
|
||||||
|
num-bigint = "^0.1.35"
|
||||||
rust-crypto = "^0.2"
|
rust-crypto = "^0.2"
|
||||||
rand = "^0.3"
|
rand = "^0.3"
|
||||||
time = "^0.1"
|
time = "^0.1"
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use core::target::Target;
|
use bigint::{BigInt, Sign, BigUint};
|
||||||
|
|
||||||
|
use core::target::Difficulty;
|
||||||
|
|
||||||
/// The block subsidy amount
|
/// The block subsidy amount
|
||||||
pub const REWARD: u64 = 1_000_000_000;
|
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
|
/// 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
|
/// seconds block time. Increases Cuckoo size shift by one when next_target
|
||||||
/// reaches soft max.
|
/// 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
|
// 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
|
// long as we're not at the max size already; target gets 2x to compensate for
|
||||||
// increased next_target
|
// increased next_target
|
||||||
let soft_min = SOFT_MIN_TARGET >>
|
let soft_min = one.clone() <<
|
||||||
(((prev_cuckoo_sz - cmp::min(DEFAULT_SIZESHIFT, prev_cuckoo_sz)) * 8) as usize);
|
(((prev_cuckoo_sz - cmp::min(DEFAULT_SIZESHIFT, prev_cuckoo_sz)) *
|
||||||
let (ptarget, clen) = if prev_target < soft_min && prev_cuckoo_sz < MAX_SIZESHIFT {
|
8 + 16) as usize);
|
||||||
(prev_target << 1, prev_cuckoo_sz + 1)
|
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 {
|
} else {
|
||||||
(prev_target, prev_cuckoo_sz)
|
(prev_diff, prev_cuckoo_sz)
|
||||||
};
|
};
|
||||||
|
|
||||||
// target is increased/decreased by multiples of 1/1024th of itself
|
// signed deviation from desired value divided by ten and bounded in [-6, 6]
|
||||||
let incdec = ptarget >> 10;
|
let delta = cmp::max(cmp::min((ts - prev_ts - (BLOCK_TIME_SEC as i64)), 60), -60);
|
||||||
// signed deviation from desired value divided by ten and bounded in [-3, 3]
|
let delta_bigi = BigInt::new(if delta >= 0 { Sign::Plus } else { Sign::Minus },
|
||||||
let delta = cmp::max(cmp::min((ts - prev_ts - (BLOCK_TIME_SEC as i64)) / 10, 3),
|
vec![delta.abs() as u32]);
|
||||||
-3);
|
let new_diff = pdiff.clone() - ((pdiff >> 10) + one.clone()) * delta_bigi / ten;
|
||||||
// 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// cannot exceed the maximum target
|
// cannot be lower than one
|
||||||
if new_target > MAX_TARGET {
|
if new_diff < one {
|
||||||
(MAX_TARGET, clen)
|
(Difficulty::one(), clen)
|
||||||
} else {
|
} 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
|
/// 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.
|
/// happening. Needs to be long enough to not overlap with a long reorg.
|
||||||
/// Rational
|
/// Rational
|
||||||
|
@ -110,52 +107,46 @@ pub const MAX_MSG_LEN: u64 = 20_000_000;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use core::target::Difficulty;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// Checks different next_target adjustments and difficulty boundaries
|
/// Checks different next_target adjustments and difficulty boundaries
|
||||||
fn next_target_adjustment() {
|
fn next_target_adjustment() {
|
||||||
// can't do lower than min
|
// can't do lower than min
|
||||||
assert_eq!(next_target(60, 0, MAX_TARGET, 26), (MAX_TARGET, 26));
|
assert_eq!(next_target(60, 0, Difficulty::one(), 26),
|
||||||
assert_eq!(next_target(90, 30, MAX_TARGET, 26), (MAX_TARGET, 26));
|
(Difficulty::one(), 26));
|
||||||
assert_eq!(next_target(60, 0, MAX_TARGET, 26), (MAX_TARGET, 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
|
// lower next_target if gap too short
|
||||||
assert_eq!(next_target(50, 0, MAX_TARGET, 26).0,
|
assert_eq!(next_target(30, 0, Difficulty::one(), 26).0,
|
||||||
(MAX_TARGET - (MAX_TARGET >> 10)).truncate());
|
Difficulty::from_num(4));
|
||||||
assert_eq!(next_target(40, 0, MAX_TARGET, 26).0,
|
assert_eq!(next_target(50, 0, Difficulty::one(), 26).0,
|
||||||
(MAX_TARGET - ((MAX_TARGET >> 10) << 1)).truncate());
|
Difficulty::from_num(2));
|
||||||
assert_eq!(next_target(0, 20, MAX_TARGET, 26).0,
|
assert_eq!(next_target(40, 0, Difficulty::from_num(1024 * 8), 26).0,
|
||||||
(MAX_TARGET - ((MAX_TARGET >> 10) << 2)).truncate());
|
Difficulty::from_num(1024 * 8 + 18));
|
||||||
|
|
||||||
// raise next_target if gap too wide
|
// lower difficulty if gap too wide
|
||||||
let lower_target = MAX_TARGET >> 8;
|
assert_eq!(next_target(70, 0, Difficulty::from_num(10), 26).0,
|
||||||
assert_eq!(next_target(70, 0, lower_target, 26).0,
|
Difficulty::from_num(9));
|
||||||
(lower_target + (lower_target >> 10)).truncate());
|
assert_eq!(next_target(90, 0, Difficulty::from_num(1024 * 8), 26).0,
|
||||||
assert_eq!(next_target(80, 0, lower_target, 26).0,
|
Difficulty::from_num(1024 * 8 - 9 * 3));
|
||||||
(lower_target + ((lower_target >> 10) << 1)).truncate());
|
|
||||||
assert_eq!(next_target(200, 0, lower_target, 26).0,
|
|
||||||
(lower_target + ((lower_target >> 10) << 2)).truncate());
|
|
||||||
|
|
||||||
// close enough, no adjustment
|
// identical, no adjustment
|
||||||
assert_eq!(next_target(65, 0, lower_target, 26).0, lower_target);
|
assert_eq!(next_target(60, 0, Difficulty::from_num(1024 * 8), 26).0,
|
||||||
assert_eq!(next_target(55, 0, lower_target, 26).0, lower_target);
|
Difficulty::from_num(1024 * 8));
|
||||||
|
|
||||||
// increase cuckoo size if next_target goes above soft max, target is doubled,
|
// increase cuckoo size if next_target goes above soft max, target is doubled,
|
||||||
// up to 29
|
// up to 29
|
||||||
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 1, 25),
|
assert_eq!(next_target(60, 0, Difficulty::from_num(1 << 16), 25),
|
||||||
((SOFT_MIN_TARGET >> 1) << 1, 26));
|
(Difficulty::from_num(1 << 16), 25));
|
||||||
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 9, 26),
|
assert_eq!(next_target(60, 0, Difficulty::from_num((1 << 16) + 1), 25),
|
||||||
((SOFT_MIN_TARGET >> 9) << 1, 27));
|
(Difficulty::from_num(1 << 15), 26));
|
||||||
assert_eq!(next_target(60, 0, SOFT_MIN_TARGET >> 17, 27),
|
assert_eq!(next_target(60, 0, Difficulty::from_num((1 << 24) + 1), 26),
|
||||||
((SOFT_MIN_TARGET >> 17) << 1, 28));
|
(Difficulty::from_num(1 << 23), 27));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ use std::collections::HashSet;
|
||||||
use core::Committed;
|
use core::Committed;
|
||||||
use core::{Input, Output, Proof, TxProof, Transaction};
|
use core::{Input, Output, Proof, TxProof, Transaction};
|
||||||
use core::transaction::merkle_inputs_outputs;
|
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::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
use core::target::Target;
|
use core::target::Difficulty;
|
||||||
use ser::{self, Readable, Reader, Writeable, Writer};
|
use ser::{self, Readable, Reader, Writeable, Writer};
|
||||||
|
|
||||||
/// Block header, fairly standard compared to other blockchains.
|
/// Block header, fairly standard compared to other blockchains.
|
||||||
|
@ -38,14 +38,16 @@ pub struct BlockHeader {
|
||||||
pub timestamp: time::Tm,
|
pub timestamp: time::Tm,
|
||||||
/// Length of the cuckoo cycle used to mine this block.
|
/// Length of the cuckoo cycle used to mine this block.
|
||||||
pub cuckoo_len: u8,
|
pub cuckoo_len: u8,
|
||||||
/// Difficulty used to mine the block.
|
|
||||||
pub target: Target,
|
|
||||||
pub utxo_merkle: Hash,
|
pub utxo_merkle: Hash,
|
||||||
pub tx_merkle: Hash,
|
pub tx_merkle: Hash,
|
||||||
/// Nonce increment used to mine this block.
|
/// Nonce increment used to mine this block.
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
/// Proof of work data.
|
/// Proof of work data.
|
||||||
pub pow: Proof,
|
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 {
|
impl Default for BlockHeader {
|
||||||
|
@ -55,7 +57,8 @@ impl Default for BlockHeader {
|
||||||
previous: ZERO_HASH,
|
previous: ZERO_HASH,
|
||||||
timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }),
|
timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }),
|
||||||
cuckoo_len: 20, // only for tests
|
cuckoo_len: 20, // only for tests
|
||||||
target: MAX_TARGET,
|
difficulty: Difficulty::one(),
|
||||||
|
total_difficulty: Difficulty::one(),
|
||||||
utxo_merkle: ZERO_HASH,
|
utxo_merkle: ZERO_HASH,
|
||||||
tx_merkle: ZERO_HASH,
|
tx_merkle: ZERO_HASH,
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
@ -72,7 +75,6 @@ impl Writeable for BlockHeader {
|
||||||
[write_fixed_bytes, &self.previous],
|
[write_fixed_bytes, &self.previous],
|
||||||
[write_i64, self.timestamp.to_timespec().sec],
|
[write_i64, self.timestamp.to_timespec().sec],
|
||||||
[write_u8, self.cuckoo_len]);
|
[write_u8, self.cuckoo_len]);
|
||||||
try!(self.target.write(writer));
|
|
||||||
ser_multiwrite!(writer,
|
ser_multiwrite!(writer,
|
||||||
[write_fixed_bytes, &self.utxo_merkle],
|
[write_fixed_bytes, &self.utxo_merkle],
|
||||||
[write_fixed_bytes, &self.tx_merkle]);
|
[write_fixed_bytes, &self.tx_merkle]);
|
||||||
|
@ -80,7 +82,10 @@ impl Writeable for BlockHeader {
|
||||||
// avoid complicating PoW
|
// avoid complicating PoW
|
||||||
try!(writer.write_u64(self.nonce));
|
try!(writer.write_u64(self.nonce));
|
||||||
// proof
|
// 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<BlockHeader> for BlockHeader {
|
||||||
let height = try!(reader.read_u64());
|
let height = try!(reader.read_u64());
|
||||||
let previous = try!(Hash::read(reader));
|
let previous = try!(Hash::read(reader));
|
||||||
let (timestamp, cuckoo_len) = ser_multiread!(reader, read_i64, read_u8);
|
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 utxo_merkle = try!(Hash::read(reader));
|
||||||
let tx_merkle = try!(Hash::read(reader));
|
let tx_merkle = try!(Hash::read(reader));
|
||||||
let nonce = try!(reader.read_u64());
|
let nonce = try!(reader.read_u64());
|
||||||
let pow = try!(Proof::read(reader));
|
let pow = try!(Proof::read(reader));
|
||||||
|
let difficulty = try!(Difficulty::read(reader));
|
||||||
|
let total_difficulty = try!(Difficulty::read(reader));
|
||||||
|
|
||||||
Ok(BlockHeader {
|
Ok(BlockHeader {
|
||||||
height: height,
|
height: height,
|
||||||
|
@ -104,11 +110,12 @@ impl Readable<BlockHeader> for BlockHeader {
|
||||||
nsec: 0,
|
nsec: 0,
|
||||||
}),
|
}),
|
||||||
cuckoo_len: cuckoo_len,
|
cuckoo_len: cuckoo_len,
|
||||||
target: target,
|
|
||||||
utxo_merkle: utxo_merkle,
|
utxo_merkle: utxo_merkle,
|
||||||
tx_merkle: tx_merkle,
|
tx_merkle: tx_merkle,
|
||||||
pow: pow,
|
pow: pow,
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
|
difficulty: difficulty,
|
||||||
|
total_difficulty: total_difficulty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -243,6 +250,9 @@ impl Block {
|
||||||
height: prev.height + 1,
|
height: prev.height + 1,
|
||||||
timestamp: time::now(),
|
timestamp: time::now(),
|
||||||
previous: prev.hash(),
|
previous: prev.hash(),
|
||||||
|
total_difficulty: Difficulty::from_hash(&prev.hash()) +
|
||||||
|
prev.total_difficulty.clone(),
|
||||||
|
cuckoo_len: prev.cuckoo_len,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
|
@ -292,6 +302,8 @@ impl Block {
|
||||||
header: BlockHeader {
|
header: BlockHeader {
|
||||||
tx_merkle: tx_merkle,
|
tx_merkle: tx_merkle,
|
||||||
pow: self.header.pow.clone(),
|
pow: self.header.pow.clone(),
|
||||||
|
difficulty: self.header.difficulty.clone(),
|
||||||
|
total_difficulty: self.header.total_difficulty.clone(),
|
||||||
..self.header
|
..self.header
|
||||||
},
|
},
|
||||||
inputs: new_inputs,
|
inputs: new_inputs,
|
||||||
|
@ -317,7 +329,12 @@ impl Block {
|
||||||
|
|
||||||
Block {
|
Block {
|
||||||
// compact will fix the merkle tree
|
// 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,
|
inputs: all_inputs,
|
||||||
outputs: all_outputs,
|
outputs: all_outputs,
|
||||||
proofs: all_proofs,
|
proofs: all_proofs,
|
||||||
|
|
|
@ -131,8 +131,8 @@ impl Proof {
|
||||||
|
|
||||||
/// Converts the proof to a proof-of-work Target so they can be compared.
|
/// Converts the proof to a proof-of-work Target so they can be compared.
|
||||||
/// Hashes the Cuckoo Proof data.
|
/// Hashes the Cuckoo Proof data.
|
||||||
pub fn to_target(self) -> target::Target {
|
pub fn to_difficulty(self) -> target::Difficulty {
|
||||||
target::Target(self.hash().0)
|
target::Difficulty::from_hash(&self.hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,292 +12,69 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 byteorder::{ByteOrder, BigEndian};
|
||||||
use std::fmt;
|
use std::ops::Add;
|
||||||
use std::ops::{Add, Sub, Shl, Shr, Index, IndexMut};
|
|
||||||
use tiny_keccak::Keccak;
|
|
||||||
|
|
||||||
|
use bigint::BigUint;
|
||||||
|
|
||||||
|
use core::hash::Hash;
|
||||||
use ser::{self, Reader, Writer, Writeable, Readable};
|
use ser::{self, Reader, Writer, Writeable, Readable};
|
||||||
|
|
||||||
/// A Bitcoin-style target, implemented as a 32 bytes positive big number that
|
/// The target is the 32-bytes hash block hashes must be lower than.
|
||||||
/// can be compared against a proof of work. Serializes in a compact format
|
pub const MAX_TARGET: [u8; 32] = [0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/// composed of a one-byte exponent and a 4 bytes mantissa. Hence a target will
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
/// only ever have a u32 worth of significant numbers.
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
|
||||||
pub struct Target(pub [u8; 32]);
|
|
||||||
|
|
||||||
impl fmt::Display for Target {
|
/// The difficulty is defined as the maximum target divided by the block hash.
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
for i in self.0[..].iter().cloned() {
|
pub struct Difficulty {
|
||||||
try!(write!(f, "{:02x}", i));
|
pub num: BigUint,
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
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<Difficulty> 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> {
|
fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> {
|
||||||
let (exp, mantissa) = self.split();
|
let data = self.num.to_bytes_be();
|
||||||
try!(writer.write_u8(exp));
|
try!(writer.write_u8(data.len() as u8));
|
||||||
writer.write_u32(mantissa)
|
writer.write_fixed_bytes(&data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Readable<Target> for Target {
|
impl Readable<Difficulty> for Difficulty {
|
||||||
fn read(reader: &mut Reader) -> Result<Target, ser::Error> {
|
fn read(reader: &mut Reader) -> Result<Difficulty, ser::Error> {
|
||||||
let (exp, mantissa) = ser_multiread!(reader, read_u8, read_u32);
|
let dlen = try!(reader.read_u8());
|
||||||
Target::join(exp, mantissa)
|
let data = try!(reader.read_fixed_bytes(dlen as usize));
|
||||||
}
|
Ok(Difficulty { num: BigUint::from_bytes_be(&data[..]) })
|
||||||
}
|
|
||||||
|
|
||||||
impl Target {
|
|
||||||
/// Takes a u32 mantissa, bringing it to the provided exponent to build a
|
|
||||||
/// new target.
|
|
||||||
fn join(exp: u8, mantissa: u32) -> Result<Target, ser::Error> {
|
|
||||||
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<usize> for Target {
|
|
||||||
type Output = u8;
|
|
||||||
fn index(&self, idx: usize) -> &u8 {
|
|
||||||
&self.0[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<usize> 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<usize> 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<usize> 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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
use core;
|
use core;
|
||||||
use consensus::{DEFAULT_SIZESHIFT, MAX_TARGET};
|
use consensus::DEFAULT_SIZESHIFT;
|
||||||
use core::hash::Hashed;
|
use core::hash::Hashed;
|
||||||
|
use core::target::Difficulty;
|
||||||
|
|
||||||
// Genesis block definition. It has no rewards, no inputs, no outputs, no
|
// Genesis block definition. It has no rewards, no inputs, no outputs, no
|
||||||
// fees and a height of zero.
|
// fees and a height of zero.
|
||||||
|
@ -34,7 +35,8 @@ pub fn genesis() -> core::Block {
|
||||||
..time::empty_tm()
|
..time::empty_tm()
|
||||||
},
|
},
|
||||||
cuckoo_len: DEFAULT_SIZESHIFT,
|
cuckoo_len: DEFAULT_SIZESHIFT,
|
||||||
target: MAX_TARGET,
|
difficulty: Difficulty::one(),
|
||||||
|
total_difficulty: Difficulty::one(),
|
||||||
utxo_merkle: [].hash(),
|
utxo_merkle: [].hash(),
|
||||||
tx_merkle: [].hash(),
|
tx_merkle: [].hash(),
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate crypto;
|
extern crate crypto;
|
||||||
|
extern crate num_bigint as bigint;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate secp256k1zkp as secp;
|
extern crate secp256k1zkp as secp;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
|
@ -30,7 +30,7 @@ use time;
|
||||||
use consensus::EASINESS;
|
use consensus::EASINESS;
|
||||||
use core::{Block, Proof};
|
use core::{Block, Proof};
|
||||||
use core::hash::{Hash, Hashed};
|
use core::hash::{Hash, Hashed};
|
||||||
use core::target::Target;
|
use core::target::Difficulty;
|
||||||
use pow::cuckoo::{Cuckoo, Miner, Error};
|
use pow::cuckoo::{Cuckoo, Miner, Error};
|
||||||
|
|
||||||
use ser;
|
use ser;
|
||||||
|
@ -94,9 +94,9 @@ pub fn verify(b: &Block) -> bool {
|
||||||
|
|
||||||
pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool {
|
pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool {
|
||||||
let hash = PowHeader::from_block(b).hash();
|
let hash = PowHeader::from_block(b).hash();
|
||||||
// make sure the hash is smaller than our target before going into more
|
// make sure the pow hash shows a difficulty at least as large as the target
|
||||||
// expensive validation
|
// difficulty
|
||||||
if b.header.target < b.header.pow.to_target() {
|
if b.header.difficulty > b.header.pow.to_difficulty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Cuckoo::new(hash.to_slice(), cuckoo_sz).verify(b.header.pow, EASINESS as u64)
|
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
|
/// Runs a naive single-threaded proof of work computation over the provided
|
||||||
/// block, until the required difficulty target is reached. May take a
|
/// block, until the required difficulty target is reached. May take a
|
||||||
/// while for a low target...
|
/// while for a low target...
|
||||||
pub fn pow(b: &Block, target: Target) -> Result<(Proof, u64), Error> {
|
pub fn pow(b: &Block, diff: Difficulty) -> Result<(Proof, u64), Error> {
|
||||||
pow_size(b, target, b.header.cuckoo_len as u32)
|
pow_size(b, diff, b.header.cuckoo_len as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as default pow function but uses the much easier Cuckoo20 (mostly for
|
/// Same as default pow function but uses the much easier Cuckoo20 (mostly for
|
||||||
/// tests).
|
/// tests).
|
||||||
pub fn pow20(b: &Block, target: Target) -> Result<(Proof, u64), Error> {
|
pub fn pow20(b: &Block, diff: Difficulty) -> Result<(Proof, u64), Error> {
|
||||||
pow_size(b, target, 20)
|
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 mut pow_header = PowHeader::from_block(b);
|
||||||
let start_nonce = pow_header.nonce;
|
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
|
// is not meant as a fast miner implementation
|
||||||
let pow_hash = pow_header.hash();
|
let pow_hash = pow_header.hash();
|
||||||
|
|
||||||
// if we found a cycle (not guaranteed) and the proof is lower that the target,
|
// if we found a cycle (not guaranteed) and the proof hash is higher that the
|
||||||
// we're all good
|
// diff, we're all good
|
||||||
if let Ok(proof) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() {
|
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));
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use consensus::MAX_TARGET;
|
|
||||||
use core::Proof;
|
use core::Proof;
|
||||||
|
use core::target::Difficulty;
|
||||||
use genesis;
|
use genesis;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn genesis_pow() {
|
fn genesis_pow() {
|
||||||
let mut b = genesis::genesis();
|
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!(nonce > 0);
|
||||||
assert!(proof.to_target() < MAX_TARGET);
|
assert!(proof.to_difficulty() >= Difficulty::one());
|
||||||
b.header.pow = proof;
|
b.header.pow = proof;
|
||||||
b.header.nonce = nonce;
|
b.header.nonce = nonce;
|
||||||
b.header.cuckoo_len = 20;
|
b.header.cuckoo_len = 20;
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
|
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::ops::Deref;
|
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
use adapters::ChainToNetAdapter;
|
use adapters::ChainToNetAdapter;
|
||||||
use core::consensus;
|
use core::consensus;
|
||||||
use core::core;
|
use core::core;
|
||||||
use core::core::hash::{Hash, Hashed};
|
use core::core::hash::{Hash, Hashed};
|
||||||
|
use core::core::target::Difficulty;
|
||||||
use core::pow;
|
use core::pow;
|
||||||
use core::pow::cuckoo;
|
use core::pow::cuckoo;
|
||||||
use chain;
|
use chain;
|
||||||
|
@ -79,7 +79,7 @@ impl Miner {
|
||||||
consensus::EASINESS,
|
consensus::EASINESS,
|
||||||
b.header.cuckoo_len as u32);
|
b.header.cuckoo_len as u32);
|
||||||
if let Ok(proof) = miner.mine() {
|
if let Ok(proof) = miner.mine() {
|
||||||
if proof.to_target() <= b.header.target {
|
if proof.to_difficulty() >= b.header.difficulty {
|
||||||
sol = Some(proof);
|
sol = Some(proof);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -122,8 +122,8 @@ impl Miner {
|
||||||
if now_sec == head_sec {
|
if now_sec == head_sec {
|
||||||
now_sec += 1;
|
now_sec += 1;
|
||||||
}
|
}
|
||||||
let (target, cuckoo_len) =
|
let (difficulty, cuckoo_len) =
|
||||||
consensus::next_target(now_sec, head_sec, head.target, head.cuckoo_len);
|
consensus::next_target(now_sec, head_sec, head.difficulty.clone(), head.cuckoo_len);
|
||||||
|
|
||||||
let mut rng = rand::OsRng::new().unwrap();
|
let mut rng = rand::OsRng::new().unwrap();
|
||||||
let secp_inst = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
|
let secp_inst = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
|
||||||
|
@ -133,8 +133,8 @@ impl Miner {
|
||||||
// TODO populate inputs and outputs from pool transactions
|
// TODO populate inputs and outputs from pool transactions
|
||||||
let mut b = core::Block::new(head, vec![], skey).unwrap();
|
let mut b = core::Block::new(head, vec![], skey).unwrap();
|
||||||
b.header.nonce = rng.gen();
|
b.header.nonce = rng.gen();
|
||||||
b.header.target = target;
|
|
||||||
b.header.cuckoo_len = cuckoo_len;
|
b.header.cuckoo_len = cuckoo_len;
|
||||||
|
b.header.difficulty = difficulty;
|
||||||
b.header.timestamp = time::at(time::Timespec::new(now_sec, 0));
|
b.header.timestamp = time::at(time::Timespec::new(now_sec, 0));
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue