Use template parameters to statically initialise Equihash

This commit is contained in:
Jack Grigg
2016-05-05 15:00:44 +12:00
parent 39f5cb35f9
commit e95747288a
7 changed files with 124 additions and 75 deletions

View File

@@ -19,31 +19,18 @@
#include <iostream>
#include <stdexcept>
void validate_params(int n, int k)
{
if (k>=n) {
std::cerr << "n must be larger than k\n";
throw invalid_params();
}
if (n % 8 != 0) {
std::cerr << "Parameters must satisfy n = 0 mod 8\n";
throw invalid_params();
}
if ((n/(k+1)) % 8 != 0) {
std::cerr << "Parameters must satisfy n/(k+1) = 0 mod 8\n";
throw invalid_params();
}
}
int Equihash::InitialiseState(eh_HashState& base_state)
template<unsigned int N, unsigned int K>
int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
{
unsigned int n = N;
unsigned int k = K;
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
memcpy(personalization, "ZcashPOW", 8);
memcpy(personalization+8, &n, 4);
memcpy(personalization+12, &k, 4);
return crypto_generichash_blake2b_init_salt_personal(&base_state,
NULL, 0, // No key.
n/8,
N/8,
NULL, // No salt.
personalization);
}
@@ -238,27 +225,21 @@ eh_trunc* TruncatedStepRow::GetPartialSolution(eh_index soln_size) const
return p;
}
Equihash::Equihash(unsigned int n, unsigned int k) :
n(n), k(k)
template<unsigned int N, unsigned int K>
std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& base_state)
{
validate_params(n, k);
}
std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_state)
{
assert(CollisionBitLength() + 1 < 8*sizeof(eh_index));
eh_index init_size { 1 << (CollisionBitLength() + 1) };
eh_index init_size { 1 << (CollisionBitLength + 1) };
// 1) Generate first list
LogPrint("pow", "Generating first list\n");
std::vector<FullStepRow> X;
X.reserve(init_size);
for (eh_index i = 0; i < init_size; i++) {
X.emplace_back(n, base_state, i);
X.emplace_back(N, base_state, i);
}
// 3) Repeat step 2 until 2n/(k+1) bits remain
for (int r = 1; r < k && X.size() > 0; r++) {
for (int r = 1; r < K && X.size() > 0; r++) {
LogPrint("pow", "Round %d:\n", r);
// 2a) Sort the list
LogPrint("pow", "- Sorting list\n");
@@ -272,7 +253,7 @@ std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_st
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1;
while (i+j < X.size() &&
HasCollision(X[i], X[i+j], CollisionByteLength())) {
HasCollision(X[i], X[i+j], CollisionByteLength)) {
j++;
}
@@ -281,7 +262,7 @@ std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_st
for (int m = l + 1; m < j; m++) {
if (DistinctIndices(X[i+l], X[i+m])) {
Xc.push_back(X[i+l] ^ X[i+m]);
Xc.back().TrimHash(CollisionByteLength());
Xc.back().TrimHash(CollisionByteLength);
}
}
}
@@ -383,14 +364,14 @@ void CollideBranches(std::vector<FullStepRow>& X, const unsigned int clen, const
}
}
std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& base_state)
template<unsigned int N, unsigned int K>
std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state)
{
assert(CollisionBitLength() + 1 < 8*sizeof(eh_index));
eh_index init_size { 1 << (CollisionBitLength() + 1) };
eh_index init_size { 1 << (CollisionBitLength + 1) };
// First run the algorithm with truncated indices
eh_index soln_size { 1 << k };
eh_index soln_size { 1 << K };
std::vector<eh_trunc*> partialSolns;
{
@@ -399,11 +380,11 @@ std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& bas
std::vector<TruncatedStepRow> Xt;
Xt.reserve(init_size);
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);
}
// 3) Repeat step 2 until 2n/(k+1) bits remain
for (int r = 1; r < k && Xt.size() > 0; r++) {
for (int r = 1; r < K && Xt.size() > 0; r++) {
LogPrint("pow", "Round %d:\n", r);
// 2a) Sort the list
LogPrint("pow", "- Sorting list\n");
@@ -417,7 +398,7 @@ std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& bas
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
int j = 1;
while (i+j < Xt.size() &&
HasCollision(Xt[i], Xt[i+j], CollisionByteLength())) {
HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) {
j++;
}
@@ -426,7 +407,7 @@ std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& bas
for (int m = l + 1; m < j; m++) {
// We truncated, so don't check for distinct indices here
Xc.push_back(Xt[i+l] ^ Xt[i+m]);
Xc.back().TrimHash(CollisionByteLength());
Xc.back().TrimHash(CollisionByteLength);
}
}
@@ -477,7 +458,7 @@ std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& bas
// Now for each solution run the algorithm again to recreate the indices
LogPrint("pow", "Culling solutions\n");
std::set<std::vector<eh_index>> solns;
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength() + 1) };
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
int invalidCount = 0;
for (eh_trunc* partialSoln : partialSolns) {
// 1) Generate first list of possibilities
@@ -487,8 +468,8 @@ std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& bas
std::vector<FullStepRow> ic;
ic.reserve(recreate_size);
for (eh_index j = 0; j < recreate_size; j++) {
eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength() + 1) };
ic.emplace_back(n, base_state, newIndex);
eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) };
ic.emplace_back(N, base_state, newIndex);
}
X.push_back(ic);
}
@@ -505,7 +486,7 @@ std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& bas
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());
CollideBranches(ic, CollisionByteLength(), CollisionBitLength() + 1, partialSoln[(1<<r)*v], partialSoln[(1<<r)*(v+1)]);
CollideBranches(ic, 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)
@@ -535,9 +516,10 @@ deletesolution:
return solns;
}
bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
{
eh_index soln_size { 1u << k };
eh_index soln_size { 1u << K };
if (soln.size() != soln_size) {
LogPrint("pow", "Invalid solution size: %d\n", soln.size());
return false;
@@ -546,13 +528,13 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_in
std::vector<FullStepRow> X;
X.reserve(soln_size);
for (eh_index i : soln) {
X.emplace_back(n, base_state, i);
X.emplace_back(N, base_state, i);
}
while (X.size() > 1) {
std::vector<FullStepRow> Xc;
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", "X[i] = %s\n", X[i].GetHex());
LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex());
@@ -567,7 +549,7 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_in
return false;
}
Xc.push_back(X[i] ^ X[i+1]);
Xc.back().TrimHash(CollisionByteLength());
Xc.back().TrimHash(CollisionByteLength);
}
X = Xc;
}
@@ -575,3 +557,15 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_in
assert(X.size() == 1);
return X[0].IsZero();
}
// Explicit instantiations for Equihash<96,5>
template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<96,5>::BasicSolve(const eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state);
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
// Explicit instantiations for Equihash<48,5>
template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<48,5>::BasicSolve(const eh_HashState& base_state);
template std::set<std::vector<eh_index>> Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state);
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);