From 2aea64d0966f5bf6e7266872003854eb0018dcad Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 19 Sep 2018 09:28:09 +0100 Subject: [PATCH 01/22] cuck placeholder --- core/src/pow/cuckatoo.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 core/src/pow/cuckatoo.rs diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs new file mode 100644 index 0000000000..d84f69ebf3 --- /dev/null +++ b/core/src/pow/cuckatoo.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of Cuckatoo Cycle designed by John Tromp. + +struct Graph { + + +} From 54ae89e8e2f7fe3f790f930d601e4c94deeeffbd Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 19 Sep 2018 09:28:27 +0100 Subject: [PATCH 02/22] rustfmt --- core/src/pow/cuckatoo.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index d84f69ebf3..9fce672cb5 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -14,7 +14,4 @@ //! Implementation of Cuckatoo Cycle designed by John Tromp. -struct Graph { - - -} +struct Graph {} From e5cbcb0bc4a279aa285d384ae105c1839098db8e Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 19 Sep 2018 16:12:48 +0100 Subject: [PATCH 03/22] cuckatoo, early days --- Cargo.lock | 3 ++ core/Cargo.toml | 1 + core/src/pow/cuckatoo.rs | 113 ++++++++++++++++++++++++++++++++++++++- core/src/pow/mod.rs | 2 + 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4e43c426eb..bf4fc584cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,6 +687,7 @@ dependencies = [ "grin_wallet 0.3.0", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1378,6 +1379,7 @@ name = "num" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-complex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1455,6 +1457,7 @@ name = "num-rational" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index ba46283d70..8827ba4e77 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,6 +14,7 @@ failure = "0.1" failure_derive = "0.1" lazy_static = "1" lru-cache = "0.1" +num = "0.2" num-bigint = "0.2" rand = "0.5" serde = "1" diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 9fce672cb5..d80bcfa432 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -13,5 +13,116 @@ // limitations under the License. //! Implementation of Cuckatoo Cycle designed by John Tromp. +use std::ops::{Mul, BitOrAssign}; +use pow::num::{ToPrimitive, PrimInt}; -struct Graph {} +trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign{} +impl EdgeType for u32 {} +impl EdgeType for u64 {} + +/// An edge in the Cuckatoo/Cuckoo graph, reference to 2 endpoints +struct Edge +where + T: EdgeType, +{ + u: T, + v: T, +} + +/// An element of an adjencency list +struct Link +where + T: EdgeType, +{ + next: T, + to: T, +} + +struct Graph +where + T: EdgeType, +{ + /// Maximum number of edges + max_edges: T, + /// Maximum nodes + max_nodes: u64, + /// AKA halfedges, twice the number of edges + num_links: u64, + /// Adjacency links + links: Vec>, + /// Index into links array + adj_list: Vec, + // TODO: + // bitmap visited; + // TODO: + /// Maximum solutions + max_sols: u32, + // TODO: + // proof* sols + /// Number of solutions in the graph + num_sols: u32, +} + +impl Graph +where + T: EdgeType, +{ + /// Create a new graph with given parameters + pub fn new(max_edges: T, max_sols: u32) -> Graph { + Graph { + max_edges: max_edges, + max_nodes: 2 * max_edges.to_u64().unwrap(), + num_links: 0, + links: vec![], + adj_list: vec![], + max_sols: max_sols, + num_sols: 0, + } + } + + /// Add an edge to the graph + pub fn add_edge(&mut self, u: T, mut v: T) { + v |= self.max_edges; + self.num_links += 1; + let ulink = self.num_links; + // the two halfedges of an edge differ only in last bit + self.num_links += 1; + let vlink = self.num_links; + self.links[ulink as usize].next = self.adj_list[u.to_u64().unwrap() as usize]; + self.links[vlink as usize].next = self.adj_list[v.to_u64().unwrap() as usize]; + //TODO: Incomplete + //self.adj_list[u.to_u64().unwrap() as usize] = ulink; + //self.adj_list[v.to_u64().unwrap() as usize] = vlink; + + } +} + +/// Cuckoo solver context +struct CuckooContext +where + T: EdgeType +{ + siphash_keys: [u64; 4], + easiness: T, + graph: Graph, +} + +impl CuckooContext +where + T: EdgeType, +{ + /// New Solver context + pub fn new(header:&[u8], nonce: u32, easiness:T, max_edges: T, max_sols: u32) -> CuckooContext { + CuckooContext { + siphash_keys: [0; 4], + easiness: easiness, + graph: Graph::new(max_edges, max_sols), + } + } +} + +#[test] +fn cuckatoo() { + let ctx_u32 = CuckooContext::new(&[0u8;3], 0, 100u32, 10u32, 10); + let ctx_u64 = CuckooContext::new(&[0u8;3], 0, 100u64, 10u64, 10); +} diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index db38ce741e..8299e382bc 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -32,10 +32,12 @@ extern crate blake2_rfc as blake2; extern crate chrono; extern crate rand; extern crate serde; +extern crate num; extern crate grin_util as util; pub mod cuckoo; +mod cuckatoo; mod siphash; use chrono::prelude::{DateTime, NaiveDateTime, Utc}; From 480584eede622241ae83834116bb232e2256dfe6 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 19 Sep 2018 16:13:02 +0100 Subject: [PATCH 04/22] rustfmt --- core/src/pow/cuckatoo.rs | 21 +++++++++++++-------- core/src/pow/mod.rs | 4 ++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index d80bcfa432..e40e19c025 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -13,10 +13,10 @@ // limitations under the License. //! Implementation of Cuckatoo Cycle designed by John Tromp. -use std::ops::{Mul, BitOrAssign}; -use pow::num::{ToPrimitive, PrimInt}; +use pow::num::{PrimInt, ToPrimitive}; +use std::ops::{BitOrAssign, Mul}; -trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign{} +trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign {} impl EdgeType for u32 {} impl EdgeType for u64 {} @@ -93,14 +93,13 @@ where //TODO: Incomplete //self.adj_list[u.to_u64().unwrap() as usize] = ulink; //self.adj_list[v.to_u64().unwrap() as usize] = vlink; - } } /// Cuckoo solver context struct CuckooContext where - T: EdgeType + T: EdgeType, { siphash_keys: [u64; 4], easiness: T, @@ -112,7 +111,13 @@ where T: EdgeType, { /// New Solver context - pub fn new(header:&[u8], nonce: u32, easiness:T, max_edges: T, max_sols: u32) -> CuckooContext { + pub fn new( + header: &[u8], + nonce: u32, + easiness: T, + max_edges: T, + max_sols: u32, + ) -> CuckooContext { CuckooContext { siphash_keys: [0; 4], easiness: easiness, @@ -123,6 +128,6 @@ where #[test] fn cuckatoo() { - let ctx_u32 = CuckooContext::new(&[0u8;3], 0, 100u32, 10u32, 10); - let ctx_u64 = CuckooContext::new(&[0u8;3], 0, 100u64, 10u64, 10); + let ctx_u32 = CuckooContext::new(&[0u8; 3], 0, 100u32, 10u32, 10); + let ctx_u64 = CuckooContext::new(&[0u8; 3], 0, 100u64, 10u64, 10); } diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 8299e382bc..750539dc33 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -30,14 +30,14 @@ extern crate blake2_rfc as blake2; extern crate chrono; +extern crate num; extern crate rand; extern crate serde; -extern crate num; extern crate grin_util as util; -pub mod cuckoo; mod cuckatoo; +pub mod cuckoo; mod siphash; use chrono::prelude::{DateTime, NaiveDateTime, Utc}; From bb8349ffa97163458ab07f27e31f72f0d31ffac2 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Thu, 20 Sep 2018 15:56:19 +0100 Subject: [PATCH 05/22] data structures are in place, siphash key creation is consistent with @tromp --- core/src/pow/cuckatoo.rs | 126 ++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 20 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index e40e19c025..e942d98b94 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -1,4 +1,3 @@ -// Copyright 2018 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,8 +13,17 @@ //! Implementation of Cuckatoo Cycle designed by John Tromp. use pow::num::{PrimInt, ToPrimitive}; +use std::mem; use std::ops::{BitOrAssign, Mul}; +use blake2::blake2b::blake2b; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use croaring::Bitmap; +use std::io::Cursor; + +use core::Proof; +use util; + trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign {} impl EdgeType for u32 {} impl EdgeType for u64 {} @@ -52,15 +60,12 @@ where links: Vec>, /// Index into links array adj_list: Vec, - // TODO: - // bitmap visited; - // TODO: + /// + visited: Bitmap, /// Maximum solutions max_sols: u32, - // TODO: - // proof* sols - /// Number of solutions in the graph - num_sols: u32, + /// + solutions: Vec, } impl Graph @@ -69,14 +74,16 @@ where { /// Create a new graph with given parameters pub fn new(max_edges: T, max_sols: u32) -> Graph { + let max_nodes = 2 * max_edges.to_u64().unwrap(); Graph { max_edges: max_edges, - max_nodes: 2 * max_edges.to_u64().unwrap(), + max_nodes: max_nodes, num_links: 0, - links: vec![], - adj_list: vec![], + links: Vec::with_capacity(max_nodes as usize), + adj_list: Vec::with_capacity(max_nodes as usize), + visited: Bitmap::create(), max_sols: max_sols, - num_sols: 0, + solutions: vec![], } } @@ -94,6 +101,11 @@ where //self.adj_list[u.to_u64().unwrap() as usize] = ulink; //self.adj_list[v.to_u64().unwrap() as usize] = vlink; } + + pub fn byte_count(&self) -> u64 { + self.max_nodes * (mem::size_of::>() as u64 + mem::size_of::() as u64) + + (self.max_edges.to_u64().unwrap() / 32) * mem::size_of::() as u64 + } } /// Cuckoo solver context @@ -104,6 +116,7 @@ where siphash_keys: [u64; 4], easiness: T, graph: Graph, + proof_size: u8, } impl CuckooContext @@ -112,22 +125,95 @@ where { /// New Solver context pub fn new( - header: &[u8], - nonce: u32, - easiness: T, - max_edges: T, + edge_bits: u8, + proof_size: u8, + easiness_pct: u32, max_sols: u32, ) -> CuckooContext { + let num_edges = 1 << edge_bits; + let num_nodes = 2 * num_edges as u64; + let easiness = easiness_pct.to_u64().unwrap() * num_nodes / 100; CuckooContext { siphash_keys: [0; 4], - easiness: easiness, - graph: Graph::new(max_edges, max_sols), + easiness: T::from(easiness).unwrap(), + graph: Graph::new(T::from(num_edges).unwrap(), max_sols), + proof_size: proof_size, + } + } + + /// Extract siphash keys from header + pub fn create_keys(&mut self, header: Vec) { + let h = blake2b(32, &[], &header); + let hb = h.as_bytes(); + let mut rdr = Cursor::new(hb); + self.siphash_keys = [ + rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), + ]; + } + + /// Get a siphash key as a hex string (for display convenience) + pub fn sipkey_hex(&self, index: usize) -> String { + let mut rdr = vec![]; + rdr.write_u64::(self.siphash_keys[index]) + .unwrap(); + util::to_hex(rdr) + } + + /// Return number of bytes used by the graph + pub fn byte_count(&self) -> u64 { + self.graph.byte_count() + } + + /// Set the header and optional nonce in the last part of the header + pub fn set_header_nonce(&mut self, mut header: Vec, nonce: Option) { + let len = header.len(); + header.truncate(len - mem::size_of::()); + if let Some(n) = nonce { + header.write_u32::(n).unwrap(); } + self.create_keys(header); } + + /// Simple implementation of algorithm + pub fn find_cycles_simple(&mut self) {} } #[test] fn cuckatoo() { - let ctx_u32 = CuckooContext::new(&[0u8; 3], 0, 100u32, 10u32, 10); - let ctx_u64 = CuckooContext::new(&[0u8; 3], 0, 100u64, 10u64, 10); + let easiness_pct = 50; + let nonce = 1546569; + let range = 1; + let header = [0u8; 80].to_vec(); + let proof_size = 42; + let edge_bits = 15; + let max_sols = 4; + + println!( + "Looking for {}-cycle on cuckatoo{}(\"{}\",{}) with {}% edges", + proof_size, + edge_bits, + String::from_utf8(header.clone()).unwrap(), + nonce, + easiness_pct + ); + let mut ctx_u32 = CuckooContext::::new(edge_bits, proof_size, easiness_pct, max_sols); + let mut bytes = ctx_u32.byte_count(); + let mut unit = 0; + while bytes >= 10240 { + bytes >>= 10; + unit += 1; + } + println!("Using {}{}B memory", bytes, [' ', 'K', 'M', 'G', 'T'][unit]); + ctx_u32.set_header_nonce(header, Some(nonce)); + println!( + "Nonce {} k0 k1 k2 k3 {} {} {} {}", + nonce, + ctx_u32.sipkey_hex(0), + ctx_u32.sipkey_hex(1), + ctx_u32.sipkey_hex(2), + ctx_u32.sipkey_hex(3) + ); } From f7bc80a6ef4d4ca59f2d56ec8f8cf74321afe022 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Fri, 21 Sep 2018 14:12:49 +0100 Subject: [PATCH 06/22] solver in place, (not yet working) --- core/src/pow/cuckatoo.rs | 166 ++++++++++++++++++++++++++++++++------- 1 file changed, 137 insertions(+), 29 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index e942d98b94..408d4f918d 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -21,22 +21,14 @@ use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use croaring::Bitmap; use std::io::Cursor; -use core::Proof; +use pow::siphash::siphash24; +use pow::Proof; use util; trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign {} impl EdgeType for u32 {} impl EdgeType for u64 {} -/// An edge in the Cuckatoo/Cuckoo graph, reference to 2 endpoints -struct Edge -where - T: EdgeType, -{ - u: T, - v: T, -} - /// An element of an adjencency list struct Link where @@ -54,8 +46,6 @@ where max_edges: T, /// Maximum nodes max_nodes: u64, - /// AKA halfedges, twice the number of edges - num_links: u64, /// Adjacency links links: Vec>, /// Index into links array @@ -66,6 +56,10 @@ where max_sols: u32, /// solutions: Vec, + /// proof size + proof_size: usize, + /// define NIL type + nil: T, } impl Graph @@ -73,39 +67,130 @@ where T: EdgeType, { /// Create a new graph with given parameters - pub fn new(max_edges: T, max_sols: u32) -> Graph { + pub fn new(max_edges: T, max_sols: u32, proof_size: usize) -> Graph { let max_nodes = 2 * max_edges.to_u64().unwrap(); Graph { max_edges: max_edges, max_nodes: max_nodes, - num_links: 0, links: Vec::with_capacity(max_nodes as usize), - adj_list: Vec::with_capacity(max_nodes as usize), + adj_list: vec![T::from(T::max_value()).unwrap(); max_nodes as usize], visited: Bitmap::create(), max_sols: max_sols, solutions: vec![], + proof_size: proof_size, + nil: T::from(T::max_value()).unwrap(), } } /// Add an edge to the graph pub fn add_edge(&mut self, u: T, mut v: T) { v |= self.max_edges; - self.num_links += 1; - let ulink = self.num_links; - // the two halfedges of an edge differ only in last bit - self.num_links += 1; - let vlink = self.num_links; - self.links[ulink as usize].next = self.adj_list[u.to_u64().unwrap() as usize]; - self.links[vlink as usize].next = self.adj_list[v.to_u64().unwrap() as usize]; - //TODO: Incomplete - //self.adj_list[u.to_u64().unwrap() as usize] = ulink; - //self.adj_list[v.to_u64().unwrap() as usize] = vlink; + assert!(u != self.nil && v != self.nil); + let ulink = self.links.len(); + let vlink = self.links.len() + 1; + self.links.push(Link { + next: self.adj_list[u.to_u64().unwrap() as usize], + to: u, + }); + self.links.push(Link { + next: self.adj_list[v.to_u64().unwrap() as usize], + to: v, + }); + self.adj_list[u.to_u64().unwrap() as usize] = T::from(ulink).unwrap(); + self.adj_list[v.to_u64().unwrap() as usize] = T::from(vlink).unwrap(); + } + + // remove lnk from u's adjacency list + fn remove_adj(&mut self, u: T, lnk: T) { + let mut lp = self.adj_list[u.to_u64().unwrap() as usize]; + while lp != lnk { + assert!(lp != self.nil); + lp = self.links[lp.to_u64().unwrap() as usize].next; + } + lp = self.links[lnk.to_u64().unwrap() as usize].next; + self.links[lnk.to_u64().unwrap() as usize].to = self.nil; + } + + fn remove_link(&mut self, lnk: T) { + let u = self.links[lnk.to_u64().unwrap() as usize].to; + if u == self.nil { + return; + } + self.remove_adj(u, lnk); + if self.adj_list[u.to_u64().unwrap() as usize] == self.nil { + let mut rl = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; + while rl != self.nil { + self.links[rl.to_u64().unwrap() as usize].to = self.nil; + self.remove_link(rl ^ T::one()); + rl = self.links[rl.to_u64().unwrap() as usize].next; + } + self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize] = self.nil; + } } pub fn byte_count(&self) -> u64 { self.max_nodes * (mem::size_of::>() as u64 + mem::size_of::() as u64) + (self.max_edges.to_u64().unwrap() / 32) * mem::size_of::() as u64 } + + fn test_bit(&mut self, u: u64) -> bool { + self.visited.contains(u as u32) + } + + fn cycles_with_link(&mut self, len: u32, u: T, dest: T) { + if self.test_bit(u.to_u64().unwrap() >> 1) { + println!("Already visited"); + return; + } + assert!(u != self.nil); + if (u ^ T::one()) == dest { + if len == self.proof_size as u32 { + if self.solutions.len() < self.max_sols as usize { + // create next solution + self.solutions.push(Proof::zero(self.proof_size)); + } + return; + } else if len == self.proof_size as u32 { + return; + } + } + let mut au1 = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; + if au1 != self.nil { + self.visited.add((u.to_u64().unwrap() >> 1) as u32); + while au1 != self.nil { + let i = self.solutions.len() - 1; + // TODO: ??? + //self.solutions[i].nonces[len as usize] = au1.to_u64().unwrap() / 2; + let link_index = (au1 ^ T::one()).to_u64().unwrap() as usize; + let link = self.links[link_index].to; + /*if link == self.nil { + break; + }*/ + self.cycles_with_link(len + 1, link, dest); + au1 = self.links[au1.to_u64().unwrap() as usize].next; + } + self.visited.remove((u.to_u64().unwrap() >> 1) as u32); + } + } + + /// detect all cycles in the graph (up to proofsize) + pub fn cycles(&mut self) -> usize { + let mut n_links = self.links.len(); + self.solutions.push(Proof::zero(self.proof_size)); + while n_links > 0 { + let sol_index = self.solutions.len() - 1; + n_links -= 2; + let u = self.links[n_links].to; + let v = self.links[n_links + 1].to; + if u != self.nil && v != self.nil { + self.solutions[sol_index].nonces[0] = n_links as u64 / 2; + self.cycles_with_link(1, u, v); + self.remove_link(T::from(n_links).unwrap()); + self.remove_link(T::from(n_links + 1).unwrap()); + } + } + self.solutions.len() - 1 + } } /// Cuckoo solver context @@ -116,7 +201,8 @@ where siphash_keys: [u64; 4], easiness: T, graph: Graph, - proof_size: u8, + proof_size: usize, + edge_mask: T, } impl CuckooContext @@ -126,7 +212,7 @@ where /// New Solver context pub fn new( edge_bits: u8, - proof_size: u8, + proof_size: usize, easiness_pct: u32, max_sols: u32, ) -> CuckooContext { @@ -136,8 +222,9 @@ where CuckooContext { siphash_keys: [0; 4], easiness: T::from(easiness).unwrap(), - graph: Graph::new(T::from(num_edges).unwrap(), max_sols), + graph: Graph::new(T::from(num_edges).unwrap(), max_sols, proof_size), proof_size: proof_size, + edge_mask: T::from(num_edges - 1).unwrap(), } } @@ -177,8 +264,27 @@ where self.create_keys(header); } + /// Return siphash masked for type + pub fn sipnode(&self, edge: T, uorv: u64) -> T { + let hash_u64 = siphash24(self.siphash_keys, 2 * edge.to_u64().unwrap() + uorv); + let masked = hash_u64 & self.edge_mask.to_u64().unwrap(); + T::from(masked).unwrap() + } + /// Simple implementation of algorithm - pub fn find_cycles_simple(&mut self) {} + pub fn find_cycles_simple(&mut self, disp_callback: Option) -> usize { + for n in 0..self.easiness.to_u64().unwrap() { + let u = self.sipnode(T::from(n).unwrap(), 0); + let v = self.sipnode(T::from(n).unwrap(), 1); + self.graph + .add_edge(T::from(u).unwrap(), T::from(v).unwrap()); + if let Some(d) = disp_callback { + d(&self); + } + } + let n_sols = self.graph.cycles(); + n_sols + } } #[test] @@ -216,4 +322,6 @@ fn cuckatoo() { ctx_u32.sipkey_hex(2), ctx_u32.sipkey_hex(3) ); + let num_sols = ctx_u32.find_cycles_simple(None); + println!("Num sols found: {}", num_sols); } From 5dc77262f128293fad9babf2415e29cc232807ed Mon Sep 17 00:00:00 2001 From: yeastplume Date: Mon, 24 Sep 2018 16:49:03 +0100 Subject: [PATCH 07/22] cuckatoo test solver working with test nonce --- core/src/pow/cuckatoo.rs | 59 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 408d4f918d..56cc76d589 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -13,7 +13,7 @@ //! Implementation of Cuckatoo Cycle designed by John Tromp. use pow::num::{PrimInt, ToPrimitive}; -use std::mem; +use std::{mem, fmt}; use std::ops::{BitOrAssign, Mul}; use blake2::blake2b::blake2b; @@ -30,6 +30,7 @@ impl EdgeType for u32 {} impl EdgeType for u64 {} /// An element of an adjencency list +#[derive(Debug, Clone, Eq, PartialEq)] struct Link where T: EdgeType, @@ -38,6 +39,16 @@ where to: T, } +impl fmt::Display for Link +where + T: EdgeType +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(next: {}, to: {})", self.next.to_u64().unwrap(), self.to.to_u64().unwrap()) + } +} + + struct Graph where T: EdgeType, @@ -55,7 +66,7 @@ where /// Maximum solutions max_sols: u32, /// - solutions: Vec, + pub solutions: Vec, /// proof size proof_size: usize, /// define NIL type @@ -103,11 +114,15 @@ where // remove lnk from u's adjacency list fn remove_adj(&mut self, u: T, lnk: T) { let mut lp = self.adj_list[u.to_u64().unwrap() as usize]; + let mut moved = false; while lp != lnk { assert!(lp != self.nil); lp = self.links[lp.to_u64().unwrap() as usize].next; + moved = true; } - lp = self.links[lnk.to_u64().unwrap() as usize].next; + if !moved { + self.adj_list[u.to_u64().unwrap() as usize] = self.links[lnk.to_u64().unwrap() as usize].next; + } self.links[lnk.to_u64().unwrap() as usize].to = self.nil; } @@ -138,38 +153,36 @@ where } fn cycles_with_link(&mut self, len: u32, u: T, dest: T) { - if self.test_bit(u.to_u64().unwrap() >> 1) { - println!("Already visited"); + if self.test_bit((u >> 1).to_u64().unwrap()) { return; } assert!(u != self.nil); if (u ^ T::one()) == dest { + println!("{}-cycle found", len); if len == self.proof_size as u32 { if self.solutions.len() < self.max_sols as usize { // create next solution self.solutions.push(Proof::zero(self.proof_size)); } return; - } else if len == self.proof_size as u32 { + } + } else if len == self.proof_size as u32 { return; - } } let mut au1 = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; if au1 != self.nil { - self.visited.add((u.to_u64().unwrap() >> 1) as u32); + self.visited.add((u >> 1).to_u64().unwrap() as u32); while au1 != self.nil { let i = self.solutions.len() - 1; - // TODO: ??? - //self.solutions[i].nonces[len as usize] = au1.to_u64().unwrap() / 2; + self.solutions[i].nonces[len as usize] = au1.to_u64().unwrap() / 2; let link_index = (au1 ^ T::one()).to_u64().unwrap() as usize; let link = self.links[link_index].to; - /*if link == self.nil { - break; - }*/ - self.cycles_with_link(len + 1, link, dest); + if link != self.nil { + self.cycles_with_link(len + 1, link, dest); + } au1 = self.links[au1.to_u64().unwrap() as usize].next; } - self.visited.remove((u.to_u64().unwrap() >> 1) as u32); + self.visited.remove((u >> 1).to_u64().unwrap() as u32); } } @@ -200,7 +213,7 @@ where { siphash_keys: [u64; 4], easiness: T, - graph: Graph, + pub graph: Graph, proof_size: usize, edge_mask: T, } @@ -234,17 +247,17 @@ where let hb = h.as_bytes(); let mut rdr = Cursor::new(hb); self.siphash_keys = [ - rdr.read_u64::().unwrap(), - rdr.read_u64::().unwrap(), - rdr.read_u64::().unwrap(), - rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), + rdr.read_u64::().unwrap(), ]; } /// Get a siphash key as a hex string (for display convenience) pub fn sipkey_hex(&self, index: usize) -> String { let mut rdr = vec![]; - rdr.write_u64::(self.siphash_keys[index]) + rdr.write_u64::(self.siphash_keys[index]) .unwrap(); util::to_hex(rdr) } @@ -324,4 +337,8 @@ fn cuckatoo() { ); let num_sols = ctx_u32.find_cycles_simple(None); println!("Num sols found: {}", num_sols); + for i in 0..num_sols { + let sol = ctx_u32.graph.solutions[i].clone(); + println!("{:?}", sol); + } } From 06bfef6d83d341392260ff741e36b5f6f665c7d7 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Tue, 25 Sep 2018 09:13:11 +0100 Subject: [PATCH 08/22] rustfmt --- core/src/pow/cuckatoo.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 56cc76d589..069bce3bfd 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -13,8 +13,8 @@ //! Implementation of Cuckatoo Cycle designed by John Tromp. use pow::num::{PrimInt, ToPrimitive}; -use std::{mem, fmt}; use std::ops::{BitOrAssign, Mul}; +use std::{fmt, mem}; use blake2::blake2b::blake2b; use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -39,16 +39,20 @@ where to: T, } -impl fmt::Display for Link +impl fmt::Display for Link where - T: EdgeType + T: EdgeType, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "(next: {}, to: {})", self.next.to_u64().unwrap(), self.to.to_u64().unwrap()) + write!( + f, + "(next: {}, to: {})", + self.next.to_u64().unwrap(), + self.to.to_u64().unwrap() + ) } } - struct Graph where T: EdgeType, @@ -121,8 +125,9 @@ where moved = true; } if !moved { - self.adj_list[u.to_u64().unwrap() as usize] = self.links[lnk.to_u64().unwrap() as usize].next; - } + self.adj_list[u.to_u64().unwrap() as usize] = + self.links[lnk.to_u64().unwrap() as usize].next; + } self.links[lnk.to_u64().unwrap() as usize].to = self.nil; } @@ -165,9 +170,9 @@ where self.solutions.push(Proof::zero(self.proof_size)); } return; - } + } } else if len == self.proof_size as u32 { - return; + return; } let mut au1 = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; if au1 != self.nil { From dcf91b370cd48f38abef1292c6fe7a3997fd2b74 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Tue, 25 Sep 2018 10:25:22 +0100 Subject: [PATCH 09/22] update solver to remove adjacency list removals --- core/src/pow/cuckatoo.rs | 78 +++++++++------------------------------- 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 069bce3bfd..8de204c93e 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -87,11 +87,11 @@ where Graph { max_edges: max_edges, max_nodes: max_nodes, - links: Vec::with_capacity(max_nodes as usize), - adj_list: vec![T::from(T::max_value()).unwrap(); max_nodes as usize], + links: Vec::with_capacity(2 * max_nodes as usize), + adj_list: vec![T::from(T::max_value()).unwrap(); 2 * max_nodes as usize], visited: Bitmap::create(), max_sols: max_sols, - solutions: vec![], + solutions: vec![Proof::zero(proof_size); 1], proof_size: proof_size, nil: T::from(T::max_value()).unwrap(), } @@ -99,10 +99,19 @@ where /// Add an edge to the graph pub fn add_edge(&mut self, u: T, mut v: T) { - v |= self.max_edges; - assert!(u != self.nil && v != self.nil); + assert!(u < T::from(self.max_nodes).unwrap()); + assert!(v < T::from(self.max_nodes).unwrap()); + v = v + T::from(self.max_nodes).unwrap(); + let adj_u = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; + let adj_v = self.adj_list[(v ^ T::one()).to_u64().unwrap() as usize]; + if adj_u != self.nil && adj_v != self.nil { + let sol_index = self.solutions.len() - 1; + self.solutions[sol_index].nonces[0] = self.links.len() as u64 / 2; + self.cycles_with_link(1, u, v); + } let ulink = self.links.len(); let vlink = self.links.len() + 1; + assert!(T::from(vlink).unwrap() != self.nil); self.links.push(Link { next: self.adj_list[u.to_u64().unwrap() as usize], to: u, @@ -115,42 +124,9 @@ where self.adj_list[v.to_u64().unwrap() as usize] = T::from(vlink).unwrap(); } - // remove lnk from u's adjacency list - fn remove_adj(&mut self, u: T, lnk: T) { - let mut lp = self.adj_list[u.to_u64().unwrap() as usize]; - let mut moved = false; - while lp != lnk { - assert!(lp != self.nil); - lp = self.links[lp.to_u64().unwrap() as usize].next; - moved = true; - } - if !moved { - self.adj_list[u.to_u64().unwrap() as usize] = - self.links[lnk.to_u64().unwrap() as usize].next; - } - self.links[lnk.to_u64().unwrap() as usize].to = self.nil; - } - - fn remove_link(&mut self, lnk: T) { - let u = self.links[lnk.to_u64().unwrap() as usize].to; - if u == self.nil { - return; - } - self.remove_adj(u, lnk); - if self.adj_list[u.to_u64().unwrap() as usize] == self.nil { - let mut rl = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; - while rl != self.nil { - self.links[rl.to_u64().unwrap() as usize].to = self.nil; - self.remove_link(rl ^ T::one()); - rl = self.links[rl.to_u64().unwrap() as usize].next; - } - self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize] = self.nil; - } - } - pub fn byte_count(&self) -> u64 { - self.max_nodes * (mem::size_of::>() as u64 + mem::size_of::() as u64) - + (self.max_edges.to_u64().unwrap() / 32) * mem::size_of::() as u64 + 2 * self.max_edges.to_u64().unwrap() * mem::size_of::>() as u64 + + mem::size_of::() as u64 * 2 * self.max_nodes } fn test_bit(&mut self, u: u64) -> bool { @@ -161,7 +137,6 @@ where if self.test_bit((u >> 1).to_u64().unwrap()) { return; } - assert!(u != self.nil); if (u ^ T::one()) == dest { println!("{}-cycle found", len); if len == self.proof_size as u32 { @@ -190,25 +165,6 @@ where self.visited.remove((u >> 1).to_u64().unwrap() as u32); } } - - /// detect all cycles in the graph (up to proofsize) - pub fn cycles(&mut self) -> usize { - let mut n_links = self.links.len(); - self.solutions.push(Proof::zero(self.proof_size)); - while n_links > 0 { - let sol_index = self.solutions.len() - 1; - n_links -= 2; - let u = self.links[n_links].to; - let v = self.links[n_links + 1].to; - if u != self.nil && v != self.nil { - self.solutions[sol_index].nonces[0] = n_links as u64 / 2; - self.cycles_with_link(1, u, v); - self.remove_link(T::from(n_links).unwrap()); - self.remove_link(T::from(n_links + 1).unwrap()); - } - } - self.solutions.len() - 1 - } } /// Cuckoo solver context @@ -300,7 +256,7 @@ where d(&self); } } - let n_sols = self.graph.cycles(); + let n_sols = self.graph.solutions.len() - 1; n_sols } } From c30cd80e8ddf5aa3ccb7d9a08c839f1e1fc59f61 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Tue, 25 Sep 2018 12:27:10 +0100 Subject: [PATCH 10/22] verifier functioning --- core/src/pow/cuckatoo.rs | 77 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 8de204c93e..80a9042a80 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -177,6 +177,7 @@ where pub graph: Graph, proof_size: usize, edge_mask: T, + num_edges: T, } impl CuckooContext @@ -199,6 +200,7 @@ where graph: Graph::new(T::from(num_edges).unwrap(), max_sols, proof_size), proof_size: proof_size, edge_mask: T::from(num_edges - 1).unwrap(), + num_edges: T::from(num_edges).unwrap(), } } @@ -257,8 +259,83 @@ where } } let n_sols = self.graph.solutions.len() - 1; + for s in &mut self.graph.solutions { + s.nonces.sort(); + } + for s in &self.graph.solutions { + println!("Verification: {}", self.verify(&s)); + } n_sols } + + /// Verify that given edges are ascending and form a cycle in a header-generated + /// graph + pub fn verify(&self, proof: &Proof) -> bool { + let nonces = &proof.nonces; + let mut uvs = vec![0u64; 2 * proof.proof_size()]; + let mut xor0:u64 = (self.proof_size as u64 / 2) & 1; + let mut xor1:u64 = xor0; + + for n in 0..proof.proof_size() { + if nonces[n] > self.edge_mask.to_u64().unwrap() { + // POW TOO BIG + println!("TOO BIG"); + return false; + } + if n > 0 && nonces[n] <= nonces[n-1] { + // POW TOO SMALL + println!("TOO SMALL"); + return false; + } + uvs[2 * n] = self.sipnode(T::from(nonces[n]).unwrap(), 0).to_u64().unwrap(); + uvs[2 * n + 1] = self.sipnode(T::from(nonces[n]).unwrap(), 1).to_u64().unwrap(); + xor0 ^= uvs[2 * n]; + xor1 ^= uvs[2 * n + 1]; + } + if xor0 | xor1 != 0 { + // POW NON MATCHING + println!("NON MATCHING"); + return false; + } + let mut n = 0; + let mut i = 0; + let mut j; + loop { // follow cycle + j = i; + let mut k = j; + loop { + k = (k + 2) % (2 * self.proof_size); + if k == i { + break; + } + if uvs[k] >> 1 == uvs[i] >> 1 { // find other edge endpoint matching one at i + if j != i { + // POW_BRANCH + println!("POW_BRANCH"); // already found one before + return false; + } + j = k; + } + } + if j == i || uvs[j] == uvs[i] { + // POW_DEAD_END + println!("POW_DEAD_END"); + return false; + } + i = j ^ 1; + n += 1; + if i == 0 { + break; + } + } + if n == self.proof_size { + true + } else { + //POW_SHORT_CYCLE + println!("POW_SHORT_CYCLE"); + false + } + } } #[test] From 3b59da2fe49d4b445b3839159f0f6880fb2b4dac Mon Sep 17 00:00:00 2001 From: yeastplume Date: Tue, 25 Sep 2018 12:27:32 +0100 Subject: [PATCH 11/22] rustfmt --- core/src/pow/cuckatoo.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 80a9042a80..a22c54a269 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -273,8 +273,8 @@ where pub fn verify(&self, proof: &Proof) -> bool { let nonces = &proof.nonces; let mut uvs = vec![0u64; 2 * proof.proof_size()]; - let mut xor0:u64 = (self.proof_size as u64 / 2) & 1; - let mut xor1:u64 = xor0; + let mut xor0: u64 = (self.proof_size as u64 / 2) & 1; + let mut xor1: u64 = xor0; for n in 0..proof.proof_size() { if nonces[n] > self.edge_mask.to_u64().unwrap() { @@ -282,13 +282,19 @@ where println!("TOO BIG"); return false; } - if n > 0 && nonces[n] <= nonces[n-1] { + if n > 0 && nonces[n] <= nonces[n - 1] { // POW TOO SMALL println!("TOO SMALL"); return false; } - uvs[2 * n] = self.sipnode(T::from(nonces[n]).unwrap(), 0).to_u64().unwrap(); - uvs[2 * n + 1] = self.sipnode(T::from(nonces[n]).unwrap(), 1).to_u64().unwrap(); + uvs[2 * n] = self + .sipnode(T::from(nonces[n]).unwrap(), 0) + .to_u64() + .unwrap(); + uvs[2 * n + 1] = self + .sipnode(T::from(nonces[n]).unwrap(), 1) + .to_u64() + .unwrap(); xor0 ^= uvs[2 * n]; xor1 ^= uvs[2 * n + 1]; } @@ -300,7 +306,8 @@ where let mut n = 0; let mut i = 0; let mut j; - loop { // follow cycle + loop { + // follow cycle j = i; let mut k = j; loop { @@ -308,7 +315,8 @@ where if k == i { break; } - if uvs[k] >> 1 == uvs[i] >> 1 { // find other edge endpoint matching one at i + if uvs[k] >> 1 == uvs[i] >> 1 { + // find other edge endpoint matching one at i if j != i { // POW_BRANCH println!("POW_BRANCH"); // already found one before From 38d957499d5a8315e3c6e266331a9df8ec72c5a3 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Tue, 25 Sep 2018 15:47:15 +0100 Subject: [PATCH 12/22] Proper error handing in Cuckatoo module, couple of tests --- core/src/pow/cuckatoo.rs | 389 ++++++++++++++++++++++++--------------- core/src/pow/error.rs | 94 ++++++++++ core/src/pow/mod.rs | 3 +- 3 files changed, 339 insertions(+), 147 deletions(-) create mode 100644 core/src/pow/error.rs diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index a22c54a269..756d963349 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -21,11 +21,13 @@ use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use croaring::Bitmap; use std::io::Cursor; +use pow::error::{Error, ErrorKind}; use pow::siphash::siphash24; use pow::Proof; use util; -trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign {} +/// Operations needed for edge type (going to be u32 or u64) +pub trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign {} impl EdgeType for u32 {} impl EdgeType for u64 {} @@ -47,8 +49,8 @@ where write!( f, "(next: {}, to: {})", - self.next.to_u64().unwrap(), - self.to.to_u64().unwrap() + self.next.to_u64().unwrap_or(0), + self.to.to_u64().unwrap_or(0) ) } } @@ -82,105 +84,128 @@ where T: EdgeType, { /// Create a new graph with given parameters - pub fn new(max_edges: T, max_sols: u32, proof_size: usize) -> Graph { - let max_nodes = 2 * max_edges.to_u64().unwrap(); - Graph { + pub fn new(max_edges: T, max_sols: u32, proof_size: usize) -> Result, Error> { + let max_nodes = 2 * max_edges.to_u64().ok_or(ErrorKind::IntegerCast)?; + Ok(Graph { max_edges: max_edges, max_nodes: max_nodes, - links: Vec::with_capacity(2 * max_nodes as usize), - adj_list: vec![T::from(T::max_value()).unwrap(); 2 * max_nodes as usize], + links: vec![], + adj_list: vec![], visited: Bitmap::create(), max_sols: max_sols, - solutions: vec![Proof::zero(proof_size); 1], + solutions: vec![], proof_size: proof_size, - nil: T::from(T::max_value()).unwrap(), - } + nil: T::from(T::max_value()).ok_or(ErrorKind::IntegerCast)?, + }) + } + + pub fn reset(&mut self) -> Result<(), Error> { + //TODO: Can be optimised + self.links = Vec::with_capacity(2 * self.max_nodes as usize); + self.adj_list = vec![ + T::from(T::max_value()).ok_or(ErrorKind::IntegerCast)?; + 2 * self.max_nodes as usize + ]; + self.solutions = vec![Proof::zero(self.proof_size); 1]; + self.visited = Bitmap::create(); + Ok(()) + } + + pub fn byte_count(&self) -> Result { + Ok(2 + * self.max_edges.to_u64().ok_or(ErrorKind::IntegerCast)? + * mem::size_of::>() as u64 + mem::size_of::() as u64 * 2 * self.max_nodes) } /// Add an edge to the graph - pub fn add_edge(&mut self, u: T, mut v: T) { - assert!(u < T::from(self.max_nodes).unwrap()); - assert!(v < T::from(self.max_nodes).unwrap()); - v = v + T::from(self.max_nodes).unwrap(); - let adj_u = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; - let adj_v = self.adj_list[(v ^ T::one()).to_u64().unwrap() as usize]; + pub fn add_edge(&mut self, u: T, mut v: T) -> Result<(), Error> { + let max_nodes_t = T::from(self.max_nodes).ok_or(ErrorKind::IntegerCast)?; + if u >= max_nodes_t || v >= max_nodes_t { + return Err(ErrorKind::EdgeAddition)?; + } + v = v + T::from(self.max_nodes).ok_or(ErrorKind::IntegerCast)?; + let adj_u = self.adj_list[(u ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + let adj_v = self.adj_list[(v ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; if adj_u != self.nil && adj_v != self.nil { let sol_index = self.solutions.len() - 1; self.solutions[sol_index].nonces[0] = self.links.len() as u64 / 2; - self.cycles_with_link(1, u, v); + self.cycles_with_link(1, u, v)?; } let ulink = self.links.len(); let vlink = self.links.len() + 1; - assert!(T::from(vlink).unwrap() != self.nil); + if T::from(vlink).ok_or(ErrorKind::IntegerCast)? == self.nil { + return Err(ErrorKind::EdgeAddition)?; + } self.links.push(Link { - next: self.adj_list[u.to_u64().unwrap() as usize], + next: self.adj_list[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize], to: u, }); self.links.push(Link { - next: self.adj_list[v.to_u64().unwrap() as usize], + next: self.adj_list[v.to_u64().ok_or(ErrorKind::IntegerCast)? as usize], to: v, }); - self.adj_list[u.to_u64().unwrap() as usize] = T::from(ulink).unwrap(); - self.adj_list[v.to_u64().unwrap() as usize] = T::from(vlink).unwrap(); - } - - pub fn byte_count(&self) -> u64 { - 2 * self.max_edges.to_u64().unwrap() * mem::size_of::>() as u64 - + mem::size_of::() as u64 * 2 * self.max_nodes + self.adj_list[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = + T::from(ulink).ok_or(ErrorKind::IntegerCast)?; + self.adj_list[v.to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = + T::from(vlink).ok_or(ErrorKind::IntegerCast)?; + Ok(()) } fn test_bit(&mut self, u: u64) -> bool { self.visited.contains(u as u32) } - fn cycles_with_link(&mut self, len: u32, u: T, dest: T) { - if self.test_bit((u >> 1).to_u64().unwrap()) { - return; + fn cycles_with_link(&mut self, len: u32, u: T, dest: T) -> Result<(), Error> { + if self.test_bit((u >> 1).to_u64().ok_or(ErrorKind::IntegerCast)?) { + return Ok(()); } if (u ^ T::one()) == dest { - println!("{}-cycle found", len); if len == self.proof_size as u32 { if self.solutions.len() < self.max_sols as usize { // create next solution self.solutions.push(Proof::zero(self.proof_size)); } - return; + return Ok(()); } } else if len == self.proof_size as u32 { - return; + return Ok(()); } - let mut au1 = self.adj_list[(u ^ T::one()).to_u64().unwrap() as usize]; + let mut au1 = + self.adj_list[(u ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; if au1 != self.nil { - self.visited.add((u >> 1).to_u64().unwrap() as u32); + self.visited + .add((u >> 1).to_u64().ok_or(ErrorKind::IntegerCast)? as u32); while au1 != self.nil { let i = self.solutions.len() - 1; - self.solutions[i].nonces[len as usize] = au1.to_u64().unwrap() / 2; - let link_index = (au1 ^ T::one()).to_u64().unwrap() as usize; + self.solutions[i].nonces[len as usize] = + au1.to_u64().ok_or(ErrorKind::IntegerCast)? / 2; + let link_index = (au1 ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize; let link = self.links[link_index].to; if link != self.nil { - self.cycles_with_link(len + 1, link, dest); + self.cycles_with_link(len + 1, link, dest)?; } - au1 = self.links[au1.to_u64().unwrap() as usize].next; + au1 = self.links[au1.to_u64().ok_or(ErrorKind::IntegerCast)? as usize].next; } - self.visited.remove((u >> 1).to_u64().unwrap() as u32); + self.visited + .remove((u >> 1).to_u64().ok_or(ErrorKind::IntegerCast)? as u32); } + Ok(()) } } -/// Cuckoo solver context -struct CuckooContext +/// Cuckatoo solver context +pub struct CuckatooContext where T: EdgeType, { siphash_keys: [u64; 4], easiness: T, - pub graph: Graph, + graph: Graph, proof_size: usize, edge_mask: T, - num_edges: T, } -impl CuckooContext +impl CuckatooContext where T: EdgeType, { @@ -190,118 +215,125 @@ where proof_size: usize, easiness_pct: u32, max_sols: u32, - ) -> CuckooContext { + ) -> Result, Error> { let num_edges = 1 << edge_bits; let num_nodes = 2 * num_edges as u64; - let easiness = easiness_pct.to_u64().unwrap() * num_nodes / 100; - CuckooContext { + let easiness = easiness_pct.to_u64().ok_or(ErrorKind::IntegerCast)? * num_nodes / 100; + Ok(CuckatooContext { siphash_keys: [0; 4], - easiness: T::from(easiness).unwrap(), - graph: Graph::new(T::from(num_edges).unwrap(), max_sols, proof_size), + easiness: T::from(easiness).ok_or(ErrorKind::IntegerCast)?, + graph: Graph::new( + T::from(num_edges).ok_or(ErrorKind::IntegerCast)?, + max_sols, + proof_size, + )?, proof_size: proof_size, - edge_mask: T::from(num_edges - 1).unwrap(), - num_edges: T::from(num_edges).unwrap(), - } + edge_mask: T::from(num_edges - 1).ok_or(ErrorKind::IntegerCast)?, + }) } /// Extract siphash keys from header - pub fn create_keys(&mut self, header: Vec) { + pub fn create_keys(&mut self, header: Vec) -> Result<(), Error> { let h = blake2b(32, &[], &header); let hb = h.as_bytes(); let mut rdr = Cursor::new(hb); self.siphash_keys = [ - rdr.read_u64::().unwrap(), - rdr.read_u64::().unwrap(), - rdr.read_u64::().unwrap(), - rdr.read_u64::().unwrap(), + rdr.read_u64::()?, + rdr.read_u64::()?, + rdr.read_u64::()?, + rdr.read_u64::()?, ]; + Ok(()) } /// Get a siphash key as a hex string (for display convenience) - pub fn sipkey_hex(&self, index: usize) -> String { + pub fn sipkey_hex(&self, index: usize) -> Result { let mut rdr = vec![]; - rdr.write_u64::(self.siphash_keys[index]) - .unwrap(); - util::to_hex(rdr) + rdr.write_u64::(self.siphash_keys[index])?; + Ok(util::to_hex(rdr)) } /// Return number of bytes used by the graph - pub fn byte_count(&self) -> u64 { + pub fn byte_count(&self) -> Result { self.graph.byte_count() } /// Set the header and optional nonce in the last part of the header - pub fn set_header_nonce(&mut self, mut header: Vec, nonce: Option) { + pub fn set_header_nonce( + &mut self, + mut header: Vec, + nonce: Option, + ) -> Result<(), Error> { let len = header.len(); header.truncate(len - mem::size_of::()); if let Some(n) = nonce { - header.write_u32::(n).unwrap(); + header.write_u32::(n)?; } - self.create_keys(header); + self.create_keys(header)?; + self.graph.reset()?; + Ok(()) } /// Return siphash masked for type - pub fn sipnode(&self, edge: T, uorv: u64) -> T { - let hash_u64 = siphash24(self.siphash_keys, 2 * edge.to_u64().unwrap() + uorv); - let masked = hash_u64 & self.edge_mask.to_u64().unwrap(); - T::from(masked).unwrap() + pub fn sipnode(&self, edge: T, uorv: u64) -> Result { + let hash_u64 = siphash24( + self.siphash_keys, + 2 * edge.to_u64().ok_or(ErrorKind::IntegerCast)? + uorv, + ); + let masked = hash_u64 & self.edge_mask.to_u64().ok_or(ErrorKind::IntegerCast)?; + Ok(T::from(masked).ok_or(ErrorKind::IntegerCast)?) } /// Simple implementation of algorithm - pub fn find_cycles_simple(&mut self, disp_callback: Option) -> usize { - for n in 0..self.easiness.to_u64().unwrap() { - let u = self.sipnode(T::from(n).unwrap(), 0); - let v = self.sipnode(T::from(n).unwrap(), 1); - self.graph - .add_edge(T::from(u).unwrap(), T::from(v).unwrap()); - if let Some(d) = disp_callback { - d(&self); - } + pub fn find_cycles_simple(&mut self) -> Result, Error> { + for n in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? { + let u = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 0)?; + let v = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 1)?; + self.graph.add_edge( + T::from(u).ok_or(ErrorKind::IntegerCast)?, + T::from(v).ok_or(ErrorKind::IntegerCast)?, + )?; } - let n_sols = self.graph.solutions.len() - 1; + self.graph.solutions.pop(); for s in &mut self.graph.solutions { s.nonces.sort(); } for s in &self.graph.solutions { - println!("Verification: {}", self.verify(&s)); + self.verify(&s)?; } - n_sols + Ok(self.graph.solutions.clone()) } /// Verify that given edges are ascending and form a cycle in a header-generated /// graph - pub fn verify(&self, proof: &Proof) -> bool { + pub fn verify(&self, proof: &Proof) -> Result<(), Error> { let nonces = &proof.nonces; let mut uvs = vec![0u64; 2 * proof.proof_size()]; let mut xor0: u64 = (self.proof_size as u64 / 2) & 1; let mut xor1: u64 = xor0; for n in 0..proof.proof_size() { - if nonces[n] > self.edge_mask.to_u64().unwrap() { - // POW TOO BIG - println!("TOO BIG"); - return false; + if nonces[n] > self.edge_mask.to_u64().ok_or(ErrorKind::IntegerCast)? { + return Err(ErrorKind::Verification("edge too big".to_owned()))?; } if n > 0 && nonces[n] <= nonces[n - 1] { - // POW TOO SMALL - println!("TOO SMALL"); - return false; + return Err(ErrorKind::Verification("edges not ascending".to_owned()))?; } uvs[2 * n] = self - .sipnode(T::from(nonces[n]).unwrap(), 0) + .sipnode(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 0)? .to_u64() - .unwrap(); + .ok_or(ErrorKind::IntegerCast)?; uvs[2 * n + 1] = self - .sipnode(T::from(nonces[n]).unwrap(), 1) + .sipnode(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 1)? .to_u64() - .unwrap(); + .ok_or(ErrorKind::IntegerCast)?; xor0 ^= uvs[2 * n]; xor1 ^= uvs[2 * n + 1]; } if xor0 | xor1 != 0 { - // POW NON MATCHING - println!("NON MATCHING"); - return false; + return Err(ErrorKind::Verification( + "endpoints don't match up".to_owned(), + ))?; } let mut n = 0; let mut i = 0; @@ -318,17 +350,13 @@ where if uvs[k] >> 1 == uvs[i] >> 1 { // find other edge endpoint matching one at i if j != i { - // POW_BRANCH - println!("POW_BRANCH"); // already found one before - return false; + return Err(ErrorKind::Verification("branch in cycle".to_owned()))?; } j = k; } } if j == i || uvs[j] == uvs[i] { - // POW_DEAD_END - println!("POW_DEAD_END"); - return false; + return Err(ErrorKind::Verification("cycle dead ends".to_owned()))?; } i = j ^ 1; n += 1; @@ -337,54 +365,123 @@ where } } if n == self.proof_size { - true + Ok(()) } else { - //POW_SHORT_CYCLE - println!("POW_SHORT_CYCLE"); - false + Err(ErrorKind::Verification("cycle too short".to_owned()))? } } } -#[test] -fn cuckatoo() { - let easiness_pct = 50; - let nonce = 1546569; - let range = 1; - let header = [0u8; 80].to_vec(); - let proof_size = 42; - let edge_bits = 15; - let max_sols = 4; +#[cfg(test)] +mod test { + use super::*; - println!( - "Looking for {}-cycle on cuckatoo{}(\"{}\",{}) with {}% edges", - proof_size, - edge_bits, - String::from_utf8(header.clone()).unwrap(), - nonce, - easiness_pct - ); - let mut ctx_u32 = CuckooContext::::new(edge_bits, proof_size, easiness_pct, max_sols); - let mut bytes = ctx_u32.byte_count(); - let mut unit = 0; - while bytes >= 10240 { - bytes >>= 10; - unit += 1; + #[test] + fn cuckatoo() { + let ret = basic_solve(); + if let Err(r) = ret { + panic!("basic_solve: Error: {}", r); + } + let ret = test_mine_32(); + if let Err(r) = ret { + panic!("test_mine_32: Error: {}", r); + } + let sols_32 = ret.unwrap(); + let ret = test_mine_64(); + if let Err(r) = ret { + panic!("test_mine_64: Error: {}", r); + } + let sols_64 = ret.unwrap(); + assert_eq!(sols_64, sols_32); } - println!("Using {}{}B memory", bytes, [' ', 'K', 'M', 'G', 'T'][unit]); - ctx_u32.set_header_nonce(header, Some(nonce)); - println!( - "Nonce {} k0 k1 k2 k3 {} {} {} {}", - nonce, - ctx_u32.sipkey_hex(0), - ctx_u32.sipkey_hex(1), - ctx_u32.sipkey_hex(2), - ctx_u32.sipkey_hex(3) - ); - let num_sols = ctx_u32.find_cycles_simple(None); - println!("Num sols found: {}", num_sols); - for i in 0..num_sols { - let sol = ctx_u32.graph.solutions[i].clone(); - println!("{:?}", sol); + + fn test_mine_32() -> Result { + println!("Test mine 32"); + let easiness_pct = 50; + let nonce = 0; + let range = 30; + let header = [0u8; 80].to_vec(); + let proof_size = 42; + let edge_bits = 15; + let max_sols = 4; + let mut ctx_u32 = + CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; + let mut num_sols = 0; + for r in 0..range { + ctx_u32.set_header_nonce(header.clone(), Some(nonce + r))?; + let sols = ctx_u32.find_cycles_simple()?; + for s in sols { + println!("Solution found: {:?}", s); + num_sols += 1; + } + } + Ok(num_sols) + } + + fn test_mine_64() -> Result { + println!("Test mine 64"); + let easiness_pct = 50; + let nonce = 0; + let range = 30; + let header = [0u8; 80].to_vec(); + let proof_size = 42; + let edge_bits = 15; + let max_sols = 4; + let mut ctx_u64 = + CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; + let mut num_sols = 0; + for r in 0..range { + ctx_u64.set_header_nonce(header.clone(), Some(nonce + r))?; + let sols = ctx_u64.find_cycles_simple()?; + for s in sols { + println!("Solution found: {:?}", s); + num_sols += 1; + } + } + Ok(num_sols) + } + + fn basic_solve() -> Result<(), Error> { + let easiness_pct = 50; + let nonce = 1546569; + let _range = 1; + let header = [0u8; 80].to_vec(); + let proof_size = 42; + let edge_bits = 15; + let max_sols = 4; + + println!( + "Looking for {}-cycle on cuckatoo{}(\"{}\",{}) with {}% edges", + proof_size, + edge_bits, + String::from_utf8(header.clone()).unwrap(), + nonce, + easiness_pct + ); + let mut ctx_u32 = + CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; + let mut bytes = ctx_u32.byte_count()?; + let mut unit = 0; + while bytes >= 10240 { + bytes >>= 10; + unit += 1; + } + println!("Using {}{}B memory", bytes, [' ', 'K', 'M', 'G', 'T'][unit]); + ctx_u32.set_header_nonce(header, Some(nonce))?; + println!( + "Nonce {} k0 k1 k2 k3 {} {} {} {}", + nonce, + ctx_u32.sipkey_hex(0)?, + ctx_u32.sipkey_hex(1)?, + ctx_u32.sipkey_hex(2)?, + ctx_u32.sipkey_hex(3)? + ); + let sols = ctx_u32.find_cycles_simple()?; + // We know this nonce has 2 solutions + assert_eq!(sols.len(), 2); + for s in sols { + println!("{:?}", s); + } + Ok(()) } } diff --git a/core/src/pow/error.rs b/core/src/pow/error.rs new file mode 100644 index 0000000000..9f02ad1a5d --- /dev/null +++ b/core/src/pow/error.rs @@ -0,0 +1,94 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Cuckatoo specific errors +use failure::{Backtrace, Context, Fail}; +use std::fmt::{self, Display}; +use std::io; + +/// Cuckatoo solver or validation error +#[derive(Debug)] +pub struct Error { + inner: Context, +} + +#[derive(Clone, Debug, Eq, Fail, PartialEq)] +/// Libwallet error types +pub enum ErrorKind { + /// Verification error + #[fail(display = "Verification Error: {}", _0)] + Verification(String), + /// Failure to cast from/to generic integer type + #[fail(display = "IntegerCast")] + IntegerCast, + /// IO Error + #[fail(display = "IO Error")] + IOError, + /// Unexpected Edge Error + #[fail(display = "Edge Addition Error")] + EdgeAddition, +} + +impl Fail for Error { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl Error { + /// Return errorkind + pub fn kind(&self) -> ErrorKind { + self.inner.get_context().clone() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Error { + inner: Context::new(kind), + } + } +} + +impl From> for Error { + fn from(inner: Context) -> Error { + Error { inner: inner } + } +} + +impl From for Error { + fn from(_error: fmt::Error) -> Error { + Error { + inner: Context::new(ErrorKind::IntegerCast), + } + } +} + +impl From for Error { + fn from(_error: io::Error) -> Error { + Error { + inner: Context::new(ErrorKind::IOError), + } + } +} diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index d8192b216b..d7cd29d0a3 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -36,8 +36,9 @@ extern crate serde; extern crate grin_util as util; -mod cuckatoo; +pub mod cuckatoo; pub mod cuckoo; +mod error; mod siphash; mod types; From d7b91586d4d6c94f2e750515f56f2a586cd79cce Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 26 Sep 2018 13:22:05 +0100 Subject: [PATCH 13/22] modify cuckoo/cuckatoo solvers and verifiers to function identically, in advance of trait refactoring --- core/src/pow/common.rs | 120 ++++++++++ core/src/pow/cuckatoo.rs | 133 ++--------- core/src/pow/cuckoo.rs | 9 +- core/src/pow/cuckoo_ctx.rs | 448 +++++++++++++++++++++++++++++++++++++ core/src/pow/error.rs | 12 + core/src/pow/miner.rs | 0 core/src/pow/mod.rs | 2 + core/src/pow/siphash.rs | 10 +- core/src/pow/types.rs | 2 + 9 files changed, 612 insertions(+), 124 deletions(-) create mode 100644 core/src/pow/common.rs create mode 100644 core/src/pow/cuckoo_ctx.rs create mode 100644 core/src/pow/miner.rs diff --git a/core/src/pow/common.rs b/core/src/pow/common.rs new file mode 100644 index 0000000000..aaba758ebd --- /dev/null +++ b/core/src/pow/common.rs @@ -0,0 +1,120 @@ + +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Common types and traits for cuckoo/cuckatoo family of solvers + +use std::{fmt, mem}; +use std::ops::{BitOrAssign, Mul}; +use std::hash::Hash; +use pow::num::{PrimInt, ToPrimitive}; +use pow::error::{Error, ErrorKind}; +use pow::siphash::siphash24; +use blake2::blake2b::blake2b; +use std::io::Cursor; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +/// Operations needed for edge type (going to be u32 or u64) +pub trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign + Hash {} +impl EdgeType for u32 {} +impl EdgeType for u64 {} + +/// An edge in the Cuckoo graph, simply references two u64 nodes. +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct Edge +where + T: EdgeType, +{ + pub u: T, + pub v: T, +} + +impl fmt::Display for Edge +where + T: EdgeType, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "(u: {}, v: {})", + self.u.to_u64().unwrap_or(0), + self.v.to_u64().unwrap_or(0) + ) + } +} + +/// An element of an adjencency list +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Link +where + T: EdgeType, +{ + pub next: T, + pub to: T, +} + +impl fmt::Display for Link +where + T: EdgeType, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "(next: {}, to: {})", + self.next.to_u64().unwrap_or(0), + self.to.to_u64().unwrap_or(0) + ) + } +} + +pub fn set_header_nonce(header: Vec, nonce: Option) -> Result<[u64;4], Error> { + let len = header.len(); + let mut header = header.clone(); + if let Some(n) = nonce { + header.truncate(len - mem::size_of::()); + header.write_u32::(n)?; + } + create_siphash_keys(header) +} + +pub fn create_siphash_keys(header: Vec) -> Result<[u64;4], Error> { + let h = blake2b(32, &[], &header); + let hb = h.as_bytes(); + let mut rdr = Cursor::new(hb); + Ok([ + rdr.read_u64::()?, + rdr.read_u64::()?, + rdr.read_u64::()?, + rdr.read_u64::()?, + ]) +} + +/// Return siphash masked for type +pub fn sipnode(keys: &[u64;4], edge: T, edge_mask: &T, uorv: u64, shift: bool) -> Result +where + T: EdgeType, +{ + let hash_u64 = siphash24( + keys, + 2 * edge.to_u64().ok_or(ErrorKind::IntegerCast)? + uorv, + ); + let mut masked = hash_u64 & edge_mask.to_u64().ok_or(ErrorKind::IntegerCast)?; + if shift { + masked = masked << 1; + masked |= uorv; + } + Ok(T::from(masked).ok_or(ErrorKind::IntegerCast)?) +} + diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 756d963349..8ff47bf1c1 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -12,49 +12,18 @@ // limitations under the License. //! Implementation of Cuckatoo Cycle designed by John Tromp. -use pow::num::{PrimInt, ToPrimitive}; -use std::ops::{BitOrAssign, Mul}; -use std::{fmt, mem}; +use pow::num::ToPrimitive; +use std::mem; -use blake2::blake2b::blake2b; -use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; use croaring::Bitmap; -use std::io::Cursor; use pow::error::{Error, ErrorKind}; -use pow::siphash::siphash24; +use pow::common::{self, Link}; use pow::Proof; +use pow::common::EdgeType; use util; -/// Operations needed for edge type (going to be u32 or u64) -pub trait EdgeType: PrimInt + ToPrimitive + Mul + BitOrAssign {} -impl EdgeType for u32 {} -impl EdgeType for u64 {} - -/// An element of an adjencency list -#[derive(Debug, Clone, Eq, PartialEq)] -struct Link -where - T: EdgeType, -{ - next: T, - to: T, -} - -impl fmt::Display for Link -where - T: EdgeType, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "(next: {}, to: {})", - self.next.to_u64().unwrap_or(0), - self.to.to_u64().unwrap_or(0) - ) - } -} - struct Graph where T: EdgeType, @@ -232,20 +201,6 @@ where }) } - /// Extract siphash keys from header - pub fn create_keys(&mut self, header: Vec) -> Result<(), Error> { - let h = blake2b(32, &[], &header); - let hb = h.as_bytes(); - let mut rdr = Cursor::new(hb); - self.siphash_keys = [ - rdr.read_u64::()?, - rdr.read_u64::()?, - rdr.read_u64::()?, - rdr.read_u64::()?, - ]; - Ok(()) - } - /// Get a siphash key as a hex string (for display convenience) pub fn sipkey_hex(&self, index: usize) -> Result { let mut rdr = vec![]; @@ -269,23 +224,18 @@ where if let Some(n) = nonce { header.write_u32::(n)?; } - self.create_keys(header)?; + self.siphash_keys = common::set_header_nonce(header, nonce)?; self.graph.reset()?; Ok(()) } /// Return siphash masked for type - pub fn sipnode(&self, edge: T, uorv: u64) -> Result { - let hash_u64 = siphash24( - self.siphash_keys, - 2 * edge.to_u64().ok_or(ErrorKind::IntegerCast)? + uorv, - ); - let masked = hash_u64 & self.edge_mask.to_u64().ok_or(ErrorKind::IntegerCast)?; - Ok(T::from(masked).ok_or(ErrorKind::IntegerCast)?) + fn sipnode(&self, edge: T, uorv: u64) -> Result { + common::sipnode::(&self.siphash_keys, edge, &self.edge_mask, uorv, false) } /// Simple implementation of algorithm - pub fn find_cycles_simple(&mut self) -> Result, Error> { + pub fn find_cycles(&mut self) -> Result, Error> { for n in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? { let u = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 0)?; let v = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 1)?; @@ -301,7 +251,11 @@ where for s in &self.graph.solutions { self.verify(&s)?; } - Ok(self.graph.solutions.clone()) + if self.graph.solutions.len() == 0 { + Err(ErrorKind::NoSolution)? + } else { + Ok(self.graph.solutions.clone()) + } } /// Verify that given edges are ascending and form a cycle in a header-generated @@ -382,63 +336,6 @@ mod test { if let Err(r) = ret { panic!("basic_solve: Error: {}", r); } - let ret = test_mine_32(); - if let Err(r) = ret { - panic!("test_mine_32: Error: {}", r); - } - let sols_32 = ret.unwrap(); - let ret = test_mine_64(); - if let Err(r) = ret { - panic!("test_mine_64: Error: {}", r); - } - let sols_64 = ret.unwrap(); - assert_eq!(sols_64, sols_32); - } - - fn test_mine_32() -> Result { - println!("Test mine 32"); - let easiness_pct = 50; - let nonce = 0; - let range = 30; - let header = [0u8; 80].to_vec(); - let proof_size = 42; - let edge_bits = 15; - let max_sols = 4; - let mut ctx_u32 = - CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; - let mut num_sols = 0; - for r in 0..range { - ctx_u32.set_header_nonce(header.clone(), Some(nonce + r))?; - let sols = ctx_u32.find_cycles_simple()?; - for s in sols { - println!("Solution found: {:?}", s); - num_sols += 1; - } - } - Ok(num_sols) - } - - fn test_mine_64() -> Result { - println!("Test mine 64"); - let easiness_pct = 50; - let nonce = 0; - let range = 30; - let header = [0u8; 80].to_vec(); - let proof_size = 42; - let edge_bits = 15; - let max_sols = 4; - let mut ctx_u64 = - CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; - let mut num_sols = 0; - for r in 0..range { - ctx_u64.set_header_nonce(header.clone(), Some(nonce + r))?; - let sols = ctx_u64.find_cycles_simple()?; - for s in sols { - println!("Solution found: {:?}", s); - num_sols += 1; - } - } - Ok(num_sols) } fn basic_solve() -> Result<(), Error> { @@ -476,7 +373,7 @@ mod test { ctx_u32.sipkey_hex(2)?, ctx_u32.sipkey_hex(3)? ); - let sols = ctx_u32.find_cycles_simple()?; + let sols = ctx_u32.find_cycles()?; // We know this nonce has 2 solutions assert_eq!(sols.len(), 2); for s in sols { diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index efe88af041..c4115c2a5a 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -54,7 +54,10 @@ impl Cuckoo { /// generate a seed. In practice for PoW applications the byte array is a /// serialized block header. pub fn from_hash(header_hash: &[u8], sizeshift: u8) -> Cuckoo { + println!("header:{:?}", header_hash); let size = 1 << sizeshift; + println!("Edge mask: {}", (1 << sizeshift) / 2 - 1); + println!("siphash keys [0]: {}", u8_to_u64(header_hash, 0)); Cuckoo { v: [ u8_to_u64(header_hash, 0), @@ -71,7 +74,7 @@ impl Cuckoo { /// simply materialized as a u64 from a nonce and an offset (generally 0 or /// 1). fn new_node(&self, nonce: u64, uorv: u64) -> u64 { - return ((siphash24(self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv; + 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 @@ -171,6 +174,9 @@ impl Miner { let size = 1 << sizeshift; let graph = vec![0; size + 1]; let easiness = (ease as u64) * (size as u64) / 100; + println!("Size: {}", size); + println!("Easiness: {}", easiness); + Miner { easiness, cuckoo, @@ -187,6 +193,7 @@ impl Miner { for nonce in 0..self.easiness { us[0] = self.cuckoo.new_node(nonce, 0) as u32; vs[0] = self.cuckoo.new_node(nonce, 1) as u32; + println!("Added us[0], vs[0]: {}, {}", us[0], vs[0]); let u = self.graph[us[0] as usize]; let v = self.graph[vs[0] as usize]; if us[0] == 0 { diff --git a/core/src/pow/cuckoo_ctx.rs b/core/src/pow/cuckoo_ctx.rs new file mode 100644 index 0000000000..1c878f38e5 --- /dev/null +++ b/core/src/pow/cuckoo_ctx.rs @@ -0,0 +1,448 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! 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 pow::num::ToPrimitive; +use pow::common::{self, EdgeType, Edge}; +use pow::error::{Error, ErrorKind}; +use pow::Proof; + +use std::cmp; +use std::collections::HashSet; + +const MAXPATHLEN: usize = 8192; + +/// Cuckoo cycle context +pub struct CuckooContext +where + T: EdgeType, +{ + edge_bits: u8, + siphash_keys: [u64; 4], + easiness: T, + proof_size: usize, + edge_mask: T, + graph: Vec, + _max_sols: usize, +} + +impl CuckooContext +where + T: EdgeType, +{ + /// Create a new cuckoo context with given parameters + pub fn new( + edge_bits: u8, + proof_size: usize, + easiness_pct: u32, + max_sols: usize, + ) -> Result, Error> { + let num_edges = 1 << edge_bits; + let easiness = easiness_pct.to_u64().ok_or(ErrorKind::IntegerCast)? * num_edges / 100; + Ok(CuckooContext { + edge_bits: edge_bits, + siphash_keys: [0; 4], + easiness: T::from(easiness).ok_or(ErrorKind::IntegerCast)?, + proof_size: proof_size, + edge_mask: T::from(num_edges / 2 - 1).ok_or(ErrorKind::IntegerCast)?, + graph: vec![T::zero(); num_edges as usize + 1], + _max_sols: max_sols, + }) + } + + fn reset(&mut self) -> Result<(), Error> { + let num_edges = 1 << self.edge_bits; + self.graph = vec![T::zero(); num_edges + 1]; + Ok(()) + } + + /// Set the header and optional nonce in the last part of the header + /// and create siphash keys + pub fn set_header_nonce( + &mut self, + header: Vec, + nonce: Option, + ) -> Result<(), Error> { + self.siphash_keys = common::set_header_nonce(header, nonce)?; + self.reset()?; + Ok(()) + } + + /// 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). + fn new_node(&self, edge: T, uorv: u64) -> Result { + common::sipnode::(&self.siphash_keys, edge, &self.edge_mask, uorv, true) + } + + /// 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. + fn new_edge(&self, nonce: T) -> Result, Error> { + Ok(Edge { + u: self.new_node(nonce, 0)?, + v: self.new_node(nonce, 1)?, + }) + } + + fn path(&self, mut u: T, us: &mut [T]) -> Result { + let mut nu = 0; + while u != T::zero() { + nu += 1; + if nu >= MAXPATHLEN { + while nu != 0 && us[(nu - 1) as usize] != u { + nu -= 1; + } + return Err(ErrorKind::Path)?; + } + us[nu as usize] = u; + u = self.graph[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + } + Ok(T::from(nu).ok_or(ErrorKind::IntegerCast)?) + } + + fn update_graph(&mut self, mut nu: usize, us: &[T], mut nv: usize, vs: &[T]) -> Result<(), Error> { + if nu < nv { + while nu != 0 { + nu -= 1; + self.graph[us[nu + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[nu]; + } + self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[0]; + } else { + while nv != 0 { + nv -= 1; + self.graph[vs[nv + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[nv]; + } + self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[0]; + } + Ok(()) + } + + fn find_sol(&mut self, mut nu: usize, us: &[T], mut nv: usize, vs: &[T]) -> Result, Error> { + 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 == self.proof_size { + self.solution(&us, nu as u64, &vs, nv as u64) + } else { + Err(ErrorKind::InvalidCycle(nu + nv + 1))? + } + } else { + Err(ErrorKind::NoCycle)? + } + } + + fn solution(&mut self, us: &[T], mut nu: u64, vs: &[T], mut nv: u64) -> Result, Error> { + let mut cycle = HashSet::new(); + cycle.insert(Edge { + u: us[0], + v: vs[0], + }); + while nu != 0 { + // u's in even position; v's in odd + nu = nu - 1; + cycle.insert(Edge { + u: us[((nu + 1) & !1) as usize], + v: us[(nu | 1) as usize], + }); + } + while nv != 0 { + // u's in odd position; v's in even + nv -= 1; + cycle.insert(Edge { + u: vs[(nv | 1) as usize], + v: vs[((nv + 1) & !1) as usize], + }); + } + let mut n = 0; + let mut sol = vec![T::zero(); self.proof_size]; + for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { + let edge = self.new_edge(T::from(nonce).ok_or(ErrorKind::IntegerCast)?)?; + if cycle.contains(&edge) { + sol[n] = T::from(nonce).ok_or(ErrorKind::IntegerCast)?; + n += 1; + cycle.remove(&edge); + } + } + return if n == self.proof_size { + Ok(sol) + } else { + Err(ErrorKind::NoCycle)? + }; + } + + /// Searches for a solution (simple implementation) + pub fn find_cycles(&mut self) -> Result, Error> { + let mut us = [T::zero(); MAXPATHLEN]; + let mut vs = [T::zero(); MAXPATHLEN]; + for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { + us[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 0)?; + vs[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 1)?; + let u = self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + let v = self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + if us[0] == T::zero() { + continue; // ignore duplicate edges + } + let nu = self.path(u, &mut us)?.to_u64().ok_or(ErrorKind::IntegerCast)? as usize; + let nv = self.path(v, &mut vs)?.to_u64().ok_or(ErrorKind::IntegerCast)? as usize; + + let sol = self.find_sol(nu, &us, nv, &vs); + match sol { + Ok(s) => { + let mut proof = Proof::new(map_vec!(s.to_vec(), |&n| n.to_u64().unwrap_or(0))); + proof.cuckoo_sizeshift = self.edge_bits; + return Ok(vec![proof]); + } + Err(e) => { + match e.kind() { + ErrorKind::InvalidCycle(_) => continue, + ErrorKind::NoCycle => self.update_graph(nu, &us, nv, &vs)?, + _ => return Err(e) + } + } + } + } + Err(ErrorKind::NoSolution)? + } + + /// 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) -> Result<(), Error> { + let easiness = self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)?; + let nonces = &proof.nonces; + let mut us = vec![T::zero(); proof.proof_size()]; + let mut vs = vec![T::zero(); proof.proof_size()]; + for n in 0..proof.proof_size() { + if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) { + return Err(ErrorKind::Verification("edge wrong size".to_owned()))?; + } + us[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 0)?; + vs[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 1)?; + } + let mut i = 0; + let mut count = proof.proof_size(); + loop { + let mut j = i; + for k in 0..proof.proof_size() { + // find unique other j with same vs[j] + if k != i && vs[k] == vs[i] { + if j != i { + return Err(ErrorKind::Verification("".to_owned()))?; + } + j = k; + } + } + if j == i { + return Err(ErrorKind::Verification("".to_owned()))?; + } + i = j; + for k in 0..proof.proof_size() { + // find unique other i with same us[i] + if k != j && us[k] == us[j] { + if i != j { + return Err(ErrorKind::Verification("".to_owned()))?; + } + i = k; + } + } + if i == j { + return Err(ErrorKind::Verification("".to_owned()))?; + } + count -= 2; + if i == 0 { + break; + } + } + match count == 0 { + true => Ok(()), + false => Err(ErrorKind::Verification("Invalid solution".to_owned()))? + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + static V1: [u64; 42] = [ + 0x3bbd, 0x4e96, 0x1013b, 0x1172b, 0x1371b, 0x13e6a, 0x1aaa6, 0x1b575, 0x1e237, 0x1ee88, + 0x22f94, 0x24223, 0x25b4f, 0x2e9f3, 0x33b49, 0x34063, 0x3454a, 0x3c081, 0x3d08e, 0x3d863, + 0x4285a, 0x42f22, 0x43122, 0x4b853, 0x4cd0c, 0x4f280, 0x557d5, 0x562cf, 0x58e59, 0x59a62, + 0x5b568, 0x644b9, 0x657e9, 0x66337, 0x6821c, 0x7866f, 0x7e14b, 0x7ec7c, 0x7eed7, 0x80643, + 0x8628c, 0x8949e, + ]; + static V2: [u64; 42] = [ + 0x5e3a, 0x8a8b, 0x103d8, 0x1374b, 0x14780, 0x16110, 0x1b571, 0x1c351, 0x1c826, 0x28228, + 0x2909f, 0x29516, 0x2c1c4, 0x334eb, 0x34cdd, 0x38a2c, 0x3ad23, 0x45ac5, 0x46afe, 0x50f43, + 0x51ed6, 0x52ddd, 0x54a82, 0x5a46b, 0x5dbdb, 0x60f6f, 0x60fcd, 0x61c78, 0x63899, 0x64dab, + 0x6affc, 0x6b569, 0x72639, 0x73987, 0x78806, 0x7b98e, 0x7c7d7, 0x7ddd4, 0x7fa88, 0x8277c, + 0x832d9, 0x8ba6f, + ]; + static V3: [u64; 42] = [ + 0x308b, 0x9004, 0x91fc, 0x983e, 0x9d67, 0xa293, 0xb4cb, 0xb6c8, 0xccc8, 0xdddc, 0xf04d, + 0x1372f, 0x16ec9, 0x17b61, 0x17d03, 0x1e3bc, 0x1fb0f, 0x29e6e, 0x2a2ca, 0x2a719, 0x3a078, + 0x3b7cc, 0x3c71d, 0x40daa, 0x43e17, 0x46adc, 0x4b359, 0x4c3aa, 0x4ce92, 0x4d06e, 0x51140, + 0x565ac, 0x56b1f, 0x58a8b, 0x5e410, 0x5e607, 0x5ebb5, 0x5f8ae, 0x7aeac, 0x7b902, 0x7d6af, + 0x7f400, + ]; + // cuckoo28 at 50% edges of letter 'u' + static V4: [u64; 42] = [ + 0xf7243, 0x11f130, 0x193812, 0x23b565, 0x279ac3, 0x69b270, 0xe0778f, 0xef51fc, 0x10bf6e8, + 0x13ccf7d, 0x1551177, 0x1b6cfd2, 0x1f872c3, 0x2075681, 0x2e23ccc, 0x2e4c0aa, 0x2f607f1, + 0x3007eeb, 0x3407e9a, 0x35423f9, 0x39e48bf, 0x45e3bf6, 0x46aa484, 0x47c0fe1, 0x4b1d5a6, + 0x4bae0ba, 0x4dfdbaf, 0x5048eda, 0x537da6b, 0x5402887, 0x56b8897, 0x5bd8e8b, 0x622de20, + 0x62be5ce, 0x62d538e, 0x6464518, 0x650a6d5, 0x66ec4fa, 0x66f9476, 0x6b1e5f6, 0x6fd5d88, + 0x701f37b, + ]; + + #[test] + fn cuckoo_context() { + let ret = mine20_vectors::(); + if let Err(r) = ret { + panic!("mine20_vectors u32: Error: {}", r); + } + let ret = mine20_vectors::(); + if let Err(r) = ret { + panic!("mine20_vectors u64: Error: {}", r); + } + let ret = validate20_vectors::(); + if let Err(r) = ret { + panic!("validate20_vectors u32: Error: {}", r); + } + let ret = validate20_vectors::(); + if let Err(r) = ret { + panic!("validate20_vectors u64: Error: {}", r); + } + let ret = validate_fail::(); + if let Err(r) = ret { + panic!("validate_fail u32: Error: {}", r); + } + let ret = validate_fail::(); + if let Err(r) = ret { + panic!("validate_fail u64: Error: {}", r); + } + let ret = mine16_validate::(); + if let Err(r) = ret { + panic!("mine16_validate u32: Error: {}", r); + } + let ret = mine16_validate::(); + if let Err(r) = ret { + panic!("mine16_validate u64: Error: {}", r); + } + } + + /// Find a 42-cycle on Cuckoo20 at 75% easiness and verify against a few + /// known cycle proofs + /// generated by other implementations. + fn mine20_vectors() -> Result<(), Error> + where + T: EdgeType, + { + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; + let mut proof = Proof::new(V1.to_vec()); + proof.cuckoo_sizeshift = 20; + assert_eq!(proof, res[0]); + + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; + let mut proof = Proof::new(V2.to_vec()); + proof.cuckoo_sizeshift = 20; + assert_eq!(proof, res[0]); + + //re-use context + cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; + let mut proof = Proof::new(V3.to_vec()); + proof.cuckoo_sizeshift = 20; + assert_eq!(proof, res[0]); + Ok(()) + } + + fn validate20_vectors() -> Result<(), Error> + where + T: EdgeType, + { + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + assert!( + cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok() + ); + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + assert!( + cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok() + ); + cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; + assert!( + cuckoo_ctx.verify(&Proof::new(V3.to_vec().clone())).is_ok() + ); + Ok(()) + } + + fn validate_fail() -> Result<(), Error> + where + T: EdgeType + { + // edge checks + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + // edge checks + assert!( + !cuckoo_ctx.verify(&Proof::new(vec![0; 42])).is_ok() + ); + assert!( + !cuckoo_ctx.verify(&Proof::new(vec![0xffff; 42])).is_ok() + ); + // wrong data for proof + cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + assert!( + !cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok() + ); + let mut test_header = [0; 32]; + test_header[0] = 24; + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 50, 10)?; + cuckoo_ctx.set_header_nonce(test_header.to_vec(), None)?; + assert!( + !cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok() + ); + Ok(()) + } + + fn mine16_validate() -> Result<(), Error> + where + T: EdgeType + { + for n in 1..5 { + let h = [n; 32]; + let mut cuckoo_ctx = CuckooContext::::new(16, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce(h.to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; + assert!(cuckoo_ctx.verify(&res[0]).is_ok()) + } + Ok(()) + } +} diff --git a/core/src/pow/error.rs b/core/src/pow/error.rs index 9f02ad1a5d..55e48be059 100644 --- a/core/src/pow/error.rs +++ b/core/src/pow/error.rs @@ -38,6 +38,18 @@ pub enum ErrorKind { /// Unexpected Edge Error #[fail(display = "Edge Addition Error")] EdgeAddition, + /// Path Error + #[fail(display = "Path Error")] + Path, + /// Invalid cycle + #[fail(display = "Invalid Cycle length: {}", _0)] + InvalidCycle(usize), + /// No Cycle + #[fail(display = "No Cycle")] + NoCycle, + /// No Solution + #[fail(display = "No Solution")] + NoSolution, } impl Fail for Error { diff --git a/core/src/pow/miner.rs b/core/src/pow/miner.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index d7cd29d0a3..893dbe2512 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -38,6 +38,8 @@ extern crate grin_util as util; pub mod cuckatoo; pub mod cuckoo; +pub mod cuckoo_ctx; +mod common; mod error; mod siphash; mod types; diff --git a/core/src/pow/siphash.rs b/core/src/pow/siphash.rs index 509285698a..fb12cf103e 100644 --- a/core/src/pow/siphash.rs +++ b/core/src/pow/siphash.rs @@ -16,7 +16,7 @@ //! Jean-Philippe Aumasson and Daniel J. Bernstein. /// Implements siphash 2-4 specialized for a 4 u64 array key and a u64 nonce -pub fn siphash24(v: [u64; 4], nonce: u64) -> u64 { +pub fn siphash24(v: &[u64; 4], nonce: u64) -> u64 { let mut v0 = v[0]; let mut v1 = v[1]; let mut v2 = v[2]; @@ -73,9 +73,9 @@ mod test { /// the fact that the Java impl uses a long, aka a signed 64 bits number). #[test] fn hash_some() { - assert_eq!(siphash24([1, 2, 3, 4], 10), 928382149599306901); - assert_eq!(siphash24([1, 2, 3, 4], 111), 10524991083049122233); - assert_eq!(siphash24([9, 7, 6, 7], 12), 1305683875471634734); - assert_eq!(siphash24([9, 7, 6, 7], 10), 11589833042187638814); + assert_eq!(siphash24(&[1, 2, 3, 4], 10), 928382149599306901); + assert_eq!(siphash24(&[1, 2, 3, 4], 111), 10524991083049122233); + assert_eq!(siphash24(&[9, 7, 6, 7], 12), 1305683875471634734); + assert_eq!(siphash24(&[9, 7, 6, 7], 10), 11589833042187638814); } } diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index c1c40d37c3..49848a6578 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -26,6 +26,8 @@ use core::hash::Hashed; use global; use ser::{self, Readable, Reader, Writeable, Writer}; +use pow::num::ToPrimitive; + /// The difficulty is defined as the maximum target divided by the block hash. #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub struct Difficulty { From 6ca0bdd9936c5b79d8d95014269dc08742e776e9 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 26 Sep 2018 13:22:13 +0100 Subject: [PATCH 14/22] rustfmt --- core/src/pow/common.rs | 38 ++++++++------- core/src/pow/cuckatoo.rs | 4 +- core/src/pow/cuckoo.rs | 2 +- core/src/pow/cuckoo_ctx.rs | 95 ++++++++++++++++++-------------------- core/src/pow/miner.rs | 1 + core/src/pow/mod.rs | 2 +- 6 files changed, 71 insertions(+), 71 deletions(-) diff --git a/core/src/pow/common.rs b/core/src/pow/common.rs index aaba758ebd..a7a1420fc6 100644 --- a/core/src/pow/common.rs +++ b/core/src/pow/common.rs @@ -1,4 +1,3 @@ - // Copyright 2018 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,14 +14,14 @@ //! Common types and traits for cuckoo/cuckatoo family of solvers -use std::{fmt, mem}; -use std::ops::{BitOrAssign, Mul}; -use std::hash::Hash; -use pow::num::{PrimInt, ToPrimitive}; +use blake2::blake2b::blake2b; use pow::error::{Error, ErrorKind}; +use pow::num::{PrimInt, ToPrimitive}; use pow::siphash::siphash24; -use blake2::blake2b::blake2b; +use std::hash::Hash; use std::io::Cursor; +use std::ops::{BitOrAssign, Mul}; +use std::{fmt, mem}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -79,17 +78,17 @@ where } } -pub fn set_header_nonce(header: Vec, nonce: Option) -> Result<[u64;4], Error> { - let len = header.len(); - let mut header = header.clone(); - if let Some(n) = nonce { - header.truncate(len - mem::size_of::()); - header.write_u32::(n)?; - } - create_siphash_keys(header) +pub fn set_header_nonce(header: Vec, nonce: Option) -> Result<[u64; 4], Error> { + let len = header.len(); + let mut header = header.clone(); + if let Some(n) = nonce { + header.truncate(len - mem::size_of::()); + header.write_u32::(n)?; + } + create_siphash_keys(header) } -pub fn create_siphash_keys(header: Vec) -> Result<[u64;4], Error> { +pub fn create_siphash_keys(header: Vec) -> Result<[u64; 4], Error> { let h = blake2b(32, &[], &header); let hb = h.as_bytes(); let mut rdr = Cursor::new(hb); @@ -102,7 +101,13 @@ pub fn create_siphash_keys(header: Vec) -> Result<[u64;4], Error> { } /// Return siphash masked for type -pub fn sipnode(keys: &[u64;4], edge: T, edge_mask: &T, uorv: u64, shift: bool) -> Result +pub fn sipnode( + keys: &[u64; 4], + edge: T, + edge_mask: &T, + uorv: u64, + shift: bool, +) -> Result where T: EdgeType, { @@ -117,4 +122,3 @@ where } Ok(T::from(masked).ok_or(ErrorKind::IntegerCast)?) } - diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 8ff47bf1c1..1c1d2a6a05 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -18,10 +18,10 @@ use std::mem; use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; use croaring::Bitmap; -use pow::error::{Error, ErrorKind}; +use pow::common::EdgeType; use pow::common::{self, Link}; +use pow::error::{Error, ErrorKind}; use pow::Proof; -use pow::common::EdgeType; use util; struct Graph diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index c4115c2a5a..c697f6d0a1 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -74,7 +74,7 @@ impl Cuckoo { /// simply materialized as a u64 from a nonce and an offset (generally 0 or /// 1). fn new_node(&self, nonce: u64, uorv: u64) -> u64 { - return ((siphash24(&self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv; + 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 diff --git a/core/src/pow/cuckoo_ctx.rs b/core/src/pow/cuckoo_ctx.rs index 1c878f38e5..3b3a53f635 100644 --- a/core/src/pow/cuckoo_ctx.rs +++ b/core/src/pow/cuckoo_ctx.rs @@ -17,9 +17,9 @@ //! simple miner is included, mostly for testing purposes. John Tromp's Tomato //! miner will be much faster in almost every environment. -use pow::num::ToPrimitive; -use pow::common::{self, EdgeType, Edge}; +use pow::common::{self, Edge, EdgeType}; use pow::error::{Error, ErrorKind}; +use pow::num::ToPrimitive; use pow::Proof; use std::cmp; @@ -73,11 +73,7 @@ where /// Set the header and optional nonce in the last part of the header /// and create siphash keys - pub fn set_header_nonce( - &mut self, - header: Vec, - nonce: Option, - ) -> Result<(), Error> { + pub fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { self.siphash_keys = common::set_header_nonce(header, nonce)?; self.reset()?; Ok(()) @@ -116,7 +112,13 @@ where Ok(T::from(nu).ok_or(ErrorKind::IntegerCast)?) } - fn update_graph(&mut self, mut nu: usize, us: &[T], mut nv: usize, vs: &[T]) -> Result<(), Error> { + fn update_graph( + &mut self, + mut nu: usize, + us: &[T], + mut nv: usize, + vs: &[T], + ) -> Result<(), Error> { if nu < nv { while nu != 0 { nu -= 1; @@ -133,7 +135,13 @@ where Ok(()) } - fn find_sol(&mut self, mut nu: usize, us: &[T], mut nv: usize, vs: &[T]) -> Result, Error> { + fn find_sol( + &mut self, + mut nu: usize, + us: &[T], + mut nv: usize, + vs: &[T], + ) -> Result, Error> { if us[nu] == vs[nv] { let min = cmp::min(nu, nv); nu -= min; @@ -154,10 +162,7 @@ where fn solution(&mut self, us: &[T], mut nu: u64, vs: &[T], mut nv: u64) -> Result, Error> { let mut cycle = HashSet::new(); - cycle.insert(Edge { - u: us[0], - v: vs[0], - }); + cycle.insert(Edge { u: us[0], v: vs[0] }); while nu != 0 { // u's in even position; v's in odd nu = nu - 1; @@ -203,8 +208,14 @@ where if us[0] == T::zero() { continue; // ignore duplicate edges } - let nu = self.path(u, &mut us)?.to_u64().ok_or(ErrorKind::IntegerCast)? as usize; - let nv = self.path(v, &mut vs)?.to_u64().ok_or(ErrorKind::IntegerCast)? as usize; + let nu = self + .path(u, &mut us)? + .to_u64() + .ok_or(ErrorKind::IntegerCast)? as usize; + let nv = self + .path(v, &mut vs)? + .to_u64() + .ok_or(ErrorKind::IntegerCast)? as usize; let sol = self.find_sol(nu, &us, nv, &vs); match sol { @@ -213,13 +224,11 @@ where proof.cuckoo_sizeshift = self.edge_bits; return Ok(vec![proof]); } - Err(e) => { - match e.kind() { - ErrorKind::InvalidCycle(_) => continue, - ErrorKind::NoCycle => self.update_graph(nu, &us, nv, &vs)?, - _ => return Err(e) - } - } + Err(e) => match e.kind() { + ErrorKind::InvalidCycle(_) => continue, + ErrorKind::NoCycle => self.update_graph(nu, &us, nv, &vs)?, + _ => return Err(e), + }, } } Err(ErrorKind::NoSolution)? @@ -276,7 +285,7 @@ where } match count == 0 { true => Ok(()), - false => Err(ErrorKind::Verification("Invalid solution".to_owned()))? + false => Err(ErrorKind::Verification("Invalid solution".to_owned()))?, } } } @@ -356,8 +365,8 @@ mod test { /// known cycle proofs /// generated by other implementations. fn mine20_vectors() -> Result<(), Error> - where - T: EdgeType, + where + T: EdgeType, { let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; @@ -383,58 +392,44 @@ mod test { } fn validate20_vectors() -> Result<(), Error> - where - T: EdgeType, + where + T: EdgeType, { let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; - assert!( - cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok() - ); + assert!(cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; - assert!( - cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok() - ); + assert!(cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok()); cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; - assert!( - cuckoo_ctx.verify(&Proof::new(V3.to_vec().clone())).is_ok() - ); + assert!(cuckoo_ctx.verify(&Proof::new(V3.to_vec().clone())).is_ok()); Ok(()) } fn validate_fail() -> Result<(), Error> where - T: EdgeType + T: EdgeType, { // edge checks let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; // edge checks - assert!( - !cuckoo_ctx.verify(&Proof::new(vec![0; 42])).is_ok() - ); - assert!( - !cuckoo_ctx.verify(&Proof::new(vec![0xffff; 42])).is_ok() - ); + assert!(!cuckoo_ctx.verify(&Proof::new(vec![0; 42])).is_ok()); + assert!(!cuckoo_ctx.verify(&Proof::new(vec![0xffff; 42])).is_ok()); // wrong data for proof cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; - assert!( - !cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok() - ); + assert!(!cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); let mut test_header = [0; 32]; test_header[0] = 24; let mut cuckoo_ctx = CuckooContext::::new(20, 42, 50, 10)?; cuckoo_ctx.set_header_nonce(test_header.to_vec(), None)?; - assert!( - !cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok() - ); + assert!(!cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok()); Ok(()) } fn mine16_validate() -> Result<(), Error> where - T: EdgeType + T: EdgeType, { for n in 1..5 { let h = [n; 32]; diff --git a/core/src/pow/miner.rs b/core/src/pow/miner.rs index e69de29bb2..8b13789179 100644 --- a/core/src/pow/miner.rs +++ b/core/src/pow/miner.rs @@ -0,0 +1 @@ + diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 893dbe2512..2f0748af55 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -36,10 +36,10 @@ extern crate serde; extern crate grin_util as util; +mod common; pub mod cuckatoo; pub mod cuckoo; pub mod cuckoo_ctx; -mod common; mod error; mod siphash; mod types; From 58a0d1ba2410290101cd487b8c89e472f669a82b Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 26 Sep 2018 16:43:59 +0100 Subject: [PATCH 15/22] refactor PoW context into trait, default to using cuckoo context --- chain/src/chain.rs | 6 +- chain/src/pipe.rs | 6 +- core/src/global.rs | 30 +- core/src/pow/cuckatoo.rs | 33 +- core/src/pow/cuckoo.rs | 581 ++++++++++++++-------------- core/src/pow/cuckoo_ctx.rs | 443 --------------------- core/src/pow/miner.rs | 1 - core/src/pow/mod.rs | 28 +- core/src/pow/types.rs | 19 +- servers/src/common/types.rs | 6 +- servers/src/mining/stratumserver.rs | 2 +- servers/src/mining/test_miner.rs | 14 +- 12 files changed, 402 insertions(+), 767 deletions(-) delete mode 100644 core/src/pow/cuckoo_ctx.rs delete mode 100644 core/src/pow/miner.rs diff --git a/chain/src/chain.rs b/chain/src/chain.rs index 78aae1c4a9..0dc681592b 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -28,7 +28,7 @@ use core::core::merkle_proof::MerkleProof; use core::core::verifier_cache::VerifierCache; use core::core::{Block, BlockHeader, BlockSums, Output, OutputIdentifier, Transaction, TxKernel}; use core::global; -use core::pow::Difficulty; +use core::pow::{self, Difficulty}; use error::{Error, ErrorKind}; use grin_store::Error::NotFoundErr; use pipe; @@ -152,7 +152,7 @@ pub struct Chain { block_hashes_cache: Arc>>, verifier_cache: Arc>, // POW verification function - pow_verifier: fn(&BlockHeader, u8) -> bool, + pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>, archive_mode: bool, } @@ -168,7 +168,7 @@ impl Chain { db_env: Arc, adapter: Arc, genesis: Block, - pow_verifier: fn(&BlockHeader, u8) -> bool, + pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>, verifier_cache: Arc>, archive_mode: bool, ) -> Result { diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index b4b2db8e2c..ad5c33261f 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -27,7 +27,7 @@ use core::core::verifier_cache::VerifierCache; use core::core::Committed; use core::core::{Block, BlockHeader, BlockSums}; use core::global; -use core::pow::Difficulty; +use core::pow::{self, Difficulty}; use error::{Error, ErrorKind}; use grin_store; use store; @@ -47,7 +47,7 @@ pub struct BlockContext { /// The head pub head: Tip, /// The POW verification function - pub pow_verifier: fn(&BlockHeader, u8) -> bool, + pub pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>, /// MMR sum tree states pub txhashset: Arc>, /// Recently processed blocks to avoid double-processing @@ -414,7 +414,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E if shift != consensus::SECOND_POW_SIZESHIFT && header.pow.scaling_difficulty != 1 { return Err(ErrorKind::InvalidScaling.into()); } - if !(ctx.pow_verifier)(header, shift) { + if !(ctx.pow_verifier)(header, shift).is_ok() { error!( LOGGER, "pipe: validate_header bad cuckoo shift size {}", shift diff --git a/core/src/global.rs b/core/src/global.rs index 1c8cab938b..17a2f19164 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -22,7 +22,7 @@ use consensus::{ DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, MEDIAN_TIME_WINDOW, PROOFSIZE, REFERENCE_SIZESHIFT, }; -use pow::Difficulty; +use pow::{self, CuckooContext, Difficulty, EdgeType, PoWContext}; /// An enum collecting sets of parameters used throughout the /// code wherever mining is needed. This should allow for /// different sets of parameters for different purposes, @@ -93,10 +93,24 @@ impl Default for ChainTypes { } } +/// PoW test mining and verifier context +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum PoWContextTypes { + /// Classic Cuckoo + Cuckoo, + /// Bleeding edge Cuckatoo + Cuckatoo, +} + + lazy_static!{ /// The mining parameter mode pub static ref CHAIN_TYPE: RwLock = RwLock::new(ChainTypes::Mainnet); + + /// PoW context type to instantiate + pub static ref POW_CONTEXT_TYPE: RwLock = + RwLock::new(PoWContextTypes::Cuckoo); } /// Set the mining mode @@ -105,6 +119,20 @@ pub fn set_mining_mode(mode: ChainTypes) { *param_ref = mode; } +/// Return either a cuckoo context or a cuckatoo context +/// Single change point +pub fn create_pow_context(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result>, pow::Error> +where + T: EdgeType, +{ + // Perform whatever tests, configuration etc are needed to determine desired context + edge size + // + params + // Hardcode to regular cuckoo for now + CuckooContext::::new(edge_bits, proof_size, easiness_pct, max_sols) + // Or switch to cuckatoo as follows: + // CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols) +} + /// The minimum acceptable sizeshift pub fn min_sizeshift() -> u8 { let param_ref = CHAIN_TYPE.read().unwrap(); diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 1c1d2a6a05..be1e1c373c 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -21,7 +21,7 @@ use croaring::Bitmap; use pow::common::EdgeType; use pow::common::{self, Link}; use pow::error::{Error, ErrorKind}; -use pow::Proof; +use pow::{PoWContext, Proof}; use util; struct Graph @@ -174,12 +174,33 @@ where edge_mask: T, } +impl PoWContext for CuckatooContext +where + T: EdgeType, +{ + fn new(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result, Error> { + Ok(Box::new(CuckatooContext::::new_impl(edge_bits, proof_size, easiness_pct, max_sols)?)) + } + + fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { + self.set_header_nonce_impl(header, nonce) + } + + fn find_cycles(&mut self) -> Result, Error> { + self.find_cycles_impl() + } + + fn verify(&self, proof: &Proof) -> Result<(), Error> { + self.verify_impl(proof) + } +} + impl CuckatooContext where T: EdgeType, { /// New Solver context - pub fn new( + pub fn new_impl( edge_bits: u8, proof_size: usize, easiness_pct: u32, @@ -214,7 +235,7 @@ where } /// Set the header and optional nonce in the last part of the header - pub fn set_header_nonce( + pub fn set_header_nonce_impl( &mut self, mut header: Vec, nonce: Option, @@ -235,7 +256,7 @@ where } /// Simple implementation of algorithm - pub fn find_cycles(&mut self) -> Result, Error> { + pub fn find_cycles_impl(&mut self) -> Result, Error> { for n in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? { let u = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 0)?; let v = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 1)?; @@ -249,7 +270,7 @@ where s.nonces.sort(); } for s in &self.graph.solutions { - self.verify(&s)?; + self.verify_impl(&s)?; } if self.graph.solutions.len() == 0 { Err(ErrorKind::NoSolution)? @@ -260,7 +281,7 @@ where /// Verify that given edges are ascending and form a cycle in a header-generated /// graph - pub fn verify(&self, proof: &Proof) -> Result<(), Error> { + pub fn verify_impl(&self, proof: &Proof) -> Result<(), Error> { let nonces = &proof.nonces; let mut uvs = vec![0u64; 2 * proof.proof_size()]; let mut xor0: u64 = (self.proof_size as u64 / 2) & 1; diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index c697f6d0a1..ddf38f319d 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -17,240 +17,152 @@ //! simple miner is included, mostly for testing purposes. John Tromp's Tomato //! miner will be much faster in almost every environment. +use pow::common::{self, Edge, EdgeType}; +use pow::error::{Error, ErrorKind}; +use pow::num::ToPrimitive; +use pow::{PoWContext, Proof}; + use std::cmp; use std::collections::HashSet; -use core::BlockHeader; -use pow::siphash::siphash24; -use pow::Proof; - const MAXPATHLEN: usize = 8192; -/// A cuckoo-cycle related error -#[derive(Debug)] -pub enum Error { - /// Unable to find a short enough path - Path, - /// Unable to find a solution - NoSolution, -} - -/// 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, -} - /// Cuckoo cycle context -pub struct Cuckoo { - mask: u64, - size: u64, - v: [u64; 4], +pub struct CuckooContext +where + T: EdgeType, +{ + edge_bits: u8, + siphash_keys: [u64; 4], + easiness: T, + proof_size: usize, + edge_mask: T, + graph: Vec, + _max_sols: u32, } -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 from_hash(header_hash: &[u8], sizeshift: u8) -> Cuckoo { - println!("header:{:?}", header_hash); - let size = 1 << sizeshift; - println!("Edge mask: {}", (1 << sizeshift) / 2 - 1); - println!("siphash keys [0]: {}", u8_to_u64(header_hash, 0)); - Cuckoo { - v: [ - 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, - } +impl PoWContext for CuckooContext +where + T: EdgeType, +{ + fn new(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result, Error> { + Ok(Box::new(CuckooContext::::new_impl(edge_bits, proof_size, easiness_pct, max_sols)?)) } - /// 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). - fn new_node(&self, nonce: u64, uorv: u64) -> u64 { - return ((siphash24(&self.v, 2 * nonce + uorv) & self.mask) << 1) | uorv; + fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { + self.set_header_nonce_impl(header, nonce) } - /// 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. - fn new_edge(&self, nonce: u64) -> Edge { - Edge { - u: self.new_node(nonce, 0), - v: self.new_node(nonce, 1), - } + fn find_cycles(&mut self) -> Result, Error> { + self.find_cycles_impl() } - /// 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.nonces; - let mut us = vec![0; proof.proof_size()]; - let mut vs = vec![0; proof.proof_size()]; - for n in 0..proof.proof_size() { - 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 = proof.proof_size(); - loop { - let mut j = i; - for k in 0..proof.proof_size() { - // 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..proof.proof_size() { - // 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 + fn verify(&self, proof: &Proof) -> Result<(), Error> { + self.verify_impl(proof) } } -/// 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, - proof_size: usize, - cuckoo: Cuckoo, - graph: Vec, - sizeshift: u8, -} - -/// What type of cycle we have found? -enum CycleSol { - /// A cycle of the right length is a valid proof. - ValidProof(Vec), - /// A cycle of the wrong length is great, but not a proof. - InvalidCycle(usize), - /// No cycles have been found. - NoCycle, -} - -impl Miner { - /// 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) +impl CuckooContext +where + T: EdgeType, +{ + /// Create a new cuckoo context with given parameters + pub fn new_impl( + edge_bits: u8, + proof_size: usize, + easiness_pct: u32, + max_sols: u32, + ) -> Result, Error> { + let num_edges = 1 << edge_bits; + let easiness = easiness_pct.to_u64().ok_or(ErrorKind::IntegerCast)? * num_edges / 100; + Ok(CuckooContext { + edge_bits: edge_bits, + siphash_keys: [0; 4], + easiness: T::from(easiness).ok_or(ErrorKind::IntegerCast)?, + proof_size: proof_size, + edge_mask: T::from(num_edges / 2 - 1).ok_or(ErrorKind::IntegerCast)?, + graph: vec![T::zero(); num_edges as usize + 1], + _max_sols: max_sols, + }) } - /// 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; - println!("Size: {}", size); - println!("Easiness: {}", easiness); + fn reset(&mut self) -> Result<(), Error> { + let num_edges = 1 << self.edge_bits; + self.graph = vec![T::zero(); num_edges + 1]; + Ok(()) + } - Miner { - easiness, - cuckoo, - graph, - proof_size, - sizeshift, - } + /// Set the header and optional nonce in the last part of the header + /// and create siphash keys + pub fn set_header_nonce_impl(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { + self.siphash_keys = common::set_header_nonce(header, nonce)?; + self.reset()?; + Ok(()) } - /// Searches for a solution - pub fn mine(&mut self) -> Result { - let mut us = [0; MAXPATHLEN]; - let mut vs = [0; MAXPATHLEN]; - for nonce in 0..self.easiness { - us[0] = self.cuckoo.new_node(nonce, 0) as u32; - vs[0] = self.cuckoo.new_node(nonce, 1) as u32; - println!("Added us[0], vs[0]: {}, {}", us[0], vs[0]); - let u = self.graph[us[0] as usize]; - let v = self.graph[vs[0] as usize]; - if us[0] == 0 { - continue; // ignore duplicate edges - } - let nu = self.path(u, &mut us)? as usize; - let nv = self.path(v, &mut vs)? as usize; + /// 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). + fn new_node(&self, edge: T, uorv: u64) -> Result { + common::sipnode::(&self.siphash_keys, edge, &self.edge_mask, uorv, true) + } - let sol = self.find_sol(nu, &us, nv, &vs); - match sol { - CycleSol::ValidProof(res) => { - let mut proof = Proof::new(map_vec!(res.to_vec(), |&n| n as u64)); - proof.cuckoo_sizeshift = self.sizeshift; - return Ok(proof); - } - CycleSol::InvalidCycle(_) => continue, - CycleSol::NoCycle => { - self.update_graph(nu, &us, nv, &vs); - } - } - } - Err(Error::NoSolution) + /// 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. + fn new_edge(&self, nonce: T) -> Result, Error> { + Ok(Edge { + u: self.new_node(nonce, 0)?, + v: self.new_node(nonce, 1)?, + }) } - fn path(&self, mut u: u32, us: &mut [u32]) -> Result { + fn path(&self, mut u: T, us: &mut [T]) -> Result { let mut nu = 0; - while u != 0 { + while u != T::zero() { nu += 1; if nu >= MAXPATHLEN { while nu != 0 && us[(nu - 1) as usize] != u { nu -= 1; } - return Err(Error::Path); + return Err(ErrorKind::Path)?; } us[nu as usize] = u; - u = self.graph[u as usize]; + u = self.graph[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; } - Ok(nu as u32) + Ok(T::from(nu).ok_or(ErrorKind::IntegerCast)?) } - fn update_graph(&mut self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) { + fn update_graph( + &mut self, + mut nu: usize, + us: &[T], + mut nv: usize, + vs: &[T], + ) -> Result<(), Error> { if nu < nv { while nu != 0 { nu -= 1; - self.graph[us[nu + 1] as usize] = us[nu]; + self.graph[us[nu + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[nu]; } - self.graph[us[0] as usize] = vs[0]; + self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[0]; } else { while nv != 0 { nv -= 1; - self.graph[vs[nv + 1] as usize] = vs[nv]; + self.graph[vs[nv + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[nv]; } - self.graph[vs[0] as usize] = us[0]; + self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[0]; } + Ok(()) } - fn find_sol(&mut self, mut nu: usize, us: &[u32], mut nv: usize, vs: &[u32]) -> CycleSol { + fn find_sol( + &mut self, + mut nu: usize, + us: &[T], + mut nv: usize, + vs: &[T], + ) -> Result, Error> { if us[nu] == vs[nv] { let min = cmp::min(nu, nv); nu -= min; @@ -260,71 +172,148 @@ impl Miner { nv += 1; } if nu + nv + 1 == self.proof_size { - self.solution(&us, nu as u32, &vs, nv as u32) + self.solution(&us, nu as u64, &vs, nv as u64) } else { - CycleSol::InvalidCycle(nu + nv + 1) + Err(ErrorKind::InvalidCycle(nu + nv + 1))? } } else { - CycleSol::NoCycle + Err(ErrorKind::NoCycle)? } } - fn solution(&mut self, us: &[u32], mut nu: u32, vs: &[u32], mut nv: u32) -> CycleSol { + fn solution(&mut self, us: &[T], mut nu: u64, vs: &[T], mut nv: u64) -> Result, Error> { let mut cycle = HashSet::new(); - cycle.insert(Edge { - u: us[0] as u64, - v: vs[0] as u64, - }); + cycle.insert(Edge { u: us[0], v: vs[0] }); while nu != 0 { // u's in even position; v's in odd - nu -= 1; + nu = nu - 1; cycle.insert(Edge { - u: us[((nu + 1) & !1) as usize] as u64, - v: us[(nu | 1) as usize] as u64, + u: us[((nu + 1) & !1) as usize], + v: us[(nu | 1) as usize], }); } 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, + u: vs[(nv | 1) as usize], + v: vs[((nv + 1) & !1) as usize], }); } let mut n = 0; - let mut sol = vec![0; self.proof_size]; - for nonce in 0..self.easiness { - let edge = self.cuckoo.new_edge(nonce); + let mut sol = vec![T::zero(); self.proof_size]; + for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { + let edge = self.new_edge(T::from(nonce).ok_or(ErrorKind::IntegerCast)?)?; if cycle.contains(&edge) { - sol[n] = nonce as u32; + sol[n] = T::from(nonce).ok_or(ErrorKind::IntegerCast)?; n += 1; cycle.remove(&edge); } } return if n == self.proof_size { - CycleSol::ValidProof(sol) + Ok(sol) } else { - CycleSol::NoCycle + Err(ErrorKind::NoCycle)? }; } -} -/// Utility to transform a 8 bytes of a byte array into a u64. -fn u8_to_u64(p: &[u8], 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 + /// Searches for a solution (simple implementation) + pub fn find_cycles_impl(&mut self) -> Result, Error> { + let mut us = [T::zero(); MAXPATHLEN]; + let mut vs = [T::zero(); MAXPATHLEN]; + for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { + us[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 0)?; + vs[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 1)?; + let u = self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + let v = self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + if us[0] == T::zero() { + continue; // ignore duplicate edges + } + let nu = self + .path(u, &mut us)? + .to_u64() + .ok_or(ErrorKind::IntegerCast)? as usize; + let nv = self + .path(v, &mut vs)? + .to_u64() + .ok_or(ErrorKind::IntegerCast)? as usize; + + let sol = self.find_sol(nu, &us, nv, &vs); + match sol { + Ok(s) => { + let mut proof = Proof::new(map_vec!(s.to_vec(), |&n| n.to_u64().unwrap_or(0))); + proof.cuckoo_sizeshift = self.edge_bits; + return Ok(vec![proof]); + } + Err(e) => match e.kind() { + ErrorKind::InvalidCycle(_) => continue, + ErrorKind::NoCycle => self.update_graph(nu, &us, nv, &vs)?, + _ => return Err(e), + }, + } + } + Err(ErrorKind::NoSolution)? + } + + /// 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_impl(&self, proof: &Proof) -> Result<(), Error> { + let easiness = self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)?; + let nonces = &proof.nonces; + let mut us = vec![T::zero(); proof.proof_size()]; + let mut vs = vec![T::zero(); proof.proof_size()]; + for n in 0..proof.proof_size() { + if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) { + return Err(ErrorKind::Verification("edge wrong size".to_owned()))?; + } + us[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 0)?; + vs[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 1)?; + } + let mut i = 0; + let mut count = proof.proof_size(); + loop { + let mut j = i; + for k in 0..proof.proof_size() { + // find unique other j with same vs[j] + if k != i && vs[k] == vs[i] { + if j != i { + return Err(ErrorKind::Verification("".to_owned()))?; + } + j = k; + } + } + if j == i { + return Err(ErrorKind::Verification("".to_owned()))?; + } + i = j; + for k in 0..proof.proof_size() { + // find unique other i with same us[i] + if k != j && us[k] == us[j] { + if i != j { + return Err(ErrorKind::Verification("".to_owned()))?; + } + i = k; + } + } + if i == j { + return Err(ErrorKind::Verification("".to_owned()))?; + } + count -= 2; + if i == 0 { + break; + } + } + match count == 0 { + true => Ok(()), + false => Err(ErrorKind::Verification("Invalid solution".to_owned()))?, + } + } } #[cfg(test)] mod test { use super::*; - use blake2; static V1: [u64; 42] = [ 0x3bbd, 0x4e96, 0x1013b, 0x1172b, 0x1371b, 0x13e6a, 0x1aaa6, 0x1b575, 0x1e237, 0x1ee88, @@ -357,99 +346,119 @@ mod test { 0x701f37b, ]; - fn blake2(data: &[u8]) -> blake2::blake2b::Blake2bResult { - blake2::blake2b::blake2b(32, &[], data) + #[test] + fn cuckoo_context() { + let ret = mine20_vectors::(); + if let Err(r) = ret { + panic!("mine20_vectors u32: Error: {}", r); + } + let ret = mine20_vectors::(); + if let Err(r) = ret { + panic!("mine20_vectors u64: Error: {}", r); + } + let ret = validate20_vectors::(); + if let Err(r) = ret { + panic!("validate20_vectors u32: Error: {}", r); + } + let ret = validate20_vectors::(); + if let Err(r) = ret { + panic!("validate20_vectors u64: Error: {}", r); + } + let ret = validate_fail::(); + if let Err(r) = ret { + panic!("validate_fail u32: Error: {}", r); + } + let ret = validate_fail::(); + if let Err(r) = ret { + panic!("validate_fail u64: Error: {}", r); + } + let ret = mine16_validate::(); + if let Err(r) = ret { + panic!("mine16_validate u32: Error: {}", r); + } + let ret = mine16_validate::(); + if let Err(r) = ret { + panic!("mine16_validate u64: Error: {}", r); + } } /// 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::from_hash(blake2(&[49]).as_bytes(), 75, 42, 20) - .mine() - .unwrap(); + fn mine20_vectors() -> Result<(), Error> + where + T: EdgeType, + { + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; let mut proof = Proof::new(V1.to_vec()); proof.cuckoo_sizeshift = 20; - assert_eq!(proof, nonces1); + assert_eq!(proof, res[0]); - let nonces2 = Miner::from_hash(blake2(&[50]).as_bytes(), 70, 42, 20) - .mine() - .unwrap(); + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; let mut proof = Proof::new(V2.to_vec()); proof.cuckoo_sizeshift = 20; - assert_eq!(proof, nonces2); + assert_eq!(proof, res[0]); - let nonces3 = Miner::from_hash(blake2(&[51]).as_bytes(), 70, 42, 20) - .mine() - .unwrap(); + //re-use context + cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; let mut proof = Proof::new(V3.to_vec()); proof.cuckoo_sizeshift = 20; - assert_eq!(proof, nonces3); - } - - #[test] - fn validate20_vectors() { - 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) - ); + assert_eq!(proof, res[0]); + Ok(()) } - /// Just going to disable this for now, as it's painful to try and get a - /// valid cuckoo28 vector (TBD: 30 is more relevant now anyhow) - #[test] - #[ignore] - fn validate28_vectors() { - let mut test_header = [0; 32]; - test_header[0] = 24; - assert!(Cuckoo::from_hash(&test_header, 28).verify(&Proof::new(V4.to_vec().clone()), 50)); + fn validate20_vectors() -> Result<(), Error> + where + T: EdgeType, + { + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + assert!(cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + assert!(cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok()); + cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; + assert!(cuckoo_ctx.verify(&Proof::new(V3.to_vec().clone())).is_ok()); + Ok(()) } - #[test] - fn validate_fail() { + fn validate_fail() -> Result<(), Error> + where + T: EdgeType, + { + // edge checks + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; // edge checks - 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) - ); + assert!(!cuckoo_ctx.verify(&Proof::new(vec![0; 42])).is_ok()); + assert!(!cuckoo_ctx.verify(&Proof::new(vec![0xffff; 42])).is_ok()); // wrong data for proof - assert!( - !Cuckoo::from_hash(blake2(&[50]).as_bytes(), 20) - .verify(&Proof::new(V1.to_vec().clone()), 75) - ); + cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + assert!(!cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); let mut test_header = [0; 32]; test_header[0] = 24; - assert!( - !Cuckoo::from_hash(blake2(&test_header).as_bytes(), 20) - .verify(&Proof::new(V4.to_vec().clone()), 50) - ); + let mut cuckoo_ctx = CuckooContext::::new(20, 42, 50, 10)?; + cuckoo_ctx.set_header_nonce(test_header.to_vec(), None)?; + assert!(!cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok()); + Ok(()) } - #[test] - fn mine20_validate() { - // cuckoo20 - for n in 1..5 { - let h = [n; 32]; - let nonces = Miner::from_hash(&h, 75, 42, 20).mine().unwrap(); - assert!(Cuckoo::from_hash(&h, 20).verify(&nonces, 75)); - } - // cuckoo18 + fn mine16_validate() -> Result<(), Error> + where + T: EdgeType, + { for n in 1..5 { let h = [n; 32]; - let nonces = Miner::from_hash(&h, 75, 42, 18).mine().unwrap(); - assert!(Cuckoo::from_hash(&h, 18).verify(&nonces, 75)); + let mut cuckoo_ctx = CuckooContext::::new(16, 42, 75, 10)?; + cuckoo_ctx.set_header_nonce(h.to_vec(), None)?; + let res = cuckoo_ctx.find_cycles()?; + assert!(cuckoo_ctx.verify(&res[0]).is_ok()) } + Ok(()) } } diff --git a/core/src/pow/cuckoo_ctx.rs b/core/src/pow/cuckoo_ctx.rs deleted file mode 100644 index 3b3a53f635..0000000000 --- a/core/src/pow/cuckoo_ctx.rs +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright 2018 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! 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 pow::common::{self, Edge, EdgeType}; -use pow::error::{Error, ErrorKind}; -use pow::num::ToPrimitive; -use pow::Proof; - -use std::cmp; -use std::collections::HashSet; - -const MAXPATHLEN: usize = 8192; - -/// Cuckoo cycle context -pub struct CuckooContext -where - T: EdgeType, -{ - edge_bits: u8, - siphash_keys: [u64; 4], - easiness: T, - proof_size: usize, - edge_mask: T, - graph: Vec, - _max_sols: usize, -} - -impl CuckooContext -where - T: EdgeType, -{ - /// Create a new cuckoo context with given parameters - pub fn new( - edge_bits: u8, - proof_size: usize, - easiness_pct: u32, - max_sols: usize, - ) -> Result, Error> { - let num_edges = 1 << edge_bits; - let easiness = easiness_pct.to_u64().ok_or(ErrorKind::IntegerCast)? * num_edges / 100; - Ok(CuckooContext { - edge_bits: edge_bits, - siphash_keys: [0; 4], - easiness: T::from(easiness).ok_or(ErrorKind::IntegerCast)?, - proof_size: proof_size, - edge_mask: T::from(num_edges / 2 - 1).ok_or(ErrorKind::IntegerCast)?, - graph: vec![T::zero(); num_edges as usize + 1], - _max_sols: max_sols, - }) - } - - fn reset(&mut self) -> Result<(), Error> { - let num_edges = 1 << self.edge_bits; - self.graph = vec![T::zero(); num_edges + 1]; - Ok(()) - } - - /// Set the header and optional nonce in the last part of the header - /// and create siphash keys - pub fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { - self.siphash_keys = common::set_header_nonce(header, nonce)?; - self.reset()?; - Ok(()) - } - - /// 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). - fn new_node(&self, edge: T, uorv: u64) -> Result { - common::sipnode::(&self.siphash_keys, edge, &self.edge_mask, uorv, true) - } - - /// 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. - fn new_edge(&self, nonce: T) -> Result, Error> { - Ok(Edge { - u: self.new_node(nonce, 0)?, - v: self.new_node(nonce, 1)?, - }) - } - - fn path(&self, mut u: T, us: &mut [T]) -> Result { - let mut nu = 0; - while u != T::zero() { - nu += 1; - if nu >= MAXPATHLEN { - while nu != 0 && us[(nu - 1) as usize] != u { - nu -= 1; - } - return Err(ErrorKind::Path)?; - } - us[nu as usize] = u; - u = self.graph[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; - } - Ok(T::from(nu).ok_or(ErrorKind::IntegerCast)?) - } - - fn update_graph( - &mut self, - mut nu: usize, - us: &[T], - mut nv: usize, - vs: &[T], - ) -> Result<(), Error> { - if nu < nv { - while nu != 0 { - nu -= 1; - self.graph[us[nu + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[nu]; - } - self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[0]; - } else { - while nv != 0 { - nv -= 1; - self.graph[vs[nv + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[nv]; - } - self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[0]; - } - Ok(()) - } - - fn find_sol( - &mut self, - mut nu: usize, - us: &[T], - mut nv: usize, - vs: &[T], - ) -> Result, Error> { - 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 == self.proof_size { - self.solution(&us, nu as u64, &vs, nv as u64) - } else { - Err(ErrorKind::InvalidCycle(nu + nv + 1))? - } - } else { - Err(ErrorKind::NoCycle)? - } - } - - fn solution(&mut self, us: &[T], mut nu: u64, vs: &[T], mut nv: u64) -> Result, Error> { - let mut cycle = HashSet::new(); - cycle.insert(Edge { u: us[0], v: vs[0] }); - while nu != 0 { - // u's in even position; v's in odd - nu = nu - 1; - cycle.insert(Edge { - u: us[((nu + 1) & !1) as usize], - v: us[(nu | 1) as usize], - }); - } - while nv != 0 { - // u's in odd position; v's in even - nv -= 1; - cycle.insert(Edge { - u: vs[(nv | 1) as usize], - v: vs[((nv + 1) & !1) as usize], - }); - } - let mut n = 0; - let mut sol = vec![T::zero(); self.proof_size]; - for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { - let edge = self.new_edge(T::from(nonce).ok_or(ErrorKind::IntegerCast)?)?; - if cycle.contains(&edge) { - sol[n] = T::from(nonce).ok_or(ErrorKind::IntegerCast)?; - n += 1; - cycle.remove(&edge); - } - } - return if n == self.proof_size { - Ok(sol) - } else { - Err(ErrorKind::NoCycle)? - }; - } - - /// Searches for a solution (simple implementation) - pub fn find_cycles(&mut self) -> Result, Error> { - let mut us = [T::zero(); MAXPATHLEN]; - let mut vs = [T::zero(); MAXPATHLEN]; - for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { - us[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 0)?; - vs[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 1)?; - let u = self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; - let v = self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; - if us[0] == T::zero() { - continue; // ignore duplicate edges - } - let nu = self - .path(u, &mut us)? - .to_u64() - .ok_or(ErrorKind::IntegerCast)? as usize; - let nv = self - .path(v, &mut vs)? - .to_u64() - .ok_or(ErrorKind::IntegerCast)? as usize; - - let sol = self.find_sol(nu, &us, nv, &vs); - match sol { - Ok(s) => { - let mut proof = Proof::new(map_vec!(s.to_vec(), |&n| n.to_u64().unwrap_or(0))); - proof.cuckoo_sizeshift = self.edge_bits; - return Ok(vec![proof]); - } - Err(e) => match e.kind() { - ErrorKind::InvalidCycle(_) => continue, - ErrorKind::NoCycle => self.update_graph(nu, &us, nv, &vs)?, - _ => return Err(e), - }, - } - } - Err(ErrorKind::NoSolution)? - } - - /// 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) -> Result<(), Error> { - let easiness = self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)?; - let nonces = &proof.nonces; - let mut us = vec![T::zero(); proof.proof_size()]; - let mut vs = vec![T::zero(); proof.proof_size()]; - for n in 0..proof.proof_size() { - if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) { - return Err(ErrorKind::Verification("edge wrong size".to_owned()))?; - } - us[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 0)?; - vs[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 1)?; - } - let mut i = 0; - let mut count = proof.proof_size(); - loop { - let mut j = i; - for k in 0..proof.proof_size() { - // find unique other j with same vs[j] - if k != i && vs[k] == vs[i] { - if j != i { - return Err(ErrorKind::Verification("".to_owned()))?; - } - j = k; - } - } - if j == i { - return Err(ErrorKind::Verification("".to_owned()))?; - } - i = j; - for k in 0..proof.proof_size() { - // find unique other i with same us[i] - if k != j && us[k] == us[j] { - if i != j { - return Err(ErrorKind::Verification("".to_owned()))?; - } - i = k; - } - } - if i == j { - return Err(ErrorKind::Verification("".to_owned()))?; - } - count -= 2; - if i == 0 { - break; - } - } - match count == 0 { - true => Ok(()), - false => Err(ErrorKind::Verification("Invalid solution".to_owned()))?, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - static V1: [u64; 42] = [ - 0x3bbd, 0x4e96, 0x1013b, 0x1172b, 0x1371b, 0x13e6a, 0x1aaa6, 0x1b575, 0x1e237, 0x1ee88, - 0x22f94, 0x24223, 0x25b4f, 0x2e9f3, 0x33b49, 0x34063, 0x3454a, 0x3c081, 0x3d08e, 0x3d863, - 0x4285a, 0x42f22, 0x43122, 0x4b853, 0x4cd0c, 0x4f280, 0x557d5, 0x562cf, 0x58e59, 0x59a62, - 0x5b568, 0x644b9, 0x657e9, 0x66337, 0x6821c, 0x7866f, 0x7e14b, 0x7ec7c, 0x7eed7, 0x80643, - 0x8628c, 0x8949e, - ]; - static V2: [u64; 42] = [ - 0x5e3a, 0x8a8b, 0x103d8, 0x1374b, 0x14780, 0x16110, 0x1b571, 0x1c351, 0x1c826, 0x28228, - 0x2909f, 0x29516, 0x2c1c4, 0x334eb, 0x34cdd, 0x38a2c, 0x3ad23, 0x45ac5, 0x46afe, 0x50f43, - 0x51ed6, 0x52ddd, 0x54a82, 0x5a46b, 0x5dbdb, 0x60f6f, 0x60fcd, 0x61c78, 0x63899, 0x64dab, - 0x6affc, 0x6b569, 0x72639, 0x73987, 0x78806, 0x7b98e, 0x7c7d7, 0x7ddd4, 0x7fa88, 0x8277c, - 0x832d9, 0x8ba6f, - ]; - static V3: [u64; 42] = [ - 0x308b, 0x9004, 0x91fc, 0x983e, 0x9d67, 0xa293, 0xb4cb, 0xb6c8, 0xccc8, 0xdddc, 0xf04d, - 0x1372f, 0x16ec9, 0x17b61, 0x17d03, 0x1e3bc, 0x1fb0f, 0x29e6e, 0x2a2ca, 0x2a719, 0x3a078, - 0x3b7cc, 0x3c71d, 0x40daa, 0x43e17, 0x46adc, 0x4b359, 0x4c3aa, 0x4ce92, 0x4d06e, 0x51140, - 0x565ac, 0x56b1f, 0x58a8b, 0x5e410, 0x5e607, 0x5ebb5, 0x5f8ae, 0x7aeac, 0x7b902, 0x7d6af, - 0x7f400, - ]; - // cuckoo28 at 50% edges of letter 'u' - static V4: [u64; 42] = [ - 0xf7243, 0x11f130, 0x193812, 0x23b565, 0x279ac3, 0x69b270, 0xe0778f, 0xef51fc, 0x10bf6e8, - 0x13ccf7d, 0x1551177, 0x1b6cfd2, 0x1f872c3, 0x2075681, 0x2e23ccc, 0x2e4c0aa, 0x2f607f1, - 0x3007eeb, 0x3407e9a, 0x35423f9, 0x39e48bf, 0x45e3bf6, 0x46aa484, 0x47c0fe1, 0x4b1d5a6, - 0x4bae0ba, 0x4dfdbaf, 0x5048eda, 0x537da6b, 0x5402887, 0x56b8897, 0x5bd8e8b, 0x622de20, - 0x62be5ce, 0x62d538e, 0x6464518, 0x650a6d5, 0x66ec4fa, 0x66f9476, 0x6b1e5f6, 0x6fd5d88, - 0x701f37b, - ]; - - #[test] - fn cuckoo_context() { - let ret = mine20_vectors::(); - if let Err(r) = ret { - panic!("mine20_vectors u32: Error: {}", r); - } - let ret = mine20_vectors::(); - if let Err(r) = ret { - panic!("mine20_vectors u64: Error: {}", r); - } - let ret = validate20_vectors::(); - if let Err(r) = ret { - panic!("validate20_vectors u32: Error: {}", r); - } - let ret = validate20_vectors::(); - if let Err(r) = ret { - panic!("validate20_vectors u64: Error: {}", r); - } - let ret = validate_fail::(); - if let Err(r) = ret { - panic!("validate_fail u32: Error: {}", r); - } - let ret = validate_fail::(); - if let Err(r) = ret { - panic!("validate_fail u64: Error: {}", r); - } - let ret = mine16_validate::(); - if let Err(r) = ret { - panic!("mine16_validate u32: Error: {}", r); - } - let ret = mine16_validate::(); - if let Err(r) = ret { - panic!("mine16_validate u64: Error: {}", r); - } - } - - /// Find a 42-cycle on Cuckoo20 at 75% easiness and verify against a few - /// known cycle proofs - /// generated by other implementations. - fn mine20_vectors() -> Result<(), Error> - where - T: EdgeType, - { - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; - let res = cuckoo_ctx.find_cycles()?; - let mut proof = Proof::new(V1.to_vec()); - proof.cuckoo_sizeshift = 20; - assert_eq!(proof, res[0]); - - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; - cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; - let res = cuckoo_ctx.find_cycles()?; - let mut proof = Proof::new(V2.to_vec()); - proof.cuckoo_sizeshift = 20; - assert_eq!(proof, res[0]); - - //re-use context - cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; - let res = cuckoo_ctx.find_cycles()?; - let mut proof = Proof::new(V3.to_vec()); - proof.cuckoo_sizeshift = 20; - assert_eq!(proof, res[0]); - Ok(()) - } - - fn validate20_vectors() -> Result<(), Error> - where - T: EdgeType, - { - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; - assert!(cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; - cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; - assert!(cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok()); - cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; - assert!(cuckoo_ctx.verify(&Proof::new(V3.to_vec().clone())).is_ok()); - Ok(()) - } - - fn validate_fail() -> Result<(), Error> - where - T: EdgeType, - { - // edge checks - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; - // edge checks - assert!(!cuckoo_ctx.verify(&Proof::new(vec![0; 42])).is_ok()); - assert!(!cuckoo_ctx.verify(&Proof::new(vec![0xffff; 42])).is_ok()); - // wrong data for proof - cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; - assert!(!cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); - let mut test_header = [0; 32]; - test_header[0] = 24; - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 50, 10)?; - cuckoo_ctx.set_header_nonce(test_header.to_vec(), None)?; - assert!(!cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok()); - Ok(()) - } - - fn mine16_validate() -> Result<(), Error> - where - T: EdgeType, - { - for n in 1..5 { - let h = [n; 32]; - let mut cuckoo_ctx = CuckooContext::::new(16, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce(h.to_vec(), None)?; - let res = cuckoo_ctx.find_cycles()?; - assert!(cuckoo_ctx.verify(&res[0]).is_ok()) - } - Ok(()) - } -} diff --git a/core/src/pow/miner.rs b/core/src/pow/miner.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/core/src/pow/miner.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 2f0748af55..2276f3a7ba 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -39,7 +39,6 @@ extern crate grin_util as util; mod common; pub mod cuckatoo; pub mod cuckoo; -pub mod cuckoo_ctx; mod error; mod siphash; mod types; @@ -49,15 +48,21 @@ use consensus; use core::{Block, BlockHeader}; use genesis; use global; -use pow::cuckoo::{Cuckoo, Error}; +pub use pow::cuckoo::CuckooContext; +pub use pow::cuckatoo::CuckatooContext; pub use self::types::*; +pub use self::common::EdgeType; +pub use pow::error::Error; + +const MAX_SOLS:u32 = 10; /// 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::from_hash(bh.pre_pow_hash().as_ref(), cuckoo_sz) - .verify(&bh.pow.proof, consensus::EASINESS as u64) +pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u8) -> Result<(), Error> { + let mut ctx = global::create_pow_context::(cuckoo_sz, bh.pow.proof.nonces.len(), consensus::EASINESS, MAX_SOLS)?; + ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None)?; + ctx.verify(&bh.pow.proof) } /// Mines a genesis block using the internal miner @@ -74,7 +79,7 @@ pub fn mine_genesis_block() -> Result { let sz = global::min_sizeshift(); let proof_size = global::proofsize(); - pow_size(&mut gen.header, genesis_difficulty, proof_size, sz).unwrap(); + pow_size(&mut gen.header, genesis_difficulty, proof_size, sz)?; Ok(gen) } @@ -86,7 +91,8 @@ pub fn pow_size( diff: Difficulty, proof_size: usize, sz: u8, -) -> Result<(), Error> { +) -> Result<(), Error> +{ let start_nonce = bh.pow.nonce; // set the nonce for faster solution finding in user testing @@ -98,8 +104,10 @@ pub fn pow_size( loop { // 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(bh, consensus::EASINESS, proof_size, sz).mine() { - bh.pow.proof = proof; + let mut ctx = global::create_pow_context::(sz, proof_size, consensus::EASINESS, MAX_SOLS)?; + ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None)?; + if let Ok(proofs) = ctx.find_cycles() { + bh.pow.proof = proofs[0].clone(); if bh.pow.to_difficulty() >= diff { return Ok(()); } @@ -137,6 +145,6 @@ mod test { ).unwrap(); assert!(b.header.pow.nonce != 310); assert!(b.header.pow.to_difficulty() >= Difficulty::one()); - assert!(verify_size(&b.header, global::min_sizeshift())); + assert!(verify_size(&b.header, global::min_sizeshift()).is_ok()); } } diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index 49848a6578..f348d664da 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -26,7 +26,24 @@ use core::hash::Hashed; use global; use ser::{self, Readable, Reader, Writeable, Writer}; -use pow::num::ToPrimitive; +use pow::common::EdgeType; +use pow::error::Error; + +/// Generic trait for a solver/verifier providing common interface into Cuckoo-family PoW +/// Mostly used for verification, but also for test mining if necessary +pub trait PoWContext +where + T: EdgeType +{ + /// Create new instance of context with appropriate parameters + fn new(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result, Error>; + /// Sets the header along with an optional nonce at the end + fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error>; + /// find solutions using the stored parameters and header + fn find_cycles(&mut self) -> Result, Error>; + /// Verify a solution with the stored parameters + fn verify(&self, proof: &Proof) -> Result<(), Error>; +} /// The difficulty is defined as the maximum target divided by the block hash. #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] diff --git a/servers/src/common/types.rs b/servers/src/common/types.rs index 9d2a5f7c2d..9682f4d7b1 100644 --- a/servers/src/common/types.rs +++ b/servers/src/common/types.rs @@ -43,7 +43,7 @@ pub enum Error { /// Error originating from wallet API. Wallet(wallet::Error), /// Error originating from the cuckoo miner - Cuckoo(pow::cuckoo::Error), + Cuckoo(pow::Error), } impl From for Error { @@ -63,8 +63,8 @@ impl From for Error { } } -impl From for Error { - fn from(e: pow::cuckoo::Error) -> Error { +impl From for Error { + fn from(e: pow::Error) -> Error { Error::Cuckoo(e) } } diff --git a/servers/src/mining/stratumserver.rs b/servers/src/mining/stratumserver.rs index e7a935b632..b1b6b3bacc 100644 --- a/servers/src/mining/stratumserver.rs +++ b/servers/src/mining/stratumserver.rs @@ -529,7 +529,7 @@ impl StratumServer { ); } else { // Do some validation but dont submit - if !pow::verify_size(&b.header, global::min_sizeshift()) { + if !pow::verify_size(&b.header, global::min_sizeshift()).is_ok() { // Return error status error!( LOGGER, diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index 7c73221347..4146763ef0 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -26,7 +26,7 @@ use common::types::StratumServerConfig; use core::core::hash::{Hash, Hashed}; use core::core::verifier_cache::VerifierCache; use core::core::{Block, BlockHeader}; -use core::pow::cuckoo; +use core::pow::PoWContext; use core::{consensus, global}; use mining::mine_block; use pool; @@ -96,14 +96,10 @@ impl Miner { let mut iter_count = 0; while head.hash() == *latest_hash && Utc::now().timestamp() < deadline { - if let Ok(proof) = cuckoo::Miner::new( - &b.header, - consensus::EASINESS, - global::proofsize(), - global::min_sizeshift(), - ).mine() - { - b.header.pow.proof = proof; + let mut ctx = global::create_pow_context::(global::min_sizeshift(), global::proofsize(), consensus::EASINESS, 10).unwrap(); + ctx.set_header_nonce(b.header.pre_pow_hash().to_vec(), None).unwrap(); + if let Ok(proofs) = ctx.find_cycles() { + b.header.pow.proof = proofs[0].clone(); let proof_diff = b.header.pow.to_difficulty(); if proof_diff >= (b.header.total_difficulty() - head.total_difficulty()) { return true; From eb22b203d4313d490b921547a6174b9438d66c43 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Wed, 26 Sep 2018 16:44:12 +0100 Subject: [PATCH 16/22] rustfmt --- chain/src/pipe.rs | 6 +++--- core/src/global.rs | 8 ++++++-- core/src/pow/cuckatoo.rs | 14 ++++++++++++-- core/src/pow/cuckoo.rs | 20 +++++++++++++++++--- core/src/pow/mod.rs | 21 +++++++++++++-------- core/src/pow/types.rs | 9 +++++++-- servers/src/mining/test_miner.rs | 10 ++++++++-- 7 files changed, 66 insertions(+), 22 deletions(-) diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index ad5c33261f..4d51b7b994 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -501,7 +501,8 @@ fn validate_block( &prev.total_kernel_offset, &prev.total_kernel_sum, verifier_cache, - ).map_err(|e| ErrorKind::InvalidBlockProof(e))?; + ) + .map_err(|e| ErrorKind::InvalidBlockProof(e))?; Ok(()) } @@ -544,8 +545,7 @@ fn verify_block_sums(b: &Block, ext: &mut txhashset::Extension) -> Result<(), Er let offset = b.header.total_kernel_offset(); // Verify the kernel sums for the block_sums with the new block applied. - let (utxo_sum, kernel_sum) = - (block_sums, b as &Committed).verify_kernel_sums(overage, offset)?; + let (utxo_sum, kernel_sum) = (block_sums, b as &Committed).verify_kernel_sums(overage, offset)?; // Save the new block_sums for the new block to the db via the batch. ext.batch.save_block_sums( diff --git a/core/src/global.rs b/core/src/global.rs index 17a2f19164..f8ba3ccaa9 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -102,7 +102,6 @@ pub enum PoWContextTypes { Cuckatoo, } - lazy_static!{ /// The mining parameter mode pub static ref CHAIN_TYPE: RwLock = @@ -121,7 +120,12 @@ pub fn set_mining_mode(mode: ChainTypes) { /// Return either a cuckoo context or a cuckatoo context /// Single change point -pub fn create_pow_context(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result>, pow::Error> +pub fn create_pow_context( + edge_bits: u8, + proof_size: usize, + easiness_pct: u32, + max_sols: u32, +) -> Result>, pow::Error> where T: EdgeType, { diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index be1e1c373c..cab4c35180 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -178,8 +178,18 @@ impl PoWContext for CuckatooContext where T: EdgeType, { - fn new(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result, Error> { - Ok(Box::new(CuckatooContext::::new_impl(edge_bits, proof_size, easiness_pct, max_sols)?)) + fn new( + edge_bits: u8, + proof_size: usize, + easiness_pct: u32, + max_sols: u32, + ) -> Result, Error> { + Ok(Box::new(CuckatooContext::::new_impl( + edge_bits, + proof_size, + easiness_pct, + max_sols, + )?)) } fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index ddf38f319d..e32285f966 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -45,8 +45,18 @@ impl PoWContext for CuckooContext where T: EdgeType, { - fn new(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result, Error> { - Ok(Box::new(CuckooContext::::new_impl(edge_bits, proof_size, easiness_pct, max_sols)?)) + fn new( + edge_bits: u8, + proof_size: usize, + easiness_pct: u32, + max_sols: u32, + ) -> Result, Error> { + Ok(Box::new(CuckooContext::::new_impl( + edge_bits, + proof_size, + easiness_pct, + max_sols, + )?)) } fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { @@ -94,7 +104,11 @@ where /// Set the header and optional nonce in the last part of the header /// and create siphash keys - pub fn set_header_nonce_impl(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { + pub fn set_header_nonce_impl( + &mut self, + header: Vec, + nonce: Option, + ) -> Result<(), Error> { self.siphash_keys = common::set_header_nonce(header, nonce)?; self.reset()?; Ok(()) diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 2276f3a7ba..833d68e096 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -49,18 +49,23 @@ use core::{Block, BlockHeader}; use genesis; use global; -pub use pow::cuckoo::CuckooContext; -pub use pow::cuckatoo::CuckatooContext; -pub use self::types::*; pub use self::common::EdgeType; +pub use self::types::*; +pub use pow::cuckatoo::CuckatooContext; +pub use pow::cuckoo::CuckooContext; pub use pow::error::Error; -const MAX_SOLS:u32 = 10; +const MAX_SOLS: u32 = 10; /// 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) -> Result<(), Error> { - let mut ctx = global::create_pow_context::(cuckoo_sz, bh.pow.proof.nonces.len(), consensus::EASINESS, MAX_SOLS)?; + let mut ctx = global::create_pow_context::( + cuckoo_sz, + bh.pow.proof.nonces.len(), + consensus::EASINESS, + MAX_SOLS, + )?; ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None)?; ctx.verify(&bh.pow.proof) } @@ -91,8 +96,7 @@ pub fn pow_size( diff: Difficulty, proof_size: usize, sz: u8, -) -> Result<(), Error> -{ +) -> Result<(), Error> { let start_nonce = bh.pow.nonce; // set the nonce for faster solution finding in user testing @@ -104,7 +108,8 @@ pub fn pow_size( loop { // if we found a cycle (not guaranteed) and the proof hash is higher that the // diff, we're all good - let mut ctx = global::create_pow_context::(sz, proof_size, consensus::EASINESS, MAX_SOLS)?; + let mut ctx = + global::create_pow_context::(sz, proof_size, consensus::EASINESS, MAX_SOLS)?; ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None)?; if let Ok(proofs) = ctx.find_cycles() { bh.pow.proof = proofs[0].clone(); diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index f348d664da..57147c5dae 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -33,10 +33,15 @@ use pow::error::Error; /// Mostly used for verification, but also for test mining if necessary pub trait PoWContext where - T: EdgeType + T: EdgeType, { /// Create new instance of context with appropriate parameters - fn new(edge_bits: u8, proof_size: usize, easiness_pct: u32, max_sols: u32) -> Result, Error>; + fn new( + edge_bits: u8, + proof_size: usize, + easiness_pct: u32, + max_sols: u32, + ) -> Result, Error>; /// Sets the header along with an optional nonce at the end fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error>; /// find solutions using the stored parameters and header diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index 4146763ef0..ed92e32784 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -96,8 +96,14 @@ impl Miner { let mut iter_count = 0; while head.hash() == *latest_hash && Utc::now().timestamp() < deadline { - let mut ctx = global::create_pow_context::(global::min_sizeshift(), global::proofsize(), consensus::EASINESS, 10).unwrap(); - ctx.set_header_nonce(b.header.pre_pow_hash().to_vec(), None).unwrap(); + let mut ctx = global::create_pow_context::( + global::min_sizeshift(), + global::proofsize(), + consensus::EASINESS, + 10, + ).unwrap(); + ctx.set_header_nonce(b.header.pre_pow_hash().to_vec(), None) + .unwrap(); if let Ok(proofs) = ctx.find_cycles() { b.header.pow.proof = proofs[0].clone(); let proof_diff = b.header.pow.to_difficulty(); From 9f60884edc11770adbd2c103daf946731e3cf744 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Thu, 27 Sep 2018 13:55:20 +0100 Subject: [PATCH 17/22] create macros for integer casting/unwraps --- core/src/pow/common.rs | 29 +++++++++++++ core/src/pow/cuckatoo.rs | 92 +++++++++++++++------------------------- core/src/pow/cuckoo.rs | 50 ++++++++++------------ core/src/pow/mod.rs | 1 + 4 files changed, 87 insertions(+), 85 deletions(-) diff --git a/core/src/pow/common.rs b/core/src/pow/common.rs index a7a1420fc6..142b21251a 100644 --- a/core/src/pow/common.rs +++ b/core/src/pow/common.rs @@ -122,3 +122,32 @@ where } Ok(T::from(masked).ok_or(ErrorKind::IntegerCast)?) } + +/// Macros to clean up integer unwrapping +#[macro_export] +macro_rules! to_u64 { + ($n:expr) => { + $n.to_u64().ok_or(ErrorKind::IntegerCast)? + }; +} + +#[macro_export] +macro_rules! to_u32 { + ($n:expr) => { + $n.to_u64().ok_or(ErrorKind::IntegerCast)? as u32 + }; +} + +#[macro_export] +macro_rules! to_usize { + ($n:expr) => { + $n.to_u64().ok_or(ErrorKind::IntegerCast)? as usize + }; +} + +#[macro_export] +macro_rules! to_edge { + ($n:expr) => { + T::from($n).ok_or(ErrorKind::IntegerCast)? + }; +} diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index cab4c35180..5d757182c2 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -18,8 +18,7 @@ use std::mem; use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; use croaring::Bitmap; -use pow::common::EdgeType; -use pow::common::{self, Link}; +use pow::common::{self, EdgeType, Link}; use pow::error::{Error, ErrorKind}; use pow::{PoWContext, Proof}; use util; @@ -54,7 +53,7 @@ where { /// Create a new graph with given parameters pub fn new(max_edges: T, max_sols: u32, proof_size: usize) -> Result, Error> { - let max_nodes = 2 * max_edges.to_u64().ok_or(ErrorKind::IntegerCast)?; + let max_nodes = 2 * to_u64!(max_edges); Ok(Graph { max_edges: max_edges, max_nodes: max_nodes, @@ -64,37 +63,35 @@ where max_sols: max_sols, solutions: vec![], proof_size: proof_size, - nil: T::from(T::max_value()).ok_or(ErrorKind::IntegerCast)?, + nil: T::max_value(), }) } pub fn reset(&mut self) -> Result<(), Error> { //TODO: Can be optimised self.links = Vec::with_capacity(2 * self.max_nodes as usize); - self.adj_list = vec![ - T::from(T::max_value()).ok_or(ErrorKind::IntegerCast)?; - 2 * self.max_nodes as usize - ]; + self.adj_list = vec![T::max_value(); 2 * self.max_nodes as usize]; self.solutions = vec![Proof::zero(self.proof_size); 1]; self.visited = Bitmap::create(); Ok(()) } pub fn byte_count(&self) -> Result { - Ok(2 - * self.max_edges.to_u64().ok_or(ErrorKind::IntegerCast)? - * mem::size_of::>() as u64 + mem::size_of::() as u64 * 2 * self.max_nodes) + Ok( + 2 * to_u64!(self.max_edges) * mem::size_of::>() as u64 + + mem::size_of::() as u64 * 2 * self.max_nodes, + ) } /// Add an edge to the graph pub fn add_edge(&mut self, u: T, mut v: T) -> Result<(), Error> { - let max_nodes_t = T::from(self.max_nodes).ok_or(ErrorKind::IntegerCast)?; + let max_nodes_t = to_edge!(self.max_nodes); if u >= max_nodes_t || v >= max_nodes_t { return Err(ErrorKind::EdgeAddition)?; } - v = v + T::from(self.max_nodes).ok_or(ErrorKind::IntegerCast)?; - let adj_u = self.adj_list[(u ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; - let adj_v = self.adj_list[(v ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + v = v + to_edge!(self.max_nodes); + let adj_u = self.adj_list[to_usize!(u ^ T::one())]; + let adj_v = self.adj_list[to_usize!(v ^ T::one())]; if adj_u != self.nil && adj_v != self.nil { let sol_index = self.solutions.len() - 1; self.solutions[sol_index].nonces[0] = self.links.len() as u64 / 2; @@ -102,21 +99,19 @@ where } let ulink = self.links.len(); let vlink = self.links.len() + 1; - if T::from(vlink).ok_or(ErrorKind::IntegerCast)? == self.nil { + if to_edge!(vlink) == self.nil { return Err(ErrorKind::EdgeAddition)?; } self.links.push(Link { - next: self.adj_list[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize], + next: self.adj_list[to_usize!(u)], to: u, }); self.links.push(Link { - next: self.adj_list[v.to_u64().ok_or(ErrorKind::IntegerCast)? as usize], + next: self.adj_list[to_usize!(v)], to: v, }); - self.adj_list[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = - T::from(ulink).ok_or(ErrorKind::IntegerCast)?; - self.adj_list[v.to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = - T::from(vlink).ok_or(ErrorKind::IntegerCast)?; + self.adj_list[to_usize!(u)] = T::from(ulink).ok_or(ErrorKind::IntegerCast)?; + self.adj_list[to_usize!(v)] = T::from(vlink).ok_or(ErrorKind::IntegerCast)?; Ok(()) } @@ -125,7 +120,7 @@ where } fn cycles_with_link(&mut self, len: u32, u: T, dest: T) -> Result<(), Error> { - if self.test_bit((u >> 1).to_u64().ok_or(ErrorKind::IntegerCast)?) { + if self.test_bit(to_u64!(u >> 1)) { return Ok(()); } if (u ^ T::one()) == dest { @@ -139,24 +134,20 @@ where } else if len == self.proof_size as u32 { return Ok(()); } - let mut au1 = - self.adj_list[(u ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + let mut au1 = self.adj_list[to_usize!(u ^ T::one())]; if au1 != self.nil { - self.visited - .add((u >> 1).to_u64().ok_or(ErrorKind::IntegerCast)? as u32); + self.visited.add(to_u32!(u >> 1)); while au1 != self.nil { let i = self.solutions.len() - 1; - self.solutions[i].nonces[len as usize] = - au1.to_u64().ok_or(ErrorKind::IntegerCast)? / 2; - let link_index = (au1 ^ T::one()).to_u64().ok_or(ErrorKind::IntegerCast)? as usize; + self.solutions[i].nonces[len as usize] = to_u64!(au1) / 2; + let link_index = to_usize!(au1 ^ T::one()); let link = self.links[link_index].to; if link != self.nil { self.cycles_with_link(len + 1, link, dest)?; } - au1 = self.links[au1.to_u64().ok_or(ErrorKind::IntegerCast)? as usize].next; + au1 = self.links[to_usize!(au1)].next; } - self.visited - .remove((u >> 1).to_u64().ok_or(ErrorKind::IntegerCast)? as u32); + self.visited.remove(to_u32!(u >> 1)); } Ok(()) } @@ -218,17 +209,13 @@ where ) -> Result, Error> { let num_edges = 1 << edge_bits; let num_nodes = 2 * num_edges as u64; - let easiness = easiness_pct.to_u64().ok_or(ErrorKind::IntegerCast)? * num_nodes / 100; + let easiness = to_u64!(easiness_pct) * num_nodes / 100; Ok(CuckatooContext { siphash_keys: [0; 4], - easiness: T::from(easiness).ok_or(ErrorKind::IntegerCast)?, - graph: Graph::new( - T::from(num_edges).ok_or(ErrorKind::IntegerCast)?, - max_sols, - proof_size, - )?, + easiness: to_edge!(easiness), + graph: Graph::new(to_edge!(num_edges), max_sols, proof_size)?, proof_size: proof_size, - edge_mask: T::from(num_edges - 1).ok_or(ErrorKind::IntegerCast)?, + edge_mask: to_edge!(num_edges - 1), }) } @@ -267,13 +254,10 @@ where /// Simple implementation of algorithm pub fn find_cycles_impl(&mut self) -> Result, Error> { - for n in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? { - let u = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 0)?; - let v = self.sipnode(T::from(n).ok_or(ErrorKind::IntegerCast)?, 1)?; - self.graph.add_edge( - T::from(u).ok_or(ErrorKind::IntegerCast)?, - T::from(v).ok_or(ErrorKind::IntegerCast)?, - )?; + for n in 0..to_u64!(self.easiness) { + let u = self.sipnode(to_edge!(n), 0)?; + let v = self.sipnode(to_edge!(n), 1)?; + self.graph.add_edge(to_edge!(u), to_edge!(v))?; } self.graph.solutions.pop(); for s in &mut self.graph.solutions { @@ -298,20 +282,14 @@ where let mut xor1: u64 = xor0; for n in 0..proof.proof_size() { - if nonces[n] > self.edge_mask.to_u64().ok_or(ErrorKind::IntegerCast)? { + if nonces[n] > to_u64!(self.edge_mask) { return Err(ErrorKind::Verification("edge too big".to_owned()))?; } if n > 0 && nonces[n] <= nonces[n - 1] { return Err(ErrorKind::Verification("edges not ascending".to_owned()))?; } - uvs[2 * n] = self - .sipnode(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 0)? - .to_u64() - .ok_or(ErrorKind::IntegerCast)?; - uvs[2 * n + 1] = self - .sipnode(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 1)? - .to_u64() - .ok_or(ErrorKind::IntegerCast)?; + uvs[2 * n] = to_u64!(self.sipnode(to_edge!(nonces[n]), 0)?); + uvs[2 * n + 1] = to_u64!(self.sipnode(to_edge!(nonces[n]), 1)?); xor0 ^= uvs[2 * n]; xor1 ^= uvs[2 * n + 1]; } diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index e32285f966..e7ba7fc978 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -84,13 +84,13 @@ where max_sols: u32, ) -> Result, Error> { let num_edges = 1 << edge_bits; - let easiness = easiness_pct.to_u64().ok_or(ErrorKind::IntegerCast)? * num_edges / 100; + let easiness = to_u64!(easiness_pct) * num_edges / 100; Ok(CuckooContext { edge_bits: edge_bits, siphash_keys: [0; 4], - easiness: T::from(easiness).ok_or(ErrorKind::IntegerCast)?, + easiness: to_edge!(easiness), proof_size: proof_size, - edge_mask: T::from(num_edges / 2 - 1).ok_or(ErrorKind::IntegerCast)?, + edge_mask: to_edge!(num_edges / 2 - 1), graph: vec![T::zero(); num_edges as usize + 1], _max_sols: max_sols, }) @@ -142,9 +142,9 @@ where return Err(ErrorKind::Path)?; } us[nu as usize] = u; - u = self.graph[u.to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + u = self.graph[to_usize!(u)]; } - Ok(T::from(nu).ok_or(ErrorKind::IntegerCast)?) + Ok(to_edge!(nu)) } fn update_graph( @@ -157,15 +157,15 @@ where if nu < nv { while nu != 0 { nu -= 1; - self.graph[us[nu + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[nu]; + self.graph[to_usize!(us[nu + 1])] = us[nu]; } - self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[0]; + self.graph[to_usize!(us[0])] = vs[0]; } else { while nv != 0 { nv -= 1; - self.graph[vs[nv + 1].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = vs[nv]; + self.graph[to_usize!(vs[nv + 1])] = vs[nv]; } - self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize] = us[0]; + self.graph[to_usize!(vs[0])] = us[0]; } Ok(()) } @@ -216,10 +216,10 @@ where } let mut n = 0; let mut sol = vec![T::zero(); self.proof_size]; - for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { - let edge = self.new_edge(T::from(nonce).ok_or(ErrorKind::IntegerCast)?)?; + for nonce in 0..to_usize!(self.easiness) { + let edge = self.new_edge(to_edge!(nonce))?; if cycle.contains(&edge) { - sol[n] = T::from(nonce).ok_or(ErrorKind::IntegerCast)?; + sol[n] = to_edge!(nonce); n += 1; cycle.remove(&edge); } @@ -235,22 +235,16 @@ where pub fn find_cycles_impl(&mut self) -> Result, Error> { let mut us = [T::zero(); MAXPATHLEN]; let mut vs = [T::zero(); MAXPATHLEN]; - for nonce in 0..self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)? as usize { - us[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 0)?; - vs[0] = self.new_node(T::from(nonce).ok_or(ErrorKind::IntegerCast)?, 1)?; - let u = self.graph[us[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; - let v = self.graph[vs[0].to_u64().ok_or(ErrorKind::IntegerCast)? as usize]; + for nonce in 0..to_usize!(self.easiness) { + us[0] = self.new_node(to_edge!(nonce), 0)?; + vs[0] = self.new_node(to_edge!(nonce), 1)?; + let u = self.graph[to_usize!(us[0])]; + let v = self.graph[to_usize!(vs[0])]; if us[0] == T::zero() { continue; // ignore duplicate edges } - let nu = self - .path(u, &mut us)? - .to_u64() - .ok_or(ErrorKind::IntegerCast)? as usize; - let nv = self - .path(v, &mut vs)? - .to_u64() - .ok_or(ErrorKind::IntegerCast)? as usize; + let nu = to_usize!(self.path(u, &mut us)?); + let nv = to_usize!(self.path(v, &mut vs)?); let sol = self.find_sol(nu, &us, nv, &vs); match sol { @@ -273,7 +267,7 @@ where /// 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_impl(&self, proof: &Proof) -> Result<(), Error> { - let easiness = self.easiness.to_u64().ok_or(ErrorKind::IntegerCast)?; + let easiness = to_u64!(self.easiness); let nonces = &proof.nonces; let mut us = vec![T::zero(); proof.proof_size()]; let mut vs = vec![T::zero(); proof.proof_size()]; @@ -281,8 +275,8 @@ where if nonces[n] >= easiness || (n != 0 && nonces[n] <= nonces[n - 1]) { return Err(ErrorKind::Verification("edge wrong size".to_owned()))?; } - us[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 0)?; - vs[n] = self.new_node(T::from(nonces[n]).ok_or(ErrorKind::IntegerCast)?, 1)?; + us[n] = self.new_node(to_edge!(nonces[n]), 0)?; + vs[n] = self.new_node(to_edge!(nonces[n]), 1)?; } let mut i = 0; let mut count = proof.proof_size(); diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 833d68e096..c3ef8992a8 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -36,6 +36,7 @@ extern crate serde; extern crate grin_util as util; +#[macro_use] mod common; pub mod cuckatoo; pub mod cuckoo; From 60cb8da724f098649a927ed1e77ee223c6d46bf1 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Thu, 27 Sep 2018 15:58:46 +0100 Subject: [PATCH 18/22] don't instantiate structs when just verifying, add test validation vector for cuckatoo 29 --- core/src/pow/cuckatoo.rs | 80 ++++++++++++++++++++++++++++---- core/src/pow/cuckoo.rs | 29 ++++++------ core/src/pow/mod.rs | 4 +- core/src/pow/types.rs | 3 +- servers/src/mining/test_miner.rs | 2 +- 5 files changed, 93 insertions(+), 25 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 5d757182c2..739f896fd9 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -183,8 +183,8 @@ where )?)) } - fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { - self.set_header_nonce_impl(header, nonce) + fn set_header_nonce(&mut self, header: Vec, nonce: Option, solve:bool) -> Result<(), Error> { + self.set_header_nonce_impl(header, nonce, solve) } fn find_cycles(&mut self) -> Result, Error> { @@ -236,6 +236,7 @@ where &mut self, mut header: Vec, nonce: Option, + solve: bool ) -> Result<(), Error> { let len = header.len(); header.truncate(len - mem::size_of::()); @@ -243,7 +244,9 @@ where header.write_u32::(n)?; } self.siphash_keys = common::set_header_nonce(header, nonce)?; - self.graph.reset()?; + if solve { + self.graph.reset()?; + } Ok(()) } @@ -339,15 +342,76 @@ where mod test { use super::*; + // Cuckatoo 29 Solution for Header [0u8;80] - nonce 20 + static V1_29: [u64; 42] = [ + 0x48a9e2, 0x9cf043, 0x155ca30, 0x18f4783, 0x248f86c, 0x2629a64, 0x5bad752, 0x72e3569, + 0x93db760, 0x97d3b37, 0x9e05670, 0xa315d5a, 0xa3571a1, 0xa48db46, 0xa7796b6, 0xac43611, + 0xb64912f, 0xbb6c71e, 0xbcc8be1, 0xc38a43a, 0xd4faa99, 0xe018a66, 0xe37e49c, 0xfa975fa, + 0x11786035, 0x1243b60a, 0x12892da0, 0x141b5453, 0x1483c3a0, 0x1505525e, 0x1607352c, 0x16181fe3, + 0x17e3a1da, 0x180b651e, 0x1899d678, 0x1931b0bb, 0x19606448, 0x1b041655, 0x1b2c20ad, 0x1bd7a83c, + 0x1c05d5b0, 0x1c0b9caa, + ]; + #[test] fn cuckatoo() { - let ret = basic_solve(); + let ret = basic_solve::(); + if let Err(r) = ret { + panic!("basic_solve u32: Error: {}", r); + } + let ret = basic_solve::(); + if let Err(r) = ret { + panic!("basic_solve u64: Error: {}", r); + } + let ret = validate29_vectors::(); + if let Err(r) = ret { + panic!("validate_29_vectors u32: Error: {}", r); + } + let ret = validate29_vectors::(); + if let Err(r) = ret { + panic!("validate_29_vectors u64: Error: {}", r); + } + let ret = validate_fail::(); + if let Err(r) = ret { + panic!("validate_fail u32: Error: {}", r); + } + let ret = validate_fail::(); if let Err(r) = ret { - panic!("basic_solve: Error: {}", r); + panic!("validate_fail u64: Error: {}", r); } } - fn basic_solve() -> Result<(), Error> { + fn validate29_vectors() -> Result<(), Error> + where + T: EdgeType, + { + let mut ctx = CuckatooContext::::new(29, 42, 50, 10)?; + ctx.set_header_nonce([0u8; 80].to_vec(), Some(20), false)?; + assert!(ctx.verify(&Proof::new(V1_29.to_vec().clone())).is_ok()); + Ok(()) + } + + fn validate_fail() -> Result<(), Error> + where + T: EdgeType, + { + let mut ctx = CuckatooContext::::new(29, 42, 50, 10)?; + let mut header = [0u8; 80]; + header[0] = 1u8; + ctx.set_header_nonce(header.to_vec(), Some(20), false)?; + assert!(!ctx.verify(&Proof::new(V1_29.to_vec().clone())).is_ok()); + header[0] = 0u8; + ctx.set_header_nonce(header.to_vec(), Some(20), false)?; + assert!(ctx.verify(&Proof::new(V1_29.to_vec().clone())).is_ok()); + let mut bad_proof = V1_29.clone(); + bad_proof[0] = 0x48a9e1; + assert!(!ctx.verify(&Proof::new(bad_proof.to_vec())).is_ok()); + Ok(()) + } + + fn basic_solve() -> Result<(), Error> + where + T: EdgeType + { let easiness_pct = 50; let nonce = 1546569; let _range = 1; @@ -365,7 +429,7 @@ mod test { easiness_pct ); let mut ctx_u32 = - CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; + CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; let mut bytes = ctx_u32.byte_count()?; let mut unit = 0; while bytes >= 10240 { @@ -373,7 +437,7 @@ mod test { unit += 1; } println!("Using {}{}B memory", bytes, [' ', 'K', 'M', 'G', 'T'][unit]); - ctx_u32.set_header_nonce(header, Some(nonce))?; + ctx_u32.set_header_nonce(header, Some(nonce), true)?; println!( "Nonce {} k0 k1 k2 k3 {} {} {} {}", nonce, diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index e7ba7fc978..243232bd8f 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -59,8 +59,8 @@ where )?)) } - fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error> { - self.set_header_nonce_impl(header, nonce) + fn set_header_nonce(&mut self, header: Vec, nonce: Option, solve: bool) -> Result<(), Error> { + self.set_header_nonce_impl(header, nonce, solve) } fn find_cycles(&mut self) -> Result, Error> { @@ -108,9 +108,12 @@ where &mut self, header: Vec, nonce: Option, + solve: bool, ) -> Result<(), Error> { self.siphash_keys = common::set_header_nonce(header, nonce)?; - self.reset()?; + if solve { + self.reset()?; + } Ok(()) } @@ -398,21 +401,21 @@ mod test { T: EdgeType, { let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None, true)?; let res = cuckoo_ctx.find_cycles()?; let mut proof = Proof::new(V1.to_vec()); proof.cuckoo_sizeshift = 20; assert_eq!(proof, res[0]); let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; - cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None, true)?; let res = cuckoo_ctx.find_cycles()?; let mut proof = Proof::new(V2.to_vec()); proof.cuckoo_sizeshift = 20; assert_eq!(proof, res[0]); //re-use context - cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([51].to_vec(), None, true)?; let res = cuckoo_ctx.find_cycles()?; let mut proof = Proof::new(V3.to_vec()); proof.cuckoo_sizeshift = 20; @@ -425,12 +428,12 @@ mod test { T: EdgeType, { let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None, false)?; assert!(cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); let mut cuckoo_ctx = CuckooContext::::new(20, 42, 70, 10)?; - cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None, false)?; assert!(cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok()); - cuckoo_ctx.set_header_nonce([51].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([51].to_vec(), None, false)?; assert!(cuckoo_ctx.verify(&Proof::new(V3.to_vec().clone())).is_ok()); Ok(()) } @@ -441,17 +444,17 @@ mod test { { // edge checks let mut cuckoo_ctx = CuckooContext::::new(20, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce([49].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([49].to_vec(), None, false)?; // edge checks assert!(!cuckoo_ctx.verify(&Proof::new(vec![0; 42])).is_ok()); assert!(!cuckoo_ctx.verify(&Proof::new(vec![0xffff; 42])).is_ok()); // wrong data for proof - cuckoo_ctx.set_header_nonce([50].to_vec(), None)?; + cuckoo_ctx.set_header_nonce([50].to_vec(), None, false)?; assert!(!cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); let mut test_header = [0; 32]; test_header[0] = 24; let mut cuckoo_ctx = CuckooContext::::new(20, 42, 50, 10)?; - cuckoo_ctx.set_header_nonce(test_header.to_vec(), None)?; + cuckoo_ctx.set_header_nonce(test_header.to_vec(), None, false)?; assert!(!cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok()); Ok(()) } @@ -463,7 +466,7 @@ mod test { for n in 1..5 { let h = [n; 32]; let mut cuckoo_ctx = CuckooContext::::new(16, 42, 75, 10)?; - cuckoo_ctx.set_header_nonce(h.to_vec(), None)?; + cuckoo_ctx.set_header_nonce(h.to_vec(), None, false)?; let res = cuckoo_ctx.find_cycles()?; assert!(cuckoo_ctx.verify(&res[0]).is_ok()) } diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index c3ef8992a8..a37e2bbe34 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -67,7 +67,7 @@ pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u8) -> Result<(), Error> { consensus::EASINESS, MAX_SOLS, )?; - ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None)?; + ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None, false)?; ctx.verify(&bh.pow.proof) } @@ -111,7 +111,7 @@ pub fn pow_size( // diff, we're all good let mut ctx = global::create_pow_context::(sz, proof_size, consensus::EASINESS, MAX_SOLS)?; - ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None)?; + ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None, true)?; if let Ok(proofs) = ctx.find_cycles() { bh.pow.proof = proofs[0].clone(); if bh.pow.to_difficulty() >= diff { diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index 57147c5dae..1d27e13f7d 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -43,7 +43,8 @@ where max_sols: u32, ) -> Result, Error>; /// Sets the header along with an optional nonce at the end - fn set_header_nonce(&mut self, header: Vec, nonce: Option) -> Result<(), Error>; + /// solve: whether to set up structures for a solve (true) or just validate (false) + fn set_header_nonce(&mut self, header: Vec, nonce: Option, solve: bool) -> Result<(), Error>; /// find solutions using the stored parameters and header fn find_cycles(&mut self) -> Result, Error>; /// Verify a solution with the stored parameters diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index ed92e32784..8c1b62a63e 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -102,7 +102,7 @@ impl Miner { consensus::EASINESS, 10, ).unwrap(); - ctx.set_header_nonce(b.header.pre_pow_hash().to_vec(), None) + ctx.set_header_nonce(b.header.pre_pow_hash().to_vec(), None, true) .unwrap(); if let Ok(proofs) = ctx.find_cycles() { b.header.pow.proof = proofs[0].clone(); From ddadb73ee52d1468875bdc1e7c95502847bfb3b5 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Thu, 27 Sep 2018 15:58:53 +0100 Subject: [PATCH 19/22] rustfmt --- core/src/pow/cuckatoo.rs | 28 ++++++++++++++++------------ core/src/pow/cuckoo.rs | 7 ++++++- core/src/pow/types.rs | 7 ++++++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index 739f896fd9..b7aaa905b1 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -183,7 +183,12 @@ where )?)) } - fn set_header_nonce(&mut self, header: Vec, nonce: Option, solve:bool) -> Result<(), Error> { + fn set_header_nonce( + &mut self, + header: Vec, + nonce: Option, + solve: bool, + ) -> Result<(), Error> { self.set_header_nonce_impl(header, nonce, solve) } @@ -236,7 +241,7 @@ where &mut self, mut header: Vec, nonce: Option, - solve: bool + solve: bool, ) -> Result<(), Error> { let len = header.len(); header.truncate(len - mem::size_of::()); @@ -344,12 +349,12 @@ mod test { // Cuckatoo 29 Solution for Header [0u8;80] - nonce 20 static V1_29: [u64; 42] = [ - 0x48a9e2, 0x9cf043, 0x155ca30, 0x18f4783, 0x248f86c, 0x2629a64, 0x5bad752, 0x72e3569, - 0x93db760, 0x97d3b37, 0x9e05670, 0xa315d5a, 0xa3571a1, 0xa48db46, 0xa7796b6, 0xac43611, - 0xb64912f, 0xbb6c71e, 0xbcc8be1, 0xc38a43a, 0xd4faa99, 0xe018a66, 0xe37e49c, 0xfa975fa, - 0x11786035, 0x1243b60a, 0x12892da0, 0x141b5453, 0x1483c3a0, 0x1505525e, 0x1607352c, 0x16181fe3, - 0x17e3a1da, 0x180b651e, 0x1899d678, 0x1931b0bb, 0x19606448, 0x1b041655, 0x1b2c20ad, 0x1bd7a83c, - 0x1c05d5b0, 0x1c0b9caa, + 0x48a9e2, 0x9cf043, 0x155ca30, 0x18f4783, 0x248f86c, 0x2629a64, 0x5bad752, 0x72e3569, + 0x93db760, 0x97d3b37, 0x9e05670, 0xa315d5a, 0xa3571a1, 0xa48db46, 0xa7796b6, 0xac43611, + 0xb64912f, 0xbb6c71e, 0xbcc8be1, 0xc38a43a, 0xd4faa99, 0xe018a66, 0xe37e49c, 0xfa975fa, + 0x11786035, 0x1243b60a, 0x12892da0, 0x141b5453, 0x1483c3a0, 0x1505525e, 0x1607352c, + 0x16181fe3, 0x17e3a1da, 0x180b651e, 0x1899d678, 0x1931b0bb, 0x19606448, 0x1b041655, + 0x1b2c20ad, 0x1bd7a83c, 0x1c05d5b0, 0x1c0b9caa, ]; #[test] @@ -408,9 +413,9 @@ mod test { Ok(()) } - fn basic_solve() -> Result<(), Error> + fn basic_solve() -> Result<(), Error> where - T: EdgeType + T: EdgeType, { let easiness_pct = 50; let nonce = 1546569; @@ -428,8 +433,7 @@ mod test { nonce, easiness_pct ); - let mut ctx_u32 = - CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; + let mut ctx_u32 = CuckatooContext::::new(edge_bits, proof_size, easiness_pct, max_sols)?; let mut bytes = ctx_u32.byte_count()?; let mut unit = 0; while bytes >= 10240 { diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index 243232bd8f..b545b19333 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -59,7 +59,12 @@ where )?)) } - fn set_header_nonce(&mut self, header: Vec, nonce: Option, solve: bool) -> Result<(), Error> { + fn set_header_nonce( + &mut self, + header: Vec, + nonce: Option, + solve: bool, + ) -> Result<(), Error> { self.set_header_nonce_impl(header, nonce, solve) } diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index 1d27e13f7d..0669da4ade 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -44,7 +44,12 @@ where ) -> Result, Error>; /// Sets the header along with an optional nonce at the end /// solve: whether to set up structures for a solve (true) or just validate (false) - fn set_header_nonce(&mut self, header: Vec, nonce: Option, solve: bool) -> Result<(), Error>; + fn set_header_nonce( + &mut self, + header: Vec, + nonce: Option, + solve: bool, + ) -> Result<(), Error>; /// find solutions using the stored parameters and header fn find_cycles(&mut self) -> Result, Error>; /// Verify a solution with the stored parameters From 6c0e921e5e533843032620a7ef15e4cf5d5e5cd3 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Thu, 27 Sep 2018 16:10:52 +0100 Subject: [PATCH 20/22] don't init cuckoo structs if just validating --- core/src/pow/cuckoo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index b545b19333..fa248fbee5 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -96,7 +96,7 @@ where easiness: to_edge!(easiness), proof_size: proof_size, edge_mask: to_edge!(num_edges / 2 - 1), - graph: vec![T::zero(); num_edges as usize + 1], + graph: vec![], _max_sols: max_sols, }) } From ac302b44a5e8f3832277bedfaed2b95b34ef90e0 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Thu, 27 Sep 2018 21:07:23 +0100 Subject: [PATCH 21/22] test fix --- core/src/pow/cuckoo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs index fa248fbee5..b545b19333 100644 --- a/core/src/pow/cuckoo.rs +++ b/core/src/pow/cuckoo.rs @@ -96,7 +96,7 @@ where easiness: to_edge!(easiness), proof_size: proof_size, edge_mask: to_edge!(num_edges / 2 - 1), - graph: vec![], + graph: vec![T::zero(); num_edges as usize + 1], _max_sols: max_sols, }) } From 32c9436e6e1a2cca43eee5c45aa35a6bf263b5f5 Mon Sep 17 00:00:00 2001 From: yeastplume Date: Fri, 28 Sep 2018 10:57:45 +0100 Subject: [PATCH 22/22] ensure BH hashing for POW is only done within miner/validators --- core/src/core/block.rs | 25 ++++++++++++++----------- core/src/pow/mod.rs | 4 ++-- servers/src/mining/test_miner.rs | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 81eb375ca1..51267f45a9 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -25,7 +25,7 @@ use std::sync::{Arc, RwLock}; use consensus::{self, reward, REWARD}; use core::committed::{self, Committed}; use core::compact_block::{CompactBlock, CompactBlockBody}; -use core::hash::{Hash, HashWriter, Hashed, ZERO_HASH}; +use core::hash::{Hash, Hashed, ZERO_HASH}; use core::verifier_cache::VerifierCache; use core::{ transaction, Commitment, Input, KernelFeatures, Output, OutputFeatures, Transaction, @@ -278,16 +278,19 @@ impl BlockHeader { Ok(()) } - /// Returns the pre-pow hash, as the post-pow hash - /// should just be the hash of the POW - pub fn pre_pow_hash(&self) -> Hash { - let mut hasher = HashWriter::default(); - self.write_pre_pow(&mut hasher).unwrap(); - self.pow.write_pre_pow(self.version, &mut hasher).unwrap(); - hasher.write_u64(self.pow.nonce).unwrap(); - let mut ret = [0; 32]; - hasher.finalize(&mut ret); - Hash(ret) + /// Return the pre-pow, unhashed + /// Let the cuck(at)oo miner/verifier handle the hashing + /// for consistency with how this call is performed everywhere + /// else + pub fn pre_pow(&self) -> Vec { + let mut header_buf = vec![]; + { + let mut writer = ser::BinWriter::new(&mut header_buf); + self.write_pre_pow(&mut writer).unwrap(); + self.pow.write_pre_pow(self.version, &mut writer).unwrap(); + writer.write_u64(self.pow.nonce).unwrap(); + } + header_buf } /// Total difficulty accumulated by the proof of work on this header diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index a37e2bbe34..e76fd35406 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -67,7 +67,7 @@ pub fn verify_size(bh: &BlockHeader, cuckoo_sz: u8) -> Result<(), Error> { consensus::EASINESS, MAX_SOLS, )?; - ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None, false)?; + ctx.set_header_nonce(bh.pre_pow(), None, false)?; ctx.verify(&bh.pow.proof) } @@ -111,7 +111,7 @@ pub fn pow_size( // diff, we're all good let mut ctx = global::create_pow_context::(sz, proof_size, consensus::EASINESS, MAX_SOLS)?; - ctx.set_header_nonce(bh.pre_pow_hash().to_vec(), None, true)?; + ctx.set_header_nonce(bh.pre_pow(), None, true)?; if let Ok(proofs) = ctx.find_cycles() { bh.pow.proof = proofs[0].clone(); if bh.pow.to_difficulty() >= diff { diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index 8c1b62a63e..5316df3a75 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -102,7 +102,7 @@ impl Miner { consensus::EASINESS, 10, ).unwrap(); - ctx.set_header_nonce(b.header.pre_pow_hash().to_vec(), None, true) + ctx.set_header_nonce(b.header.pre_pow(), None, true) .unwrap(); if let Ok(proofs) = ctx.find_cycles() { b.header.pow.proof = proofs[0].clone();