[primitives] Add Joinsplit primitives

This commit is contained in:
jc 2018-03-28 21:34:51 -04:00
parent 8e04b9dd54
commit 27a9fcf14c
42 changed files with 2298 additions and 137 deletions

0
.gitmodules vendored Normal file
View File

View File

@ -86,7 +86,7 @@ $(OSX_APP)/Contents/PkgInfo:
$(OSX_APP)/Contents/Resources/empty.lproj:
$(MKDIR_P) $(@D)
@touch $@
@touch $@
$(OSX_APP)/Contents/Info.plist: $(OSX_PLIST)
$(MKDIR_P) $(@D)
@ -242,7 +242,7 @@ cov: test_bitcoin.coverage/.dirstamp cov-zcash total.coverage/.dirstamp
endif
dist_bin_SCRIPTS = zcutil/fetch-params.sh
dist_bin_SCRIPTS = #zcutil/fetch-params.sh
dist_noinst_SCRIPTS = autogen.sh
EXTRA_DIST = $(DIST_SHARE) test/functional/test_runner.py test/functional test/zcash $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS)
@ -327,4 +327,3 @@ clean-docs:
clean-local: clean-docs
rm -rf coverage_percent.txt test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP)
rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache

View File

@ -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

View File

@ -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 \

View File

@ -24,5 +24,6 @@ zcash_CreateJoinSplit_LDADD = \
$(LIBSNARK) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBBITCOIN_CONSENSUS) \
$(BOOST_LIBS) \
$(LIBZCASH_LIBS)

View File

@ -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

View File

@ -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()

View File

@ -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

816
src/crypto/equihash.cpp Normal file
View File

@ -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);

279
src/crypto/equihash.h Normal file
View File

@ -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

49
src/crypto/equihash.tcc Normal file
View File

@ -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;
}

View File

@ -8,6 +8,7 @@
#include <assert.h>
#include <string.h>
#include <atomic>
#include <stdexcept>
#if defined(__x86_64__) || defined(__amd64__)
#if defined(USE_ASM)
@ -149,8 +150,8 @@ bool SelfTest(TransformType tr) {
static const unsigned char in1[65] = {0, 0x80};
static const unsigned char in2[129] = {
0,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0
};
@ -241,6 +242,22 @@ void CSHA256::Finalize(unsigned char hash[OUTPUT_SIZE])
WriteBE32(hash + 28, s[7]);
}
void CSHA256::FinalizeNoPadding(unsigned char hash[OUTPUT_SIZE], bool enforce_compression)
{
if (enforce_compression && bytes != 64) {
throw std::length_error("SHA256Compress should be invoked with a 512-bit block");
}
WriteBE32(hash, s[0]);
WriteBE32(hash + 4, s[1]);
WriteBE32(hash + 8, s[2]);
WriteBE32(hash + 12, s[3]);
WriteBE32(hash + 16, s[4]);
WriteBE32(hash + 20, s[5]);
WriteBE32(hash + 24, s[6]);
WriteBE32(hash + 28, s[7]);
}
CSHA256& CSHA256::Reset()
{
bytes = 0;

View File

@ -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();
};

View File

@ -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;

View File

@ -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 {

View File

@ -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";

View File

@ -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.

View File

@ -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);
}

148
src/primitives/joinsplit.h Normal file
View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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());

View File

@ -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));

View File

@ -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;
/**
@ -173,11 +176,11 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
#define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__))
#define READWRITEAS(type, obj) (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj)))
/**
/**
* Implement three methods for serializable objects. These are actually wrappers over
* "SerializationOp" template, which implements the body of each class' serialization
* code. Adding "ADD_SERIALIZE_METHODS" in the body of the class causes these wrappers to be
* added as members.
* added as members.
*/
#define ADD_SERIALIZE_METHODS \
template<typename Stream> \
@ -308,16 +311,16 @@ uint64_t ReadCompactSize(Stream& is)
* sure the encoding is one-to-one, one is subtracted from all but the last digit.
* Thus, the byte sequence a[] with length len, where all but the last byte
* has bit 128 set, encodes the number:
*
*
* (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1))
*
*
* Properties:
* * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes)
* * Every integer has exactly one encoding
* * Encoding does not depend on size of original integer type
* * No redundancy: every (infinite) byte sequence corresponds to a list
* of encoded integers.
*
*
* 0: [0x00] 256: [0x81 0x00]
* 1: [0x01] 16383: [0xFE 0x7F]
* 127: [0x7F] 16384: [0xFF 0x00]
@ -546,12 +549,24 @@ template<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

View File

@ -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()

View File

@ -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;

