Auto merge of #1120 - str4d:1055-asynchronous-miner-interrupts, r=str4d

Make Equihash solvers cancellable

The miner only cancels the solver when chainActive.Tip() changes.

Closes #1055
This commit is contained in:
zkbot
2016-07-25 23:51:23 +00:00
9 changed files with 282 additions and 50 deletions

View File

@@ -6,6 +6,7 @@ zcash_gtest_SOURCES = \
gtest/main.cpp \ gtest/main.cpp \
gtest/test_tautology.cpp \ gtest/test_tautology.cpp \
gtest/test_checktransaction.cpp \ gtest/test_checktransaction.cpp \
gtest/test_equihash.cpp \
gtest/test_joinsplit.cpp \ gtest/test_joinsplit.cpp \
gtest/test_noteencryption.cpp \ gtest/test_noteencryption.cpp \
gtest/test_merkletree.cpp \ gtest/test_merkletree.cpp \

View File

@@ -21,6 +21,8 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
EhSolverCancelledException solver_cancelled;
template<unsigned int N, unsigned int K> template<unsigned int N, unsigned int K>
int Equihash<N,K>::InitialiseState(eh_HashState& base_state) int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
{ {
@@ -183,15 +185,15 @@ TruncatedStepRow<WIDTH>& TruncatedStepRow<WIDTH>::operator=(const TruncatedStepR
} }
template<size_t WIDTH> template<size_t WIDTH>
eh_trunc* TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t len, size_t lenIndices) const std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t len, size_t lenIndices) const
{ {
eh_trunc* p = new eh_trunc[lenIndices]; std::shared_ptr<eh_trunc> p (new eh_trunc[lenIndices]);
std::copy(hash+len, hash+len+lenIndices, p); std::copy(hash+len, hash+len+lenIndices, p.get());
return p; return p;
} }
template<unsigned int N, unsigned int K> template<unsigned int N, unsigned int K>
std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& base_state) std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled)
{ {
eh_index init_size { 1 << (CollisionBitLength + 1) }; eh_index init_size { 1 << (CollisionBitLength + 1) };
@@ -203,6 +205,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
X.reserve(init_size); X.reserve(init_size);
for (eh_index i = 0; i < init_size; i++) { for (eh_index i = 0; i < init_size; i++) {
X.emplace_back(N, base_state, i); X.emplace_back(N, base_state, i);
if (cancelled(ListGeneration)) throw solver_cancelled;
} }
// 3) Repeat step 2 until 2n/(k+1) bits remain // 3) Repeat step 2 until 2n/(k+1) bits remain
@@ -211,6 +214,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
// 2a) Sort the list // 2a) Sort the list
LogPrint("pow", "- Sorting list\n"); LogPrint("pow", "- Sorting list\n");
std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength)); std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength));
if (cancelled(ListSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n"); LogPrint("pow", "- Finding collisions\n");
int i = 0; int i = 0;
@@ -240,6 +244,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
} }
i += j; i += j;
if (cancelled(ListColliding)) throw solver_cancelled;
} }
// 2e) Handle edge case where final table entry has no collision // 2e) Handle edge case where final table entry has no collision
@@ -259,6 +264,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
hashLen -= CollisionByteLength; hashLen -= CollisionByteLength;
lenIndices *= 2; lenIndices *= 2;
if (cancelled(RoundEnd)) throw solver_cancelled;
} }
// k+1) Find a collision on last 2n(k+1) bits // k+1) Find a collision on last 2n(k+1) bits
@@ -267,6 +273,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
if (X.size() > 1) { if (X.size() > 1) {
LogPrint("pow", "- Sorting list\n"); LogPrint("pow", "- Sorting list\n");
std::sort(X.begin(), X.end(), CompareSR(hashLen)); std::sort(X.begin(), X.end(), CompareSR(hashLen));
if (cancelled(FinalSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n"); LogPrint("pow", "- Finding collisions\n");
int i = 0; int i = 0;
while (i < X.size() - 1) { while (i < X.size() - 1) {
@@ -286,6 +293,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
} }
i += j; i += j;
if (cancelled(FinalColliding)) throw solver_cancelled;
} }
} else } else
LogPrint("pow", "- List is empty\n"); LogPrint("pow", "- List is empty\n");
@@ -346,16 +354,17 @@ void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, cons
} }
template<unsigned int N, unsigned int K> template<unsigned int N, unsigned int K>
std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state) std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled)
{ {
eh_index init_size { 1 << (CollisionBitLength + 1) }; eh_index init_size { 1 << (CollisionBitLength + 1) };
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
// First run the algorithm with truncated indices // First run the algorithm with truncated indices
eh_index soln_size { 1 << K }; eh_index soln_size { 1 << K };
// Each element of partialSolns is dynamically allocated in a call to std::vector<std::shared_ptr<eh_trunc>> partialSolns;
// GetTruncatedIndices(), and freed at the end of this function. std::set<std::vector<eh_index>> solns;
std::vector<eh_trunc*> partialSolns; int invalidCount = 0;
{ {
// 1) Generate first list // 1) Generate first list
@@ -366,6 +375,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
Xt.reserve(init_size); Xt.reserve(init_size);
for (eh_index i = 0; i < init_size; i++) { for (eh_index i = 0; i < init_size; i++) {
Xt.emplace_back(N, base_state, i, CollisionBitLength + 1); Xt.emplace_back(N, base_state, i, CollisionBitLength + 1);
if (cancelled(ListGeneration)) throw solver_cancelled;
} }
// 3) Repeat step 2 until 2n/(k+1) bits remain // 3) Repeat step 2 until 2n/(k+1) bits remain
@@ -374,6 +384,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
// 2a) Sort the list // 2a) Sort the list
LogPrint("pow", "- Sorting list\n"); LogPrint("pow", "- Sorting list\n");
std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength)); std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength));
if (cancelled(ListSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n"); LogPrint("pow", "- Finding collisions\n");
int i = 0; int i = 0;
@@ -402,6 +413,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
} }
i += j; i += j;
if (cancelled(ListColliding)) throw solver_cancelled;
} }
// 2e) Handle edge case where final table entry has no collision // 2e) Handle edge case where final table entry has no collision
@@ -421,6 +433,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
hashLen -= CollisionByteLength; hashLen -= CollisionByteLength;
lenIndices *= 2; lenIndices *= 2;
if (cancelled(RoundEnd)) throw solver_cancelled;
} }
// k+1) Find a collision on last 2n(k+1) bits // k+1) Find a collision on last 2n(k+1) bits
@@ -428,6 +441,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
if (Xt.size() > 1) { if (Xt.size() > 1) {
LogPrint("pow", "- Sorting list\n"); LogPrint("pow", "- Sorting list\n");
std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen)); std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen));
if (cancelled(FinalSorting)) throw solver_cancelled;
LogPrint("pow", "- Finding collisions\n"); LogPrint("pow", "- Finding collisions\n");
int i = 0; int i = 0;
while (i < Xt.size() - 1) { while (i < Xt.size() - 1) {
@@ -445,6 +459,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
} }
i += j; i += j;
if (cancelled(FinalColliding)) throw solver_cancelled;
} }
} else } else
LogPrint("pow", "- List is empty\n"); LogPrint("pow", "- List is empty\n");
@@ -455,10 +470,7 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
// Now for each solution run the algorithm again to recreate the indices // Now for each solution run the algorithm again to recreate the indices
LogPrint("pow", "Culling solutions\n"); LogPrint("pow", "Culling solutions\n");
std::set<std::vector<eh_index>> solns; for (std::shared_ptr<eh_trunc> partialSoln : partialSolns) {
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
int invalidCount = 0;
for (eh_trunc* partialSoln : partialSolns) {
size_t hashLen; size_t hashLen;
size_t lenIndices; size_t lenIndices;
std::vector<boost::optional<std::vector<FullStepRow<FinalFullWidth>>>> X; std::vector<boost::optional<std::vector<FullStepRow<FinalFullWidth>>>> X;
@@ -470,8 +482,9 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
std::vector<FullStepRow<FinalFullWidth>> icv; std::vector<FullStepRow<FinalFullWidth>> icv;
icv.reserve(recreate_size); icv.reserve(recreate_size);
for (eh_index j = 0; j < recreate_size; j++) { for (eh_index j = 0; j < recreate_size; j++) {
eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) }; eh_index newIndex { UntruncateIndex(partialSoln.get()[i], j, CollisionBitLength + 1) };
icv.emplace_back(N, base_state, newIndex); icv.emplace_back(N, base_state, newIndex);
if (cancelled(PartialGeneration)) throw solver_cancelled;
} }
boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv; boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv;
@@ -487,11 +500,12 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
ic->reserve(ic->size() + X[r]->size()); ic->reserve(ic->size() + X[r]->size());
ic->insert(ic->end(), X[r]->begin(), X[r]->end()); ic->insert(ic->end(), X[r]->begin(), X[r]->end());
std::sort(ic->begin(), ic->end(), CompareSR(hashLen)); std::sort(ic->begin(), ic->end(), CompareSR(hashLen));
if (cancelled(PartialSorting)) throw solver_cancelled;
size_t lti = rti-(1<<r); size_t lti = rti-(1<<r);
CollideBranches(*ic, hashLen, lenIndices, CollideBranches(*ic, hashLen, lenIndices,
CollisionByteLength, CollisionByteLength,
CollisionBitLength + 1, CollisionBitLength + 1,
partialSoln[lti], partialSoln[rti]); partialSoln.get()[lti], partialSoln.get()[rti]);
// 2d) Check if this has become an invalid solution // 2d) Check if this has become an invalid solution
if (ic->size() == 0) if (ic->size() == 0)
@@ -509,7 +523,9 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
X.push_back(ic); X.push_back(ic);
break; break;
} }
if (cancelled(PartialSubtreeEnd)) throw solver_cancelled;
} }
if (cancelled(PartialIndexEnd)) throw solver_cancelled;
} }
// We are at the top of the tree // We are at the top of the tree
@@ -517,13 +533,11 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
for (FullStepRow<FinalFullWidth> row : *X[K]) { for (FullStepRow<FinalFullWidth> row : *X[K]) {
solns.insert(row.GetIndices(hashLen, lenIndices)); solns.insert(row.GetIndices(hashLen, lenIndices));
} }
goto deletesolution; if (cancelled(PartialEnd)) throw solver_cancelled;
continue;
invalidsolution: invalidsolution:
invalidCount++; invalidCount++;
deletesolution:
delete[] partialSoln;
} }
LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount); LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount);
@@ -577,18 +591,18 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
// Explicit instantiations for Equihash<96,3> // Explicit instantiations for Equihash<96,3>
template int Equihash<96,3>::InitialiseState(eh_HashState& base_state); template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<96,3>::BasicSolve(const eh_HashState& base_state); template std::set<std::vector<eh_index>> Equihash<96,3>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
template std::set<std::vector<eh_index>> Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state); template std::set<std::vector<eh_index>> Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln); template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
// Explicit instantiations for Equihash<96,5> // Explicit instantiations for Equihash<96,5>
template int Equihash<96,5>::InitialiseState(eh_HashState& base_state); template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<96,5>::BasicSolve(const eh_HashState& base_state); template std::set<std::vector<eh_index>> Equihash<96,5>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
template std::set<std::vector<eh_index>> Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state); template std::set<std::vector<eh_index>> Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln); template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
// Explicit instantiations for Equihash<48,5> // Explicit instantiations for Equihash<48,5>
template int Equihash<48,5>::InitialiseState(eh_HashState& base_state); template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<48,5>::BasicSolve(const eh_HashState& base_state); template std::set<std::vector<eh_index>> Equihash<48,5>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
template std::set<std::vector<eh_index>> Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state); template std::set<std::vector<eh_index>> Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln); template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);

