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