50
src/uint252.h Normal file
View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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>
@ -65,15 +65,15 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
if (minDepth < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minconf cannot be negative");
}
if (fromAddress.size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "From address parameter missing");
}
if (tOutputs.size() == 0 && zOutputs.size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "No recipients");
}
fromtaddr_ = CBitcoinAddress(fromAddress);
isfromtaddr_ = fromtaddr_.IsValid();
isfromzaddr_ = false;
@ -88,7 +88,7 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
if (!pwalletMain->GetSpendingKey(addr, key)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, no spending key found for zaddr");
}
isfromzaddr_ = true;
frompaymentaddress_ = addr;
spendingkey_ = key;
@ -224,9 +224,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any non-coinbase UTXOs to spend.");
}
}
}
}
}
if (isfromzaddr_ && !find_unspent_notes()) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no unspent notes found for zaddr from address.");
}
@ -262,7 +262,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
strprintf("Insufficient transparent funds, have %s, need %s",
FormatMoney(t_inputs_total), FormatMoney(targetAmount)));
}
if (isfromzaddr_ && (z_inputs_total < targetAmount)) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
strprintf("Insufficient protected funds, have %s, need %s",
@ -351,18 +351,18 @@ bool AsyncRPCOperation_sendmany::main_impl() {
/**
* SCENARIO #1
*
*
* taddr -> taddrs
*
*
* There are no zaddrs or joinsplits involved.
*/
if (isPureTaddrOnlyTx) {
add_taddr_outputs_to_tx();
CAmount funds = selectedUTXOAmount;
CAmount fundsSpent = t_outputs_total + minersFee;
CAmount change = funds - fundsSpent;
if (change > 0) {
add_taddr_change_output_to_tx(change);
@ -371,7 +371,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
FormatMoney(change)
);
}
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("rawtxn", EncodeHexTx(tx_)));
sign_send_raw_transaction(obj);
@ -381,7 +381,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
* END SCENARIO #1
*/
// Prepare raw transaction to handle JoinSplits
CMutableTransaction mtx(tx_);
crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_);
@ -421,10 +421,10 @@ bool AsyncRPCOperation_sendmany::main_impl() {
/**
* SCENARIO #2
*
*
* taddr -> taddrs
* -> zaddrs
*
*
* Note: Consensus rule states that coinbase utxos can only be sent to a zaddr.
* Local wallet rule does not allow any change when sending coinbase utxos
* since there is currently no way to specify a change address and we don't
@ -432,11 +432,11 @@ bool AsyncRPCOperation_sendmany::main_impl() {
*/
if (isfromtaddr_) {
add_taddr_outputs_to_tx();
CAmount funds = selectedUTXOAmount;
CAmount fundsSpent = t_outputs_total + minersFee + z_outputs_total;
CAmount change = funds - fundsSpent;
if (change > 0) {
if (selectedUTXOCoinbase) {
assert(isSingleZaddrOutput);
@ -473,7 +473,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
jso.memo = get_memo_from_hex_string(hexMemo);
}
info.vjsout.push_back(jso);
// Funds are removed from the value pool and enter the private pool
info.vpub_old += value;
}
@ -484,16 +484,16 @@ bool AsyncRPCOperation_sendmany::main_impl() {
}
/**
* END SCENARIO #2
*/
*/
/**
* SCENARIO #3
*
*
* zaddr -> taddrs
* -> zaddrs
*
*
* Send to zaddrs by chaining JoinSplits together and immediately consuming any change
* Send to taddrs by creating dummy z outputs and accumulating value in a change note
* which is used to set vpub_new in the last chained joinsplit.
@ -619,9 +619,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
vOutPoints.push_back(jso);
vInputNotes.push_back(note);
jsInputValue += noteFunds;
int wtxHeight = -1;
int wtxDepth = -1;
{
@ -644,14 +644,14 @@ bool AsyncRPCOperation_sendmany::main_impl() {
wtxDepth
);
}
// Add history of previous commitments to witness
if (vInputNotes.size() > 0) {
if (vInputWitnesses.size()==0) {
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment");
}
for (auto & optionalWitness : vInputWitnesses) {
if (!optionalWitness) {
throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null");
@ -766,7 +766,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
* Raw transaction as hex string should be in object field "rawtxn"
*/
void AsyncRPCOperation_sendmany::sign_send_raw_transaction(UniValue obj)
{
{
// Sign the raw transaction
UniValue rawtxnValue = find_value(obj, "rawtxn");
if (rawtxnValue.isNull()) {
@ -861,7 +861,7 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
if (isCoinbase && fAcceptCoinbase==false) {
continue;
}
CAmount nValue = out.tx->vout[out.i].nValue;
SendManyInputUTXO utxo(out.tx->GetHash(), out.i, nValue, isCoinbase);
t_inputs_.push_back(utxo);
@ -895,7 +895,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
HexStr(data).substr(0, 10)
);
}
if (z_inputs_.size() == 0) {
return false;
}
@ -1136,7 +1136,7 @@ void AsyncRPCOperation_sendmany::add_taddr_change_output_to_tx(CAmount amount) {
boost::array<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_sendmany::get_memo_from_hex_string(std::string s) {
boost::array<unsigned char, ZC_MEMO_SIZE> memo = {{0x00}};
std::vector<unsigned char> rawMemo = ParseHex(s.c_str());
// If ParseHex comes across a non-hex char, it will stop but still return results so far.
@ -1144,11 +1144,11 @@ boost::array<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_sendmany::get_memo_f
if (slen % 2 !=0 || (slen>0 && rawMemo.size()!=slen/2)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo must be in hexadecimal format");
}
if (rawMemo.size() > ZC_MEMO_SIZE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Memo size of %d is too big, maximum allowed is %d", rawMemo.size(), ZC_MEMO_SIZE));
}
// copy vector into boost array
int lenMemo = rawMemo.size();
for (int i = 0; i < ZC_MEMO_SIZE && i < lenMemo; i++) {
@ -1171,4 +1171,3 @@ UniValue AsyncRPCOperation_sendmany::getStatus() const {
obj.push_back(Pair("params", contextinfo_ ));
return obj;
}

View File

@ -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>

View File

@ -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 */

139
src/wallet/joinsplit.h Normal file
View File

@ -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

View File

@ -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>

View File

@ -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);
}

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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);

64
src/zkaddr.cpp Normal file
View File

@ -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;

57
src/zkaddr.h Normal file
View File

@ -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