mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-08 12:21:09 +03:00
Block now uses the new Target type. Consensus next_target calculation adjusting every block based on deviation and increasing Cuckoo size gradually. Block time of a minute until we learn more from mining ecosystem, so enough PoW attempts can be made even on CPUs.
This commit is contained in:
parent
44545525f4
commit
3c5e2b2958
7 changed files with 201 additions and 58 deletions
|
@ -19,40 +19,146 @@
|
||||||
//! enough, consensus-relevant constants and short functions should be kept
|
//! enough, consensus-relevant constants and short functions should be kept
|
||||||
//! here.
|
//! here.
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use core::target::Target;
|
||||||
|
|
||||||
/// The block subsidy amount
|
/// The block subsidy amount
|
||||||
pub const REWARD: u64 = 1_000_000_000;
|
pub const REWARD: u64 = 1_000_000_000;
|
||||||
|
|
||||||
/// Block interval, in seconds, the network will tune its difficulty for. Note
|
/// Block interval, in seconds, the network will tune its next_target for. Note
|
||||||
/// that contrarily to bitcoin, we may reduce this value in the future as
|
/// that we may reduce this value in the future as we get more data on mining
|
||||||
/// networks improve and block propagation is optimized (adjusting the reward
|
/// with Cuckoo Cycle, networks improve and block propagation is optimized
|
||||||
/// accordingly).
|
/// (adjusting the reward accordingly).
|
||||||
pub const BLOCK_TIME_SEC: u8 = 30;
|
pub const BLOCK_TIME_SEC: u8 = 60;
|
||||||
|
|
||||||
/// Cuckoo-cycle proof size (cycle length)
|
/// Cuckoo-cycle proof size (cycle length)
|
||||||
pub const PROOFSIZE: usize = 42;
|
pub const PROOFSIZE: usize = 42;
|
||||||
|
|
||||||
/// Default Cuckoo Cycle size shift used is 28. We may decide to increase it
|
/// Origin Cuckoo Cycle size shift used by the genesis block.
|
||||||
/// when hashrate increases. May also start lower.
|
pub const DEFAULT_SIZESHIFT: u8 = 25;
|
||||||
pub const SIZESHIFT: u32 = 28;
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
|
/// Default Cuckoo Cycle easiness, high enough to have good likeliness to find
|
||||||
/// a solution.
|
/// a solution.
|
||||||
pub const EASINESS: u32 = 50;
|
pub const EASINESS: u32 = 50;
|
||||||
|
|
||||||
|
/// 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_target: Target, prev_cuckoo_sz: u8) -> (Target, u8) {
|
||||||
|
// 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 - DEFAULT_SIZESHIFT) * 8) as usize);
|
||||||
|
let (ptarget, clen) = if prev_target < soft_min && prev_cuckoo_sz < MAX_SIZESHIFT {
|
||||||
|
(prev_target << 1, prev_cuckoo_sz + 1)
|
||||||
|
} else {
|
||||||
|
(prev_target, 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
// cannot exceed the maximum target
|
||||||
|
if new_target > MAX_TARGET {
|
||||||
|
(MAX_TARGET, clen)
|
||||||
|
} else {
|
||||||
|
(new_target, clen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Max target hash, lowest next_target
|
||||||
|
pub const MAX_TARGET: Target = Target([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]);
|
||||||
|
|
||||||
|
/// Target limit under which we start increasing the size shift on Cuckoo cycle.
|
||||||
|
pub const SOFT_MIN_TARGET: Target = Target([0, 0, 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]);
|
||||||
|
|
||||||
/// 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. Rational
|
/// happening. Needs to be long enough to not overlap with a long reorg.
|
||||||
|
/// Rational
|
||||||
/// behind the value is the longest bitcoin fork was about 30 blocks, so 5h. We
|
/// behind the value is the longest bitcoin fork was about 30 blocks, so 5h. We
|
||||||
/// add an order of magnitude to be safe and round to 48h of blocks to make it
|
/// add an order of magnitude to be safe and round to 48h of blocks to make it
|
||||||
/// easier to reason about.
|
/// easier to reason about.
|
||||||
pub const CUT_THROUGH_HORIZON: u32 = 48 * 3600 / (BLOCK_TIME_SEC as u32);
|
pub const CUT_THROUGH_HORIZON: u32 = 48 * 3600 / (BLOCK_TIME_SEC as u32);
|
||||||
|
|
||||||
/// Max target hash, lowest difficulty
|
|
||||||
pub const MAX_TARGET: [u32; PROOFSIZE] =
|
|
||||||
[0xfff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
|
||||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
|
||||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
|
||||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff];
|
|
||||||
|
|
||||||
/// The maximum number of inputs or outputs a transaction may have
|
/// The maximum number of inputs or outputs a transaction may have
|
||||||
/// and be deserializable. Only for DoS protection.
|
/// and be deserializable. Only for DoS protection.
|
||||||
pub const MAX_IN_OUT_LEN: u64 = 50000;
|
pub const MAX_IN_OUT_LEN: u64 = 50000;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
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));
|
||||||
|
|
||||||
|
// lower next_target if gap too short, even negative
|
||||||
|
assert_eq!(next_target(50, 0, MAX_TARGET, 26).0,
|
||||||
|
MAX_TARGET - (MAX_TARGET >> 10));
|
||||||
|
assert_eq!(next_target(40, 0, MAX_TARGET, 26).0,
|
||||||
|
MAX_TARGET - ((MAX_TARGET >> 10) << 1));
|
||||||
|
assert_eq!(next_target(0, 20, MAX_TARGET, 26).0,
|
||||||
|
MAX_TARGET - ((MAX_TARGET >> 10) << 2));
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
assert_eq!(next_target(80, 0, lower_target, 26).0,
|
||||||
|
lower_target + ((lower_target >> 10) << 1));
|
||||||
|
assert_eq!(next_target(200, 0, lower_target, 26).0,
|
||||||
|
lower_target + ((lower_target >> 10) << 2));
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,19 +23,28 @@ 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::{PROOFSIZE, REWARD, MAX_IN_OUT_LEN};
|
use consensus::{PROOFSIZE, REWARD, DEFAULT_SIZESHIFT, MAX_IN_OUT_LEN, MAX_TARGET};
|
||||||
use core::hash::{Hash, Hashed, ZERO_HASH};
|
use core::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
|
use core::target::Target;
|
||||||
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.
|
||||||
pub struct BlockHeader {
|
pub struct BlockHeader {
|
||||||
|
/// Height of this block since the genesis block (height 0)
|
||||||
pub height: u64,
|
pub height: u64,
|
||||||
|
/// Hash of the block previous to this in the chain.
|
||||||
pub previous: Hash,
|
pub previous: Hash,
|
||||||
|
/// Timestamp at which the block was built.
|
||||||
pub timestamp: time::Tm,
|
pub timestamp: time::Tm,
|
||||||
pub td: u64, // total difficulty up to this block
|
/// 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 utxo_merkle: Hash,
|
||||||
pub tx_merkle: Hash,
|
pub tx_merkle: Hash,
|
||||||
|
/// Nonce increment used to mine this block.
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
|
/// Proof of work data.
|
||||||
pub pow: Proof,
|
pub pow: Proof,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +54,8 @@ impl Default for BlockHeader {
|
||||||
height: 0,
|
height: 0,
|
||||||
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 }),
|
||||||
td: 0,
|
cuckoo_len: 20, // only for tests
|
||||||
|
target: MAX_TARGET,
|
||||||
utxo_merkle: ZERO_HASH,
|
utxo_merkle: ZERO_HASH,
|
||||||
tx_merkle: ZERO_HASH,
|
tx_merkle: ZERO_HASH,
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
@ -62,6 +72,9 @@ impl Writeable for BlockHeader {
|
||||||
[write_u64, self.height],
|
[write_u64, self.height],
|
||||||
[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]);
|
||||||
|
try!(self.target.write(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]);
|
||||||
// make sure to not introduce any variable length data before the nonce to
|
// make sure to not introduce any variable length data before the nonce to
|
||||||
|
@ -71,7 +84,7 @@ impl Writeable for BlockHeader {
|
||||||
for n in 0..42 {
|
for n in 0..42 {
|
||||||
try!(writer.write_u32(self.pow.0[n]));
|
try!(writer.write_u32(self.pow.0[n]));
|
||||||
}
|
}
|
||||||
writer.write_u64(self.td)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,13 +127,11 @@ impl Writeable for Block {
|
||||||
/// from a binary stream.
|
/// from a binary stream.
|
||||||
impl Readable<Block> for Block {
|
impl Readable<Block> for Block {
|
||||||
fn read(reader: &mut Reader) -> Result<Block, ser::Error> {
|
fn read(reader: &mut Reader) -> Result<Block, ser::Error> {
|
||||||
let (height, previous, timestamp, utxo_merkle, tx_merkle, nonce) = ser_multiread!(reader,
|
let (height, previous, timestamp, cuckoo_len) =
|
||||||
read_u64,
|
ser_multiread!(reader, read_u64, read_32_bytes, read_i64, read_u8);
|
||||||
read_32_bytes,
|
let target = try!(Target::read(reader));
|
||||||
read_i64,
|
let (utxo_merkle, tx_merkle, nonce) =
|
||||||
read_32_bytes,
|
ser_multiread!(reader, read_32_bytes, read_32_bytes, read_u64);
|
||||||
read_32_bytes,
|
|
||||||
read_u64);
|
|
||||||
|
|
||||||
// cuckoo cycle of 42 nodes
|
// cuckoo cycle of 42 nodes
|
||||||
let mut pow = [0; PROOFSIZE];
|
let mut pow = [0; PROOFSIZE];
|
||||||
|
@ -148,7 +159,8 @@ impl Readable<Block> for Block {
|
||||||
sec: timestamp,
|
sec: timestamp,
|
||||||
nsec: 0,
|
nsec: 0,
|
||||||
}),
|
}),
|
||||||
td: td,
|
cuckoo_len: cuckoo_len,
|
||||||
|
target: target,
|
||||||
utxo_merkle: Hash::from_vec(utxo_merkle),
|
utxo_merkle: Hash::from_vec(utxo_merkle),
|
||||||
tx_merkle: Hash::from_vec(tx_merkle),
|
tx_merkle: Hash::from_vec(tx_merkle),
|
||||||
pow: Proof(pow),
|
pow: Proof(pow),
|
||||||
|
|
|
@ -23,6 +23,8 @@ use tiny_keccak::Keccak;
|
||||||
|
|
||||||
use ser::{self, AsFixedBytes};
|
use ser::{self, AsFixedBytes};
|
||||||
|
|
||||||
|
pub const ZERO_HASH: Hash = Hash([0; 32]);
|
||||||
|
|
||||||
/// A hash to uniquely (or close enough) identify one of the main blockchain
|
/// A hash to uniquely (or close enough) identify one of the main blockchain
|
||||||
/// constructs. Used pervasively for blocks, transactions and ouputs.
|
/// constructs. Used pervasively for blocks, transactions and ouputs.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
@ -56,15 +58,13 @@ impl Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ZERO_HASH: Hash = Hash([0; 32]);
|
|
||||||
|
|
||||||
/// Serializer that outputs a hash of the serialized object
|
/// Serializer that outputs a hash of the serialized object
|
||||||
pub struct HashWriter {
|
pub struct HashWriter {
|
||||||
state: Keccak,
|
state: Keccak,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HashWriter {
|
impl HashWriter {
|
||||||
fn finalize(self, output: &mut [u8]) {
|
pub fn finalize(self, output: &mut [u8]) {
|
||||||
self.state.finalize(output);
|
self.state.finalize(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ use secp::pedersen::*;
|
||||||
use consensus::PROOFSIZE;
|
use consensus::PROOFSIZE;
|
||||||
pub use self::block::{Block, BlockHeader};
|
pub use self::block::{Block, BlockHeader};
|
||||||
pub use self::transaction::{Transaction, Input, Output, TxProof};
|
pub use self::transaction::{Transaction, Input, Output, TxProof};
|
||||||
use self::hash::{Hash, Hashed, ZERO_HASH};
|
use self::hash::{Hash, Hashed, HashWriter, ZERO_HASH};
|
||||||
use ser::{Writeable, Writer, Error};
|
use ser::{Writeable, Writer, Error};
|
||||||
|
|
||||||
/// Implemented by types that hold inputs and outputs including Pedersen
|
/// Implemented by types that hold inputs and outputs including Pedersen
|
||||||
|
@ -109,11 +109,24 @@ impl Clone for Proof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hashed for Proof {
|
||||||
|
fn hash(&self) -> Hash {
|
||||||
|
let mut hasher = HashWriter::default();
|
||||||
|
let mut ret = [0; 32];
|
||||||
|
for p in self.0.iter() {
|
||||||
|
hasher.write_u32(*p).unwrap();
|
||||||
|
}
|
||||||
|
hasher.finalize(&mut ret);
|
||||||
|
Hash(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Proof {
|
impl Proof {
|
||||||
/// Builds a proof with all bytes zeroed out
|
/// Builds a proof with all bytes zeroed out
|
||||||
pub fn zero() -> Proof {
|
pub fn zero() -> Proof {
|
||||||
Proof([0; 42])
|
Proof([0; 42])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a proof from a vector of exactly PROOFSIZE (42) u32.
|
/// Builds a proof from a vector of exactly PROOFSIZE (42) u32.
|
||||||
pub fn from_vec(v: Vec<u32>) -> Proof {
|
pub fn from_vec(v: Vec<u32>) -> Proof {
|
||||||
assert!(v.len() == PROOFSIZE);
|
assert!(v.len() == PROOFSIZE);
|
||||||
|
@ -123,6 +136,7 @@ impl Proof {
|
||||||
}
|
}
|
||||||
Proof(p)
|
Proof(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the proof to a vector of u64s
|
/// Converts the proof to a vector of u64s
|
||||||
pub fn to_u64s(&self) -> Vec<u64> {
|
pub fn to_u64s(&self) -> Vec<u64> {
|
||||||
let mut nonces = Vec::with_capacity(PROOFSIZE);
|
let mut nonces = Vec::with_capacity(PROOFSIZE);
|
||||||
|
@ -131,10 +145,17 @@ impl Proof {
|
||||||
}
|
}
|
||||||
nonces
|
nonces
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the proof to a vector of u32s
|
/// Converts the proof to a vector of u32s
|
||||||
pub fn to_u32s(&self) -> Vec<u32> {
|
pub fn to_u32s(&self) -> Vec<u32> {
|
||||||
self.0.to_vec()
|
self.0.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Two hashes that will get hashed together in a Merkle tree to build the next
|
/// Two hashes that will get hashed together in a Merkle tree to build the next
|
||||||
|
|
|
@ -244,14 +244,15 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shift_truncate() {
|
fn shift_truncate() {
|
||||||
assert_eq!((Target::join(0, 0xffff).unwrap() >> 8) << 8, Target::join(0, 0xff00).unwrap());
|
assert_eq!((Target::join(0, 0xffff).unwrap() >> 8) << 8,
|
||||||
|
Target::join(0, 0xff00).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn split_fit() {
|
fn split_fit() {
|
||||||
let t = Target::join(10 * 8, ::std::u32::MAX).unwrap();
|
let t = Target::join(10 * 8, ::std::u32::MAX).unwrap();
|
||||||
let (exp, mant) = t.split();
|
let (exp, mant) = t.split();
|
||||||
assert_eq!(exp, 10*8);
|
assert_eq!(exp, 10 * 8);
|
||||||
assert_eq!(mant, ::std::u32::MAX);
|
assert_eq!(mant, ::std::u32::MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
use core;
|
use core;
|
||||||
|
use consensus::{DEFAULT_SIZESHIFT, MAX_TARGET};
|
||||||
|
|
||||||
use tiny_keccak::Keccak;
|
use tiny_keccak::Keccak;
|
||||||
|
|
||||||
|
@ -38,7 +39,8 @@ pub fn genesis() -> core::Block {
|
||||||
tm_mday: 4,
|
tm_mday: 4,
|
||||||
..time::empty_tm()
|
..time::empty_tm()
|
||||||
},
|
},
|
||||||
td: 0,
|
cuckoo_len: DEFAULT_SIZESHIFT,
|
||||||
|
target: MAX_TARGET,
|
||||||
utxo_merkle: core::hash::Hash::from_vec(empty_h.to_vec()),
|
utxo_merkle: core::hash::Hash::from_vec(empty_h.to_vec()),
|
||||||
tx_merkle: core::hash::Hash::from_vec(empty_h.to_vec()),
|
tx_merkle: core::hash::Hash::from_vec(empty_h.to_vec()),
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
|
|
@ -27,9 +27,10 @@ mod cuckoo;
|
||||||
|
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
use consensus::{SIZESHIFT, 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 pow::cuckoo::{Cuckoo, Miner, Error};
|
use pow::cuckoo::{Cuckoo, Miner, Error};
|
||||||
|
|
||||||
use ser;
|
use ser;
|
||||||
|
@ -87,21 +88,21 @@ impl PowHeader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates the proof of work of a given header.
|
/// Validates the proof of work of a given header.
|
||||||
pub fn verify(b: &Block, target: Proof) -> bool {
|
pub fn verify(b: &Block, target: Target) -> bool {
|
||||||
verify_size(b, target, SIZESHIFT)
|
verify_size(b, target, b.header.cuckoo_len as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as default verify function but uses the much easier Cuckoo20 (mostly
|
/// Same as default verify function but uses the much easier Cuckoo20 (mostly
|
||||||
/// for tests).
|
/// for tests).
|
||||||
pub fn verify20(b: &Block, target: Proof) -> bool {
|
pub fn verify20(b: &Block, target: Target) -> bool {
|
||||||
verify_size(b, target, 20)
|
verify_size(b, target, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_size(b: &Block, target: Proof, sizeshift: u32) -> bool {
|
pub fn verify_size(b: &Block, target: Target, sizeshift: 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 hash is smaller than our target before going into more
|
||||||
// expensive validation
|
// expensive validation
|
||||||
if target < b.header.pow {
|
if target < b.header.pow.to_target() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Cuckoo::new(hash.to_slice(), sizeshift).verify(b.header.pow, EASINESS as u64)
|
Cuckoo::new(hash.to_slice(), sizeshift).verify(b.header.pow, EASINESS as u64)
|
||||||
|
@ -110,17 +111,17 @@ pub fn verify_size(b: &Block, target: Proof, sizeshift: 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: Proof) -> Result<(Proof, u64), Error> {
|
pub fn pow(b: &Block, target: Target) -> Result<(Proof, u64), Error> {
|
||||||
pow_size(b, target, SIZESHIFT)
|
pow_size(b, target, 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: Proof) -> Result<(Proof, u64), Error> {
|
pub fn pow20(b: &Block, target: Target) -> Result<(Proof, u64), Error> {
|
||||||
pow_size(b, target, 20)
|
pow_size(b, target, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pow_size(b: &Block, target: Proof, sizeshift: u32) -> Result<(Proof, u64), Error> {
|
fn pow_size(b: &Block, target: Target, 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;
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ fn pow_size(b: &Block, target: Proof, sizeshift: u32) -> Result<(Proof, u64), Er
|
||||||
// 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 is lower that the target,
|
||||||
// we're all good
|
// 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 <= target {
|
if proof.to_target() <= target {
|
||||||
return Ok((proof, pow_header.nonce));
|
return Ok((proof, pow_header.nonce));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,11 +160,11 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn genesis_pow() {
|
fn genesis_pow() {
|
||||||
let mut b = genesis::genesis();
|
let mut b = genesis::genesis();
|
||||||
let (proof, nonce) = pow20(&b, Proof(MAX_TARGET)).unwrap();
|
let (proof, nonce) = pow20(&b, MAX_TARGET).unwrap();
|
||||||
assert!(nonce > 0);
|
assert!(nonce > 0);
|
||||||
assert!(proof < Proof(MAX_TARGET));
|
assert!(proof.to_target() < MAX_TARGET);
|
||||||
b.header.pow = proof;
|
b.header.pow = proof;
|
||||||
b.header.nonce = nonce;
|
b.header.nonce = nonce;
|
||||||
assert!(verify20(&b, Proof(MAX_TARGET)));
|
assert!(verify20(&b, MAX_TARGET));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue