diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index f1ce6cfb..80c196a1 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -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)); } } } diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 9dd35a46..49010675 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -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(()); diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index ff5ca28e..901d794a 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -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(),