Cuckaroo Testnet4 hard fork (#1997)

* PoW context is now properly picked depending on the chain type,
edge bits and block height. Height const for T4 hard fork leaving
a couple weeks to have miners in place. Removed now unused Cuckoo context.
* Simplified block siphash
* Fix servers crate compilation
* Tiny bit cleaner block siphash. Maybe.
* Cuckatoo min edge bits update for T4 and mainnet
* Fix header size tests, Cuckatoo31 default means one more bit per edge
* Remove redundant param from verify_size
This commit is contained in:
Ignotus Peverell 2018-11-24 12:33:17 -08:00 committed by GitHub
parent 71342d11b1
commit c5b9d867ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 116 additions and 525 deletions

View file

@ -155,7 +155,7 @@ pub struct Chain {
block_hashes_cache: Arc<RwLock<LruCache<Hash, bool>>>,
verifier_cache: Arc<RwLock<VerifierCache>>,
// 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<lmdb::Environment>,
adapter: Arc<ChainAdapter + Send + Sync>,
genesis: Block,
pow_verifier: fn(&BlockHeader, u8) -> Result<(), pow::Error>,
pow_verifier: fn(&BlockHeader) -> Result<(), pow::Error>,
verifier_cache: Arc<RwLock<VerifierCache>>,
archive_mode: bool,
) -> Result<Chain, Error> {

View file

@ -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

View file

@ -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;

View file

@ -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<T>(
height: u64,
edge_bits: u8,
proof_size: usize,
max_sols: u32,
) -> Result<Box<impl PoWContext<T>>, pow::Error>
) -> Result<Box<dyn PoWContext<T>>, pow::Error>
where
T: EdgeType,
T: EdgeType + 'static,
{
CuckatooContext::<T>::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,
}
}

View file

