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:
Ignotus Peverell
2018-06-22 22:59:56 +01:00
committed by GitHub
parent 24cd5bc3e3
commit 3530b8300c
3 changed files with 55 additions and 37 deletions
+52 -27
View File
@@ -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
View File
@@ -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(());
+1 -2
View File
@@ -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(),