Store the Equihash solution in minimal representation in the block header

The genesis blocks and miner tests have been regenerated, because changing the
block header serialisation format changes the block hash, and thus validity.

The Equihash solutions have been removed from the bloom test inputs for
simplicity (block validity is not checked there; only a valid serialisation is
necessary).
This commit is contained in:
Jack Grigg
2016-08-14 15:04:13 +12:00
parent 20abe2083c
commit 5be6abbf84
15 changed files with 284 additions and 230 deletions

View File

@@ -165,6 +165,39 @@ eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int
return (i << (ilen - 8)) | r;
}
std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
size_t cBitLen)
{
assert(((cBitLen+1)+7)/8 <= sizeof(eh_index));
size_t lenIndices { 8*sizeof(eh_index)*minimal.size()/(cBitLen+1) };
size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
std::vector<unsigned char> array(lenIndices);
ExpandArray(minimal.data(), minimal.size(),
array.data(), lenIndices, cBitLen+1, bytePad);
std::vector<eh_index> ret;
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
ret.push_back(ArrayToEhIndex(array.data()+i));
}
return ret;
}
std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
size_t cBitLen)
{
assert(((cBitLen+1)+7)/8 <= sizeof(eh_index));
size_t lenIndices { indices.size()*sizeof(eh_index) };
size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) };
size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
std::vector<unsigned char> array(lenIndices);
for (int i = 0; i < indices.size(); i++) {
EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index)));
}
std::vector<unsigned char> ret(minLen);
CompressArray(array.data(), lenIndices,
ret.data(), minLen, cBitLen+1, bytePad);
return ret;
}
template<size_t WIDTH>
StepRow<WIDTH>::StepRow(const unsigned char* hashIn, size_t hInLen,
size_t hLen, size_t cBitLen)
@@ -224,12 +257,14 @@ bool StepRow<WIDTH>::IsZero(size_t len)
}
template<size_t WIDTH>
std::vector<eh_index> FullStepRow<WIDTH>::GetIndices(size_t len, size_t lenIndices) const
std::vector<unsigned char> FullStepRow<WIDTH>::GetIndices(size_t len, size_t lenIndices,
size_t cBitLen) const
{
std::vector<eh_index> ret;
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
ret.push_back(ArrayToEhIndex(hash+len+i));
}
assert(((cBitLen+1)+7)/8 <= sizeof(eh_index));
size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) };
size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
std::vector<unsigned char> ret(minLen);
CompressArray(hash+len, lenIndices, ret.data(), minLen, cBitLen+1, bytePad);
return ret;
}
@@ -287,7 +322,7 @@ std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t le
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
{
eh_index init_size { 1 << (CollisionBitLength + 1) };
@@ -386,7 +421,8 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
for (int m = l + 1; m < j; m++) {
FullStepRow<FinalFullWidth> res(X[i+l], X[i+m], hashLen, lenIndices, 0);
if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices) &&
validBlock(res.GetIndices(hashLen, 2*lenIndices))) {
validBlock(res.GetIndices(hashLen, 2*lenIndices,
CollisionBitLength))) {
return true;
}
}
@@ -455,7 +491,7 @@ void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, cons
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
{
eh_index init_size { 1 << (CollisionBitLength + 1) };
@@ -589,7 +625,7 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
// Now for each solution run the algorithm again to recreate the indices
LogPrint("pow", "Culling solutions\n");
for (std::shared_ptr<eh_trunc> partialSoln : partialSolns) {
std::set<std::vector<eh_index>> solns;
std::set<std::vector<unsigned char>> solns;
size_t hashLen;
size_t lenIndices;
unsigned char tmpHash[HashOutput];
@@ -656,7 +692,7 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
// We are at the top of the tree
assert(X.size() == K+1);
for (FullStepRow<FinalFullWidth> row : *X[K]) {
solns.insert(row.GetIndices(hashLen, lenIndices));
solns.insert(row.GetIndices(hashLen, lenIndices, CollisionBitLength));
}
for (auto soln : solns) {
if (validBlock(soln))
@@ -674,18 +710,18 @@ invalidsolution:
}
template<unsigned int N, unsigned int K>
bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln)
{
eh_index soln_size { 1u << K };
if (soln.size() != soln_size) {
LogPrint("pow", "Invalid solution size: %d\n", soln.size());
if (soln.size() != SolutionWidth) {
LogPrint("pow", "Invalid solution length: %d (expected %d)\n",
soln.size(), SolutionWidth);
return false;
}
std::vector<FullStepRow<FinalFullWidth>> X;
X.reserve(soln_size);
X.reserve(1 << K);
unsigned char tmpHash[HashOutput];
for (eh_index i : soln) {
for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) {
GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput);
X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8),
N/8, HashLength, CollisionBitLength, i);
@@ -724,39 +760,39 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
// Explicit instantiations for Equihash<96,3>
template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<200,9>
template int Equihash<200,9>::InitialiseState(eh_HashState& base_state);
template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<96,5>
template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
// Explicit instantiations for Equihash<48,5>
template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);

