Use fixed-width array for storing hash and indices

This commit is contained in:
Jack Grigg
2016-05-07 16:36:54 +12:00
parent 29d9986c83
commit d4d76536a5
3 changed files with 186 additions and 202 deletions

View File

@@ -35,7 +35,7 @@ int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
personalization); personalization);
} }
void EhIndexToArray(eh_index i, unsigned char* array) void EhIndexToArray(const eh_index i, unsigned char* array)
{ {
assert(sizeof(eh_index) == 4); assert(sizeof(eh_index) == 4);
array[0] = (i >> 24) & 0xFF; array[0] = (i >> 24) & 0xFF;
@@ -44,7 +44,7 @@ void EhIndexToArray(eh_index i, unsigned char* array)
array[3] = i & 0xFF; array[3] = i & 0xFF;
} }
eh_index ArrayToEhIndex(unsigned char* array) eh_index ArrayToEhIndex(const unsigned char* array)
{ {
assert(sizeof(eh_index) == 4); assert(sizeof(eh_index) == 4);
eh_index ret {array[0]}; eh_index ret {array[0]};
@@ -57,22 +57,21 @@ eh_index ArrayToEhIndex(unsigned char* array)
return ret; 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 // Truncate to 8 bits
assert(sizeof(eh_trunc) == 1); assert(sizeof(eh_trunc) == 1);
return (i >> (ilen - 8)) & 0xff; 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}; eh_index i{t};
return (i << (ilen - 8)) | r; return (i << (ilen - 8)) | r;
} }
StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : template<size_t WIDTH>
hash {new unsigned char[n/8]}, StepRow<WIDTH>::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i)
len {n/8}
{ {
eh_HashState state; eh_HashState state;
state = base_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); 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) : template<size_t WIDTH>
hash {new unsigned char[a.len]}, FullStepRow<WIDTH>::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
len {a.len} 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) : template<size_t WIDTH> template<size_t W>
StepRow {n, base_state, i}, FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
lenIndices {sizeof(eh_index)} StepRow<WIDTH> {a}
{ {
unsigned char* p = new unsigned char[len+lenIndices]; assert(len+lenIndices <= W);
std::copy(hash, hash+len, p); assert(len-trim+(2*lenIndices) <= WIDTH);
EhIndexToArray(i, p+len); for (int i = trim; i < len; i++)
delete[] hash; hash[i-trim] = a.hash[i] ^ b.hash[i];
hash = p; 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);
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);
} else { } else {
std::copy(b.hash+b.len, b.hash+b.len+b.lenIndices, p+len); std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim);
std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len+b.lenIndices); 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+WIDTH, hash);
std::copy(a.hash, a.hash+a.len+a.lenIndices, p);
delete[] hash;
hash = p;
len = a.len;
lenIndices = a.lenIndices;
return *this; return *this;
} }
bool StepRow::IsZero() template<size_t WIDTH>
bool StepRow<WIDTH>::IsZero(size_t len)
{ {
char res = 0; char res = 0;
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
@@ -157,7 +126,8 @@ bool StepRow::IsZero()
return res == 0; 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; std::vector<eh_index> ret;
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) { for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
@@ -166,7 +136,8 @@ std::vector<eh_index> FullStepRow::GetIndices() const
return ret; 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; bool res = true;
for (int j = 0; j < l; j++) for (int j = 0; j < l; j++)
@@ -174,92 +145,40 @@ bool HasCollision(StepRow& a, StepRow& b, int l)
return res; return res;
} }
// Checks if the intersection of a.indices and b.indices is empty template<size_t WIDTH>
bool DistinctIndices(const FullStepRow& a, const FullStepRow& b) 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(); hash[n/8] = TruncateIndex(i, ilen);
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;
} }
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; assert(len+lenIndices <= W);
} assert(len-trim+(2*lenIndices) <= WIDTH);
for (int i = trim; i < len; i++)
TruncatedStepRow::TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen) : hash[i-trim] = a.hash[i] ^ b.hash[i];
StepRow {n, base_state, i}, if (a.IndicesBefore(b, len, lenIndices)) {
lenIndices {1} 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);
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);
} else { } else {
std::copy(b.hash+b.len, b.hash+b.len+b.lenIndices, p+len); std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim);
std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len+b.lenIndices); 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+WIDTH, hash);
std::copy(a.hash, a.hash+a.len+a.lenIndices, p);
delete[] hash;
hash = p;
len = a.len;
lenIndices = a.lenIndices;
return *this; 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]; eh_trunc* p = new eh_trunc[lenIndices];
std::copy(hash+len, hash+len+lenIndices, p); std::copy(hash+len, hash+len+lenIndices, p);
return 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 // 1) Generate first list
LogPrint("pow", "Generating first list\n"); LogPrint("pow", "Generating first list\n");
size_t hashLen = N/8; size_t hashLen = N/8;
std::vector<FullStepRow> X; size_t lenIndices = sizeof(eh_index);
std::vector<FullStepRow<FullWidth>> X;
X.reserve(init_size); X.reserve(init_size);
for (eh_index i = 0; i < init_size; i++) { for (eh_index i = 0; i < init_size; i++) {
X.emplace_back(N, base_state, 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"); LogPrint("pow", "- Finding collisions\n");
int i = 0; int i = 0;
int posFree = 0; int posFree = 0;
std::vector<FullStepRow> Xc; std::vector<FullStepRow<FullWidth>> Xc;
while (i < X.size() - 1) { while (i < X.size() - 1) {
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1; 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)) // 2c) Calculate tuples (X_i ^ X_j, (i, j))
for (int l = 0; l < j - 1; l++) { for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) { for (int m = l + 1; m < j; m++) {
if (DistinctIndices(X[i+l], X[i+m])) { if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
Xc.emplace_back(X[i+l], X[i+m], CollisionByteLength); 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; hashLen -= CollisionByteLength;
lenIndices *= 2;
} }
// k+1) Find a collision on last 2n(k+1) bits // 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)); std::sort(X.begin(), X.end(), CompareSR(hashLen));
LogPrint("pow", "- Finding collisions\n"); LogPrint("pow", "- Finding collisions\n");
for (int i = 0; i < X.size() - 1; i++) { for (int i = 0; i < X.size() - 1; i++) {
FullStepRow res(X[i], X[i+1], 0); FullStepRow<FinalFullWidth> res(X[i], X[i+1], hashLen, lenIndices, 0);
if (res.IsZero() && DistinctIndices(X[i], X[i+1])) { if (res.IsZero(hashLen) && DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) {
solns.insert(res.GetIndices()); solns.insert(res.GetIndices(hashLen, 2*lenIndices));
} }
} }
} else } else
@@ -353,11 +274,12 @@ std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& ba
return solns; 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 i = 0;
int posFree = 0; int posFree = 0;
std::vector<FullStepRow> Xc; std::vector<FullStepRow<WIDTH>> Xc;
while (i < X.size() - 1) { while (i < X.size() - 1) {
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1; 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)) // 2c) Calculate tuples (X_i ^ X_j, (i, j))
for (int l = 0; l < j - 1; l++) { for (int l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) { for (int m = l + 1; m < j; m++) {
if (DistinctIndices(X[i+l], X[i+m])) { if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) {
if (IsValidBranch(X[i+l], ilen, lt) && IsValidBranch(X[i+m], ilen, rt)) { if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) {
Xc.emplace_back(X[i+l], X[i+m], clen); Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen);
} else if (IsValidBranch(X[i+m], ilen, lt) && IsValidBranch(X[i+l], ilen, rt)) { } 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], clen); 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 // 1) Generate first list
LogPrint("pow", "Generating first list\n"); LogPrint("pow", "Generating first list\n");
size_t hashLen = N/8; size_t hashLen = N/8;
std::vector<TruncatedStepRow> Xt; size_t lenIndices = sizeof(eh_trunc);
std::vector<TruncatedStepRow<TruncatedWidth>> Xt;
Xt.reserve(init_size); Xt.reserve(init_size);
for (eh_index i = 0; i < init_size; i++) { for (eh_index i = 0; i < init_size; i++) {
Xt.emplace_back(N, base_state, i, CollisionBitLength + 1); 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"); LogPrint("pow", "- Finding collisions\n");
int i = 0; int i = 0;
int posFree = 0; int posFree = 0;
std::vector<TruncatedStepRow> Xc; std::vector<TruncatedStepRow<TruncatedWidth>> Xc;
while (i < Xt.size() - 1) { while (i < Xt.size() - 1) {
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1; 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 l = 0; l < j - 1; l++) {
for (int m = l + 1; m < j; m++) { for (int m = l + 1; m < j; m++) {
// We truncated, so don't check for distinct indices here // 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; hashLen -= CollisionByteLength;
lenIndices *= 2;
} }
// k+1) Find a collision on last 2n(k+1) bits // 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)); std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen));
LogPrint("pow", "- Finding collisions\n"); LogPrint("pow", "- Finding collisions\n");
for (int i = 0; i < Xt.size() - 1; i++) { for (int i = 0; i < Xt.size() - 1; i++) {
TruncatedStepRow res(Xt[i], Xt[i+1], 0); TruncatedStepRow<FinalTruncatedWidth> res(Xt[i], Xt[i+1], hashLen, lenIndices, 0);
if (res.IsZero()) { if (res.IsZero(hashLen)) {
partialSolns.push_back(res.GetPartialSolution(soln_size)); partialSolns.push_back(res.GetTruncatedIndices(hashLen, 2*lenIndices));
} }
} }
} else } else
@@ -505,10 +429,11 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
for (eh_trunc* partialSoln : partialSolns) { for (eh_trunc* partialSoln : partialSolns) {
// 1) Generate first list of possibilities // 1) Generate first list of possibilities
size_t hashLen = N/8; 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); X.reserve(soln_size);
for (eh_index i = 0; i < soln_size; i++) { for (eh_index i = 0; i < soln_size; i++) {
std::vector<FullStepRow> ic; std::vector<FullStepRow<FinalFullWidth>> ic;
ic.reserve(recreate_size); ic.reserve(recreate_size);
for (eh_index j = 0; j < recreate_size; j++) { for (eh_index j = 0; j < recreate_size; j++) {
eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) }; 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 // 3) Repeat step 2 for each level of the tree
for (int r = 0; X.size() > 1; r++) { 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); Xc.reserve(X.size()/2);
// 2a) For each pair of lists: // 2a) For each pair of lists:
for (int v = 0; v < X.size(); v += 2) { for (int v = 0; v < X.size(); v += 2) {
// 2b) Merge the lists // 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.reserve(X[v].size() + X[v+1].size());
ic.insert(ic.end(), X[v+1].begin(), X[v+1].end()); ic.insert(ic.end(), X[v+1].begin(), X[v+1].end());
std::sort(ic.begin(), ic.end(), CompareSR(hashLen)); 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 // 2v) Check if this has become an invalid solution
if (ic.size() == 0) if (ic.size() == 0)
@@ -540,12 +465,13 @@ std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState
X = Xc; X = Xc;
hashLen -= CollisionByteLength; hashLen -= CollisionByteLength;
lenIndices *= 2;
} }
// We are at the top of the tree // We are at the top of the tree
assert(X.size() == 1); assert(X.size() == 1);
for (FullStepRow row : X[0]) { for (FullStepRow<FinalFullWidth> row : X[0]) {
solns.insert(row.GetIndices()); solns.insert(row.GetIndices(hashLen, lenIndices));
} }
goto deletesolution; goto deletesolution;
@@ -569,36 +495,40 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
return false; return false;
} }
std::vector<FullStepRow> X; std::vector<FullStepRow<FinalFullWidth>> X;
X.reserve(soln_size); X.reserve(soln_size);
for (eh_index i : soln) { for (eh_index i : soln) {
X.emplace_back(N, base_state, i); X.emplace_back(N, base_state, i);
} }
size_t hashLen = N/8;
size_t lenIndices = sizeof(eh_index);
while (X.size() > 1) { while (X.size() > 1) {
std::vector<FullStepRow> Xc; std::vector<FullStepRow<FinalFullWidth>> Xc;
for (int i = 0; i < X.size(); i += 2) { for (int i = 0; i < X.size(); i += 2) {
if (!HasCollision(X[i], X[i+1], CollisionByteLength)) { if (!HasCollision(X[i], X[i+1], CollisionByteLength)) {
LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n"); LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
LogPrint("pow", "X[i] = %s\n", X[i].GetHex()); LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen));
LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex()); LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen));
return false; return false;
} }
if (X[i+1].IndicesBefore(X[i])) { if (X[i+1].IndicesBefore(X[i], hashLen)) {
return false; return false;
LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n"); 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"); LogPrint("pow", "Invalid solution: duplicate indices\n");
return false; return false;
} }
Xc.emplace_back(X[i], X[i+1], CollisionByteLength); Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength);
} }
X = Xc; X = Xc;
hashLen -= CollisionByteLength;
lenIndices *= 2;
} }
assert(X.size() == 1); assert(X.size() == 1);
return X[0].IsZero(); return X[0].IsZero(hashLen);
} }
// Explicit instantiations for Equihash<96,5> // Explicit instantiations for Equihash<96,5>

