Skip to content

Commit

Permalink
Store the Equihash solution in minimal representation in the block he…
Browse files Browse the repository at this point in the history
…ader

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).
  • Loading branch information
str4d committed Aug 16, 2016
1 parent 20abe20 commit 5be6abb
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 230 deletions.
28 changes: 15 additions & 13 deletions qa/rpc-tests/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,40 +206,42 @@ def run_test(self):
# /rest/block/ #
################

# Block header length of 237 is:
# - 76 bytes: regular fields
# Block header length of 177 is:
# - 108 bytes: regular fields
# - 32 bytes: nonce
# - 129 bytes: Equihash solution vector:
# - 1 byte vector length
# - 2^k 4-byte indices
# - 37 bytes: Equihash solution:
# - 1 byte length
# - 2^k ((n/(k+1))+1)-bit indices.
# For regtest parameters (n = 48, k = 5),
# this is 32 9-bit indices

# check binary format
response = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"bin", "", True)
assert_equal(response.status, 200)
assert_greater_than(int(response.getheader('content-length')), 269)
assert_greater_than(int(response.getheader('content-length')), 177)
response_str = response.read()

# compare with block header
response_header = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"bin", "", True)
assert_equal(response_header.status, 200)
assert_equal(int(response_header.getheader('content-length')), 269)
assert_equal(int(response_header.getheader('content-length')), 177)
response_header_str = response_header.read()
assert_equal(response_str[0:269], response_header_str)
assert_equal(response_str[0:177], response_header_str)

# check block hex format
response_hex = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+"hex", "", True)
assert_equal(response_hex.status, 200)
assert_greater_than(int(response_hex.getheader('content-length')), 506)
assert_greater_than(int(response_hex.getheader('content-length')), 354)
response_hex_str = response_hex.read()
assert_equal(response_str.encode("hex")[0:506], response_hex_str[0:506])
assert_equal(response_str.encode("hex")[0:354], response_hex_str[0:354])

# compare with hex block header
response_header_hex = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"hex", "", True)
assert_equal(response_header_hex.status, 200)
assert_greater_than(int(response_header_hex.getheader('content-length')), 506)
assert_greater_than(int(response_header_hex.getheader('content-length')), 354)
response_header_hex_str = response_header_hex.read()
assert_equal(response_hex_str[0:506], response_header_hex_str[0:506])
assert_equal(response_header_str.encode("hex")[0:506], response_header_hex_str[0:506])
assert_equal(response_hex_str[0:354], response_header_hex_str[0:354])
assert_equal(response_header_str.encode("hex")[0:354], response_header_hex_str[0:354])

# check json format
json_string = http_get_call(url.hostname, url.port, '/rest/block/'+bb_hash+self.FORMAT_SEPARATOR+'json')
Expand Down
2 changes: 1 addition & 1 deletion src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class CBlockIndex
unsigned int nTime;
unsigned int nBits;
uint256 nNonce;
std::vector<uint32_t> nSolution;
std::vector<unsigned char> nSolution;

//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId;
Expand Down
14 changes: 7 additions & 7 deletions src/chainparams.cpp

Large diffs are not rendered by default.

92 changes: 64 additions & 28 deletions src/crypto/equihash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) };
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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) };
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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))
Expand All @@ -674,18 +710,18 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
}

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);
Expand Down Expand Up @@ -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);
26 changes: 18 additions & 8 deletions src/crypto/equihash.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -93,9 +98,13 @@ class FullStepRow : public StepRow<WIDTH>
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);
};

Expand Down Expand Up @@ -164,17 +173,18 @@ class Equihash
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"
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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; });
Expand Down
8 changes: 3 additions & 5 deletions src/crypto/equihash.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
Loading

0 comments on commit 5be6abb

Please sign in to comment.