Merge remote-tracking branch 'zcash/master' into dev

# Conflicts:
#	.gitignore
#	README.md
#	src/Makefile.gtest.include
#	src/gtest/test_checkblock.cpp
#	src/init.cpp
#	src/main.cpp
#	src/main.h
#	src/rpcserver.cpp
#	src/test/checkblock_tests.cpp
#	src/util.cpp
This commit is contained in:
jl777
2017-01-25 09:26:28 +02:00
80 changed files with 1225 additions and 313 deletions

View File

@@ -59,6 +59,7 @@ LIBZCASH_CONSENSUS=
endif
bin_PROGRAMS =
noinst_PROGRAMS =
TESTS =
if BUILD_BITCOIND
@@ -482,7 +483,7 @@ libzcashconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -DBUILD_BITCO
endif
#
CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno
CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno */*.gcno wallet/*/*.gcno
DISTCLEANFILES = obj/build.h

View File

@@ -1,3 +1,4 @@
TESTS += komodo-gtest
bin_PROGRAMS += komodo-gtest
@@ -10,6 +11,13 @@ komodo_gtest_SOURCES = \
gtest/json_test_vectors.h \
# gtest/test_foundersreward.cpp \
gtest/test_wallet_zkeys.cpp \
# These tests are order-dependent, because they
# depend on global state (see #1539)
if ENABLE_WALLET
zcash_gtest_SOURCES += \
wallet/gtest/test_wallet_zkeys.cpp
endif
zcash_gtest_SOURCES += \
gtest/test_jsonspirit.cpp \
gtest/test_tautology.cpp \
gtest/test_equihash.cpp \

View File

@@ -1,5 +1,5 @@
TESTS += test/test_bitcoin
bin_PROGRAMS += test/test_bitcoin
noinst_PROGRAMS += test/test_bitcoin
TEST_SRCDIR = test
TEST_BINARY=test/test_bitcoin$(EXEEXT)

View File

@@ -1,5 +1,6 @@
bin_PROGRAMS += \
zcash/GenerateParams
noinst_PROGRAMS += \
zcash/GenerateParams \
zcash/CreateJoinSplit
# tool for generating our public parameters
zcash_GenerateParams_SOURCES = zcash/GenerateParams.cpp
@@ -9,3 +10,13 @@ zcash_GenerateParams_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH_LIBS)
# tool for profiling the creation of joinsplits
zcash_CreateJoinSplit_SOURCES = zcash/CreateJoinSplit.cpp
zcash_CreateJoinSplit_LDADD = \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH) \
$(BOOST_LIBS) \
$(LIBZCASH_LIBS)

View File

@@ -66,7 +66,8 @@ public:
return creation_time_;
}
Value getStatus() const;
// Override this method to add data to the default status object.
virtual Value getStatus() const;
Value getError() const;

View File

@@ -87,6 +87,8 @@ static bool AppInitRPC(int argc, char* argv[])
" komodo-cli [options] help <command> " + _("Get help for a command") + "\n";
strUsage += "\n" + HelpMessageCli();
} else {
strUsage += LicenseInfo();
}
fprintf(stdout, "%s", strUsage.c_str());
@@ -112,12 +114,6 @@ static bool AppInitRPC(int argc, char* argv[])
Object CallRPC(const string& strMethod, const Array& params)
{
if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
throw runtime_error(strprintf(
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
"If the file does not exist, create it with owner-readable-only file permissions."),
GetConfigFile().string().c_str()));
// Connect to localhost
bool fUseSSL = GetBoolArg("-rpcssl", false);
boost::asio::io_service io_service;
@@ -131,10 +127,24 @@ Object CallRPC(const string& strMethod, const Array& params)
if (!fConnected)
throw CConnectionFailed("couldn't connect to server");
// Find credentials to use
std::string strRPCUserColonPass;
if (mapArgs["-rpcpassword"] == "") {
// Try fall back to cookie-based authentication if no password is provided
if (!GetAuthCookie(&strRPCUserColonPass)) {
throw runtime_error(strprintf(
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
"If the file does not exist, create it with owner-readable-only file permissions."),
GetConfigFile().string().c_str()));
}
} else {
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
}
// HTTP basic authentication
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
map<string, string> mapRequestHeaders;
mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
mapRequestHeaders["Authorization"] = string("Basic ") + EncodeBase64(strRPCUserColonPass);
// Send request
string strRequest = JSONRPCRequest(strMethod, params, 1);

View File

