diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index e8a8fcb22..0e66e4886 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -21,6 +21,8 @@ #include +EhSolverCancelledException solver_cancelled; + template int Equihash::InitialiseState(eh_HashState& base_state) { @@ -191,7 +193,7 @@ eh_trunc* TruncatedStepRow::GetTruncatedIndices(size_t len, size_t lenInd } template -std::set> Equihash::BasicSolve(const eh_HashState& base_state) +std::set> Equihash::BasicSolve(const eh_HashState& base_state, const std::function cancelled) { eh_index init_size { 1 << (CollisionBitLength + 1) }; @@ -203,6 +205,8 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba X.reserve(init_size); for (eh_index i = 0; i < init_size; i++) { X.emplace_back(N, base_state, i); + // Slow down checking to prevent segfaults (??) + if (i % 10000 == 0 && cancelled()) throw solver_cancelled; } // 3) Repeat step 2 until 2n/(k+1) bits remain @@ -211,6 +215,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba // 2a) Sort the list LogPrint("pow", "- Sorting list\n"); std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength)); + if (cancelled()) throw solver_cancelled; LogPrint("pow", "- Finding collisions\n"); int i = 0; @@ -240,6 +245,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba } i += j; + if (cancelled()) throw solver_cancelled; } // 2e) Handle edge case where final table entry has no collision @@ -259,6 +265,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba hashLen -= CollisionByteLength; lenIndices *= 2; + if (cancelled()) throw solver_cancelled; } // k+1) Find a collision on last 2n(k+1) bits @@ -286,6 +293,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba } i += j; + if (cancelled()) throw solver_cancelled; } } else LogPrint("pow", "- List is empty\n"); @@ -346,7 +354,7 @@ void CollideBranches(std::vector>& X, const size_t hlen, cons } template -std::set> Equihash::OptimisedSolve(const eh_HashState& base_state) +std::set> Equihash::OptimisedSolve(const eh_HashState& base_state, const std::function cancelled) { eh_index init_size { 1 << (CollisionBitLength + 1) }; @@ -366,6 +374,8 @@ std::set> Equihash::OptimisedSolve(const eh_HashState Xt.reserve(init_size); for (eh_index i = 0; i < init_size; i++) { Xt.emplace_back(N, base_state, i, CollisionBitLength + 1); + // Slow down checking to prevent segfaults (??) + if (i % 10000 == 0 && cancelled()) throw solver_cancelled; } // 3) Repeat step 2 until 2n/(k+1) bits remain @@ -374,6 +384,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState // 2a) Sort the list LogPrint("pow", "- Sorting list\n"); std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength)); + if (cancelled()) throw solver_cancelled; LogPrint("pow", "- Finding collisions\n"); int i = 0; @@ -402,6 +413,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState } i += j; + if (cancelled()) throw solver_cancelled; } // 2e) Handle edge case where final table entry has no collision @@ -421,6 +433,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState hashLen -= CollisionByteLength; lenIndices *= 2; + if (cancelled()) throw solver_cancelled; } // k+1) Find a collision on last 2n(k+1) bits @@ -428,6 +441,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState if (Xt.size() > 1) { LogPrint("pow", "- Sorting list\n"); std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen)); + if (cancelled()) throw solver_cancelled; LogPrint("pow", "- Finding collisions\n"); int i = 0; while (i < Xt.size() - 1) { @@ -445,6 +459,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState } i += j; + if (cancelled()) break; } } else LogPrint("pow", "- List is empty\n"); @@ -458,6 +473,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState std::set> solns; eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) }; int invalidCount = 0; + if (cancelled()) goto cancelsolver; for (eh_trunc* partialSoln : partialSolns) { size_t hashLen; size_t lenIndices; @@ -472,6 +488,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState for (eh_index j = 0; j < recreate_size; j++) { eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) }; icv.emplace_back(N, base_state, newIndex); + if (cancelled()) goto cancelsolver; } boost::optional>> ic = icv; @@ -487,6 +504,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState ic->reserve(ic->size() + X[r]->size()); ic->insert(ic->end(), X[r]->begin(), X[r]->end()); std::sort(ic->begin(), ic->end(), CompareSR(hashLen)); + if (cancelled()) goto cancelsolver; size_t lti = rti-(1<> Equihash::OptimisedSolve(const eh_HashState X.push_back(ic); break; } + if (cancelled()) goto cancelsolver; } + if (cancelled()) goto cancelsolver; } // We are at the top of the tree @@ -517,17 +537,24 @@ std::set> Equihash::OptimisedSolve(const eh_HashState for (FullStepRow row : *X[K]) { solns.insert(row.GetIndices(hashLen, lenIndices)); } - goto deletesolution; + if (cancelled()) goto cancelsolver; + continue; invalidsolution: invalidCount++; - -deletesolution: - delete[] partialSoln; } LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount); + for (eh_trunc* partialSoln : partialSolns) { + delete[] partialSoln; + } return solns; + +cancelsolver: + for (eh_trunc* partialSoln : partialSolns) { + delete[] partialSoln; + } + throw solver_cancelled; } template @@ -577,18 +604,18 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector< // Explicit instantiations for Equihash<96,3> template int Equihash<96,3>::InitialiseState(eh_HashState& base_state); -template std::set> Equihash<96,3>::BasicSolve(const eh_HashState& base_state); -template std::set> Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state); +template std::set> Equihash<96,3>::BasicSolve(const eh_HashState& base_state, const std::function cancelled); +template std::set> Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, const std::function cancelled); template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector soln); // Explicit instantiations for Equihash<96,5> template int Equihash<96,5>::InitialiseState(eh_HashState& base_state); -template std::set> Equihash<96,5>::BasicSolve(const eh_HashState& base_state); -template std::set> Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state); +template std::set> Equihash<96,5>::BasicSolve(const eh_HashState& base_state, const std::function cancelled); +template std::set> Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, const std::function cancelled); template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); // Explicit instantiations for Equihash<48,5> template int Equihash<48,5>::InitialiseState(eh_HashState& base_state); -template std::set> Equihash<48,5>::BasicSolve(const eh_HashState& base_state); -template std::set> Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state); +template std::set> Equihash<48,5>::BasicSolve(const eh_HashState& base_state, const std::function cancelled); +template std::set> Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, const std::function cancelled); template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); diff --git a/src/crypto/equihash.h b/src/crypto/equihash.h index 49be6dfad..e87c693a2 100644 --- a/src/crypto/equihash.h +++ b/src/crypto/equihash.h @@ -12,6 +12,8 @@ #include "sodium.h" #include +#include +#include #include #include @@ -108,6 +110,14 @@ public: eh_trunc* GetTruncatedIndices(size_t len, size_t lenIndices) const; }; +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; } template @@ -130,8 +140,8 @@ public: Equihash() { } int InitialiseState(eh_HashState& base_state); - std::set> BasicSolve(const eh_HashState& base_state); - std::set> OptimisedSolve(const eh_HashState& base_state); + std::set> BasicSolve(const eh_HashState& base_state, const std::function cancelled); + std::set> OptimisedSolve(const eh_HashState& base_state, const std::function cancelled); bool IsValidSolution(const eh_HashState& base_state, std::vector soln); }; @@ -152,27 +162,31 @@ static Equihash<48,5> Eh48_5; throw std::invalid_argument("Unsupported Equihash parameters"); \ } -#define EhBasicSolve(n, k, base_state, solns) \ - if (n == 96 && k == 3) { \ - solns = Eh96_3.BasicSolve(base_state); \ - } else if (n == 96 && k == 5) { \ - solns = Eh96_5.BasicSolve(base_state); \ - } else if (n == 48 && k == 5) { \ - solns = Eh48_5.BasicSolve(base_state); \ - } else { \ +#define EhBasicSolve(n, k, base_state, solns, cancelled) \ + if (n == 96 && k == 3) { \ + solns = Eh96_3.BasicSolve(base_state, cancelled); \ + } else if (n == 96 && k == 5) { \ + solns = Eh96_5.BasicSolve(base_state, cancelled); \ + } else if (n == 48 && k == 5) { \ + solns = Eh48_5.BasicSolve(base_state, cancelled); \ + } else { \ throw std::invalid_argument("Unsupported Equihash parameters"); \ } +#define EhBasicSolveUncancellable(n, k, base_state, solns) \ + EhBasicSolve(n, k, base_state, solns, [] { return false; }) -#define EhOptimisedSolve(n, k, base_state, solns) \ - if (n == 96 && k == 3) { \ - solns = Eh96_3.OptimisedSolve(base_state); \ - } else if (n == 96 && k == 5) { \ - solns = Eh96_5.OptimisedSolve(base_state); \ - } else if (n == 48 && k == 5) { \ - solns = Eh48_5.OptimisedSolve(base_state); \ - } else { \ +#define EhOptimisedSolve(n, k, base_state, solns, cancelled) \ + if (n == 96 && k == 3) { \ + solns = Eh96_3.OptimisedSolve(base_state, cancelled); \ + } else if (n == 96 && k == 5) { \ + solns = Eh96_5.OptimisedSolve(base_state, cancelled); \ + } else if (n == 48 && k == 5) { \ + solns = Eh48_5.OptimisedSolve(base_state, cancelled); \ + } else { \ throw std::invalid_argument("Unsupported Equihash parameters"); \ } +#define EhOptimisedSolveUncancellable(n, k, base_state, solns) \ + EhOptimisedSolve(n, k, base_state, solns, [] { return false; }) #define EhIsValidSolution(n, k, base_state, soln, ret) \ if (n == 96 && k == 3) { \ diff --git a/src/miner.cpp b/src/miner.cpp index 95ec6df85..5065977cb 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -21,6 +21,7 @@ #ifdef ENABLE_WALLET #include "crypto/equihash.h" #include "wallet/wallet.h" +#include #endif #include "sodium.h" @@ -519,7 +520,12 @@ void static BitcoinMiner(CWallet *pwallet) LogPrint("pow", "Running Equihash solver with nNonce = %s\n", pblock->nNonce.ToString()); std::set> solns; - EhOptimisedSolve(n, k, curr_state, solns); + try { + std::function cancelled = [pindexPrev] { return pindexPrev != chainActive.Tip(); }; + EhOptimisedSolve(n, k, curr_state, solns, cancelled); + } catch (EhSolverCancelledException&) { + LogPrint("pow", "Equihash solver cancelled\n"); + } LogPrint("pow", "Solutions: %d\n", solns.size()); // Write the solution to the hash and compute the result. diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 82bbe0c12..95b32df96 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -189,7 +189,7 @@ Value generate(const Array& params, bool fHelp) // (x_1, x_2, ...) = A(I, V, n, k) std::set> solns; - EhBasicSolve(n, k, curr_state, solns); + EhBasicSolveUncancellable(n, k, curr_state, solns); for (auto soln : solns) { bool isValid; diff --git a/src/test/equihash_tests.cpp b/src/test/equihash_tests.cpp index dbe757a7a..cf4d98c31 100644 --- a/src/test/equihash_tests.cpp +++ b/src/test/equihash_tests.cpp @@ -50,7 +50,7 @@ void TestEquihashSolvers(unsigned int n, unsigned int k, const std::string &I, c // First test the basic solver std::set> ret; - EhBasicSolve(n, k, state, ret); + EhBasicSolveUncancellable(n, k, state, ret); BOOST_TEST_MESSAGE("[Basic] Number of solutions: " << ret.size()); std::stringstream strm; 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 std::set> retOpt; - EhOptimisedSolve(n, k, state, retOpt); + EhOptimisedSolveUncancellable(n, k, state, retOpt); BOOST_TEST_MESSAGE("[Optimised] Number of solutions: " << retOpt.size()); strm.str(""); PrintSolutions(strm, retOpt); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index b0a0bead4..adc606aa7 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // (x_1, x_2, ...) = A(I, V, n, k) std::set> solns; - EhOptimisedSolve(n, k, curr_state, solns); + EhOptimisedSolveUncancellable(n, k, curr_state, solns); bool ret; for (auto soln : solns) { diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index abe631f8b..5e90c55b5 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -118,7 +118,7 @@ double benchmark_solve_equihash() timer_start(); std::set> solns; - EhOptimisedSolve(n, k, eh_state, solns); + EhOptimisedSolveUncancellable(n, k, eh_state, solns); return timer_stop(); }