// Copyright (c) 2016-2020 The Hush developers // Copyright (c) 2016 Jack Grigg // Copyright (c) 2016 The Zcash developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html #ifndef HUSH_EQUIHASH_H #define HUSH_EQUIHASH_H #include "crypto/sha256.h" #include "utilstrencodings.h" #include "sodium.h" #include "hush_nk.h" #include #include #include #include #include #include #include #include typedef crypto_generichash_blake2b_state eh_HashState; typedef uint32_t eh_index; typedef uint8_t eh_trunc; #define BLAKE2B_OUTBYTES 64 extern uint64_t ASSETCHAINS_NK[2]; 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 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); eh_index ArrayToEhIndex(const unsigned char* array); eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen); std::vector GetIndicesFromMinimal(std::vector minimal, size_t cBitLen); std::vector GetMinimalFromIndices(std::vector indices, size_t cBitLen); template class StepRow { template friend class StepRow; friend class CompareSR; protected: unsigned char hash[WIDTH]; public: StepRow(const unsigned char* hashIn, size_t hInLen, size_t hLen, size_t cBitLen); ~StepRow() { } template StepRow(const StepRow& a); bool IsZero(size_t len); std::string GetHex(size_t len) { return HexStr(hash, hash+len); } template friend bool HasCollision(StepRow& a, StepRow& b, size_t l); }; class CompareSR { private: size_t len; public: CompareSR(size_t l) : len {l} { } template inline bool operator()(const StepRow& a, const StepRow& b) { return memcmp(a.hash, b.hash, len) < 0; } }; template bool HasCollision(StepRow& a, StepRow& b, size_t l); template class FullStepRow : public StepRow { template friend class FullStepRow; using StepRow::hash; public: FullStepRow(const unsigned char* hashIn, size_t hInLen, size_t hLen, size_t cBitLen, eh_index i); ~FullStepRow() { } FullStepRow(const FullStepRow& a) : StepRow {a} { } template FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, size_t trim); FullStepRow& operator=(const FullStepRow& a); inline bool IndicesBefore(const FullStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } std::vector GetIndices(size_t len, size_t lenIndices, size_t cBitLen) const; template friend bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices); template friend bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t); }; template class TruncatedStepRow : public StepRow { template friend class TruncatedStepRow; using StepRow::hash; public: TruncatedStepRow(const unsigned char* hashIn, size_t hInLen, size_t hLen, size_t cBitLen, eh_index i, unsigned int ilen); ~TruncatedStepRow() { } TruncatedStepRow(const TruncatedStepRow& a) : StepRow {a} { } template TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim); TruncatedStepRow& operator=(const TruncatedStepRow& a); inline bool IndicesBefore(const TruncatedStepRow& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; } std::shared_ptr GetTruncatedIndices(size_t len, size_t lenIndices) const; }; enum EhSolverCancelCheck { ListGeneration, ListSorting, ListColliding, RoundEnd, FinalSorting, FinalColliding, PartialGeneration, PartialSorting, PartialSubtreeEnd, PartialIndexEnd, PartialEnd }; class EhSolverCancelledException : public std::exception { virtual const char* what() const throw() { return "Equihash solver was cancelled"; } }; inline constexpr const size_t max(const size_t A, const size_t B) { return A > B ? A : B; } inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) { return (1 << K)*(N/(K+1)+1)/8; } constexpr uint8_t GetSizeInBytes(size_t N) { return static_cast((N + 7) / 8); } template class Equihash { private: BOOST_STATIC_ASSERT(K < N); //BOOST_STATIC_ASSERT(N % 8 == 0); BOOST_STATIC_ASSERT((N/(K+1)) + 1 < 8*sizeof(eh_index)); public: enum : size_t { IndicesPerHashOutput=512/N }; enum : size_t { HashOutput = IndicesPerHashOutput * GetSizeInBytes(N) }; enum : size_t { CollisionBitLength=N/(K+1) }; enum : size_t { CollisionByteLength=(CollisionBitLength+7)/8 }; enum : size_t { HashLength=(K+1)*CollisionByteLength }; enum : size_t { FullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K-1)) }; enum : size_t { FinalFullWidth=2*CollisionByteLength+sizeof(eh_index)*(1 << (K)) }; 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))) }; enum : size_t { SolutionWidth=(1 << K)*(CollisionBitLength+1)/8 }; Equihash() { } int InitialiseState(eh_HashState& base_state); #ifdef ENABLE_MINING bool BasicSolve(const eh_HashState& base_state, const std::function&)> validBlock, const std::function cancelled); bool OptimisedSolve(const eh_HashState& base_state, const std::function&)> validBlock, const std::function cancelled); #endif bool IsValidSolution(const eh_HashState& base_state, std::vector soln); }; #include "equihash.tcc" /* * Equihash 200,9 (KMD/Zcash) * Equihash 150,5 (beam) * Equihash 144,5 (SnowGem) * Equihash 96,5 (Minex) * Equihash 48,5 (regtest) * Equihash 210,9 (Aion) */ static Equihash<200,9> Eh200_9; static Equihash<150,5> Eh150_5; static Equihash<144,5> Eh144_5; static Equihash Eh96_5; static Equihash<48,5> Eh48_5; static Equihash<210,9> Eh210_9; #define EhInitialiseState(n, k, base_state) \ if (n == 200 && k == 9) { \ Eh200_9.InitialiseState(base_state); \ } else if (n == 150 && k == 5) { \ Eh150_5.InitialiseState(base_state); \ } else if (n == 144 && k == 5) { \ Eh144_5.InitialiseState(base_state); \ } else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) { \ Eh96_5.InitialiseState(base_state); \ } else if (n == 48 && k == 5) { \ Eh48_5.InitialiseState(base_state); \ } else if (n == 210 && k == 9) { \ Eh210_9.InitialiseState(base_state); \ } else { \ throw std::invalid_argument("Unsupported Equihash parameters"); \ } #ifdef ENABLE_MINING inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state, const std::function&)> validBlock, const std::function cancelled) { if (n == 200 && k == 9) { return Eh200_9.BasicSolve(base_state, validBlock, cancelled); } else if (n == 150 && k == 5) { return Eh150_5.BasicSolve(base_state, validBlock, cancelled); } else if (n == 144 && k == 5) { return Eh144_5.BasicSolve(base_state, validBlock, cancelled); } else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) { return Eh96_5.BasicSolve(base_state, validBlock, cancelled); } else if (n == 48 && k == 5) { return Eh48_5.BasicSolve(base_state, validBlock, cancelled); } else if (n == 210 && k == 9) { return Eh210_9.BasicSolve(base_state, validBlock, cancelled); } else { throw std::invalid_argument("Unsupported Equihash parameters"); } } inline bool EhBasicSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state, const std::function&)> validBlock) { 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, const std::function&)> validBlock, const std::function cancelled) { if (n == 200 && k == 9) { return Eh200_9.OptimisedSolve(base_state, validBlock, cancelled); } else if (n == 150 && k == 5) { return Eh150_5.OptimisedSolve(base_state, validBlock, cancelled); } else if (n == 144 && k == 5) { return Eh144_5.OptimisedSolve(base_state, validBlock, cancelled); } else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) { return Eh96_5.OptimisedSolve(base_state, validBlock, cancelled); } else if (n == 48 && k == 5) { return Eh48_5.OptimisedSolve(base_state, validBlock, cancelled); } else if (n == 210 && k == 9) { return Eh210_9.OptimisedSolve(base_state, validBlock, cancelled); } else { throw std::invalid_argument("Unsupported Equihash parameters"); } } inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state, const std::function&)> validBlock) { return EhOptimisedSolve(n, k, base_state, validBlock, [](EhSolverCancelCheck pos) { return false; }); } #endif // ENABLE_MINING #define EhIsValidSolution(n, k, base_state, soln, ret) \ if (n == 200 && k == 9) { \ ret = Eh200_9.IsValidSolution(base_state, soln); \ } else if (n == 150 && k == 5) { \ ret = Eh150_5.IsValidSolution(base_state, soln); \ } else if (n == 144 && k == 5) { \ ret = Eh144_5.IsValidSolution(base_state, soln); \ } else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) { \ ret = Eh96_5.IsValidSolution(base_state, soln); \ } else if (n == 48 && k == 5) { \ ret = Eh48_5.IsValidSolution(base_state, soln); \ } else if (n == 210 && k == 9) { \ ret = Eh210_9.IsValidSolution(base_state, soln); \ } else { \ throw std::invalid_argument("Unsupported Equihash parameters"); \ } #endif // HUSH_EQUIHASH_H