View File

@@ -34,6 +34,11 @@ void CompressArray(const unsigned char* in, size_t in_len,
eh_index ArrayToEhIndex(const unsigned char* array);
eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen);
std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
size_t cBitLen);
std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
size_t cBitLen);
template<size_t WIDTH>
class StepRow
{
@@ -93,9 +98,13 @@ public:
FullStepRow& operator=(const FullStepRow<WIDTH>& a);
inline bool IndicesBefore(const FullStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
std::vector<eh_index> GetIndices(size_t len, size_t lenIndices) const;
std::vector<unsigned char> GetIndices(size_t len, size_t lenIndices,
size_t cBitLen) const;
template<size_t W>
friend bool DistinctIndices(const FullStepRow<W>& a, const FullStepRow<W>& b,
size_t len, size_t lenIndices);
template<size_t W>
friend bool IsValidBranch(const FullStepRow<W>& a, const size_t len, const unsigned int ilen, const eh_trunc t);
};
@@ -164,17 +173,18 @@ public:
enum : size_t { FinalFullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K)) };
enum : size_t { TruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K-1))) };
enum : size_t { FinalTruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K))) };
enum : size_t { SolutionWidth=(1 << K)*(CollisionBitLength+1)/8 };
Equihash() { }
int InitialiseState(eh_HashState& base_state);
bool BasicSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
bool OptimisedSolve(const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled);
bool IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
bool IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
};
#include "equihash.tcc"
@@ -198,7 +208,7 @@ static Equihash<48,5> Eh48_5;
}
inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
{
if (n == 96 && k == 3) {
@@ -215,14 +225,14 @@ inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& bas
}
inline bool EhBasicSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock)
const std::function<bool(std::vector<unsigned char>)> validBlock)
{
return EhBasicSolve(n, k, base_state, validBlock,
[](EhSolverCancelCheck pos) { return false; });
}
inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock,
const std::function<bool(std::vector<unsigned char>)> validBlock,
const std::function<bool(EhSolverCancelCheck)> cancelled)
{
if (n == 96 && k == 3) {
@@ -239,7 +249,7 @@ inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState&
}
inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state,
const std::function<bool(std::vector<eh_index>)> validBlock)
const std::function<bool(std::vector<unsigned char>)> validBlock)
{
return EhOptimisedSolve(n, k, base_state, validBlock,
[](EhSolverCancelCheck pos) { return false; });

View File

@@ -10,11 +10,9 @@
template<size_t WIDTH>
bool DistinctIndices(const FullStepRow<WIDTH>& a, const FullStepRow<WIDTH>& b, size_t len, size_t lenIndices)
{
std::vector<eh_index> vIndicesA = a.GetIndices(len, lenIndices);
std::vector<eh_index> vIndicesB = b.GetIndices(len, lenIndices);
for(auto const& value1: vIndicesA) {
for(auto const& value2: vIndicesB) {
if (value1==value2) {
for(size_t i = 0; i < lenIndices; i += sizeof(eh_index)) {
for(size_t j = 0; j < lenIndices; j += sizeof(eh_index)) {
if (memcmp(a.hash+len+i, b.hash+len+j, sizeof(eh_index)) == 0) {
return false;
}
}