ac_nk params

This commit is contained in:
blackjok3r
2019-04-17 13:42:02 +08:00
parent c2c425d855
commit 18854e2af7
9 changed files with 308 additions and 135 deletions

View File

@@ -54,33 +54,84 @@
#define __BYTE_ORDER BYTE_ORDER
#endif
*/
static EhSolverCancelledException solver_cancelled;
int8_t ZeroizeUnusedBits(size_t N, unsigned char* hash, size_t hLen)
{
uint8_t rem = N % 8;
if (rem)
{
// clear lowest 8-rem bits
const size_t step = GetSizeInBytes(N);
for (size_t i = step - 1; i < hLen; i += step)
{
uint8_t b = 0xff << (8-rem);
hash[i] &= b;
}
}
return(0);
}
template<unsigned int N, unsigned int K>
int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
{
uint32_t le_N = htole32(N);
uint32_t le_K = htole32(K);
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
memcpy(personalization, "ZcashPoW", 8);
if ( ASSETCHAINS_NK[0] == 0 && ASSETCHAINS_NK[1] == 0 )
memcpy(personalization, "ZcashPoW", 8);
else
memcpy(personalization, "NandKPoW", 8);
memcpy(personalization+8, &le_N, 4);
memcpy(personalization+12, &le_K, 4);
const uint8_t outlen = (512 / N) * GetSizeInBytes(N);
BOOST_STATIC_ASSERT(!((!outlen) || (outlen > BLAKE2B_OUTBYTES)));
return crypto_generichash_blake2b_init_salt_personal(&base_state,
NULL, 0, // No key.
(512/N)*N/8,
outlen,
NULL, // No salt.
personalization);
}
void GenerateHash(const eh_HashState& base_state, eh_index g,
unsigned char* hash, size_t hLen)
unsigned char* hash, size_t hLen, size_t N)
{
eh_HashState state;
state = base_state;
eh_index lei = htole32(g);
crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
sizeof(eh_index));
crypto_generichash_blake2b_final(&state, hash, hLen);
if ( ASSETCHAINS_NK[0] == 0 && ASSETCHAINS_NK[1] == 0 )
{
eh_HashState state;
state = base_state;
eh_index lei = htole32(g);
crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
sizeof(eh_index));
crypto_generichash_blake2b_final(&state, hash, hLen);
}
else
{
uint32_t myHash[16] = {0};
uint32_t startIndex = g & 0xFFFFFFF0;
for (uint32_t g2 = startIndex; g2 <= g; g2++) {
uint32_t tmpHash[16] = {0};
eh_HashState state;
state = base_state;
eh_index lei = htole32(g2);
crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
sizeof(eh_index));
crypto_generichash_blake2b_final(&state, (unsigned char*)&tmpHash[0], static_cast<uint8_t>(hLen));
for (uint32_t idx = 0; idx < 16; idx++) myHash[idx] += tmpHash[idx];
}
memcpy(hash, &myHash[0], hLen);
ZeroizeUnusedBits(N, hash, hLen);
}
}
void ExpandArray(const unsigned char* in, size_t in_len,
@@ -88,7 +139,7 @@ void ExpandArray(const unsigned char* in, size_t in_len,
size_t bit_len, size_t byte_pad)
{
assert(bit_len >= 8);
assert(8*sizeof(uint32_t) >= 7+bit_len);
assert(8*sizeof(uint32_t) >= bit_len);
size_t out_width { (bit_len+7)/8 + byte_pad };
assert(out_len == 8*out_width*in_len/bit_len);
@@ -131,10 +182,10 @@ void CompressArray(const unsigned char* in, size_t in_len,
size_t bit_len, size_t byte_pad)
{
assert(bit_len >= 8);
assert(8*sizeof(uint32_t) >= 7+bit_len);
assert(8*sizeof(uint32_t) >= bit_len);
size_t in_width { (bit_len+7)/8 + byte_pad };
assert(out_len == bit_len*in_len/(8*in_width));
assert(out_len == (bit_len*in_len/in_width + 7)/8);
uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 };
@@ -148,17 +199,23 @@ void CompressArray(const unsigned char* in, size_t in_len,
// When we have fewer than 8 bits left in the accumulator, read the next
// input element.
if (acc_bits < 8) {
if (j < in_len) {
acc_value = acc_value << bit_len;
for (size_t x = byte_pad; x < in_width; x++) {
acc_value = acc_value | (
(
// Apply bit_len_mask across byte boundaries
in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF)
) << (8*(in_width-x-1))); // Big-endian
in[j + x] & ((bit_len_mask >> (8 * (in_width - x - 1))) & 0xFF)
) << (8 * (in_width - x - 1))); // Big-endian
}
j += in_width;
acc_bits += bit_len;
}
else {
acc_value <<= 8 - acc_bits;
acc_bits += 8 - acc_bits;;
}
}
acc_bits -= 8;
out[i] = (acc_value >> acc_bits) & 0xFF;
@@ -207,7 +264,7 @@ std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
ExpandArray(minimal.data(), minimal.size(),
array.data(), lenIndices, cBitLen+1, bytePad);
std::vector<eh_index> ret;
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
for (size_t i = 0; i < lenIndices; i += sizeof(eh_index)) {
ret.push_back(ArrayToEhIndex(array.data()+i));
}
return ret;
@@ -221,7 +278,7 @@ std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) };
size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
std::vector<unsigned char> array(lenIndices);
for (int i = 0; i < indices.size(); i++) {
for (size_t i = 0; i < indices.size(); i++) {
EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index)));
}
std::vector<unsigned char> ret(minLen);
@@ -254,12 +311,12 @@ FullStepRow<WIDTH>::FullStepRow(const unsigned char* hashIn, size_t hInLen,
}
template<size_t WIDTH> template<size_t W>
FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, size_t trim) :
StepRow<WIDTH> {a}
{
assert(len+lenIndices <= W);
assert(len-trim+(2*lenIndices) <= WIDTH);
for (int i = trim; i < len; i++)
for (size_t i = trim; i < len; i++)
hash[i-trim] = a.hash[i] ^ b.hash[i];
if (a.IndicesBefore(b, len, lenIndices)) {
std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
@@ -281,7 +338,7 @@ template<size_t WIDTH>
bool StepRow<WIDTH>::IsZero(size_t len)
{
// This doesn't need to be constant time.
for (int i = 0; i < len; i++) {
for (size_t i = 0; i < len; i++) {
if (hash[i] != 0)
return false;
}
@@ -301,10 +358,10 @@ std::vector<unsigned char> FullStepRow<WIDTH>::GetIndices(size_t len, size_t len
}
template<size_t WIDTH>
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l)
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, size_t l)
{
// This doesn't need to be constant time.
for (int j = 0; j < l; j++) {
for (size_t j = 0; j < l; j++) {
if (a.hash[j] != b.hash[j])
return false;
}
@@ -326,7 +383,7 @@ TruncatedStepRow<WIDTH>::TruncatedStepRow(const TruncatedStepRow<W>& a, const Tr
{
assert(len+lenIndices <= W);
assert(len-trim+(2*lenIndices) <= WIDTH);
for (int i = trim; i < len; i++)
for (size_t i = static_cast<size_t>(trim); i < len; i++)
hash[i-trim] = a.hash[i] ^ b.hash[i];
if (a.IndicesBefore(b, len, lenIndices)) {
std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
@@ -355,10 +412,10 @@ std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t le
#ifdef ENABLE_MINING
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
{
eh_index init_size { 1 << (CollisionBitLength + 1) };
eh_index init_size { 1U << (CollisionBitLength + 1) };
// 1) Generate first list
LogPrint("pow", "Generating first list\n");
@@ -368,16 +425,16 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
X.reserve(init_size);
unsigned char tmpHash[HashOutput];
for (eh_index g = 0; X.size() < init_size; g++) {
GenerateHash(base_state, g, tmpHash, HashOutput);
GenerateHash(base_state, g, tmpHash, HashOutput, N);
for (eh_index i = 0; i < IndicesPerHashOutput && X.size() < init_size; i++) {
X.emplace_back(tmpHash+(i*N/8), N/8, HashLength,
CollisionBitLength, (g*IndicesPerHashOutput)+i);
X.emplace_back(tmpHash+(i*GetSizeInBytes(N)), GetSizeInBytes(N), HashLength,
CollisionBitLength, static_cast<int>(g*IndicesPerHashOutput)+i);
}
if (cancelled(ListGeneration)) throw solver_cancelled;
}
// 3) Repeat step 2 until 2n/(k+1) bits remain
for (int r = 1; r < K && X.size() > 0; r++) {
for (unsigned int r = 1; r < K && X.size() > 0; r++) {
LogPrint("pow", "Round %d:\n", r);
// 2a) Sort the list
LogPrint("pow", "- Sorting list\n");
@@ -385,20 +442,20 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
if (cancelled(ListSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n");
int i = 0;
int posFree = 0;
size_t i = 0;
size_t posFree = 0;
std::vector<FullStepRow<FullWidth>> Xc;
while (i < X.size() - 1) {
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1;
size_t j = 1;
while (i+j < X.size() &&
HasCollision(X[i], X[i+j], CollisionByteLength)) {
j++;
}
// 2c) Calculate tuples (X_i ^ X_j, (i, j))
for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) {
for (size_t l = 0; l < j - 1; l++) {
for (size_t m = l + 1; m < j; m++) {
if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength);
}
@@ -442,16 +499,16 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
std::sort(X.begin(), X.end(), CompareSR(hashLen));
if (cancelled(FinalSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n");
int i = 0;
size_t i = 0;
while (i < X.size() - 1) {
int j = 1;
size_t j = 1;
while (i+j < X.size() &&
HasCollision(X[i], X[i+j], hashLen)) {
j++;
}
for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) {
for (size_t l = 0; l < j - 1; l++) {
for (size_t m = l + 1; m < j; m++) {
FullStepRow<FinalFullWidth> res(X[i+l], X[i+m], hashLen, lenIndices, 0);
if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
auto soln = res.GetIndices(hashLen, 2*lenIndices, CollisionBitLength);
@@ -475,20 +532,21 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
template<size_t WIDTH>
void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, const size_t lenIndices, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt)
{
int i = 0;
int posFree = 0;
size_t i = 0;
size_t posFree = 0;
assert(X.size() > 0);
std::vector<FullStepRow<WIDTH>> Xc;
while (i < X.size() - 1) {
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1;
size_t j = 1;
while (i+j < X.size() &&
HasCollision(X[i], X[i+j], clen)) {
j++;
}
// 2c) Calculate tuples (X_i ^ X_j, (i, j))
for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) {
for (size_t l = 0; l < j - 1; l++) {
for (size_t m = l + 1; m < j; m++) {
if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) {
if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) {
Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen);
@@ -526,10 +584,10 @@ void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, cons
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
{
eh_index init_size { 1 << (CollisionBitLength + 1) };
eh_index init_size { 1U << (CollisionBitLength + 1) };
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
// First run the algorithm with truncated indices
@@ -547,16 +605,16 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
Xt.reserve(init_size);
unsigned char tmpHash[HashOutput];
for (eh_index g = 0; Xt.size() < init_size; g++) {
GenerateHash(base_state, g, tmpHash, HashOutput);
GenerateHash(base_state, g, tmpHash, HashOutput, N);
for (eh_index i = 0; i < IndicesPerHashOutput && Xt.size() < init_size; i++) {
Xt.emplace_back(tmpHash+(i*N/8), N/8, HashLength, CollisionBitLength,
(g*IndicesPerHashOutput)+i, CollisionBitLength + 1);
Xt.emplace_back(tmpHash+(i*GetSizeInBytes(N)), GetSizeInBytes(N), HashLength, CollisionBitLength,
static_cast<eh_index>(g*IndicesPerHashOutput)+i, static_cast<unsigned int>(CollisionBitLength + 1));
}
if (cancelled(ListGeneration)) throw solver_cancelled;
}
// 3) Repeat step 2 until 2n/(k+1) bits remain
for (int r = 1; r < K && Xt.size() > 0; r++) {
for (unsigned int r = 1; r < K && Xt.size() > 0; r++) {
LogPrint("pow", "Round %d:\n", r);
// 2a) Sort the list
LogPrint("pow", "- Sorting list\n");
@@ -564,21 +622,21 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
if (cancelled(ListSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n");
int i = 0;
int posFree = 0;
size_t i = 0;
size_t posFree = 0;
std::vector<TruncatedStepRow<TruncatedWidth>> Xc;
while (i < Xt.size() - 1) {
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1;
size_t j = 1;
while (i+j < Xt.size() &&
HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) {
j++;
}
// 2c) Calculate tuples (X_i ^ X_j, (i, j))
bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen));
for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) {
//bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen));
for (size_t l = 0; l < j - 1; l++) {
for (size_t m = l + 1; m < j; m++) {
// We truncated, so don't check for distinct indices here
TruncatedStepRow<TruncatedWidth> Xi {Xt[i+l], Xt[i+m],
hashLen, lenIndices,
@@ -628,16 +686,16 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen));
if (cancelled(FinalSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n");
int i = 0;
size_t i = 0;
while (i < Xt.size() - 1) {
int j = 1;
size_t j = 1;
while (i+j < Xt.size() &&
HasCollision(Xt[i], Xt[i+j], hashLen)) {
j++;
}
for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) {
for (size_t l = 0; l < j - 1; l++) {
for (size_t m = l + 1; m < j; m++) {
TruncatedStepRow<FinalTruncatedWidth> res(Xt[i+l], Xt[i+m],
hashLen, lenIndices, 0);
auto soln = res.GetTruncatedIndices(hashLen, 2*lenIndices);
@@ -676,10 +734,10 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
eh_index newIndex { UntruncateIndex(partialSoln.get()[i], j, CollisionBitLength + 1) };
if (j == 0 || newIndex % IndicesPerHashOutput == 0) {
GenerateHash(base_state, newIndex/IndicesPerHashOutput,
tmpHash, HashOutput);
tmpHash, HashOutput, N);
}
icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * N/8),
N/8, HashLength, CollisionBitLength, newIndex);
icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * GetSizeInBytes(N)),
GetSizeInBytes(N), HashLength, CollisionBitLength, newIndex);
if (cancelled(PartialGeneration)) throw solver_cancelled;
}
boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv;
@@ -697,7 +755,7 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
ic->insert(ic->end(), X[r]->begin(), X[r]->end());
std::sort(ic->begin(), ic->end(), CompareSR(hashLen));
if (cancelled(PartialSorting)) throw solver_cancelled;
size_t lti = rti-(1<<r);
size_t lti = rti-(static_cast<size_t>(1)<<r);
CollideBranches(*ic, hashLen, lenIndices,
CollisionByteLength,
CollisionBitLength + 1,
@@ -760,16 +818,16 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
X.reserve(1 << K);
unsigned char tmpHash[HashOutput];
for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) {
GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput);
X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8),
N/8, HashLength, CollisionBitLength, i);
GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput, N);
X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * GetSizeInBytes(N)),
GetSizeInBytes(N), HashLength, CollisionBitLength, i);
}
size_t hashLen = HashLength;
size_t lenIndices = sizeof(eh_index);
while (X.size() > 1) {
std::vector<FullStepRow<FinalFullWidth>> Xc;
for (int i = 0; i < X.size(); i += 2) {
for (size_t i = 0; i < X.size(); i += 2) {
if (!HasCollision(X[i], X[i+1], CollisionByteLength)) {
LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen));
@@ -795,50 +853,74 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
return X[0].IsZero(hashLen);
}
// Explicit instantiations for Equihash<96,3>
template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<200,9>
template int Equihash<200,9>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<96,5>
template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
// Explicit instantiations for Equihash<96,3>
template int Equihash<150,5>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
template bool Equihash<150,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
template bool Equihash<150,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
template bool Equihash<150,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<48,5>
template int Equihash<144,5>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<144,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<144,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<144,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<96,5>
template int Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<96,5>
template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<48,5>
template int Equihash<210,9>::InitialiseState(eh_HashState& base_state);
#ifdef ENABLE_MINING
template bool Equihash<210,9>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<210,9>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
#endif
template bool Equihash<210,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);