diff --git a/Cargo.lock b/Cargo.lock index a0864c570c..b0ec5a5b5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -660,6 +660,7 @@ dependencies = [ "blake2b_simd", "byteorder", "cc", + "hex", ] [[package]] diff --git a/components/equihash/Cargo.toml b/components/equihash/Cargo.toml index 5998e54cd0..e2a9e0b054 100644 --- a/components/equihash/Cargo.toml +++ b/components/equihash/Cargo.toml @@ -9,12 +9,21 @@ license = "MIT OR Apache-2.0" edition = "2021" rust-version = "1.56.1" +[features] +default = [] + +## Builds the C++ tromp solver and Rust FFI layer. +solver = ["dep:cc"] + [dependencies] blake2b_simd = "1" byteorder = "1" [build-dependencies] -cc = "1" +cc = { version = "1", optional = true } + +[dev-dependencies] +hex = "0.4" [lib] bench = false diff --git a/components/equihash/build.rs b/components/equihash/build.rs index 19eba012cd..5b7c721fb3 100644 --- a/components/equihash/build.rs +++ b/components/equihash/build.rs @@ -1,6 +1,13 @@ +//! Build script for the equihash tromp solver in C. + fn main() { + #[cfg(feature = "solver")] cc::Build::new() .include("tromp/") .file("tromp/equi_miner.c") .compile("equitromp"); + + // Tell Cargo to only rerun this build script if the tromp C files or headers change. + #[cfg(feature = "solver")] + println!("cargo:rerun-if-changed=tromp"); } diff --git a/components/equihash/src/blake2b.rs b/components/equihash/src/blake2b.rs index 0eab303fdb..75da59d5ab 100644 --- a/components/equihash/src/blake2b.rs +++ b/components/equihash/src/blake2b.rs @@ -2,6 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . +// This module uses unsafe code for FFI into blake2b. +#![allow(unsafe_code)] + use blake2b_simd::{State, PERSONALBYTES}; use std::ptr; diff --git a/components/equihash/src/lib.rs b/components/equihash/src/lib.rs index d7d20454d2..dee6a15645 100644 --- a/components/equihash/src/lib.rs +++ b/components/equihash/src/lib.rs @@ -27,5 +27,7 @@ mod test_vectors; pub use verify::{is_valid_solution, Error}; +#[cfg(feature = "solver")] mod blake2b; +#[cfg(feature = "solver")] pub mod tromp; diff --git a/components/equihash/src/tromp.rs b/components/equihash/src/tromp.rs index 88181ce460..a0ac90eef5 100644 --- a/components/equihash/src/tromp.rs +++ b/components/equihash/src/tromp.rs @@ -1,3 +1,5 @@ +//! Rust interface to the tromp equihash solver. + use std::marker::{PhantomData, PhantomPinned}; use std::slice; @@ -15,7 +17,6 @@ struct CEqui { 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), @@ -30,23 +31,30 @@ extern "C" { 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; + /// Returns `equi_nsols()` solutions of length `2^K`, in a single memory allocation. + fn equi_sols(eq: *const CEqui) -> *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, - ); +/// Performs a single equihash solver run with equihash parameters `p` and hash state `curr_state`. +/// Returns zero or more unique solutions. +/// +/// # SAFETY +/// +/// The parameters to this function must match the hard-coded parameters in the C++ code. +/// +/// This function uses unsafe code for FFI into the tromp solver. +#[allow(unsafe_code)] +#[allow(clippy::print_stdout)] +unsafe fn worker(eq: *mut CEqui, p: verify::Params, curr_state: &State) -> Vec> { + // SAFETY: caller must supply a valid `eq` instance. + // + // Review Note: nsols is set to zero in C++ here equi_setstate(eq, curr_state); // Initialization done, start algo driver. equi_digit0(eq, 0); equi_clearslots(eq); + // SAFETY: caller must supply a `p` instance that matches the hard-coded values in the C code. for r in 1..p.k { if (r & 1) != 0 { equi_digitodd(eq, r, 0) @@ -55,25 +63,69 @@ unsafe fn worker(p: verify::Params, curr_state: &State) -> Vec> { }; equi_clearslots(eq); } + // Review Note: nsols is increased here, but only if the solution passes the strictly ordered check. + // With 256 nonces, we get to around 6/9 digits strictly ordered. 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; + //println!("{nsols} solutions of length {solution_len} at {sols:?}"); + + // SAFETY: + // - caller must supply a `p` instance that matches the hard-coded values in the C code. + // - `sols` is a single allocation containing at least `nsols` solutions. + // - this slice is a shared ref to the memory in a valid `eq` instance supplied by the caller. + let solutions: &[u32] = slice::from_raw_parts(sols, nsols * solution_len); + + /* + println!( + "{nsols} solutions of length {solution_len} as a slice of length {:?}", + solutions.len() + ); + */ + + let mut chunks = solutions.chunks_exact(solution_len); + + // SAFETY: + // - caller must supply a `p` instance that matches the hard-coded values in the C code. + // - each solution contains `solution_len` u32 values. + // - the temporary slices are shared refs to a valid `eq` instance supplied by the caller. + // - the bytes in the shared ref are copied before they are returned. + // - dropping `solutions: &[u32]` does not drop the underlying memory owned by `eq`. + let mut solutions = (&mut chunks) + .map(|solution| solution.to_vec()) + .collect::>(); + + assert_eq!(chunks.remainder().len(), 0); + + // Sometimes the solver returns identical solutions. + solutions.sort(); + solutions.dedup(); solutions - .iter() - .map(|solution| slice::from_raw_parts(*solution, solution_len).to_vec()) - .collect::>() }; - equi_free(eq); + /* + println!( + "{} solutions as cloned vectors of length {:?}", + solutions.len(), + solutions + .iter() + .map(|solution| solution.len()) + .collect::>() + ); + */ solutions } +/// Performs multiple equihash solver runs with equihash parameters `200, 9`, initialising the hash with +/// the supplied partial `input`. Between each run, generates a new nonce of length `N` using the +/// `next_nonce` function. +/// +/// Returns zero or more unique solutions. pub fn solve_200_9( input: &[u8], mut next_nonce: impl FnMut() -> Option<[u8; N]>, @@ -82,49 +134,190 @@ pub fn solve_200_9( let mut state = verify::initialise_state(p.n, p.k, p.hash_output()); state.update(input); - loop { + // Create solver and initialize it. + // + // # SAFETY + // - the parameters 200,9 match the hard-coded parameters in the C++ code. + // - tromp is compiled without multi-threading support, so each instance can only support 1 thread. + // - the blake2b functions are in the correct order in Rust and C++ initializers. + #[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(); + // Review Note: these hashes are changing when the nonce changes curr_state.update(&nonce); - let solutions = unsafe { worker(p, &curr_state) }; + // SAFETY: + // - the parameters 200,9 match the hard-coded parameters in the C++ code. + // - the eq instance is initilized above. + #[allow(unsafe_code)] + let solutions = unsafe { worker(eq, p, &curr_state) }; if !solutions.is_empty() { break solutions; } + }; + + // SAFETY: + // - the eq instance is initilized above, and not used after this point. + #[allow(unsafe_code)] + unsafe { + equi_free(eq) + }; + + solutions +} + +/// Performs multiple equihash solver runs with equihash parameters `200, 9`, initialising the hash with +/// the supplied partial `input`. Between each run, generates a new nonce of length `N` using the +/// `next_nonce` function. +/// +/// Returns zero or more unique compressed solutions. +pub fn solve_200_9_compressed( + input: &[u8], + next_nonce: impl FnMut() -> Option<[u8; N]>, +) -> Vec> { + // https://github.com/zcash/zcash/blob/6fdd9f1b81d3b228326c9826fa10696fc516444b/src/pow/tromp/equi.h#L34 + const DIGIT_BITS: usize = 200 / (9 + 1); + let solutions = solve_200_9(input, next_nonce); + + let mut solutions: Vec> = solutions + .iter() + .map(|solution| get_minimal_from_indices(solution, DIGIT_BITS)) + .collect(); + + // Just in case the solver returns solutions that become the same when compressed. + solutions.sort(); + solutions.dedup(); + + solutions +} + +// Rough translation of GetMinimalFromIndices() from: +// https://github.com/zcash/zcash/blob/6fdd9f1b81d3b228326c9826fa10696fc516444b/src/crypto/equihash.cpp#L130-L145 +fn get_minimal_from_indices(indices: &[u32], digit_bits: usize) -> Vec { + let index_bytes = (u32::BITS / 8) as usize; + let digit_bytes = ((digit_bits + 1) + 7) / 8; + assert!(digit_bytes <= index_bytes); + + let len_indices = indices.len() * index_bytes; + let min_len = (digit_bits + 1) * len_indices / (8 * index_bytes); + let byte_pad = index_bytes - digit_bytes; + + // Rough translation of EhIndexToArray(index, array_pointer) from: + // https://github.com/zcash/zcash/blob/6fdd9f1b81d3b228326c9826fa10696fc516444b/src/crypto/equihash.cpp#L123-L128 + // + // Big-endian so that lexicographic array comparison is equivalent to integer comparison. + let array: Vec = indices + .iter() + .flat_map(|index| index.to_be_bytes()) + .collect(); + assert_eq!(array.len(), len_indices); + + compress_array(array, min_len, digit_bits + 1, byte_pad) +} + +// Rough translation of CompressArray() from: +// https://github.com/zcash/zcash/blob/6fdd9f1b81d3b228326c9826fa10696fc516444b/src/crypto/equihash.cpp#L39-L76 +fn compress_array(array: Vec, out_len: usize, bit_len: usize, byte_pad: usize) -> Vec { + let mut out = Vec::with_capacity(out_len); + + let index_bytes = (u32::BITS / 8) as usize; + assert!(bit_len >= 8); + assert!(8 * index_bytes >= 7 + bit_len); + + let in_width: usize = (bit_len + 7) / 8 + byte_pad; + assert!(out_len == bit_len * array.len() / (8 * in_width)); + + let bit_len_mask: u32 = (1 << (bit_len as u32)) - 1; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + let mut acc_bits: usize = 0; + let mut acc_value: u32 = 0; + + let mut j: usize = 0; + for _i in 0..out_len { + // When we have fewer than 8 bits left in the accumulator, read the next + // input element. + if acc_bits < 8 { + acc_value <<= bit_len; + for x in byte_pad..in_width { + acc_value |= ( + // Apply bit_len_mask across byte boundaries + (array[j + x] & ((bit_len_mask >> (8 * (in_width - x - 1))) as u8)) as u32 + ) + .wrapping_shl(8 * (in_width - x - 1) as u32); // Big-endian + } + j += in_width; + acc_bits += bit_len; + } + + acc_bits -= 8; + out.push((acc_value >> acc_bits) as u8); } + + out } #[cfg(test)] mod tests { - use super::solve_200_9; + use super::solve_200_9_compressed; #[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 = [ + 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, || { - nonce[0] += 1; - if nonce[0] == 0 { - None - } else { - Some(nonce) - } + let solutions = solve_200_9_compressed(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() { - println!("Found no solutions"); + // Expected solution rate is documented at: + // https://github.com/tromp/equihash/blob/master/README.md + panic!("Found no solutions after {nonce_count} runs, expected 1.88 solutions per run",); } else { println!("Found {} solutions:", solutions.len()); - for solution in solutions { - println!("- {:?}", solution); + 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"); } } } diff --git a/components/equihash/tromp/equi.h b/components/equihash/tromp/equi.h index 0d159df392..7b3969f52f 100644 --- a/components/equihash/tromp/equi.h +++ b/components/equihash/tromp/equi.h @@ -4,10 +4,6 @@ #ifndef ZCASH_POW_TROMP_EQUI_H #define ZCASH_POW_TROMP_EQUI_H -#ifdef __APPLE__ -#include "osx_barrier.h" -#endif - #include // for type bool #include // for types uint32_t,uint64_t #include // for functions memset @@ -32,10 +28,10 @@ typedef unsigned char uchar; #define DIGITBITS (WN/(NDIGITS)) #define PROOFSIZE (1< #include -#include #include typedef uint16_t u16; @@ -70,7 +73,7 @@ static const u32 SLOTMASK = SLOTRANGE-1; // number of possible values of xhash (rest of n) bits #define NRESTS (1<trees1[r/2] = (bucket1 *)(hta->heap1 + r/2); } void dealloctrees(htalloc *hta) { + if (hta == NULL) { + return; + } + free(hta->heap0); free(hta->heap1); + // Avoid use-after-free and double-free + hta->heap0 = NULL; + hta->heap1 = NULL; + + for (int r=0; rtrees0[r/2] = NULL; + else + hta->trees1[r/2] = NULL; + hta->alloced = 0; } void *htalloc_alloc(htalloc *hta, const u32 n, const u32 sz) { void *mem = calloc(n, sz); @@ -214,7 +231,7 @@ typedef struct htalloc htalloc; typedef au32 bsizes[NBUCKETS]; -u32 min(const u32 a, const u32 b) { +u32 minu32(const u32 a, const u32 b) { return a < b ? a : b; } @@ -228,15 +245,13 @@ struct equi { bsizes *nslots; // PUT IN BUCKET STRUCT proof *sols; au32 nsols; - u32 nthreads; u32 xfull; u32 hfull; u32 bfull; - pthread_barrier_t barry; }; typedef struct equi equi; + void equi_clearslots(equi *eq); equi *equi_new( - const u32 n_threads, blake2b_clone blake2b_clone, blake2b_free blake2b_free, blake2b_update blake2b_update, @@ -244,28 +259,47 @@ typedef struct equi equi; ) { assert(sizeof(hashunit) == 4); equi *eq = malloc(sizeof(equi)); - eq->nthreads = n_threads; eq->blake2b_clone = blake2b_clone; eq->blake2b_free = blake2b_free; eq->blake2b_update = blake2b_update; eq->blake2b_finalize = blake2b_finalize; - const int err = pthread_barrier_init(&eq->barry, NULL, eq->nthreads); - assert(!err); + alloctrees(&eq->hta); eq->nslots = (bsizes *)htalloc_alloc(&eq->hta, 2 * NBUCKETS, sizeof(au32)); eq->sols = (proof *)htalloc_alloc(&eq->hta, MAXSOLS, sizeof(proof)); + + // C malloc() does not guarantee zero-initialized memory (but calloc() does) + eq->blake_ctx = NULL; + eq->nsols = 0; + equi_clearslots(eq); + return eq; } void equi_free(equi *eq) { + if (eq == NULL) { + return; + } + dealloctrees(&eq->hta); + free(eq->nslots); free(eq->sols); eq->blake2b_free(eq->blake_ctx); + // Avoid use-after-free and double-free + eq->nslots = NULL; + eq->sols = NULL; + eq->blake_ctx = NULL; + free(eq); } void equi_setstate(equi *eq, const BLAKE2bState *ctx) { + if (eq->blake_ctx) { + eq->blake2b_free(eq->blake_ctx); + } + eq->blake_ctx = eq->blake2b_clone(ctx); memset(eq->nslots, 0, NBUCKETS * sizeof(au32)); // only nslots[0] needs zeroing + equi_clearslots(eq); eq->nsols = 0; } void equi_clearslots(equi *eq) { @@ -280,8 +314,8 @@ typedef struct equi equi; } u32 getnslots(equi *eq, const u32 r, const u32 bid) { // SHOULD BE METHOD IN BUCKET STRUCT au32 *nslot = &eq->nslots[r&1][bid]; - const u32 n = min(*nslot, NSLOTS); - nslot = 0; + const u32 n = minu32(*nslot, NSLOTS); + *nslot = 0; return n; } void orderindices(u32 *indices, u32 size) { @@ -319,8 +353,15 @@ typedef struct equi equi; listindices1(eq, WK, t, prf); // assume WK odd qsort(prf, PROOFSIZE, sizeof(u32), &compu32); for (u32 i=1; i proof[%d], actual: %d <= %d\n", + i, i-1, prf[i], prf[i-1] + ); + */ return; + } #ifdef EQUIHASH_TROMP_ATOMIC u32 soli = std::atomic_fetch_add_explicit(&eq->nsols, 1U, std::memory_order_relaxed); #else @@ -334,7 +375,7 @@ typedef struct equi equi; u32 binsizes[65]; memset(binsizes, 0, 65 * sizeof(u32)); for (u32 bucketid = 0; bucketid < NBUCKETS; bucketid++) { - u32 bsize = min(eq->nslots[r&1][bucketid], NSLOTS) >> (SLOTBITS-6); + u32 bsize = minu32(eq->nslots[r&1][bucketid], NSLOTS) >> (SLOTBITS-6); binsizes[bsize]++; } for (u32 i=0; i < 65; i++) { @@ -364,7 +405,7 @@ typedef struct equi equi; u32 nextbo; }; typedef struct htlayout htlayout; - + htlayout htlayout_new(equi *eq, u32 r) { htlayout htl; htl.hta = eq->hta; @@ -480,12 +521,15 @@ typedef struct equi equi; BLAKE2bState* state; htlayout htl = htlayout_new(eq, 0); const u32 hashbytes = hashsize(0); - for (u32 block = id; block < NBLOCKS; block += eq->nthreads) { + for (u32 block = id; block < NBLOCKS; block++) { state = eq->blake2b_clone(eq->blake_ctx); u32 leb = htole32(block); eq->blake2b_update(state, (uchar *)&leb, sizeof(u32)); eq->blake2b_finalize(state, hash, HASHOUT); eq->blake2b_free(state); + // Avoid use-after-free and double-free + state = NULL; + for (u32 i = 0; inthreads) { + for (u32 bucketid=id; bucketid < NBUCKETS; bucketid++) { collisiondata_clear(&cd); slot0 *buck = htl.hta.trees0[(r-1)/2][bucketid]; // optimize by updating previous buck?! u32 bsize = getnslots(eq, r-1, bucketid); // optimize by putting bucketsize with block?! @@ -565,11 +609,11 @@ typedef struct equi equi; } } } - + void equi_digiteven(equi *eq, const u32 r, const u32 id) { htlayout htl = htlayout_new(eq, r); collisiondata cd; - for (u32 bucketid=id; bucketid < NBUCKETS; bucketid += eq->nthreads) { + for (u32 bucketid=id; bucketid < NBUCKETS; bucketid++) { collisiondata_clear(&cd); slot1 *buck = htl.hta.trees1[(r-1)/2][bucketid]; // OPTIMIZE BY UPDATING PREVIOUS u32 bsize = getnslots(eq, r-1, bucketid); @@ -617,12 +661,12 @@ typedef struct equi equi; } } } - + void equi_digitK(equi *eq, const u32 id) { collisiondata cd; htlayout htl = htlayout_new(eq, WK); u32 nc = 0; - for (u32 bucketid = id; bucketid < NBUCKETS; bucketid += eq->nthreads) { + for (u32 bucketid = id; bucketid < NBUCKETS; bucketid++) { collisiondata_clear(&cd); slot0 *buck = htl.hta.trees0[(WK-1)/2][bucketid]; u32 bsize = getnslots(eq, WK-1, bucketid); @@ -637,7 +681,7 @@ nc++, candidate(eq, tree_from_bid(bucketid, s0, s1)); } } } -//printf(" %d candidates ", nc); +//printf(" %d candidates\n", nc); } size_t equi_nsols(const equi *eq) { @@ -649,50 +693,36 @@ nc++, candidate(eq, tree_from_bid(bucketid, s0, s1)); typedef struct { u32 id; - pthread_t thread; equi *eq; } thread_ctx; -void barrier(pthread_barrier_t *barry) { - const int rc = pthread_barrier_wait(barry); - if (rc != 0 && rc != PTHREAD_BARRIER_SERIAL_THREAD) { -// printf("Could not wait on barrier\n"); - pthread_exit(NULL); - } -} - void *worker(void *vp) { thread_ctx *tp = (thread_ctx *)vp; equi *eq = tp->eq; - if (tp->id == 0) +// if (tp->id == 0) // printf("Digit 0\n"); - barrier(&eq->barry); + if (tp->id == 0) { + equi_clearslots(eq); + } equi_digit0(eq, tp->id); - barrier(&eq->barry); if (tp->id == 0) { - eq->xfull = eq->bfull = eq->hfull = 0; + equi_clearslots(eq); showbsizes(eq, 0); } - barrier(&eq->barry); for (u32 r = 1; r < WK; r++) { - if (tp->id == 0) +// if (tp->id == 0) // printf("Digit %d", r); - barrier(&eq->barry); r&1 ? equi_digitodd(eq, r, tp->id) : equi_digiteven(eq, r, tp->id); - barrier(&eq->barry); if (tp->id == 0) { // printf(" x%d b%d h%d\n", eq->xfull, eq->bfull, eq->hfull); - eq->xfull = eq->bfull = eq->hfull = 0; + equi_clearslots(eq); showbsizes(eq, r); } - barrier(&eq->barry); } - if (tp->id == 0) +// if (tp->id == 0) // printf("Digit %d\n", WK); equi_digitK(eq, tp->id); - barrier(&eq->barry); - pthread_exit(NULL); return 0; } diff --git a/components/equihash/tromp/osx_barrier.h b/components/equihash/tromp/osx_barrier.h deleted file mode 100644 index 659c40bf59..0000000000 --- a/components/equihash/tromp/osx_barrier.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef ZCASH_POW_TROMP_OSX_BARRIER_H -#define ZCASH_POW_TROMP_OSX_BARRIER_H - -#ifdef __APPLE__ - -#ifndef PTHREAD_BARRIER_H_ -#define PTHREAD_BARRIER_H_ - -#include -#include - -typedef int pthread_barrierattr_t; -#define PTHREAD_BARRIER_SERIAL_THREAD 1 - -typedef struct -{ - pthread_mutex_t mutex; - pthread_cond_t cond; - int count; - int tripCount; -} pthread_barrier_t; - - -int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - if(count == 0) - { - errno = EINVAL; - return -1; - } - if(pthread_mutex_init(&barrier->mutex, 0) < 0) - { - return -1; - } - if(pthread_cond_init(&barrier->cond, 0) < 0) - { - pthread_mutex_destroy(&barrier->mutex); - return -1; - } - barrier->tripCount = count; - barrier->count = 0; - - return 0; -} - -int pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - pthread_cond_destroy(&barrier->cond); - pthread_mutex_destroy(&barrier->mutex); - return 0; -} - -int pthread_barrier_wait(pthread_barrier_t *barrier) -{ - pthread_mutex_lock(&barrier->mutex); - ++(barrier->count); - if(barrier->count >= barrier->tripCount) - { - barrier->count = 0; - pthread_cond_broadcast(&barrier->cond); - pthread_mutex_unlock(&barrier->mutex); - return PTHREAD_BARRIER_SERIAL_THREAD; - } - else - { - pthread_cond_wait(&barrier->cond, &(barrier->mutex)); - pthread_mutex_unlock(&barrier->mutex); - return 0; - } -} - -#endif // PTHREAD_BARRIER_H_ -#endif // __APPLE__ - -#endif // ZCASH_POW_TROMP_OSX_BARRIER_H diff --git a/components/equihash/tromp/portable_endian.h b/components/equihash/tromp/portable_endian.h new file mode 100644 index 0000000000..4a71ce7a7a --- /dev/null +++ b/components/equihash/tromp/portable_endian.h @@ -0,0 +1,130 @@ +// +// endian.h +// +// https://gist.github.com/panzi/6856583 +// +// I, Mathias Panzenböck, place this file hereby into the public domain. Use +// it at your own risk for whatever you like. In case there are +// jurisdictions that don't support putting things in the public domain you +// can also consider it to be "dual licensed" under the BSD, MIT and Apache +// licenses, if you want to. This code is trivial anyway. Consider it an +// example on how to get the endian conversion functions on different +// platforms. + +// Downloaded from https://raw.githubusercontent.com/mikepb/endian.h/master/endian.h +// on 12 January 2024. + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include + +// Not available in librustzcash CI +//# include + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error platform not supported + +#endif + +#endif