Integrate Tromp solver into miner code and remove its dependency on extra BLAKE2b implementation.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
@@ -243,8 +243,13 @@ libbitcoin_wallet_a_SOURCES = \
|
|||||||
$(BITCOIN_CORE_H) \
|
$(BITCOIN_CORE_H) \
|
||||||
$(LIBZCASH_H)
|
$(LIBZCASH_H)
|
||||||
|
|
||||||
|
EQUIHASH_TROMP_SOURCES = \
|
||||||
|
pow/tromp/equi_miner.h \
|
||||||
|
pow/tromp/equi.h \
|
||||||
|
pow/tromp/osx_barrier.h
|
||||||
|
|
||||||
# crypto primitives library
|
# crypto primitives library
|
||||||
crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES)
|
crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) -DEQUIHASH_TROMP_ATOMIC
|
||||||
crypto_libbitcoin_crypto_a_SOURCES = \
|
crypto_libbitcoin_crypto_a_SOURCES = \
|
||||||
crypto/common.h \
|
crypto/common.h \
|
||||||
crypto/equihash.cpp \
|
crypto/equihash.cpp \
|
||||||
@@ -261,7 +266,8 @@ crypto_libbitcoin_crypto_a_SOURCES = \
|
|||||||
crypto/sha256.cpp \
|
crypto/sha256.cpp \
|
||||||
crypto/sha256.h \
|
crypto/sha256.h \
|
||||||
crypto/sha512.cpp \
|
crypto/sha512.cpp \
|
||||||
crypto/sha512.h
|
crypto/sha512.h \
|
||||||
|
${EQUIHASH_TROMP_SOURCES}
|
||||||
|
|
||||||
# univalue JSON library
|
# univalue JSON library
|
||||||
univalue_libbitcoin_univalue_a_SOURCES = \
|
univalue_libbitcoin_univalue_a_SOURCES = \
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "miner.h"
|
#include "miner.h"
|
||||||
|
#include "pow/tromp/equi_miner.h"
|
||||||
|
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
@@ -453,6 +454,9 @@ void static BitcoinMiner(CWallet *pwallet)
|
|||||||
unsigned int n = chainparams.EquihashN();
|
unsigned int n = chainparams.EquihashN();
|
||||||
unsigned int k = chainparams.EquihashK();
|
unsigned int k = chainparams.EquihashK();
|
||||||
|
|
||||||
|
std::string solver = GetArg("-equihashsolver", "default");
|
||||||
|
LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);
|
||||||
|
|
||||||
std::mutex m_cs;
|
std::mutex m_cs;
|
||||||
bool cancelSolver = false;
|
bool cancelSolver = false;
|
||||||
boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
|
boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
|
||||||
@@ -524,8 +528,8 @@ void static BitcoinMiner(CWallet *pwallet)
|
|||||||
pblock->nNonce.size());
|
pblock->nNonce.size());
|
||||||
|
|
||||||
// (x_1, x_2, ...) = A(I, V, n, k)
|
// (x_1, x_2, ...) = A(I, V, n, k)
|
||||||
LogPrint("pow", "Running Equihash solver with nNonce = %s\n",
|
LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",
|
||||||
pblock->nNonce.ToString());
|
solver, pblock->nNonce.ToString());
|
||||||
|
|
||||||
std::function<bool(std::vector<unsigned char>)> validBlock =
|
std::function<bool(std::vector<unsigned char>)> validBlock =
|
||||||
[&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
|
[&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
|
||||||
@@ -559,14 +563,48 @@ void static BitcoinMiner(CWallet *pwallet)
|
|||||||
std::lock_guard<std::mutex> lock{m_cs};
|
std::lock_guard<std::mutex> lock{m_cs};
|
||||||
return cancelSolver;
|
return cancelSolver;
|
||||||
};
|
};
|
||||||
try {
|
|
||||||
// If we find a valid block, we rebuild
|
if (solver == "tromp") {
|
||||||
if (EhOptimisedSolve(n, k, curr_state, validBlock, cancelled))
|
// Create solver and initialize it.
|
||||||
break;
|
equi eq(1);
|
||||||
} catch (EhSolverCancelledException&) {
|
eq.setstate(&curr_state);
|
||||||
LogPrint("pow", "Equihash solver cancelled\n");
|
|
||||||
std::lock_guard<std::mutex> lock{m_cs};
|
// Intialization done, start algo driver.
|
||||||
cancelSolver = false;
|
eq.digit0(0);
|
||||||
|
eq.xfull = eq.bfull = eq.hfull = 0;
|
||||||
|
eq.showbsizes(0);
|
||||||
|
for (u32 r = 1; r < WK; r++) {
|
||||||
|
(r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0);
|
||||||
|
eq.xfull = eq.bfull = eq.hfull = 0;
|
||||||
|
eq.showbsizes(r);
|
||||||
|
}
|
||||||
|
eq.digitK(0);
|
||||||
|
|
||||||
|
// Convert solution indices to byte array (decompress) and pass it to validBlock method.
|
||||||
|
for (size_t s = 0; s < eq.nsols; s++) {
|
||||||
|
LogPrint("pow", "Checking solution %d\n", s+1);
|
||||||
|
std::vector<eh_index> index_vector(PROOFSIZE);
|
||||||
|
for (size_t i = 0; i < PROOFSIZE; i++) {
|
||||||
|
index_vector[i] = eq.sols[s][i];
|
||||||
|
}
|
||||||
|
std::vector<unsigned char> sol_char = GetMinimalFromIndices(index_vector, DIGITBITS);
|
||||||
|
|
||||||
|
if (validBlock(sol_char)) {
|
||||||
|
// If we find a POW solution, do not try other solutions
|
||||||
|
// because they become invalid as we created a new block in blockchain.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// If we find a valid block, we rebuild
|
||||||
|
if (EhOptimisedSolve(n, k, curr_state, validBlock, cancelled))
|
||||||
|
break;
|
||||||
|
} catch (EhSolverCancelledException&) {
|
||||||
|
LogPrint("pow", "Equihash solver cancelled\n");
|
||||||
|
std::lock_guard<std::mutex> lock{m_cs};
|
||||||
|
cancelSolver = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for stop or if block needs to be rebuilt
|
// Check for stop or if block needs to be rebuilt
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
// Equihash solver
|
// Equihash solver
|
||||||
// Copyright (c) 2016-2016 John Tromp
|
// Copyright (c) 2016-2016 John Tromp, The Zcash developers
|
||||||
|
|
||||||
#include "blake/blake2.h"
|
#include "sodium.h"
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include "osx_barrier.h"
|
#include "pow/tromp/osx_barrier.h"
|
||||||
#include <machine/endian.h>
|
|
||||||
#include <libkern/OSByteOrder.h>
|
|
||||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
|
||||||
#else
|
|
||||||
#include <endian.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include "compat/endian.h"
|
||||||
|
|
||||||
#include <stdint.h> // for types uint32_t,uint64_t
|
#include <stdint.h> // for types uint32_t,uint64_t
|
||||||
#include <string.h> // for functions memset
|
#include <string.h> // for functions memset
|
||||||
#include <stdlib.h> // for function qsort
|
#include <stdlib.h> // for function qsort
|
||||||
@@ -38,46 +35,20 @@ static const u32 HASHOUT = HASHESPERBLAKE*WN/8;
|
|||||||
|
|
||||||
typedef u32 proof[PROOFSIZE];
|
typedef u32 proof[PROOFSIZE];
|
||||||
|
|
||||||
void setheader(blake2b_state *ctx, const char *header, const u32 headerlen, u32 nce) {
|
|
||||||
uint32_t le_N = htole32(WN);
|
|
||||||
uint32_t le_K = htole32(WK);
|
|
||||||
uchar personal[] = "ZcashPoW01230123";
|
|
||||||
memcpy(personal+8, &le_N, 4);
|
|
||||||
memcpy(personal+12, &le_K, 4);
|
|
||||||
blake2b_param P[1];
|
|
||||||
P->digest_length = HASHOUT;
|
|
||||||
P->key_length = 0;
|
|
||||||
P->fanout = 1;
|
|
||||||
P->depth = 1;
|
|
||||||
P->leaf_length = 0;
|
|
||||||
P->node_offset = 0;
|
|
||||||
P->node_depth = 0;
|
|
||||||
P->inner_length = 0;
|
|
||||||
memset(P->reserved, 0, sizeof(P->reserved));
|
|
||||||
memset(P->salt, 0, sizeof(P->salt));
|
|
||||||
memcpy(P->personal, (const uint8_t *)personal, 16);
|
|
||||||
blake2b_init_param(ctx, P);
|
|
||||||
blake2b_update(ctx, (const uchar *)header, headerlen);
|
|
||||||
uchar nonce[32];
|
|
||||||
memset(nonce, 0, 32);
|
|
||||||
uint32_t le_nonce = htole32(nce);
|
|
||||||
memcpy(nonce, &le_nonce, 4);
|
|
||||||
blake2b_update(ctx, nonce, 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum verify_code { POW_OK, POW_DUPLICATE, POW_OUT_OF_ORDER, POW_NONZERO_XOR };
|
enum verify_code { POW_OK, POW_DUPLICATE, POW_OUT_OF_ORDER, POW_NONZERO_XOR };
|
||||||
const char *errstr[] = { "OK", "duplicate index", "indices out of order", "nonzero xor" };
|
const char *errstr[] = { "OK", "duplicate index", "indices out of order", "nonzero xor" };
|
||||||
|
|
||||||
void genhash(blake2b_state *ctx, u32 idx, uchar *hash) {
|
void genhash(const crypto_generichash_blake2b_state *ctx, u32 idx, uchar *hash) {
|
||||||
blake2b_state state = *ctx;
|
crypto_generichash_blake2b_state state = *ctx;
|
||||||
u32 leb = htole32(idx / HASHESPERBLAKE);
|
u32 leb = htole32(idx / HASHESPERBLAKE);
|
||||||
blake2b_update(&state, (uchar *)&leb, sizeof(u32));
|
crypto_generichash_blake2b_update(&state, (uchar *)&leb, sizeof(u32));
|
||||||
uchar blakehash[HASHOUT];
|
uchar blakehash[HASHOUT];
|
||||||
blake2b_final(&state, blakehash, HASHOUT);
|
crypto_generichash_blake2b_final(&state, blakehash, HASHOUT);
|
||||||
memcpy(hash, blakehash + (idx % HASHESPERBLAKE) * WN/8, WN/8);
|
memcpy(hash, blakehash + (idx % HASHESPERBLAKE) * WN/8, WN/8);
|
||||||
}
|
}
|
||||||
|
|
||||||
int verifyrec(blake2b_state *ctx, u32 *indices, uchar *hash, int r) {
|
int verifyrec(const crypto_generichash_blake2b_state *ctx, u32 *indices, uchar *hash, int r) {
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
genhash(ctx, *indices, hash);
|
genhash(ctx, *indices, hash);
|
||||||
return POW_OK;
|
return POW_OK;
|
||||||
@@ -119,11 +90,9 @@ bool duped(proof prf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify Wagner conditions
|
// verify Wagner conditions
|
||||||
int verify(u32 indices[PROOFSIZE], const char *header, const u32 headerlen, const u32 nonce) {
|
int verify(u32 indices[PROOFSIZE], const crypto_generichash_blake2b_state *ctx) {
|
||||||
if (duped(indices))
|
if (duped(indices))
|
||||||
return POW_DUPLICATE;
|
return POW_DUPLICATE;
|
||||||
blake2b_state ctx;
|
|
||||||
setheader(&ctx, header, headerlen, nonce);
|
|
||||||
uchar hash[WN/8];
|
uchar hash[WN/8];
|
||||||
return verifyrec(&ctx, indices, hash, WK);
|
return verifyrec(ctx, indices, hash, WK);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Equihash solver
|
// Equihash solver
|
||||||
// Copyright (c) 2016 John Tromp
|
// Copyright (c) 2016 John Tromp, The Zcash developers
|
||||||
|
|
||||||
// Fix N, K, such that n = N/(k+1) is integer
|
// Fix N, K, such that n = N/(k+1) is integer
|
||||||
// Fix M = 2^{n+1} hashes each of length N bits,
|
// Fix M = 2^{n+1} hashes each of length N bits,
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
// the i*n 0s, each bucket having 4 * 2^RESTBITS slots,
|
// the i*n 0s, each bucket having 4 * 2^RESTBITS slots,
|
||||||
// twice the number of subtrees expected to land there.
|
// twice the number of subtrees expected to land there.
|
||||||
|
|
||||||
#include "equi.h"
|
#include "pow/tromp/equi.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
typedef uint16_t u16;
|
typedef uint16_t u16;
|
||||||
typedef uint64_t u64;
|
typedef uint64_t u64;
|
||||||
|
|
||||||
#ifdef ATOMIC
|
#ifdef EQUIHASH_TROMP_ATOMIC
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
typedef std::atomic<u32> au32;
|
typedef std::atomic<u32> au32;
|
||||||
#else
|
#else
|
||||||
@@ -204,7 +204,7 @@ u32 min(const u32 a, const u32 b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct equi {
|
struct equi {
|
||||||
blake2b_state blake_ctx;
|
crypto_generichash_blake2b_state blake_ctx;
|
||||||
htalloc hta;
|
htalloc hta;
|
||||||
bsizes *nslots; // PUT IN BUCKET STRUCT
|
bsizes *nslots; // PUT IN BUCKET STRUCT
|
||||||
proof *sols;
|
proof *sols;
|
||||||
@@ -228,13 +228,13 @@ struct equi {
|
|||||||
free(nslots);
|
free(nslots);
|
||||||
free(sols);
|
free(sols);
|
||||||
}
|
}
|
||||||
void setnonce(const char *header, const u32 headerlen, const u32 nonce) {
|
void setstate(const crypto_generichash_blake2b_state *ctx) {
|
||||||
setheader(&blake_ctx, header, headerlen, nonce);
|
blake_ctx = *ctx;
|
||||||
memset(nslots, 0, NBUCKETS * sizeof(au32)); // only nslots[0] needs zeroing
|
memset(nslots, 0, NBUCKETS * sizeof(au32)); // only nslots[0] needs zeroing
|
||||||
nsols = 0;
|
nsols = 0;
|
||||||
}
|
}
|
||||||
u32 getslot(const u32 r, const u32 bucketi) {
|
u32 getslot(const u32 r, const u32 bucketi) {
|
||||||
#ifdef ATOMIC
|
#ifdef EQUIHASH_TROMP_ATOMIC
|
||||||
return std::atomic_fetch_add_explicit(&nslots[r&1][bucketi], 1U, std::memory_order_relaxed);
|
return std::atomic_fetch_add_explicit(&nslots[r&1][bucketi], 1U, std::memory_order_relaxed);
|
||||||
#else
|
#else
|
||||||
return nslots[r&1][bucketi]++;
|
return nslots[r&1][bucketi]++;
|
||||||
@@ -282,7 +282,7 @@ struct equi {
|
|||||||
for (u32 i=1; i<PROOFSIZE; i++)
|
for (u32 i=1; i<PROOFSIZE; i++)
|
||||||
if (prf[i] <= prf[i-1])
|
if (prf[i] <= prf[i-1])
|
||||||
return;
|
return;
|
||||||
#ifdef ATOMIC
|
#ifdef EQUIHASH_TROMP_ATOMIC
|
||||||
u32 soli = std::atomic_fetch_add_explicit(&nsols, 1U, std::memory_order_relaxed);
|
u32 soli = std::atomic_fetch_add_explicit(&nsols, 1U, std::memory_order_relaxed);
|
||||||
#else
|
#else
|
||||||
u32 soli = nsols++;
|
u32 soli = nsols++;
|
||||||
@@ -431,14 +431,14 @@ struct equi {
|
|||||||
|
|
||||||
void digit0(const u32 id) {
|
void digit0(const u32 id) {
|
||||||
uchar hash[HASHOUT];
|
uchar hash[HASHOUT];
|
||||||
blake2b_state state;
|
crypto_generichash_blake2b_state state;
|
||||||
htlayout htl(this, 0);
|
htlayout htl(this, 0);
|
||||||
const u32 hashbytes = hashsize(0);
|
const u32 hashbytes = hashsize(0);
|
||||||
for (u32 block = id; block < NBLOCKS; block += nthreads) {
|
for (u32 block = id; block < NBLOCKS; block += nthreads) {
|
||||||
state = blake_ctx;
|
state = blake_ctx;
|
||||||
u32 leb = htole32(block);
|
u32 leb = htole32(block);
|
||||||
blake2b_update(&state, (uchar *)&leb, sizeof(u32));
|
crypto_generichash_blake2b_update(&state, (uchar *)&leb, sizeof(u32));
|
||||||
blake2b_final(&state, hash, HASHOUT);
|
crypto_generichash_blake2b_final(&state, hash, HASHOUT);
|
||||||
for (u32 i = 0; i<HASHESPERBLAKE; i++) {
|
for (u32 i = 0; i<HASHESPERBLAKE; i++) {
|
||||||
const uchar *ph = hash + i * WN/8;
|
const uchar *ph = hash + i * WN/8;
|
||||||
#if BUCKBITS == 16 && RESTBITS == 4
|
#if BUCKBITS == 16 && RESTBITS == 4
|
||||||
|
|||||||
Reference in New Issue
Block a user