View File

@@ -12,6 +12,9 @@
#include "sodium.h" #include "sodium.h"
#include <cstring> #include <cstring>
#include <exception>
#include <functional>
#include <memory>
#include <set> #include <set>
#include <vector> #include <vector>
@@ -105,7 +108,29 @@ public:
TruncatedStepRow& operator=(const TruncatedStepRow<WIDTH>& a); TruncatedStepRow& operator=(const TruncatedStepRow<WIDTH>& a);
inline bool IndicesBefore(const TruncatedStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } inline bool IndicesBefore(const TruncatedStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
eh_trunc* GetTruncatedIndices(size_t len, size_t lenIndices) const; std::shared_ptr<eh_trunc> GetTruncatedIndices(size_t len, size_t lenIndices) const;
};
enum EhSolverCancelCheck
{
ListGeneration,
ListSorting,
ListColliding,
RoundEnd,
FinalSorting,
FinalColliding,
PartialGeneration,
PartialSorting,
PartialSubtreeEnd,
PartialIndexEnd,
PartialEnd
};
class EhSolverCancelledException : public std::exception
{
virtual const char* what() const throw() {
return "Equihash solver was cancelled";
}
}; };
inline constexpr const size_t max(const size_t A, const size_t B) { return A > B ? A : B; } inline constexpr const size_t max(const size_t A, const size_t B) { return A > B ? A : B; }
@@ -130,8 +155,8 @@ public:
Equihash() { } Equihash() { }
int InitialiseState(eh_HashState& base_state); int InitialiseState(eh_HashState& base_state);
std::set<std::vector<eh_index>> BasicSolve(const eh_HashState& base_state); std::set<std::vector<eh_index>> BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
std::set<std::vector<eh_index>> OptimisedSolve(const eh_HashState& base_state); std::set<std::vector<eh_index>> OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
bool IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln); bool IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
}; };
@@ -152,27 +177,31 @@ static Equihash<48,5> Eh48_5;
throw std::invalid_argument("Unsupported Equihash parameters"); \ throw std::invalid_argument("Unsupported Equihash parameters"); \
} }
#define EhBasicSolve(n, k, base_state, solns) \ #define EhBasicSolve(n, k, base_state, solns, cancelled) \
if (n == 96 && k == 3) { \ if (n == 96 && k == 3) { \
solns = Eh96_3.BasicSolve(base_state); \ solns = Eh96_3.BasicSolve(base_state, cancelled); \
} else if (n == 96 && k == 5) { \ } else if (n == 96 && k == 5) { \
solns = Eh96_5.BasicSolve(base_state); \ solns = Eh96_5.BasicSolve(base_state, cancelled); \
} else if (n == 48 && k == 5) { \ } else if (n == 48 && k == 5) { \
solns = Eh48_5.BasicSolve(base_state); \ solns = Eh48_5.BasicSolve(base_state, cancelled); \
} else { \ } else { \
throw std::invalid_argument("Unsupported Equihash parameters"); \ throw std::invalid_argument("Unsupported Equihash parameters"); \
} }
#define EhBasicSolveUncancellable(n, k, base_state, solns) \
EhBasicSolve(n, k, base_state, solns, [](EhSolverCancelCheck pos) { return false; })
#define EhOptimisedSolve(n, k, base_state, solns) \ #define EhOptimisedSolve(n, k, base_state, solns, cancelled) \
if (n == 96 && k == 3) { \ if (n == 96 && k == 3) { \
solns = Eh96_3.OptimisedSolve(base_state); \ solns = Eh96_3.OptimisedSolve(base_state, cancelled); \
} else if (n == 96 && k == 5) { \ } else if (n == 96 && k == 5) { \
solns = Eh96_5.OptimisedSolve(base_state); \ solns = Eh96_5.OptimisedSolve(base_state, cancelled); \
} else if (n == 48 && k == 5) { \ } else if (n == 48 && k == 5) { \
solns = Eh48_5.OptimisedSolve(base_state); \ solns = Eh48_5.OptimisedSolve(base_state, cancelled); \
} else { \ } else { \
throw std::invalid_argument("Unsupported Equihash parameters"); \ throw std::invalid_argument("Unsupported Equihash parameters"); \
} }
#define EhOptimisedSolveUncancellable(n, k, base_state, solns) \
EhOptimisedSolve(n, k, base_state, solns, [](EhSolverCancelCheck pos) { return false; })
#define EhIsValidSolution(n, k, base_state, soln, ret) \ #define EhIsValidSolution(n, k, base_state, soln, ret) \
if (n == 96 && k == 3) { \ if (n == 96 && k == 3) { \

162
src/gtest/test_equihash.cpp Normal file
View File

@@ -0,0 +1,162 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "crypto/equihash.h"
TEST(equihash_tests, check_basic_solver_cancelled) {
Equihash<48,5> Eh48_5;
crypto_generichash_blake2b_state state;
Eh48_5.InitialiseState(state);
std::set<std::vector<unsigned int>> solns;
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return false;
}));
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == ListGeneration;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == ListSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == ListColliding;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == RoundEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == FinalSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == FinalColliding;
}), EhSolverCancelledException);
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialGeneration;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialSorting;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialSubtreeEnd;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialIndexEnd;
}));
}
{
ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialEnd;
}));
}
}
TEST(equihash_tests, check_optimised_solver_cancelled) {
Equihash<48,5> Eh48_5;
crypto_generichash_blake2b_state state;
Eh48_5.InitialiseState(state);
std::set<std::vector<unsigned int>> solns;
{
ASSERT_NO_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return false;
}));
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == ListGeneration;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == ListSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == ListColliding;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == RoundEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == FinalSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == FinalColliding;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialGeneration;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialSorting;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialSubtreeEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialIndexEnd;
}), EhSolverCancelledException);
}
{
ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) {
return pos == PartialEnd;
}), EhSolverCancelledException);
}
}

