Use fixed-width array for storing hash and indices
This commit is contained in:
parent
29d9986c83
commit
d4d76536a5
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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…
Reference in New Issue