@@ -115,6 +115,24 @@ bool AppInit(int argc, char* argv[])
try
{
ReadConfigFile(mapArgs, mapMultiArgs);
} catch (const missing_zcash_conf& e) {
fprintf(stderr,
(_("Before starting zcashd, you need to create a configuration file:\n"
"%s\n"
"It can be completely empty! That indicates you are happy with the default\n"
"configuration of zcashd. But requiring a configuration file to start ensures\n"
"that zcashd won't accidentally compromise your privacy if there was a default\n"
"option you needed to change.\n"
"\n"
"You can look at the example configuration file for suggestions of default\n"
"options that you may want to change. It should be in one of these locations,\n"
"depending on how you installed Zcash:\n") +
_("- Source code: %s\n"
"- .deb package: %s\n")).c_str(),
GetConfigFile().string().c_str(),
"contrib/DEBIAN/examples/zcash.conf",
"/usr/share/doc/zcash/examples/zcash.conf");
return false;
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
return false;

View File

@@ -16,7 +16,7 @@
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 1
#define CLIENT_VERSION_MINOR 0
#define CLIENT_VERSION_REVISION 3
#define CLIENT_VERSION_REVISION 5
#define CLIENT_VERSION_BUILD 50
//! Set to true for release, false for prerelease or test build

View File

@@ -3,6 +3,7 @@
#include "consensus/validation.h"
#include "main.h"
#include "zcash/Proof.hpp"
class MockCValidationState : public CValidationState {
public:
@@ -22,12 +23,14 @@ public:
};
TEST(CheckBlock, VersionTooLow) {
auto verifier = libzcash::ProofVerifier::Strict();
CBlock block;
block.nVersion = 1;
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1);
EXPECT_FALSE(CheckBlock(0,0,block, state, false, false));
EXPECT_FALSE(CheckBlock(0,0,block, state, verifier, false, false));
}
TEST(ContextualCheckBlock, BadCoinbaseHeight) {

View File

@@ -2,7 +2,9 @@
#include "keystore.h"
#include "random.h"
#ifdef ENABLE_WALLET
#include "wallet/crypter.h"
#endif
#include "zcash/Address.hpp"
TEST(keystore_tests, store_and_retrieve_spending_key) {
@@ -44,6 +46,7 @@ TEST(keystore_tests, store_and_retrieve_note_decryptor) {
EXPECT_EQ(ZCNoteDecryption(sk.viewing_key()), decOut);
}
#ifdef ENABLE_WALLET
class TestCCryptoKeyStore : public CCryptoKeyStore
{
public:
@@ -125,3 +128,4 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
ASSERT_EQ(1, addrs.count(addr));
ASSERT_EQ(1, addrs.count(addr2));
}
#endif

View File

@@ -79,6 +79,9 @@ void test_tree(
// The tree doesn't have a 'last' element added since it's blank.
ASSERT_THROW(tree.last(), std::runtime_error);
// The tree is empty.
ASSERT_TRUE(tree.size() == 0);
// We need to witness at every single point in the tree, so
// that the consistency of the tree and the merkle paths can
// be checked.
@@ -93,6 +96,9 @@ void test_tree(
// Now append a commitment to the tree
tree.append(test_commitment);
// Size incremented by one.
ASSERT_TRUE(tree.size() == i+1);
// Last element added to the tree was `test_commitment`
ASSERT_TRUE(tree.last() == test_commitment);

View File

@@ -22,6 +22,67 @@ typedef libsnark::default_r1cs_ppzksnark_pp::Fqe_type curve_Fq2;
#include "version.h"
#include "utilstrencodings.h"
TEST(proofs, g2_subgroup_check)
{
// all G2 elements are order r
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * curve_G2::random_element() == curve_G2::zero());
// but that doesn't mean all elements that satisfy the curve equation are in G2...
curve_G2 p = curve_G2::one();
while (1) {
// This will construct an order r(2q-r) point with high probability
p.X = curve_Fq2::random_element();
try {
p.Y = ((p.X.squared() * p.X) + libsnark::alt_bn128_twist_coeff_b).sqrt();
break;
} catch(...) {}
}
ASSERT_TRUE(p.is_well_formed()); // it's on the curve
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p != curve_G2::zero()); // but not the order r subgroup..
{
// libsnark unfortunately doesn't check, and the pairing will complete
auto e = curve_Fr("149");
auto a = curve_pp::reduced_pairing(curve_G1::one(), p);
auto b = curve_pp::reduced_pairing(e * curve_G1::one(), p);
// though it will not preserve bilinearity
ASSERT_TRUE((a^e) != b);
}
{
// so, our decompression API should not allow you to decompress G2 elements of that form!
CompressedG2 badp(p);
try {
auto newp = badp.to_libsnark_g2<curve_G2>();
FAIL() << "Expected std::runtime_error";
} catch (std::runtime_error const & err) {
EXPECT_EQ(err.what(), std::string("point is not in G2"));
} catch(...) {
FAIL() << "Expected std::runtime_error";
}
}
// educational purposes: showing that E'(Fp2) is of order r(2q-r),
// by multiplying our random point in E' by (2q-r) = (q + q - r) to
// get an element in G2
{
auto p1 = libsnark::alt_bn128_modulus_q * p;
p1 = p1 + p1;
p1 = p1 - (libsnark::alt_bn128_modulus_r * p);
ASSERT_TRUE(p1.is_well_formed());
ASSERT_TRUE(libsnark::alt_bn128_modulus_r * p1 == curve_G2::zero());
CompressedG2 goodp(p1);
auto newp = goodp.to_libsnark_g2<curve_G2>();
ASSERT_TRUE(newp == p1);
}
}
TEST(proofs, sqrt_zero)
{
ASSERT_TRUE(curve_Fq::zero() == curve_Fq::zero().sqrt());

View File

@@ -289,6 +289,7 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
}
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt("-exportdir=<dir>", _("Specify directory to be used when exporting data"));
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
@@ -376,11 +377,12 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1));
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
}
string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, prune"; // Don't translate these and qt below
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, lock, mempool, net, partitioncheck, pow, proxy, prune, "
"rand, reindex, rpc, selectcoins, zrpc, zrpcunsafe"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT)
debugCategories += ", qt";
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + _("<category> can be:") + " " + debugCategories + ".");
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + debugCategories + ".");
#ifdef ENABLE_WALLET
strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0));
strUsage += HelpMessageOpt("-genproclimit=<n>", strprintf(_("Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)"), 1));
@@ -625,6 +627,17 @@ static void ZC_LoadParams()
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
if (!(boost::filesystem::exists(pk_path) && boost::filesystem::exists(vk_path))) {
uiInterface.ThreadSafeMessageBox(strprintf(
_("Cannot find the Zcash network parameters in the following directory:\n"
"%s\n"
"Please run 'zcash-fetch-params' or './zcutil/fetch-params.sh' and then restart."),
ZC_GetParamsDir()),
"", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
}
pzcashParams = ZCJoinSplit::Unopened();
LogPrintf("Loading verifying key from %s\n", vk_path.string().c_str());

View File

@@ -34,7 +34,5 @@ enum HelpMessageMode {
/** Help for options shared between UI and daemon (for -help) */
std::string HelpMessage(HelpMessageMode mode);
/** Returns licensing information (for -version) */
std::string LicenseInfo();
#endif // BITCOIN_INIT_H

View File

@@ -860,7 +860,8 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
return nSigOps;
}
bool CheckTransaction(const CTransaction& tx, CValidationState &state)
bool CheckTransaction(const CTransaction& tx, CValidationState &state,
libzcash::ProofVerifier& verifier)
{
// Don't count coinbase transactions because mining skews the count
if (!tx.IsCoinBase()) {
@@ -871,7 +872,6 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return false;
} else {
// Ensure that zk-SNARKs verify
auto verifier = libzcash::ProofVerifier::Strict();
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
@@ -1082,11 +1082,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
AssertLockHeld(cs_main);
if (pfMissingInputs)
*pfMissingInputs = false;
if (!CheckTransaction(tx, state))
{
fprintf(stderr,"AcceptToMemoryPool CheckTransaction failed\n");
auto verifier = libzcash::ProofVerifier::Strict();
if (!CheckTransaction(tx, state, verifier))
return error("AcceptToMemoryPool: CheckTransaction failed");
}
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
{
@@ -2142,7 +2140,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
// Check it again in case a previous version let a bad block in
if (!CheckBlock(pindex->nHeight,pindex,block, state, !fJustCheck, !fJustCheck))
bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
auto verifier = libzcash::ProofVerifier::Strict();
auto disabledVerifier = libzcash::ProofVerifier::Disabled();
// Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in
if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck))
return false;
// verify that the view's current state corresponds to the previous block
@@ -2161,9 +2164,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return true;
}
<<<<<<< HEAD
bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
//if ( KOMODO_TESTNET_EXPIRATION != 0 && pindex->nHeight > KOMODO_TESTNET_EXPIRATION ) // "testnet"
// return(false);
=======
>>>>>>> zcash/master
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
@@ -2189,7 +2195,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
CBlockUndo blockundo;
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
CCheckQueueControl<CScriptCheck> control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
int64_t nTimeStart = GetTimeMicros();
CAmount nFees = 0;
@@ -2251,7 +2257,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
nFees += view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime) - tx.GetValueOut();
sum += interest;
std::vector<CScriptCheck> vChecks;
if (!ContextualCheckInputs(tx, state, view, fScriptChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL))
if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
}
@@ -3094,7 +3100,9 @@ bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& bl
}
int32_t komodo_check_deposit(int32_t height,const CBlock& block);
bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state,
libzcash::ProofVerifier& verifier,
bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context.
@@ -3139,7 +3147,7 @@ bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidat
// Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!CheckTransaction(tx, state))
if (!CheckTransaction(tx, state, verifier))
return error("CheckBlock(): CheckTransaction failed");
unsigned int nSigOps = 0;
@@ -3310,7 +3318,9 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
if (fTooFarAhead) return true; // Block height is too high
}
if ((!CheckBlock(pindex->nHeight,pindex,block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
// See method docstring for why this is always disabled
auto verifier = libzcash::ProofVerifier::Disabled();
if ((!CheckBlock(pindex->nHeight,block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
@@ -3361,11 +3371,12 @@ bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBloc
{
// Preliminary checks
bool checked;
auto verifier = libzcash::ProofVerifier::Disabled();
if ( chainActive.Tip() != 0 )
komodo_currentheight_set(chainActive.Tip()->nHeight);
if ( ASSETCHAINS_SYMBOL[0] == 0 )
checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state);
else checked = CheckBlock(0,0,*pblock, state);
checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier);
else checked = CheckBlock(0,0,*pblock, state, verifier);
{
LOCK(cs_main);
bool fRequested = MarkBlockAsReceived(pblock->GetHash());
@@ -3400,11 +3411,13 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
// JoinSplit proofs are verified in ConnectBlock
auto verifier = libzcash::ProofVerifier::Disabled();
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
if (!CheckBlock(indexDummy.nHeight,0,block, state, fCheckPOW, fCheckMerkleRoot))
if (!CheckBlock(indexDummy.nHeight,0,block, state, verifier, fCheckPOW, fCheckMerkleRoot))
return false;
if (!ContextualCheckBlock(block, state, pindexPrev))
return false;
@@ -3725,6 +3738,8 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0;
CValidationState state;
// No need to verify JoinSplits twice
auto verifier = libzcash::ProofVerifier::Disabled();
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{
boost::this_thread::interruption_point();
@@ -3736,7 +3751,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state))
if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,block, state, verifier))
return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {

View File

@@ -339,7 +339,7 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight);
/** Context-independent validity checks */
bool CheckTransaction(const CTransaction& tx, CValidationState& state);
bool CheckTransaction(const CTransaction& tx, CValidationState& state, libzcash::ProofVerifier& verifier);
bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state);
/** Check for standard transaction types
@@ -415,8 +415,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false);
/** Context-independent validity checks */
bool CheckBlockHeader(int32_t height,CBlockIndex *pindex,const CBlockHeader& blockhdr, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
bool CheckBlockHeader(int32_t height,CBlockIndex *pindex,const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state,
libzcash::ProofVerifier& verifier,
bool fCheckPOW = true, bool fCheckMerkleRoot = true);
/** Context-dependent validity checks */
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev);
@@ -425,7 +427,13 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */
/**
* Store block on disk.
* JoinSplit proofs are never verified, because:
* - AcceptBlock doesn't perform script checks either.
* - The only caller of AcceptBlock verifies JoinSplit proofs elsewhere.
* If dbp is non-NULL, the file is known to already reside on disk
*/
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp);
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);

