From 51eb5273f53b7eb394b299f1e76c31c0f47fcdfe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 27 Jul 2016 19:15:49 +1200 Subject: [PATCH 1/2] Equihash: Pass each obtained solution to a callback for immediate checking Closes #1143 --- src/crypto/equihash.cpp | 46 +++++++++++------ src/crypto/equihash.h | 70 +++++++++++++++++--------- src/gtest/test_equihash.cpp | 98 +++++++++++++++++++++++++++---------- src/miner.cpp | 37 +++++++------- src/rpcmining.cpp | 18 +++---- src/test/equihash_tests.cpp | 14 +++++- src/zcbenchmarks.cpp | 3 +- 7 files changed, 189 insertions(+), 97 deletions(-) diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index 515ae3302..cc9804752 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -193,7 +193,9 @@ std::shared_ptr TruncatedStepRow::GetTruncatedIndices(size_t le } template -std::set> Equihash::BasicSolve(const eh_HashState& base_state, const std::function cancelled) +bool Equihash::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) { eh_index init_size { 1 << (CollisionBitLength + 1) }; @@ -269,7 +271,6 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba // k+1) Find a collision on last 2n(k+1) bits LogPrint("pow", "Final round:\n"); - std::set> solns; if (X.size() > 1) { LogPrint("pow", "- Sorting list\n"); std::sort(X.begin(), X.end(), CompareSR(hashLen)); @@ -286,8 +287,9 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba for (int l = 0; l < j - 1; l++) { for (int m = l + 1; m < j; m++) { FullStepRow res(X[i+l], X[i+m], hashLen, lenIndices, 0); - if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) { - solns.insert(res.GetIndices(hashLen, 2*lenIndices)); + if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices) && + validBlock(res.GetIndices(hashLen, 2*lenIndices))) { + return true; } } } @@ -298,7 +300,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba } else LogPrint("pow", "- List is empty\n"); - return solns; + return false; } template @@ -354,7 +356,9 @@ void CollideBranches(std::vector>& X, const size_t hlen, cons } template -std::set> Equihash::OptimisedSolve(const eh_HashState& base_state, const std::function cancelled) +bool Equihash::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) { eh_index init_size { 1 << (CollisionBitLength + 1) }; eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) }; @@ -363,7 +367,6 @@ std::set> Equihash::OptimisedSolve(const eh_HashState eh_index soln_size { 1 << K }; std::vector> partialSolns; - std::set> solns; int invalidCount = 0; { @@ -531,7 +534,8 @@ std::set> Equihash::OptimisedSolve(const eh_HashState // We are at the top of the tree assert(X.size() == K+1); for (FullStepRow row : *X[K]) { - solns.insert(row.GetIndices(hashLen, lenIndices)); + if (validBlock(row.GetIndices(hashLen, lenIndices))) + return true; } if (cancelled(PartialEnd)) throw solver_cancelled; continue; @@ -541,7 +545,7 @@ invalidsolution: } LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount); - return solns; + return false; } template @@ -591,18 +595,30 @@ 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, 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>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + 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, 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>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + 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, 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>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + 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 4ba0e1c00..383e77221 100644 --- a/src/crypto/equihash.h +++ b/src/crypto/equihash.h @@ -155,8 +155,12 @@ public: Equihash() { } int InitialiseState(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 BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); + bool OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); bool IsValidSolution(const eh_HashState& base_state, std::vector soln); }; @@ -177,31 +181,49 @@ static Equihash<48,5> Eh48_5; throw std::invalid_argument("Unsupported Equihash parameters"); \ } -#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"); \ +inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + if (n == 96 && k == 3) { + return Eh96_3.BasicSolve(base_state, validBlock, cancelled); + } else if (n == 96 && k == 5) { + return Eh96_5.BasicSolve(base_state, validBlock, cancelled); + } else if (n == 48 && k == 5) { + return Eh48_5.BasicSolve(base_state, validBlock, cancelled); + } else { + 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, 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"); \ +inline bool EhBasicSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock) +{ + return EhBasicSolve(n, k, base_state, validBlock, + [](EhSolverCancelCheck pos) { return false; }); +} + +inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + if (n == 96 && k == 3) { + return Eh96_3.OptimisedSolve(base_state, validBlock, cancelled); + } else if (n == 96 && k == 5) { + return Eh96_5.OptimisedSolve(base_state, validBlock, cancelled); + } else if (n == 48 && k == 5) { + return Eh48_5.OptimisedSolve(base_state, validBlock, cancelled); + } else { + throw std::invalid_argument("Unsupported Equihash parameters"); } -#define EhOptimisedSolveUncancellable(n, k, base_state, solns) \ - EhOptimisedSolve(n, k, base_state, solns, [](EhSolverCancelCheck pos) { return false; }) +} + +inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state, + const std::function)> validBlock) +{ + return EhOptimisedSolve(n, k, base_state, validBlock, + [](EhSolverCancelCheck pos) { return false; }); +} #define EhIsValidSolution(n, k, base_state, soln, ret) \ if (n == 96 && k == 3) { \ diff --git a/src/gtest/test_equihash.cpp b/src/gtest/test_equihash.cpp index 7e281990a..0b9051833 100644 --- a/src/gtest/test_equihash.cpp +++ b/src/gtest/test_equihash.cpp @@ -7,76 +7,99 @@ TEST(equihash_tests, check_basic_solver_cancelled) { Equihash<48,5> Eh48_5; crypto_generichash_blake2b_state state; Eh48_5.InitialiseState(state); - std::set> solns; { - ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return false; })); } { - ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == ListGeneration; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == ListSorting; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == ListColliding; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == RoundEnd; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == FinalSorting; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == FinalColliding; }), EhSolverCancelledException); } { - ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialGeneration; })); } { - ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialSorting; })); } { - ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialSubtreeEnd; })); } { - ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialIndexEnd; })); } { - ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.BasicSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialEnd; })); } @@ -86,76 +109,99 @@ TEST(equihash_tests, check_optimised_solver_cancelled) { Equihash<48,5> Eh48_5; crypto_generichash_blake2b_state state; Eh48_5.InitialiseState(state); - std::set> solns; { - ASSERT_NO_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_NO_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return false; })); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == ListGeneration; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == ListSorting; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == ListColliding; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == RoundEnd; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == FinalSorting; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == FinalColliding; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialGeneration; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialSorting; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialSubtreeEnd; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialIndexEnd; }), EhSolverCancelledException); } { - ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](EhSolverCancelCheck pos) { + ASSERT_THROW(Eh48_5.OptimisedSolve(state, [](std::vector soln) { + return false; + }, [](EhSolverCancelCheck pos) { return pos == PartialEnd; }), EhSolverCancelledException); } diff --git a/src/miner.cpp b/src/miner.cpp index d110bb425..7f4949104 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -529,26 +529,16 @@ void static BitcoinMiner(CWallet *pwallet) // (x_1, x_2, ...) = A(I, V, n, k) LogPrint("pow", "Running Equihash solver with nNonce = %s\n", pblock->nNonce.ToString()); - std::set> solns; - try { - std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { - std::lock_guard lock{m_cs}; - return cancelSolver; - }; - EhOptimisedSolve(n, k, curr_state, solns, cancelled); - } catch (EhSolverCancelledException&) { - LogPrint("pow", "Equihash solver cancelled\n"); - std::lock_guard lock{m_cs}; - cancelSolver = false; - } - LogPrint("pow", "Solutions: %d\n", solns.size()); - // Write the solution to the hash and compute the result. - for (auto soln : solns) { + std::function)> validBlock = + [&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams] + (std::vector soln) { + // Write the solution to the hash and compute the result. + LogPrint("pow", "- Checking solution against target\n"); pblock->nSolution = soln; if (UintToArith256(pblock->GetHash()) > hashTarget) { - continue; + return false; } // Found a solution @@ -566,7 +556,20 @@ void static BitcoinMiner(CWallet *pwallet) if (chainparams.MineBlocksOnDemand()) throw boost::thread_interrupted(); - break; + return true; + }; + std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { + std::lock_guard lock{m_cs}; + return cancelSolver; + }; + 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 lock{m_cs}; + cancelSolver = false; } // Check for stop or if block needs to be rebuilt diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 95b32df96..98e312688 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -188,19 +188,13 @@ Value generate(const Array& params, bool fHelp) pblock->nNonce.size()); // (x_1, x_2, ...) = A(I, V, n, k) - std::set> solns; - EhBasicSolveUncancellable(n, k, curr_state, solns); - - for (auto soln : solns) { - bool isValid; - EhIsValidSolution(n, k, curr_state, soln, isValid); - assert(isValid); + std::function)> validBlock = + [&pblock](std::vector soln) { pblock->nSolution = soln; - - if (CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { - goto endloop; - } - } + return CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()); + }; + if (EhBasicSolveUncancellable(n, k, curr_state, validBlock)); + goto endloop; } endloop: CValidationState state; diff --git a/src/test/equihash_tests.cpp b/src/test/equihash_tests.cpp index cf4d98c31..0f0d4f071 100644 --- a/src/test/equihash_tests.cpp +++ b/src/test/equihash_tests.cpp @@ -50,7 +50,12 @@ void TestEquihashSolvers(unsigned int n, unsigned int k, const std::string &I, c // First test the basic solver std::set> ret; - EhBasicSolveUncancellable(n, k, state, ret); + std::function)> validBlock = + [&ret](std::vector soln) { + ret.insert(soln); + return false; + }; + EhBasicSolveUncancellable(n, k, state, validBlock); BOOST_TEST_MESSAGE("[Basic] Number of solutions: " << ret.size()); std::stringstream strm; PrintSolutions(strm, ret); @@ -59,7 +64,12 @@ 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; - EhOptimisedSolveUncancellable(n, k, state, retOpt); + std::function)> validBlockOpt = + [&retOpt](std::vector soln) { + retOpt.insert(soln); + return false; + }; + EhOptimisedSolveUncancellable(n, k, state, validBlockOpt); BOOST_TEST_MESSAGE("[Optimised] Number of solutions: " << retOpt.size()); strm.str(""); PrintSolutions(strm, retOpt); diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 5e90c55b5..98bf7e043 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -118,7 +118,8 @@ double benchmark_solve_equihash() timer_start(); std::set> solns; - EhOptimisedSolveUncancellable(n, k, eh_state, solns); + EhOptimisedSolveUncancellable(n, k, eh_state, + [](std::vector soln) { return false; }); return timer_stop(); } From 4a0bdc90200e63926029357e304cd0fa7716d61e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 30 Jul 2016 01:28:17 +1200 Subject: [PATCH 2/2] Fix bug in 'generate' RPC method that caused it to fail with high probability --- src/rpcmining.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 98e312688..6b979aa34 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -193,7 +193,7 @@ Value generate(const Array& params, bool fHelp) pblock->nSolution = soln; return CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()); }; - if (EhBasicSolveUncancellable(n, k, curr_state, validBlock)); + if (EhBasicSolveUncancellable(n, k, curr_state, validBlock)) goto endloop; } endloop: