Skip to content

Commit

Permalink
equihash: Add Rust API for Tromp solver
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Jan 4, 2024
1 parent b5896f5 commit cb60b5b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 8 deletions.
10 changes: 5 additions & 5 deletions components/equihash/src/blake2b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() };

Expand All @@ -37,15 +37,15 @@ 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) };

state.update(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) };

Expand Down
3 changes: 3 additions & 0 deletions components/equihash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ mod verify;
mod test_vectors;

pub use verify::{is_valid_solution, Error};

mod blake2b;
pub mod tromp;
109 changes: 109 additions & 0 deletions components/equihash/src/tromp.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<u32>> {
// 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::<Vec<_>>()
};

equi_free(eq);

solutions
}

pub fn solve_200_9(input: &[u8], nonce: &[u8]) -> Vec<Vec<u32>> {
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);
}
}
}
}
6 changes: 3 additions & 3 deletions components/equihash/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct Node {
}

impl Params {
fn new(n: u32, k: u32) -> Result<Self, Error> {
pub(crate) fn new(n: u32, k: u32) -> Result<Self, Error> {
// 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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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<u8> = Vec::from("ZcashPoW");
personalization.write_u32::<LittleEndian>(n).unwrap();
personalization.write_u32::<LittleEndian>(k).unwrap();
Expand Down
10 changes: 10 additions & 0 deletions components/equihash/tromp/equi_miner.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit cb60b5b

Please sign in to comment.