View File

@@ -20,6 +20,7 @@
CCriticalSection cs_metrics;
boost::synchronized_value<int64_t> nNodeStartTime;
boost::synchronized_value<int64_t> nNextRefresh;
AtomicCounter transactionsValidated;
AtomicCounter ehSolverRuns;
AtomicCounter solutionTargetChecks;
@@ -60,10 +61,20 @@ double GetLocalSolPS()
return GetLocalSolPS_INTERNAL(GetUptime());
}
void TriggerRefresh()
{
*nNextRefresh = GetTime();
// Ensure that the refresh has started before we return
MilliSleep(200);
}
static bool metrics_ThreadSafeMessageBox(const std::string& message,
const std::string& caption,
unsigned int style)
{
// The SECURE flag has no effect in the metrics UI.
style &= ~CClientUIInterface::SECURE;
std::string strCaption;
// Check for usage of predefined caption
switch (style) {
@@ -85,6 +96,9 @@ static bool metrics_ThreadSafeMessageBox(const std::string& message,
if (u->size() > 5) {
u->pop_back();
}
TriggerRefresh();
return false;
}
static void metrics_InitMessage(const std::string& message)
@@ -247,8 +261,21 @@ int printMessageBox(size_t cols)
std::cout << _("Messages:") << std::endl;
for (auto it = u->cbegin(); it != u->cend(); ++it) {
std::cout << *it << std::endl;
// Handle wrapped lines
lines += (it->size() / cols);
// Handle newlines and wrapped lines
size_t i = 0;
size_t j = 0;
while (j < it->size()) {
i = it->find('\n', j);
if (i == std::string::npos) {
i = it->size();
} else {
// Newline
lines++;
}
// Wrapped lines
lines += ((i-j) / cols);
j = i + 1;
}
}
std::cout << std::endl;
return lines;
@@ -333,8 +360,8 @@ void ThreadShowMetricsScreen()
std::cout << "----------------------------------------" << std::endl;
}
int64_t nWaitEnd = GetTime() + nRefresh;
while (GetTime() < nWaitEnd) {
*nNextRefresh = GetTime() + nRefresh;
while (GetTime() < *nNextRefresh) {
boost::this_thread::interruption_point();
MilliSleep(200);
}

View File

@@ -34,6 +34,8 @@ void TrackMinedBlock(uint256 hash);
void MarkStartTime();
double GetLocalSolPS();
void TriggerRefresh();
void ConnectMetricsScreen();
void ThreadShowMetricsScreen();

View File

@@ -806,6 +806,7 @@ Value getblockchaininfo(const Array& params, bool fHelp)
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n"
" \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n"
" \"commitments\": xxxxxx, (numeric) the current number of note commitments in the commitment tree\n"
" \"softforks\": [ (array) status of softforks in progress\n"
" {\n"
" \"id\": \"xxxx\", (string) name of softfork\n"
@@ -837,6 +838,10 @@ Value getblockchaininfo(const Array& params, bool fHelp)
obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex()));
obj.push_back(Pair("pruned", fPruneMode));
ZCIncrementalMerkleTree tree;
pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), tree);
obj.push_back(Pair("commitments", tree.size()));
const Consensus::Params& consensusParams = Params().GetConsensus();
CBlockIndex* tip = chainActive.Tip();
Array softforks;

View File

