diff --git a/core/src/pow/.cuckoo.rs.rustfmt b/core/src/pow/.cuckoo.rs.rustfmt deleted file mode 100644 index 3cdba622b..000000000 --- a/core/src/pow/.cuckoo.rs.rustfmt +++ /dev/null @@ -1,444 +0,0 @@ -//! Implementation of Cuckoo Cycle designed by John Tromp. Ported to Rust from -//! the C and Java code at https://github.com/tromp/cuckoo. Note that only the -//! simple miner is included, mostly for testing purposes. John Tromp's Tomato -//! miner will be much faster in almost every environment. - -use std::collections::HashSet; -use std::cmp; -use std::fmt; - -use crypto::digest::Digest; -use crypto::sha2::Sha256; - -use pow::siphash::siphash24; - -const PROOFSIZE: usize = 42; -const MAXPATHLEN: usize = 8192; - -/// A Cuckoo proof representing the nonces for a cycle of the right size. -pub struct Proof([u32; PROOFSIZE]); - -impl fmt::Debug for Proof { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "Cuckoo(")); - for (i, val) in self.0[..].iter().enumerate() { - try!(write!(f, "{:x}", val)); - if i < PROOFSIZE - 1 { - write!(f, " "); - } - } - write!(f, ")") - } -} -impl PartialEq for Proof { - fn eq(&self, other: &Proof) -> bool { - self.0[..] == other.0[..] - } -} -impl Eq for Proof {} -impl Clone for Proof { - #[inline] - fn clone(&self) -> Proof { - let mut cp = [0; PROOFSIZE]; - for (i, n) in self.0.iter().enumerate() { - cp[i] = *n; - } - Proof(cp) - } -} - -impl Proof { - fn to_u64s(&self) -> Vec<u64> { - let mut nonces = Vec::with_capacity(PROOFSIZE); - for n in self.0.iter() { - nonces.push(*n as u64); - } - nonces - } -} - -/// An edge in the Cuckoo graph, simply references two u64 nodes. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -struct Edge { - u: u64, - v: u64, -} - -pub struct Cuckoo { - mask: u64, - size: u64, - v: [u64; 4], -} - -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: u32) -> Cuckoo { - let size = 1 << sizeshift; - let mut hasher = Sha256::new(); - let mut hashed = [0; 32]; - hasher.input(header); - hasher.result(&mut hashed); - - let k0 = u8_to_u64(hashed, 0); - let k1 = u8_to_u64(hashed, 8); - let mut v = [0; 4]; - v[0] = k0 ^ 0x736f6d6570736575; - v[1] = k1 ^ 0x646f72616e646f6d; - v[2] = k0 ^ 0x6c7967656e657261; - v[3] = k1 ^ 0x7465646279746573; - // println!("{:?} {:?} {:?} {:?}", v[0], v[1], v[2], v[3]); - Cuckoo { - v: v, - size: size, - mask: (1 << sizeshift) / 2 - 1, - } - } - - /// Generates a node in the cuckoo graph generated from our seed. A node is - /// simply materialized as a u64 from a nonce and an offset (generally 0 or - /// 1). - pub fn new_node(&self, nonce: u64, uorv: u64) -> u64 { - return ((siphash24(self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv; - } - - /// Creates a new edge in the cuckoo graph generated by our seed from a - /// nonce. Generates two node coordinates from the nonce and links them - /// together. - pub fn new_edge(&self, nonce: u64) -> Edge { - Edge { - u: self.new_node(nonce, 0), - v: self.new_node(nonce, 1), - } - } - - /// Assuming increasing nonces all smaller than easiness, verifies the - /// nonces form a cycle in a Cuckoo graph. Each nonce generates an edge, we - /// build the nodes on both side of that edge and count the connections. - pub fn verify(&self, proof: Proof, ease: u64) -> bool { - let easiness = ease * (self.size as u64) / 100; - let nonces = proof.to_u64s(); - let mut us = [0; PROOFSIZE]; - let mut vs = [0; PROOFSIZE]; - for n in 0..PROOFSIZE { - if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) { - return false; - } - us[n] = self.new_node(nonces[n], 0); - vs[n] = self.new_node(nonces[n], 1); - } - let mut i = 0; - let mut count = PROOFSIZE; - loop { - let mut j = i; - for k in 0..PROOFSIZE { - // find unique other j with same vs[j] - if k != i && vs[k] == vs[i] { - if j != i { - return false; - } - j = k; - } - } - if j == i { - return false; - } - i = j; - for k in 0..PROOFSIZE { - // find unique other i with same us[i] - if k != j && us[k] == us[j] { - if i != j { - return false; - } - i = k; - } - } - if i == j { - return false; - } - count -= 2; - if i == 0 { - break; - } - } - count == 0 - } -} - -#[derive(Debug)] -pub enum Error { - PathError, - NoSolutionError, -} - -/// Miner for the Cuckoo Cycle algorithm. While the verifier will work for -/// graph sizes up to a u64, the miner is limited to u32 to be more memory -/// compact (so shift <= 32). Non-optimized for now and and so mostly used for -/// tests, being impractical with sizes greater than 2^22. -pub struct Miner { - easiness: u64, - size: usize, - cuckoo: Cuckoo, - graph: Vec<u32>, -} - -/// What type of cycle we have found? -enum CycleSol { - /// A cycle of the right length is a valid proof. - ValidProof([u32; PROOFSIZE]), - /// A cycle of the wrong length is great, but not a proof. - InvalidCycle(usize), - /// No cycles have been found. - NoCycle, -} - -impl Miner { - pub fn new(header: &[u8], ease: u64, sizeshift: u32) -> Miner { - let cuckoo = Cuckoo::new(header, sizeshift); - let size = 1 << sizeshift; - let graph = vec![0; size + 1]; - let easiness = ease * (size as u64) / 100; - Miner { - easiness: easiness, - size: size, - cuckoo: cuckoo, - graph: graph, - } - } - - pub fn mine(&mut self) -> Result<Proof, Error> { - let mut us = [0; MAXPATHLEN]; - let mut vs = [0; MAXPATHLEN]; - // println!("{}", self.easiness); - let mut m = 0; - for nonce in 0..self.easiness { - m += 1; - // println!("- {}", nonce); - us[0] = self.cuckoo.new_node(nonce, 0) as u32; - vs[0] = self.cuckoo.new_node(nonce, 1) as u32; - let u = self.graph[us[0] as usize]; - let v = self.graph[vs[0] as usize]; - if us[0] == 0 { - continue; // ignore duplicate edges - } - // println!("{}, {}, {}, {}", us[0], vs[0], u, v); - // println!(" ^{}, {}", us[0], vs[0]); - // println!(" _{}, {}", u, v); - let nu = try!(if nonce == 481921 { - self.path_p(u, &mut us) - } else { - self.path(u, &mut us) - }) as usize; - let nv = try!(if nonce == 481921 { - self.path_p(v, &mut vs) - } else { - self.path(v, &mut vs) - }) as usize; - // println!(" &{}, {}", nu, nv); - - let sol = self.find_sol(nu, &us, nv, &vs); - match sol { - CycleSol::ValidProof(res) => return Ok(Proof(res)), - CycleSol::InvalidCycle(_) => continue, - CycleSol::NoCycle => { - self.update_graph(nu, &us, nv, &vs); - } - } - } - // println!("== {}", m); - Err(Error::NoSolutionError) - } - - fn path(&self, mut u: u32, us: &mut [u32]) -> Result<u32, Error> { - let mut nu = 0; - while u != 0 { - nu += 1; - if nu >= MAXPATHLEN { - while nu != 0 && us[(nu - 1) as usize] != u { - nu -= 1; - } - return Err(Error::PathError); - } - us[nu as usize] = u; - u = self.graph[u as usize]; - } - Ok(nu as u32) - } - - fn path_p(&self, mut u: u32, us: &mut [u32]) -> Result<u32, Error> { - let mut nu = 0; - while u != 0 { - // println!("{}", u); - nu += 1; - if nu >= MAXPATHLEN { - while nu != 0 && us[(nu - 1) as usize] != u { - nu -= 1; - } - return Err(Error::PathError); - } - us[nu as usize] = u; - u = self.graph[u as usize]; - } - Ok(nu as u32) - } - - fn update_graph(&mut self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) { - if nu < nv { - while nu != 0 { - nu -= 1; - // self.graph[us[nu + 1] as usize] = us[nu]; - self.set_graph(us[nu + 1] as usize, us[nu]); - } - // self.graph[us[0] as usize] = vs[0]; - self.set_graph(us[0] as usize, vs[0]); - } else { - while nv != 0 { - nv -= 1; - // self.graph[vs[nv + 1] as usize] = vs[nv]; - self.set_graph(vs[nv + 1] as usize, vs[nv]); - } - // self.graph[vs[0] as usize] = us[0]; - self.set_graph(vs[0] as usize, us[0]); - } - } - - fn set_graph(&mut self, idx: usize, val: u32) { - // println!("set {} = {}", idx, val); - self.graph[idx] = val; - } - - fn find_sol(&self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) -> CycleSol { - if us[nu] == vs[nv] { - let min = cmp::min(nu, nv); - nu -= min; - nv -= min; - while us[nu] != vs[nv] { - nu += 1; - nv += 1; - } - if nu + nv + 1 == PROOFSIZE { - self.solution(&us, nu as u32, &vs, nv as u32) - } else { - CycleSol::InvalidCycle(nu + nv + 1) - } - } else { - CycleSol::NoCycle - } - } - - fn solution(&self, us: &[u32], mut nu: u32, vs: &[u32], mut nv: u32) -> CycleSol { - let mut cycle = HashSet::new(); - cycle.insert(Edge { - u: us[0] as u64, - v: vs[0] as u64, - }); - while nu != 0 { - // u's in even position; v's in odd - nu -= 1; - cycle.insert(Edge { - u: us[((nu + 1) & !1) as usize] as u64, - v: us[(nu | 1) as usize] as u64, - }); - } - while nv != 0 { - // u's in odd position; v's in even - nv -= 1; - cycle.insert(Edge { - u: vs[(nv | 1) as usize] as u64, - v: vs[((nv + 1) & !1) as usize] as u64, - }); - } - let mut n = 0; - let mut sol = [0; PROOFSIZE]; - for nonce in 0..self.easiness { - let edge = self.cuckoo.new_edge(nonce); - if cycle.contains(&edge) { - sol[n] = nonce as u32; - n += 1; - cycle.remove(&edge); - } - } - return if n == PROOFSIZE { - CycleSol::ValidProof(sol) - } else { - CycleSol::NoCycle - }; - } -} - - -/// Utility to transform a 8 bytes of a byte array into a u64. -fn u8_to_u64(p: [u8; 32], i: usize) -> u64 { - (p[i] as u64) | (p[i + 1] as u64) << 8 | (p[i + 2] as u64) << 16 | (p[i + 3] as u64) << 24 | - (p[i + 4] as u64) << 32 | (p[i + 5] as u64) << 40 | - (p[i + 6] as u64) << 48 | (p[i + 7] as u64) << 56 -} - -#[cfg(test)] -mod test { - use super::*; - - static V1: Proof = Proof([0xe13, 0x410c, 0x7974, 0x8317, 0xb016, 0xb992, 0xe3c8, 0x1038a, - 0x116f0, 0x15ed2, 0x165a2, 0x17793, 0x17dd1, 0x1f885, 0x20932, - 0x20936, 0x2171b, 0x28968, 0x2b184, 0x30b8e, 0x31d28, 0x35782, - 0x381ea, 0x38321, 0x3b414, 0x3e14b, 0x43615, 0x49a51, 0x4a319, - 0x58271, 0x5dbb9, 0x5dbcf, 0x62db4, 0x653d2, 0x655f6, 0x66382, - 0x7057d, 0x765b0, 0x79c7c, 0x83167, 0x86e7b, 0x8a5f4]); - static V2: Proof = Proof([0x33b8, 0x3fd9, 0x8f2b, 0xba0d, 0x11e2d, 0x1d51d, 0x2786e, 0x29625, - 0x2a862, 0x2a972, 0x2e6d7, 0x319df, 0x37ce7, 0x3f771, 0x4373b, - 0x439b7, 0x48626, 0x49c7d, 0x4a6f1, 0x4a808, 0x4e518, 0x519e3, - 0x526bb, 0x54988, 0x564e9, 0x58a6c, 0x5a4dd, 0x63fa2, 0x68ad1, - 0x69e52, 0x6bf53, 0x70841, 0x76343, 0x763a4, 0x79681, 0x7d006, - 0x7d633, 0x7eebe, 0x7fe7c, 0x811fa, 0x863c1, 0x8b149]); - static V3: Proof = Proof([0x24ae, 0x5180, 0x9f3d, 0xd379, 0x102c9, 0x15787, 0x16df4, 0x19509, - 0x19a78, 0x235a0, 0x24210, 0x24410, 0x2567f, 0x282c3, 0x2d986, - 0x2efde, 0x319d7, 0x334d7, 0x336dd, 0x34296, 0x35809, 0x3ad40, - 0x46d81, 0x48c92, 0x4b374, 0x4c353, 0x4fe4c, 0x50e4f, 0x53202, - 0x5d167, 0x6527c, 0x6a8b5, 0x6c70d, 0x76d90, 0x794f4, 0x7c411, - 0x7c5d4, 0x7f59f, 0x7fead, 0x872d8, 0x875b4, 0x95c6b]); - - /// Find a 42-cycle on Cuckoo20 at 75% easiness and verifiy against a few - /// known cycle proofs - /// generated by other implementations. - #[test] - fn mine20_vectors() { - let nonces1 = Miner::new(&[49], 75, 20).mine().unwrap(); - assert_eq!(V1, nonces1); - - let nonces2 = Miner::new(&[50], 70, 20).mine().unwrap(); - assert_eq!(V2, nonces2); - - let nonces3 = Miner::new(&[51], 70, 20).mine().unwrap(); - assert_eq!(V3, nonces3); - } - - #[test] - fn validate20_vectors() { - assert!(Cuckoo::new(&[49], 20).verify(V1.clone(), 75)); - assert!(Cuckoo::new(&[50], 20).verify(V2.clone(), 70)); - assert!(Cuckoo::new(&[51], 20).verify(V3.clone(), 70)); - } - - #[test] - fn validate_fail() { - assert!(!Cuckoo::new(&[49], 20).verify(Proof([0; 42]), 75)); - assert!(!Cuckoo::new(&[50], 20).verify(V1.clone(), 75)); - } - - #[test] - fn mine20_validate() { - // cuckoo20 - for n in 1..5 { - let h = [n; 32]; - let nonces = Miner::new(&h, 75, 20).mine().unwrap(); - assert!(Cuckoo::new(&h, 20).verify(nonces, 75)); - } - // cuckoo18 - for n in 1..5 { - let h = [n; 32]; - let nonces = Miner::new(&h, 75, 18).mine().unwrap(); - assert!(Cuckoo::new(&h, 18).verify(nonces, 75)); - } - } -} diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index b5ed3ad40..40f163962 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -28,7 +28,7 @@ pub mod cuckoo; use time; use consensus::EASINESS; -use core::{Block, Proof}; +use core::{BlockHeader, Proof}; use core::hash::{Hash, Hashed}; use core::target::Difficulty; use pow::cuckoo::{Cuckoo, Miner, Error}; @@ -37,58 +37,58 @@ use ser; use ser::{Writeable, Writer}; /// Validates the proof of work of a given header. -pub fn verify(b: &Block) -> bool { - verify_size(b, b.header.cuckoo_len as u32) +pub fn verify(bh: &BlockHeader) -> bool { + verify_size(bh, bh.cuckoo_len as u32) } -pub fn verify_size(b: &Block, cuckoo_sz: u32) -> bool { +pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u32) -> bool { // make sure the pow hash shows a difficulty at least as large as the target // difficulty - if b.header.difficulty > b.header.pow.to_difficulty() { + if bh.difficulty > bh.pow.to_difficulty() { return false; } - Cuckoo::new(b.hash().to_slice(), cuckoo_sz).verify(b.header.pow, EASINESS as u64) + Cuckoo::new(bh.hash().to_slice(), 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(b: &mut Block, diff: Difficulty) -> Result<(), Error> { - let cuckoo_len = b.header.cuckoo_len as u32; - pow_size(b, diff, cuckoo_len) +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 /// tests). -pub fn pow20(b: &mut Block, diff: Difficulty) -> Result<(), Error> { - pow_size(b, diff, 20) +pub fn pow20(bh: &mut BlockHeader, diff: Difficulty) -> Result<(), Error> { + pow_size(bh, diff, 20) } -pub fn pow_size(b: &mut Block, diff: Difficulty, sizeshift: u32) -> Result<(), Error> { - let start_nonce = b.header.nonce; +pub fn pow_size(bh: &mut BlockHeader, diff: Difficulty, sizeshift: u32) -> Result<(), Error> { + let start_nonce = bh.nonce; // 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 = b.hash(); + let pow_hash = bh.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) = Miner::new(pow_hash.to_slice(), EASINESS, sizeshift).mine() { if proof.to_difficulty() >= diff { - b.header.pow = proof; + bh.pow = proof; return Ok(()); } } // otherwise increment the nonce - b.header.nonce += 1; + bh.nonce += 1; // and if we're back where we started, update the time (changes the hash as // well) - if b.header.nonce == start_nonce { - b.header.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 }); + if bh.nonce == start_nonce { + bh.timestamp = time::at_utc(time::Timespec { sec: 0, nsec: 0 }); } } } @@ -104,9 +104,9 @@ mod test { fn genesis_pow() { let mut b = genesis::genesis(); b.header.nonce = 310; - pow20(&mut b, Difficulty::one()).unwrap(); + pow20(&mut b.header, Difficulty::one()).unwrap(); assert!(b.header.nonce != 310); assert!(b.header.pow.to_difficulty() >= Difficulty::one()); - assert!(verify_size(&b, 20)); + assert!(verify_size(&b.header, 20)); } }