From a3361e778b36aeddb2b0a291da843f5b16007b1c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sat, 16 Apr 2016 01:14:37 +1200 Subject: [PATCH] Refactor StepRow to make optimisation easier --- src/crypto/equihash.cpp | 33 ++++++++++--------- src/crypto/equihash.h | 41 +++++++++++++++-------- src/test/equihash_tests.cpp | 65 ++++++++++++++++++++----------------- 3 files changed, 81 insertions(+), 58 deletions(-) diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index 1383c73eb..f8bc5ddde 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -50,15 +50,12 @@ int Equihash::InitialiseState(eh_HashState& base_state) StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : hash {new unsigned char[n/8]}, - len {n/8}, - indices {i} + len {n/8} { eh_HashState state; state = base_state; crypto_generichash_blake2b_update(&state, (unsigned char*) &i, sizeof(eh_index)); crypto_generichash_blake2b_final(&state, hash, n/8); - - assert(indices.size() == 1); } StepRow::~StepRow() @@ -68,14 +65,20 @@ StepRow::~StepRow() StepRow::StepRow(const StepRow& a) : hash {new unsigned char[a.len]}, - len {a.len}, - indices(a.indices) + len {a.len} { for (int i = 0; i < len; i++) hash[i] = a.hash[i]; } -StepRow& StepRow::operator=(const StepRow& a) +FullStepRow::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : + StepRow {n, base_state, i}, + indices {i} +{ + assert(indices.size() == 1); +} + +FullStepRow& FullStepRow::operator=(const FullStepRow& a) { unsigned char* p = new unsigned char[a.len]; for (int i = 0; i < a.len; i++) @@ -87,7 +90,7 @@ StepRow& StepRow::operator=(const StepRow& a) return *this; } -StepRow& StepRow::operator^=(const StepRow& a) +FullStepRow& FullStepRow::operator^=(const FullStepRow& a) { if (a.len != len) { throw std::invalid_argument("Hash length differs"); @@ -105,7 +108,7 @@ StepRow& StepRow::operator^=(const StepRow& a) return *this; } -void StepRow::TrimHash(int l) +void FullStepRow::TrimHash(int l) { unsigned char* p = new unsigned char[len-l]; for (int i = 0; i < len-l; i++) @@ -132,7 +135,7 @@ bool HasCollision(StepRow& a, StepRow& b, int l) } // Checks if the intersection of a.indices and b.indices is empty -bool DistinctIndices(const StepRow& a, const StepRow& b) +bool DistinctIndices(const FullStepRow& a, const FullStepRow& b) { std::vector aSrt(a.indices); std::vector bSrt(b.indices); @@ -165,7 +168,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& base_st // 1) Generate first list LogPrint("pow", "Generating first list\n"); - std::vector X; + std::vector X; X.reserve(init_size); for (eh_index i = 0; i < init_size; i++) { X.emplace_back(n, base_state, i); @@ -181,7 +184,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& base_st LogPrint("pow", "- Finding collisions\n"); int i = 0; int posFree = 0; - std::vector Xc; + std::vector 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; @@ -233,7 +236,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& base_st std::sort(X.begin(), X.end()); LogPrint("pow", "- Finding collisions\n"); for (int i = 0; i < X.size() - 1; i++) { - StepRow res = X[i] ^ X[i+1]; + FullStepRow res = X[i] ^ X[i+1]; if (res.IsZero() && DistinctIndices(X[i], X[i+1])) { solns.insert(res.GetSolution()); } @@ -252,14 +255,14 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector X; + std::vector X; X.reserve(soln_size); for (eh_index i : soln) { X.emplace_back(n, base_state, i); } while (X.size() > 1) { - std::vector Xc; + std::vector Xc; for (int 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"); diff --git a/src/crypto/equihash.h b/src/crypto/equihash.h index 7aca34208..2603024ae 100644 --- a/src/crypto/equihash.h +++ b/src/crypto/equihash.h @@ -22,38 +22,53 @@ struct invalid_params { }; class StepRow { -private: +protected: unsigned char* hash; unsigned int len; - std::vector indices; public: StepRow(unsigned int n, const eh_HashState& base_state, eh_index i); ~StepRow(); StepRow(const StepRow& a); - StepRow& operator=(const StepRow& a); - StepRow& operator^=(const StepRow& a); - void TrimHash(int l); bool IsZero(); - bool IndicesBefore(const StepRow& a) { return indices[0] < a.indices[0]; } - std::vector GetSolution() { return std::vector(indices); } std::string GetHex() { return HexStr(hash, hash+len); } - friend inline const StepRow operator^(const StepRow& a, const StepRow& b) { - if (a.indices[0] < b.indices[0]) { return StepRow(a) ^= b; } - else { return StepRow(b) ^= a; } - } friend inline bool operator==(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, a.len) == 0; } friend inline bool operator<(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, a.len) < 0; } friend bool HasCollision(StepRow& a, StepRow& b, int l); - friend bool DistinctIndices(const StepRow& a, const StepRow& b); }; bool HasCollision(StepRow& a, StepRow& b, int l); -bool DistinctIndices(const StepRow& a, const StepRow& b); + +class FullStepRow : public StepRow +{ +private: + std::vector indices; + +public: + FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i); + ~FullStepRow() { } + + FullStepRow(const FullStepRow& a) : StepRow {a}, indices(a.indices) { } + FullStepRow& operator=(const FullStepRow& a); + FullStepRow& operator^=(const FullStepRow& a); + + void TrimHash(int l); + bool IndicesBefore(const FullStepRow& a) { return indices[0] < a.indices[0]; } + std::vector GetSolution() { return std::vector(indices); } + + friend inline const FullStepRow operator^(const FullStepRow& a, const FullStepRow& b) { + if (a.indices[0] < b.indices[0]) { return FullStepRow(a) ^= b; } + else { return FullStepRow(b) ^= a; } + } + + friend bool DistinctIndices(const FullStepRow& a, const FullStepRow& b); +}; + +bool DistinctIndices(const FullStepRow& a, const FullStepRow& b); class Equihash { diff --git a/src/test/equihash_tests.cpp b/src/test/equihash_tests.cpp index 3d57258db..2066af919 100644 --- a/src/test/equihash_tests.cpp +++ b/src/test/equihash_tests.cpp @@ -19,7 +19,28 @@ BOOST_FIXTURE_TEST_SUITE(equihash_tests, BasicTestingSetup) -void TestEquihashBasicSolver(unsigned int n, unsigned int k, const std::string &I, const arith_uint256 &nonce, const std::set> &solns) { +void PrintSolution(std::stringstream &strm, std::vector soln) { + strm << " {"; + const char* separator = ""; + for (uint32_t index : soln) { + strm << separator << index; + separator = ", "; + } + strm << "}"; +} + +void PrintSolutions(std::stringstream &strm, std::set> solns) { + strm << "{"; + const char* soln_separator = ""; + for (std::vector soln : solns) { + strm << soln_separator << "\n"; + soln_separator = ","; + PrintSolution(strm, soln); + } + strm << "\n}"; +} + +void TestEquihashSolvers(unsigned int n, unsigned int k, const std::string &I, const arith_uint256 &nonce, const std::set> &solns) { Equihash eh {n, k}; crypto_generichash_blake2b_state state; eh.InitialiseState(state); @@ -27,22 +48,12 @@ void TestEquihashBasicSolver(unsigned int n, unsigned int k, const std::string & BOOST_TEST_MESSAGE("Running solver: n = " << n << ", k = " << k << ", I = " << I << ", V = " << V.GetHex()); crypto_generichash_blake2b_update(&state, (unsigned char*)&I[0], I.size()); crypto_generichash_blake2b_update(&state, V.begin(), V.size()); + + // First test the basic solver std::set> ret = eh.BasicSolve(state); - BOOST_TEST_MESSAGE("Number of solutions: " << ret.size()); + BOOST_TEST_MESSAGE("[Basic] Number of solutions: " << ret.size()); std::stringstream strm; - strm << "{"; - const char* soln_separator = ""; - for (std::vector soln : ret) { - strm << soln_separator << "\n {"; - soln_separator = ","; - const char* separator = ""; - for (uint32_t index : soln) { - strm << separator << index; - separator = ", "; - } - strm << "}"; - } - strm << "\n}"; + PrintSolutions(strm, ret); BOOST_TEST_MESSAGE(strm.str()); BOOST_CHECK(ret == solns); } @@ -56,44 +67,38 @@ void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I, crypto_generichash_blake2b_update(&state, V.begin(), V.size()); BOOST_TEST_MESSAGE("Running validator: n = " << n << ", k = " << k << ", I = " << I << ", V = " << V.GetHex() << ", expected = " << expected << ", soln ="); std::stringstream strm; - strm << " {"; - const char* separator = ""; - for (uint32_t index : soln) { - strm << separator << index; - separator = ", "; - } - strm << "}"; + PrintSolution(strm, soln); BOOST_TEST_MESSAGE(strm.str()); BOOST_CHECK(eh.IsValidSolution(state, soln) == expected); } BOOST_AUTO_TEST_CASE(solver_testvectors) { - TestEquihashBasicSolver(96, 5, "block header", 0, { + TestEquihashSolvers(96, 5, "block header", 0, { {182, 100500, 71010, 81262, 11318, 81082, 84339, 106327, 25622, 123074, 50681, 128728, 27919, 122921, 33794, 39634, 3948, 33776, 39058, 39177, 35372, 67678, 81195, 120032, 5452, 128944, 110158, 118138, 37893, 65666, 49222, 126229} }); - TestEquihashBasicSolver(96, 5, "block header", 1, { + TestEquihashSolvers(96, 5, "block header", 1, { {1510, 43307, 63800, 74710, 37892, 71424, 63310, 110898, 2260, 70172, 12353, 35063, 13433, 71777, 35871, 80964, 14030, 50499, 35055, 77037, 41990, 79370, 72784, 99843, 16721, 125719, 127888, 131048, 85492, 126861, 89702, 129167}, {1623, 18648, 8014, 121335, 5288, 33890, 35968, 74704, 2909, 53346, 41954, 48211, 68872, 110549, 110905, 113986, 20660, 119394, 30054, 37492, 23025, 110409, 55861, 65351, 45769, 128708, 82357, 124990, 76854, 130060, 99713, 119536} }); - TestEquihashBasicSolver(96, 5, "block header", 2, { + TestEquihashSolvers(96, 5, "block header", 2, { {17611, 81207, 44397, 50188, 43411, 119224, 90094, 99790, 21704, 122576, 34295, 98391, 22200, 82614, 108526, 114425, 20019, 69354, 28160, 34999, 31902, 103318, 49332, 65015, 60702, 107535, 76891, 81801, 69559, 83079, 125721, 129893} }); - TestEquihashBasicSolver(96, 5, "block header", 11, { + TestEquihashSolvers(96, 5, "block header", 11, { }); - TestEquihashBasicSolver(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 0, { + TestEquihashSolvers(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 0, { {2140, 64888, 7062, 37067, 11292, 27641, 53514, 70723, 6685, 73669, 18151, 88834, 55608, 76507, 84243, 125869, 5425, 22827, 37743, 119459, 37587, 118338, 39127, 40622, 16812, 26417, 112391, 120791, 22472, 74552, 43030, 129191}, {2742, 14130, 3738, 38739, 60817, 92878, 102087, 102882, 7493, 114098, 11019, 96605, 53351, 65844, 92194, 111605, 12488, 21213, 93833, 103682, 74551, 80813, 93325, 109313, 24782, 124251, 39372, 50621, 35398, 90386, 66867, 79277} }); - TestEquihashBasicSolver(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 1, { + TestEquihashSolvers(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 1, { }); - TestEquihashBasicSolver(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 2, { + TestEquihashSolvers(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 2, { {2219, 49740, 102167, 108576, 15546, 73320, 29506, 94663, 13900, 74954, 16748, 35617, 42643, 58400, 60768, 63883, 4677, 111178, 35802, 120953, 21542, 89457, 97759, 128494, 24444, 99755, 97152, 108239, 39816, 92800, 85532, 88575}, {2258, 41741, 8329, 74706, 8166, 80151, 31480, 86606, 5417, 79683, 97197, 100351, 18608, 61819, 65689, 79940, 13038, 28092, 21997, 62813, 22268, 119557, 58111, 63811, 45789, 72308, 50865, 81180, 91695, 127084, 93402, 95676}, {3279, 96607, 78609, 102949, 32765, 54059, 79472, 96147, 25943, 36652, 47276, 71714, 26590, 29892, 44598, 58988, 12323, 42327, 60194, 87786, 60951, 103949, 71481, 81826, 13535, 88167, 17392, 74652, 21924, 64941, 54660, 72151}, {8970, 81710, 78816, 97295, 22433, 83703, 59463, 101258, 9014, 75982, 102935, 111574, 27277, 30040, 54221, 107719, 18593, 89276, 94385, 119768, 34013, 63600, 46240, 87288, 46573, 80865, 47845, 67566, 92645, 121901, 102751, 104818} }); - TestEquihashBasicSolver(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 11, { + TestEquihashSolvers(96, 5, "Equihash is an asymmetric PoW based on the Generalised Birthday problem.", 11, { {3298, 28759, 56287, 109050, 13166, 122018, 75757, 109249, 7616, 83872, 103256, 119576, 43182, 121748, 81417, 120122, 23405, 129542, 68426, 117326, 56427, 118027, 73904, 77697, 41334, 118772, 89089, 130655, 107174, 128610, 107577, 118332} }); }