use std::marker::{PhantomData, PhantomPinned};
use std::slice;
use std::vec::Vec;
use blake2b_simd::State;
use crate::{blake2b, minimal::minimal_from_indices, params::Params, verify};
#[repr(C)]
struct CEqui {
_f: [u8; 0],
_m: PhantomData<(*mut u8, PhantomPinned)>,
}
#[link(name = "equitromp")]
unsafe extern "C" {
#[allow(improper_ctypes)]
fn equi_new(
blake2b_clone: extern "C" fn(state: *const State) -> *mut State,
blake2b_free: extern "C" fn(state: *mut State),
blake2b_update: extern "C" fn(state: *mut State, input: *const u8, input_len: usize),
blake2b_finalize: extern "C" fn(state: *mut State, output: *mut u8, output_len: usize),
) -> *mut CEqui;
fn equi_free(eq: *mut CEqui);
#[allow(improper_ctypes)]
fn equi_setstate(eq: *mut CEqui, ctx: *const State);
fn equi_clearslots(eq: *mut CEqui);
fn equi_digit0(eq: *mut CEqui, id: u32);
fn equi_digitodd(eq: *mut CEqui, r: u32, id: u32);
fn equi_digiteven(eq: *mut CEqui, r: u32, id: u32);
fn equi_digitK(eq: *mut CEqui, id: u32);
fn equi_nsols(eq: *const CEqui) -> usize;
fn equi_sols(eq: *const CEqui) -> *const u32;
}
#[allow(unsafe_code)]
#[allow(clippy::print_stdout)]
unsafe fn worker(eq: *mut CEqui, p: Params, curr_state: &State) -> Vec<Vec<u32>> {
unsafe { equi_setstate(eq, curr_state) };
unsafe { equi_digit0(eq, 0) };
unsafe { equi_clearslots(eq) };
for r in 1..p.k {
if (r & 1) != 0 {
unsafe { equi_digitodd(eq, r, 0) }
} else {
unsafe { equi_digiteven(eq, r, 0) }
};
unsafe { equi_clearslots(eq) };
}
unsafe { equi_digitK(eq, 0) };
{
let nsols = unsafe { equi_nsols(eq) };
let sols = unsafe { equi_sols(eq) };
let solution_len = 1 << p.k;
let solutions: &[u32] = unsafe { slice::from_raw_parts(sols, nsols * solution_len) };
let mut chunks = solutions.chunks_exact(solution_len);
let mut solutions = (&mut chunks)
.map(|solution| solution.to_vec())
.collect::<Vec<_>>();
assert_eq!(chunks.remainder().len(), 0);
solutions.sort();
solutions.dedup();
solutions
}
}
fn solve_200_9_uncompressed<const N: usize>(
input: &[u8],
mut next_nonce: impl FnMut() -> Option<[u8; N]>,
) -> Vec<Vec<u32>> {
let p = Params::new(200, 9).expect("should be valid");
let mut state = verify::initialise_state(p.n, p.k, p.hash_output());
state.update(input);
#[allow(unsafe_code)]
let eq = unsafe {
equi_new(
blake2b::blake2b_clone,
blake2b::blake2b_free,
blake2b::blake2b_update,
blake2b::blake2b_finalize,
)
};
let solutions = loop {
let nonce = match next_nonce() {
Some(nonce) => nonce,
None => break vec![],
};
let mut curr_state = state.clone();
curr_state.update(&nonce);
#[allow(unsafe_code)]
let solutions = unsafe { worker(eq, p, &curr_state) };
if !solutions.is_empty() {
break solutions;
}
};
#[allow(unsafe_code)]
unsafe {
equi_free(eq)
};
solutions
}
pub fn solve_200_9<const N: usize>(
input: &[u8],
next_nonce: impl FnMut() -> Option<[u8; N]>,
) -> Vec<Vec<u8>> {
let p = Params::new(200, 9).expect("should be valid");
let solutions = solve_200_9_uncompressed(input, next_nonce);
let mut solutions: Vec<Vec<u8>> = solutions
.iter()
.map(|solution| minimal_from_indices(p, solution))
.collect();
solutions.sort();
solutions.dedup();
solutions
}
#[cfg(test)]
mod tests {
use std::println;
use super::solve_200_9;
#[test]
#[allow(clippy::print_stdout)]
fn run_solver() {
let input = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem.";
let mut nonce: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
];
let mut nonces = 0..=32_u32;
let nonce_count = nonces.clone().count();
let solutions = solve_200_9(input, || {
let variable_nonce = nonces.next()?;
println!("Using variable nonce [0..4] of {}", variable_nonce);
let variable_nonce = variable_nonce.to_le_bytes();
nonce[0] = variable_nonce[0];
nonce[1] = variable_nonce[1];
nonce[2] = variable_nonce[2];
nonce[3] = variable_nonce[3];
Some(nonce)
});
if solutions.is_empty() {
panic!("Found no solutions after {nonce_count} runs, expected 1.88 solutions per run",);
} else {
println!("Found {} solutions:", solutions.len());
for (sol_num, solution) in solutions.iter().enumerate() {
println!("Validating solution {sol_num}:-\n{}", hex::encode(solution));
crate::is_valid_solution(200, 9, input, &nonce, solution).unwrap_or_else(|error| {
panic!(
"unexpected invalid equihash 200, 9 solution:\n\
error: {error:?}\n\
input: {input:?}\n\
nonce: {nonce:?}\n\
solution: {solution:?}"
)
});
println!("Solution {sol_num} is valid!\n");
}
}
}
}