Apply new difficulty algo, remove configurable cuckoo size
Integrate the new difficulty calculation into the block chain validation, the miner and tests. As the difficulty calculation doesn't use varying Cuckoo sizes anymore and we narrowed down reasonable final Cuckoo Cycle parameters, removed all Cuckoo Cycle sizes from block headers. Formalized easier Cuckoo Cycle sizes for testing (and possibly testnet) by introducing a test mode in configuration. Updated all tests.
This commit is contained in:
+25
-53
@@ -20,6 +20,7 @@
|
||||
//! here.
|
||||
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
|
||||
use bigint::{BigInt, Sign};
|
||||
|
||||
@@ -37,17 +38,11 @@ pub const BLOCK_TIME_SEC: i64 = 60;
|
||||
/// Cuckoo-cycle proof size (cycle length)
|
||||
pub const PROOFSIZE: usize = 42;
|
||||
|
||||
/// Origin Cuckoo Cycle size shift used by the genesis block.
|
||||
pub const DEFAULT_SIZESHIFT: u8 = 25;
|
||||
/// Default Cuckoo Cycle size shift used for mining and validating.
|
||||
pub const DEFAULT_SIZESHIFT: u8 = 30;
|
||||
|
||||
/// Maximum Cuckoo Cycle size shift we'll ever use. We adopt a schedule that
|
||||
/// progressively increases the size as the target becomes lower.
|
||||
/// Start => 25
|
||||
/// MAX_TARGET >> 12 => 26
|
||||
/// MAX_TARGET >> 20 => 27
|
||||
/// MAX_TARGET >> 28 => 28
|
||||
/// MAX_TARGET >> 36 => 29
|
||||
pub const MAX_SIZESHIFT: u8 = 29;
|
||||
/// Lower Cuckoo size shift for tests and testnet
|
||||
pub const TEST_SIZESHIFT: u8 = 12;
|
||||
|
||||
/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
|
||||
/// a solution.
|
||||
@@ -75,11 +70,28 @@ pub const UPPER_TIME_BOUND: i64 = BLOCK_TIME_WINDOW * 4 / 3;
|
||||
|
||||
pub const LOWER_TIME_BOUND: i64 = BLOCK_TIME_WINDOW * 5 / 6;
|
||||
|
||||
/// Error when computing the next difficulty adjustment.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TargetError {
|
||||
err: String,
|
||||
pub struct TargetError(pub String);
|
||||
|
||||
impl fmt::Display for TargetError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Error computing new difficulty: {}", self.0)
|
||||
}
|
||||
}
|
||||
pub fn next_target2<T>(cursor: T) -> Result<Difficulty, TargetError>
|
||||
|
||||
/// Computes the proof-of-work difficulty that the next block should comply
|
||||
/// with. Takes an iterator over past blocks, from latest (highest height) to
|
||||
/// oldest (lowest height). The iterator produces pairs of timestamp and
|
||||
/// difficulty for each block.
|
||||
///
|
||||
/// The difficulty calculation is based on both Digishield and GravityWave
|
||||
/// family of difficulty computation, coming to something very close to Zcash.
|
||||
/// The refence difficulty is an average of the difficulty over a window of
|
||||
/// 23 blocks. The corresponding timespan is calculated by using the
|
||||
/// difference between the median timestamps at the beginning and the end
|
||||
/// of the window.
|
||||
pub fn next_difficulty<T>(cursor: T) -> Result<Difficulty, TargetError>
|
||||
where T: IntoIterator<Item = Result<(i64, Difficulty), TargetError>>
|
||||
{
|
||||
|
||||
@@ -141,46 +153,6 @@ pub fn next_target2<T>(cursor: T) -> Result<Difficulty, TargetError>
|
||||
Difficulty::from_num(adj_ts as u32))
|
||||
}
|
||||
|
||||
/// Difficulty adjustment somewhat inspired by Ethereum's. Tuned to add or
|
||||
/// 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_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 = 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.into_biguint());
|
||||
let (pdiff, clen) = if prev_diff > soft_min && prev_cuckoo_sz < MAX_SIZESHIFT {
|
||||
(prev_diff / two, prev_cuckoo_sz + 1)
|
||||
} else {
|
||||
(prev_diff, prev_cuckoo_sz)
|
||||
};
|
||||
|
||||
// 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 be lower than one
|
||||
if new_diff < one {
|
||||
(Difficulty::one(), clen)
|
||||
} else {
|
||||
(Difficulty::from_biguint(new_diff.to_biguint().unwrap()), clen)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::target::Difficulty;
|
||||
|
||||
@@ -43,8 +43,6 @@ pub struct BlockHeader {
|
||||
pub previous: Hash,
|
||||
/// Timestamp at which the block was built.
|
||||
pub timestamp: time::Tm,
|
||||
/// Length of the cuckoo cycle used to mine this block.
|
||||
pub cuckoo_len: u8,
|
||||
/// Merkle root of the UTXO set
|
||||
pub utxo_merkle: Hash,
|
||||
/// Merkle tree of hashes for all inputs, outputs and kernels in the block
|
||||
@@ -67,7 +65,6 @@ impl Default for BlockHeader {
|
||||
height: 0,
|
||||
previous: ZERO_HASH,
|
||||
timestamp: time::at_utc(time::Timespec { sec: 0, nsec: 0 }),
|
||||
cuckoo_len: 20, // only for tests
|
||||
difficulty: Difficulty::one(),
|
||||
total_difficulty: Difficulty::one(),
|
||||
utxo_merkle: ZERO_HASH,
|
||||
@@ -86,7 +83,6 @@ impl Writeable for BlockHeader {
|
||||
[write_u64, self.height],
|
||||
[write_fixed_bytes, &self.previous],
|
||||
[write_i64, self.timestamp.to_timespec().sec],
|
||||
[write_u8, self.cuckoo_len],
|
||||
[write_fixed_bytes, &self.utxo_merkle],
|
||||
[write_fixed_bytes, &self.tx_merkle],
|
||||
[write_u8, self.features.bits()]);
|
||||
@@ -107,7 +103,7 @@ impl Readable for BlockHeader {
|
||||
fn read(reader: &mut Reader) -> Result<BlockHeader, ser::Error> {
|
||||
let height = try!(reader.read_u64());
|
||||
let previous = try!(Hash::read(reader));
|
||||
let (timestamp, cuckoo_len) = ser_multiread!(reader, read_i64, read_u8);
|
||||
let timestamp = reader.read_i64()?;
|
||||
let utxo_merkle = try!(Hash::read(reader));
|
||||
let tx_merkle = try!(Hash::read(reader));
|
||||
let (features, nonce) = ser_multiread!(reader, read_u8, read_u64);
|
||||
@@ -122,7 +118,6 @@ impl Readable for BlockHeader {
|
||||
sec: timestamp,
|
||||
nsec: 0,
|
||||
}),
|
||||
cuckoo_len: cuckoo_len,
|
||||
utxo_merkle: utxo_merkle,
|
||||
tx_merkle: tx_merkle,
|
||||
features: BlockFeatures::from_bits(features).ok_or(ser::Error::CorruptedData)?,
|
||||
@@ -284,7 +279,6 @@ impl Block {
|
||||
timestamp: time::now(),
|
||||
previous: prev.hash(),
|
||||
total_difficulty: prev.pow.to_difficulty() + prev.total_difficulty.clone(),
|
||||
cuckoo_len: prev.cuckoo_len,
|
||||
..Default::default()
|
||||
},
|
||||
inputs: inputs,
|
||||
|
||||
@@ -34,7 +34,6 @@ pub fn genesis() -> core::Block {
|
||||
tm_mday: 4,
|
||||
..time::empty_tm()
|
||||
},
|
||||
cuckoo_len: DEFAULT_SIZESHIFT,
|
||||
difficulty: Difficulty::one(),
|
||||
total_difficulty: Difficulty::one(),
|
||||
utxo_merkle: [].hash(),
|
||||
|
||||
+4
-15
@@ -33,11 +33,6 @@ use core::hash::Hashed;
|
||||
use core::target::Difficulty;
|
||||
use pow::cuckoo::{Cuckoo, Miner, Error};
|
||||
|
||||
/// Validates the proof of work of a given header.
|
||||
pub fn verify(bh: &BlockHeader) -> bool {
|
||||
verify_size(bh, bh.cuckoo_len as u32)
|
||||
}
|
||||
|
||||
/// Validates the proof of work of a given header, and that the proof of work
|
||||
/// satisfies the requirements of the header.
|
||||
pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u32) -> bool {
|
||||
@@ -49,21 +44,15 @@ pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u32) -> bool {
|
||||
Cuckoo::new(&bh.hash()[..], cuckoo_sz).verify(bh.pow, EASINESS as u64)
|
||||
}
|
||||
|
||||
/// 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(bh: &mut BlockHeader, diff: Difficulty) -> Result<(), Error> {
|
||||
let cuckoo_len = bh.cuckoo_len as u32;
|
||||
pow_size(bh, diff, cuckoo_len)
|
||||
}
|
||||
|
||||
/// Same as default pow function but uses the much easier Cuckoo20 (mostly for
|
||||
/// Uses the much easier Cuckoo20 (mostly for
|
||||
/// tests).
|
||||
pub fn pow20(bh: &mut BlockHeader, diff: Difficulty) -> Result<(), Error> {
|
||||
pow_size(bh, diff, 20)
|
||||
}
|
||||
|
||||
/// Actual pow function, takes an arbitrary pow size as input
|
||||
/// 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_size(bh: &mut BlockHeader, diff: Difficulty, sizeshift: u32) -> Result<(), Error> {
|
||||
let start_nonce = bh.nonce;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user