@@ -405,6 +405,7 @@ static Value BIP22ValidationResult(const CValidationState& state)
return "valid?";
}
#ifdef ENABLE_WALLET
Value getblocktemplate(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
@@ -697,6 +698,7 @@ Value getblocktemplate(const Array& params, bool fHelp)
return result;
}
#endif
class submitblock_StateCatcher : public CValidationInterface
{

View File

@@ -6,6 +6,7 @@
#include "rpcprotocol.h"
#include "clientversion.h"
#include "random.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
@@ -13,6 +14,7 @@
#include "version.h"
#include <stdint.h>
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
@@ -288,3 +290,68 @@ Object JSONRPCError(int code, const string& message)
error.push_back(Pair("message", message));
return error;
}
/** Username used when cookie authentication is in use (arbitrary, only for
* recognizability in debugging/logging purposes)
*/
static const std::string COOKIEAUTH_USER = "__cookie__";
/** Default name for auth cookie file */
static const std::string COOKIEAUTH_FILE = ".cookie";
boost::filesystem::path GetAuthCookieFile()
{
boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
if (!path.is_complete()) path = GetDataDir() / path;
return path;
}
bool GenerateAuthCookie(std::string *cookie_out)
{
unsigned char rand_pwd[32];
GetRandBytes(rand_pwd, 32);
std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
/** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
std::ofstream file;
boost::filesystem::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
return false;
}
file << cookie;
file.close();
LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
if (cookie_out)
*cookie_out = cookie;
return true;
}
bool GetAuthCookie(std::string *cookie_out)
{
std::ifstream file;
std::string cookie;
boost::filesystem::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open())
return false;
std::getline(file, cookie);
file.close();
if (cookie_out)
*cookie_out = cookie;
return true;
}
void DeleteAuthCookie()
{
try {
boost::filesystem::remove(GetAuthCookieFile());
} catch (const boost::filesystem::filesystem_error& e) {
LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
}
}

View File

@@ -14,6 +14,7 @@
#include <boost/iostreams/stream.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/filesystem.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
@@ -165,4 +166,13 @@ json_spirit::Object JSONRPCReplyObj(const json_spirit::Value& result, const json
std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id);
json_spirit::Object JSONRPCError(int code, const std::string& message);
/** Get name of RPC authentication cookie file */
boost::filesystem::path GetAuthCookieFile();
/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
#endif // BITCOIN_RPCPROTOCOL_H

View File

@@ -309,7 +309,9 @@ static const CRPCCommand vRPCCommands[] =
{ "blockchain", "kvupdate", &kvupdate, true },
/* Mining */
#ifdef ENABLE_WALLET
{ "mining", "getblocktemplate", &getblocktemplate, true },
#endif
{ "mining", "getmininginfo", &getmininginfo, true },
{ "mining", "getlocalsolps", &getlocalsolps, true },
{ "mining", "getnetworksolps", &getnetworksolps, true },
@@ -629,10 +631,9 @@ void StartRPCThreads()
strAllowed += subnet.ToString() + " ";
LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
if (((mapArgs["-rpcpassword"] == "") ||
(mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword())
if (mapArgs["-rpcpassword"] == "")
{
/*<<<<<<< HEAD
unsigned char rand_pwd[32];
GetRandBytes(rand_pwd, 32);
uiInterface.ThreadSafeMessageBox(strprintf(
@@ -651,6 +652,18 @@ void StartRPCThreads()
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE);
StartShutdown();
return;
=======*/
LogPrintf("No rpcpassword set - using random cookie authentication\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
uiInterface.ThreadSafeMessageBox(
_("Error: A fatal internal error occured, see debug.log for details"), // Same message as AbortNode
"", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
}
} else {
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
//>>>>>>> zcash/master
}
assert(rpc_io_service == NULL);
@@ -817,6 +830,8 @@ void StopRPCThreads()
}
deadlineTimers.clear();
DeleteAuthCookie();
rpc_io_service->stop();
g_rpcSignals.Stopped();
if (rpc_worker_group != NULL)

View File

@@ -7,6 +7,7 @@
#include "main.h"
#include "test/test_bitcoin.h"
#include "utiltime.h"
#include "zcash/Proof.hpp"
#include <cstdio>
@@ -56,7 +57,8 @@ BOOST_AUTO_TEST_CASE(May15)
// After May 15'th, big blocks are OK:
forkingBlock.nTime = tMay15; // Invalidates PoW
BOOST_CHECK(CheckBlock(0,0,forkingBlock, state, false, false));
auto verifier = libzcash::ProofVerifier::Strict();
BOOST_CHECK(CheckBlock(forkingBlock, state, verifier, false, false));
}
SetMockTime(0);

View File

@@ -358,16 +358,26 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet)
pwalletMain->GetPaymentAddresses(addrs);
BOOST_CHECK(addrs.size()==1);
// Set up paths
boost::filesystem::path tmppath = boost::filesystem::temp_directory_path();
boost::filesystem::path tmpfilename = boost::filesystem::unique_path("%%%%%%%%");
boost::filesystem::path exportfilepath = tmppath / tmpfilename;
// export will fail since exportdir is not set
BOOST_CHECK_THROW(CallRPC(string("z_exportwallet ") + tmpfilename.string()), runtime_error);
// set exportdir
mapArgs["-exportdir"] = tmppath.native();
// run some tests
BOOST_CHECK_THROW(CallRPC("z_exportwallet"), runtime_error);
BOOST_CHECK_THROW(CallRPC("z_exportwallet toomany args"), runtime_error);
boost::filesystem::path temp = boost::filesystem::temp_directory_path() /
boost::filesystem::unique_path();
const std::string path = temp.native();
BOOST_CHECK_THROW(CallRPC(string("z_exportwallet invalid!*/_chars.txt")), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(string("z_exportwallet ") + tmpfilename.string()));
BOOST_CHECK_NO_THROW(CallRPC(string("z_exportwallet ") + path));
auto addr = paymentAddress.Get();
libzcash::SpendingKey key;
@@ -382,7 +392,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet)
EnsureWalletIsUnlocked();
ifstream file;
file.open(path.c_str(), std::ios::in | std::ios::ate);
file.open(exportfilepath.string().c_str(), std::ios::in | std::ios::ate);
BOOST_CHECK(file.is_open());
bool fVerified = false;
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());

View File

@@ -29,6 +29,7 @@
#include "zcash/Note.hpp"
#include "zcash/Address.hpp"
#include "zcash/Proof.hpp"
using namespace std;
using namespace json_spirit;
@@ -97,6 +98,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
auto verifier = libzcash::ProofVerifier::Strict();
ScriptError err;
BOOST_FOREACH(Value& tv, tests)
{
@@ -141,7 +143,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
stream >> tx;
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier), strTest);
BOOST_CHECK(state.IsValid());
for (unsigned int i = 0; i < tx.vin.size(); i++)
@@ -173,6 +175,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
auto verifier = libzcash::ProofVerifier::Strict();
ScriptError err;
BOOST_FOREACH(Value& tv, tests)
{
@@ -217,7 +220,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
stream >> tx;
CValidationState state;
fValid = CheckTransaction(tx, state) && state.IsValid();
fValid = CheckTransaction(tx, state, verifier) && state.IsValid();
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
{
@@ -246,11 +249,12 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
CMutableTransaction tx;
stream >> tx;
CValidationState state;
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state) && state.IsValid(), "Simple deserialized transaction should be valid.");
auto verifier = libzcash::ProofVerifier::Strict();
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier) && state.IsValid(), "Simple deserialized transaction should be valid.");
// Check that duplicate txins fail
tx.vin.push_back(tx.vin[0]);
BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state, verifier) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
}
//
@@ -373,6 +377,7 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
{
auto verifier = libzcash::ProofVerifier::Strict();
CMutableTransaction tx;
tx.nVersion = 2;
{
@@ -424,23 +429,23 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
JSDescription *jsdesc = &newTx.vjoinsplit[0];
jsdesc->vpub_old = -1;
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-negative");
jsdesc->vpub_old = MAX_MONEY + 1;
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-toolarge");
jsdesc->vpub_old = 0;
jsdesc->vpub_new = -1;
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-negative");
jsdesc->vpub_new = MAX_MONEY + 1;
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-toolarge");
jsdesc->vpub_new = (MAX_MONEY / 2) + 10;
@@ -450,7 +455,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
JSDescription *jsdesc2 = &newTx.vjoinsplit[1];
jsdesc2->vpub_new = (MAX_MONEY / 2) + 10;
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-txns-txintotal-toolarge");
}
{
@@ -464,7 +469,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
jsdesc->nullifiers[0] = GetRandHash();
jsdesc->nullifiers[1] = jsdesc->nullifiers[0];
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-joinsplits-nullifiers-duplicate");
jsdesc->nullifiers[1] = GetRandHash();
@@ -475,7 +480,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
jsdesc2->nullifiers[0] = GetRandHash();
jsdesc2->nullifiers[1] = jsdesc->nullifiers[0];
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-joinsplits-nullifiers-duplicate");
}
{
@@ -494,7 +499,7 @@ BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
CTransaction finalNewTx(newTx);
BOOST_CHECK(finalNewTx.IsCoinBase());
}
BOOST_CHECK(!CheckTransaction(newTx, state));
BOOST_CHECK(!CheckTransaction(newTx, state, verifier));
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-joinsplits");
}
}

View File

