diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index 1eb3dccf7..4694d2240 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -35,7 +35,7 @@ int Equihash::InitialiseState(eh_HashState& base_state) personalization); } -void EhIndexToArray(eh_index i, unsigned char* array) +void EhIndexToArray(const eh_index i, unsigned char* array) { assert(sizeof(eh_index) == 4); array[0] = (i >> 24) & 0xFF; @@ -44,7 +44,7 @@ void EhIndexToArray(eh_index i, unsigned char* array) array[3] = i & 0xFF; } -eh_index ArrayToEhIndex(unsigned char* array) +eh_index ArrayToEhIndex(const unsigned char* array) { assert(sizeof(eh_index) == 4); eh_index ret {array[0]}; @@ -57,22 +57,21 @@ eh_index ArrayToEhIndex(unsigned char* array) return ret; } -eh_trunc TruncateIndex(eh_index i, unsigned int ilen) +eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen) { // Truncate to 8 bits assert(sizeof(eh_trunc) == 1); return (i >> (ilen - 8)) & 0xff; } -eh_index UntruncateIndex(eh_trunc t, eh_index r, unsigned int ilen) +eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int ilen) { eh_index i{t}; return (i << (ilen - 8)) | r; } -StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : - hash {new unsigned char[n/8]}, - len {n/8} +template +StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) { eh_HashState state; state = base_state; @@ -80,76 +79,46 @@ StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : crypto_generichash_blake2b_final(&state, hash, n/8); } -StepRow::~StepRow() +template template +StepRow::StepRow(const StepRow& a) { - delete[] hash; + assert(W <= WIDTH); + std::copy(a.hash, a.hash+W, hash); } -StepRow::StepRow(const StepRow& a) : - hash {new unsigned char[a.len]}, - len {a.len} +template +FullStepRow::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : + StepRow {n, base_state, i} { - std::copy(a.hash, a.hash+a.len, hash); + EhIndexToArray(i, hash+(n/8)); } -FullStepRow::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : - StepRow {n, base_state, i}, - lenIndices {sizeof(eh_index)} +template template +FullStepRow::FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim) : + StepRow {a} { - unsigned char* p = new unsigned char[len+lenIndices]; - std::copy(hash, hash+len, p); - EhIndexToArray(i, p+len); - delete[] hash; - hash = p; -} - -FullStepRow::FullStepRow(const FullStepRow& a) : - StepRow {a}, - lenIndices {a.lenIndices} -{ - unsigned char* p = new unsigned char[a.len+a.lenIndices]; - std::copy(a.hash, a.hash+a.len+a.lenIndices, p); - delete[] hash; - hash = p; -} - -FullStepRow::FullStepRow(const FullStepRow& a, const FullStepRow& b, int trim) : - StepRow {a}, - lenIndices {a.lenIndices+b.lenIndices} -{ - if (a.len != b.len) { - throw std::invalid_argument("Hash length differs"); - } - if (a.lenIndices != b.lenIndices) { - throw std::invalid_argument("Number of indices differs"); - } - unsigned char* p = new unsigned char[a.len-trim+a.lenIndices+b.lenIndices]; - for (int i = trim; i < a.len; i++) - p[i-trim] = a.hash[i] ^ b.hash[i]; - len = a.len-trim; - if (a.IndicesBefore(b)) { - std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len); - std::copy(b.hash+b.len, b.hash+b.len+b.lenIndices, p+len+a.lenIndices); + assert(len+lenIndices <= W); + assert(len-trim+(2*lenIndices) <= WIDTH); + for (int i = trim; i < len; i++) + hash[i-trim] = a.hash[i] ^ b.hash[i]; + if (a.IndicesBefore(b, len)) { + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); } else { - std::copy(b.hash+b.len, b.hash+b.len+b.lenIndices, p+len); - std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len+b.lenIndices); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); } - delete[] hash; - hash = p; } -FullStepRow& FullStepRow::operator=(const FullStepRow& a) +template +FullStepRow& FullStepRow::operator=(const FullStepRow& a) { - unsigned char* p = new unsigned char[a.len+a.lenIndices]; - std::copy(a.hash, a.hash+a.len+a.lenIndices, p); - delete[] hash; - hash = p; - len = a.len; - lenIndices = a.lenIndices; + std::copy(a.hash, a.hash+WIDTH, hash); return *this; } -bool StepRow::IsZero() +template +bool StepRow::IsZero(size_t len) { char res = 0; for (int i = 0; i < len; i++) @@ -157,7 +126,8 @@ bool StepRow::IsZero() return res == 0; } -std::vector FullStepRow::GetIndices() const +template +std::vector FullStepRow::GetIndices(size_t len, size_t lenIndices) const { std::vector ret; for (int i = 0; i < lenIndices; i += sizeof(eh_index)) { @@ -166,7 +136,8 @@ std::vector FullStepRow::GetIndices() const return ret; } -bool HasCollision(StepRow& a, StepRow& b, int l) +template +bool HasCollision(StepRow& a, StepRow& b, int l) { bool res = true; for (int j = 0; j < l; j++) @@ -174,92 +145,40 @@ bool HasCollision(StepRow& a, StepRow& b, int l) return res; } -// Checks if the intersection of a.indices and b.indices is empty -bool DistinctIndices(const FullStepRow& a, const FullStepRow& b) +template +TruncatedStepRow::TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen) : + StepRow {n, base_state, i} { - std::vector aSrt = a.GetIndices(); - std::vector bSrt = b.GetIndices(); - - std::sort(aSrt.begin(), aSrt.end()); - std::sort(bSrt.begin(), bSrt.end()); - - unsigned int i = 0; - for (unsigned int j = 0; j < bSrt.size(); j++) { - while (aSrt[i] < bSrt[j]) { - i++; - if (i == aSrt.size()) { return true; } - } - assert(aSrt[i] >= bSrt[j]); - if (aSrt[i] == bSrt[j]) { return false; } - } - return true; + hash[n/8] = TruncateIndex(i, ilen); } -bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t) +template template +TruncatedStepRow::TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim) : + StepRow {a} { - return TruncateIndex(ArrayToEhIndex(a.hash+a.len), ilen) == t; -} - -TruncatedStepRow::TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen) : - StepRow {n, base_state, i}, - lenIndices {1} -{ - unsigned char* p = new unsigned char[len+lenIndices]; - std::copy(hash, hash+len, p); - p[len] = TruncateIndex(i, ilen); - delete[] hash; - hash = p; -} - -TruncatedStepRow::TruncatedStepRow(const TruncatedStepRow& a) : - StepRow {a}, - lenIndices {a.lenIndices} -{ - unsigned char* p = new unsigned char[a.len+a.lenIndices]; - std::copy(a.hash, a.hash+a.len+a.lenIndices, p); - delete[] hash; - hash = p; -} - -TruncatedStepRow::TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, int trim) : - StepRow {a}, - lenIndices {a.lenIndices+b.lenIndices} -{ - if (a.len != b.len) { - throw std::invalid_argument("Hash length differs"); - } - if (a.lenIndices != b.lenIndices) { - throw std::invalid_argument("Number of indices differs"); - } - unsigned char* p = new unsigned char[a.len-trim+a.lenIndices+b.lenIndices]; - for (int i = trim; i < a.len; i++) - p[i-trim] = a.hash[i] ^ b.hash[i]; - len = a.len-trim; - if (a.IndicesBefore(b)) { - std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len); - std::copy(b.hash+b.len, b.hash+b.len+b.lenIndices, p+len+a.lenIndices); + assert(len+lenIndices <= W); + assert(len-trim+(2*lenIndices) <= WIDTH); + for (int i = trim; i < len; i++) + hash[i-trim] = a.hash[i] ^ b.hash[i]; + if (a.IndicesBefore(b, len, lenIndices)) { + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); } else { - std::copy(b.hash+b.len, b.hash+b.len+b.lenIndices, p+len); - std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len+b.lenIndices); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); } - delete[] hash; - hash = p; } -TruncatedStepRow& TruncatedStepRow::operator=(const TruncatedStepRow& a) +template +TruncatedStepRow& TruncatedStepRow::operator=(const TruncatedStepRow& a) { - unsigned char* p = new unsigned char[a.len+a.lenIndices]; - std::copy(a.hash, a.hash+a.len+a.lenIndices, p); - delete[] hash; - hash = p; - len = a.len; - lenIndices = a.lenIndices; + std::copy(a.hash, a.hash+WIDTH, hash); return *this; } -eh_trunc* TruncatedStepRow::GetPartialSolution(eh_index soln_size) const +template +eh_trunc* TruncatedStepRow::GetTruncatedIndices(size_t len, size_t lenIndices) const { - assert(lenIndices == soln_size); eh_trunc* p = new eh_trunc[lenIndices]; std::copy(hash+len, hash+len+lenIndices, p); return p; @@ -273,7 +192,8 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba // 1) Generate first list LogPrint("pow", "Generating first list\n"); size_t hashLen = N/8; - std::vector X; + size_t lenIndices = sizeof(eh_index); + std::vector> X; X.reserve(init_size); for (eh_index i = 0; i < init_size; i++) { X.emplace_back(N, base_state, i); @@ -289,7 +209,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba 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; @@ -301,8 +221,8 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba // 2c) Calculate tuples (X_i ^ X_j, (i, j)) for (int l = 0; l < j - 1; l++) { for (int m = l + 1; m < j; m++) { - if (DistinctIndices(X[i+l], X[i+m])) { - Xc.emplace_back(X[i+l], X[i+m], CollisionByteLength); + if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) { + Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength); } } } @@ -332,6 +252,7 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba } hashLen -= CollisionByteLength; + lenIndices *= 2; } // k+1) Find a collision on last 2n(k+1) bits @@ -342,9 +263,9 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba std::sort(X.begin(), X.end(), CompareSR(hashLen)); LogPrint("pow", "- Finding collisions\n"); for (int i = 0; i < X.size() - 1; i++) { - FullStepRow res(X[i], X[i+1], 0); - if (res.IsZero() && DistinctIndices(X[i], X[i+1])) { - solns.insert(res.GetIndices()); + FullStepRow res(X[i], X[i+1], hashLen, lenIndices, 0); + if (res.IsZero(hashLen) && DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) { + solns.insert(res.GetIndices(hashLen, 2*lenIndices)); } } } else @@ -353,11 +274,12 @@ std::set> Equihash::BasicSolve(const eh_HashState& ba return solns; } -void CollideBranches(std::vector& X, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt) +template +void CollideBranches(std::vector>& X, const size_t hlen, const size_t lenIndices, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt) { 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; @@ -369,11 +291,11 @@ void CollideBranches(std::vector& X, const unsigned int clen, const // 2c) Calculate tuples (X_i ^ X_j, (i, j)) for (int l = 0; l < j - 1; l++) { for (int m = l + 1; m < j; m++) { - if (DistinctIndices(X[i+l], X[i+m])) { - if (IsValidBranch(X[i+l], ilen, lt) && IsValidBranch(X[i+m], ilen, rt)) { - Xc.emplace_back(X[i+l], X[i+m], clen); - } else if (IsValidBranch(X[i+m], ilen, lt) && IsValidBranch(X[i+l], ilen, rt)) { - Xc.emplace_back(X[i+m], X[i+l], clen); + if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) { + if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) { + Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen); + } else if (IsValidBranch(X[i+m], hlen, ilen, lt) && IsValidBranch(X[i+l], hlen, ilen, rt)) { + Xc.emplace_back(X[i+m], X[i+l], hlen, lenIndices, clen); } } } @@ -418,7 +340,8 @@ std::set> Equihash::OptimisedSolve(const eh_HashState // 1) Generate first list LogPrint("pow", "Generating first list\n"); size_t hashLen = N/8; - std::vector Xt; + size_t lenIndices = sizeof(eh_trunc); + std::vector> Xt; Xt.reserve(init_size); for (eh_index i = 0; i < init_size; i++) { Xt.emplace_back(N, base_state, i, CollisionBitLength + 1); @@ -434,7 +357,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState LogPrint("pow", "- Finding collisions\n"); int i = 0; int posFree = 0; - std::vector Xc; + std::vector> Xc; while (i < Xt.size() - 1) { // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits int j = 1; @@ -447,7 +370,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState for (int l = 0; l < j - 1; l++) { for (int m = l + 1; m < j; m++) { // We truncated, so don't check for distinct indices here - Xc.emplace_back(Xt[i+l], Xt[i+m], CollisionByteLength); + Xc.emplace_back(Xt[i+l], Xt[i+m], hashLen, lenIndices, CollisionByteLength); } } @@ -476,6 +399,7 @@ std::set> Equihash::OptimisedSolve(const eh_HashState } hashLen -= CollisionByteLength; + lenIndices *= 2; } // k+1) Find a collision on last 2n(k+1) bits @@ -485,9 +409,9 @@ std::set> Equihash::OptimisedSolve(const eh_HashState std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen)); LogPrint("pow", "- Finding collisions\n"); for (int i = 0; i < Xt.size() - 1; i++) { - TruncatedStepRow res(Xt[i], Xt[i+1], 0); - if (res.IsZero()) { - partialSolns.push_back(res.GetPartialSolution(soln_size)); + TruncatedStepRow res(Xt[i], Xt[i+1], hashLen, lenIndices, 0); + if (res.IsZero(hashLen)) { + partialSolns.push_back(res.GetTruncatedIndices(hashLen, 2*lenIndices)); } } } else @@ -505,10 +429,11 @@ std::set> Equihash::OptimisedSolve(const eh_HashState for (eh_trunc* partialSoln : partialSolns) { // 1) Generate first list of possibilities size_t hashLen = N/8; - std::vector> X; + size_t lenIndices = sizeof(eh_index); + std::vector>> X; X.reserve(soln_size); for (eh_index i = 0; i < soln_size; i++) { - std::vector ic; + std::vector> ic; ic.reserve(recreate_size); for (eh_index j = 0; j < recreate_size; j++) { eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) }; @@ -519,17 +444,17 @@ std::set> Equihash::OptimisedSolve(const eh_HashState // 3) Repeat step 2 for each level of the tree for (int r = 0; X.size() > 1; r++) { - std::vector> Xc; + std::vector>> Xc; Xc.reserve(X.size()/2); // 2a) For each pair of lists: for (int v = 0; v < X.size(); v += 2) { // 2b) Merge the lists - std::vector ic(X[v]); + std::vector> ic(X[v]); ic.reserve(X[v].size() + X[v+1].size()); ic.insert(ic.end(), X[v+1].begin(), X[v+1].end()); std::sort(ic.begin(), ic.end(), CompareSR(hashLen)); - CollideBranches(ic, CollisionByteLength, CollisionBitLength + 1, partialSoln[(1<> Equihash::OptimisedSolve(const eh_HashState X = Xc; hashLen -= CollisionByteLength; + lenIndices *= 2; } // We are at the top of the tree assert(X.size() == 1); - for (FullStepRow row : X[0]) { - solns.insert(row.GetIndices()); + for (FullStepRow row : X[0]) { + solns.insert(row.GetIndices(hashLen, lenIndices)); } goto deletesolution; @@ -569,36 +495,40 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector< return false; } - std::vector X; + std::vector> X; X.reserve(soln_size); for (eh_index i : soln) { X.emplace_back(N, base_state, i); } + size_t hashLen = N/8; + size_t lenIndices = sizeof(eh_index); 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"); - LogPrint("pow", "X[i] = %s\n", X[i].GetHex()); - LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex()); + LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen)); + LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen)); return false; } - if (X[i+1].IndicesBefore(X[i])) { + if (X[i+1].IndicesBefore(X[i], hashLen)) { return false; LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n"); } - if (!DistinctIndices(X[i], X[i+1])) { + if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) { LogPrint("pow", "Invalid solution: duplicate indices\n"); return false; } - Xc.emplace_back(X[i], X[i+1], CollisionByteLength); + Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength); } X = Xc; + hashLen -= CollisionByteLength; + lenIndices *= 2; } assert(X.size() == 1); - return X[0].IsZero(); + return X[0].IsZero(hashLen); } // Explicit instantiations for Equihash<96,5> diff --git a/src/crypto/equihash.h b/src/crypto/equihash.h index a1c390d1c..20952cb6a 100644 --- a/src/crypto/equihash.h +++ b/src/crypto/equihash.h @@ -21,26 +21,31 @@ typedef crypto_generichash_blake2b_state eh_HashState; typedef uint32_t eh_index; typedef uint8_t eh_trunc; -eh_index ArrayToEhIndex(unsigned char* array); +eh_index ArrayToEhIndex(const unsigned char* array); +eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen); +template class StepRow { + template + friend class StepRow; friend class CompareSR; protected: - unsigned char* hash; - unsigned int len; + unsigned char hash[WIDTH]; public: StepRow(unsigned int n, const eh_HashState& base_state, eh_index i); - ~StepRow(); + ~StepRow() { } - StepRow(const StepRow& a); + template + StepRow(const StepRow& a); - bool IsZero(); - std::string GetHex() { return HexStr(hash, hash+len); } + bool IsZero(size_t len); + std::string GetHex(size_t len) { return HexStr(hash, hash+len); } - friend bool HasCollision(StepRow& a, StepRow& b, int l); + template + friend bool HasCollision(StepRow& a, StepRow& b, int l); }; class CompareSR @@ -51,48 +56,56 @@ private: public: CompareSR(size_t l) : len {l} { } - inline bool operator()(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, len) < 0; } + template + inline bool operator()(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, len) < 0; } }; -bool HasCollision(StepRow& a, StepRow& b, int l); +template +bool HasCollision(StepRow& a, StepRow& b, int l); -class FullStepRow : public StepRow +template +class FullStepRow : public StepRow { -private: - unsigned int lenIndices; + template + friend class FullStepRow; + + using StepRow::hash; public: FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i); ~FullStepRow() { } - FullStepRow(const FullStepRow& a); - FullStepRow(const FullStepRow& a, const FullStepRow& b, int trim); - FullStepRow& operator=(const FullStepRow& a); + FullStepRow(const FullStepRow& a) : StepRow {a} { } + template + FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim); + FullStepRow& operator=(const FullStepRow& a); - inline bool IndicesBefore(const FullStepRow& a) const { return ArrayToEhIndex(hash+len) < ArrayToEhIndex(a.hash+a.len); } - std::vector GetIndices() const; + inline bool IndicesBefore(const FullStepRow& a, size_t len) const { return ArrayToEhIndex(hash+len) < ArrayToEhIndex(a.hash+len); } + std::vector GetIndices(size_t len, size_t lenIndices) const; - friend bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t); + template + friend bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t); }; -bool DistinctIndices(const FullStepRow& a, const FullStepRow& b); -bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t); - -class TruncatedStepRow : public StepRow +template +class TruncatedStepRow : public StepRow { -private: - unsigned int lenIndices; + template + friend class TruncatedStepRow; + + using StepRow::hash; public: TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen); ~TruncatedStepRow() { } - TruncatedStepRow(const TruncatedStepRow& a); - TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, int trim); - TruncatedStepRow& operator=(const TruncatedStepRow& a); + TruncatedStepRow(const TruncatedStepRow& a) : StepRow {a} { } + template + TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim); + TruncatedStepRow& operator=(const TruncatedStepRow& a); - inline bool IndicesBefore(const TruncatedStepRow& a) const { return memcmp(hash+len, a.hash+a.len, lenIndices) < 0; } - eh_trunc* GetPartialSolution(eh_index soln_size) const; + inline bool IndicesBefore(const TruncatedStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } + eh_trunc* GetTruncatedIndices(size_t len, size_t lenIndices) const; }; template @@ -107,6 +120,10 @@ private: public: enum { CollisionBitLength=N/(K+1) }; enum { CollisionByteLength=CollisionBitLength/8 }; + enum : size_t { FullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K-1)) }; + enum : size_t { FinalFullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K)) }; + enum : size_t { TruncatedWidth=2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K-1)) }; + enum : size_t { FinalTruncatedWidth=2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K)) }; Equihash() { } @@ -116,6 +133,8 @@ public: bool IsValidSolution(const eh_HashState& base_state, std::vector soln); }; +#include "equihash.tcc" + static Equihash<96,5> Eh965; static Equihash<48,5> Eh485; diff --git a/src/crypto/equihash.tcc b/src/crypto/equihash.tcc new file mode 100644 index 000000000..e3ad9858c --- /dev/null +++ b/src/crypto/equihash.tcc @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +// Checks if the intersection of a.indices and b.indices is empty +template +bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices) +{ + std::vector aSrt = a.GetIndices(len, lenIndices); + std::vector bSrt = b.GetIndices(len, lenIndices); + + std::sort(aSrt.begin(), aSrt.end()); + std::sort(bSrt.begin(), bSrt.end()); + + unsigned int i = 0; + for (unsigned int j = 0; j < bSrt.size(); j++) { + while (aSrt[i] < bSrt[j]) { + i++; + if (i == aSrt.size()) { return true; } + } + assert(aSrt[i] >= bSrt[j]); + if (aSrt[i] == bSrt[j]) { return false; } + } + return true; +} + +template +bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t) +{ + return TruncateIndex(ArrayToEhIndex(a.hash+len), ilen) == t; +}