]> Git Repo - VerusCoin.git/blame - src/crypto/equihash.cpp
macifiy the build so that this compiles on a mac
[VerusCoin.git] / src / crypto / equihash.cpp
CommitLineData
6d25662f
JG
1// Copyright (c) 2016 Jack Grigg
2// Copyright (c) 2016 The Zcash developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6// Implementation of the Equihash Proof-of-Work algorithm.
7//
8// Reference
9// =========
10// Alex Biryukov and Dmitry Khovratovich
11// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem
12// NDSS ’16, 21-24 February 2016, San Diego, CA, USA
13// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf
14
15#include "crypto/equihash.h"
16#include "util.h"
17
18#include <algorithm>
6d25662f
JG
19#include <iostream>
20#include <stdexcept>
21
0a66f013
JG
22#include <boost/optional.hpp>
23
e891d64b
JB
24#ifdef __APPLE__
25#include <machine/endian.h>
26#include <libkern/OSByteOrder.h>
27
28#define htobe16(x) OSSwapHostToBigInt16(x)
29#define htole16(x) OSSwapHostToLittleInt16(x)
30#define be16toh(x) OSSwapBigToHostInt16(x)
31#define le16toh(x) OSSwapLittleToHostInt16(x)
32
33#define htobe32(x) OSSwapHostToBigInt32(x)
34#define htole32(x) OSSwapHostToLittleInt32(x)
35#define be32toh(x) OSSwapBigToHostInt32(x)
36#define le32toh(x) OSSwapLittleToHostInt32(x)
37
38#define htobe64(x) OSSwapHostToBigInt64(x)
39#define htole64(x) OSSwapHostToLittleInt64(x)
40#define be64toh(x) OSSwapBigToHostInt64(x)
41#define le64toh(x) OSSwapLittleToHostInt64(x)
42
43#define __BIG_ENDIAN BIG_ENDIAN
44#define __LITTLE_ENDIAN LITTLE_ENDIAN
45#define __BYTE_ORDER BYTE_ORDER
46#else
47#include
48#include
49#endif
50
2dbabb11
JG
51EhSolverCancelledException solver_cancelled;
52
e9574728
JG
53template<unsigned int N, unsigned int K>
54int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
6d25662f 55{
09e9a329
JG
56 uint32_t le_N = htole32(N);
57 uint32_t le_K = htole32(K);
6d25662f 58 unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
a6dcf2ee 59 memcpy(personalization, "ZcashPoW", 8);
09e9a329
JG
60 memcpy(personalization+8, &le_N, 4);
61 memcpy(personalization+12, &le_K, 4);
6d25662f
JG
62 return crypto_generichash_blake2b_init_salt_personal(&base_state,
63 NULL, 0, // No key.
caa0348f 64 (512/N)*N/8,
6d25662f
JG
65 NULL, // No salt.
66 personalization);
67}
68
caa0348f
JG
69void GenerateHash(const eh_HashState& base_state, eh_index g,
70 unsigned char* hash, size_t hLen)
71{
72 eh_HashState state;
73 state = base_state;
caa0348f 74 eh_index lei = htole32(g);
e273f05d
JG
75 crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
76 sizeof(eh_index));
caa0348f
JG
77 crypto_generichash_blake2b_final(&state, hash, hLen);
78}
79
881ffbfc
JG
80void ExpandArray(const unsigned char* in, size_t in_len,
81 unsigned char* out, size_t out_len,
20abe208 82 size_t bit_len, size_t byte_pad)
881ffbfc
JG
83{
84 assert(bit_len >= 8);
85 assert(8*sizeof(uint32_t) >= 7+bit_len);
86
20abe208 87 size_t out_width { (bit_len+7)/8 + byte_pad };
881ffbfc
JG
88 assert(out_len == 8*out_width*in_len/bit_len);
89
90 uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 };
91
92 // The acc_bits least-significant bits of acc_value represent a bit sequence
93 // in big-endian order.
94 size_t acc_bits = 0;
95 uint32_t acc_value = 0;
96
97 size_t j = 0;
98 for (size_t i = 0; i < in_len; i++) {
99 acc_value = (acc_value << 8) | in[i];
100 acc_bits += 8;
101
102 // When we have bit_len or more bits in the accumulator, write the next
103 // output element.
104 if (acc_bits >= bit_len) {
105 acc_bits -= bit_len;
20abe208
JG
106 for (size_t x = 0; x < byte_pad; x++) {
107 out[j+x] = 0;
108 }
109 for (size_t x = byte_pad; x < out_width; x++) {
881ffbfc
JG
110 out[j+x] = (
111 // Big-endian
112 acc_value >> (acc_bits+(8*(out_width-x-1)))
113 ) & (
114 // Apply bit_len_mask across byte boundaries
115 (bit_len_mask >> (8*(out_width-x-1))) & 0xFF
116 );
117 }
118 j += out_width;
119 }
120 }
121}
122
123void CompressArray(const unsigned char* in, size_t in_len,
124 unsigned char* out, size_t out_len,
20abe208 125 size_t bit_len, size_t byte_pad)
881ffbfc
JG
126{
127 assert(bit_len >= 8);
128 assert(8*sizeof(uint32_t) >= 7+bit_len);
129
20abe208 130 size_t in_width { (bit_len+7)/8 + byte_pad };
881ffbfc
JG
131 assert(out_len == bit_len*in_len/(8*in_width));
132
133 uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 };
134
135 // The acc_bits least-significant bits of acc_value represent a bit sequence
136 // in big-endian order.
137 size_t acc_bits = 0;
138 uint32_t acc_value = 0;
139
140 size_t j = 0;
141 for (size_t i = 0; i < out_len; i++) {
142 // When we have fewer than 8 bits left in the accumulator, read the next
143 // input element.
144 if (acc_bits < 8) {
145 acc_value = acc_value << bit_len;
20abe208 146 for (size_t x = byte_pad; x < in_width; x++) {
881ffbfc
JG
147 acc_value = acc_value | (
148 (
149 // Apply bit_len_mask across byte boundaries
150 in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF)
151 ) << (8*(in_width-x-1))); // Big-endian
152 }
153 j += in_width;
154 acc_bits += bit_len;
155 }
156
157 acc_bits -= 8;
158 out[i] = (acc_value >> acc_bits) & 0xFF;
159 }
160}
161
09e9a329
JG
162// Big-endian so that lexicographic array comparison is equivalent to integer
163// comparison
d4d76536 164void EhIndexToArray(const eh_index i, unsigned char* array)
29d9986c 165{
bcf79c78 166 BOOST_STATIC_ASSERT(sizeof(eh_index) == 4);
933cb4cd
JG
167 eh_index bei = htobe32(i);
168 memcpy(array, &bei, sizeof(eh_index));
29d9986c
JG
169}
170
09e9a329
JG
171// Big-endian so that lexicographic array comparison is equivalent to integer
172// comparison
d4d76536 173eh_index ArrayToEhIndex(const unsigned char* array)
29d9986c 174{
bcf79c78 175 BOOST_STATIC_ASSERT(sizeof(eh_index) == 4);
933cb4cd
JG
176 eh_index bei;
177 memcpy(&bei, array, sizeof(eh_index));
178 return be32toh(bei);
29d9986c
JG
179}
180
d4d76536 181eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen)
c92c1f60
JG
182{
183 // Truncate to 8 bits
bcf79c78 184 BOOST_STATIC_ASSERT(sizeof(eh_trunc) == 1);
c92c1f60
JG
185 return (i >> (ilen - 8)) & 0xff;
186}
187
d4d76536 188eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int ilen)
c92c1f60
JG
189{
190 eh_index i{t};
191 return (i << (ilen - 8)) | r;
192}
193
5be6abbf
JG
194std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
195 size_t cBitLen)
6d25662f 196{
5be6abbf
JG
197 assert(((cBitLen+1)+7)/8 <= sizeof(eh_index));
198 size_t lenIndices { 8*sizeof(eh_index)*minimal.size()/(cBitLen+1) };
199 size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
200 std::vector<unsigned char> array(lenIndices);
201 ExpandArray(minimal.data(), minimal.size(),
202 array.data(), lenIndices, cBitLen+1, bytePad);
203 std::vector<eh_index> ret;
204 for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
205 ret.push_back(ArrayToEhIndex(array.data()+i));
206 }
207 return ret;
208}
036dcbd9 209
5be6abbf
JG
210std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
211 size_t cBitLen)
212{
213 assert(((cBitLen+1)+7)/8 <= sizeof(eh_index));
214 size_t lenIndices { indices.size()*sizeof(eh_index) };
215 size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) };
216 size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
217 std::vector<unsigned char> array(lenIndices);
218 for (int i = 0; i < indices.size(); i++) {
219 EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index)));
036dcbd9 220 }
5be6abbf
JG
221 std::vector<unsigned char> ret(minLen);
222 CompressArray(array.data(), lenIndices,
223 ret.data(), minLen, cBitLen+1, bytePad);
224 return ret;
225}
226
d4d76536 227template<size_t WIDTH>
caa0348f
JG
228StepRow<WIDTH>::StepRow(const unsigned char* hashIn, size_t hInLen,
229 size_t hLen, size_t cBitLen)
6d25662f 230{
caa0348f
JG
231 assert(hLen <= WIDTH);
232 ExpandArray(hashIn, hInLen, hash, hLen, cBitLen);
6d25662f
JG
233}
234
d4d76536
JG
235template<size_t WIDTH> template<size_t W>
236StepRow<WIDTH>::StepRow(const StepRow<W>& a)
6d25662f 237{
bcf79c78 238 BOOST_STATIC_ASSERT(W <= WIDTH);
d4d76536 239 std::copy(a.hash, a.hash+W, hash);
6d25662f
JG
240}
241
d4d76536 242template<size_t WIDTH>
caa0348f
JG
243FullStepRow<WIDTH>::FullStepRow(const unsigned char* hashIn, size_t hInLen,
244 size_t hLen, size_t cBitLen, eh_index i) :
245 StepRow<WIDTH> {hashIn, hInLen, hLen, cBitLen}
6d25662f 246{
036dcbd9 247 EhIndexToArray(i, hash+hLen);
6d25662f
JG
248}
249
d4d76536
JG
250template<size_t WIDTH> template<size_t W>
251FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
252 StepRow<WIDTH> {a}
a3361e77 253{
d4d76536
JG
254 assert(len+lenIndices <= W);
255 assert(len-trim+(2*lenIndices) <= WIDTH);
256 for (int i = trim; i < len; i++)
257 hash[i-trim] = a.hash[i] ^ b.hash[i];
d07cf629 258 if (a.IndicesBefore(b, len, lenIndices)) {
d4d76536
JG
259 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
260 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 261 } else {
d4d76536
JG
262 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim);
263 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 264 }
6d25662f
JG
265}
266
d4d76536
JG
267template<size_t WIDTH>
268FullStepRow<WIDTH>& FullStepRow<WIDTH>::operator=(const FullStepRow<WIDTH>& a)
6d25662f 269{
d4d76536 270 std::copy(a.hash, a.hash+WIDTH, hash);
a683cc85 271 return *this;
6d25662f
JG
272}
273
d4d76536
JG
274template<size_t WIDTH>
275bool StepRow<WIDTH>::IsZero(size_t len)
6d25662f 276{
447444ae
JG
277 // This doesn't need to be constant time.
278 for (int i = 0; i < len; i++) {
279 if (hash[i] != 0)
280 return false;
281 }
282 return true;
6d25662f
JG
283}
284
d4d76536 285template<size_t WIDTH>
5be6abbf
JG
286std::vector<unsigned char> FullStepRow<WIDTH>::GetIndices(size_t len, size_t lenIndices,
287 size_t cBitLen) const
29d9986c 288{
5be6abbf
JG
289 assert(((cBitLen+1)+7)/8 <= sizeof(eh_index));
290 size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) };
291 size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
292 std::vector<unsigned char> ret(minLen);
293 CompressArray(hash+len, lenIndices, ret.data(), minLen, cBitLen+1, bytePad);
29d9986c
JG
294 return ret;
295}
296
d4d76536
JG
297template<size_t WIDTH>
298bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l)
6d25662f 299{
447444ae
JG
300 // This doesn't need to be constant time.
301 for (int j = 0; j < l; j++) {
302 if (a.hash[j] != b.hash[j])
303 return false;
304 }
305 return true;
6d25662f
JG
306}
307
d4d76536 308template<size_t WIDTH>
caa0348f
JG
309TruncatedStepRow<WIDTH>::TruncatedStepRow(const unsigned char* hashIn, size_t hInLen,
310 size_t hLen, size_t cBitLen,
311 eh_index i, unsigned int ilen) :
312 StepRow<WIDTH> {hashIn, hInLen, hLen, cBitLen}
6d25662f 313{
036dcbd9 314 hash[hLen] = TruncateIndex(i, ilen);
6d25662f
JG
315}
316
d4d76536
JG
317template<size_t WIDTH> template<size_t W>
318TruncatedStepRow<WIDTH>::TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
319 StepRow<WIDTH> {a}
c92c1f60 320{
d4d76536
JG
321 assert(len+lenIndices <= W);
322 assert(len-trim+(2*lenIndices) <= WIDTH);
323 for (int i = trim; i < len; i++)
324 hash[i-trim] = a.hash[i] ^ b.hash[i];
325 if (a.IndicesBefore(b, len, lenIndices)) {
326 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
327 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 328 } else {
d4d76536
JG
329 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim);
330 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 331 }
c92c1f60
JG
332}
333
d4d76536
JG
334template<size_t WIDTH>
335TruncatedStepRow<WIDTH>& TruncatedStepRow<WIDTH>::operator=(const TruncatedStepRow<WIDTH>& a)
39f5cb35 336{
d4d76536 337 std::copy(a.hash, a.hash+WIDTH, hash);
a683cc85 338 return *this;
39f5cb35
JG
339}
340
d4d76536 341template<size_t WIDTH>
215b9e13 342std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t len, size_t lenIndices) const
39f5cb35 343{
10310478 344 std::shared_ptr<eh_trunc> p (new eh_trunc[lenIndices], std::default_delete<eh_trunc[]>());
215b9e13 345 std::copy(hash+len, hash+len+lenIndices, p.get());
39f5cb35
JG
346 return p;
347}
348
e9574728 349template<unsigned int N, unsigned int K>
51eb5273 350bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
5be6abbf 351 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273 352 const std::function<bool(EhSolverCancelCheck)> cancelled)
6d25662f 353{
e9574728 354 eh_index init_size { 1 << (CollisionBitLength + 1) };
6d25662f
JG
355
356 // 1) Generate first list
357 LogPrint("pow", "Generating first list\n");
036dcbd9 358 size_t hashLen = HashLength;
d4d76536
JG
359 size_t lenIndices = sizeof(eh_index);
360 std::vector<FullStepRow<FullWidth>> X;
6d25662f 361 X.reserve(init_size);
caa0348f
JG
362 unsigned char tmpHash[HashOutput];
363 for (eh_index g = 0; X.size() < init_size; g++) {
364 GenerateHash(base_state, g, tmpHash, HashOutput);
365 for (eh_index i = 0; i < IndicesPerHashOutput && X.size() < init_size; i++) {
366 X.emplace_back(tmpHash+(i*N/8), N/8, HashLength,
367 CollisionBitLength, (g*IndicesPerHashOutput)+i);
368 }
5a360a5c 369 if (cancelled(ListGeneration)) throw solver_cancelled;
6d25662f
JG
370 }
371
372 // 3) Repeat step 2 until 2n/(k+1) bits remain
e9574728 373 for (int r = 1; r < K && X.size() > 0; r++) {
6d25662f
JG
374 LogPrint("pow", "Round %d:\n", r);
375 // 2a) Sort the list
376 LogPrint("pow", "- Sorting list\n");
d151ab4f 377 std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength));
5b4ebcd5 378 if (cancelled(ListSorting)) throw solver_cancelled;
6d25662f
JG
379
380 LogPrint("pow", "- Finding collisions\n");
381 int i = 0;
382 int posFree = 0;
d4d76536 383 std::vector<FullStepRow<FullWidth>> Xc;
6d25662f
JG
384 while (i < X.size() - 1) {
385 // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
386 int j = 1;
387 while (i+j < X.size() &&
e9574728 388 HasCollision(X[i], X[i+j], CollisionByteLength)) {
6d25662f
JG
389 j++;
390 }
391
392 // 2c) Calculate tuples (X_i ^ X_j, (i, j))
393 for (int l = 0; l < j - 1; l++) {
394 for (int m = l + 1; m < j; m++) {
d4d76536
JG
395 if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
396 Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength);
6d25662f
JG
397 }
398 }
399 }
400
401 // 2d) Store tuples on the table in-place if possible
402 while (posFree < i+j && Xc.size() > 0) {
403 X[posFree++] = Xc.back();
404 Xc.pop_back();
405 }
406
407 i += j;
5b4ebcd5 408 if (cancelled(ListColliding)) throw solver_cancelled;
6d25662f
JG
409 }
410
411 // 2e) Handle edge case where final table entry has no collision
412 while (posFree < X.size() && Xc.size() > 0) {
413 X[posFree++] = Xc.back();
414 Xc.pop_back();
415 }
416
417 if (Xc.size() > 0) {
418 // 2f) Add overflow to end of table
419 X.insert(X.end(), Xc.begin(), Xc.end());
420 } else if (posFree < X.size()) {
421 // 2g) Remove empty space at the end
422 X.erase(X.begin()+posFree, X.end());
423 X.shrink_to_fit();
424 }
639c4004
JG
425
426 hashLen -= CollisionByteLength;
d4d76536 427 lenIndices *= 2;
5b4ebcd5 428 if (cancelled(RoundEnd)) throw solver_cancelled;
6d25662f
JG
429 }
430
431 // k+1) Find a collision on last 2n(k+1) bits
432 LogPrint("pow", "Final round:\n");
6d25662f
JG
433 if (X.size() > 1) {
434 LogPrint("pow", "- Sorting list\n");
639c4004 435 std::sort(X.begin(), X.end(), CompareSR(hashLen));
5b4ebcd5 436 if (cancelled(FinalSorting)) throw solver_cancelled;
6d25662f 437 LogPrint("pow", "- Finding collisions\n");
1bb40a42
JG
438 int i = 0;
439 while (i < X.size() - 1) {
440 int j = 1;
441 while (i+j < X.size() &&
442 HasCollision(X[i], X[i+j], hashLen)) {
443 j++;
6d25662f 444 }
1bb40a42
JG
445
446 for (int l = 0; l < j - 1; l++) {
447 for (int m = l + 1; m < j; m++) {
448 FullStepRow<FinalFullWidth> res(X[i+l], X[i+m], hashLen, lenIndices, 0);
c6a7e897
DH
449 if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
450 auto soln = res.GetIndices(hashLen, 2*lenIndices, CollisionBitLength);
451 assert(soln.size() == equihash_solution_size(N, K));
452 if (validBlock(soln)) {
453 return true;
454 }
1bb40a42
JG
455 }
456 }
457 }
458
459 i += j;
5b4ebcd5 460 if (cancelled(FinalColliding)) throw solver_cancelled;
6d25662f
JG
461 }
462 } else
463 LogPrint("pow", "- List is empty\n");
464
51eb5273 465 return false;
6d25662f
JG
466}
467
d4d76536
JG
468template<size_t WIDTH>
469void 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)
c92c1f60
JG
470{
471 int i = 0;
472 int posFree = 0;
d4d76536 473 std::vector<FullStepRow<WIDTH>> Xc;
c92c1f60
JG
474 while (i < X.size() - 1) {
475 // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
476 int j = 1;
477 while (i+j < X.size() &&
478 HasCollision(X[i], X[i+j], clen)) {
479 j++;
480 }
481
482 // 2c) Calculate tuples (X_i ^ X_j, (i, j))
483 for (int l = 0; l < j - 1; l++) {
484 for (int m = l + 1; m < j; m++) {
d4d76536
JG
485 if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) {
486 if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) {
487 Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen);
488 } else if (IsValidBranch(X[i+m], hlen, ilen, lt) && IsValidBranch(X[i+l], hlen, ilen, rt)) {
489 Xc.emplace_back(X[i+m], X[i+l], hlen, lenIndices, clen);
c92c1f60
JG
490 }
491 }
492 }
493 }
494
495 // 2d) Store tuples on the table in-place if possible
496 while (posFree < i+j && Xc.size() > 0) {
497 X[posFree++] = Xc.back();
498 Xc.pop_back();
499 }
500
501 i += j;
502 }
503
504 // 2e) Handle edge case where final table entry has no collision
505 while (posFree < X.size() && Xc.size() > 0) {
506 X[posFree++] = Xc.back();
507 Xc.pop_back();
508 }
509
510 if (Xc.size() > 0) {
511 // 2f) Add overflow to end of table
512 X.insert(X.end(), Xc.begin(), Xc.end());
513 } else if (posFree < X.size()) {
514 // 2g) Remove empty space at the end
515 X.erase(X.begin()+posFree, X.end());
516 X.shrink_to_fit();
517 }
518}
519
e9574728 520template<unsigned int N, unsigned int K>
51eb5273 521bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
5be6abbf 522 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273 523 const std::function<bool(EhSolverCancelCheck)> cancelled)
c92c1f60 524{
e9574728 525 eh_index init_size { 1 << (CollisionBitLength + 1) };
1655db28 526 eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
c92c1f60
JG
527
528 // First run the algorithm with truncated indices
529
6b4f4475 530 const eh_index soln_size { 1 << K };
215b9e13 531 std::vector<std::shared_ptr<eh_trunc>> partialSolns;
1655db28 532 int invalidCount = 0;
c92c1f60
JG
533 {
534
535 // 1) Generate first list
536 LogPrint("pow", "Generating first list\n");
036dcbd9 537 size_t hashLen = HashLength;
d4d76536
JG
538 size_t lenIndices = sizeof(eh_trunc);
539 std::vector<TruncatedStepRow<TruncatedWidth>> Xt;
c92c1f60 540 Xt.reserve(init_size);
caa0348f
JG
541 unsigned char tmpHash[HashOutput];
542 for (eh_index g = 0; Xt.size() < init_size; g++) {
543 GenerateHash(base_state, g, tmpHash, HashOutput);
544 for (eh_index i = 0; i < IndicesPerHashOutput && Xt.size() < init_size; i++) {
545 Xt.emplace_back(tmpHash+(i*N/8), N/8, HashLength, CollisionBitLength,
546 (g*IndicesPerHashOutput)+i, CollisionBitLength + 1);
547 }
5a360a5c 548 if (cancelled(ListGeneration)) throw solver_cancelled;
c92c1f60
JG
549 }
550
551 // 3) Repeat step 2 until 2n/(k+1) bits remain
e9574728 552 for (int r = 1; r < K && Xt.size() > 0; r++) {
c92c1f60
JG
553 LogPrint("pow", "Round %d:\n", r);
554 // 2a) Sort the list
555 LogPrint("pow", "- Sorting list\n");
d151ab4f 556 std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength));
5b4ebcd5 557 if (cancelled(ListSorting)) throw solver_cancelled;
c92c1f60
JG
558
559 LogPrint("pow", "- Finding collisions\n");
560 int i = 0;
561 int posFree = 0;
d4d76536 562 std::vector<TruncatedStepRow<TruncatedWidth>> Xc;
c92c1f60
JG
563 while (i < Xt.size() - 1) {
564 // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
565 int j = 1;
566 while (i+j < Xt.size() &&
e9574728 567 HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) {
c92c1f60
JG
568 j++;
569 }
570
571 // 2c) Calculate tuples (X_i ^ X_j, (i, j))
d4af3dd5 572 bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen));
c92c1f60
JG
573 for (int l = 0; l < j - 1; l++) {
574 for (int m = l + 1; m < j; m++) {
575 // We truncated, so don't check for distinct indices here
d4af3dd5
JG
576 TruncatedStepRow<TruncatedWidth> Xi {Xt[i+l], Xt[i+m],
577 hashLen, lenIndices,
578 CollisionByteLength};
579 if (!(Xi.IsZero(hashLen-CollisionByteLength) &&
6b4f4475
JG
580 IsProbablyDuplicate<soln_size>(Xi.GetTruncatedIndices(hashLen-CollisionByteLength, 2*lenIndices),
581 2*lenIndices))) {
d4af3dd5
JG
582 Xc.emplace_back(Xi);
583 }
c92c1f60
JG
584 }
585 }
586
587 // 2d) Store tuples on the table in-place if possible
588 while (posFree < i+j && Xc.size() > 0) {
589 Xt[posFree++] = Xc.back();
590 Xc.pop_back();
591 }
592
593 i += j;
5b4ebcd5 594 if (cancelled(ListColliding)) throw solver_cancelled;
c92c1f60
JG
595 }
596
597 // 2e) Handle edge case where final table entry has no collision
598 while (posFree < Xt.size() && Xc.size() > 0) {
599 Xt[posFree++] = Xc.back();
600 Xc.pop_back();
601 }
602
603 if (Xc.size() > 0) {
604 // 2f) Add overflow to end of table
605 Xt.insert(Xt.end(), Xc.begin(), Xc.end());
606 } else if (posFree < Xt.size()) {
607 // 2g) Remove empty space at the end
608 Xt.erase(Xt.begin()+posFree, Xt.end());
609 Xt.shrink_to_fit();
610 }
639c4004
JG
611
612 hashLen -= CollisionByteLength;
d4d76536 613 lenIndices *= 2;
5b4ebcd5 614 if (cancelled(RoundEnd)) throw solver_cancelled;
c92c1f60
JG
615 }
616
617 // k+1) Find a collision on last 2n(k+1) bits
618 LogPrint("pow", "Final round:\n");
619 if (Xt.size() > 1) {
620 LogPrint("pow", "- Sorting list\n");
639c4004 621 std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen));
5b4ebcd5 622 if (cancelled(FinalSorting)) throw solver_cancelled;
c92c1f60 623 LogPrint("pow", "- Finding collisions\n");
1bb40a42
JG
624 int i = 0;
625 while (i < Xt.size() - 1) {
626 int j = 1;
627 while (i+j < Xt.size() &&
628 HasCollision(Xt[i], Xt[i+j], hashLen)) {
629 j++;
c92c1f60 630 }
1bb40a42
JG
631
632 for (int l = 0; l < j - 1; l++) {
633 for (int m = l + 1; m < j; m++) {
3c654f38
JG
634 TruncatedStepRow<FinalTruncatedWidth> res(Xt[i+l], Xt[i+m],
635 hashLen, lenIndices, 0);
636 auto soln = res.GetTruncatedIndices(hashLen, 2*lenIndices);
637 if (!IsProbablyDuplicate<soln_size>(soln, 2*lenIndices)) {
638 partialSolns.push_back(soln);
639 }
1bb40a42
JG
640 }
641 }
642
643 i += j;
215b9e13 644 if (cancelled(FinalColliding)) throw solver_cancelled;
c92c1f60
JG
645 }
646 } else
647 LogPrint("pow", "- List is empty\n");
648
649 } // Ensure Xt goes out of scope and is destroyed
650
651 LogPrint("pow", "Found %d partial solutions\n", partialSolns.size());
652
653 // Now for each solution run the algorithm again to recreate the indices
654 LogPrint("pow", "Culling solutions\n");
215b9e13 655 for (std::shared_ptr<eh_trunc> partialSoln : partialSolns) {
5be6abbf 656 std::set<std::vector<unsigned char>> solns;
0a66f013
JG
657 size_t hashLen;
658 size_t lenIndices;
caa0348f 659 unsigned char tmpHash[HashOutput];
0a66f013
JG
660 std::vector<boost::optional<std::vector<FullStepRow<FinalFullWidth>>>> X;
661 X.reserve(K+1);
662
663 // 3) Repeat steps 1 and 2 for each partial index
39f5cb35 664 for (eh_index i = 0; i < soln_size; i++) {
0a66f013
JG
665 // 1) Generate first list of possibilities
666 std::vector<FullStepRow<FinalFullWidth>> icv;
667 icv.reserve(recreate_size);
c92c1f60 668 for (eh_index j = 0; j < recreate_size; j++) {
215b9e13 669 eh_index newIndex { UntruncateIndex(partialSoln.get()[i], j, CollisionBitLength + 1) };
caa0348f
JG
670 if (j == 0 || newIndex % IndicesPerHashOutput == 0) {
671 GenerateHash(base_state, newIndex/IndicesPerHashOutput,
672 tmpHash, HashOutput);
673 }
674 icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * N/8),
675 N/8, HashLength, CollisionBitLength, newIndex);
215b9e13 676 if (cancelled(PartialGeneration)) throw solver_cancelled;
c92c1f60 677 }
0a66f013 678 boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv;
c92c1f60
JG
679
680 // 2a) For each pair of lists:
036dcbd9 681 hashLen = HashLength;
0a66f013
JG
682 lenIndices = sizeof(eh_index);
683 size_t rti = i;
684 for (size_t r = 0; r <= K; r++) {
685 // 2b) Until we are at the top of a subtree:
686 if (r < X.size()) {
687 if (X[r]) {
688 // 2c) Merge the lists
689 ic->reserve(ic->size() + X[r]->size());
690 ic->insert(ic->end(), X[r]->begin(), X[r]->end());
691 std::sort(ic->begin(), ic->end(), CompareSR(hashLen));
215b9e13 692 if (cancelled(PartialSorting)) throw solver_cancelled;
0a66f013
JG
693 size_t lti = rti-(1<<r);
694 CollideBranches(*ic, hashLen, lenIndices,
695 CollisionByteLength,
696 CollisionBitLength + 1,
215b9e13 697 partialSoln.get()[lti], partialSoln.get()[rti]);
0a66f013
JG
698
699 // 2d) Check if this has become an invalid solution
700 if (ic->size() == 0)
701 goto invalidsolution;
702
703 X[r] = boost::none;
704 hashLen -= CollisionByteLength;
705 lenIndices *= 2;
706 rti = lti;
707 } else {
708 X[r] = *ic;
709 break;
710 }
711 } else {
712 X.push_back(ic);
713 break;
714 }
215b9e13 715 if (cancelled(PartialSubtreeEnd)) throw solver_cancelled;
c92c1f60 716 }
215b9e13 717 if (cancelled(PartialIndexEnd)) throw solver_cancelled;
c92c1f60
JG
718 }
719
720 // We are at the top of the tree
0a66f013
JG
721 assert(X.size() == K+1);
722 for (FullStepRow<FinalFullWidth> row : *X[K]) {
c6a7e897
DH
723 auto soln = row.GetIndices(hashLen, lenIndices, CollisionBitLength);
724 assert(soln.size() == equihash_solution_size(N, K));
725 solns.insert(soln);
23acf867
JG
726 }
727 for (auto soln : solns) {
728 if (validBlock(soln))
51eb5273 729 return true;
c92c1f60 730 }
215b9e13 731 if (cancelled(PartialEnd)) throw solver_cancelled;
2dbabb11 732 continue;
c92c1f60
JG
733
734invalidsolution:
735 invalidCount++;
736 }
737 LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount);
738
51eb5273 739 return false;
c92c1f60
JG
740}
741
e9574728 742template<unsigned int N, unsigned int K>
5be6abbf 743bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln)
6d25662f 744{
5be6abbf
JG
745 if (soln.size() != SolutionWidth) {
746 LogPrint("pow", "Invalid solution length: %d (expected %d)\n",
747 soln.size(), SolutionWidth);
6d25662f
JG
748 return false;
749 }
750
d4d76536 751 std::vector<FullStepRow<FinalFullWidth>> X;
5be6abbf 752 X.reserve(1 << K);
caa0348f 753 unsigned char tmpHash[HashOutput];
5be6abbf 754 for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) {
caa0348f
JG
755 GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput);
756 X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8),
757 N/8, HashLength, CollisionBitLength, i);
6d25662f
JG
758 }
759
036dcbd9 760 size_t hashLen = HashLength;
d4d76536 761 size_t lenIndices = sizeof(eh_index);
6d25662f 762 while (X.size() > 1) {
d4d76536 763 std::vector<FullStepRow<FinalFullWidth>> Xc;
6d25662f 764 for (int i = 0; i < X.size(); i += 2) {
e9574728 765 if (!HasCollision(X[i], X[i+1], CollisionByteLength)) {
6d25662f 766 LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
d4d76536
JG
767 LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen));
768 LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen));
6d25662f
JG
769 return false;
770 }
d07cf629 771 if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) {
6d25662f 772 LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
cc6c9ec0 773 return false;
6d25662f 774 }
d4d76536 775 if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) {
6d25662f
JG
776 LogPrint("pow", "Invalid solution: duplicate indices\n");
777 return false;
778 }
d4d76536 779 Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength);
6d25662f
JG
780 }
781 X = Xc;
d4d76536
JG
782 hashLen -= CollisionByteLength;
783 lenIndices *= 2;
6d25662f
JG
784 }
785
786 assert(X.size() == 1);
d4d76536 787 return X[0].IsZero(hashLen);
6d25662f 788}
e9574728 789
ae37d2a4
JG
790// Explicit instantiations for Equihash<96,3>
791template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
51eb5273 792template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state,
5be6abbf 793 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273
JG
794 const std::function<bool(EhSolverCancelCheck)> cancelled);
795template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state,
5be6abbf 796 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273 797 const std::function<bool(EhSolverCancelCheck)> cancelled);
5be6abbf 798template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
ae37d2a4 799
eeb41778
JG
800// Explicit instantiations for Equihash<200,9>
801template int Equihash<200,9>::InitialiseState(eh_HashState& base_state);
802template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state,
5be6abbf 803 const std::function<bool(std::vector<unsigned char>)> validBlock,
eeb41778
JG
804 const std::function<bool(EhSolverCancelCheck)> cancelled);
805template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state,
5be6abbf 806 const std::function<bool(std::vector<unsigned char>)> validBlock,
eeb41778 807 const std::function<bool(EhSolverCancelCheck)> cancelled);
5be6abbf 808template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
eeb41778 809
e9574728
JG
810// Explicit instantiations for Equihash<96,5>
811template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
51eb5273 812template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state,
5be6abbf 813 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273
JG
814 const std::function<bool(EhSolverCancelCheck)> cancelled);
815template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state,
5be6abbf 816 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273 817 const std::function<bool(EhSolverCancelCheck)> cancelled);
5be6abbf 818template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
e9574728
JG
819
820// Explicit instantiations for Equihash<48,5>
821template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
51eb5273 822template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state,
5be6abbf 823 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273
JG
824 const std::function<bool(EhSolverCancelCheck)> cancelled);
825template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
5be6abbf 826 const std::function<bool(std::vector<unsigned char>)> validBlock,
51eb5273 827 const std::function<bool(EhSolverCancelCheck)> cancelled);
5be6abbf 828template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
This page took 0.201692 seconds and 4 git commands to generate.