@@ -501,6 +501,26 @@ const boost::filesystem::path &ZC_GetParamsDir()
return path;
}
// Return the user specified export directory. Create directory if it doesn't exist.
// If user did not set option, return an empty path.
// If there is a filesystem problem, throw an exception.
const boost::filesystem::path GetExportDir()
{
namespace fs = boost::filesystem;
fs::path path;
if (mapArgs.count("-exportdir")) {
path = fs::system_complete(mapArgs["-exportdir"]);
if (fs::exists(path) && !fs::is_directory(path)) {
throw std::runtime_error(strprintf("The -exportdir '%s' already exists and is not a directory", path.string()));
}
if (!fs::exists(path) && !fs::create_directories(path)) {
throw std::runtime_error(strprintf("Failed to create directory at -exportdir '%s'", path.string()));
}
}
return path;
}
const boost::filesystem::path &GetDataDir(bool fNetSpecific)
{
namespace fs = boost::filesystem;
@@ -556,7 +576,7 @@ void ReadConfigFile(map<string, string>& mapSettingsRet,
{
boost::filesystem::ifstream streamConfig(GetConfigFile());
if (!streamConfig.good())
return; // No komodo.conf file is OK
throw missing_zcash_conf();
set<string> setOptions;
setOptions.insert("*");
@@ -837,3 +857,16 @@ void SetThreadPriority(int nPriority)
#endif // PRIO_THREAD
#endif // WIN32
}
std::string LicenseInfo()
{
return FormatParagraph(strprintf(_("Copyright (C) 2009-%i The Bitcoin Core Developers"), COPYRIGHT_YEAR)) + "\n" +
FormatParagraph(strprintf(_("Copyright (C) 2015-%i The Zcash Developers"), COPYRIGHT_YEAR)) + "\n" +
"\n" +
FormatParagraph(_("This is experimental software.")) + "\n" +
"\n" +
FormatParagraph(_("Distributed under the MIT software license, see the accompanying file COPYING or <http://www.opensource.org/licenses/mit-license.php>.")) + "\n" +
"\n" +
FormatParagraph(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.")) +
"\n";
}

View File

@@ -127,6 +127,10 @@ boost::filesystem::path GetConfigFile();
boost::filesystem::path GetPidFile();
void CreatePidFile(const boost::filesystem::path &path, pid_t pid);
#endif
class missing_zcash_conf : public std::runtime_error {
public:
missing_zcash_conf() : std::runtime_error("Missing komodo.conf") { }
};
void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet, std::map<std::string, std::vector<std::string> >& mapMultiSettingsRet);
#ifdef WIN32
boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
@@ -134,6 +138,10 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
boost::filesystem::path GetTempPath();
void ShrinkDebugFile();
void runCommand(std::string strCommand);
const boost::filesystem::path GetExportDir();
/** Returns licensing information (for -version) */
std::string LicenseInfo();
inline bool IsSwitchChar(char c)
{

View File

@@ -30,6 +30,22 @@ string SanitizeString(const string& str)
return strResult;
}
string SanitizeFilename(const string& str)
{
/**
* safeChars chosen to restrict filename, keeping it simple to avoid cross-platform issues.
* http://stackoverflow.com/a/2306003
*/
static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890");
string strResult;
for (std::string::size_type i = 0; i < str.size(); i++)
{
if (safeChars.find(str[i]) != std::string::npos)
strResult.push_back(str[i]);
}
return strResult;
}
const signed char p_util_hexdigit[256] =
{ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,

View File

@@ -22,6 +22,7 @@
/** This is needed because the foreach macro can't get over the comma in pair<t1, t2> */
#define PAIRTYPE(t1, t2) std::pair<t1, t2>
std::string SanitizeFilename(const std::string& str);
std::string SanitizeString(const std::string& str);
std::vector<unsigned char> ParseHex(const char* psz);
std::vector<unsigned char> ParseHex(const std::string& str);

View File

@@ -53,8 +53,9 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
std::vector<SendManyRecipient> tOutputs,
std::vector<SendManyRecipient> zOutputs,
int minDepth,
CAmount fee) :
fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee)
CAmount fee,
Value contextInfo) :
fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee), contextinfo_(contextInfo)
{
assert(fee_ > 0);
@@ -88,7 +89,7 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
isfromzaddr_ = true;
frompaymentaddress_ = addr;
spendingkey_ = key;
} catch (std::runtime_error e) {
} catch (const std::runtime_error& e) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("runtime error: ") + e.what());
}
}
@@ -108,17 +109,20 @@ void AsyncRPCOperation_sendmany::main() {
try {
success = main_impl();
} catch (Object objError) {
} catch (const Object& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (runtime_error e) {
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (logic_error e) {
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
@@ -450,13 +454,22 @@ bool AsyncRPCOperation_sendmany::main_impl() {
info.notes.push_back(note);
outPoints.push_back(outPoint);
LogPrint("zrpc", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s)\n",
int wtxHeight = -1;
int wtxDepth = -1;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
const CWalletTx& wtx = pwalletMain->mapWallet[outPoint.hash];
wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight;
wtxDepth = wtx.GetDepthInMainChain();
}
LogPrint("zrpc", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",
getId().substr(0, 10),
outPoint.hash.ToString().substr(0, 10),
outPoint.js,
int(outPoint.n), // uint8_t
FormatMoney(noteFunds, false)
FormatMoney(noteFunds, false),
wtxHeight,
wtxDepth
);
@@ -579,7 +592,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
FormatMoney(plaintext.value, false)
);
} catch (const std::exception e) {
} catch (const std::exception& e) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what()));
}
}
@@ -613,12 +626,22 @@ bool AsyncRPCOperation_sendmany::main_impl() {
jsInputValue += noteFunds;
LogPrint("zrpc", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s)\n",
int wtxHeight = -1;
int wtxDepth = -1;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash];
wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight;
wtxDepth = wtx.GetDepthInMainChain();
}
LogPrint("zrpc", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",
getId().substr(0, 10),
jso.hash.ToString().substr(0, 10),
jso.js,
int(jso.n), // uint8_t
FormatMoney(noteFunds, false)
FormatMoney(noteFunds, false),
wtxHeight,
wtxDepth
);
}
@@ -778,6 +801,12 @@ void AsyncRPCOperation_sendmany::sign_send_raw_transaction(Object obj)
o.push_back(Pair("hex", signedtxn));
set_result(Value(o));
}
// Keep the signed transaction so we can hash to the same txid
CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
stream >> tx;
tx_ = tx;
}
@@ -1096,4 +1125,18 @@ boost::array<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_sendmany::get_memo_f
return memo;
}
/**
* Override getStatus() to append the operation's input parameters to the default status object.
*/
Value AsyncRPCOperation_sendmany::getStatus() const {
Value v = AsyncRPCOperation::getStatus();
if (contextinfo_.is_null()) {
return v;
}
Object obj = v.get_obj();
obj.push_back(Pair("method", "z_sendmany"));
obj.push_back(Pair("params", contextinfo_ ));
return Value(obj);
}

View File