View File

@@ -21,12 +21,14 @@
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
#include "crypto/equihash.h" #include "crypto/equihash.h"
#include "wallet/wallet.h" #include "wallet/wallet.h"
#include <functional>
#endif #endif
#include "sodium.h" #include "sodium.h"
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <mutex>
using namespace std; using namespace std;
@@ -454,6 +456,15 @@ 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::mutex m_cs;
bool cancelSolver = false;
boost::signals2::connection c = uiInterface.NotifyBlockTip.connect(
[&m_cs, &cancelSolver](const uint256& hashNewTip) mutable {
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = true;
}
);
try { try {
while (true) { while (true) {
if (chainparams.MiningRequiresPeers()) { if (chainparams.MiningRequiresPeers()) {
@@ -519,7 +530,17 @@ void static BitcoinMiner(CWallet *pwallet)
LogPrint("pow", "Running Equihash solver with nNonce = %s\n", LogPrint("pow", "Running Equihash solver with nNonce = %s\n",
pblock->nNonce.ToString()); pblock->nNonce.ToString());
std::set<std::vector<unsigned int>> solns; std::set<std::vector<unsigned int>> solns;
EhOptimisedSolve(n, k, curr_state, solns); try {
std::function<bool(EhSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) {
std::lock_guard<std::mutex> lock{m_cs};
return cancelSolver;
};
EhOptimisedSolve(n, k, curr_state, solns, cancelled);
} catch (EhSolverCancelledException&) {
LogPrint("pow", "Equihash solver cancelled\n");
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
}
LogPrint("pow", "Solutions: %d\n", solns.size()); LogPrint("pow", "Solutions: %d\n", solns.size());
// Write the solution to the hash and compute the result. // Write the solution to the hash and compute the result.
@@ -534,7 +555,11 @@ void static BitcoinMiner(CWallet *pwallet)
SetThreadPriority(THREAD_PRIORITY_NORMAL); SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("ZcashMiner:\n"); LogPrintf("ZcashMiner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex()); LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
ProcessBlockFound(pblock, *pwallet, reservekey); if (ProcessBlockFound(pblock, *pwallet, reservekey)) {
// Ignore chain updates caused by us
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
}
SetThreadPriority(THREAD_PRIORITY_LOWEST); SetThreadPriority(THREAD_PRIORITY_LOWEST);
// In regression test mode, stop mining after a block is found. // In regression test mode, stop mining after a block is found.
@@ -577,6 +602,7 @@ void static BitcoinMiner(CWallet *pwallet)
LogPrintf("ZcashMiner runtime error: %s\n", e.what()); LogPrintf("ZcashMiner runtime error: %s\n", e.what());
return; return;
} }
c.disconnect();
} }
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)

View File

@@ -189,7 +189,7 @@ Value generate(const Array& params, bool fHelp)
// (x_1, x_2, ...) = A(I, V, n, k) // (x_1, x_2, ...) = A(I, V, n, k)
std::set<std::vector<unsigned int>> solns; std::set<std::vector<unsigned int>> solns;
EhBasicSolve(n, k, curr_state, solns); EhBasicSolveUncancellable(n, k, curr_state, solns);
for (auto soln : solns) { for (auto soln : solns) {
bool isValid; bool isValid;

View File

@@ -50,7 +50,7 @@ void TestEquihashSolvers(unsigned int n, unsigned int k, const std::string &I, c
// First test the basic solver // First test the basic solver
std::set<std::vector<uint32_t>> ret; std::set<std::vector<uint32_t>> ret;
EhBasicSolve(n, k, state, ret); EhBasicSolveUncancellable(n, k, state, ret);
BOOST_TEST_MESSAGE("[Basic] Number of solutions: " << ret.size()); BOOST_TEST_MESSAGE("[Basic] Number of solutions: " << ret.size());
std::stringstream strm; std::stringstream strm;
PrintSolutions(strm, ret); PrintSolutions(strm, ret);
@@ -59,7 +59,7 @@ void TestEquihashSolvers(unsigned int n, unsigned int k, const std::string &I, c
// The optimised solver should have the exact same result // The optimised solver should have the exact same result
std::set<std::vector<uint32_t>> retOpt; std::set<std::vector<uint32_t>> retOpt;
EhOptimisedSolve(n, k, state, retOpt); EhOptimisedSolveUncancellable(n, k, state, retOpt);
BOOST_TEST_MESSAGE("[Optimised] Number of solutions: " << retOpt.size()); BOOST_TEST_MESSAGE("[Optimised] Number of solutions: " << retOpt.size());
strm.str(""); strm.str("");
PrintSolutions(strm, retOpt); PrintSolutions(strm, retOpt);

View File

@@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// (x_1, x_2, ...) = A(I, V, n, k) // (x_1, x_2, ...) = A(I, V, n, k)
std::set<std::vector<unsigned int>> solns; std::set<std::vector<unsigned int>> solns;
EhOptimisedSolve(n, k, curr_state, solns); EhOptimisedSolveUncancellable(n, k, curr_state, solns);
bool ret; bool ret;
for (auto soln : solns) { for (auto soln : solns) {

View File

@@ -118,7 +118,7 @@ double benchmark_solve_equihash()
timer_start(); timer_start();
std::set<std::vector<unsigned int>> solns; std::set<std::vector<unsigned int>> solns;
EhOptimisedSolve(n, k, eh_state, solns); EhOptimisedSolveUncancellable(n, k, eh_state, solns);
return timer_stop(); return timer_stop();
} }