Oxidise the Equihash validator

This commit is contained in:
str4d
2017-12-30 23:57:51 +01:00
parent d19c9b5645
commit 94c38fa853

View File

@@ -3,25 +3,50 @@ use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::Cursor; use std::io::Cursor;
use std::mem::size_of; use std::mem::size_of;
struct Params {
n: u32,
k: u32,
}
#[derive(Clone)] #[derive(Clone)]
struct Node { struct Node {
hash: Vec<u8>, hash: Vec<u8>,
indices: Vec<u32>, indices: Vec<u32>,
} }
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 { impl Node {
fn new(a: &mut Node, b: &mut Node, trim: usize) -> Self { fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self {
let mut hash = vec![0u8; a.hash.len() - trim]; let hash: Vec<_> = a.hash
.iter()
.zip(b.hash.iter())
.skip(trim)
.map(|(a, b)| a ^ b)
.collect();
let mut indices = Vec::new(); 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) { if a.indices_before(b) {
indices.append(a.indices.as_mut()); indices.extend(a.indices.iter());
indices.append(b.indices.as_mut()); indices.extend(b.indices.iter());
} else { } else {
indices.append(b.indices.as_mut()); indices.extend(b.indices.iter());
indices.append(a.indices.as_mut()); indices.extend(a.indices.iter());
} }
Node { Node {
hash: hash, hash: hash,
@@ -36,12 +61,7 @@ impl Node {
} }
fn is_zero(&self, len: usize) -> bool { fn is_zero(&self, len: usize) -> bool {
for i in 0..len { self.hash.iter().take(len).all(|v| *v == 0)
if self.hash[i] != 0 {
return false;
}
}
return true;
} }
} }
@@ -68,8 +88,13 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
let out_width = (bit_len + 7) / 8 + byte_pad; let out_width = (bit_len + 7) / 8 + byte_pad;
let out_len = 8 * out_width * vin.len() / bit_len; let out_len = 8 * out_width * vin.len() / bit_len;
let mut vout: Vec<u8> = 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<u8> = vec![0; out_len];
let bit_len_mask: u32 = (1 << bit_len) - 1; let bit_len_mask: u32 = (1 << bit_len) - 1;
// The acc_bits least-significant bits of acc_value represent a bit sequence // 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<u8> {
let mut acc_value: u32 = 0; let mut acc_value: u32 = 0;
let mut j = 0; let mut j = 0;
for i in 0..vin.len() { for b in vin {
acc_value = (acc_value << 8) | vin[i] as u32; acc_value = (acc_value << 8) | *b as u32;
acc_bits += 8; acc_bits += 8;
// When we have bit_len or more bits in the accumulator, write the next // When we have bit_len or more bits in the accumulator, write the next
// output element. // output element.
if acc_bits >= bit_len { if acc_bits >= bit_len {
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 { for x in byte_pad..out_width {
vout[j + x] = (( vout[j + x] = ((
// Big-endian // Big-endian
@@ -124,12 +146,11 @@ fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec<u32> {
} }
fn has_collision(a: &Node, b: &Node, len: usize) -> bool { fn has_collision(a: &Node, b: &Node, len: usize) -> bool {
for i in 0..len { a.hash
if a.hash[i] != b.hash[i] { .iter()
return false; .zip(b.hash.iter())
} .take(len)
} .all(|(a, b)| a == b)
return true;
} }
fn distinct_indices(a: &Node, b: &Node) -> bool { fn distinct_indices(a: &Node, b: &Node) -> bool {
@@ -150,58 +171,54 @@ fn is_valid_solution_iterative(
nonce: &[u8], nonce: &[u8],
indices: &[u32], indices: &[u32],
) -> bool { ) -> bool {
let IndicesPerHashOutput = 512 / n; let p = Params { n: n, k: k };
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 mut state = initialise_state(n, k, HashOutput); let mut state = initialise_state(p.n, p.k, p.hash_output());
state.update(input); state.update(input);
state.update(nonce); state.update(nonce);
let mut X = Vec::new(); let mut rows = Vec::new();
for i in indices { for i in indices {
let hash = generate_hash(&state, i / IndicesPerHashOutput); let hash = generate_hash(&state, i / p.indices_per_hash_output());
let start = ((i % IndicesPerHashOutput) * n / 8) as usize; let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize;
let end = start + (n as usize) / 8; let end = start + (p.n as usize) / 8;
X.push(Node { rows.push(Node {
hash: expand_array(&hash.as_bytes()[start..end], CollisionBitLength, 0), hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0),
indices: vec![*i], indices: vec![*i],
}); });
} }
let mut hash_len = hash_length; let mut hash_len = p.hash_length();
while X.len() > 1 { while rows.len() > 1 {
let mut Xc = Vec::new(); let mut cur_rows = Vec::new();
for pair in X.chunks(2) { for pair in rows.chunks(2) {
let mut a = pair[0].clone(); let a = &pair[0];
let mut b = pair[1].clone(); let b = &pair[1];
if !has_collision(&a, &b, CollisionByteLength) { if !has_collision(a, b, p.collision_byte_length()) {
// error!("Invalid solution: invalid collision length between StepRows"); // error!("Invalid solution: invalid collision length between StepRows");
return false; return false;
} }
if b.indices_before(&a) { if b.indices_before(a) {
// error!("Invalid solution: Index tree incorrectly ordered"); // error!("Invalid solution: Index tree incorrectly ordered");
return false; return false;
} }
if !distinct_indices(&a, &b) { if !distinct_indices(a, b) {
// error!("Invalid solution: duplicate indices"); // error!("Invalid solution: duplicate indices");
return false; 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; rows = cur_rows;
hash_len -= CollisionByteLength; hash_len -= p.collision_byte_length();
} }
assert!(X.len() == 1); assert!(rows.len() == 1);
return X[0].is_zero(hash_len); return rows[0].is_zero(hash_len);
} }
pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool {
let CollisionBitLength = (n / (k + 1)) as usize; let p = Params { n: n, k: k };
let indices = indices_from_minimal(soln, CollisionBitLength); let indices = indices_from_minimal(soln, p.collision_bit_length());
is_valid_solution_iterative(n, k, input, nonce, &indices) is_valid_solution_iterative(n, k, input, nonce, &indices)
} }