2016-02-28 12:15:53 -08:00
|
|
|
// Copyright (c) 2016 Jack Grigg
|
2022-05-11 15:47:12 -07:00
|
|
|
// Copyright (c) 2016-2022 The Zcash developers
|
2016-02-28 12:15:53 -08:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2019-07-18 07:16:09 -07:00
|
|
|
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2020-10-27 04:46:32 -07:00
|
|
|
#ifndef ZCASH_CRYPTO_EQUIHASH_H
|
|
|
|
#define ZCASH_CRYPTO_EQUIHASH_H
|
2020-07-25 07:41:39 -07:00
|
|
|
|
2020-07-25 08:33:02 -07:00
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
2020-07-25 07:41:39 -07:00
|
|
|
|
|
|
|
inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) {
|
|
|
|
return (1 << K)*(N/(K+1)+1)/8;
|
|
|
|
}
|
|
|
|
|
2020-07-25 08:33:02 -07:00
|
|
|
typedef uint32_t eh_index;
|
|
|
|
typedef uint8_t eh_trunc;
|
|
|
|
|
|
|
|
std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
|
|
|
|
size_t cBitLen);
|
|
|
|
void CompressArray(const unsigned char* in, size_t in_len,
|
|
|
|
unsigned char* out, size_t out_len,
|
|
|
|
size_t bit_len, size_t byte_pad=0);
|
|
|
|
void ExpandArray(const unsigned char* in, size_t in_len,
|
|
|
|
unsigned char* out, size_t out_len,
|
|
|
|
size_t bit_len, size_t byte_pad=0);
|
|
|
|
void EhIndexToArray(const eh_index i, unsigned char* array);
|
|
|
|
|
2020-07-25 07:41:39 -07:00
|
|
|
|
2020-07-14 04:15:07 -07:00
|
|
|
#ifdef ENABLE_MINING
|
2016-02-28 12:15:53 -08:00
|
|
|
|
|
|
|
#include "crypto/sha256.h"
|
|
|
|
#include "utilstrencodings.h"
|
|
|
|
|
|
|
|
#include <cstring>
|
2016-07-20 00:06:49 -07:00
|
|
|
#include <exception>
|
2020-05-24 15:49:13 -07:00
|
|
|
#include <stdexcept>
|
2016-07-20 00:06:49 -07:00
|
|
|
#include <functional>
|
2016-02-28 12:15:53 -08:00
|
|
|
#include <set>
|
|
|
|
|
2020-07-14 03:43:09 -07:00
|
|
|
#include <rust/blake2b.h>
|
|
|
|
|
|
|
|
struct eh_HashState {
|
|
|
|
std::unique_ptr<BLAKE2bState, decltype(&blake2b_free)> inner;
|
|
|
|
|
|
|
|
eh_HashState() : inner(nullptr, blake2b_free) {}
|
|
|
|
|
|
|
|
eh_HashState(size_t length, unsigned char personalization[BLAKE2bPersonalBytes]) : inner(blake2b_init(length, personalization), blake2b_free) {}
|
2016-05-04 20:00:44 -07:00
|
|
|
|
2020-07-14 03:43:09 -07:00
|
|
|
eh_HashState(eh_HashState&& baseState) : inner(std::move(baseState.inner)) {}
|
|
|
|
eh_HashState(const eh_HashState& baseState) : inner(blake2b_clone(baseState.inner.get()), blake2b_free) {}
|
|
|
|
eh_HashState& operator=(eh_HashState&& baseState)
|
|
|
|
{
|
|
|
|
if (this != &baseState) {
|
|
|
|
inner = std::move(baseState.inner);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
eh_HashState& operator=(const eh_HashState& baseState)
|
|
|
|
{
|
|
|
|
if (this != &baseState) {
|
|
|
|
inner.reset(blake2b_clone(baseState.inner.get()));
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Update(const unsigned char *input, size_t inputLen);
|
|
|
|
void Finalize(unsigned char *hash, size_t hLen);
|
|
|
|
};
|
2016-08-12 21:45:53 -07:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
eh_index ArrayToEhIndex(const unsigned char* array);
|
|
|
|
eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen);
|
2016-05-05 19:13:15 -07:00
|
|
|
|
2016-08-13 20:04:13 -07:00
|
|
|
std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
|
|
|
|
size_t cBitLen);
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t WIDTH>
|
2016-02-28 12:15:53 -08:00
|
|
|
class StepRow
|
|
|
|
{
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
|
|
|
friend class StepRow;
|
2016-05-05 17:17:42 -07:00
|
|
|
friend class CompareSR;
|
|
|
|
|
2016-04-15 06:14:37 -07:00
|
|
|
protected:
|
2016-05-06 21:36:54 -07:00
|
|
|
unsigned char hash[WIDTH];
|
2016-02-28 12:15:53 -08:00
|
|
|
|
|
|
|
public:
|
2016-08-13 06:14:09 -07:00
|
|
|
StepRow(const unsigned char* hashIn, size_t hInLen,
|
|
|
|
size_t hLen, size_t cBitLen);
|
2016-05-06 21:36:54 -07:00
|
|
|
~StepRow() { }
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
|
|
|
StepRow(const StepRow<W>& a);
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
bool IsZero(size_t len);
|
|
|
|
std::string GetHex(size_t len) { return HexStr(hash, hash+len); }
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
|
|
|
friend bool HasCollision(StepRow<W>& a, StepRow<W>& b, int l);
|
2016-02-28 12:15:53 -08:00
|
|
|
};
|
|
|
|
|
2016-05-05 17:17:42 -07:00
|
|
|
class CompareSR
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CompareSR(size_t l) : len {l} { }
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
|
|
|
inline bool operator()(const StepRow<W>& a, const StepRow<W>& b) { return memcmp(a.hash, b.hash, len) < 0; }
|
2016-05-05 17:17:42 -07:00
|
|
|
};
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t WIDTH>
|
|
|
|
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l);
|
2016-04-15 06:14:37 -07:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t WIDTH>
|
|
|
|
class FullStepRow : public StepRow<WIDTH>
|
2016-04-15 06:14:37 -07:00
|
|
|
{
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
|
|
|
friend class FullStepRow;
|
|
|
|
|
|
|
|
using StepRow<WIDTH>::hash;
|
2016-04-15 06:14:37 -07:00
|
|
|
|
|
|
|
public:
|
2016-08-13 06:14:09 -07:00
|
|
|
FullStepRow(const unsigned char* hashIn, size_t hInLen,
|
|
|
|
size_t hLen, size_t cBitLen, eh_index i);
|
2016-04-15 06:14:37 -07:00
|
|
|
~FullStepRow() { }
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
FullStepRow(const FullStepRow<WIDTH>& a) : StepRow<WIDTH> {a} { }
|
|
|
|
template<size_t W>
|
2020-11-09 12:20:41 -08:00
|
|
|
FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int lenTrim);
|
2016-05-06 21:36:54 -07:00
|
|
|
FullStepRow& operator=(const FullStepRow<WIDTH>& a);
|
2016-04-15 06:14:37 -07:00
|
|
|
|
2016-06-08 23:02:29 -07:00
|
|
|
inline bool IndicesBefore(const FullStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
|
2016-08-13 20:04:13 -07:00
|
|
|
std::vector<unsigned char> GetIndices(size_t len, size_t lenIndices,
|
|
|
|
size_t cBitLen) const;
|
2016-04-15 06:14:37 -07:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
2016-08-13 20:04:13 -07:00
|
|
|
friend bool DistinctIndices(const FullStepRow<W>& a, const FullStepRow<W>& b,
|
|
|
|
size_t len, size_t lenIndices);
|
|
|
|
template<size_t W>
|
2016-05-06 21:36:54 -07:00
|
|
|
friend bool IsValidBranch(const FullStepRow<W>& a, const size_t len, const unsigned int ilen, const eh_trunc t);
|
2016-04-15 06:14:37 -07:00
|
|
|
};
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t WIDTH>
|
|
|
|
class TruncatedStepRow : public StepRow<WIDTH>
|
2016-04-18 18:41:06 -07:00
|
|
|
{
|
2016-05-06 21:36:54 -07:00
|
|
|
template<size_t W>
|
|
|
|
friend class TruncatedStepRow;
|
|
|
|
|
|
|
|
using StepRow<WIDTH>::hash;
|
2016-04-18 18:41:06 -07:00
|
|
|
|
|
|
|
public:
|
2016-08-13 06:14:09 -07:00
|
|
|
TruncatedStepRow(const unsigned char* hashIn, size_t hInLen,
|
|
|
|
size_t hLen, size_t cBitLen,
|
|
|
|
eh_index i, unsigned int ilen);
|
2016-04-18 18:41:06 -07:00
|
|
|
~TruncatedStepRow() { }
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
TruncatedStepRow(const TruncatedStepRow<WIDTH>& a) : StepRow<WIDTH> {a} { }
|
|
|
|
template<size_t W>
|
2020-11-09 12:20:41 -08:00
|
|
|
TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int lenTrim);
|
2016-05-06 21:36:54 -07:00
|
|
|
TruncatedStepRow& operator=(const TruncatedStepRow<WIDTH>& a);
|
2016-04-18 18:41:06 -07:00
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
inline bool IndicesBefore(const TruncatedStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
|
2016-07-22 05:31:47 -07:00
|
|
|
std::shared_ptr<eh_trunc> GetTruncatedIndices(size_t len, size_t lenIndices) const;
|
2016-04-18 18:41:06 -07:00
|
|
|
};
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2016-07-20 21:39:32 -07:00
|
|
|
enum EhSolverCancelCheck
|
|
|
|
{
|
|
|
|
ListGeneration,
|
|
|
|
ListSorting,
|
|
|
|
ListColliding,
|
|
|
|
RoundEnd,
|
|
|
|
FinalSorting,
|
|
|
|
FinalColliding,
|
|
|
|
PartialGeneration,
|
|
|
|
PartialSorting,
|
|
|
|
PartialSubtreeEnd,
|
|
|
|
PartialIndexEnd,
|
|
|
|
PartialEnd
|
|
|
|
};
|
|
|
|
|
2016-07-20 00:06:49 -07:00
|
|
|
class EhSolverCancelledException : public std::exception
|
|
|
|
{
|
2016-07-20 21:39:32 -07:00
|
|
|
virtual const char* what() const throw() {
|
|
|
|
return "Equihash solver was cancelled";
|
|
|
|
}
|
2016-07-20 00:06:49 -07:00
|
|
|
};
|
|
|
|
|
2016-06-07 22:08:18 -07:00
|
|
|
inline constexpr const size_t max(const size_t A, const size_t B) { return A > B ? A : B; }
|
|
|
|
|
2016-05-04 20:00:44 -07:00
|
|
|
template<unsigned int N, unsigned int K>
|
2016-02-28 12:15:53 -08:00
|
|
|
class Equihash
|
|
|
|
{
|
|
|
|
private:
|
2020-10-20 11:54:44 -07:00
|
|
|
static_assert(K < N);
|
|
|
|
static_assert(N % 8 == 0);
|
|
|
|
static_assert((N/(K+1)) + 1 < 8*sizeof(eh_index));
|
2016-02-28 12:15:53 -08:00
|
|
|
|
|
|
|
public:
|
2016-08-13 06:14:09 -07:00
|
|
|
enum : size_t { IndicesPerHashOutput=512/N };
|
|
|
|
enum : size_t { HashOutput=IndicesPerHashOutput*N/8 };
|
|
|
|
enum : size_t { CollisionBitLength=N/(K+1) };
|
2016-08-05 07:34:07 -07:00
|
|
|
enum : size_t { CollisionByteLength=(CollisionBitLength+7)/8 };
|
|
|
|
enum : size_t { HashLength=(K+1)*CollisionByteLength };
|
2016-05-06 21:36:54 -07:00
|
|
|
enum : size_t { FullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K-1)) };
|
|
|
|
enum : size_t { FinalFullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K)) };
|
2016-08-05 07:34:07 -07:00
|
|
|
enum : size_t { TruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K-1))) };
|
|
|
|
enum : size_t { FinalTruncatedWidth=max(HashLength+sizeof(eh_trunc), 2*CollisionByteLength+sizeof(eh_trunc)*(1 << (K))) };
|
2016-08-13 20:04:13 -07:00
|
|
|
enum : size_t { SolutionWidth=(1 << K)*(CollisionBitLength+1)/8 };
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2016-05-04 20:00:44 -07:00
|
|
|
Equihash() { }
|
2016-02-28 12:15:53 -08:00
|
|
|
|
2020-07-14 03:43:09 -07:00
|
|
|
void InitialiseState(eh_HashState& base_state);
|
2016-07-27 00:15:49 -07:00
|
|
|
bool BasicSolve(const eh_HashState& base_state,
|
2016-08-13 20:04:13 -07:00
|
|
|
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
2016-07-27 00:15:49 -07:00
|
|
|
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
|
|
|
bool OptimisedSolve(const eh_HashState& base_state,
|
2016-08-13 20:04:13 -07:00
|
|
|
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
2016-07-27 00:15:49 -07:00
|
|
|
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
2016-02-28 12:15:53 -08:00
|
|
|
};
|
|
|
|
|
2016-05-06 21:36:54 -07:00
|
|
|
#include "equihash.tcc"
|
|
|
|
|
2016-06-07 22:08:18 -07:00
|
|
|
static Equihash<96,3> Eh96_3;
|
2016-08-05 15:29:09 -07:00
|
|
|
static Equihash<200,9> Eh200_9;
|
2016-06-01 04:15:04 -07:00
|
|
|
static Equihash<96,5> Eh96_5;
|
|
|
|
static Equihash<48,5> Eh48_5;
|
|
|
|
|
|
|
|
#define EhInitialiseState(n, k, base_state) \
|
2016-06-07 22:08:18 -07:00
|
|
|
if (n == 96 && k == 3) { \
|
|
|
|
Eh96_3.InitialiseState(base_state); \
|
2016-08-05 15:29:09 -07:00
|
|
|
} else if (n == 200 && k == 9) { \
|
|
|
|
Eh200_9.InitialiseState(base_state); \
|
2016-06-07 22:08:18 -07:00
|
|
|
} else if (n == 96 && k == 5) { \
|
2016-06-01 04:15:04 -07:00
|
|
|
Eh96_5.InitialiseState(base_state); \
|
|
|
|
} else if (n == 48 && k == 5) { \
|
|
|
|
Eh48_5.InitialiseState(base_state); \
|
|
|
|
} else { \
|
2016-05-04 20:00:44 -07:00
|
|
|
throw std::invalid_argument("Unsupported Equihash parameters"); \
|
|
|
|
}
|
|
|
|
|
2016-07-27 00:15:49 -07:00
|
|
|
inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
2016-08-13 20:04:13 -07:00
|
|
|
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
2016-07-27 00:15:49 -07:00
|
|
|
const std::function<bool(EhSolverCancelCheck)> cancelled)
|
|
|
|
{
|
|
|
|
if (n == 96 && k == 3) {
|
|
|
|
return Eh96_3.BasicSolve(base_state, validBlock, cancelled);
|
2016-08-05 15:29:09 -07:00
|
|
|
} else if (n == 200 && k == 9) {
|
|
|
|
return Eh200_9.BasicSolve(base_state, validBlock, cancelled);
|
2016-07-27 00:15:49 -07:00
|
|
|
} else if (n == 96 && k == 5) {
|
|
|
|
return Eh96_5.BasicSolve(base_state, validBlock, cancelled);
|
|
|
|
} else if (n == 48 && k == 5) {
|
|
|
|
return Eh48_5.BasicSolve(base_state, validBlock, cancelled);
|
|
|
|
} else {
|
|
|
|
throw std::invalid_argument("Unsupported Equihash parameters");
|
2016-05-04 20:00:44 -07:00
|
|
|
}
|
2016-07-27 00:15:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool EhBasicSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
2016-08-13 20:04:13 -07:00
|
|
|
const std::function<bool(std::vector<unsigned char>)> validBlock)
|
2016-07-27 00:15:49 -07:00
|
|
|
{
|
|
|
|
return EhBasicSolve(n, k, base_state, validBlock,
|
|
|
|
[](EhSolverCancelCheck pos) { return false; });
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
2016-08-13 20:04:13 -07:00
|
|
|
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
2016-07-27 00:15:49 -07:00
|
|
|
const std::function<bool(EhSolverCancelCheck)> cancelled)
|
|
|
|
{
|
|
|
|
if (n == 96 && k == 3) {
|
|
|
|
return Eh96_3.OptimisedSolve(base_state, validBlock, cancelled);
|
2016-08-05 15:29:09 -07:00
|
|
|
} else if (n == 200 && k == 9) {
|
|
|
|
return Eh200_9.OptimisedSolve(base_state, validBlock, cancelled);
|
2016-07-27 00:15:49 -07:00
|
|
|
} else if (n == 96 && k == 5) {
|
|
|
|
return Eh96_5.OptimisedSolve(base_state, validBlock, cancelled);
|
|
|
|
} else if (n == 48 && k == 5) {
|
|
|
|
return Eh48_5.OptimisedSolve(base_state, validBlock, cancelled);
|
|
|
|
} else {
|
|
|
|
throw std::invalid_argument("Unsupported Equihash parameters");
|
2016-05-04 20:00:44 -07:00
|
|
|
}
|
2016-07-27 00:15:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
2016-08-13 20:04:13 -07:00
|
|
|
const std::function<bool(std::vector<unsigned char>)> validBlock)
|
2016-07-27 00:15:49 -07:00
|
|
|
{
|
|
|
|
return EhOptimisedSolve(n, k, base_state, validBlock,
|
|
|
|
[](EhSolverCancelCheck pos) { return false; });
|
|
|
|
}
|
2016-11-06 11:40:34 -08:00
|
|
|
#endif // ENABLE_MINING
|
2016-05-04 20:00:44 -07:00
|
|
|
|
2020-10-27 04:46:32 -07:00
|
|
|
#endif // ZCASH_CRYPTO_EQUIHASH_H
|