[primitives] Add Joinsplit primitives
This commit is contained in:
parent
8e04b9dd54
commit
27a9fcf14c
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -24,5 +24,6 @@ zcash_CreateJoinSplit_LDADD = \
|
|||
$(LIBSNARK) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBBITCOIN_CONSENSUS) \
|
||||
$(BOOST_LIBS) \
|
||||
$(LIBZCASH_LIBS)
|
||||
|
|
31
src/base58.h
31
src/base58.h
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <support/allocators/zeroafterfree.h>
|
||||
|
||||
/**
|
||||
* Encode a byte sequence as a base58-encoded string.
|
||||
|
@ -58,4 +59,34 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
|
|||
*/
|
||||
bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
|
||||
|
||||
/**
|
||||
* Base class for all base58-encoded data
|
||||
*/
|
||||
class CBase58Data
|
||||
{
|
||||
protected:
|
||||
//! the version byte(s)
|
||||
std::vector<unsigned char> vchVersion;
|
||||
|
||||
//! the actually encoded data
|
||||
typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar;
|
||||
vector_uchar vchData;
|
||||
|
||||
CBase58Data();
|
||||
void SetData(const std::vector<unsigned char> &vchVersionIn, const void* pdata, size_t nSize);
|
||||
void SetData(const std::vector<unsigned char> &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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
#include <config/bitcoin-config.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sodium.h>
|
||||
#include <compat/endian.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -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 <algorithm>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
EhSolverCancelledException solver_cancelled;
|
||||
|
||||
template<unsigned int N, unsigned int K>
|
||||
int Equihash<N,K>::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<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> 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<unsigned char> array(lenIndices);
|
||||
ExpandArray(minimal.data(), minimal.size(),
|
||||
array.data(), lenIndices, cBitLen+1, bytePad);
|
||||
std::vector<eh_index> ret;
|
||||
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
|
||||
ret.push_back(ArrayToEhIndex(array.data()+i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> 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<unsigned char> array(lenIndices);
|
||||
for (int i = 0; i < indices.size(); i++) {
|
||||
EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index)));
|
||||
}
|
||||
std::vector<unsigned char> ret(minLen);
|
||||
CompressArray(array.data(), lenIndices,
|
||||
ret.data(), minLen, cBitLen+1, bytePad);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t WIDTH>
|
||||
StepRow<WIDTH>::StepRow(const unsigned char* hashIn, size_t hInLen,
|
||||
size_t hLen, size_t cBitLen)
|
||||
{
|
||||
assert(hLen <= WIDTH);
|
||||
ExpandArray(hashIn, hInLen, hash, hLen, cBitLen);
|
||||
}
|
||||
|
||||
template<size_t WIDTH> template<size_t W>
|
||||
StepRow<WIDTH>::StepRow(const StepRow<W>& a)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(W <= WIDTH);
|
||||
std::copy(a.hash, a.hash+W, hash);
|
||||
}
|
||||
|
||||
template<size_t WIDTH>
|
||||
FullStepRow<WIDTH>::FullStepRow(const unsigned char* hashIn, size_t hInLen,
|
||||
size_t hLen, size_t cBitLen, eh_index i) :
|
||||
StepRow<WIDTH> {hashIn, hInLen, hLen, cBitLen}
|
||||
{
|
||||
EhIndexToArray(i, hash+hLen);
|
||||
}
|
||||
|
||||
template<size_t WIDTH> template<size_t W>
|
||||
FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
|
||||
StepRow<WIDTH> {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<size_t WIDTH>
|
||||
FullStepRow<WIDTH>& FullStepRow<WIDTH>::operator=(const FullStepRow<WIDTH>& a)
|
||||
{
|
||||
std::copy(a.hash, a.hash+WIDTH, hash);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t WIDTH>
|
||||
bool StepRow<WIDTH>::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<size_t WIDTH>
|
||||
std::vector<unsigned char> FullStepRow<WIDTH>::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<unsigned char> ret(minLen);
|
||||
CompressArray(hash+len, lenIndices, ret.data(), minLen, cBitLen+1, bytePad);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<size_t WIDTH>
|
||||
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& 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<size_t WIDTH>
|
||||
TruncatedStepRow<WIDTH>::TruncatedStepRow(const unsigned char* hashIn, size_t hInLen,
|
||||
size_t hLen, size_t cBitLen,
|
||||
eh_index i, unsigned int ilen) :
|
||||
StepRow<WIDTH> {hashIn, hInLen, hLen, cBitLen}
|
||||
{
|
||||
hash[hLen] = TruncateIndex(i, ilen);
|
||||
}
|
||||
|
||||
template<size_t WIDTH> template<size_t W>
|
||||
TruncatedStepRow<WIDTH>::TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
|
||||
StepRow<WIDTH> {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<size_t WIDTH>
|
||||
TruncatedStepRow<WIDTH>& TruncatedStepRow<WIDTH>::operator=(const TruncatedStepRow<WIDTH>& a)
|
||||
{
|
||||
std::copy(a.hash, a.hash+WIDTH, hash);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t WIDTH>
|
||||
std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t len, size_t lenIndices) const
|
||||
{
|
||||
std::shared_ptr<eh_trunc> p (new eh_trunc[lenIndices], std::default_delete<eh_trunc[]>());
|
||||
std::copy(hash+len, hash+len+lenIndices, p.get());
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MINING
|
||||
template<unsigned int N, unsigned int K>
|
||||
bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> 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<FullStepRow<FullWidth>> 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<FullStepRow<FullWidth>> 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<FinalFullWidth> 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<size_t WIDTH>
|
||||
void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, const size_t lenIndices, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt)
|
||||
{
|
||||
int i = 0;
|
||||
int posFree = 0;
|
||||
std::vector<FullStepRow<WIDTH>> 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<unsigned int N, unsigned int K>
|
||||
bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> 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<std::shared_ptr<eh_trunc>> 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<TruncatedStepRow<TruncatedWidth>> 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<TruncatedStepRow<TruncatedWidth>> 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<TruncatedWidth> Xi {Xt[i+l], Xt[i+m],
|
||||
hashLen, lenIndices,
|
||||
CollisionByteLength};
|
||||
if (!(Xi.IsZero(hashLen-CollisionByteLength) &&
|
||||
IsProbablyDuplicate<soln_size>(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<FinalTruncatedWidth> res(Xt[i+l], Xt[i+m],
|
||||
hashLen, lenIndices, 0);
|
||||
auto soln = res.GetTruncatedIndices(hashLen, 2*lenIndices);
|
||||
if (!IsProbablyDuplicate<soln_size>(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<eh_trunc> partialSoln : partialSolns) {
|
||||
std::set<std::vector<unsigned char>> solns;
|
||||
size_t hashLen;
|
||||
size_t lenIndices;
|
||||
unsigned char tmpHash[HashOutput];
|
||||
std::vector<boost::optional<std::vector<FullStepRow<FinalFullWidth>>>> 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<FullStepRow<FinalFullWidth>> 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<std::vector<FullStepRow<FinalFullWidth>>> 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<<r);
|
||||
CollideBranches(*ic, hashLen, lenIndices,
|
||||
CollisionByteLength,
|
||||
CollisionBitLength + 1,
|
||||
partialSoln.get()[lti], partialSoln.get()[rti]);
|
||||
|
||||
// 2d) Check if this has become an invalid solution
|
||||
if (ic->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<FinalFullWidth> 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<unsigned int N, unsigned int K>
|
||||
bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln)
|
||||
{
|
||||
if (soln.size() != SolutionWidth) {
|
||||
LogPrint(BCLog::EQUIHASH, "Invalid solution length: %d (expected %d)\n",
|
||||
soln.size(), SolutionWidth);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<FullStepRow<FinalFullWidth>> 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<FullStepRow<FinalFullWidth>> 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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> 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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> 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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> 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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
|
@ -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 <cstring>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
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<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
|
||||
size_t cBitLen);
|
||||
std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
|
||||
size_t cBitLen);
|
||||
|
||||
template<size_t WIDTH>
|
||||
class StepRow
|
||||
{
|
||||
template<size_t W>
|
||||
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<size_t W>
|
||||
StepRow(const StepRow<W>& a);
|
||||
|
||||
bool IsZero(size_t len);
|
||||
std::string GetHex(size_t len) { return HexStr(hash, hash+len); }
|
||||
|
||||
template<size_t W>
|
||||
friend bool HasCollision(StepRow<W>& a, StepRow<W>& b, int l);
|
||||
};
|
||||
|
||||
class CompareSR
|
||||
{
|
||||
private:
|
||||
size_t len;
|
||||
|
||||
public:
|
||||
CompareSR(size_t l) : len {l} { }
|
||||
|
||||
template<size_t W>
|
||||
inline bool operator()(const StepRow<W>& a, const StepRow<W>& b) { return memcmp(a.hash, b.hash, len) < 0; }
|
||||
};
|
||||
|
||||
template<size_t WIDTH>
|
||||
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l);
|
||||
|
||||
template<size_t WIDTH>
|
||||
class FullStepRow : public StepRow<WIDTH>
|
||||
{
|
||||
template<size_t W>
|
||||
friend class FullStepRow;
|
||||
|
||||
using StepRow<WIDTH>::hash;
|
||||
|
||||
public:
|
||||
FullStepRow(const unsigned char* hashIn, size_t hInLen,
|
||||
size_t hLen, size_t cBitLen, eh_index i);
|
||||
~FullStepRow() { }
|
||||
|
||||
FullStepRow(const FullStepRow<WIDTH>& a) : StepRow<WIDTH> {a} { }
|
||||
template<size_t W>
|
||||
FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim);
|
||||
FullStepRow& operator=(const FullStepRow<WIDTH>& a);
|
||||
|
||||
inline bool IndicesBefore(const FullStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
|
||||
std::vector<unsigned char> GetIndices(size_t len, size_t lenIndices,
|
||||
size_t cBitLen) const;
|
||||
|
||||
template<size_t W>
|
||||
friend bool DistinctIndices(const FullStepRow<W>& a, const FullStepRow<W>& b,
|
||||
size_t len, size_t lenIndices);
|
||||
template<size_t W>
|
||||
friend bool IsValidBranch(const FullStepRow<W>& a, const size_t len, const unsigned int ilen, const eh_trunc t);
|
||||
};
|
||||
|
||||
template<size_t WIDTH>
|
||||
class TruncatedStepRow : public StepRow<WIDTH>
|
||||
{
|
||||
template<size_t W>
|
||||
friend class TruncatedStepRow;
|
||||
|
||||
using StepRow<WIDTH>::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<WIDTH>& a) : StepRow<WIDTH> {a} { }
|
||||
template<size_t W>
|
||||
TruncatedStepRow(const TruncatedStepRow<W>& a, const TruncatedStepRow<W>& b, size_t len, size_t lenIndices, int trim);
|
||||
TruncatedStepRow& operator=(const TruncatedStepRow<WIDTH>& a);
|
||||
|
||||
inline bool IndicesBefore(const TruncatedStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
|
||||
std::shared_ptr<eh_trunc> 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<unsigned int N, unsigned int K>
|
||||
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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
bool OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
bool IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> 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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> 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<bool(std::vector<unsigned char>)> 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<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> 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<bool(std::vector<unsigned char>)> 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
|
|
@ -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 <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
// Checks if the intersection of a.indices and b.indices is empty
|
||||
template<size_t WIDTH>
|
||||
bool DistinctIndices(const FullStepRow<WIDTH>& a, const FullStepRow<WIDTH>& 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<size_t MAX_INDICES>
|
||||
bool IsProbablyDuplicate(std::shared_ptr<eh_trunc> 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<size_t WIDTH>
|
||||
bool IsValidBranch(const FullStepRow<WIDTH>& a, const size_t len, const unsigned int ilen, const eh_trunc t)
|
||||
{
|
||||
return TruncateIndex(ArrayToEhIndex(a.hash+len), ilen) == t;
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <atomic>
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__)
|
||||
#if defined(USE_ASM)
|
||||
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ std::unique_ptr<CBlockTemplate> 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;
|
||||
|
|
|
@ -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 <paymentdisclosuredb.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "leveldbwrapper.h"
|
||||
#include <util.h>
|
||||
#include <dbwrapper.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
|
@ -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<std::mutex> 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 {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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<unsigned char> 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 <typename Stream, typename Operation>
|
||||
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.
|
||||
|
|
|
@ -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 <primitives/joinsplit.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
||||
#include <hash.h>
|
||||
#include <tinyformat.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
JSDescription::JSDescription(ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& anchor,
|
||||
const std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& 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<libzcash::Note, ZC_NUM_JS_OUTPUTS> 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<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof,
|
||||
uint256 *esk, // payment disclosure
|
||||
std::function<int(int)> 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);
|
||||
}
|
|
@ -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 <boost/array.hpp>
|
||||
|
||||
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<uint256, ZC_NUM_JS_INPUTS> 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<uint256, ZC_NUM_JS_OUTPUTS> 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<ZCNoteEncryption::Ciphertext, ZC_NUM_JS_OUTPUTS> ciphertexts = {{ {{0}} }};
|
||||
|
||||
// Random seed
|
||||
uint256 randomSeed;
|
||||
|
||||
// MACs
|
||||
// The verification of the JoinSplit requires these MACs
|
||||
// to be provided as an input.
|
||||
std::array<uint256, ZC_NUM_JS_INPUTS> 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<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
const std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& 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<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS>& outputs,
|
||||
std::array<size_t, ZC_NUM_JS_INPUTS>& inputMap,
|
||||
std::array<size_t, ZC_NUM_JS_OUTPUTS>& outputMap,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new,
|
||||
bool computeProof = true, // Set to false in some tests
|
||||
uint256 *esk = nullptr, // payment disclosure
|
||||
std::function<int(int)> 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 <typename Stream, typename Operation>
|
||||
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
|
|
@ -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<JSDescription>::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<JSDescription>::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);
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <primitives/joinsplit.h>
|
||||
#include <zcash/NoteEncryption.hpp>
|
||||
#include <zcash/Zcash.h>
|
||||
#include <zcash/JoinSplit.hpp>
|
||||
#include <zcash/Proof.hpp>
|
||||
|
||||
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<typename Stream, typename TxType>
|
||||
|
@ -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<unsigned char, 64> joinsplit_sig_t;
|
||||
|
||||
// Default transaction version.
|
||||
static const int32_t CURRENT_VERSION=2;
|
||||
|
||||
|
@ -282,6 +304,10 @@ public:
|
|||
const std::vector<CTxOut> vout;
|
||||
const int32_t nVersion;
|
||||
const uint32_t nLockTime;
|
||||
const std::vector<JSDescription> 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<CTxOut> vout;
|
||||
int32_t nVersion;
|
||||
uint32_t nLockTime;
|
||||
std::vector<JSDescription> vjoinsplit;
|
||||
uint256 joinSplitPubKey;
|
||||
CTransaction::joinsplit_sig_t joinSplitSig = {{0}};
|
||||
|
||||
CMutableTransaction();
|
||||
explicit CMutableTransaction(const CTransaction& tx);
|
||||
|
|
31
src/random.h
31
src/random.h
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <limits>
|
||||
#include <functional>
|
||||
|
||||
/* 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 <typename RandomAccessIterator, typename MapRandomAccessIterator>
|
||||
void MappedShuffle(RandomAccessIterator first,
|
||||
MapRandomAccessIterator mapFirst,
|
||||
size_t len,
|
||||
std::function<int(int)> 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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <consensus/params.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
#include <crypto/equihash.h>
|
||||
#include <init.h>
|
||||
#include <validation.h>
|
||||
#include <key_io.h>
|
||||
|
@ -116,6 +117,8 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> 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<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
|
||||
|
@ -126,16 +129,52 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> 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<bool(std::vector<unsigned char>)> validBlock =
|
||||
[&pblock](std::vector<unsigned char> 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<const CBlock> shared_pblock = std::make_shared<const CBlock>(*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));
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include <prevector.h>
|
||||
#include <span.h>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
static const unsigned int MAX_SIZE = 0x02000000;
|
||||
|
||||
/**
|
||||
|
@ -546,12 +549,24 @@ template<typename Stream, typename T, typename A> void Unserialize_impl(Stream&
|
|||
template<typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream& is, std::vector<T, A>& v, const V&);
|
||||
template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v);
|
||||
|
||||
/**
|
||||
* array
|
||||
*/
|
||||
template<typename Stream, typename T, std::size_t N> void Serialize(Stream& os, const std::array<T, N>& item);
|
||||
template<typename Stream, typename T, std::size_t N> void Unserialize(Stream& is, std::array<T, N>& item);
|
||||
|
||||
/**
|
||||
* pair
|
||||
*/
|
||||
template<typename Stream, typename K, typename T> void Serialize(Stream& os, const std::pair<K, T>& item);
|
||||
template<typename Stream, typename K, typename T> void Unserialize(Stream& is, std::pair<K, T>& item);
|
||||
|
||||
/**
|
||||
* optional
|
||||
*/
|
||||
template<typename Stream, typename T> void Serialize(Stream& os, const boost::optional<T>& item);
|
||||
template<typename Stream, typename T> void Unserialize(Stream& is, boost::optional<T>& item);
|
||||
|
||||
/**
|
||||
* map
|
||||
*/
|
||||
|
@ -753,6 +768,24 @@ inline void Unserialize(Stream& is, std::vector<T, A>& v)
|
|||
Unserialize_impl(is, v, T());
|
||||
}
|
||||
|
||||
/**
|
||||
* array
|
||||
*/
|
||||
template<typename Stream, typename T, std::size_t N>
|
||||
void Serialize(Stream& os, const std::array<T, N>& item)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
Serialize(os, item[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream, typename T, std::size_t N>
|
||||
void Unserialize(Stream& is, std::array<T, N>& item)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
Unserialize(is, item[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -773,6 +806,41 @@ void Unserialize(Stream& is, std::pair<K, T>& item)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* optional
|
||||
*/
|
||||
template<typename Stream, typename T>
|
||||
void Serialize(Stream& os, const boost::optional<T>& 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<typename Stream, typename T>
|
||||
void Unserialize(Stream& is, boost::optional<T>& 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
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#include <test/test_bitcoin.h>
|
||||
#include <crypto/sha256.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
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()
|
|
@ -72,7 +72,7 @@ std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
|
|||
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
|
||||
|
||||
while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
|
||||
++(pblock->nNonce);
|
||||
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
|
||||
}
|
||||
|
||||
return pblock;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef UINT252_H
|
||||
#define UINT252_H
|
||||
|
||||
#include <vector>
|
||||
#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 <typename Stream, typename Operation>
|
||||
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
|
50
src/util.cpp
50
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)
|
||||
{
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <asyncrpcoperation_sendmany.h>
|
||||
#include <asyncrpcqueue.h>
|
||||
#include <amount.h>
|
||||
#include <consensus/upgrades.h>
|
||||
#include <core_io.h>
|
||||
#include <init.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 <validation.h>
|
||||
#include <rpcprotocol.h>
|
||||
#include <zcash/IncrementalMerkleTree.hpp>
|
||||
#include <sodium.h>
|
||||
#include <miner.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
@ -1171,4 +1171,3 @@ UniValue AsyncRPCOperation_sendmany::getStatus() const {
|
|||
obj.push_back(Pair("params", contextinfo_ ));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#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 <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 <iostream>
|
||||
#include <chrono>
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
#ifndef ASYNCRPCOPERATION_SHIELDCOINBASE_H
|
||||
#define ASYNCRPCOPERATION_SHIELDCOINBASE_H
|
||||
|
||||
#include "asyncrpcoperation.h"
|
||||
#include "amount.h"
|
||||
#include "base58.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "wallet.h"
|
||||
#include <asyncrpcoperation.h>
|
||||
#include <amount.h>
|
||||
#include <base58.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <zcash/JoinSplit.hpp>
|
||||
#include <zcash/Address.hpp>
|
||||
#include <wallet.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
|
@ -126,4 +126,3 @@ public:
|
|||
|
||||
|
||||
#endif /* ASYNCRPCOPERATION_SHIELDCOINBASE_H */
|
||||
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
// 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.
|
||||
|
||||
#ifndef BITCOIN_WALLET_JOINSPLIT_H
|
||||
#define BITCOIN_WALLET_JOINSPLIT_H
|
||||
|
||||
#include <uint256.h>
|
||||
#include <zcash/Address.hpp>
|
||||
|
||||
/** A note outpoint */
|
||||
class JSOutPoint
|
||||
{
|
||||
public:
|
||||
// Transaction hash
|
||||
uint256 hash;
|
||||
// Index into CTransaction.vjoinsplit
|
||||
size_t js;
|
||||
// Index into JSDescription fields of length ZC_NUM_JS_OUTPUTS
|
||||
uint8_t n;
|
||||
|
||||
JSOutPoint() { SetNull(); }
|
||||
JSOutPoint(uint256 h, size_t js, uint8_t n) : hash {h}, js {js}, n {n} { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(hash);
|
||||
READWRITE(js);
|
||||
READWRITE(n);
|
||||
}
|
||||
|
||||
void SetNull() { hash.SetNull(); }
|
||||
bool IsNull() const { return hash.IsNull(); }
|
||||
|
||||
friend bool operator<(const JSOutPoint& a, const JSOutPoint& b) {
|
||||
return (a.hash < b.hash ||
|
||||
(a.hash == b.hash && a.js < b.js) ||
|
||||
(a.hash == b.hash && a.js == b.js && a.n < b.n));
|
||||
}
|
||||
|
||||
friend bool operator==(const JSOutPoint& a, const JSOutPoint& b) {
|
||||
return (a.hash == b.hash && a.js == b.js && a.n == b.n);
|
||||
}
|
||||
|
||||
friend bool operator!=(const JSOutPoint& a, const JSOutPoint& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
class CNoteData
|
||||
{
|
||||
public:
|
||||
libzcash::PaymentAddress address;
|
||||
|
||||
/**
|
||||
* Cached note nullifier. May not be set if the wallet was not unlocked when
|
||||
* this was CNoteData was created. If not set, we always assume that the
|
||||
* note has not been spent.
|
||||
*
|
||||
* It's okay to cache the nullifier in the wallet, because we are storing
|
||||
* the spending key there too, which could be used to derive this.
|
||||
* If the wallet is encrypted, this means that someone with access to the
|
||||
* locked wallet cannot spend notes, but can connect received notes to the
|
||||
* transactions they are spent in. This is the same security semantics as
|
||||
* for transparent addresses.
|
||||
*/
|
||||
boost::optional<uint256> nullifier;
|
||||
|
||||
/**
|
||||
* Cached incremental witnesses for spendable Notes.
|
||||
* Beginning of the list is the most recent witness.
|
||||
*/
|
||||
std::list<ZCIncrementalWitness> witnesses;
|
||||
|
||||
/**
|
||||
* Block height corresponding to the most current witness.
|
||||
*
|
||||
* When we first create a CNoteData in CWallet::FindMyNotes, this is set to
|
||||
* -1 as a placeholder. The next time CWallet::ChainTip is called, we can
|
||||
* determine what height the witness cache for this note is valid for (even
|
||||
* if no witnesses were cached), and so can set the correct value in
|
||||
* CWallet::IncrementNoteWitnesses and CWallet::DecrementNoteWitnesses.
|
||||
*/
|
||||
int witnessHeight;
|
||||
|
||||
CNoteData() : address(), nullifier(), witnessHeight {-1} { }
|
||||
CNoteData(libzcash::PaymentAddress a) :
|
||||
address {a}, nullifier(), witnessHeight {-1} { }
|
||||
CNoteData(libzcash::PaymentAddress a, uint256 n) :
|
||||
address {a}, nullifier {n}, witnessHeight {-1} { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(address);
|
||||
READWRITE(nullifier);
|
||||
READWRITE(witnesses);
|
||||
READWRITE(witnessHeight);
|
||||
}
|
||||
|
||||
friend bool operator<(const CNoteData& a, const CNoteData& b) {
|
||||
return (a.address < b.address ||
|
||||
(a.address == b.address && a.nullifier < b.nullifier));
|
||||
}
|
||||
|
||||
friend bool operator==(const CNoteData& a, const CNoteData& b) {
|
||||
return (a.address == b.address && a.nullifier == b.nullifier);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CNoteData& a, const CNoteData& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<JSOutPoint, CNoteData> mapNoteData_t;
|
||||
|
||||
/** Decrypted note and its location in a transaction. */
|
||||
struct CNotePlaintextEntry
|
||||
{
|
||||
JSOutPoint jsop;
|
||||
libzcash::PaymentAddress address;
|
||||
libzcash::NotePlaintext plaintext;
|
||||
};
|
||||
|
||||
/** Decrypted note, location in a transaction, and confirmation height. */
|
||||
struct CUnspentNotePlaintextEntry {
|
||||
JSOutPoint jsop;
|
||||
libzcash::PaymentAddress address;
|
||||
libzcash::NotePlaintext plaintext;
|
||||
int nHeight;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -18,6 +18,7 @@
|
|||
#include <util.h>
|
||||
#include <wallet/crypter.h>
|
||||
#include <wallet/coinselection.h>
|
||||
#include <wallet/joinsplit.h>
|
||||
#include <wallet/walletdb.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(a_pk);
|
||||
READWRITE(pk_enc);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(a_pk);
|
||||
READWRITE(sk_enc);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "../util.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
#include <../util.h>
|
||||
#include <primitives/joinsplit.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <zcash/JoinSplit.hpp>
|
||||
|
||||
#include <libsnark/common/profiling.hpp>
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(authentication_path);
|
||||
READWRITE(index);
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(left);
|
||||
READWRITE(right);
|
||||
READWRITE(parents);
|
||||
|
@ -156,7 +156,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(tree);
|
||||
READWRITE(filled);
|
||||
READWRITE(cursor);
|
||||
|
|
|
@ -40,7 +40,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = 0x00;
|
||||
READWRITE(leadingByte);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(data);
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(data);
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = G1_PREFIX_MASK;
|
||||
|
||||
if (y_lsb) {
|
||||
|
@ -143,7 +143,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char leadingByte = G2_PREFIX_MASK;
|
||||
|
||||
if (y_gt) {
|
||||
|
@ -204,7 +204,7 @@ public:
|
|||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(g_A);
|
||||
READWRITE(g_A_prime);
|
||||
READWRITE(g_B);
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) 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 <streams.h>
|
||||
#include <zkaddr.h>
|
||||
|
||||
template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
|
||||
bool CZCEncoding<DATA_TYPE, PREFIX, SER_SIZE>::Set(const DATA_TYPE& addr)
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << addr;
|
||||
std::vector<unsigned char> addrSerialized(ss.begin(), ss.end());
|
||||
assert(addrSerialized.size() == SER_SIZE);
|
||||
SetData(Params().Base58Prefix(PREFIX), &addrSerialized[0], SER_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
|
||||
DATA_TYPE CZCEncoding<DATA_TYPE, PREFIX, SER_SIZE>::Get() const
|
||||
{
|
||||
if (vchData.size() != SER_SIZE) {
|
||||
throw std::runtime_error(
|
||||
PrependName(" is invalid")
|
||||
);
|
||||
}
|
||||
|
||||
if (vchVersion != Params().Base58Prefix(PREFIX)) {
|
||||
throw std::runtime_error(
|
||||
PrependName(" is for wrong network type")
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> serialized(vchData.begin(), vchData.end());
|
||||
|
||||
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
|
||||
DATA_TYPE ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Explicit instantiations for libzcash::PaymentAddress
|
||||
template bool CZCEncoding<libzcash::PaymentAddress,
|
||||
CChainParams::ZCPAYMENT_ADDRRESS,
|
||||
libzcash::SerializedPaymentAddressSize>::Set(const libzcash::PaymentAddress& addr);
|
||||
template libzcash::PaymentAddress CZCEncoding<libzcash::PaymentAddress,
|
||||
CChainParams::ZCPAYMENT_ADDRRESS,
|
||||
libzcash::SerializedPaymentAddressSize>::Get() const;
|
||||
|
||||
// Explicit instantiations for libzcash::ViewingKey
|
||||
template bool CZCEncoding<libzcash::ViewingKey,
|
||||
CChainParams::ZCVIEWING_KEY,
|
||||
libzcash::SerializedViewingKeySize>::Set(const libzcash::ViewingKey& vk);
|
||||
template libzcash::ViewingKey CZCEncoding<libzcash::ViewingKey,
|
||||
CChainParams::ZCVIEWING_KEY,
|
||||
libzcash::SerializedViewingKeySize>::Get() const;
|
||||
|
||||
// Explicit instantiations for libzcash::SpendingKey
|
||||
template bool CZCEncoding<libzcash::SpendingKey,
|
||||
CChainParams::ZCSPENDING_KEY,
|
||||
libzcash::SerializedSpendingKeySize>::Set(const libzcash::SpendingKey& sk);
|
||||
template libzcash::SpendingKey CZCEncoding<libzcash::SpendingKey,
|
||||
CChainParams::ZCSPENDING_KEY,
|
||||
libzcash::SerializedSpendingKeySize>::Get() const;
|
|
@ -0,0 +1,57 @@
|
|||
// 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.
|
||||
|
||||
#ifndef BITCOIN_ZKADDR_H
|
||||
#define BITCOIN_ZKADDR_H
|
||||
|
||||
#include <base58.h>
|
||||
#include <chainparams.h>
|
||||
#include <zcash/Address.hpp>
|
||||
|
||||
template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
|
||||
class CZCEncoding : public CBase58Data {
|
||||
protected:
|
||||
virtual std::string PrependName(const std::string& s) const = 0;
|
||||
|
||||
public:
|
||||
bool Set(const DATA_TYPE& addr);
|
||||
|
||||
DATA_TYPE Get() const;
|
||||
};
|
||||
|
||||
class CZCPaymentAddress : public CZCEncoding<libzcash::PaymentAddress, CChainParams::ZCPAYMENT_ADDRRESS, libzcash::SerializedPaymentAddressSize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "payment address" + s; }
|
||||
|
||||
public:
|
||||
CZCPaymentAddress() {}
|
||||
|
||||
CZCPaymentAddress(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
|
||||
CZCPaymentAddress(const libzcash::PaymentAddress& addr) { Set(addr); }
|
||||
};
|
||||
|
||||
class CZCViewingKey : public CZCEncoding<libzcash::ViewingKey, CChainParams::ZCVIEWING_KEY, libzcash::SerializedViewingKeySize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "viewing key" + s; }
|
||||
|
||||
public:
|
||||
CZCViewingKey() {}
|
||||
|
||||
CZCViewingKey(const std::string& strViewingKey) { SetString(strViewingKey.c_str(), 3); }
|
||||
CZCViewingKey(const libzcash::ViewingKey& vk) { Set(vk); }
|
||||
};
|
||||
|
||||
class CZCSpendingKey : public CZCEncoding<libzcash::SpendingKey, CChainParams::ZCSPENDING_KEY, libzcash::SerializedSpendingKeySize> {
|
||||
protected:
|
||||
std::string PrependName(const std::string& s) const { return "spending key" + s; }
|
||||
|
||||
public:
|
||||
CZCSpendingKey() {}
|
||||
|
||||
CZCSpendingKey(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
|
||||
CZCSpendingKey(const libzcash::SpendingKey& addr) { Set(addr); }
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue