Browse Source

Use fixed-width array for storing hash and indices

pull/145/head
Jack Grigg 8 years ago
parent
commit
d4d76536a5
  1. 274
      src/crypto/equihash.cpp
  2. 79
      src/crypto/equihash.h
  3. 35
      src/crypto/equihash.tcc

274
src/crypto/equihash.cpp

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

79
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<size_t WIDTH>
class StepRow
{
template<size_t W>
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<size_t W>
StepRow(const StepRow<W>& 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<size_t W>
friend bool HasCollision(StepRow<W>& a, StepRow<W>& 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<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:
unsigned int lenIndices;
template<size_t W>
friend class FullStepRow;
using StepRow<WIDTH>::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<WIDTH>& a) : StepRow<WIDTH> {a} { }
template<size_t W>
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); }
std::vector<eh_index> GetIndices() const;
inline bool IndicesBefore(const FullStepRow<WIDTH>& a, size_t len) const { return ArrayToEhIndex(hash+len) < ArrayToEhIndex(a.hash+len); }
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);
bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t);
class TruncatedStepRow : public StepRow
template<size_t WIDTH>
class TruncatedStepRow : public StepRow<WIDTH>
{
private:
unsigned int lenIndices;
template<size_t W>
friend class TruncatedStepRow;
using StepRow<WIDTH>::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<WIDTH>& a) : StepRow<WIDTH> {a} { }
template<size_t W>
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; }
eh_trunc* GetPartialSolution(eh_index soln_size) const;
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* GetTruncatedIndices(size_t len, size_t lenIndices) const;
};
template<unsigned int N, unsigned int K>
@ -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<eh_index> soln);
};
#include "equihash.tcc"
static Equihash<96,5> Eh965;
static Equihash<48,5> Eh485;

35
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 <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;
}
Loading…
Cancel
Save