Remove redundant hashing of header before proof of work (#1118)
We used to hash the hash of the header, needlessly. This also makes a few function signatures more explicit. Note that this is consensus breaking and should be accompanied with a similar change on the cuckoo-pow repository: https://github.com/mimblewimble/cuckoo-miner/commit/6fcecf516a111424d8bd5542fe20250e4f02cd8f Fixes #1066
This commit is contained in:
+52
-27
@@ -20,8 +20,7 @@
|
||||
use std::cmp;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use blake2;
|
||||
|
||||
use core::BlockHeader;
|
||||
use core::Proof;
|
||||
use pow::siphash::siphash24;
|
||||
|
||||
@@ -54,16 +53,14 @@ impl Cuckoo {
|
||||
/// Initializes a new Cuckoo Cycle setup, using the provided byte array to
|
||||
/// generate a seed. In practice for PoW applications the byte array is a
|
||||
/// serialized block header.
|
||||
pub fn new(header: &[u8], sizeshift: u8) -> Cuckoo {
|
||||
pub fn from_hash(header_hash: &[u8], sizeshift: u8) -> Cuckoo {
|
||||
let size = 1 << sizeshift;
|
||||
let hashed = blake2::blake2b::blake2b(32, &[], header);
|
||||
let hashed = hashed.as_bytes();
|
||||
Cuckoo {
|
||||
v: [
|
||||
u8_to_u64(hashed, 0),
|
||||
u8_to_u64(hashed, 8),
|
||||
u8_to_u64(hashed, 16),
|
||||
u8_to_u64(hashed, 24),
|
||||
u8_to_u64(header_hash, 0),
|
||||
u8_to_u64(header_hash, 8),
|
||||
u8_to_u64(header_hash, 16),
|
||||
u8_to_u64(header_hash, 24),
|
||||
],
|
||||
size: size,
|
||||
mask: (1 << sizeshift) / 2 - 1,
|
||||
@@ -162,9 +159,14 @@ enum CycleSol {
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
/// Creates a new miner
|
||||
pub fn new(header: &[u8], ease: u32, proof_size: usize, sizeshift: u8) -> Miner {
|
||||
let cuckoo = Cuckoo::new(header, sizeshift);
|
||||
/// Creates a new miner for the provided block header
|
||||
pub fn new(header: &BlockHeader, ease: u32, proof_size: usize, sizeshift: u8) -> Miner {
|
||||
Miner::from_hash(header.pre_pow_hash().as_ref(), ease, proof_size, sizeshift)
|
||||
}
|
||||
|
||||
/// Creates a new miner directly from a hash
|
||||
pub fn from_hash(header_hash: &[u8], ease: u32, proof_size: usize, sizeshift: u8) -> Miner {
|
||||
let cuckoo = Cuckoo::from_hash(header_hash, sizeshift);
|
||||
let size = 1 << sizeshift;
|
||||
let graph = vec![0; size + 1];
|
||||
let easiness = (ease as u64) * (size as u64) / 100;
|
||||
@@ -306,6 +308,7 @@ fn u8_to_u64(p: &[u8], i: usize) -> u64 {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use blake2;
|
||||
use core::Proof;
|
||||
|
||||
static V1: [u32; 42] = [
|
||||
@@ -339,26 +342,45 @@ mod test {
|
||||
0x701f37b,
|
||||
];
|
||||
|
||||
fn blake2(data: &[u8]) -> blake2::blake2b::Blake2bResult {
|
||||
blake2::blake2b::blake2b(32, &[], data)
|
||||
}
|
||||
|
||||
/// Find a 42-cycle on Cuckoo20 at 75% easiness and verify against a few
|
||||
/// known cycle proofs
|
||||
/// generated by other implementations.
|
||||
#[test]
|
||||
fn mine20_vectors() {
|
||||
let nonces1 = Miner::new(&[49], 75, 42, 20).mine().unwrap();
|
||||
let nonces1 = Miner::from_hash(blake2(&[49]).as_bytes(), 75, 42, 20)
|
||||
.mine()
|
||||
.unwrap();
|
||||
assert_eq!(Proof::new(V1.to_vec()), nonces1);
|
||||
|
||||
let nonces2 = Miner::new(&[50], 70, 42, 20).mine().unwrap();
|
||||
let nonces2 = Miner::from_hash(blake2(&[50]).as_bytes(), 70, 42, 20)
|
||||
.mine()
|
||||
.unwrap();
|
||||
assert_eq!(Proof::new(V2.to_vec()), nonces2);
|
||||
|
||||
let nonces3 = Miner::new(&[51], 70, 42, 20).mine().unwrap();
|
||||
let nonces3 = Miner::from_hash(blake2(&[51]).as_bytes(), 70, 42, 20)
|
||||
.mine()
|
||||
.unwrap();
|
||||
assert_eq!(Proof::new(V3.to_vec()), nonces3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate20_vectors() {
|
||||
assert!(Cuckoo::new(&[49], 20).verify(Proof::new(V1.to_vec().clone()), 75));
|
||||
assert!(Cuckoo::new(&[50], 20).verify(Proof::new(V2.to_vec().clone()), 70));
|
||||
assert!(Cuckoo::new(&[51], 20).verify(Proof::new(V3.to_vec().clone()), 70));
|
||||
assert!(
|
||||
Cuckoo::from_hash(blake2(&[49]).as_bytes(), 20)
|
||||
.verify(Proof::new(V1.to_vec().clone()), 75)
|
||||
);
|
||||
assert!(
|
||||
Cuckoo::from_hash(blake2(&[50]).as_bytes(), 20)
|
||||
.verify(Proof::new(V2.to_vec().clone()), 70)
|
||||
);
|
||||
assert!(
|
||||
Cuckoo::from_hash(blake2(&[51]).as_bytes(), 20)
|
||||
.verify(Proof::new(V3.to_vec().clone()), 70)
|
||||
);
|
||||
}
|
||||
|
||||
/// Just going to disable this for now, as it's painful to try and get a
|
||||
@@ -368,19 +390,22 @@ mod test {
|
||||
fn validate28_vectors() {
|
||||
let mut test_header = [0; 32];
|
||||
test_header[0] = 24;
|
||||
assert!(Cuckoo::new(&test_header, 28).verify(Proof::new(V4.to_vec().clone()), 50));
|
||||
assert!(Cuckoo::from_hash(&test_header, 28).verify(Proof::new(V4.to_vec().clone()), 50));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_fail() {
|
||||
// edge checks
|
||||
assert!(!Cuckoo::new(&[49], 20).verify(Proof::new(vec![0; 42]), 75));
|
||||
assert!(!Cuckoo::new(&[49], 20).verify(Proof::new(vec![0xffff; 42]), 75));
|
||||
assert!(!Cuckoo::from_hash(blake2(&[49]).as_bytes(), 20).verify(Proof::new(vec![0; 42]), 75));
|
||||
assert!(!Cuckoo::from_hash(blake2(&[49]).as_bytes(), 20)
|
||||
.verify(Proof::new(vec![0xffff; 42]), 75));
|
||||
// wrong data for proof
|
||||
assert!(!Cuckoo::new(&[50], 20).verify(Proof::new(V1.to_vec().clone()), 75));
|
||||
assert!(!Cuckoo::from_hash(blake2(&[50]).as_bytes(), 20)
|
||||
.verify(Proof::new(V1.to_vec().clone()), 75));
|
||||
let mut test_header = [0; 32];
|
||||
test_header[0] = 24;
|
||||
assert!(!Cuckoo::new(&test_header, 20).verify(Proof::new(V4.to_vec().clone()), 50));
|
||||
assert!(!Cuckoo::from_hash(blake2(&test_header).as_bytes(), 20)
|
||||
.verify(Proof::new(V4.to_vec().clone()), 50));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -388,14 +413,14 @@ mod test {
|
||||
// cuckoo20
|
||||
for n in 1..5 {
|
||||
let h = [n; 32];
|
||||
let nonces = Miner::new(&h, 75, 42, 20).mine().unwrap();
|
||||
assert!(Cuckoo::new(&h, 20).verify(nonces, 75));
|
||||
let nonces = Miner::from_hash(&h, 75, 42, 20).mine().unwrap();
|
||||
assert!(Cuckoo::from_hash(&h, 20).verify(nonces, 75));
|
||||
}
|
||||
// cuckoo18
|
||||
for n in 1..5 {
|
||||
let h = [n; 32];
|
||||
let nonces = Miner::new(&h, 75, 42, 18).mine().unwrap();
|
||||
assert!(Cuckoo::new(&h, 18).verify(nonces, 75));
|
||||
let nonces = Miner::from_hash(&h, 75, 42, 18).mine().unwrap();
|
||||
assert!(Cuckoo::from_hash(&h, 18).verify(nonces, 75));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -48,7 +48,7 @@ use pow::cuckoo::{Cuckoo, Error};
|
||||
/// 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: u8) -> bool {
|
||||
Cuckoo::new(&bh.pre_pow_hash()[..], cuckoo_sz)
|
||||
Cuckoo::from_hash(bh.pre_pow_hash().as_ref(), cuckoo_sz)
|
||||
.verify(bh.pow.clone(), consensus::EASINESS as u64)
|
||||
}
|
||||
|
||||
@@ -88,15 +88,9 @@ pub fn pow_size(
|
||||
|
||||
// try to find a cuckoo cycle on that header hash
|
||||
loop {
|
||||
// can be trivially optimized by avoiding re-serialization every time but this
|
||||
// is not meant as a fast miner implementation
|
||||
let pow_hash = bh.pre_pow_hash();
|
||||
|
||||
// if we found a cycle (not guaranteed) and the proof hash is higher that the
|
||||
// diff, we're all good
|
||||
if let Ok(proof) =
|
||||
cuckoo::Miner::new(&pow_hash[..], consensus::EASINESS, proof_size, sz).mine()
|
||||
{
|
||||
if let Ok(proof) = cuckoo::Miner::new(bh, consensus::EASINESS, proof_size, sz).mine() {
|
||||
if proof.to_difficulty() >= diff {
|
||||
bh.pow = proof.clone();
|
||||
return Ok(());
|
||||
|
||||
@@ -97,9 +97,8 @@ impl Miner {
|
||||
|
||||
let mut sol = None;
|
||||
while head.hash() == *latest_hash && time::get_time().sec < deadline {
|
||||
let pow_hash = b.header.pre_pow_hash();
|
||||
if let Ok(proof) = cuckoo::Miner::new(
|
||||
&pow_hash[..],
|
||||
&b.header,
|
||||
consensus::EASINESS,
|
||||
global::proofsize(),
|
||||
global::sizeshift(),
|
||||
|
||||
Reference in New Issue
Block a user