Refactor StepRow to make optimisation easier
This commit is contained in:
@@ -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<eh_index> aSrt(a.indices);
|
||||
std::vector<eh_index> bSrt(b.indices);
|
||||
@@ -165,7 +168,7 @@ std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_st
|
||||
|
||||
// 1) Generate first list
|
||||
LogPrint("pow", "Generating first list\n");
|
||||
std::vector<StepRow> X;
|
||||
std::vector<FullStepRow> 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<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_st
|
||||
LogPrint("pow", "- Finding collisions\n");
|
||||
int i = 0;
|
||||
int posFree = 0;
|
||||
std::vector<StepRow> Xc;
|
||||
std::vector<FullStepRow> 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<std::vector<eh_index>> 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<eh_in
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<StepRow> X;
|
||||
std::vector<FullStepRow> X;
|
||||
X.reserve(soln_size);
|
||||
for (eh_index i : soln) {
|
||||
X.emplace_back(n, base_state, i);
|
||||
}
|
||||
|
||||
while (X.size() > 1) {
|
||||
std::vector<StepRow> Xc;
|
||||
std::vector<FullStepRow> 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");
|
||||
|
||||
@@ -22,38 +22,53 @@ struct invalid_params { };
|
||||
|
||||
class StepRow
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
unsigned char* hash;
|
||||
unsigned int len;
|
||||
std::vector<eh_index> 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<eh_index> GetSolution() { return std::vector<eh_index>(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<eh_index> 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<eh_index> GetSolution() { return std::vector<eh_index>(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
|
||||
{
|
||||
|
||||
@@ -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<std::vector<uint32_t>> &solns) {
|
||||
void PrintSolution(std::stringstream &strm, std::vector<uint32_t> soln) {
|
||||
strm << " {";
|
||||
const char* separator = "";
|
||||
for (uint32_t index : soln) {
|
||||
strm << separator << index;
|
||||
separator = ", ";
|
||||
}
|
||||
strm << "}";
|
||||
}
|
||||
|
||||
void PrintSolutions(std::stringstream &strm, std::set<std::vector<uint32_t>> solns) {
|
||||
strm << "{";
|
||||
const char* soln_separator = "";
|
||||
for (std::vector<uint32_t> 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<std::vector<uint32_t>> &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<std::vector<uint32_t>> 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<uint32_t> 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}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user