|
|
@ -35,7 +35,7 @@ int Equihash<N,K>::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<size_t WIDTH> |
|
|
|
StepRow<WIDTH>::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<size_t WIDTH> template<size_t W> |
|
|
|
StepRow<WIDTH>::StepRow(const StepRow<W>& 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<size_t WIDTH> |
|
|
|
FullStepRow<WIDTH>::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : |
|
|
|
StepRow<WIDTH> {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<size_t WIDTH> template<size_t W> |
|
|
|
FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) : |
|
|
|
StepRow<WIDTH> {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<size_t WIDTH> |
|
|
|
FullStepRow<WIDTH>& FullStepRow<WIDTH>::operator=(const FullStepRow<WIDTH>& 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<size_t WIDTH> |
|
|
|
bool StepRow<WIDTH>::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<eh_index> FullStepRow::GetIndices() const |
|
|
|
template<size_t WIDTH> |
|
|
|
std::vector<eh_index> FullStepRow<WIDTH>::GetIndices(size_t len, size_t lenIndices) const |
|
|
|
{ |
|
|
|
std::vector<eh_index> ret; |
|
|
|
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) { |
|
|
@ -166,7 +136,8 @@ std::vector<eh_index> FullStepRow::GetIndices() const |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
bool HasCollision(StepRow& a, StepRow& b, int l) |
|
|
|
template<size_t WIDTH> |
|
|
|
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& 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<size_t WIDTH> |
|
|
|
TruncatedStepRow<WIDTH>::TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen) : |
|
|
|
StepRow<WIDTH> {n, base_state, i} |
|
|
|
{ |
|
|
|
std::vector<eh_index> aSrt = a.GetIndices(); |
|
|
|
std::vector<eh_index> 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<size_t WIDTH> template<size_t W> |
|
|
|
TruncatedStepRow<WIDTH>::TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int trim) : |
|
|
|
StepRow<WIDTH> {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<size_t WIDTH> |
|
|
|
TruncatedStepRow<WIDTH>& TruncatedStepRow<WIDTH>::operator=(const TruncatedStepRow<WIDTH>& 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<size_t WIDTH> |
|
|
|
eh_trunc* TruncatedStepRow<WIDTH>::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<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba |
|
|
|
// 1) Generate first list
|
|
|
|
LogPrint("pow", "Generating first list\n"); |
|
|
|
size_t hashLen = N/8; |
|
|
|
std::vector<FullStepRow> X; |
|
|
|
size_t lenIndices = sizeof(eh_index); |
|
|
|
std::vector<FullStepRow<FullWidth>> 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<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba |
|
|
|
LogPrint("pow", "- Finding collisions\n"); |
|
|
|
int i = 0; |
|
|
|
int posFree = 0; |
|
|
|
std::vector<FullStepRow> Xc; |
|
|
|
std::vector<FullStepRow<FullWidth>> 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<std::vector<eh_index>> Equihash<N,K>::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<std::vector<eh_index>> Equihash<N,K>::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<std::vector<eh_index>> Equihash<N,K>::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<FinalFullWidth> 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<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba |
|
|
|
return solns; |
|
|
|
} |
|
|
|
|
|
|
|
void CollideBranches(std::vector<FullStepRow>& X, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt) |
|
|
|
template<size_t WIDTH> |
|
|
|
void CollideBranches(std::vector<FullStepRow<WIDTH>>& 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<FullStepRow> Xc; |
|
|
|
std::vector<FullStepRow<WIDTH>> 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<FullStepRow>& 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<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState |
|
|
|
// 1) Generate first list
|
|
|
|
LogPrint("pow", "Generating first list\n"); |
|
|
|
size_t hashLen = N/8; |
|
|
|
std::vector<TruncatedStepRow> Xt; |
|
|
|
size_t lenIndices = sizeof(eh_trunc); |
|
|
|
std::vector<TruncatedStepRow<TruncatedWidth>> 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<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState |
|
|
|
LogPrint("pow", "- Finding collisions\n"); |
|
|
|
int i = 0; |
|
|
|
int posFree = 0; |
|
|
|
std::vector<TruncatedStepRow> Xc; |
|
|
|
std::vector<TruncatedStepRow<TruncatedWidth>> 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<std::vector<eh_index>> Equihash<N,K>::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<std::vector<eh_index>> Equihash<N,K>::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<std::vector<eh_index>> Equihash<N,K>::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<FinalTruncatedWidth> 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<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState |
|
|
|
for (eh_trunc* partialSoln : partialSolns) { |
|
|
|
// 1) Generate first list of possibilities
|
|
|
|
size_t hashLen = N/8; |
|
|
|
std::vector<std::vector<FullStepRow>> X; |
|
|
|
size_t lenIndices = sizeof(eh_index); |
|
|
|
std::vector<std::vector<FullStepRow<FinalFullWidth>>> X; |
|
|
|
X.reserve(soln_size); |
|
|
|
for (eh_index i = 0; i < soln_size; i++) { |
|
|
|
std::vector<FullStepRow> ic; |
|
|
|
std::vector<FullStepRow<FinalFullWidth>> 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<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState |
|
|
|
|
|
|
|
// 3) Repeat step 2 for each level of the tree
|
|
|
|
for (int r = 0; X.size() > 1; r++) { |
|
|
|
std::vector<std::vector<FullStepRow>> Xc; |
|
|
|
std::vector<std::vector<FullStepRow<FinalFullWidth>>> 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<FullStepRow> ic(X[v]); |
|
|
|
std::vector<FullStepRow<FinalFullWidth>> 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<<r)*v], partialSoln[(1<<r)*(v+1)]); |
|
|
|
CollideBranches(ic, hashLen, lenIndices, CollisionByteLength, CollisionBitLength + 1, partialSoln[(1<<r)*v], partialSoln[(1<<r)*(v+1)]); |
|
|
|
|
|
|
|
// 2v) Check if this has become an invalid solution
|
|
|
|
if (ic.size() == 0) |
|
|
@ -540,12 +465,13 @@ std::set<std::vector<eh_index>> Equihash<N,K>::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<FinalFullWidth> row : X[0]) { |
|
|
|
solns.insert(row.GetIndices(hashLen, lenIndices)); |
|
|
|
} |
|
|
|
goto deletesolution; |
|
|
|
|
|
|
@ -569,36 +495,40 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector< |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<FullStepRow> X; |
|
|
|
std::vector<FullStepRow<FinalFullWidth>> 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<FullStepRow> Xc; |
|
|
|
std::vector<FullStepRow<FinalFullWidth>> 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>
|
|
|
|