View File

@@ -21,26 +21,31 @@ typedef crypto_generichash_blake2b_state eh_HashState;
typedef uint32_t eh_index; typedef uint32_t eh_index;
typedef uint8_t eh_trunc; 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<size_t WIDTH>
class StepRow class StepRow
{ {
template<size_t W>
friend class StepRow;
friend class CompareSR; friend class CompareSR;
protected: protected:
unsigned char* hash; unsigned char hash[WIDTH];
unsigned int len;
public: public:
StepRow(unsigned int n, const eh_HashState& base_state, eh_index i); StepRow(unsigned int n, const eh_HashState& base_state, eh_index i);
~StepRow(); ~StepRow() { }
StepRow(const StepRow& a); template<size_t W>
StepRow(const StepRow<W>& a);
bool IsZero(); bool IsZero(size_t len);
std::string GetHex() { return HexStr(hash, hash+len); } std::string GetHex(size_t len) { return HexStr(hash, hash+len); }
friend bool HasCollision(StepRow& a, StepRow& b, int l); template<size_t W>
friend bool HasCollision(StepRow<W>& a, StepRow<W>& b, int l);
}; };
class CompareSR class CompareSR
@@ -51,48 +56,56 @@ private:
public: public:
CompareSR(size_t l) : len {l} { } CompareSR(size_t l) : len {l} { }
inline bool operator()(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, len) < 0; } template<size_t W>
inline bool operator()(const StepRow<W>& a, const StepRow<W>& b) { return memcmp(a.hash, b.hash, len) < 0; }
}; };
bool HasCollision(StepRow& a, StepRow& b, int l); template<size_t WIDTH>
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l);
class FullStepRow : public StepRow template<size_t WIDTH>
class FullStepRow : public StepRow<WIDTH>
{ {
private: template<size_t W>
unsigned int lenIndices; friend class FullStepRow;
using StepRow<WIDTH>::hash;
public: public:
FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i); FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i);
~FullStepRow() { } ~FullStepRow() { }
FullStepRow(const FullStepRow& a); FullStepRow(const FullStepRow<WIDTH>& a) : StepRow<WIDTH> {a} { }
FullStepRow(const FullStepRow& a, const FullStepRow& b, int trim); template<size_t W>
FullStepRow& operator=(const FullStepRow& a); FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim);
FullStepRow& operator=(const FullStepRow<WIDTH>& a);
inline bool IndicesBefore(const FullStepRow& a) const { return ArrayToEhIndex(hash+len) < ArrayToEhIndex(a.hash+a.len); } inline bool IndicesBefore(const FullStepRow<WIDTH>& a, size_t len) const { return ArrayToEhIndex(hash+len) < ArrayToEhIndex(a.hash+len); }
std::vector<eh_index> GetIndices() const; std::vector<eh_index> GetIndices(size_t len, size_t lenIndices) const;
friend bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t); template<size_t W>
friend bool IsValidBranch(const FullStepRow<W>& a, const size_t len, const unsigned int ilen, const eh_trunc t);
}; };
bool DistinctIndices(const FullStepRow& a, const FullStepRow& b); template<size_t WIDTH>
bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t); class TruncatedStepRow : public StepRow<WIDTH>
class TruncatedStepRow : public StepRow
{ {
private: template<size_t W>
unsigned int lenIndices; friend class TruncatedStepRow;
using StepRow<WIDTH>::hash;
public: public:
TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen); TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen);
~TruncatedStepRow() { } ~TruncatedStepRow() { }
TruncatedStepRow(const TruncatedStepRow& a); TruncatedStepRow(const TruncatedStepRow<WIDTH>& a) : StepRow<WIDTH> {a} { }
TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, int trim); template<size_t W>
TruncatedStepRow& operator=(const TruncatedStepRow& a); TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int trim);
TruncatedStepRow& operator=(const TruncatedStepRow<WIDTH>& a);
inline bool IndicesBefore(const TruncatedStepRow& a) const { return memcmp(hash+len, a.hash+a.len, lenIndices) < 0; } inline bool IndicesBefore(const TruncatedStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
eh_trunc* GetPartialSolution(eh_index soln_size) const; eh_trunc* GetTruncatedIndices(size_t len, size_t lenIndices) const;
}; };
template<unsigned int N, unsigned int K> template<unsigned int N, unsigned int K>
@@ -107,6 +120,10 @@ private:
public: public:
enum { CollisionBitLength=N/(K+1) }; enum { CollisionBitLength=N/(K+1) };
enum { CollisionByteLength=CollisionBitLength/8 }; 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() { } Equihash() { }
@@ -116,6 +133,8 @@ public:
bool IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln); bool IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
}; };
#include "equihash.tcc"
static Equihash<96,5> Eh965; static Equihash<96,5> Eh965;
static Equihash<48,5> Eh485; static Equihash<48,5> Eh485;

35
src/crypto/equihash.tcc Normal file
View File

@@ -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 <algorithm>
#include <cassert>
// Checks if the intersection of a.indices and b.indices is empty
template<size_t WIDTH>
bool DistinctIndices(const FullStepRow<WIDTH>& a, const FullStepRow<WIDTH>& b, size_t len, size_t lenIndices)
{
std::vector<eh_index> aSrt = a.GetIndices(len, lenIndices);
std::vector<eh_index> 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<size_t WIDTH>
bool IsValidBranch(const FullStepRow<WIDTH>& a, const size_t len, const unsigned int ilen, const eh_trunc t)
{
return TruncateIndex(ArrayToEhIndex(a.hash+len), ilen) == t;
}