diff --git a/components/equihash/src/blake2b.rs b/components/equihash/src/blake2b.rs index 432c4cb79b..0eab303fdb 100644 --- a/components/equihash/src/blake2b.rs +++ b/components/equihash/src/blake2b.rs @@ -3,14 +3,14 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . use blake2b_simd::{State, PERSONALBYTES}; -use libc::{c_uchar, size_t}; + use std::ptr; use std::slice; #[no_mangle] pub extern "C" fn blake2b_init( - output_len: size_t, - personalization: *const [c_uchar; PERSONALBYTES], + output_len: usize, + personalization: *const [u8; PERSONALBYTES], ) -> *mut State { let personalization = unsafe { personalization.as_ref().unwrap() }; @@ -37,7 +37,7 @@ pub extern "C" fn blake2b_free(state: *mut State) { } #[no_mangle] -pub extern "C" fn blake2b_update(state: *mut State, input: *const c_uchar, input_len: size_t) { +pub extern "C" fn blake2b_update(state: *mut State, input: *const u8, input_len: usize) { let state = unsafe { state.as_mut().unwrap() }; let input = unsafe { slice::from_raw_parts(input, input_len) }; @@ -45,7 +45,7 @@ pub extern "C" fn blake2b_update(state: *mut State, input: *const c_uchar, input } #[no_mangle] -pub extern "C" fn blake2b_finalize(state: *mut State, output: *mut c_uchar, output_len: size_t) { +pub extern "C" fn blake2b_finalize(state: *mut State, output: *mut u8, output_len: usize) { let state = unsafe { state.as_mut().unwrap() }; let output = unsafe { slice::from_raw_parts_mut(output, output_len) }; diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index fc23642063..d7d20454d2 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -26,3 +26,6 @@ mod verify; mod test_vectors; pub use verify::{is_valid_solution, Error}; + +mod blake2b; +pub mod tromp; diff --git a/components/equihash/src/tromp.rs b/components/equihash/src/tromp.rs new file mode 100644 index 0000000000..d03767084a --- /dev/null +++ b/components/equihash/src/tromp.rs @@ -0,0 +1,109 @@ +use std::marker::{PhantomData, PhantomPinned}; +use std::slice; + +use blake2b_simd::State; + +use crate::{blake2b, verify}; + +#[repr(C)] +struct CEqui { + _f: [u8; 0], + _m: PhantomData<(*mut u8, PhantomPinned)>, +} + +#[link(name = "equitromp")] +extern "C" { + #[allow(improper_ctypes)] + fn equi_new( + n_threads: u32, + 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 *const u32; +} + +unsafe fn worker(p: verify::Params, curr_state: &State) -> Vec> { + // Create solver and initialize it. + let eq = equi_new( + 1, + blake2b::blake2b_clone, + blake2b::blake2b_free, + blake2b::blake2b_update, + blake2b::blake2b_finalize, + ); + equi_setstate(eq, curr_state); + + // Initialization done, start algo driver. + equi_digit0(eq, 0); + equi_clearslots(eq); + for r in 1..p.k { + if (r & 1) != 0 { + equi_digitodd(eq, r, 0) + } else { + equi_digiteven(eq, r, 0) + }; + equi_clearslots(eq); + } + equi_digitK(eq, 0); + + let solutions = { + let nsols = equi_nsols(eq); + let sols = equi_sols(eq); + let solutions = slice::from_raw_parts(sols, nsols); + let solution_len = 1 << p.k; + + solutions + .iter() + .map(|solution| slice::from_raw_parts(*solution, solution_len).to_vec()) + .collect::>() + }; + + equi_free(eq); + + solutions +} + +pub fn solve_200_9(input: &[u8], nonce: &[u8]) -> Vec> { + let p = verify::Params::new(200, 9).expect("should be valid"); + let mut state = verify::initialise_state(p.n, p.k, p.hash_output()); + state.update(input); + state.update(nonce); + + unsafe { worker(p, &state) } +} + +#[cfg(test)] +mod tests { + use super::solve_200_9; + + #[test] + fn run_solver() { + let input = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; + let nonce = [ + 1, 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 solutions = solve_200_9(input, &nonce); + + if solutions.is_empty() { + println!("Found no solutions"); + } else { + println!("Found {} solutions:", solutions.len()); + for solution in solutions { + println!("- {:?}", solution); + } + } + } +} diff --git a/components/equihash/src/verify.rs b/components/equihash/src/verify.rs index 2015008838..726b8d9e80 100644 --- a/components/equihash/src/verify.rs +++ b/components/equihash/src/verify.rs @@ -21,7 +21,7 @@ struct Node { } impl Params { - fn new(n: u32, k: u32) -> Result { + pub(crate) fn new(n: u32, k: u32) -> Result { // We place the following requirements on the parameters: // - n is a multiple of 8, so the hash output has an exact byte length. // - k >= 3 so the encoded solutions have an exact byte length. @@ -36,7 +36,7 @@ impl Params { fn indices_per_hash_output(&self) -> u32 { 512 / self.n } - fn hash_output(&self) -> u8 { + pub(crate) fn hash_output(&self) -> u8 { (self.indices_per_hash_output() * self.n / 8) as u8 } fn collision_bit_length(&self) -> usize { @@ -148,7 +148,7 @@ impl fmt::Display for Kind { } } -fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { +pub(crate) fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2bState { let mut personalization: Vec = Vec::from("ZcashPoW"); personalization.write_u32::(n).unwrap(); personalization.write_u32::(k).unwrap(); diff --git a/components/equihash/tromp/equi_miner.c b/components/equihash/tromp/equi_miner.c index 56c8028697..841eca3a89 100644 --- a/components/equihash/tromp/equi_miner.c +++ b/components/equihash/tromp/equi_miner.c @@ -268,6 +268,9 @@ typedef struct equi equi; memset(eq->nslots, 0, NBUCKETS * sizeof(au32)); // only nslots[0] needs zeroing eq->nsols = 0; } + void equi_clearslots(equi *eq) { + eq->xfull = eq->bfull = eq->hfull = 0; + } u32 getslot(equi *eq, const u32 r, const u32 bucketi) { #ifdef EQUIHASH_TROMP_ATOMIC return std::atomic_fetch_add_explicit(&eq->nslots[r&1][bucketi], 1U, std::memory_order_relaxed); @@ -637,6 +640,13 @@ nc++, candidate(eq, tree_from_bid(bucketid, s0, s1)); //printf(" %d candidates ", nc); } + size_t equi_nsols(const equi *eq) { + return eq->nsols; + } + proof *equi_sols(const equi *eq) { + return eq->sols; + } + typedef struct { u32 id; pthread_t thread;