@@ -50,7 +50,7 @@ struct WitnessAnchorData {
class AsyncRPCOperation_sendmany : public AsyncRPCOperation {
public:
AsyncRPCOperation_sendmany(std::string fromAddress, std::vector<SendManyRecipient> tOutputs, std::vector<SendManyRecipient> zOutputs, int minDepth, CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE);
AsyncRPCOperation_sendmany(std::string fromAddress, std::vector<SendManyRecipient> tOutputs, std::vector<SendManyRecipient> zOutputs, int minDepth, CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE, Value contextInfo = Value::null);
virtual ~AsyncRPCOperation_sendmany();
// We don't want to be copied or moved around
@@ -61,11 +61,15 @@ public:
virtual void main();
virtual Value getStatus() const;
bool testmode = false; // Set to true to disable sending txs and generating proofs
private:
friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing
Value contextinfo_; // optional data to include in return value from getStatus()
CAmount fee_;
int mindepth_;
std::string fromaddress_;

View File

@@ -51,7 +51,7 @@ public:
void IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblock,
ZCIncrementalMerkleTree tree) {
ZCIncrementalMerkleTree& tree) {
CWallet::IncrementNoteWitnesses(pindex, pblock, tree);
}
void DecrementNoteWitnesses(const CBlockIndex* pindex) {
@@ -82,6 +82,28 @@ CWalletTx GetValidSpend(const libzcash::SpendingKey& sk,
return GetValidSpend(*params, sk, note, value);
}
JSOutPoint CreateValidBlock(TestWallet& wallet,
const libzcash::SpendingKey& sk,
const CBlockIndex& index,
CBlock& block,
ZCIncrementalMerkleTree& tree) {
auto wtx = GetValidReceive(sk, 50, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
block.vtx.push_back(wtx);
wallet.IncrementNoteWitnesses(&index, &block, tree);
return jsoutpt;
}
TEST(wallet_tests, setup_datadir_location_run_as_first_test) {
// Get temporary and unique path for file.
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
@@ -572,27 +594,14 @@ TEST(wallet_tests, cached_witnesses_chain_tip) {
wallet.AddSpendingKey(sk);
{
// First transaction (case tested in _empty_chain)
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
// First block (case tested in _empty_chain)
block1.vtx.push_back(wtx);
CBlockIndex index1(block1);
index1.nHeight = 1;
wallet.IncrementNoteWitnesses(&index1, &block1, tree);
auto jsoutpt = CreateValidBlock(wallet, sk, index1, block1, tree);
// Called to fetch anchor
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
wallet.GetNoteWitnesses(notes, witnesses, anchor1);
}
@@ -667,47 +676,21 @@ TEST(wallet_tests, CachedWitnessesDecrementFirst) {
wallet.AddSpendingKey(sk);
{
// First transaction (case tested in _empty_chain)
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
// First block (case tested in _empty_chain)
CBlock block1;
block1.vtx.push_back(wtx);
CBlockIndex index1(block1);
index1.nHeight = 1;
wallet.IncrementNoteWitnesses(&index1, &block1, tree);
CreateValidBlock(wallet, sk, index1, block1, tree);
}
{
// Second transaction (case tested in _chain_tip)
auto wtx = GetValidReceive(sk, 50, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
// Second block (case tested in _chain_tip)
index2.nHeight = 2;
auto jsoutpt = CreateValidBlock(wallet, sk, index2, block2, tree);
// Called to fetch anchor
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
// Second block (case tested in _chain_tip)
block2.vtx.push_back(wtx);
index2.nHeight = 2;
wallet.IncrementNoteWitnesses(&index2, &block2, tree);
// Called to fetch anchor
wallet.GetNoteWitnesses(notes, witnesses, anchor2);
}
@@ -753,116 +736,77 @@ TEST(wallet_tests, CachedWitnessesDecrementFirst) {
TEST(wallet_tests, CachedWitnessesCleanIndex) {
TestWallet wallet;
CBlock block1;
CBlock block2;
CBlock block3;
CBlockIndex index1(block1);
CBlockIndex index2(block2);
CBlockIndex index3(block3);
std::vector<CBlock> blocks;
std::vector<CBlockIndex> indices;
std::vector<JSOutPoint> notes;
std::vector<uint256> anchors;
ZCIncrementalMerkleTree tree;
ZCIncrementalMerkleTree riTree = tree;
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
auto sk = libzcash::SpendingKey::random();
wallet.AddSpendingKey(sk);
{
// First transaction (case tested in _empty_chain)
auto wtx = GetValidReceive(sk, 10, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
// Generate a chain
size_t numBlocks = WITNESS_CACHE_SIZE + 10;
blocks.resize(numBlocks);
indices.resize(numBlocks);
for (size_t i = 0; i < numBlocks; i++) {
indices[i].nHeight = i;
auto old = tree.root();
auto jsoutpt = CreateValidBlock(wallet, sk, indices[i], blocks[i], tree);
EXPECT_NE(old, tree.root());
notes.push_back(jsoutpt);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
// First block (case tested in _empty_chain)
block1.vtx.push_back(wtx);
index1.nHeight = 1;
wallet.IncrementNoteWitnesses(&index1, &block1, tree);
witnesses.clear();
uint256 anchor;
wallet.GetNoteWitnesses(notes, witnesses, anchor);
for (size_t j = 0; j <= i; j++) {
EXPECT_TRUE((bool) witnesses[j]);
}
anchors.push_back(anchor);
}
{
// Second transaction (case tested in _chain_tip)
auto wtx = GetValidReceive(sk, 50, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
// Second block (case tested in _chain_tip)
block2.vtx.push_back(wtx);
index2.nHeight = 2;
wallet.IncrementNoteWitnesses(&index2, &block2, tree);
}
{
// Third transaction
auto wtx = GetValidReceive(sk, 20, true);
auto note = GetNote(sk, wtx, 0, 1);
auto nullifier = note.nullifier(sk);
mapNoteData_t noteData;
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
CNoteData nd {sk.address(), nullifier};
noteData[jsoutpt] = nd;
wtx.SetNoteData(noteData);
wallet.AddToWallet(wtx, true, NULL);
std::vector<JSOutPoint> notes {jsoutpt};
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
uint256 anchor3;
// Third block
block3.vtx.push_back(wtx);
index3.nHeight = 3;
wallet.IncrementNoteWitnesses(&index3, &block3, tree);
wallet.GetNoteWitnesses(notes, witnesses, anchor3);
// Now pretend we are reindexing: the chain is cleared, and each block is
// used to increment witnesses again.
wallet.IncrementNoteWitnesses(&index1, &block1, tree);
uint256 anchor3a;
// Now pretend we are reindexing: the chain is cleared, and each block is
// used to increment witnesses again.
for (size_t i = 0; i < numBlocks; i++) {
ZCIncrementalMerkleTree riPrevTree {riTree};
wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), riTree);
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor3a);
EXPECT_TRUE((bool) witnesses[0]);
// Should equal third anchor because witness cache unaffected
EXPECT_EQ(anchor3, anchor3a);
uint256 anchor;
wallet.GetNoteWitnesses(notes, witnesses, anchor);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) witnesses[j]);
}
// Should equal final anchor because witness cache unaffected
EXPECT_EQ(anchors.back(), anchor);
wallet.IncrementNoteWitnesses(&index2, &block2, tree);
uint256 anchor3b;
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor3b);
EXPECT_TRUE((bool) witnesses[0]);
EXPECT_EQ(anchor3, anchor3b);
if ((i == 5) || (i == 50)) {
// Pretend a reorg happened that was recorded in the block files
{
wallet.DecrementNoteWitnesses(&(indices[i]));
witnesses.clear();
uint256 anchor;
wallet.GetNoteWitnesses(notes, witnesses, anchor);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) witnesses[j]);
}
// Should equal final anchor because witness cache unaffected
EXPECT_EQ(anchors.back(), anchor);
}
// Pretend a reorg happened that was recorded in the block files
wallet.DecrementNoteWitnesses(&index2);
uint256 anchor3c;
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor3c);
EXPECT_TRUE((bool) witnesses[0]);
EXPECT_EQ(anchor3, anchor3c);
wallet.IncrementNoteWitnesses(&index2, &block2, tree);
uint256 anchor3d;
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor3d);
EXPECT_TRUE((bool) witnesses[0]);
EXPECT_EQ(anchor3, anchor3d);
wallet.IncrementNoteWitnesses(&index3, &block3, tree);
uint256 anchor3e;
witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor3e);
EXPECT_TRUE((bool) witnesses[0]);
EXPECT_EQ(anchor3, anchor3e);
{
wallet.IncrementNoteWitnesses(&(indices[i]), &(blocks[i]), riPrevTree);
witnesses.clear();
uint256 anchor;
wallet.GetNoteWitnesses(notes, witnesses, anchor);
for (size_t j = 0; j < numBlocks; j++) {
EXPECT_TRUE((bool) witnesses[j]);
}
// Should equal final anchor because witness cache unaffected
EXPECT_EQ(anchors.back(), anchor);
}
}
}
}

View File

