diff --git a/chain/src/chain.rs b/chain/src/chain.rs index defab16ec..9462e7fd9 100644 --- a/chain/src/chain.rs +++ b/chain/src/chain.rs @@ -155,7 +155,7 @@ pub struct Chain { block_hashes_cache: Arc>>, verifier_cache: Arc>, // POW verification function - pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>, + pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>, archive_mode: bool, genesis: BlockHeader, } @@ -169,7 +169,7 @@ impl Chain { db_env: Arc, adapter: Arc, genesis: Block, - pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>, + pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>, verifier_cache: Arc>, archive_mode: bool, ) -> Result { diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index d6304661f..3e253f565 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -42,7 +42,7 @@ pub struct BlockContext<'a> { /// The options pub opts: Options, /// The pow verifier to use when processing a block. - pub pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>, + pub pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>, /// The active txhashset (rewindable MMRs) to use for block processing. pub txhashset: &'a mut txhashset::TxHashSet, /// The active batch to use for block processing. @@ -407,7 +407,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext) -> Result<(), E return Err(ErrorKind::LowEdgebits.into()); } let edge_bits = header.pow.edge_bits(); - if !(ctx.pow_verifier)(header, edge_bits).is_ok() { + if !(ctx.pow_verifier)(header).is_ok() { error!( "pipe: error validating header with cuckoo edge_bits {}", edge_bits diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 7ea405be6..f88101f33 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -69,12 +69,16 @@ pub fn secondary_pow_ratio(height: u64) -> u64 { /// Cuckoo-cycle proof size (cycle length) pub const PROOFSIZE: usize = 42; -/// Default Cuckoo Cycle edge_bits, used for mining and validating. -pub const DEFAULT_MIN_EDGE_BITS: u8 = 30; +/// Default Cuckatoo Cycle edge_bits, used for mining and validating. +pub const DEFAULT_MIN_EDGE_BITS: u8 = 31; -/// Secondary proof-of-work edge_bits, meant to be ASIC resistant. +/// Cuckaroo proof-of-work edge_bits, meant to be ASIC resistant. pub const SECOND_POW_EDGE_BITS: u8 = 29; +/// Block height at which testnet 4 hard forks to use Cuckaroo instead of +/// Cuckatoo for ASIC-resistant PoW +pub const T4_CUCKAROO_HARDFORK: u64 = 64_000; + /// Original reference edge_bits to compute difficulty factors for higher /// Cuckoo graph sizes, changing this would hard fork pub const BASE_EDGE_BITS: u8 = 24; diff --git a/core/src/global.rs b/core/src/global.rs index d4eb22406..a7fb85a57 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -20,9 +20,9 @@ use consensus::HeaderInfo; use consensus::{ graph_weight, BASE_EDGE_BITS, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON, DAY_HEIGHT, DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, PROOFSIZE, SECOND_POW_EDGE_BITS, - STATE_SYNC_THRESHOLD, UNIT_DIFFICULTY, + STATE_SYNC_THRESHOLD, T4_CUCKAROO_HARDFORK, UNIT_DIFFICULTY, DEFAULT_MIN_EDGE_BITS, }; -use pow::{self, CuckatooContext, EdgeType, PoWContext}; +use pow::{self, new_cuckaroo_ctx, new_cuckatoo_ctx, 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, @@ -83,6 +83,9 @@ pub const PEER_EXPIRATION_REMOVE_TIME: i64 = PEER_EXPIRATION_DAYS * 24 * 3600; /// 1_000 times natural scale factor for cuckatoo29 pub const TESTNET4_INITIAL_DIFFICULTY: u64 = 1_000 * UNIT_DIFFICULTY; +/// Cuckatoo edge_bits on T4 +pub const TESTNET4_MIN_EDGE_BITS: u8 = 30; + /// Trigger compaction check on average every day for all nodes. /// Randomized per node - roll the dice on every block to decide. /// Will compact the txhashset to remove pruned data. @@ -121,8 +124,10 @@ impl Default for ChainTypes { pub enum PoWContextTypes { /// Classic Cuckoo Cuckoo, - /// Bleeding edge Cuckatoo + /// ASIC-friendly Cuckatoo Cuckatoo, + /// ASIC-resistant Cuckaroo + Cuckaroo, } lazy_static!{ @@ -144,14 +149,30 @@ pub fn set_mining_mode(mode: ChainTypes) { /// Return either a cuckoo context or a cuckatoo context /// Single change point pub fn create_pow_context( + height: u64, edge_bits: u8, proof_size: usize, max_sols: u32, -) -> Result>, pow::Error> +) -> Result>, pow::Error> where - T: EdgeType, + T: EdgeType + 'static, { - CuckatooContext::::new(edge_bits, proof_size, max_sols) + let chain_type = CHAIN_TYPE.read().clone(); + match chain_type { + // Mainnet has Cuckaroo29 for AR and Cuckatoo30+ for AF + ChainTypes::Mainnet if edge_bits == 29 => new_cuckaroo_ctx(edge_bits, proof_size), + ChainTypes::Mainnet => new_cuckatoo_ctx(edge_bits, proof_size, max_sols), + + // T4 has Cuckatoo for everything up to hard fork, then Cuckaroo29 for AR + // and Cuckatoo30+ for AF PoW + ChainTypes::Testnet4 if edge_bits == 29 && height >= T4_CUCKAROO_HARDFORK => { + new_cuckaroo_ctx(edge_bits, proof_size) + } + ChainTypes::Testnet4 => new_cuckatoo_ctx(edge_bits, proof_size, max_sols), + + // Everything else is Cuckatoo only + _ => new_cuckatoo_ctx(edge_bits, proof_size, max_sols), + } } /// Return the type of the pos @@ -166,7 +187,8 @@ pub fn min_edge_bits() -> u8 { ChainTypes::AutomatedTesting => AUTOMATED_TESTING_MIN_EDGE_BITS, ChainTypes::UserTesting => USER_TESTING_MIN_EDGE_BITS, ChainTypes::Testnet1 => USER_TESTING_MIN_EDGE_BITS, - _ => SECOND_POW_EDGE_BITS, + ChainTypes::Testnet4 => TESTNET4_MIN_EDGE_BITS, + _ => DEFAULT_MIN_EDGE_BITS, } } diff --git a/core/src/pow/cuckaroo.rs b/core/src/pow/cuckaroo.rs index 5ac895896..1796bd3b9 100644 --- a/core/src/pow/cuckaroo.rs +++ b/core/src/pow/cuckaroo.rs @@ -28,6 +28,17 @@ use pow::error::{Error, ErrorKind}; use pow::siphash::siphash_block; use pow::{PoWContext, Proof}; +/// Instantiate a new CuckarooContext as a PowContext. Note that this can't +/// be moved in the PoWContext trait as this particular trait needs to be +/// convertible to an object trait. +pub fn new_cuckaroo_ctx(edge_bits: u8, proof_size: usize) -> Result>, Error> +where + T: EdgeType + 'static, +{ + let params = CuckooParams::new(edge_bits, proof_size)?; + Ok(Box::new(CuckarooContext { params })) +} + /// Cuckatoo cycle context. Only includes the verifier for now. pub struct CuckarooContext where @@ -40,11 +51,6 @@ impl PoWContext for CuckarooContext where T: EdgeType, { - fn new(edge_bits: u8, proof_size: usize, _max_sols: u32) -> Result, Error> { - let params = CuckooParams::new(edge_bits, proof_size)?; - Ok(Box::new(CuckarooContext { params })) - } - fn set_header_nonce( &mut self, header: Vec, @@ -155,11 +161,19 @@ mod test { #[test] fn cuckaroo19_vectors() { - let mut ctx = CuckarooContext::::new(19, 42, 0).unwrap(); + let mut ctx = new_impl::(19, 42); ctx.params.siphash_keys = V1_19_HASH.clone(); assert!(ctx.verify(&Proof::new(V1_19_SOL.to_vec().clone())).is_ok()); ctx.params.siphash_keys = V2_19_HASH.clone(); assert!(ctx.verify(&Proof::new(V2_19_SOL.to_vec().clone())).is_ok()); assert!(ctx.verify(&Proof::zero(42)).is_err()); } + + fn new_impl(edge_bits: u8, proof_size: usize) -> CuckarooContext + where + T: EdgeType, + { + let params = CuckooParams::new(edge_bits, proof_size).unwrap(); + CuckarooContext { params } + } } diff --git a/core/src/pow/cuckatoo.rs b/core/src/pow/cuckatoo.rs index d08f62e87..ff8967bde 100644 --- a/core/src/pow/cuckatoo.rs +++ b/core/src/pow/cuckatoo.rs @@ -152,6 +152,22 @@ where } } +/// Instantiate a new CuckatooContext as a PowContext. Note that this can't +/// be moved in the PoWContext trait as this particular trait needs to be +/// convertible to an object trait. +pub fn new_cuckatoo_ctx( + edge_bits: u8, + proof_size: usize, + max_sols: u32, +) -> Result>, Error> +where + T: EdgeType + 'static, +{ + Ok(Box::new(CuckatooContext::::new_impl( + edge_bits, proof_size, max_sols, + )?)) +} + /// Cuckatoo solver context pub struct CuckatooContext where @@ -165,12 +181,6 @@ impl PoWContext for CuckatooContext where T: EdgeType, { - fn new(edge_bits: u8, proof_size: usize, max_sols: u32) -> Result, Error> { - Ok(Box::new(CuckatooContext::::new_impl( - edge_bits, proof_size, max_sols, - )?)) - } - fn set_header_nonce( &mut self, header: Vec, @@ -375,7 +385,7 @@ mod test { where T: EdgeType, { - let mut ctx = CuckatooContext::::new(29, 42, 10)?; + let mut ctx = CuckatooContext::::new_impl(29, 42, 10).unwrap(); ctx.set_header_nonce([0u8; 80].to_vec(), Some(20), false)?; assert!(ctx.verify(&Proof::new(V1_29.to_vec().clone())).is_ok()); Ok(()) @@ -385,7 +395,7 @@ mod test { where T: EdgeType, { - let mut ctx = CuckatooContext::::new(29, 42, 10)?; + let mut ctx = CuckatooContext::::new_impl(29, 42, 10).unwrap(); let mut header = [0u8; 80]; header[0] = 1u8; ctx.set_header_nonce(header.to_vec(), Some(20), false)?; @@ -417,7 +427,7 @@ mod test { String::from_utf8(header.clone()).unwrap(), nonce ); - let mut ctx_u32 = CuckatooContext::::new(edge_bits, proof_size, max_sols)?; + let mut ctx_u32 = CuckatooContext::::new_impl(edge_bits, proof_size, max_sols)?; let mut bytes = ctx_u32.byte_count()?; let mut unit = 0; while bytes >= 10240 { @@ -442,4 +452,5 @@ mod test { } Ok(()) } + } diff --git a/core/src/pow/cuckoo.rs b/core/src/pow/cuckoo.rs deleted file mode 100644 index edce83f08..000000000 --- a/core/src/pow/cuckoo.rs +++ /dev/null @@ -1,464 +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 miners -//! will be much faster in almost every environment. - -use pow::common::{CuckooParams, Edge, EdgeType}; -use pow::error::{Error, ErrorKind}; -use pow::{PoWContext, Proof}; - -use std::cmp; -use std::collections::HashSet; - -const MAXPATHLEN: usize = 8192; - -/// Cuckoo cycle context -pub struct CuckooContext -where - T: EdgeType, -{ - params: CuckooParams, - graph: Vec, - _max_sols: u32, -} - -impl PoWContext for CuckooContext -where - T: EdgeType, -{ - fn new(edge_bits: u8, proof_size: usize, max_sols: u32) -> Result, Error> { - Ok(Box::new(CuckooContext::::new_impl( - edge_bits, proof_size, max_sols, - )?)) - } - - 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> { - self.find_cycles_impl() - } - - fn verify(&self, proof: &Proof) -> Result<(), Error> { - self.verify_impl(proof) - } -} - -impl CuckooContext -where - T: EdgeType, -{ - /// Create a new cuckoo context with given parameters - pub fn new_impl( - edge_bits: u8, - proof_size: usize, - max_sols: u32, - ) -> Result, Error> { - let params = CuckooParams::new(edge_bits, proof_size)?; - let num_nodes = 2 * params.num_edges as usize; - Ok(CuckooContext { - params, - graph: vec![T::zero(); num_nodes], - _max_sols: max_sols, - }) - } - - fn reset(&mut self) -> Result<(), Error> { - let num_nodes = 2 * self.params.num_edges as usize; - self.graph = vec![T::zero(); num_nodes]; - Ok(()) - } - - /// 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, - solve: bool, - ) -> Result<(), Error> { - self.params.reset_header_nonce(header, nonce)?; - if solve { - 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 { - self.params.sipnode(edge, 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[to_usize!(u)]; - } - Ok(to_edge!(nu)) - } - - 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[to_usize!(us[nu + 1])] = us[nu]; - } - self.graph[to_usize!(us[0])] = vs[0]; - } else { - while nv != 0 { - nv -= 1; - self.graph[to_usize!(vs[nv + 1])] = vs[nv]; - } - self.graph[to_usize!(vs[0])] = 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.params.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 -= 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.params.proof_size]; - for nonce in 0..self.params.num_edges { - let edge = self.new_edge(to_edge!(nonce))?; - if cycle.contains(&edge) { - sol[n] = to_edge!(nonce); - n += 1; - cycle.remove(&edge); - } - } - if n == self.params.proof_size { - Ok(sol) - } else { - Err(ErrorKind::NoCycle)? - } - } - - /// 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.params.num_edges { - 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 = 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 { - Ok(s) => { - let mut proof = Proof::new(map_vec!(s.to_vec(), |&n| n.to_u64().unwrap_or(0))); - proof.edge_bits = self.params.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 #edges, 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 num_nonces = self.params.num_edges; - 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] >= num_nonces || (n != 0 && nonces[n] <= nonces[n - 1]) { - return Err(ErrorKind::Verification("edge wrong size".to_owned()))?; - } - 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(); - 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] = [ - 0x8702, 0x12003, 0x2043f, 0x24cf8, 0x27631, 0x2beda, 0x325e5, 0x345b4, 0x36f5c, 0x3b3bc, - 0x4cef6, 0x4dfdf, 0x5036b, 0x5d528, 0x7d76b, 0x80958, 0x81649, 0x8a064, 0x935fe, 0x93c28, - 0x93fc9, 0x9aec5, 0x9c5c8, 0xa00a7, 0xa7256, 0xaa35e, 0xb9e04, 0xc8835, 0xcda49, 0xd72ea, - 0xd7f80, 0xdaa3a, 0xdafce, 0xe03fe, 0xe55a2, 0xe6e60, 0xebb9d, 0xf5248, 0xf6a4b, 0xf6d32, - 0xf7c61, 0xfd9e9, - ]; - static V2: [u64; 42] = [ - 0xab0, 0x403c, 0x509c, 0x127c0, 0x1a0b3, 0x1ffe4, 0x26180, 0x2a20a, 0x35559, 0x36dd3, - 0x3cb20, 0x4992f, 0x55b20, 0x5b507, 0x66e58, 0x6784d, 0x6fda8, 0x7363d, 0x76dd6, 0x7f13b, - 0x84672, 0x85724, 0x991cf, 0x9a6fe, 0x9b0c5, 0xa5019, 0xa7207, 0xaf32f, 0xc29f3, 0xc39d3, - 0xc78ed, 0xc9e75, 0xcd0db, 0xcd81e, 0xd02e0, 0xd05c4, 0xd8f99, 0xd9359, 0xdff3b, 0xea623, - 0xf9100, 0xfc966, - ]; - static V3: [u64; 42] = [ - 0x14ca, 0x1e80, 0x587c, 0xa2d4, 0x14f6b, 0x1b100, 0x1b74c, 0x2477d, 0x29ba4, 0x33f25, - 0x4c55f, 0x4d280, 0x50ffa, 0x53900, 0x5cf62, 0x63f66, 0x65623, 0x6fb19, 0x7a19e, 0x82eef, - 0x83d2d, 0x88015, 0x8e6c5, 0x91086, 0x97429, 0x9aa27, 0xa01b7, 0xa304b, 0xafa06, 0xb1cb3, - 0xbb9fc, 0xbf345, 0xc0761, 0xc0e78, 0xc5b99, 0xc9f09, 0xcc62c, 0xceb6e, 0xd98ad, 0xeecb3, - 0xef966, 0xfef9b, - ]; - // 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 header = [0; 4].to_vec(); - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 10)?; - cuckoo_ctx.set_header_nonce(header.clone(), Some(39), true)?; - let res = cuckoo_ctx.find_cycles()?; - let mut proof = Proof::new(V1.to_vec()); - proof.edge_bits = 20; - assert_eq!(proof, res[0]); - - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 10)?; - cuckoo_ctx.set_header_nonce(header.clone(), Some(56), true)?; - let res = cuckoo_ctx.find_cycles()?; - let mut proof = Proof::new(V2.to_vec()); - proof.edge_bits = 20; - assert_eq!(proof, res[0]); - - //re-use context - cuckoo_ctx.set_header_nonce(header, Some(66), true)?; - let res = cuckoo_ctx.find_cycles()?; - let mut proof = Proof::new(V3.to_vec()); - proof.edge_bits = 20; - assert_eq!(proof, res[0]); - Ok(()) - } - - fn validate20_vectors() -> Result<(), Error> - where - T: EdgeType, - { - let header = [0; 4].to_vec(); - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 10)?; - cuckoo_ctx.set_header_nonce(header.clone(), Some(39), false)?; - assert!(cuckoo_ctx.verify(&Proof::new(V1.to_vec().clone())).is_ok()); - let mut cuckoo_ctx = CuckooContext::::new(20, 42, 10)?; - cuckoo_ctx.set_header_nonce(header.clone(), Some(56), false)?; - assert!(cuckoo_ctx.verify(&Proof::new(V2.to_vec().clone())).is_ok()); - cuckoo_ctx.set_header_nonce(header.clone(), Some(66), false)?; - 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, 10)?; - 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, 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, 10)?; - cuckoo_ctx.set_header_nonce(test_header.to_vec(), None, false)?; - assert!(!cuckoo_ctx.verify(&Proof::new(V4.to_vec().clone())).is_ok()); - Ok(()) - } - - fn mine16_validate() -> Result<(), Error> - where - T: EdgeType, - { - let h = [0 as u8; 32]; - for n in [45 as u32, 49, 131, 143, 151].iter() { - let mut cuckoo_ctx = CuckooContext::::new(16, 42, 10)?; - cuckoo_ctx.set_header_nonce(h.to_vec(), Some(*n), false)?; - let res = cuckoo_ctx.find_cycles()?; - assert!(cuckoo_ctx.verify(&res[0]).is_ok()) - } - Ok(()) - } -} diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index 3818a346f..c78fd9358 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -40,7 +40,6 @@ extern crate grin_util as util; mod common; pub mod cuckaroo; pub mod cuckatoo; -pub mod cuckoo; mod error; #[allow(dead_code)] pub mod lean; @@ -54,17 +53,21 @@ use global; pub use self::common::EdgeType; pub use self::types::*; -pub use pow::cuckatoo::CuckatooContext; -pub use pow::cuckoo::CuckooContext; +pub use pow::cuckaroo::{new_cuckaroo_ctx, CuckarooContext}; +pub use pow::cuckatoo::{new_cuckatoo_ctx, CuckatooContext}; 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) -> Result<(), Error> { - let mut ctx = - global::create_pow_context::(cuckoo_sz, bh.pow.proof.nonces.len(), MAX_SOLS)?; +pub fn verify_size(bh: &BlockHeader) -> Result<(), Error> { + let mut ctx = global::create_pow_context::( + bh.height, + bh.pow.edge_bits(), + bh.pow.proof.nonces.len(), + MAX_SOLS, + )?; ctx.set_header_nonce(bh.pre_pow(), None, false)?; ctx.verify(&bh.pow.proof) } @@ -107,7 +110,7 @@ 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, MAX_SOLS)?; + let mut ctx = global::create_pow_context::(bh.height, sz, proof_size, MAX_SOLS)?; ctx.set_header_nonce(bh.pre_pow(), None, true)?; if let Ok(proofs) = ctx.find_cycles() { bh.pow.proof = proofs[0].clone(); @@ -140,6 +143,7 @@ mod test { fn genesis_pow() { let mut b = genesis::genesis_dev(); b.header.pow.nonce = 485; + b.header.pow.proof.edge_bits = global::min_edge_bits(); pow_size( &mut b.header, Difficulty::min(), @@ -148,6 +152,6 @@ mod test { ).unwrap(); assert!(b.header.pow.nonce != 310); assert!(b.header.pow.to_difficulty() >= Difficulty::min()); - assert!(verify_size(&b.header, global::min_edge_bits()).is_ok()); + assert!(verify_size(&b.header).is_ok()); } } diff --git a/core/src/pow/siphash.rs b/core/src/pow/siphash.rs index e560016d5..db1e56cac 100644 --- a/core/src/pow/siphash.rs +++ b/core/src/pow/siphash.rs @@ -40,24 +40,25 @@ pub fn siphash24(v: &[u64; 4], nonce: u64) -> u64 { /// truncated to its closest block start, up to the end of the block. Returns /// the resulting hash at the nonce's position. pub fn siphash_block(v: &[u64; 4], nonce: u64) -> u64 { - let mut block = Vec::with_capacity(SIPHASH_BLOCK_SIZE as usize); // beginning of the block of hashes let nonce0 = nonce & !SIPHASH_BLOCK_MASK; + let mut nonce_hash = 0; - // fill up our block with repeated hashes + // repeated hashing over the whole block let mut siphash = SipHash24::new(v); for n in nonce0..(nonce0 + SIPHASH_BLOCK_SIZE) { siphash.hash(n); - block.push(siphash.digest()); + if n == nonce { + nonce_hash = siphash.digest(); + } } - assert_eq!(block.len(), SIPHASH_BLOCK_SIZE as usize); - - // xor all-but-last with last value to avoid shortcuts in computing block - let last = block[SIPHASH_BLOCK_MASK as usize]; - for n in 0..SIPHASH_BLOCK_MASK { - block[n as usize] ^= last; + // xor the nonce with the last hash to force hashing the whole block + // unless the nonce is last in the block + if nonce == nonce0 + SIPHASH_BLOCK_MASK { + return siphash.digest(); + } else { + return nonce_hash ^ siphash.digest(); } - return block[(nonce & SIPHASH_BLOCK_MASK) as usize]; } /// Implements siphash 2-4 specialized for a 4 u64 array key and a u64 nonce diff --git a/core/src/pow/types.rs b/core/src/pow/types.rs index 29a63b982..2a38dea11 100644 --- a/core/src/pow/types.rs +++ b/core/src/pow/types.rs @@ -35,8 +35,6 @@ pub trait PoWContext where T: EdgeType, { - /// Create new instance of context with appropriate parameters - fn new(edge_bits: u8, proof_size: usize, max_sols: u32) -> 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( diff --git a/core/tests/block.rs b/core/tests/block.rs index 2e2e891d0..cfccbed58 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -258,7 +258,7 @@ fn empty_block_serialized_size() { let b = new_block(vec![], &keychain, &prev, &key_id); let mut vec = Vec::new(); ser::serialize(&mut vec, &b).expect("serialization failed"); - let target_len = 1_255; + let target_len = 1_265; assert_eq!(vec.len(), target_len); } @@ -271,7 +271,7 @@ fn block_single_tx_serialized_size() { let b = new_block(vec![&tx1], &keychain, &prev, &key_id); let mut vec = Vec::new(); ser::serialize(&mut vec, &b).expect("serialization failed"); - let target_len = 2_837; + let target_len = 2_847; assert_eq!(vec.len(), target_len); } @@ -284,7 +284,7 @@ fn empty_compact_block_serialized_size() { let cb: CompactBlock = b.into(); let mut vec = Vec::new(); ser::serialize(&mut vec, &cb).expect("serialization failed"); - let target_len = 1_263; + let target_len = 1_273; assert_eq!(vec.len(), target_len); } @@ -298,7 +298,7 @@ fn compact_block_single_tx_serialized_size() { let cb: CompactBlock = b.into(); let mut vec = Vec::new(); ser::serialize(&mut vec, &cb).expect("serialization failed"); - let target_len = 1_269; + let target_len = 1_279; assert_eq!(vec.len(), target_len); } @@ -317,7 +317,7 @@ fn block_10_tx_serialized_size() { let b = new_block(txs.iter().collect(), &keychain, &prev, &key_id); let mut vec = Vec::new(); ser::serialize(&mut vec, &b).expect("serialization failed"); - let target_len = 17_075; + let target_len = 17_085; assert_eq!(vec.len(), target_len,); } @@ -336,7 +336,7 @@ fn compact_block_10_tx_serialized_size() { let cb: CompactBlock = b.into(); let mut vec = Vec::new(); ser::serialize(&mut vec, &cb).expect("serialization failed"); - let target_len = 1_323; + let target_len = 1_333; assert_eq!(vec.len(), target_len,); } diff --git a/servers/src/mining/stratumserver.rs b/servers/src/mining/stratumserver.rs index 573f89804..e22e4df98 100644 --- a/servers/src/mining/stratumserver.rs +++ b/servers/src/mining/stratumserver.rs @@ -519,7 +519,7 @@ impl StratumServer { ); } else { // Do some validation but dont submit - if !pow::verify_size(&b.header, b.header.pow.proof.edge_bits).is_ok() { + if !pow::verify_size(&b.header).is_ok() { // Return error status error!( "(Server ID: {}) Failed to validate share at height {} with {} edge_bits with nonce {} using job_id {}", @@ -766,4 +766,3 @@ where serde_json::to_value(e).unwrap() }) } - diff --git a/servers/src/mining/test_miner.rs b/servers/src/mining/test_miner.rs index ab99b9f61..381b768b3 100644 --- a/servers/src/mining/test_miner.rs +++ b/servers/src/mining/test_miner.rs @@ -28,7 +28,6 @@ use core::core::hash::{Hash, Hashed}; use core::core::verifier_cache::VerifierCache; use core::core::{Block, BlockHeader}; use core::global; -use core::pow::PoWContext; use mining::mine_block; use pool; @@ -95,9 +94,12 @@ 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_edge_bits(), global::proofsize(), 10) - .unwrap(); + let mut ctx = global::create_pow_context::( + head.height, + global::min_edge_bits(), + global::proofsize(), + 10, + ).unwrap(); ctx.set_header_nonce(b.header.pre_pow(), None, true) .unwrap(); if let Ok(proofs) = ctx.find_cycles() {