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