@@ -430,7 +430,9 @@ Value z_exportwallet(const Array& params, bool fHelp)
"z_exportwallet \"filename\"\n"
"\nExports all wallet keys, for taddr and zaddr, in a human-readable format.\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The filename\n"
"1. \"filename\" (string, required) The filename, saved in folder set by zcashd -exportdir option\n"
"\nResult:\n"
"\"path\" (string) The full path of the destination file\n"
"\nExamples:\n"
+ HelpExampleCli("z_exportwallet", "\"test\"")
+ HelpExampleRpc("z_exportwallet", "\"test\"")
@@ -449,7 +451,9 @@ Value dumpwallet(const Array& params, bool fHelp)
"dumpwallet \"filename\"\n"
"\nDumps taddr wallet keys in a human-readable format.\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The filename\n"
"1. \"filename\" (string, required) The filename, saved in folder set by zcashd -exportdir option\n"
"\nResult:\n"
"\"path\" (string) The full path of the destination file\n"
"\nExamples:\n"
+ HelpExampleCli("dumpwallet", "\"test\"")
+ HelpExampleRpc("dumpwallet", "\"test\"")
@@ -464,8 +468,24 @@ Value dumpwallet_impl(const Array& params, bool fHelp, bool fDumpZKeys)
EnsureWalletIsUnlocked();
boost::filesystem::path exportdir;
try {
exportdir = GetExportDir();
} catch (const std::runtime_error& e) {
throw JSONRPCError(RPC_INTERNAL_ERROR, e.what());
}
if (exportdir.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot export wallet until the zcashd -exportdir option has been set");
}
std::string unclean = params[0].get_str();
std::string clean = SanitizeFilename(unclean);
if (clean.compare(unclean) != 0) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Filename is invalid as only alphanumeric characters are allowed. Try '%s' instead.", clean));
}
boost::filesystem::path exportfilepath = exportdir / clean;
ofstream file;
file.open(params[0].get_str().c_str());
file.open(exportfilepath.string().c_str());
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
@@ -523,7 +543,8 @@ Value dumpwallet_impl(const Array& params, bool fHelp, bool fDumpZKeys)
file << "# End of dump\n";
file.close();
return Value::null;
return exportfilepath.string();
}
@@ -575,6 +596,9 @@ Value z_importkey(const Array& params, bool fHelp)
pwalletMain->mapZKeyMetadata[addr].nCreateTime = 1;
// whenever a key is imported, we need to scan the whole chain
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
// We want to scan for transactions and notes
if (fRescan) {
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);

View File

@@ -34,6 +34,8 @@
#include "json/json_spirit_value.h"
#include "asyncrpcqueue.h"
#include <numeric>
using namespace std;
using namespace json_spirit;
@@ -1557,6 +1559,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
entry.push_back(Pair("fee", ValueFromAmount(-nFee)));
if (fLong)
WalletTxToJSON(wtx, entry);
entry.push_back(Pair("size", static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)));
ret.push_back(entry);
}
}
@@ -1593,6 +1596,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
entry.push_back(Pair("vout", r.vout));
if (fLong)
WalletTxToJSON(wtx, entry);
entry.push_back(Pair("size", static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)));
ret.push_back(entry);
}
}
@@ -1661,6 +1665,7 @@ Value listtransactions(const Array& params, bool fHelp)
" \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n"
" from (for receiving funds, positive amounts), or went to (for sending funds,\n"
" negative amounts).\n"
" \"size\": n, (numeric) Transaction size in bytes\n"
" }\n"
"]\n"
@@ -1998,21 +2003,38 @@ Value backupwallet(const Array& params, bool fHelp)
if (fHelp || params.size() != 1)
throw runtime_error(
"backupwallet \"destination\"\n"
"\nSafely copies wallet.dat to destination, which can be a directory or a path with filename.\n"
"\nSafely copies wallet.dat to destination filename\n"
"\nArguments:\n"
"1. \"destination\" (string) The destination directory or file\n"
"1. \"destination\" (string, required) The destination filename, saved in the directory set by -exportdir option.\n"
"\nResult:\n"
"\"path\" (string) The full path of the destination file\n"
"\nExamples:\n"
+ HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
+ HelpExampleCli("backupwallet", "\"backupdata\"")
+ HelpExampleRpc("backupwallet", "\"backupdata\"")
);
LOCK2(cs_main, pwalletMain->cs_wallet);
string strDest = params[0].get_str();
if (!BackupWallet(*pwalletMain, strDest))
boost::filesystem::path exportdir;
try {
exportdir = GetExportDir();
} catch (const std::runtime_error& e) {
throw JSONRPCError(RPC_INTERNAL_ERROR, e.what());
}
if (exportdir.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot backup wallet until the -exportdir option has been set");
}
std::string unclean = params[0].get_str();
std::string clean = SanitizeFilename(unclean);
if (clean.compare(unclean) != 0) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Filename is invalid as only alphanumeric characters are allowed. Try '%s' instead.", clean));
}
boost::filesystem::path exportfilepath = exportdir / clean;
if (!BackupWallet(*pwalletMain, exportfilepath.string()))
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
return Value::null;
return exportfilepath.string();
}
@@ -2734,7 +2756,15 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp)
} else if (benchmarktype == "parameterloading") {
sample_times.push_back(benchmark_parameter_loading());
} else if (benchmarktype == "createjoinsplit") {
sample_times.push_back(benchmark_create_joinsplit());
if (params.size() < 3) {
sample_times.push_back(benchmark_create_joinsplit());
} else {
int nThreads = params[2].get_int();
std::vector<double> vals = benchmark_create_joinsplit_threaded(nThreads);
// Divide by nThreads^2 to get average seconds per JoinSplit because
// we are running one JoinSplit per thread.
sample_times.push_back(std::accumulate(vals.begin(), vals.end(), 0.0) / (nThreads*nThreads));
}
} else if (benchmarktype == "verifyjoinsplit") {
sample_times.push_back(benchmark_verify_joinsplit(samplejoinsplit));
} else if (benchmarktype == "solveequihash") {
@@ -3224,7 +3254,7 @@ Value z_listreceivedbyaddress(const Array& params, bool fHelp)
CZCPaymentAddress address(fromaddress);
try {
zaddr = address.Get();
} catch (std::runtime_error) {
} catch (const std::runtime_error&) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
}
@@ -3291,7 +3321,7 @@ Value z_getbalance(const Array& params, bool fHelp)
CZCPaymentAddress address(fromaddress);
try {
zaddr = address.Get();
} catch (std::runtime_error) {
} catch (const std::runtime_error&) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
}
if (!pwalletMain->HaveSpendingKey(zaddr)) {
@@ -3443,6 +3473,13 @@ Value z_getoperationstatus_IMPL(const Array& params, bool fRemoveFinishedOperati
}
}
// sort results chronologically by creation_time
std::sort(ret.begin(), ret.end(), [](Value a, Value b) -> bool {
const int64_t t1 = find_value(a.get_obj(), "creation_time").get_int64();
const int64_t t2 = find_value(b.get_obj(), "creation_time").get_int64();
return t1 < t2;
});
return ret;
}
@@ -3497,7 +3534,7 @@ Value z_sendmany(const Array& params, bool fHelp)
CZCPaymentAddress address(fromaddress);
try {
zaddr = address.Get();
} catch (std::runtime_error) {
} catch (const std::runtime_error&) {
// invalid
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
}
@@ -3544,7 +3581,7 @@ Value z_sendmany(const Array& params, bool fHelp)
CZCPaymentAddress zaddr(address);
zaddr.Get();
isZaddr = true;
} catch (std::runtime_error) {
} catch (const std::runtime_error&) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address );
}
}
@@ -3624,9 +3661,17 @@ Value z_sendmany(const Array& params, bool fHelp)
}
}
// Use input parameters as the optional context info to be returned by z_getoperationstatus and z_getoperationresult.
Object o;
o.push_back(Pair("fromaddress", params[0]));
o.push_back(Pair("amounts", params[1]));
o.push_back(Pair("minconf", nMinDepth));
o.push_back(Pair("fee", std::stod(FormatMoney(nFee))));
Value contextInfo = Value(o);
// Create operation and add to global queue
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee) );
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) );
q->addOperation(operation);
AsyncRPCOperationId operationId = operation->getId();
return operationId;