@ -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<T>(edge_bits: u8, proof_size: usize) -> Result<Box<PoWContext<T>>, 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<T>
where
@ -40,11 +51,6 @@ impl<T> PoWContext<T> for CuckarooContext<T>
where
T: EdgeType,
{
fn new(edge_bits: u8, proof_size: usize, _max_sols: u32) -> Result<Box<Self>, Error> {
let params = CuckooParams::new(edge_bits, proof_size)?;
Ok(Box::new(CuckarooContext { params }))
}
fn set_header_nonce(
&mut self,
header: Vec<u8>,
@ -155,11 +161,19 @@ mod test {
#[test]
fn cuckaroo19_vectors() {
let mut ctx = CuckarooContext::<u64>::new(19, 42, 0).unwrap();
let mut ctx = new_impl::<u64>(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<T>(edge_bits: u8, proof_size: usize) -> CuckarooContext<T>
where
T: EdgeType,
{
let params = CuckooParams::new(edge_bits, proof_size).unwrap();
CuckarooContext { params }
}
}

View file

@ -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<T>(
edge_bits: u8,
proof_size: usize,
max_sols: u32,
) -> Result<Box<PoWContext<T>>, Error>
where
T: EdgeType + 'static,
{
Ok(Box::new(CuckatooContext::<T>::new_impl(
edge_bits, proof_size, max_sols,
)?))
}
/// Cuckatoo solver context
pub struct CuckatooContext<T>
where
@ -165,12 +181,6 @@ impl<T> PoWContext<T> for CuckatooContext<T>
where
T: EdgeType,
{
fn new(edge_bits: u8, proof_size: usize, max_sols: u32) -> Result<Box<Self>, Error> {
Ok(Box::new(CuckatooContext::<T>::new_impl(
edge_bits, proof_size, max_sols,
)?))
}
fn set_header_nonce(
&mut self,
header: Vec<u8>,
@ -375,7 +385,7 @@ mod test {
where
T: EdgeType,
{
let mut ctx = CuckatooContext::<T>::new(29, 42, 10)?;
let mut ctx = CuckatooContext::<u32>::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::<T>::new(29, 42, 10)?;
let mut ctx = CuckatooContext::<u32>::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::<T>::new(edge_bits, proof_size, max_sols)?;
let mut ctx_u32 = CuckatooContext::<u32>::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(())
}
}

View file

@ -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<T>
where
T: EdgeType,
{
params: CuckooParams<T>,
graph: Vec<T>,
_max_sols: u32,
}
impl<T> PoWContext<T> for CuckooContext<T>
where
T: EdgeType,
{
fn new(edge_bits: u8, proof_size: usize, max_sols: u32) -> Result<Box<Self>, Error> {
Ok(Box::new(CuckooContext::<T>::new_impl(
edge_bits, proof_size, max_sols,
)?))
}
fn set_header_nonce(
&mut self,
header: Vec<u8>,
nonce: Option<u32>,
solve: bool,
) -> Result<(), Error> {
self.set_header_nonce_impl(header, nonce, solve)
}
fn find_cycles(&mut self) -> Result<Vec<Proof>, Error> {
self.find_cycles_impl()
}
fn verify(&self, proof: &Proof) -> Result<(), Error> {
self.verify_impl(proof)
}
}
impl<T> CuckooContext<T>
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<CuckooContext<T>, 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<u8>,
nonce: Option<u32>,
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<T, Error> {
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<Edge<T>, 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<T, Error> {
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<Vec<T>, 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<Vec<T>, 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<Vec<Proof>, 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::<u32>();
if let Err(r) = ret {
panic!("mine20_vectors u32: Error: {}", r);
}
let ret = mine20_vectors::<u64>();
if let Err(r) = ret {
panic!("mine20_vectors u64: Error: {}", r);
}
let ret = validate20_vectors::<u32>();
if let Err(r) = ret {
panic!("validate20_vectors u32: Error: {}", r);
}
let ret = validate20_vectors::<u64>();
if let Err(r) = ret {
panic!("validate20_vectors u64: Error: {}", r);
}
let ret = validate_fail::<u32>();
if let Err(r) = ret {
panic!("validate_fail u32: Error: {}", r);
}
let ret = validate_fail::<u64>();
if let Err(r) = ret {
panic!("validate_fail u64: Error: {}", r);
}
let ret = mine16_validate::<u32>();
if let Err(r) = ret {
panic!("mine16_validate u32: Error: {}", r);
}
let ret = mine16_validate::<u64>();
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<T>() -> Result<(), Error>
where
T: EdgeType,
{
let header = [0; 4].to_vec();
let mut cuckoo_ctx = CuckooContext::<T>::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::<T>::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<T>() -> Result<(), Error>
where
T: EdgeType,
{
let header = [0; 4].to_vec();
let mut cuckoo_ctx = CuckooContext::<T>::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::<T>::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<T>() -> Result<(), Error>
where
T: EdgeType,
{
// edge checks
let mut cuckoo_ctx = CuckooContext::<T>::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::<T>::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<T>() -> 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::<T>::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(())
}
}

View file

@ -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::<u64>(cuckoo_sz, bh.pow.proof.nonces.len(), MAX_SOLS)?;
pub fn verify_size(bh: &BlockHeader) -> Result<(), Error> {
let mut ctx = global::create_pow_context::<u64>(
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::<u32>(sz, proof_size, MAX_SOLS)?;
let mut ctx = global::create_pow_context::<u32>(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());
}
}

View file

@ -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

View file

@ -35,8 +35,6 @@ pub trait PoWContext<T>
where
T: EdgeType,
{
/// Create new instance of context with appropriate parameters
fn new(edge_bits: u8, proof_size: usize, max_sols: u32) -> Result<Box<Self>, 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(

View file

@ -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,);
}

View file

@ -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()
})
}

View file

@ -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::<u32>(global::min_edge_bits(), global::proofsize(), 10)
.unwrap();
let mut ctx = global::create_pow_context::<u32>(
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() {