Browse Source

Refactor StepRow to make optimisation easier

pull/145/head
Jack Grigg 8 years ago
parent
commit
a3361e778b
  1. 33
      src/crypto/equihash.cpp
  2. 41
      src/crypto/equihash.h
  3. 65
      src/test/equihash_tests.cpp

33
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<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");

41
src/crypto/equihash.h

@ -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
{

65
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<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}
});
}

Loading…
Cancel
Save