View File

@@ -645,10 +645,14 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) {
CNoteData* nd = &(item.second);
// Check the validity of the cache
assert(nWitnessCacheSize >= nd->witnesses.size());
// Only increment witnesses that are behind the current height
if (nd->witnessHeight < pindex->nHeight) {
// Check the validity of the cache
// The only time a note witnessed above the current height
// would be invalid here is during a reindex when blocks
// have been decremented, and we are incrementing the blocks
// immediately after.
assert(nWitnessCacheSize >= nd->witnesses.size());
// Witnesses being incremented should always be either -1
// (never incremented or decremented) or one below pindex
assert((nd->witnessHeight == -1) ||
@@ -687,10 +691,11 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) {
CNoteData* nd = &(item.second);
// Check the validity of the cache
assert(nWitnessCacheSize >= nd->witnesses.size());
if (nd->witnessHeight < pindex->nHeight &&
nd->witnesses.size() > 0) {
// Check the validity of the cache
// See earlier comment about validity.
assert(nWitnessCacheSize >= nd->witnesses.size());
nd->witnesses.front().append(note_commitment);
}
}
@@ -699,7 +704,8 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
// If this is our note, witness it
if (txIsOurs) {
JSOutPoint jsoutpt {hash, i, j};
if (mapWallet[hash].mapNoteData.count(jsoutpt)) {
if (mapWallet[hash].mapNoteData.count(jsoutpt) &&
mapWallet[hash].mapNoteData[jsoutpt].witnessHeight < pindex->nHeight) {
CNoteData* nd = &(mapWallet[hash].mapNoteData[jsoutpt]);
if (nd->witnesses.size() > 0) {
// We think this can happen because we write out the
@@ -735,9 +741,10 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
CNoteData* nd = &(item.second);
if (nd->witnessHeight < pindex->nHeight) {
nd->witnessHeight = pindex->nHeight;
// Check the validity of the cache
// See earlier comment about validity.
assert(nWitnessCacheSize >= nd->witnesses.size());
}
// Check the validity of the cache
assert(nWitnessCacheSize >= nd->witnesses.size());
}
}
@@ -755,10 +762,12 @@ void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex)
for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) {
for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) {
CNoteData* nd = &(item.second);
// Check the validity of the cache
assert(nWitnessCacheSize >= nd->witnesses.size());
// Only increment witnesses that are not above the current height
if (nd->witnessHeight <= pindex->nHeight) {
// Check the validity of the cache
// See comment below (this would be invalid if there was a
// prior decrement).
assert(nWitnessCacheSize >= nd->witnesses.size());
// Witnesses being decremented should always be either -1
// (never incremented or decremented) or equal to pindex
assert((nd->witnessHeight == -1) ||
@@ -777,7 +786,18 @@ void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex)
for (mapNoteData_t::value_type& item : wtxItem.second.mapNoteData) {
CNoteData* nd = &(item.second);
// Check the validity of the cache
assert(nWitnessCacheSize >= nd->witnesses.size());
// Technically if there are notes witnessed above the current
// height, their cache will now be invalid (relative to the new
// value of nWitnessCacheSize). However, this would only occur
// during a reindex, and by the time the reindex reaches the tip
// of the chain again, the existing witness caches will be valid
// again.
// We don't set nWitnessCacheSize to zero at the start of the
// reindex because the on-disk blocks had already resulted in a
// chain that didn't trigger the assertion below.
if (nd->witnessHeight < pindex->nHeight) {
assert(nWitnessCacheSize >= nd->witnesses.size());
}
}
}
if ( nWitnessCacheSize <= 0 )

View File

@@ -14,6 +14,7 @@
#include "util.h"
#include "utiltime.h"
#include "wallet/wallet.h"
#include "zcash/Proof.hpp"
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
@@ -411,7 +412,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletTx wtx;
ssValue >> wtx;
CValidationState state;
if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()))
auto verifier = libzcash::ProofVerifier::Strict();
if (!(CheckTransaction(wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid()))
return false;
// Undo serialize changes in 31600

View File

@@ -0,0 +1,35 @@
// 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 "../util.h"
#include "primitives/transaction.h"
#include "zcash/JoinSplit.hpp"
#include "libsnark/common/profiling.hpp"
using namespace libzcash;
int main(int argc, char **argv)
{
libsnark::start_profiling();
auto p = ZCJoinSplit::Unopened();
p->loadVerifyingKey((ZC_GetParamsDir() / "sprout-verifying.key").string());
p->setProvingKeyPath((ZC_GetParamsDir() / "sprout-proving.key").string());
p->loadProvingKey();
// construct a proof.
for (int i = 0; i < 5; i++) {
uint256 anchor = ZCIncrementalMerkleTree().root();
uint256 pubKeyHash;
JSDescription jsdesc(*p,
pubKeyHash,
anchor,
{JSInput(), JSInput()},
{JSOutput(), JSOutput()},
0,
0);
}
}

View File

@@ -81,6 +81,25 @@ Hash IncrementalMerkleTree<Depth, Hash>::last() const {
}
}
template<size_t Depth, typename Hash>
size_t IncrementalMerkleTree<Depth, Hash>::size() const {
size_t ret = 0;
if (left) {
ret++;
}
if (right) {
ret++;
}
// Treat occupation of parents array as a binary number
// (right-shifted by 1)
for (size_t i = 0; i < parents.size(); i++) {
if (parents[i]) {
ret += (1 << (i+1));
}
}
return ret;
}
template<size_t Depth, typename Hash>
void IncrementalMerkleTree<Depth, Hash>::append(Hash obj) {
if (is_complete(Depth)) {

View File

@@ -75,6 +75,8 @@ public:
parents.size() * 32; // parents
}
size_t size() const;
void append(Hash obj);
Hash root() const {
return root(Depth, std::deque<Hash>());

View File

@@ -163,6 +163,10 @@ curve_G2 CompressedG2::to_libsnark_g2() const
assert(r.is_well_formed());
if (alt_bn128_modulus_r * r != curve_G2::zero()) {
throw std::runtime_error("point is not in G2");
}
return r;
}

View File

@@ -95,6 +95,27 @@ double benchmark_create_joinsplit()
return ret;
}
std::vector<double> benchmark_create_joinsplit_threaded(int nThreads)
{
std::vector<double> ret;
std::vector<std::future<double>> tasks;
std::vector<std::thread> threads;
for (int i = 0; i < nThreads; i++) {
std::packaged_task<double(void)> task(&benchmark_create_joinsplit);
tasks.emplace_back(task.get_future());
threads.emplace_back(std::move(task));
}
std::future_status status;
for (auto it = tasks.begin(); it != tasks.end(); it++) {
it->wait();
ret.push_back(it->get());
}
for (auto it = threads.begin(); it != threads.end(); it++) {
it->join();
}
return ret;
}
double benchmark_verify_joinsplit(const JSDescription &joinsplit)
{
struct timeval tv_start;

View File

@@ -7,6 +7,7 @@
extern double benchmark_sleep();
extern double benchmark_parameter_loading();
extern double benchmark_create_joinsplit();
extern std::vector<double> benchmark_create_joinsplit_threaded(int nThreads);
extern double benchmark_solve_equihash();
extern std::vector<double> benchmark_solve_equihash_threaded(int nThreads);
extern double benchmark_verify_joinsplit(const JSDescription &joinsplit);