diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/Makefile.am b/Makefile.am index 3442b65e6..be0ec4312 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,7 +86,7 @@ $(OSX_APP)/Contents/PkgInfo: $(OSX_APP)/Contents/Resources/empty.lproj: $(MKDIR_P) $(@D) - @touch $@ + @touch $@ $(OSX_APP)/Contents/Info.plist: $(OSX_PLIST) $(MKDIR_P) $(@D) @@ -242,7 +242,7 @@ cov: test_bitcoin.coverage/.dirstamp cov-zcash total.coverage/.dirstamp endif -dist_bin_SCRIPTS = zcutil/fetch-params.sh +dist_bin_SCRIPTS = #zcutil/fetch-params.sh dist_noinst_SCRIPTS = autogen.sh EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional test/zcash $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) @@ -327,4 +327,3 @@ clean-docs: clean-local: clean-docs rm -rf coverage_percent.txt test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP) rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache - diff --git a/src/Makefile.am b/src/Makefile.am index 070fb3c1e..74e78a276 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -214,6 +214,7 @@ BITCOIN_CORE_H = \ wallet/walletutil.h \ wallet/coinselection.h \ warnings.h \ + zkaddr.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ @@ -366,6 +367,8 @@ libbitcoin_consensus_a_SOURCES = \ prevector.h \ primitives/block.cpp \ primitives/block.h \ + primitives/joinsplit.h \ + primitives/joinsplit.cpp \ primitives/transaction.cpp \ primitives/transaction.h \ pubkey.cpp \ @@ -410,6 +413,7 @@ libbitcoin_common_a_SOURCES = \ script/sign.cpp \ script/standard.cpp \ warnings.cpp \ + zkaddr.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. @@ -550,7 +554,7 @@ libzcash_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN1 libzcash_a_CXXFLAGS = $(SAN_CXXFLAGS) $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing -libzcash_a_LIBADD = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) +libzcash_a_LDFLAGS = $(SAN_LDFLAGS) $(HARDENED_LDFLAGS) libzcash_a_CPPFLAGS += -DMONTGOMERY_OUTPUT @@ -638,7 +642,7 @@ endif if ENABLE_TESTS include Makefile.test.include -include Makefile.gtest.include +#include Makefile.gtest.include endif if ENABLE_BENCH diff --git a/src/Makefile.test.include b/src/Makefile.test.include index c5b1c37bc..461af5931 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -77,6 +77,7 @@ BITCOIN_TESTS =\ test/script_standard_tests.cpp \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ + test/sha256compress_tests.cpp \ test/sighash_tests.cpp \ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ diff --git a/src/Makefile.zcash.include b/src/Makefile.zcash.include index 177931c88..1a1e726c3 100644 --- a/src/Makefile.zcash.include +++ b/src/Makefile.zcash.include @@ -24,5 +24,6 @@ zcash_CreateJoinSplit_LDADD = \ $(LIBSNARK) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CONSENSUS) \ $(BOOST_LIBS) \ $(LIBZCASH_LIBS) diff --git a/src/base58.h b/src/base58.h index 8f2833bec..902ac75ad 100644 --- a/src/base58.h +++ b/src/base58.h @@ -16,6 +16,7 @@ #include #include +#include /** * Encode a byte sequence as a base58-encoded string. @@ -58,4 +59,34 @@ bool DecodeBase58Check(const char* psz, std::vector& vchRet); */ bool DecodeBase58Check(const std::string& str, std::vector& vchRet); +/** + * Base class for all base58-encoded data + */ +class CBase58Data +{ +protected: + //! the version byte(s) + std::vector vchVersion; + + //! the actually encoded data + typedef std::vector > vector_uchar; + vector_uchar vchData; + + CBase58Data(); + void SetData(const std::vector &vchVersionIn, const void* pdata, size_t nSize); + void SetData(const std::vector &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend); + +public: + bool SetString(const char* psz, unsigned int nVersionBytes); + bool SetString(const std::string& str, unsigned int nVersionBytes); + std::string ToString() const; + int CompareTo(const CBase58Data& b58) const; + + bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; } + bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; } + bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; } + bool operator< (const CBase58Data& b58) const { return CompareTo(b58) < 0; } + bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } +}; + #endif // BITCOIN_BASE58_H diff --git a/src/chain.h b/src/chain.h index 8e6ac8d82..4b3470a09 100644 --- a/src/chain.h +++ b/src/chain.h @@ -211,7 +211,7 @@ public: uint256 hashMerkleRoot; uint32_t nTime; uint32_t nBits; - uint32_t nNonce; + uint256 nNonce; //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; @@ -239,7 +239,7 @@ public: hashMerkleRoot = uint256(); nTime = 0; nBits = 0; - nNonce = 0; + nNonce = uint256(); } CBlockIndex() diff --git a/src/crypto/common.h b/src/crypto/common.h index 6e9d6dc82..64bac448a 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -9,9 +9,10 @@ #include #endif +#include #include #include - +#include #include uint16_t static inline ReadLE16(const unsigned char* ptr) @@ -100,4 +101,42 @@ uint64_t static inline CountBits(uint64_t x) return ret; } +int inline init_and_check_sodium() +{ + if (sodium_init() == -1) { + return -1; + } + + // What follows is a runtime test that ensures the version of libsodium + // we're linked against checks that signatures are canonical (s < L). + const unsigned char message[1] = { 0 }; + + unsigned char pk[crypto_sign_PUBLICKEYBYTES]; + unsigned char sk[crypto_sign_SECRETKEYBYTES]; + unsigned char sig[crypto_sign_BYTES]; + + crypto_sign_keypair(pk, sk); + crypto_sign_detached(sig, NULL, message, sizeof(message), sk); + + assert(crypto_sign_verify_detached(sig, message, sizeof(message), pk) == 0); + + // Copied from libsodium/crypto_sign/ed25519/ref10/open.c + static const unsigned char L[32] = + { 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }; + + // Add L to S, which starts at sig[32]. + unsigned int s = 0; + for (size_t i = 0; i < 32; i++) { + s = sig[32 + i] + L[i] + (s >> 8); + sig[32 + i] = s & 0xff; + } + + assert(crypto_sign_verify_detached(sig, message, sizeof(message), pk) != 0); + + return 0; +} + #endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp new file mode 100644 index 000000000..358b231ea --- /dev/null +++ b/src/crypto/equihash.cpp @@ -0,0 +1,816 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Implementation of the Equihash Proof-of-Work algorithm. +// +// Reference +// ========= +// Alex Biryukov and Dmitry Khovratovich +// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem +// NDSS ’16, 21-24 February 2016, San Diego, CA, USA +// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf + +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif + +#include "compat/endian.h" +#include "crypto/equihash.h" +#include "util.h" + +#include +#include +#include + +#include + +EhSolverCancelledException solver_cancelled; + +template +int Equihash::InitialiseState(eh_HashState& base_state) +{ + uint32_t le_N = htole32(N); + uint32_t le_K = htole32(K); + unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {}; + memcpy(personalization, "ZcashPoW", 8); + memcpy(personalization+8, &le_N, 4); + memcpy(personalization+12, &le_K, 4); + return crypto_generichash_blake2b_init_salt_personal(&base_state, + NULL, 0, // No key. + (512/N)*N/8, + NULL, // No salt. + personalization); +} + +void GenerateHash(const eh_HashState& base_state, eh_index g, + unsigned char* hash, size_t hLen) +{ + eh_HashState state; + state = base_state; + eh_index lei = htole32(g); + crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei, + sizeof(eh_index)); + crypto_generichash_blake2b_final(&state, hash, hLen); +} + +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) +{ + assert(bit_len >= 8); + assert(8*sizeof(uint32_t) >= 7+bit_len); + + size_t out_width { (bit_len+7)/8 + byte_pad }; + assert(out_len == 8*out_width*in_len/bit_len); + + uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 }; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + size_t acc_bits = 0; + uint32_t acc_value = 0; + + size_t j = 0; + for (size_t i = 0; i < in_len; i++) { + acc_value = (acc_value << 8) | in[i]; + acc_bits += 8; + + // When we have bit_len or more bits in the accumulator, write the next + // output element. + if (acc_bits >= bit_len) { + acc_bits -= bit_len; + for (size_t x = 0; x < byte_pad; x++) { + out[j+x] = 0; + } + for (size_t x = byte_pad; x < out_width; x++) { + out[j+x] = ( + // Big-endian + acc_value >> (acc_bits+(8*(out_width-x-1))) + ) & ( + // Apply bit_len_mask across byte boundaries + (bit_len_mask >> (8*(out_width-x-1))) & 0xFF + ); + } + j += out_width; + } + } +} + +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) +{ + assert(bit_len >= 8); + assert(8*sizeof(uint32_t) >= 7+bit_len); + + size_t in_width { (bit_len+7)/8 + byte_pad }; + assert(out_len == bit_len*in_len/(8*in_width)); + + uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 }; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + size_t acc_bits = 0; + uint32_t acc_value = 0; + + size_t j = 0; + for (size_t i = 0; i < out_len; i++) { + // When we have fewer than 8 bits left in the accumulator, read the next + // input element. + if (acc_bits < 8) { + acc_value = acc_value << bit_len; + for (size_t x = byte_pad; x < in_width; x++) { + acc_value = acc_value | ( + ( + // Apply bit_len_mask across byte boundaries + in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF) + ) << (8*(in_width-x-1))); // Big-endian + } + j += in_width; + acc_bits += bit_len; + } + + acc_bits -= 8; + out[i] = (acc_value >> acc_bits) & 0xFF; + } +} + +// Big-endian so that lexicographic array comparison is equivalent to integer +// comparison +void EhIndexToArray(const eh_index i, unsigned char* array) +{ + BOOST_STATIC_ASSERT(sizeof(eh_index) == 4); + eh_index bei = htobe32(i); + memcpy(array, &bei, sizeof(eh_index)); +} + +// Big-endian so that lexicographic array comparison is equivalent to integer +// comparison +eh_index ArrayToEhIndex(const unsigned char* array) +{ + BOOST_STATIC_ASSERT(sizeof(eh_index) == 4); + eh_index bei; + memcpy(&bei, array, sizeof(eh_index)); + return be32toh(bei); +} + +eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen) +{ + // Truncate to 8 bits + BOOST_STATIC_ASSERT(sizeof(eh_trunc) == 1); + return (i >> (ilen - 8)) & 0xff; +} + +eh_index UntruncateIndex(const eh_trunc t, const eh_index r, const unsigned int ilen) +{ + eh_index i{t}; + return (i << (ilen - 8)) | r; +} + +std::vector GetIndicesFromMinimal(std::vector minimal, + size_t cBitLen) +{ + assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); + size_t lenIndices { 8*sizeof(eh_index)*minimal.size()/(cBitLen+1) }; + size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; + std::vector array(lenIndices); + ExpandArray(minimal.data(), minimal.size(), + array.data(), lenIndices, cBitLen+1, bytePad); + std::vector ret; + for (int i = 0; i < lenIndices; i += sizeof(eh_index)) { + ret.push_back(ArrayToEhIndex(array.data()+i)); + } + return ret; +} + +std::vector GetMinimalFromIndices(std::vector indices, + size_t cBitLen) +{ + assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); + size_t lenIndices { indices.size()*sizeof(eh_index) }; + size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) }; + size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; + std::vector array(lenIndices); + for (int i = 0; i < indices.size(); i++) { + EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index))); + } + std::vector ret(minLen); + CompressArray(array.data(), lenIndices, + ret.data(), minLen, cBitLen+1, bytePad); + return ret; +} + +template +StepRow::StepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen) +{ + assert(hLen <= WIDTH); + ExpandArray(hashIn, hInLen, hash, hLen, cBitLen); +} + +template template +StepRow::StepRow(const StepRow& a) +{ + BOOST_STATIC_ASSERT(W <= WIDTH); + std::copy(a.hash, a.hash+W, hash); +} + +template +FullStepRow::FullStepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen, eh_index i) : + StepRow {hashIn, hInLen, hLen, cBitLen} +{ + EhIndexToArray(i, hash+hLen); +} + +template template +FullStepRow::FullStepRow(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices, int trim) : + StepRow {a} +{ + assert(len+lenIndices <= W); + assert(len-trim+(2*lenIndices) <= WIDTH); + for (int i = trim; i < len; i++) + hash[i-trim] = a.hash[i] ^ b.hash[i]; + if (a.IndicesBefore(b, len, lenIndices)) { + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); + } else { + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); + } +} + +template +FullStepRow& FullStepRow::operator=(const FullStepRow& a) +{ + std::copy(a.hash, a.hash+WIDTH, hash); + return *this; +} + +template +bool StepRow::IsZero(size_t len) +{ + // This doesn't need to be constant time. + for (int i = 0; i < len; i++) { + if (hash[i] != 0) + return false; + } + return true; +} + +template +std::vector FullStepRow::GetIndices(size_t len, size_t lenIndices, + size_t cBitLen) const +{ + assert(((cBitLen+1)+7)/8 <= sizeof(eh_index)); + size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) }; + size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 }; + std::vector ret(minLen); + CompressArray(hash+len, lenIndices, ret.data(), minLen, cBitLen+1, bytePad); + return ret; +} + +template +bool HasCollision(StepRow& a, StepRow& b, int l) +{ + // This doesn't need to be constant time. + for (int j = 0; j < l; j++) { + if (a.hash[j] != b.hash[j]) + return false; + } + return true; +} + +template +TruncatedStepRow::TruncatedStepRow(const unsigned char* hashIn, size_t hInLen, + size_t hLen, size_t cBitLen, + eh_index i, unsigned int ilen) : + StepRow {hashIn, hInLen, hLen, cBitLen} +{ + hash[hLen] = TruncateIndex(i, ilen); +} + +template template +TruncatedStepRow::TruncatedStepRow(const TruncatedStepRow& a, const TruncatedStepRow& b, size_t len, size_t lenIndices, int trim) : + StepRow {a} +{ + assert(len+lenIndices <= W); + assert(len-trim+(2*lenIndices) <= WIDTH); + for (int i = trim; i < len; i++) + hash[i-trim] = a.hash[i] ^ b.hash[i]; + if (a.IndicesBefore(b, len, lenIndices)) { + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim); + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim+lenIndices); + } else { + std::copy(b.hash+len, b.hash+len+lenIndices, hash+len-trim); + std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim+lenIndices); + } +} + +template +TruncatedStepRow& TruncatedStepRow::operator=(const TruncatedStepRow& a) +{ + std::copy(a.hash, a.hash+WIDTH, hash); + return *this; +} + +template +std::shared_ptr TruncatedStepRow::GetTruncatedIndices(size_t len, size_t lenIndices) const +{ + std::shared_ptr p (new eh_trunc[lenIndices], std::default_delete()); + std::copy(hash+len, hash+len+lenIndices, p.get()); + return p; +} + +#ifdef ENABLE_MINING +template +bool Equihash::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + eh_index init_size { 1 << (CollisionBitLength + 1) }; + + // 1) Generate first list + LogPrint(BCLog::EQUIHASH, "Generating first list\n"); + size_t hashLen = HashLength; + size_t lenIndices = sizeof(eh_index); + std::vector> X; + X.reserve(init_size); + unsigned char tmpHash[HashOutput]; + for (eh_index g = 0; X.size() < init_size; g++) { + GenerateHash(base_state, g, tmpHash, HashOutput); + for (eh_index i = 0; i < IndicesPerHashOutput && X.size() < init_size; i++) { + X.emplace_back(tmpHash+(i*N/8), N/8, HashLength, + CollisionBitLength, (g*IndicesPerHashOutput)+i); + } + if (cancelled(ListGeneration)) throw solver_cancelled; + } + + // 3) Repeat step 2 until 2n/(k+1) bits remain + for (int r = 1; r < K && X.size() > 0; r++) { + LogPrint(BCLog::EQUIHASH, "Round %d:\n", r); + // 2a) Sort the list + LogPrint(BCLog::EQUIHASH, "- Sorting list\n"); + std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength)); + if (cancelled(ListSorting)) throw solver_cancelled; + + LogPrint(BCLog::EQUIHASH, "- Finding collisions\n"); + int i = 0; + int posFree = 0; + std::vector> Xc; + while (i < X.size() - 1) { + // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits + int j = 1; + while (i+j < X.size() && + HasCollision(X[i], X[i+j], CollisionByteLength)) { + j++; + } + + // 2c) Calculate tuples (X_i ^ X_j, (i, j)) + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) { + Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength); + } + } + } + + // 2d) Store tuples on the table in-place if possible + while (posFree < i+j && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + i += j; + if (cancelled(ListColliding)) throw solver_cancelled; + } + + // 2e) Handle edge case where final table entry has no collision + while (posFree < X.size() && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + if (Xc.size() > 0) { + // 2f) Add overflow to end of table + X.insert(X.end(), Xc.begin(), Xc.end()); + } else if (posFree < X.size()) { + // 2g) Remove empty space at the end + X.erase(X.begin()+posFree, X.end()); + X.shrink_to_fit(); + } + + hashLen -= CollisionByteLength; + lenIndices *= 2; + if (cancelled(RoundEnd)) throw solver_cancelled; + } + + // k+1) Find a collision on last 2n(k+1) bits + LogPrint(BCLog::EQUIHASH, "Final round:\n"); + if (X.size() > 1) { + LogPrint(BCLog::EQUIHASH, "- Sorting list\n"); + std::sort(X.begin(), X.end(), CompareSR(hashLen)); + if (cancelled(FinalSorting)) throw solver_cancelled; + LogPrint(BCLog::EQUIHASH, "- Finding collisions\n"); + int i = 0; + while (i < X.size() - 1) { + int j = 1; + while (i+j < X.size() && + HasCollision(X[i], X[i+j], hashLen)) { + j++; + } + + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + FullStepRow res(X[i+l], X[i+m], hashLen, lenIndices, 0); + if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) { + auto soln = res.GetIndices(hashLen, 2*lenIndices, CollisionBitLength); + assert(soln.size() == equihash_solution_size(N, K)); + if (validBlock(soln)) { + return true; + } + } + } + } + + i += j; + if (cancelled(FinalColliding)) throw solver_cancelled; + } + } else + LogPrint(BCLog::EQUIHASH, "- List is empty\n"); + + return false; +} + +template +void CollideBranches(std::vector>& 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) +{ + int i = 0; + int posFree = 0; + std::vector> Xc; + while (i < X.size() - 1) { + // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits + int j = 1; + while (i+j < X.size() && + HasCollision(X[i], X[i+j], clen)) { + j++; + } + + // 2c) Calculate tuples (X_i ^ X_j, (i, j)) + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) { + if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) { + Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen); + } else if (IsValidBranch(X[i+m], hlen, ilen, lt) && IsValidBranch(X[i+l], hlen, ilen, rt)) { + Xc.emplace_back(X[i+m], X[i+l], hlen, lenIndices, clen); + } + } + } + } + + // 2d) Store tuples on the table in-place if possible + while (posFree < i+j && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + i += j; + } + + // 2e) Handle edge case where final table entry has no collision + while (posFree < X.size() && Xc.size() > 0) { + X[posFree++] = Xc.back(); + Xc.pop_back(); + } + + if (Xc.size() > 0) { + // 2f) Add overflow to end of table + X.insert(X.end(), Xc.begin(), Xc.end()); + } else if (posFree < X.size()) { + // 2g) Remove empty space at the end + X.erase(X.begin()+posFree, X.end()); + X.shrink_to_fit(); + } +} + +template +bool Equihash::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled) +{ + eh_index init_size { 1 << (CollisionBitLength + 1) }; + eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) }; + + // First run the algorithm with truncated indices + + const eh_index soln_size { 1 << K }; + std::vector> partialSolns; + int invalidCount = 0; + { + + // 1) Generate first list + LogPrint(BCLog::EQUIHASH, "Generating first list\n"); + size_t hashLen = HashLength; + size_t lenIndices = sizeof(eh_trunc); + std::vector> Xt; + Xt.reserve(init_size); + unsigned char tmpHash[HashOutput]; + for (eh_index g = 0; Xt.size() < init_size; g++) { + GenerateHash(base_state, g, tmpHash, HashOutput); + for (eh_index i = 0; i < IndicesPerHashOutput && Xt.size() < init_size; i++) { + Xt.emplace_back(tmpHash+(i*N/8), N/8, HashLength, CollisionBitLength, + (g*IndicesPerHashOutput)+i, CollisionBitLength + 1); + } + if (cancelled(ListGeneration)) throw solver_cancelled; + } + + // 3) Repeat step 2 until 2n/(k+1) bits remain + for (int r = 1; r < K && Xt.size() > 0; r++) { + LogPrint(BCLog::EQUIHASH, "Round %d:\n", r); + // 2a) Sort the list + LogPrint(BCLog::EQUIHASH, "- Sorting list\n"); + std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength)); + if (cancelled(ListSorting)) throw solver_cancelled; + + LogPrint(BCLog::EQUIHASH, "- Finding collisions\n"); + int i = 0; + int posFree = 0; + std::vector> Xc; + while (i < Xt.size() - 1) { + // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits + int j = 1; + while (i+j < Xt.size() && + HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) { + j++; + } + + // 2c) Calculate tuples (X_i ^ X_j, (i, j)) + bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen)); + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + // We truncated, so don't check for distinct indices here + TruncatedStepRow Xi {Xt[i+l], Xt[i+m], + hashLen, lenIndices, + CollisionByteLength}; + if (!(Xi.IsZero(hashLen-CollisionByteLength) && + IsProbablyDuplicate(Xi.GetTruncatedIndices(hashLen-CollisionByteLength, 2*lenIndices), + 2*lenIndices))) { + Xc.emplace_back(Xi); + } + } + } + + // 2d) Store tuples on the table in-place if possible + while (posFree < i+j && Xc.size() > 0) { + Xt[posFree++] = Xc.back(); + Xc.pop_back(); + } + + i += j; + if (cancelled(ListColliding)) throw solver_cancelled; + } + + // 2e) Handle edge case where final table entry has no collision + while (posFree < Xt.size() && Xc.size() > 0) { + Xt[posFree++] = Xc.back(); + Xc.pop_back(); + } + + if (Xc.size() > 0) { + // 2f) Add overflow to end of table + Xt.insert(Xt.end(), Xc.begin(), Xc.end()); + } else if (posFree < Xt.size()) { + // 2g) Remove empty space at the end + Xt.erase(Xt.begin()+posFree, Xt.end()); + Xt.shrink_to_fit(); + } + + hashLen -= CollisionByteLength; + lenIndices *= 2; + if (cancelled(RoundEnd)) throw solver_cancelled; + } + + // k+1) Find a collision on last 2n(k+1) bits + LogPrint(BCLog::EQUIHASH, "Final round:\n"); + if (Xt.size() > 1) { + LogPrint(BCLog::EQUIHASH, "- Sorting list\n"); + std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen)); + if (cancelled(FinalSorting)) throw solver_cancelled; + LogPrint(BCLog::EQUIHASH, "- Finding collisions\n"); + int i = 0; + while (i < Xt.size() - 1) { + int j = 1; + while (i+j < Xt.size() && + HasCollision(Xt[i], Xt[i+j], hashLen)) { + j++; + } + + for (int l = 0; l < j - 1; l++) { + for (int m = l + 1; m < j; m++) { + TruncatedStepRow res(Xt[i+l], Xt[i+m], + hashLen, lenIndices, 0); + auto soln = res.GetTruncatedIndices(hashLen, 2*lenIndices); + if (!IsProbablyDuplicate(soln, 2*lenIndices)) { + partialSolns.push_back(soln); + } + } + } + + i += j; + if (cancelled(FinalColliding)) throw solver_cancelled; + } + } else + LogPrint(BCLog::EQUIHASH, "- List is empty\n"); + + } // Ensure Xt goes out of scope and is destroyed + + LogPrint(BCLog::EQUIHASH, "Found %d partial solutions\n", partialSolns.size()); + + // Now for each solution run the algorithm again to recreate the indices + LogPrint(BCLog::EQUIHASH, "Culling solutions\n"); + for (std::shared_ptr partialSoln : partialSolns) { + std::set> solns; + size_t hashLen; + size_t lenIndices; + unsigned char tmpHash[HashOutput]; + std::vector>>> X; + X.reserve(K+1); + + // 3) Repeat steps 1 and 2 for each partial index + for (eh_index i = 0; i < soln_size; i++) { + // 1) Generate first list of possibilities + std::vector> icv; + icv.reserve(recreate_size); + for (eh_index j = 0; j < recreate_size; j++) { + eh_index newIndex { UntruncateIndex(partialSoln.get()[i], j, CollisionBitLength + 1) }; + if (j == 0 || newIndex % IndicesPerHashOutput == 0) { + GenerateHash(base_state, newIndex/IndicesPerHashOutput, + tmpHash, HashOutput); + } + icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * N/8), + N/8, HashLength, CollisionBitLength, newIndex); + if (cancelled(PartialGeneration)) throw solver_cancelled; + } + boost::optional>> ic = icv; + + // 2a) For each pair of lists: + hashLen = HashLength; + lenIndices = sizeof(eh_index); + size_t rti = i; + for (size_t r = 0; r <= K; r++) { + // 2b) Until we are at the top of a subtree: + if (r < X.size()) { + if (X[r]) { + // 2c) Merge the lists + ic->reserve(ic->size() + X[r]->size()); + ic->insert(ic->end(), X[r]->begin(), X[r]->end()); + std::sort(ic->begin(), ic->end(), CompareSR(hashLen)); + if (cancelled(PartialSorting)) throw solver_cancelled; + size_t lti = rti-(1<size() == 0) + goto invalidsolution; + + X[r] = boost::none; + hashLen -= CollisionByteLength; + lenIndices *= 2; + rti = lti; + } else { + X[r] = *ic; + break; + } + } else { + X.push_back(ic); + break; + } + if (cancelled(PartialSubtreeEnd)) throw solver_cancelled; + } + if (cancelled(PartialIndexEnd)) throw solver_cancelled; + } + + // We are at the top of the tree + assert(X.size() == K+1); + for (FullStepRow row : *X[K]) { + auto soln = row.GetIndices(hashLen, lenIndices, CollisionBitLength); + assert(soln.size() == equihash_solution_size(N, K)); + solns.insert(soln); + } + for (auto soln : solns) { + if (validBlock(soln)) + return true; + } + if (cancelled(PartialEnd)) throw solver_cancelled; + continue; + +invalidsolution: + invalidCount++; + } + LogPrint(BCLog::EQUIHASH, "- Number of invalid solutions found: %d\n", invalidCount); + + return false; +} +#endif // ENABLE_MINING + +template +bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector soln) +{ + if (soln.size() != SolutionWidth) { + LogPrint(BCLog::EQUIHASH, "Invalid solution length: %d (expected %d)\n", + soln.size(), SolutionWidth); + return false; + } + + std::vector> X; + X.reserve(1 << K); + unsigned char tmpHash[HashOutput]; + for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) { + GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput); + X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8), + N/8, HashLength, CollisionBitLength, i); + } + + size_t hashLen = HashLength; + size_t lenIndices = sizeof(eh_index); + while (X.size() > 1) { + std::vector> Xc; + for (int i = 0; i < X.size(); i += 2) { + if (!HasCollision(X[i], X[i+1], CollisionByteLength)) { + LogPrint(BCLog::EQUIHASH, "Invalid solution: invalid collision length between StepRows\n"); + LogPrint(BCLog::EQUIHASH, "X[i] = %s\n", X[i].GetHex(hashLen)); + LogPrint(BCLog::EQUIHASH, "X[i+1] = %s\n", X[i+1].GetHex(hashLen)); + return false; + } + if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) { + LogPrint(BCLog::EQUIHASH, "Invalid solution: Index tree incorrectly ordered\n"); + return false; + } + if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) { + LogPrint(BCLog::EQUIHASH, "Invalid solution: duplicate indices\n"); + return false; + } + Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength); + } + X = Xc; + hashLen -= CollisionByteLength; + lenIndices *= 2; + } + + assert(X.size() == 1); + return X[0].IsZero(hashLen); +} + +// Explicit instantiations for Equihash<96,3> +template int Equihash<96,3>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<200,9> +template int Equihash<200,9>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<96,5> +template int Equihash<96,5>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); + +// Explicit instantiations for Equihash<48,5> +template int Equihash<48,5>::InitialiseState(eh_HashState& base_state); +#ifdef ENABLE_MINING +template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state, + const std::function)> validBlock, + const std::function cancelled); +#endif +template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector soln); diff --git a/src/crypto/equihash.h b/src/crypto/equihash.h new file mode 100644 index 000000000..6691844ba --- /dev/null +++ b/src/crypto/equihash.h @@ -0,0 +1,279 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_EQUIHASH_H +#define BITCOIN_EQUIHASH_H + +#include "crypto/sha256.h" +#include "utilstrencodings.h" + +#include "sodium.h" + +#include +#include +#include +#include +#include +#include + +#include + +typedef crypto_generichash_blake2b_state eh_HashState; +typedef uint32_t eh_index; +typedef uint8_t eh_trunc; + +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, int 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, int 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, int 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; +} + +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*N/8 }; + 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" + +static Equihash<96,3> Eh96_3; +static Equihash<200,9> Eh200_9; +static Equihash<96,5> Eh96_5; +static Equihash<48,5> Eh48_5; + +#define EhInitialiseState(n, k, base_state) \ + if (n == 96 && k == 3) { \ + Eh96_3.InitialiseState(base_state); \ + } else if (n == 200 && k == 9) { \ + Eh200_9.InitialiseState(base_state); \ + } else if (n == 96 && k == 5) { \ + Eh96_5.InitialiseState(base_state); \ + } else if (n == 48 && k == 5) { \ + Eh48_5.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 == 96 && k == 3) { + return Eh96_3.BasicSolve(base_state, validBlock, cancelled); + } else if (n == 200 && k == 9) { + return Eh200_9.BasicSolve(base_state, validBlock, cancelled); + } 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"); + } +} + +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 == 96 && k == 3) { + return Eh96_3.OptimisedSolve(base_state, validBlock, cancelled); + } else if (n == 200 && k == 9) { + return Eh200_9.OptimisedSolve(base_state, validBlock, cancelled); + } 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"); + } +} + +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 == 96 && k == 3) { \ + ret = Eh96_3.IsValidSolution(base_state, soln); \ + } else if (n == 200 && k == 9) { \ + ret = Eh200_9.IsValidSolution(base_state, soln); \ + } else if (n == 96 && k == 5) { \ + ret = Eh96_5.IsValidSolution(base_state, soln); \ + } else if (n == 48 && k == 5) { \ + ret = Eh48_5.IsValidSolution(base_state, soln); \ + } else { \ + throw std::invalid_argument("Unsupported Equihash parameters"); \ + } + +#endif // BITCOIN_EQUIHASH_H diff --git a/src/crypto/equihash.tcc b/src/crypto/equihash.tcc new file mode 100644 index 000000000..625749e47 --- /dev/null +++ b/src/crypto/equihash.tcc @@ -0,0 +1,49 @@ +// Copyright (c) 2016 Jack Grigg +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +// Checks if the intersection of a.indices and b.indices is empty +template +bool DistinctIndices(const FullStepRow& a, const FullStepRow& b, size_t len, size_t lenIndices) +{ + for(size_t i = 0; i < lenIndices; i += sizeof(eh_index)) { + for(size_t j = 0; j < lenIndices; j += sizeof(eh_index)) { + if (memcmp(a.hash+len+i, b.hash+len+j, sizeof(eh_index)) == 0) { + return false; + } + } + } + return true; +} + +template +bool IsProbablyDuplicate(std::shared_ptr indices, size_t lenIndices) +{ + assert(lenIndices <= MAX_INDICES); + bool checked_index[MAX_INDICES] = {false}; + int count_checked = 0; + for (int z = 0; z < lenIndices; z++) { + // Skip over indices we have already paired + if (!checked_index[z]) { + for (int y = z+1; y < lenIndices; y++) { + if (!checked_index[y] && indices.get()[z] == indices.get()[y]) { + // Pair found + checked_index[y] = true; + count_checked += 2; + break; + } + } + } + } + return count_checked == lenIndices; +} + +template +bool IsValidBranch(const FullStepRow& a, const size_t len, const unsigned int ilen, const eh_trunc t) +{ + return TruncateIndex(ArrayToEhIndex(a.hash+len), ilen) == t; +} diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index f3245b8de..cd2a9d0fb 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #if defined(__x86_64__) || defined(__amd64__) #if defined(USE_ASM) @@ -149,8 +150,8 @@ bool SelfTest(TransformType tr) { static const unsigned char in1[65] = {0, 0x80}; static const unsigned char in2[129] = { 0, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }; @@ -241,6 +242,22 @@ void CSHA256::Finalize(unsigned char hash[OUTPUT_SIZE]) WriteBE32(hash + 28, s[7]); } +void CSHA256::FinalizeNoPadding(unsigned char hash[OUTPUT_SIZE], bool enforce_compression) +{ + if (enforce_compression && bytes != 64) { + throw std::length_error("SHA256Compress should be invoked with a 512-bit block"); + } + + WriteBE32(hash, s[0]); + WriteBE32(hash + 4, s[1]); + WriteBE32(hash + 8, s[2]); + WriteBE32(hash + 12, s[3]); + WriteBE32(hash + 16, s[4]); + WriteBE32(hash + 20, s[5]); + WriteBE32(hash + 24, s[6]); + WriteBE32(hash + 28, s[7]); +} + CSHA256& CSHA256::Reset() { bytes = 0; diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index dd30fe396..fbf14ab6a 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -23,6 +23,7 @@ public: CSHA256(); CSHA256& Write(const unsigned char* data, size_t len); void Finalize(unsigned char hash[OUTPUT_SIZE]); + void FinalizeNoPadding(unsigned char hash[OUTPUT_SIZE], bool enforce_compression = true); CSHA256& Reset(); }; diff --git a/src/miner.cpp b/src/miner.cpp index d4527a1d6..d7db49440 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -166,7 +166,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblock->hashPrevBlock = pindexPrev->GetBlockHash(); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); - pblock->nNonce = 0; + pblock->nNonce = uint256(); pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); CValidationState state; diff --git a/src/paymentdisclosuredb.cpp b/src/paymentdisclosuredb.cpp index ef32f2845..ccccf309f 100644 --- a/src/paymentdisclosuredb.cpp +++ b/src/paymentdisclosuredb.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "paymentdisclosuredb.h" +#include -#include "util.h" -#include "leveldbwrapper.h" +#include +#include #include @@ -35,10 +35,10 @@ PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath) LogPrintf("PaymentDisclosure: using custom path for database: %s\n", path.string()); } - TryCreateDirectory(path); + TryCreateDirectories(path); options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, path.string(), &db); - HandleError(status); // throws exception + dbwrapper_private::HandleError(status); // throws exception LogPrintf("PaymentDisclosure: Opened LevelDB successfully\n"); } @@ -57,12 +57,12 @@ bool PaymentDisclosureDB::Put(const PaymentDisclosureKey& key, const PaymentDisc std::lock_guard guard(lock_); CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(ssValue.GetSerializeSize(info)); + ssValue.reserve(GetSerializeSize(info, SER_DISK, CLIENT_VERSION)); ssValue << info; leveldb::Slice slice(&ssValue[0], ssValue.size()); leveldb::Status status = db->Put(writeOptions, key.ToString(), slice); - HandleError(status); + dbwrapper_private::HandleError(status); return true; } @@ -80,7 +80,7 @@ bool PaymentDisclosureDB::Get(const PaymentDisclosureKey& key, PaymentDisclosure if (status.IsNotFound()) return false; LogPrintf("PaymentDisclosure: LevelDB read failure: %s\n", status.ToString()); - HandleError(status); + dbwrapper_private::HandleError(status); } try { diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 21f880bcc..015c3519c 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -18,12 +18,13 @@ uint256 CBlockHeader::GetHash() const std::string CBlock::ToString() const { std::stringstream s; - s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n", + s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, hashReserved=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n", GetHash().ToString(), nVersion, hashPrevBlock.ToString(), hashMerkleRoot.ToString(), - nTime, nBits, nNonce, + hashReserved.ToString(), + nTime, nBits, nNonce.ToString(), vtx.size()); for (const auto& tx : vtx) { s << " " << tx->ToString() << "\n"; diff --git a/src/primitives/block.h b/src/primitives/block.h index 1fca55d91..672cff440 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -24,9 +24,11 @@ public: int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; + uint256 hashReserved; uint32_t nTime; uint32_t nBits; - uint32_t nNonce; + uint256 nNonce; + std::vector nSolution; CBlockHeader() { @@ -40,9 +42,11 @@ public: READWRITE(this->nVersion); READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); + READWRITE(hashReserved); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); + READWRITE(nSolution); } void SetNull() @@ -50,9 +54,11 @@ public: nVersion = 0; hashPrevBlock.SetNull(); hashMerkleRoot.SetNull(); + hashReserved.SetNull(); nTime = 0; nBits = 0; - nNonce = 0; + nNonce = uint256(); + nSolution.clear(); } bool IsNull() const @@ -110,15 +116,44 @@ public: block.nVersion = nVersion; block.hashPrevBlock = hashPrevBlock; block.hashMerkleRoot = hashMerkleRoot; + block.hashReserved = hashReserved; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; + block.nSolution = nSolution; return block; } std::string ToString() const; }; +/** + * Custom serializer for CBlockHeader that omits the nonce and solution, for use + * as input to Equihash. + */ +class CEquihashInput : private CBlockHeader +{ +public: + CEquihashInput(const CBlockHeader &header) + { + CBlockHeader::SetNull(); + *((CBlockHeader*)this) = header; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(hashPrevBlock); + READWRITE(hashMerkleRoot); + READWRITE(hashReserved); + READWRITE(nTime); + READWRITE(nBits); + } +}; + /** Describes a place in the block chain to another node such that if the * other node doesn't have the same branch, it can find a recent common trunk. * The further back it is, the further before the fork it may be. diff --git a/src/primitives/joinsplit.cpp b/src/primitives/joinsplit.cpp new file mode 100644 index 000000000..366010255 --- /dev/null +++ b/src/primitives/joinsplit.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include +#include +#include + +JSDescription::JSDescription(ZCJoinSplit& params, + const uint256& pubKeyHash, + const uint256& anchor, + const std::array& inputs, + const std::array& outputs, + CAmount vpub_old, + CAmount vpub_new, + bool computeProof, + uint256 *esk // payment disclosure + ) : vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor) +{ + std::array notes; + + proof = params.prove( + inputs, + outputs, + notes, + ciphertexts, + ephemeralKey, + pubKeyHash, + randomSeed, + macs, + nullifiers, + commitments, + vpub_old, + vpub_new, + anchor, + computeProof, + esk // payment disclosure + ); +} + +JSDescription JSDescription::Randomized( + ZCJoinSplit& params, + const uint256& pubKeyHash, + const uint256& anchor, + std::array& inputs, + std::array& outputs, + std::array& inputMap, + std::array& outputMap, + CAmount vpub_old, + CAmount vpub_new, + bool computeProof, + uint256 *esk, // payment disclosure + std::function gen + ) +{ + // Randomize the order of the inputs and outputs + inputMap = {0, 1}; + outputMap = {0, 1}; + + assert(gen); + + MappedShuffle(inputs.begin(), inputMap.begin(), ZC_NUM_JS_INPUTS, gen); + MappedShuffle(outputs.begin(), outputMap.begin(), ZC_NUM_JS_OUTPUTS, gen); + + return JSDescription( + params, pubKeyHash, anchor, inputs, outputs, + vpub_old, vpub_new, computeProof, + esk // payment disclosure + ); +} + +bool JSDescription::Verify( + ZCJoinSplit& params, + libzcash::ProofVerifier& verifier, + const uint256& pubKeyHash + ) const { + return params.verify( + proof, + verifier, + pubKeyHash, + randomSeed, + macs, + nullifiers, + commitments, + vpub_old, + vpub_new, + anchor + ); +} + +uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const +{ + return params.h_sig(randomSeed, nullifiers, pubKeyHash); +} diff --git a/src/primitives/joinsplit.h b/src/primitives/joinsplit.h new file mode 100644 index 000000000..1e6f4f65b --- /dev/null +++ b/src/primitives/joinsplit.h @@ -0,0 +1,148 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2018 The Bitcoin Private developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BTCP_PRIMITIVES_JOINSPLIT_H +#define BTCP_PRIMITIVES_JOINSPLIT_H + +#include "amount.h" +#include "random.h" +#include "script/script.h" +#include "serialize.h" +#include "uint256.h" +#include "consensus/consensus.h" + +#include "zcash/NoteEncryption.hpp" +#include "zcash/Zcash.h" +#include "zcash/JoinSplit.hpp" +#include "zcash/Proof.hpp" + +#include + +class JSDescription +{ +public: + // These values 'enter from' and 'exit to' the value + // pool, respectively. + CAmount vpub_old; + CAmount vpub_new; + + // JoinSplits are always anchored to a root in the note + // commitment tree at some point in the blockchain + // history or in the history of the current + // transaction. + uint256 anchor; + + // Nullifiers are used to prevent double-spends. They + // are derived from the secrets placed in the note + // and the secret spend-authority key known by the + // spender. + std::array nullifiers; + + // Note commitments are introduced into the commitment + // tree, blinding the public about the values and + // destinations involved in the JoinSplit. The presence of + // a commitment in the note commitment tree is required + // to spend it. + std::array commitments; + + // Ephemeral key + uint256 ephemeralKey; + + // Ciphertexts + // These contain trapdoors, values and other information + // that the recipient needs, including a memo field. It + // is encrypted using the scheme implemented in crypto/NoteEncryption.cpp + std::array ciphertexts = {{ {{0}} }}; + + // Random seed + uint256 randomSeed; + + // MACs + // The verification of the JoinSplit requires these MACs + // to be provided as an input. + std::array macs; + + // JoinSplit proof + // This is a zk-SNARK which ensures that this JoinSplit is valid. + libzcash::ZCProof proof; + + JSDescription(): vpub_old(0), vpub_new(0) { } + + JSDescription(ZCJoinSplit& params, + const uint256& pubKeyHash, + const uint256& rt, + const std::array& inputs, + const std::array& outputs, + CAmount vpub_old, + CAmount vpub_new, + bool computeProof = true, // Set to false in some tests + uint256 *esk = nullptr // payment disclosure + ); + + static JSDescription Randomized( + ZCJoinSplit& params, + const uint256& pubKeyHash, + const uint256& rt, + std::array& inputs, + std::array& outputs, + std::array& inputMap, + std::array& outputMap, + CAmount vpub_old, + CAmount vpub_new, + bool computeProof = true, // Set to false in some tests + uint256 *esk = nullptr, // payment disclosure + std::function gen = GetRandInt + ); + + // Verifies that the JoinSplit proof is correct. + bool Verify( + ZCJoinSplit& params, + libzcash::ProofVerifier& verifier, + const uint256& pubKeyHash + ) const; + + // Returns the calculated h_sig + uint256 h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(vpub_old); + READWRITE(vpub_new); + READWRITE(anchor); + READWRITE(nullifiers); + READWRITE(commitments); + READWRITE(ephemeralKey); + READWRITE(randomSeed); + READWRITE(macs); + READWRITE(proof); + READWRITE(ciphertexts); + } + + friend bool operator==(const JSDescription& a, const JSDescription& b) + { + return ( + a.vpub_old == b.vpub_old && + a.vpub_new == b.vpub_new && + a.anchor == b.anchor && + a.nullifiers == b.nullifiers && + a.commitments == b.commitments && + a.ephemeralKey == b.ephemeralKey && + a.ciphertexts == b.ciphertexts && + a.randomSeed == b.randomSeed && + a.macs == b.macs && + a.proof == b.proof + ); + } + + friend bool operator!=(const JSDescription& a, const JSDescription& b) + { + return !(a == b); + } +}; + +#endif diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 230f762a1..4e632f9ae 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -55,7 +55,7 @@ std::string CTxOut::ToString() const } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig) {} uint256 CMutableTransaction::GetHash() const { @@ -76,9 +76,9 @@ uint256 CTransaction::ComputeWitnessHash() const } /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash{}, m_witness_hash{} {} -CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} -CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} +CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig(), hash{}, m_witness_hash{} {} +CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} +CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} CAmount CTransaction::GetValueOut() const { @@ -88,9 +88,33 @@ CAmount CTransaction::GetValueOut() const if (!MoneyRange(tx_out.nValue) || !MoneyRange(nValueOut)) throw std::runtime_error(std::string(__func__) + ": value out of range"); } + + for (std::vector::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.end(); ++it) + { + // NB: vpub_old "takes" money from the value pool just as outputs do + nValueOut += it->vpub_old; + + if (!MoneyRange(it->vpub_old) || !MoneyRange(nValueOut)) + throw std::runtime_error("CTransaction::GetValueOut(): value out of range"); + } return nValueOut; } +CAmount CTransaction::GetJoinSplitValueIn() const +{ + CAmount nValue = 0; + for (std::vector::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.end(); ++it) + { + // NB: vpub_new "gives" money to the value pool just as inputs do + nValue += it->vpub_new; + + if (!MoneyRange(it->vpub_new) || !MoneyRange(nValue)) + throw std::runtime_error("CTransaction::GetJoinSplitValueIn(): value out of range"); + } + + return nValue; +} + unsigned int CTransaction::GetTotalSize() const { return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 1c846d38e..d63d6b03c 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -12,6 +12,12 @@ #include #include +#include +#include +#include +#include +#include + static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; /** An outpoint - a combination of a transaction hash and an index n into its vout */ @@ -226,6 +232,13 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) { throw std::ios_base::failure("Unknown transaction optional data"); } s >> tx.nLockTime; + if(tx.nVersion >= 2) { + s >> tx.vjoinsplit; + if (tx.vjoinsplit.size() > 0) { + s >> tx.joinSplitPubKey; + s >> tx.joinSplitSig; + } + } } template @@ -255,6 +268,13 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) { } } s << tx.nLockTime; + if(tx.nVersion >= 2) { + s << tx.vjoinsplit; + if (tx.vjoinsplit.size() > 0) { + s << tx.joinSplitPubKey; + s << tx.joinSplitSig; + } + } } @@ -264,6 +284,8 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) { class CTransaction { public: + typedef std::array joinsplit_sig_t; + // Default transaction version. static const int32_t CURRENT_VERSION=2; @@ -282,6 +304,10 @@ public: const std::vector vout; const int32_t nVersion; const uint32_t nLockTime; + const std::vector vjoinsplit; + const uint256 joinSplitPubKey; + const joinsplit_sig_t joinSplitSig = {{0}}; + private: /** Memory only. */ @@ -321,6 +347,9 @@ public: // GetValueIn() is a method on CCoinsViewCache, because // inputs must be known to compute value in. + // Return sum of JoinSplit vpub_new + CAmount GetJoinSplitValueIn() const; + /** * Get the total transaction size in bytes, including witness data. * "Total Size" defined in BIP141 and BIP144. @@ -363,6 +392,9 @@ struct CMutableTransaction std::vector vout; int32_t nVersion; uint32_t nLockTime; + std::vector vjoinsplit; + uint256 joinSplitPubKey; + CTransaction::joinsplit_sig_t joinSplitSig = {{0}}; CMutableTransaction(); explicit CMutableTransaction(const CTransaction& tx); diff --git a/src/random.h b/src/random.h index 1d6b13a53..940e525bd 100644 --- a/src/random.h +++ b/src/random.h @@ -12,6 +12,7 @@ #include #include +#include /* Seed OpenSSL PRNG with additional entropy data */ void RandAddSeed(); @@ -150,4 +151,34 @@ bool Random_SanityCheck(); /** Initialize the RNG. */ void RandomInit(); +/** + * Identity function for MappedShuffle, so that elements retain their original order. + */ +int GenIdentity(int n); + +/** + * Rearranges the elements in the range [first,first+len) randomly, assuming + * that gen is a uniform random number generator. Follows the same algorithm as + * std::shuffle in C++11 (a Durstenfeld shuffle). + * + * The elements in the range [mapFirst,mapFirst+len) are rearranged according to + * the same permutation, enabling the permutation to be tracked by the caller. + * + * gen takes an integer n and produces a uniform random output in [0,n). + */ +template + void MappedShuffle(RandomAccessIterator first, + MapRandomAccessIterator mapFirst, + size_t len, + std::function gen) +{ + for (size_t i = len-1; i > 0; --i) { + auto r = gen(i+1); + assert(r >= 0); + assert(r <= i); + std::swap(first[i], first[r]); + std::swap(mapFirst[i], mapFirst[r]); + } +} + #endif // BITCOIN_RANDOM_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 24fb522e6..29d87ebb7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -101,7 +101,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); result.pushKV("time", (int64_t)blockindex->nTime); result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); - result.pushKV("nonce", (uint64_t)blockindex->nNonce); + result.pushKV("nonce", blockindex->nNonce.GetHex()); result.pushKV("bits", strprintf("%08x", blockindex->nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); @@ -146,7 +146,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); - result.pushKV("nonce", (uint64_t)block.nNonce); + result.pushKV("nonce", block.nNonce.GetHex()); result.pushKV("bits", strprintf("%08x", block.nBits)); result.pushKV("difficulty", GetDifficulty(blockindex)); result.pushKV("chainwork", blockindex->nChainWork.GetHex()); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 203fac39e..a024ffef6 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,8 @@ UniValue generateBlocks(std::shared_ptr coinbaseScript, int nGen } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); + unsigned int n = Params().EquihashN(); + unsigned int k = Params().EquihashK(); while (nHeight < nHeightEnd && !ShutdownRequested()) { std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); @@ -126,16 +129,52 @@ UniValue generateBlocks(std::shared_ptr coinbaseScript, int nGen LOCK(cs_main); IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); } - while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { - ++pblock->nNonce; + + // Hash state + crypto_generichash_blake2b_state eh_state; + EhInitialiseState(n, k, eh_state); + + // I = the block header minus nonce and solution. + CEquihashInput I{*pblock}; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << I; + + // H(I||... + crypto_generichash_blake2b_update(&eh_state, (unsigned char*)&ss[0], ss.size()); + + while (nMaxTries > 0 && UintToArith256(pblock->nNonce) < nInnerLoopCount) { + // Yes, there is a chance every nonce could fail to satisfy the -regtest + // target -- 1 in 2^(2^256). That ain't gonna happen + pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); --nMaxTries; + + // H(I||V||... + crypto_generichash_blake2b_state curr_state; + curr_state = eh_state; + crypto_generichash_blake2b_update(&curr_state, + pblock->nNonce.begin(), + pblock->nNonce.size()); + + // (x_1, x_2, ...) = A(I, V, n, k) + std::function)> validBlock = + [&pblock](std::vector soln) { + pblock->nSolution = soln; + return CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()); + }; + bool found = EhBasicSolveUncancellable(n, k, curr_state, validBlock); + if (found) { + goto endloop; + } } + if (nMaxTries == 0) { break; } - if (pblock->nNonce == nInnerLoopCount) { + if (UintToArith256(pblock->nNonce) == nInnerLoopCount) { continue; } + + endloop: std::shared_ptr shared_pblock = std::make_shared(*pblock); if (!ProcessNewBlock(Params(), shared_pblock, true, nullptr)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); @@ -531,7 +570,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Update nTime UpdateTime(pblock, consensusParams, pindexPrev); - pblock->nNonce = 0; + pblock->nNonce = uint256(); // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration const bool fPreSegWit = (ThresholdState::ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache)); diff --git a/src/serialize.h b/src/serialize.h index e54c7483d..78459f63e 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -24,6 +24,9 @@ #include #include +#include +#include + static const unsigned int MAX_SIZE = 0x02000000; /** @@ -173,11 +176,11 @@ template const X& ReadWriteAsHelper(const X& x) { return x; } #define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) #define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper(obj))) -/** +/** * Implement three methods for serializable objects. These are actually wrappers over * "SerializationOp" template, which implements the body of each class' serialization * code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be - * added as members. + * added as members. */ #define ADD_SERIALIZE_METHODS \ template \ @@ -308,16 +311,16 @@ uint64_t ReadCompactSize(Stream& is) * sure the encoding is one-to-one, one is subtracted from all but the last digit. * Thus, the byte sequence a[] with length len, where all but the last byte * has bit 128 set, encodes the number: - * + * * (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) - * + * * Properties: * * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) * * Every integer has exactly one encoding * * Encoding does not depend on size of original integer type * * No redundancy: every (infinite) byte sequence corresponds to a list * of encoded integers. - * + * * 0: [0x00] 256: [0x81 0x00] * 1: [0x01] 16383: [0xFE 0x7F] * 127: [0x7F] 16384: [0xFF 0x00] @@ -546,12 +549,24 @@ template void Unserialize_impl(Stream& template void Unserialize_impl(Stream& is, std::vector& v, const V&); template inline void Unserialize(Stream& is, std::vector& v); +/** + * array + */ +template void Serialize(Stream& os, const std::array& item); +template void Unserialize(Stream& is, std::array& item); + /** * pair */ template void Serialize(Stream& os, const std::pair& item); template void Unserialize(Stream& is, std::pair& item); +/** + * optional + */ +template void Serialize(Stream& os, const boost::optional& item); +template void Unserialize(Stream& is, boost::optional& item); + /** * map */ @@ -753,6 +768,24 @@ inline void Unserialize(Stream& is, std::vector& v) Unserialize_impl(is, v, T()); } +/** + * array + */ +template + void Serialize(Stream& os, const std::array& item) +{ + for (size_t i = 0; i < N; i++) { + Serialize(os, item[i]); + } +} + +template + void Unserialize(Stream& is, std::array& item) +{ + for (size_t i = 0; i < N; i++) { + Unserialize(is, item[i]); + } +} /** @@ -773,6 +806,41 @@ void Unserialize(Stream& is, std::pair& item) } +/** + * optional + */ +template + void Serialize(Stream& os, const boost::optional& item) +{ + // If the value is there, put 0x01 and then serialize the value. + // If it's not, put 0x00. + if (item) { + unsigned char discriminant = 0x01; + Serialize(os, discriminant); + Serialize(os, *item); + } else { + unsigned char discriminant = 0x00; + Serialize(os, discriminant); + } +} + +template + void Unserialize(Stream& is, boost::optional& item) +{ + unsigned char discriminant = 0x00; + Unserialize(is, discriminant); + + if (discriminant == 0x00) { + item = boost::none; + } else if (discriminant == 0x01) { + T object; + Unserialize(is, object); + item = object; + } else { + throw std::ios_base::failure("non-canonical optional discriminant"); + } +} + /** * map diff --git a/src/test/sha256compress_tests.cpp b/src/test/sha256compress_tests.cpp new file mode 100644 index 000000000..5a2c31d9a --- /dev/null +++ b/src/test/sha256compress_tests.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(sha256compress_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(compression) +{ + { + unsigned char preimage[64] = {}; + CSHA256 hasher; + hasher.Write(&preimage[0], 64); + + uint256 digest; + + hasher.FinalizeNoPadding(digest.begin()); + + BOOST_CHECK_MESSAGE(digest == uint256S("d8a93718eaf9feba4362d2c091d4e58ccabe9f779957336269b4b917be9856da"), + digest.GetHex()); + } + + { + unsigned char preimage[63] = {}; + CSHA256 hasher; + hasher.Write(&preimage[0], 63); + uint256 digest; + BOOST_CHECK_THROW(hasher.FinalizeNoPadding(digest.begin()), std::length_error); + } + + { + unsigned char preimage[65] = {}; + CSHA256 hasher; + hasher.Write(&preimage[0], 65); + uint256 digest; + BOOST_CHECK_THROW(hasher.FinalizeNoPadding(digest.begin()), std::length_error); + } + + { + unsigned char n = 0x00; + CSHA256 hasher; + for (size_t i = 0; i < 64; i++) { + hasher.Write(&n, 1); + } + uint256 digest; + + hasher.FinalizeNoPadding(digest.begin()); + + BOOST_CHECK_MESSAGE(digest == uint256S("d8a93718eaf9feba4362d2c091d4e58ccabe9f779957336269b4b917be9856da"), + digest.GetHex()); + } + + { + unsigned char preimage[64] = { 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd', + 'a', 'b', 'c', 'd' + }; + CSHA256 hasher; + hasher.Write(&preimage[0], 64); + + uint256 digest; + + hasher.FinalizeNoPadding(digest.begin()); + + BOOST_CHECK_MESSAGE(digest == uint256S("da70ec41879e36b000281733d4deb27ddf41e8e343a38f2fabbd2d8611987d86"), + digest.GetHex()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 37c4f7913..306f723e1 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -72,7 +72,7 @@ std::shared_ptr FinalizeBlock(std::shared_ptr pblock) pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { - ++(pblock->nNonce); + pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); } return pblock; diff --git a/src/uint252.h b/src/uint252.h new file mode 100644 index 000000000..e7b98ddf0 --- /dev/null +++ b/src/uint252.h @@ -0,0 +1,50 @@ +#ifndef UINT252_H +#define UINT252_H + +#include +#include "uint256.h" +#include "serialize.h" + +// Wrapper of uint256 with guarantee that first +// four bits are zero. +class uint252 { +private: + uint256 contents; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(contents); + + if ((*contents.begin()) & 0xF0) { + throw std::ios_base::failure("spending key has invalid leading bits"); + } + } + + const unsigned char* begin() const + { + return contents.begin(); + } + + const unsigned char* end() const + { + return contents.end(); + } + + uint252() : contents() {}; + explicit uint252(const uint256& in) : contents(in) { + if (*contents.begin() & 0xF0) { + throw std::domain_error("leading bits are set in argument given to uint252 constructor"); + } + } + + uint256 inner() const { + return contents; + } + + friend inline bool operator==(const uint252& a, const uint252& b) { return a.contents == b.contents; } +}; + +#endif diff --git a/src/util.cpp b/src/util.cpp index 34483d95b..8e9b35899 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -726,8 +726,58 @@ static fs::path g_blocks_path_cached; static fs::path g_blocks_path_cache_net_specific; static fs::path pathCached; static fs::path pathCachedNetSpecific; +static boost::filesystem::path zc_paramsPathCached; static CCriticalSection csPathCached; +static boost::filesystem::path ZC_GetBaseParamsDir() +{ + // Copied from GetDefaultDataDir and adapter for zcash params. + + namespace fs = boost::filesystem; + // Windows < Vista: C:\Documents and Settings\Username\Application Data\ZcashParams + // Windows >= Vista: C:\Users\Username\AppData\Roaming\ZcashParams + // Mac: ~/Library/Application Support/ZcashParams + // Unix: ~/.zcash-params +#ifdef WIN32 + // Windows + return GetSpecialFolderPath(CSIDL_APPDATA) / "ZcashParams"; +#else + fs::path pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = fs::path("/"); + else + pathRet = fs::path(pszHome); +#ifdef MAC_OSX + // Mac + pathRet /= "Library/Application Support"; + TryCreateDirectory(pathRet); + return pathRet / "ZcashParams"; +#else + // Unix + return pathRet / ".zcash-params"; +#endif +#endif +} + +const boost::filesystem::path &ZC_GetParamsDir() +{ + namespace fs = boost::filesystem; + + LOCK(csPathCached); // Reuse the same lock as upstream. + + fs::path &path = zc_paramsPathCached; + + // This can be called during exceptions by LogPrintf(), so we cache the + // value so we don't have to do memory allocations after that. + if (!path.empty()) + return path; + + path = ZC_GetBaseParamsDir(); + + return path; +} + const fs::path &GetBlocksDir(bool fNetSpecific) { diff --git a/src/util.h b/src/util.h index efd8a4bd9..1a37d4f3a 100644 --- a/src/util.h +++ b/src/util.h @@ -70,6 +70,8 @@ bool error(const char* fmt, const Args&... args) return false; } +const boost::filesystem::path &ZC_GetParamsDir(); + void PrintExceptionContinue(const std::exception *pex, const char* pszThread); bool FileCommit(FILE *file); bool TruncateFile(FILE *file, unsigned int length); diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 281433b0b..3a57fdf49 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -2,27 +2,27 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "asyncrpcoperation_sendmany.h" -#include "asyncrpcqueue.h" -#include "amount.h" -#include "consensus/upgrades.h" -#include "core_io.h" -#include "init.h" -#include "main.h" -#include "net.h" -#include "netbase.h" -#include "rpcserver.h" -#include "timedata.h" -#include "util.h" -#include "utilmoneystr.h" -#include "wallet.h" -#include "walletdb.h" -#include "script/interpreter.h" -#include "utiltime.h" -#include "rpcprotocol.h" -#include "zcash/IncrementalMerkleTree.hpp" -#include "sodium.h" -#include "miner.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include