From 94c38fa853a0daad707a599249ff2d89f02d93c7 Mon Sep 17 00:00:00 2001 From: str4d Date: Sat, 30 Dec 2017 23:57:51 +0100 Subject: [PATCH] Oxidise the Equihash validator --- src/equihash.rs | 127 +++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 55 deletions(-) diff --git a/src/equihash.rs b/src/equihash.rs index b1f91099a..89416efd3 100644 --- a/src/equihash.rs +++ b/src/equihash.rs @@ -3,25 +3,50 @@ use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use std::io::Cursor; use std::mem::size_of; +struct Params { + n: u32, + k: u32, +} + #[derive(Clone)] struct Node { hash: Vec, indices: Vec, } +impl Params { + fn indices_per_hash_output(&self) -> u32 { + 512 / self.n + } + fn hash_output(&self) -> u8 { + (self.indices_per_hash_output() * self.n / 8) as u8 + } + fn collision_bit_length(&self) -> usize { + (self.n / (self.k + 1)) as usize + } + fn collision_byte_length(&self) -> usize { + (self.collision_bit_length() + 7) / 8 + } + fn hash_length(&self) -> usize { + ((self.k as usize) + 1) * self.collision_byte_length() + } +} + impl Node { - fn new(a: &mut Node, b: &mut Node, trim: usize) -> Self { - let mut hash = vec![0u8; a.hash.len() - trim]; + fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { + let hash: Vec<_> = a.hash + .iter() + .zip(b.hash.iter()) + .skip(trim) + .map(|(a, b)| a ^ b) + .collect(); let mut indices = Vec::new(); - for i in trim..a.hash.len() { - hash[i - trim] = a.hash[i] ^ b.hash[i]; - } if a.indices_before(b) { - indices.append(a.indices.as_mut()); - indices.append(b.indices.as_mut()); + indices.extend(a.indices.iter()); + indices.extend(b.indices.iter()); } else { - indices.append(b.indices.as_mut()); - indices.append(a.indices.as_mut()); + indices.extend(b.indices.iter()); + indices.extend(a.indices.iter()); } Node { hash: hash, @@ -36,12 +61,7 @@ impl Node { } fn is_zero(&self, len: usize) -> bool { - for i in 0..len { - if self.hash[i] != 0 { - return false; - } - } - return true; + self.hash.iter().take(len).all(|v| *v == 0) } } @@ -68,8 +88,13 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { let out_width = (bit_len + 7) / 8 + byte_pad; let out_len = 8 * out_width * vin.len() / bit_len; - let mut vout: Vec = vec![0; out_len]; + // Shortcut for parameters where expansion is a no-op + if out_len == vin.len() { + return vin.to_vec(); + } + + let mut vout: Vec = vec![0; out_len]; let bit_len_mask: u32 = (1 << bit_len) - 1; // The acc_bits least-significant bits of acc_value represent a bit sequence @@ -78,17 +103,14 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { let mut acc_value: u32 = 0; let mut j = 0; - for i in 0..vin.len() { - acc_value = (acc_value << 8) | vin[i] as u32; + for b in vin { + acc_value = (acc_value << 8) | *b as u32; acc_bits += 8; // When we have bit_len or more bits in the accumulator, write the next // output element. if acc_bits >= bit_len { acc_bits -= bit_len; - for x in 0..byte_pad { - vout[j + x] = 0; - } for x in byte_pad..out_width { vout[j + x] = (( // Big-endian @@ -124,12 +146,11 @@ fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { } fn has_collision(a: &Node, b: &Node, len: usize) -> bool { - for i in 0..len { - if a.hash[i] != b.hash[i] { - return false; - } - } - return true; + a.hash + .iter() + .zip(b.hash.iter()) + .take(len) + .all(|(a, b)| a == b) } fn distinct_indices(a: &Node, b: &Node) -> bool { @@ -150,58 +171,54 @@ fn is_valid_solution_iterative( nonce: &[u8], indices: &[u32], ) -> bool { - let IndicesPerHashOutput = 512 / n; - let HashOutput = (IndicesPerHashOutput * n / 8) as u8; - let CollisionBitLength = (n / (k + 1)) as usize; - let CollisionByteLength = (CollisionBitLength + 7) / 8; - let hash_length = ((k as usize) + 1) * CollisionByteLength; + let p = Params { n: n, k: k }; - let mut state = initialise_state(n, k, HashOutput); + let mut state = initialise_state(p.n, p.k, p.hash_output()); state.update(input); state.update(nonce); - let mut X = Vec::new(); + let mut rows = Vec::new(); for i in indices { - let hash = generate_hash(&state, i / IndicesPerHashOutput); - let start = ((i % IndicesPerHashOutput) * n / 8) as usize; - let end = start + (n as usize) / 8; - X.push(Node { - hash: expand_array(&hash.as_bytes()[start..end], CollisionBitLength, 0), + let hash = generate_hash(&state, i / p.indices_per_hash_output()); + let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize; + let end = start + (p.n as usize) / 8; + rows.push(Node { + hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0), indices: vec![*i], }); } - let mut hash_len = hash_length; - while X.len() > 1 { - let mut Xc = Vec::new(); - for pair in X.chunks(2) { - let mut a = pair[0].clone(); - let mut b = pair[1].clone(); - if !has_collision(&a, &b, CollisionByteLength) { + let mut hash_len = p.hash_length(); + while rows.len() > 1 { + let mut cur_rows = Vec::new(); + for pair in rows.chunks(2) { + let a = &pair[0]; + let b = &pair[1]; + if !has_collision(a, b, p.collision_byte_length()) { // error!("Invalid solution: invalid collision length between StepRows"); return false; } - if b.indices_before(&a) { + if b.indices_before(a) { // error!("Invalid solution: Index tree incorrectly ordered"); return false; } - if !distinct_indices(&a, &b) { + if !distinct_indices(a, b) { // error!("Invalid solution: duplicate indices"); return false; } - Xc.push(Node::new(&mut a, &mut b, CollisionByteLength)); + cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length())); } - X = Xc; - hash_len -= CollisionByteLength; + rows = cur_rows; + hash_len -= p.collision_byte_length(); } - assert!(X.len() == 1); - return X[0].is_zero(hash_len); + assert!(rows.len() == 1); + return rows[0].is_zero(hash_len); } pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { - let CollisionBitLength = (n / (k + 1)) as usize; - let indices = indices_from_minimal(soln, CollisionBitLength); + let p = Params { n: n, k: k }; + let indices = indices_from_minimal(soln, p.collision_bit_length()); is_valid_solution_iterative(n, k, input, nonce, &indices) }