]> Git Repo - VerusCoin.git/blame - src/crypto/equihash.cpp
Add tests that exercise the cancellation code branches
[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
2dbabb11
JG
24EhSolverCancelledException solver_cancelled;
25
e9574728
JG
26template<unsigned int N, unsigned int K>
27int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
6d25662f 28{
09e9a329
JG
29 uint32_t le_N = htole32(N);
30 uint32_t le_K = htole32(K);
6d25662f 31 unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
a6dcf2ee 32 memcpy(personalization, "ZcashPoW", 8);
09e9a329
JG
33 memcpy(personalization+8, &le_N, 4);
34 memcpy(personalization+12, &le_K, 4);
6d25662f
JG
35 return crypto_generichash_blake2b_init_salt_personal(&base_state,
36 NULL, 0, // No key.
e9574728 37 N/8,
6d25662f
JG
38 NULL, // No salt.
39 personalization);
40}
41
09e9a329
JG
42// Big-endian so that lexicographic array comparison is equivalent to integer
43// comparison
d4d76536 44void EhIndexToArray(const eh_index i, unsigned char* array)
29d9986c
JG
45{
46 assert(sizeof(eh_index) == 4);
933cb4cd
JG
47 eh_index bei = htobe32(i);
48 memcpy(array, &bei, sizeof(eh_index));
29d9986c
JG
49}
50
09e9a329
JG
51// Big-endian so that lexicographic array comparison is equivalent to integer
52// comparison
d4d76536 53eh_index ArrayToEhIndex(const unsigned char* array)
29d9986c
JG
54{
55 assert(sizeof(eh_index) == 4);
933cb4cd
JG
56 eh_index bei;
57 memcpy(&bei, array, sizeof(eh_index));
58 return be32toh(bei);
29d9986c
JG
59}
60
d4d76536 61eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen)
c92c1f60
JG
62{
63 // Truncate to 8 bits
64 assert(sizeof(eh_trunc) == 1);
65 return (i >> (ilen - 8)) & 0xff;
66}
67
d4d76536 68eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int ilen)
c92c1f60
JG
69{
70 eh_index i{t};
71 return (i << (ilen - 8)) | r;
72}
73
d4d76536
JG
74template<size_t WIDTH>
75StepRow<WIDTH>::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i)
6d25662f
JG
76{
77 eh_HashState state;
78 state = base_state;
a6dcf2ee 79 unsigned char array[sizeof(eh_index)];
933cb4cd
JG
80 eh_index lei = htole32(i);
81 memcpy(array, &lei, sizeof(eh_index));
a6dcf2ee 82 crypto_generichash_blake2b_update(&state, array, sizeof(eh_index));
6d25662f 83 crypto_generichash_blake2b_final(&state, hash, n/8);
6d25662f
JG
84}
85
d4d76536
JG
86template<size_t WIDTH> template<size_t W>
87StepRow<WIDTH>::StepRow(const StepRow<W>& a)
6d25662f 88{
d4d76536
JG
89 assert(W <= WIDTH);
90 std::copy(a.hash, a.hash+W, hash);
6d25662f
JG
91}
92
d4d76536
JG
93template<size_t WIDTH>
94FullStepRow<WIDTH>::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
95 StepRow<WIDTH> {n, base_state, i}
6d25662f 96{
d4d76536 97 EhIndexToArray(i, hash+(n/8));
6d25662f
JG
98}
99
d4d76536
JG
100template<size_t WIDTH> template<size_t W>
101FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
102 StepRow<WIDTH> {a}
a3361e77 103{
d4d76536
JG
104 assert(len+lenIndices <= W);
105 assert(len-trim+(2*lenIndices) <= WIDTH);
106 for (int i = trim; i < len; i++)
107 hash[i-trim] = a.hash[i] ^ b.hash[i];
d07cf629 108 if (a.IndicesBefore(b, len, lenIndices)) {
d4d76536
JG
109 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
110 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 111 } else {
d4d76536
JG
112 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim);
113 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 114 }
6d25662f
JG
115}
116
d4d76536
JG
117template<size_t WIDTH>
118FullStepRow<WIDTH>& FullStepRow<WIDTH>::operator=(const FullStepRow<WIDTH>& a)
6d25662f 119{
d4d76536 120 std::copy(a.hash, a.hash+WIDTH, hash);
a683cc85 121 return *this;
6d25662f
JG
122}
123
d4d76536
JG
124template<size_t WIDTH>
125bool StepRow<WIDTH>::IsZero(size_t len)
6d25662f 126{
447444ae
JG
127 // This doesn't need to be constant time.
128 for (int i = 0; i < len; i++) {
129 if (hash[i] != 0)
130 return false;
131 }
132 return true;
6d25662f
JG
133}
134
d4d76536
JG
135template<size_t WIDTH>
136std::vector<eh_index> FullStepRow<WIDTH>::GetIndices(size_t len, size_t lenIndices) const
29d9986c
JG
137{
138 std::vector<eh_index> ret;
139 for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
140 ret.push_back(ArrayToEhIndex(hash+len+i));
141 }
142 return ret;
143}
144
d4d76536
JG
145template<size_t WIDTH>
146bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l)
6d25662f 147{
447444ae
JG
148 // This doesn't need to be constant time.
149 for (int j = 0; j < l; j++) {
150 if (a.hash[j] != b.hash[j])
151 return false;
152 }
153 return true;
6d25662f
JG
154}
155
d4d76536
JG
156template<size_t WIDTH>
157TruncatedStepRow<WIDTH>::TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen) :
158 StepRow<WIDTH> {n, base_state, i}
6d25662f 159{
d4d76536 160 hash[n/8] = TruncateIndex(i, ilen);
6d25662f
JG
161}
162
d4d76536
JG
163template<size_t WIDTH> template<size_t W>
164TruncatedStepRow<WIDTH>::TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
165 StepRow<WIDTH> {a}
c92c1f60 166{
d4d76536
JG
167 assert(len+lenIndices <= W);
168 assert(len-trim+(2*lenIndices) <= WIDTH);
169 for (int i = trim; i < len; i++)
170 hash[i-trim] = a.hash[i] ^ b.hash[i];
171 if (a.IndicesBefore(b, len, lenIndices)) {
172 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
173 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 174 } else {
d4d76536
JG
175 std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim);
176 std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices);
a683cc85 177 }
c92c1f60
JG
178}
179
d4d76536
JG
180template<size_t WIDTH>
181TruncatedStepRow<WIDTH>& TruncatedStepRow<WIDTH>::operator=(const TruncatedStepRow<WIDTH>& a)
39f5cb35 182{
d4d76536 183 std::copy(a.hash, a.hash+WIDTH, hash);
a683cc85 184 return *this;
39f5cb35
JG
185}
186
d4d76536
JG
187template<size_t WIDTH>
188eh_trunc* TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t len, size_t lenIndices) const
39f5cb35 189{
39f5cb35
JG
190 eh_trunc* p = new eh_trunc[lenIndices];
191 std::copy(hash+len, hash+len+lenIndices, p);
192 return p;
193}
194
e9574728 195template<unsigned int N, unsigned int K>
5b4ebcd5 196std::set<std::vector<eh_index>> Equihash<N,K>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled)
6d25662f 197{
e9574728 198 eh_index init_size { 1 << (CollisionBitLength + 1) };
6d25662f
JG
199
200 // 1) Generate first list
201 LogPrint("pow", "Generating first list\n");
639c4004 202 size_t hashLen = N/8;
d4d76536
JG
203 size_t lenIndices = sizeof(eh_index);
204 std::vector<FullStepRow<FullWidth>> X;
6d25662f
JG
205 X.reserve(init_size);
206 for (eh_index i = 0; i < init_size; i++) {
e9574728 207 X.emplace_back(N, base_state, i);
2dbabb11 208 // Slow down checking to prevent segfaults (??)
5b4ebcd5 209 if (i % 10000 == 0 && cancelled(ListGeneration)) throw solver_cancelled;
6d25662f
JG
210 }
211
212 // 3) Repeat step 2 until 2n/(k+1) bits remain
e9574728 213 for (int r = 1; r < K && X.size() > 0; r++) {
6d25662f
JG
214 LogPrint("pow", "Round %d:\n", r);
215 // 2a) Sort the list
216 LogPrint("pow", "- Sorting list\n");
d151ab4f 217 std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength));
5b4ebcd5 218 if (cancelled(ListSorting)) throw solver_cancelled;
6d25662f
JG
219
220 LogPrint("pow", "- Finding collisions\n");
221 int i = 0;
222 int posFree = 0;
d4d76536 223 std::vector<FullStepRow<FullWidth>> Xc;
6d25662f
JG
224 while (i < X.size() - 1) {
225 // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
226 int j = 1;
227 while (i+j < X.size() &&
e9574728 228 HasCollision(X[i], X[i+j], CollisionByteLength)) {
6d25662f
JG
229 j++;
230 }
231
232 // 2c) Calculate tuples (X_i ^ X_j, (i, j))
233 for (int l = 0; l < j - 1; l++) {
234 for (int m = l + 1; m < j; m++) {
d4d76536
JG
235 if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
236 Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength);
6d25662f
JG
237 }
238 }
239 }
240
241 // 2d) Store tuples on the table in-place if possible
242 while (posFree < i+j && Xc.size() > 0) {
243 X[posFree++] = Xc.back();
244 Xc.pop_back();
245 }
246
247 i += j;
5b4ebcd5 248 if (cancelled(ListColliding)) throw solver_cancelled;
6d25662f
JG
249 }
250
251 // 2e) Handle edge case where final table entry has no collision
252 while (posFree < X.size() && Xc.size() > 0) {
253 X[posFree++] = Xc.back();
254 Xc.pop_back();
255 }
256
257 if (Xc.size() > 0) {
258 // 2f) Add overflow to end of table
259 X.insert(X.end(), Xc.begin(), Xc.end());
260 } else if (posFree < X.size()) {
261 // 2g) Remove empty space at the end
262 X.erase(X.begin()+posFree, X.end());
263 X.shrink_to_fit();
264 }
639c4004
JG
265
266 hashLen -= CollisionByteLength;
d4d76536 267 lenIndices *= 2;
5b4ebcd5 268 if (cancelled(RoundEnd)) throw solver_cancelled;
6d25662f
JG
269 }
270
271 // k+1) Find a collision on last 2n(k+1) bits
272 LogPrint("pow", "Final round:\n");
273 std::set<std::vector<eh_index>> solns;
274 if (X.size() > 1) {
275 LogPrint("pow", "- Sorting list\n");
639c4004 276 std::sort(X.begin(), X.end(), CompareSR(hashLen));
5b4ebcd5 277 if (cancelled(FinalSorting)) throw solver_cancelled;
6d25662f 278 LogPrint("pow", "- Finding collisions\n");
1bb40a42
JG
279 int i = 0;
280 while (i < X.size() - 1) {
281 int j = 1;
282 while (i+j < X.size() &&
283 HasCollision(X[i], X[i+j], hashLen)) {
284 j++;
6d25662f 285 }
1bb40a42
JG
286
287 for (int l = 0; l < j - 1; l++) {
288 for (int m = l + 1; m < j; m++) {
289 FullStepRow<FinalFullWidth> res(X[i+l], X[i+m], hashLen, lenIndices, 0);
290 if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
291 solns.insert(res.GetIndices(hashLen, 2*lenIndices));
292 }
293 }
294 }
295
296 i += j;
5b4ebcd5 297 if (cancelled(FinalColliding)) throw solver_cancelled;
6d25662f
JG
298 }
299 } else
300 LogPrint("pow", "- List is empty\n");
301
302 return solns;
303}
304
d4d76536
JG
305template<size_t WIDTH>
306void 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
307{
308 int i = 0;
309 int posFree = 0;
d4d76536 310 std::vector<FullStepRow<WIDTH>> Xc;
c92c1f60
JG
311 while (i < X.size() - 1) {
312 // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
313 int j = 1;
314 while (i+j < X.size() &&
315 HasCollision(X[i], X[i+j], clen)) {
316 j++;
317 }
318
319 // 2c) Calculate tuples (X_i ^ X_j, (i, j))
320 for (int l = 0; l < j - 1; l++) {
321 for (int m = l + 1; m < j; m++) {
d4d76536
JG
322 if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) {
323 if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) {
324 Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen);
325 } else if (IsValidBranch(X[i+m], hlen, ilen, lt) && IsValidBranch(X[i+l], hlen, ilen, rt)) {
326 Xc.emplace_back(X[i+m], X[i+l], hlen, lenIndices, clen);
c92c1f60
JG
327 }
328 }
329 }
330 }
331
332 // 2d) Store tuples on the table in-place if possible
333 while (posFree < i+j && Xc.size() > 0) {
334 X[posFree++] = Xc.back();
335 Xc.pop_back();
336 }
337
338 i += j;
339 }
340
341 // 2e) Handle edge case where final table entry has no collision
342 while (posFree < X.size() && Xc.size() > 0) {
343 X[posFree++] = Xc.back();
344 Xc.pop_back();
345 }
346
347 if (Xc.size() > 0) {
348 // 2f) Add overflow to end of table
349 X.insert(X.end(), Xc.begin(), Xc.end());
350 } else if (posFree < X.size()) {
351 // 2g) Remove empty space at the end
352 X.erase(X.begin()+posFree, X.end());
353 X.shrink_to_fit();
354 }
355}
356
e9574728 357template<unsigned int N, unsigned int K>
5b4ebcd5 358std::set<std::vector<eh_index>> Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled)
c92c1f60 359{
e9574728 360 eh_index init_size { 1 << (CollisionBitLength + 1) };
c92c1f60
JG
361
362 // First run the algorithm with truncated indices
363
e9574728 364 eh_index soln_size { 1 << K };
447444ae
JG
365 // Each element of partialSolns is dynamically allocated in a call to
366 // GetTruncatedIndices(), and freed at the end of this function.
39f5cb35 367 std::vector<eh_trunc*> partialSolns;
c92c1f60
JG
368 {
369
370 // 1) Generate first list
371 LogPrint("pow", "Generating first list\n");
639c4004 372 size_t hashLen = N/8;
d4d76536
JG
373 size_t lenIndices = sizeof(eh_trunc);
374 std::vector<TruncatedStepRow<TruncatedWidth>> Xt;
c92c1f60
JG
375 Xt.reserve(init_size);
376 for (eh_index i = 0; i < init_size; i++) {
e9574728 377 Xt.emplace_back(N, base_state, i, CollisionBitLength + 1);
2dbabb11 378 // Slow down checking to prevent segfaults (??)
5b4ebcd5 379 if (i % 10000 == 0 && cancelled(ListGeneration)) throw solver_cancelled;
c92c1f60
JG
380 }
381
382 // 3) Repeat step 2 until 2n/(k+1) bits remain
e9574728 383 for (int r = 1; r < K && Xt.size() > 0; r++) {
c92c1f60
JG
384 LogPrint("pow", "Round %d:\n", r);
385 // 2a) Sort the list
386 LogPrint("pow", "- Sorting list\n");
d151ab4f 387 std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength));
5b4ebcd5 388 if (cancelled(ListSorting)) throw solver_cancelled;
c92c1f60
JG
389
390 LogPrint("pow", "- Finding collisions\n");
391 int i = 0;
392 int posFree = 0;
d4d76536 393 std::vector<TruncatedStepRow<TruncatedWidth>> Xc;
c92c1f60
JG
394 while (i < Xt.size() - 1) {
395 // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
396 int j = 1;
397 while (i+j < Xt.size() &&
e9574728 398 HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) {
c92c1f60
JG
399 j++;
400 }
401
402 // 2c) Calculate tuples (X_i ^ X_j, (i, j))
403 for (int l = 0; l < j - 1; l++) {
404 for (int m = l + 1; m < j; m++) {
405 // We truncated, so don't check for distinct indices here
d4d76536 406 Xc.emplace_back(Xt[i+l], Xt[i+m], hashLen, lenIndices, CollisionByteLength);
c92c1f60
JG
407 }
408 }
409
410 // 2d) Store tuples on the table in-place if possible
411 while (posFree < i+j && Xc.size() > 0) {
412 Xt[posFree++] = Xc.back();
413 Xc.pop_back();
414 }
415
416 i += j;
5b4ebcd5 417 if (cancelled(ListColliding)) throw solver_cancelled;
c92c1f60
JG
418 }
419
420 // 2e) Handle edge case where final table entry has no collision
421 while (posFree < Xt.size() && Xc.size() > 0) {
422 Xt[posFree++] = Xc.back();
423 Xc.pop_back();
424 }
425
426 if (Xc.size() > 0) {
427 // 2f) Add overflow to end of table
428 Xt.insert(Xt.end(), Xc.begin(), Xc.end());
429 } else if (posFree < Xt.size()) {
430 // 2g) Remove empty space at the end
431 Xt.erase(Xt.begin()+posFree, Xt.end());
432 Xt.shrink_to_fit();
433 }
639c4004
JG
434
435 hashLen -= CollisionByteLength;
d4d76536 436 lenIndices *= 2;
5b4ebcd5 437 if (cancelled(RoundEnd)) throw solver_cancelled;
c92c1f60
JG
438 }
439
440 // k+1) Find a collision on last 2n(k+1) bits
441 LogPrint("pow", "Final round:\n");
442 if (Xt.size() > 1) {
443 LogPrint("pow", "- Sorting list\n");
639c4004 444 std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen));
5b4ebcd5 445 if (cancelled(FinalSorting)) throw solver_cancelled;
c92c1f60 446 LogPrint("pow", "- Finding collisions\n");
1bb40a42
JG
447 int i = 0;
448 while (i < Xt.size() - 1) {
449 int j = 1;
450 while (i+j < Xt.size() &&
451 HasCollision(Xt[i], Xt[i+j], hashLen)) {
452 j++;
c92c1f60 453 }
1bb40a42
JG
454
455 for (int l = 0; l < j - 1; l++) {
456 for (int m = l + 1; m < j; m++) {
457 TruncatedStepRow<FinalTruncatedWidth> res(Xt[i+l], Xt[i+m], hashLen, lenIndices, 0);
458 partialSolns.push_back(res.GetTruncatedIndices(hashLen, 2*lenIndices));
459 }
460 }
461
462 i += j;
5b4ebcd5 463 if (cancelled(FinalColliding)) break;
c92c1f60
JG
464 }
465 } else
466 LogPrint("pow", "- List is empty\n");
467
468 } // Ensure Xt goes out of scope and is destroyed
469
470 LogPrint("pow", "Found %d partial solutions\n", partialSolns.size());
471
472 // Now for each solution run the algorithm again to recreate the indices
473 LogPrint("pow", "Culling solutions\n");
474 std::set<std::vector<eh_index>> solns;
e9574728 475 eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
c92c1f60 476 int invalidCount = 0;
5b4ebcd5 477 if (cancelled(StartCulling)) goto cancelsolver;
39f5cb35 478 for (eh_trunc* partialSoln : partialSolns) {
0a66f013
JG
479 size_t hashLen;
480 size_t lenIndices;
481 std::vector<boost::optional<std::vector<FullStepRow<FinalFullWidth>>>> X;
482 X.reserve(K+1);
483
484 // 3) Repeat steps 1 and 2 for each partial index
39f5cb35 485 for (eh_index i = 0; i < soln_size; i++) {
0a66f013
JG
486 // 1) Generate first list of possibilities
487 std::vector<FullStepRow<FinalFullWidth>> icv;
488 icv.reserve(recreate_size);
c92c1f60 489 for (eh_index j = 0; j < recreate_size; j++) {
e9574728 490 eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength + 1) };
0a66f013 491 icv.emplace_back(N, base_state, newIndex);
5b4ebcd5 492 if (cancelled(PartialGeneration)) goto cancelsolver;
c92c1f60 493 }
0a66f013 494 boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv;
c92c1f60
JG
495
496 // 2a) For each pair of lists:
0a66f013
JG
497 hashLen = N/8;
498 lenIndices = sizeof(eh_index);
499 size_t rti = i;
500 for (size_t r = 0; r <= K; r++) {
501 // 2b) Until we are at the top of a subtree:
502 if (r < X.size()) {
503 if (X[r]) {
504 // 2c) Merge the lists
505 ic->reserve(ic->size() + X[r]->size());
506 ic->insert(ic->end(), X[r]->begin(), X[r]->end());
507 std::sort(ic->begin(), ic->end(), CompareSR(hashLen));
5b4ebcd5 508 if (cancelled(PartialSorting)) goto cancelsolver;
0a66f013
JG
509 size_t lti = rti-(1<<r);
510 CollideBranches(*ic, hashLen, lenIndices,
511 CollisionByteLength,
512 CollisionBitLength + 1,
513 partialSoln[lti], partialSoln[rti]);
514
515 // 2d) Check if this has become an invalid solution
516 if (ic->size() == 0)
517 goto invalidsolution;
518
519 X[r] = boost::none;
520 hashLen -= CollisionByteLength;
521 lenIndices *= 2;
522 rti = lti;
523 } else {
524 X[r] = *ic;
525 break;
526 }
527 } else {
528 X.push_back(ic);
529 break;
530 }
5b4ebcd5 531 if (cancelled(PartialSubtreeEnd)) goto cancelsolver;
c92c1f60 532 }
5b4ebcd5 533 if (cancelled(PartialIndexEnd)) goto cancelsolver;
c92c1f60
JG
534 }
535
536 // We are at the top of the tree
0a66f013
JG
537 assert(X.size() == K+1);
538 for (FullStepRow<FinalFullWidth> row : *X[K]) {
d4d76536 539 solns.insert(row.GetIndices(hashLen, lenIndices));
c92c1f60 540 }
5b4ebcd5 541 if (cancelled(PartialEnd)) goto cancelsolver;
2dbabb11 542 continue;
c92c1f60
JG
543
544invalidsolution:
545 invalidCount++;
546 }
547 LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount);
548
2dbabb11
JG
549 for (eh_trunc* partialSoln : partialSolns) {
550 delete[] partialSoln;
551 }
c92c1f60 552 return solns;
2dbabb11
JG
553
554cancelsolver:
555 for (eh_trunc* partialSoln : partialSolns) {
556 delete[] partialSoln;
557 }
558 throw solver_cancelled;
c92c1f60
JG
559}
560
e9574728
JG
561template<unsigned int N, unsigned int K>
562bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
6d25662f 563{
e9574728 564 eh_index soln_size { 1u << K };
6d25662f
JG
565 if (soln.size() != soln_size) {
566 LogPrint("pow", "Invalid solution size: %d\n", soln.size());
567 return false;
568 }
569
d4d76536 570 std::vector<FullStepRow<FinalFullWidth>> X;
6d25662f
JG
571 X.reserve(soln_size);
572 for (eh_index i : soln) {
e9574728 573 X.emplace_back(N, base_state, i);
6d25662f
JG
574 }
575
d4d76536
JG
576 size_t hashLen = N/8;
577 size_t lenIndices = sizeof(eh_index);
6d25662f 578 while (X.size() > 1) {
d4d76536 579 std::vector<FullStepRow<FinalFullWidth>> Xc;
6d25662f 580 for (int i = 0; i < X.size(); i += 2) {
e9574728 581 if (!HasCollision(X[i], X[i+1], CollisionByteLength)) {
6d25662f 582 LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
d4d76536
JG
583 LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen));
584 LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen));
6d25662f
JG
585 return false;
586 }
d07cf629 587 if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) {
6d25662f
JG
588 return false;
589 LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
590 }
d4d76536 591 if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) {
6d25662f
JG
592 LogPrint("pow", "Invalid solution: duplicate indices\n");
593 return false;
594 }
d4d76536 595 Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength);
6d25662f
JG
596 }
597 X = Xc;
d4d76536
JG
598 hashLen -= CollisionByteLength;
599 lenIndices *= 2;
6d25662f
JG
600 }
601
602 assert(X.size() == 1);
d4d76536 603 return X[0].IsZero(hashLen);
6d25662f 604}
e9574728 605
ae37d2a4
JG
606// Explicit instantiations for Equihash<96,3>
607template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
5b4ebcd5
JG
608template std::set<std::vector<eh_index>> Equihash<96,3>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
609template std::set<std::vector<eh_index>> Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
ae37d2a4
JG
610template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
611
e9574728
JG
612// Explicit instantiations for Equihash<96,5>
613template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
5b4ebcd5
JG
614template std::set<std::vector<eh_index>> Equihash<96,5>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
615template std::set<std::vector<eh_index>> Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
e9574728
JG
616template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
617
618// Explicit instantiations for Equihash<48,5>
619template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
5b4ebcd5
JG
620template std::set<std::vector<eh_index>> Equihash<48,5>::BasicSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
621template std::set<std::vector<eh_index>> Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, const std::function<bool(EhSolverCancelCheck)> cancelled);
e9574728 622template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln);
This page took 0.109143 seconds and 4 git commands to generate.