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 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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,6 +5,7 @@ authors = ["Ignotus Peverell <igno.peverell@protonmail.com>"]
|
|||
|
||||
[dependencies]
|
||||
byteorder = "^0.5"
|
||||
num-bigint = "^0.1.35"
|
||||
rust-crypto = "^0.2"
|
||||
rand = "^0.3"
|
||||
time = "^0.1"
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BlockHeader> 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<BlockHeader> 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,
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<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> {
|
||||
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<Target> for Target {
|
||||
fn read(reader: &mut Reader) -> Result<Target, ser::Error> {
|
||||
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<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());
|
||||
impl Readable<Difficulty> for Difficulty {
|
||||
fn read(reader: &mut Reader) -> Result<Difficulty, ser::Error> {
|
||||
let dlen = try!(reader.read_u8());
|
||||
let data = try!(reader.read_fixed_bytes(dlen as usize));
|
||||
Ok(Difficulty { num: BigUint::from_bytes_be(&data[..]) })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue