]>
Commit | Line | Data |
---|---|---|
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 | ||
22 | void validate_params(int n, int k) | |
23 | { | |
24 | if (k>=n) { | |
25 | std::cerr << "n must be larger than k\n"; | |
26 | throw invalid_params(); | |
27 | } | |
28 | if (n % 8 != 0) { | |
29 | std::cerr << "Parameters must satisfy n = 0 mod 8\n"; | |
30 | throw invalid_params(); | |
31 | } | |
32 | if ((n/(k+1)) % 8 != 0) { | |
33 | std::cerr << "Parameters must satisfy n/(k+1) = 0 mod 8\n"; | |
34 | throw invalid_params(); | |
35 | } | |
36 | } | |
37 | ||
38 | int Equihash::InitialiseState(eh_HashState& base_state) | |
39 | { | |
40 | unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {}; | |
41 | memcpy(personalization, "ZcashPOW", 8); | |
42 | memcpy(personalization+8, &n, 4); | |
43 | memcpy(personalization+12, &k, 4); | |
44 | return crypto_generichash_blake2b_init_salt_personal(&base_state, | |
45 | NULL, 0, // No key. | |
46 | n/8, | |
47 | NULL, // No salt. | |
48 | personalization); | |
49 | } | |
50 | ||
c92c1f60 JG |
51 | eh_trunc TruncateIndex(eh_index i, unsigned int ilen) |
52 | { | |
53 | // Truncate to 8 bits | |
54 | assert(sizeof(eh_trunc) == 1); | |
55 | return (i >> (ilen - 8)) & 0xff; | |
56 | } | |
57 | ||
58 | eh_index UntruncateIndex(eh_trunc t, eh_index r, unsigned int ilen) | |
59 | { | |
60 | eh_index i{t}; | |
61 | return (i << (ilen - 8)) | r; | |
62 | } | |
63 | ||
6d25662f JG |
64 | StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : |
65 | hash {new unsigned char[n/8]}, | |
a3361e77 | 66 | len {n/8} |
6d25662f JG |
67 | { |
68 | eh_HashState state; | |
69 | state = base_state; | |
70 | crypto_generichash_blake2b_update(&state, (unsigned char*) &i, sizeof(eh_index)); | |
71 | crypto_generichash_blake2b_final(&state, hash, n/8); | |
6d25662f JG |
72 | } |
73 | ||
74 | StepRow::~StepRow() | |
75 | { | |
76 | delete[] hash; | |
77 | } | |
78 | ||
79 | StepRow::StepRow(const StepRow& a) : | |
80 | hash {new unsigned char[a.len]}, | |
a3361e77 | 81 | len {a.len} |
6d25662f | 82 | { |
6afef0dd | 83 | std::copy(a.hash, a.hash+a.len, hash); |
6d25662f JG |
84 | } |
85 | ||
a3361e77 JG |
86 | FullStepRow::FullStepRow(unsigned int n, const eh_HashState& base_state, eh_index i) : |
87 | StepRow {n, base_state, i}, | |
88 | indices {i} | |
89 | { | |
90 | assert(indices.size() == 1); | |
91 | } | |
92 | ||
93 | FullStepRow& FullStepRow::operator=(const FullStepRow& a) | |
6d25662f JG |
94 | { |
95 | unsigned char* p = new unsigned char[a.len]; | |
6afef0dd | 96 | std::copy(a.hash, a.hash+a.len, p); |
6d25662f JG |
97 | delete[] hash; |
98 | hash = p; | |
99 | len = a.len; | |
100 | indices = a.indices; | |
101 | return *this; | |
102 | } | |
103 | ||
a3361e77 | 104 | FullStepRow& FullStepRow::operator^=(const FullStepRow& a) |
6d25662f JG |
105 | { |
106 | if (a.len != len) { | |
107 | throw std::invalid_argument("Hash length differs"); | |
108 | } | |
109 | if (a.indices.size() != indices.size()) { | |
110 | throw std::invalid_argument("Number of indices differs"); | |
111 | } | |
112 | unsigned char* p = new unsigned char[len]; | |
113 | for (int i = 0; i < len; i++) | |
114 | p[i] = hash[i] ^ a.hash[i]; | |
115 | delete[] hash; | |
116 | hash = p; | |
117 | indices.reserve(indices.size() + a.indices.size()); | |
118 | indices.insert(indices.end(), a.indices.begin(), a.indices.end()); | |
119 | return *this; | |
120 | } | |
121 | ||
a3361e77 | 122 | void FullStepRow::TrimHash(int l) |
6d25662f JG |
123 | { |
124 | unsigned char* p = new unsigned char[len-l]; | |
6afef0dd | 125 | std::copy(hash+l, hash+len, p); |
6d25662f JG |
126 | delete[] hash; |
127 | hash = p; | |
128 | len -= l; | |
129 | } | |
130 | ||
131 | bool StepRow::IsZero() | |
132 | { | |
133 | char res = 0; | |
134 | for (int i = 0; i < len; i++) | |
135 | res |= hash[i]; | |
136 | return res == 0; | |
137 | } | |
138 | ||
139 | bool HasCollision(StepRow& a, StepRow& b, int l) | |
140 | { | |
141 | bool res = true; | |
142 | for (int j = 0; j < l; j++) | |
143 | res &= a.hash[j] == b.hash[j]; | |
144 | return res; | |
145 | } | |
146 | ||
147 | // Checks if the intersection of a.indices and b.indices is empty | |
a3361e77 | 148 | bool DistinctIndices(const FullStepRow& a, const FullStepRow& b) |
6d25662f JG |
149 | { |
150 | std::vector<eh_index> aSrt(a.indices); | |
151 | std::vector<eh_index> bSrt(b.indices); | |
152 | ||
153 | std::sort(aSrt.begin(), aSrt.end()); | |
154 | std::sort(bSrt.begin(), bSrt.end()); | |
155 | ||
156 | unsigned int i = 0; | |
157 | for (unsigned int j = 0; j < bSrt.size(); j++) { | |
158 | while (aSrt[i] < bSrt[j]) { | |
159 | i++; | |
160 | if (i == aSrt.size()) { return true; } | |
161 | } | |
162 | assert(aSrt[i] >= bSrt[j]); | |
163 | if (aSrt[i] == bSrt[j]) { return false; } | |
164 | } | |
165 | return true; | |
166 | } | |
167 | ||
c92c1f60 JG |
168 | bool IsValidBranch(const FullStepRow& a, const unsigned int ilen, const eh_trunc t) |
169 | { | |
170 | return TruncateIndex(a.indices[0], ilen) == t; | |
171 | } | |
172 | ||
173 | TruncatedStepRow::TruncatedStepRow(unsigned int n, const eh_HashState& base_state, eh_index i, unsigned int ilen) : | |
174 | StepRow {n, base_state, i}, | |
39f5cb35 | 175 | lenIndices {1} |
c92c1f60 | 176 | { |
39f5cb35 JG |
177 | unsigned char* p = new unsigned char[len+lenIndices]; |
178 | std::copy(hash, hash+len, p); | |
179 | p[len] = TruncateIndex(i, ilen); | |
180 | delete[] hash; | |
181 | hash = p; | |
182 | } | |
183 | ||
184 | TruncatedStepRow::TruncatedStepRow(const TruncatedStepRow& a) : | |
185 | StepRow {a}, | |
186 | lenIndices {a.lenIndices} | |
187 | { | |
188 | unsigned char* p = new unsigned char[a.len+a.lenIndices]; | |
189 | std::copy(a.hash, a.hash+a.len+a.lenIndices, p); | |
190 | delete[] hash; | |
191 | hash = p; | |
c92c1f60 JG |
192 | } |
193 | ||
194 | TruncatedStepRow& TruncatedStepRow::operator=(const TruncatedStepRow& a) | |
195 | { | |
39f5cb35 JG |
196 | unsigned char* p = new unsigned char[a.len+a.lenIndices]; |
197 | std::copy(a.hash, a.hash+a.len+a.lenIndices, p); | |
c92c1f60 JG |
198 | delete[] hash; |
199 | hash = p; | |
200 | len = a.len; | |
39f5cb35 | 201 | lenIndices = a.lenIndices; |
c92c1f60 JG |
202 | return *this; |
203 | } | |
204 | ||
205 | TruncatedStepRow& TruncatedStepRow::operator^=(const TruncatedStepRow& a) | |
206 | { | |
207 | if (a.len != len) { | |
208 | throw std::invalid_argument("Hash length differs"); | |
209 | } | |
39f5cb35 | 210 | if (a.lenIndices != lenIndices) { |
c92c1f60 JG |
211 | throw std::invalid_argument("Number of indices differs"); |
212 | } | |
39f5cb35 | 213 | unsigned char* p = new unsigned char[len+lenIndices+a.lenIndices]; |
c92c1f60 JG |
214 | for (int i = 0; i < len; i++) |
215 | p[i] = hash[i] ^ a.hash[i]; | |
39f5cb35 JG |
216 | std::copy(hash+len, hash+len+lenIndices, p+len); |
217 | std::copy(a.hash+a.len, a.hash+a.len+a.lenIndices, p+len+lenIndices); | |
c92c1f60 JG |
218 | delete[] hash; |
219 | hash = p; | |
39f5cb35 | 220 | lenIndices += a.lenIndices; |
c92c1f60 JG |
221 | return *this; |
222 | } | |
223 | ||
39f5cb35 JG |
224 | void TruncatedStepRow::TrimHash(int l) |
225 | { | |
226 | unsigned char* p = new unsigned char[len-l+lenIndices]; | |
227 | std::copy(hash+l, hash+len+lenIndices, p); | |
228 | delete[] hash; | |
229 | hash = p; | |
230 | len -= l; | |
231 | } | |
232 | ||
233 | eh_trunc* TruncatedStepRow::GetPartialSolution(eh_index soln_size) const | |
234 | { | |
235 | assert(lenIndices == soln_size); | |
236 | eh_trunc* p = new eh_trunc[lenIndices]; | |
237 | std::copy(hash+len, hash+len+lenIndices, p); | |
238 | return p; | |
239 | } | |
240 | ||
6d25662f JG |
241 | Equihash::Equihash(unsigned int n, unsigned int k) : |
242 | n(n), k(k) | |
243 | { | |
244 | validate_params(n, k); | |
245 | } | |
246 | ||
247 | std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_state) | |
248 | { | |
249 | assert(CollisionBitLength() + 1 < 8*sizeof(eh_index)); | |
6afef0dd | 250 | eh_index init_size { 1 << (CollisionBitLength() + 1) }; |
6d25662f JG |
251 | |
252 | // 1) Generate first list | |
253 | LogPrint("pow", "Generating first list\n"); | |
a3361e77 | 254 | std::vector<FullStepRow> X; |
6d25662f JG |
255 | X.reserve(init_size); |
256 | for (eh_index i = 0; i < init_size; i++) { | |
257 | X.emplace_back(n, base_state, i); | |
258 | } | |
259 | ||
260 | // 3) Repeat step 2 until 2n/(k+1) bits remain | |
261 | for (int r = 1; r < k && X.size() > 0; r++) { | |
262 | LogPrint("pow", "Round %d:\n", r); | |
263 | // 2a) Sort the list | |
264 | LogPrint("pow", "- Sorting list\n"); | |
265 | std::sort(X.begin(), X.end()); | |
266 | ||
267 | LogPrint("pow", "- Finding collisions\n"); | |
268 | int i = 0; | |
269 | int posFree = 0; | |
a3361e77 | 270 | std::vector<FullStepRow> Xc; |
6d25662f JG |
271 | while (i < X.size() - 1) { |
272 | // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits | |
273 | int j = 1; | |
274 | while (i+j < X.size() && | |
275 | HasCollision(X[i], X[i+j], CollisionByteLength())) { | |
276 | j++; | |
277 | } | |
278 | ||
279 | // 2c) Calculate tuples (X_i ^ X_j, (i, j)) | |
280 | for (int l = 0; l < j - 1; l++) { | |
281 | for (int m = l + 1; m < j; m++) { | |
282 | if (DistinctIndices(X[i+l], X[i+m])) { | |
283 | Xc.push_back(X[i+l] ^ X[i+m]); | |
284 | Xc.back().TrimHash(CollisionByteLength()); | |
285 | } | |
286 | } | |
287 | } | |
288 | ||
289 | // 2d) Store tuples on the table in-place if possible | |
290 | while (posFree < i+j && Xc.size() > 0) { | |
291 | X[posFree++] = Xc.back(); | |
292 | Xc.pop_back(); | |
293 | } | |
294 | ||
295 | i += j; | |
296 | } | |
297 | ||
298 | // 2e) Handle edge case where final table entry has no collision | |
299 | while (posFree < X.size() && Xc.size() > 0) { | |
300 | X[posFree++] = Xc.back(); | |
301 | Xc.pop_back(); | |
302 | } | |
303 | ||
304 | if (Xc.size() > 0) { | |
305 | // 2f) Add overflow to end of table | |
306 | X.insert(X.end(), Xc.begin(), Xc.end()); | |
307 | } else if (posFree < X.size()) { | |
308 | // 2g) Remove empty space at the end | |
309 | X.erase(X.begin()+posFree, X.end()); | |
310 | X.shrink_to_fit(); | |
311 | } | |
312 | } | |
313 | ||
314 | // k+1) Find a collision on last 2n(k+1) bits | |
315 | LogPrint("pow", "Final round:\n"); | |
316 | std::set<std::vector<eh_index>> solns; | |
317 | if (X.size() > 1) { | |
318 | LogPrint("pow", "- Sorting list\n"); | |
319 | std::sort(X.begin(), X.end()); | |
320 | LogPrint("pow", "- Finding collisions\n"); | |
321 | for (int i = 0; i < X.size() - 1; i++) { | |
a3361e77 | 322 | FullStepRow res = X[i] ^ X[i+1]; |
6d25662f JG |
323 | if (res.IsZero() && DistinctIndices(X[i], X[i+1])) { |
324 | solns.insert(res.GetSolution()); | |
325 | } | |
326 | } | |
327 | } else | |
328 | LogPrint("pow", "- List is empty\n"); | |
329 | ||
330 | return solns; | |
331 | } | |
332 | ||
c92c1f60 JG |
333 | void CollideBranches(std::vector<FullStepRow>& X, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt) |
334 | { | |
335 | int i = 0; | |
336 | int posFree = 0; | |
337 | std::vector<FullStepRow> Xc; | |
338 | while (i < X.size() - 1) { | |
339 | // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits | |
340 | int j = 1; | |
341 | while (i+j < X.size() && | |
342 | HasCollision(X[i], X[i+j], clen)) { | |
343 | j++; | |
344 | } | |
345 | ||
346 | // 2c) Calculate tuples (X_i ^ X_j, (i, j)) | |
347 | for (int l = 0; l < j - 1; l++) { | |
348 | for (int m = l + 1; m < j; m++) { | |
349 | if (DistinctIndices(X[i+l], X[i+m])) { | |
350 | if (IsValidBranch(X[i+l], ilen, lt) && IsValidBranch(X[i+m], ilen, rt)) { | |
351 | Xc.push_back(X[i+l] ^ X[i+m]); | |
352 | Xc.back().TrimHash(clen); | |
353 | } else if (IsValidBranch(X[i+m], ilen, lt) && IsValidBranch(X[i+l], ilen, rt)) { | |
354 | Xc.push_back(X[i+m] ^ X[i+l]); | |
355 | Xc.back().TrimHash(clen); | |
356 | } | |
357 | } | |
358 | } | |
359 | } | |
360 | ||
361 | // 2d) Store tuples on the table in-place if possible | |
362 | while (posFree < i+j && Xc.size() > 0) { | |
363 | X[posFree++] = Xc.back(); | |
364 | Xc.pop_back(); | |
365 | } | |
366 | ||
367 | i += j; | |
368 | } | |
369 | ||
370 | // 2e) Handle edge case where final table entry has no collision | |
371 | while (posFree < X.size() && Xc.size() > 0) { | |
372 | X[posFree++] = Xc.back(); | |
373 | Xc.pop_back(); | |
374 | } | |
375 | ||
376 | if (Xc.size() > 0) { | |
377 | // 2f) Add overflow to end of table | |
378 | X.insert(X.end(), Xc.begin(), Xc.end()); | |
379 | } else if (posFree < X.size()) { | |
380 | // 2g) Remove empty space at the end | |
381 | X.erase(X.begin()+posFree, X.end()); | |
382 | X.shrink_to_fit(); | |
383 | } | |
384 | } | |
385 | ||
386 | std::set<std::vector<eh_index>> Equihash::OptimisedSolve(const eh_HashState& base_state) | |
387 | { | |
388 | assert(CollisionBitLength() + 1 < 8*sizeof(eh_index)); | |
389 | eh_index init_size { 1 << (CollisionBitLength() + 1) }; | |
390 | ||
391 | // First run the algorithm with truncated indices | |
392 | ||
39f5cb35 JG |
393 | eh_index soln_size { 1 << k }; |
394 | std::vector<eh_trunc*> partialSolns; | |
c92c1f60 JG |
395 | { |
396 | ||
397 | // 1) Generate first list | |
398 | LogPrint("pow", "Generating first list\n"); | |
399 | std::vector<TruncatedStepRow> Xt; | |
400 | Xt.reserve(init_size); | |
401 | for (eh_index i = 0; i < init_size; i++) { | |
402 | Xt.emplace_back(n, base_state, i, CollisionBitLength() + 1); | |
403 | } | |
404 | ||
405 | // 3) Repeat step 2 until 2n/(k+1) bits remain | |
406 | for (int r = 1; r < k && Xt.size() > 0; r++) { | |
407 | LogPrint("pow", "Round %d:\n", r); | |
408 | // 2a) Sort the list | |
409 | LogPrint("pow", "- Sorting list\n"); | |
410 | std::sort(Xt.begin(), Xt.end()); | |
411 | ||
412 | LogPrint("pow", "- Finding collisions\n"); | |
413 | int i = 0; | |
414 | int posFree = 0; | |
415 | std::vector<TruncatedStepRow> Xc; | |
416 | while (i < Xt.size() - 1) { | |
417 | // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits | |
418 | int j = 1; | |
419 | while (i+j < Xt.size() && | |
420 | HasCollision(Xt[i], Xt[i+j], CollisionByteLength())) { | |
421 | j++; | |
422 | } | |
423 | ||
424 | // 2c) Calculate tuples (X_i ^ X_j, (i, j)) | |
425 | for (int l = 0; l < j - 1; l++) { | |
426 | for (int m = l + 1; m < j; m++) { | |
427 | // We truncated, so don't check for distinct indices here | |
428 | Xc.push_back(Xt[i+l] ^ Xt[i+m]); | |
429 | Xc.back().TrimHash(CollisionByteLength()); | |
430 | } | |
431 | } | |
432 | ||
433 | // 2d) Store tuples on the table in-place if possible | |
434 | while (posFree < i+j && Xc.size() > 0) { | |
435 | Xt[posFree++] = Xc.back(); | |
436 | Xc.pop_back(); | |
437 | } | |
438 | ||
439 | i += j; | |
440 | } | |
441 | ||
442 | // 2e) Handle edge case where final table entry has no collision | |
443 | while (posFree < Xt.size() && Xc.size() > 0) { | |
444 | Xt[posFree++] = Xc.back(); | |
445 | Xc.pop_back(); | |
446 | } | |
447 | ||
448 | if (Xc.size() > 0) { | |
449 | // 2f) Add overflow to end of table | |
450 | Xt.insert(Xt.end(), Xc.begin(), Xc.end()); | |
451 | } else if (posFree < Xt.size()) { | |
452 | // 2g) Remove empty space at the end | |
453 | Xt.erase(Xt.begin()+posFree, Xt.end()); | |
454 | Xt.shrink_to_fit(); | |
455 | } | |
456 | } | |
457 | ||
458 | // k+1) Find a collision on last 2n(k+1) bits | |
459 | LogPrint("pow", "Final round:\n"); | |
460 | if (Xt.size() > 1) { | |
461 | LogPrint("pow", "- Sorting list\n"); | |
462 | std::sort(Xt.begin(), Xt.end()); | |
463 | LogPrint("pow", "- Finding collisions\n"); | |
464 | for (int i = 0; i < Xt.size() - 1; i++) { | |
465 | TruncatedStepRow res = Xt[i] ^ Xt[i+1]; | |
466 | if (res.IsZero()) { | |
39f5cb35 | 467 | partialSolns.push_back(res.GetPartialSolution(soln_size)); |
c92c1f60 JG |
468 | } |
469 | } | |
470 | } else | |
471 | LogPrint("pow", "- List is empty\n"); | |
472 | ||
473 | } // Ensure Xt goes out of scope and is destroyed | |
474 | ||
475 | LogPrint("pow", "Found %d partial solutions\n", partialSolns.size()); | |
476 | ||
477 | // Now for each solution run the algorithm again to recreate the indices | |
478 | LogPrint("pow", "Culling solutions\n"); | |
479 | std::set<std::vector<eh_index>> solns; | |
480 | eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength() + 1) }; | |
481 | int invalidCount = 0; | |
39f5cb35 | 482 | for (eh_trunc* partialSoln : partialSolns) { |
c92c1f60 JG |
483 | // 1) Generate first list of possibilities |
484 | std::vector<std::vector<FullStepRow>> X; | |
39f5cb35 JG |
485 | X.reserve(soln_size); |
486 | for (eh_index i = 0; i < soln_size; i++) { | |
c92c1f60 JG |
487 | std::vector<FullStepRow> ic; |
488 | ic.reserve(recreate_size); | |
489 | for (eh_index j = 0; j < recreate_size; j++) { | |
490 | eh_index newIndex { UntruncateIndex(partialSoln[i], j, CollisionBitLength() + 1) }; | |
491 | ic.emplace_back(n, base_state, newIndex); | |
492 | } | |
493 | X.push_back(ic); | |
494 | } | |
495 | ||
496 | // 3) Repeat step 2 for each level of the tree | |
497 | for (int r = 0; X.size() > 1; r++) { | |
498 | std::vector<std::vector<FullStepRow>> Xc; | |
499 | Xc.reserve(X.size()/2); | |
500 | ||
501 | // 2a) For each pair of lists: | |
502 | for (int v = 0; v < X.size(); v += 2) { | |
503 | // 2b) Merge the lists | |
504 | std::vector<FullStepRow> ic(X[v]); | |
505 | ic.reserve(X[v].size() + X[v+1].size()); | |
506 | ic.insert(ic.end(), X[v+1].begin(), X[v+1].end()); | |
507 | std::sort(ic.begin(), ic.end()); | |
508 | CollideBranches(ic, CollisionByteLength(), CollisionBitLength() + 1, partialSoln[(1<<r)*v], partialSoln[(1<<r)*(v+1)]); | |
509 | ||
510 | // 2v) Check if this has become an invalid solution | |
511 | if (ic.size() == 0) | |
512 | goto invalidsolution; | |
513 | ||
514 | Xc.push_back(ic); | |
515 | } | |
516 | ||
517 | X = Xc; | |
518 | } | |
519 | ||
520 | // We are at the top of the tree | |
521 | assert(X.size() == 1); | |
522 | for (FullStepRow row : X[0]) { | |
523 | solns.insert(row.GetSolution()); | |
524 | } | |
39f5cb35 | 525 | goto deletesolution; |
c92c1f60 JG |
526 | |
527 | invalidsolution: | |
528 | invalidCount++; | |
39f5cb35 JG |
529 | |
530 | deletesolution: | |
531 | delete[] partialSoln; | |
c92c1f60 JG |
532 | } |
533 | LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount); | |
534 | ||
535 | return solns; | |
536 | } | |
537 | ||
6d25662f JG |
538 | bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln) |
539 | { | |
675e1702 | 540 | eh_index soln_size { 1u << k }; |
6d25662f JG |
541 | if (soln.size() != soln_size) { |
542 | LogPrint("pow", "Invalid solution size: %d\n", soln.size()); | |
543 | return false; | |
544 | } | |
545 | ||
a3361e77 | 546 | std::vector<FullStepRow> X; |
6d25662f JG |
547 | X.reserve(soln_size); |
548 | for (eh_index i : soln) { | |
549 | X.emplace_back(n, base_state, i); | |
550 | } | |
551 | ||
552 | while (X.size() > 1) { | |
a3361e77 | 553 | std::vector<FullStepRow> Xc; |
6d25662f JG |
554 | for (int i = 0; i < X.size(); i += 2) { |
555 | if (!HasCollision(X[i], X[i+1], CollisionByteLength())) { | |
556 | LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n"); | |
557 | LogPrint("pow", "X[i] = %s\n", X[i].GetHex()); | |
558 | LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex()); | |
559 | return false; | |
560 | } | |
561 | if (X[i+1].IndicesBefore(X[i])) { | |
562 | return false; | |
563 | LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n"); | |
564 | } | |
565 | if (!DistinctIndices(X[i], X[i+1])) { | |
566 | LogPrint("pow", "Invalid solution: duplicate indices\n"); | |
567 | return false; | |
568 | } | |
569 | Xc.push_back(X[i] ^ X[i+1]); | |
570 | Xc.back().TrimHash(CollisionByteLength()); | |
571 | } | |
572 | X = Xc; | |
573 | } | |
574 | ||
575 | assert(X.size() == 1); | |
576 | return X[0].IsZero(); | |
577 | } |