Initial merge

This commit is contained in:
jl777
2017-03-30 04:35:16 +03:00
633 changed files with 11230 additions and 184263 deletions

View File

@@ -1,4 +1,4 @@
DIST_SUBDIRS = secp256k1
DIST_SUBDIRS = secp256k1 univalue
AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS)
@@ -21,6 +21,7 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += -I$(srcdir)/univalue/include
LIBBITCOIN_SERVER=libbitcoin_server.a
LIBBITCOIN_WALLET=libbitcoin_wallet.a
@@ -28,21 +29,22 @@ LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CLI=libbitcoin_cli.a
LIBBITCOIN_UTIL=libbitcoin_util.a
LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
LIBBITCOIN_UNIVALUE=univalue/libbitcoin_univalue.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1/libsecp256k1.la
LIBUNIVALUE=univalue/libunivalue.la
LIBZCASH=libzcash.a -lcurl
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
$(LIBUNIVALUE): $(wildcard univalue/lib/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue/
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
# But to build the less dependent modules first, we manually select their order here:
EXTRA_LIBRARIES = \
crypto/libbitcoin_crypto.a \
libbitcoin_util.a \
libbitcoin_common.a \
univalue/libbitcoin_univalue.a \
libbitcoin_server.a \
libbitcoin_cli.a \
libzcash.a
@@ -50,6 +52,9 @@ if ENABLE_WALLET
BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
EXTRA_LIBRARIES += libbitcoin_wallet.a
endif
if ENABLE_ZMQ
EXTRA_LIBRARIES += libbitcoin_zmq.a
endif
if BUILD_BITCOIN_LIBS
lib_LTLIBRARIES = libzcashconsensus.la
@@ -110,9 +115,12 @@ BITCOIN_CORE_H = \
consensus/params.h \
consensus/validation.h \
core_io.h \
core_memusage.h \
eccryptoverify.h \
ecwrapper.h \
hash.h \
httprpc.h \
httpserver.h \
init.h \
key.h \
keystore.h \
@@ -150,11 +158,13 @@ BITCOIN_CORE_H = \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
support/cleanse.h \
support/events.h \
support/pagelocker.h \
sync.h \
threadsafety.h \
timedata.h \
tinyformat.h \
torcontrol.h \
txdb.h \
txmempool.h \
ui_interface.h \
@@ -172,18 +182,12 @@ BITCOIN_CORE_H = \
wallet/db.h \
wallet/wallet.h \
wallet/wallet_ismine.h \
wallet/walletdb.h
wallet/walletdb.h \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
zmq/zmqpublishnotifier.h
JSON_H = \
json/json_spirit.h \
json/json_spirit_error_position.h \
json/json_spirit_reader.h \
json/json_spirit_reader_template.h \
json/json_spirit_stream_reader.h \
json/json_spirit_utils.h \
json/json_spirit_value.h \
json/json_spirit_writer.h \
json/json_spirit_writer_template.h
obj/build.h: FORCE
@$(MKDIR_P) $(builddir)/obj
@@ -191,8 +195,8 @@ obj/build.h: FORCE
$(abs_top_srcdir)
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# server: shared between bitcoind and bitcoin-qt
libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS)
# server: zcashd
libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_SOURCES = \
sendalert.cpp \
addrman.cpp \
@@ -203,6 +207,8 @@ libbitcoin_server_a_SOURCES = \
bloom.cpp \
chain.cpp \
checkpoints.cpp \
httprpc.cpp \
httpserver.cpp \
init.cpp \
leveldbwrapper.cpp \
main.cpp \
@@ -222,15 +228,25 @@ libbitcoin_server_a_SOURCES = \
rpcserver.cpp \
script/sigcache.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
txmempool.cpp \
validationinterface.cpp \
$(JSON_H) \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)
# wallet: shared between bitcoind and bitcoin-qt, but only linked
# when wallet enabled
if ENABLE_ZMQ
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
libbitcoin_zmq_a_SOURCES = \
zmq/zmqabstractnotifier.cpp \
zmq/zmqnotificationinterface.cpp \
zmq/zmqpublishnotifier.cpp
endif
# wallet: zcashd, but only linked when wallet enabled
libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_wallet_a_SOURCES = \
utiltest.cpp \
@@ -248,13 +264,8 @@ libbitcoin_wallet_a_SOURCES = \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)
EQUIHASH_TROMP_SOURCES = \
pow/tromp/equi_miner.h \
pow/tromp/equi.h \
pow/tromp/osx_barrier.h
# crypto primitives library
crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES) -DEQUIHASH_TROMP_ATOMIC
crypto_libbitcoin_crypto_a_CPPFLAGS = $(BITCOIN_CONFIG_INCLUDES)
crypto_libbitcoin_crypto_a_SOURCES = \
crypto/common.h \
crypto/equihash.cpp \
@@ -271,18 +282,21 @@ crypto_libbitcoin_crypto_a_SOURCES = \
crypto/sha256.cpp \
crypto/sha256.h \
crypto/sha512.cpp \
crypto/sha512.h \
crypto/sha512.h
if ENABLE_MINING
EQUIHASH_TROMP_SOURCES = \
pow/tromp/equi_miner.h \
pow/tromp/equi.h \
pow/tromp/osx_barrier.h
crypto_libbitcoin_crypto_a_CPPFLAGS += \
-DEQUIHASH_TROMP_ATOMIC
crypto_libbitcoin_crypto_a_SOURCES += \
${EQUIHASH_TROMP_SOURCES}
endif
# univalue JSON library
univalue_libbitcoin_univalue_a_SOURCES = \
univalue/univalue.cpp \
univalue/univalue.h \
univalue/univalue_escapes.h \
univalue/univalue_read.cpp \
univalue/univalue_write.cpp
# common: shared between bitcoind, and bitcoin-qt and non-server tools
# common: shared between zcashd and non-server tools
libbitcoin_common_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_common_a_SOURCES = \
amount.cpp \
@@ -339,7 +353,7 @@ if GLIBC_BACK_COMPAT
libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
endif
# cli: shared between bitcoin-cli and bitcoin-qt
# cli: zcash-cli
libbitcoin_cli_a_CPPFLAGS = $(BITCOIN_INCLUDES)
libbitcoin_cli_a_SOURCES = \
rpcclient.cpp \
@@ -361,7 +375,7 @@ endif
komodod_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UNIVALUE) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH) \
@@ -369,6 +383,10 @@ komodod_LDADD = \
$(LIBMEMENV) \
$(LIBSECP256K1)
if ENABLE_ZMQ
zcashd_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_WALLET
komodod_LDADD += libbitcoin_wallet.a
endif
@@ -379,14 +397,15 @@ komodod_LDADD += \
$(SSL_LIBS) \
$(CRYPTO_LIBS) \
$(MINIUPNPC_LIBS) \
$(EVENT_PTHREADS_LIBS) \
$(EVENT_LIBS) \
$(LIBZCASH) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH_LIBS)
#
# bitcoin-cli binary #
komodo_cli_SOURCES = bitcoin-cli.cpp
komodo_cli_CPPFLAGS = $(BITCOIN_INCLUDES)
komodo_cli_CPPFLAGS = $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
komodo_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS
@@ -395,10 +414,12 @@ endif
komodo_cli_LDADD = \
$(LIBBITCOIN_CLI) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
$(BOOST_LIBS) \
$(SSL_LIBS) \
$(CRYPTO_LIBS) \
$(EVENT_LIBS) \
$(LIBZCASH) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH_LIBS)
@@ -415,7 +436,8 @@ endif
# FIXME: Is libzcash needed for zcash_tx?
komodo_tx_LDADD = \
$(LIBBITCOIN_UNIVALUE) \
#$(LIBBITCOIN_UNIVALUE) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
$(LIBSECP256K1) \
@@ -501,7 +523,7 @@ clean-local:
.mm.o:
$(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(CXXFLAGS) -c -o $@ $<
$(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o $@ $<
check-symbols: $(bin_PROGRAMS)
if GLIBC_BACK_COMPAT
@@ -524,12 +546,4 @@ endif
#include Makefile.gtest.include
#endif
if ENABLE_QT
include Makefile.qt.include
endif
if ENABLE_QT_TESTS
include Makefile.qttest.include
endif
include Makefile.zcash.include

View File

@@ -18,7 +18,6 @@ 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 \
gtest/test_joinsplit.cpp \
@@ -26,6 +25,7 @@ zcash_gtest_SOURCES += \
gtest/test_noteencryption.cpp \
gtest/test_merkletree.cpp \
gtest/test_metrics.cpp \
gtest/test_miner.cpp \
gtest/test_pow.cpp \
gtest/test_random.cpp \
gtest/test_rpc.cpp \
@@ -44,11 +44,14 @@ komodo_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -
komodo_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
if ENABLE_ZMQ
zcash_gtest_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_WALLET
komodo_gtest_LDADD += $(LIBBITCOIN_WALLET)
endif
komodo_gtest_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS)
komodo_gtest_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS)
komodo_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static

View File

@@ -69,6 +69,7 @@ BITCOIN_TESTS =\
test/pmt_tests.cpp \
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/raii_event_tests.cpp \
test/reverselock_tests.cpp \
test/rpc_tests.cpp \
test/sanity_tests.cpp \
@@ -83,6 +84,7 @@ BITCOIN_TESTS =\
test/test_bitcoin.cpp \
test/test_bitcoin.h \
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
test/uint256_tests.cpp \
test/univalue_tests.cpp \
@@ -97,9 +99,9 @@ BITCOIN_TESTS += \
endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS)
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
test_test_bitcoin_CPPFLAGS = -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
@@ -107,6 +109,10 @@ endif
test_test_bitcoin_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(ZMQ_LIBS)
endif
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
@@ -127,6 +133,7 @@ check-local:
@echo "Running test/bitcoin-util-test.py..."
$(AM_V_at)srcdir=$(srcdir) PYTHONPATH=$(builddir)/test $(srcdir)/test/bitcoin-util-test.py
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
%.json.h: %.json
@$(MKDIR_P) $(@D)

View File

@@ -15,8 +15,8 @@ zcash_GenerateParams_LDADD = \
zcash_CreateJoinSplit_SOURCES = zcash/CreateJoinSplit.cpp
zcash_CreateJoinSplit_LDADD = \
$(LIBBITCOIN_COMMON) \
$(LIBZCASH) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBZCASH) \
$(BOOST_LIBS) \
$(LIBZCASH_LIBS)

View File

@@ -470,7 +470,7 @@ public:
}
//! Return the number of (unique) addresses in all tables.
int size()
size_t size() const
{
return vRandom.size();
}

View File

@@ -50,7 +50,7 @@ std::string CUnsignedAlert::ToString() const
BOOST_FOREACH(int n, setCancel)
strSetCancel += strprintf("%d ", n);
std::string strSetSubVer;
BOOST_FOREACH(std::string str, setSubVer)
BOOST_FOREACH(const std::string& str, setSubVer)
strSetSubVer += "\"" + str + "\" ";
return strprintf(
"CAlert(\n"
@@ -112,7 +112,7 @@ bool CAlert::Cancels(const CAlert& alert) const
return (alert.nID <= nCancel || setCancel.count(alert.nID));
}
bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const
bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const
{
// TODO: rework for client-version-embedded-in-strSubVer ?
return (IsInEffect() &&

View File

@@ -97,7 +97,7 @@ public:
uint256 GetHash() const;
bool IsInEffect() const;
bool Cancels(const CAlert& alert) const;
bool AppliesTo(int nVersion, std::string strSubVerIn) const;
bool AppliesTo(int nVersion, const std::string& strSubVerIn) const;
bool AppliesToMe() const;
bool RelayTo(CNode* pnode) const;
bool CheckSignature(const std::vector<unsigned char>& alertKey) const;

View File

@@ -16,8 +16,16 @@ typedef int64_t CAmount;
static const CAmount COIN = 100000000;
static const CAmount CENT = 1000000;
/** No amount larger than this (in satoshi) is valid */
extern CAmount MAX_MONEY;
/** No amount larger than this (in satoshi) is valid.
*
* Note that this constant is *not* the total money supply, which in Bitcoin
* currently happens to be less than 21,000,000 BTC for various reasons, but
* rather a sanity check. As this sanity check is used by consensus-critical
* validation code, the exact value of the MAX_MONEY constant is consensus
* critical; in unusual circumstances like a(nother) overflow bug that allowed
* for the creation of coins out of thin air modification could lead to a fork.
* */
static const CAmount MAX_MONEY = 21000000 * COIN;
inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
/** Type-safe wrapper class to for fee rates

View File

@@ -13,7 +13,6 @@
#include <chrono>
using namespace std;
using namespace json_spirit;
static boost::uuids::random_generator uuidgen;
@@ -109,34 +108,34 @@ void AsyncRPCOperation::main() {
*/
// Otherwise, if the operation was a success:
Value v("We have a result!");
UniValue v(UniValue::VSTR, "We have a result!");
set_result(v);
set_state(OperationStatus::SUCCESS);
}
/**
* Return the error of the completed operation as a Value object.
* If there is no error, return null Value.
* Return the error of the completed operation as a UniValue object.
* If there is no error, return null UniValue.
*/
Value AsyncRPCOperation::getError() const {
UniValue AsyncRPCOperation::getError() const {
if (!isFailed()) {
return Value::null;
return NullUniValue;
}
std::lock_guard<std::mutex> guard(lock_);
Object error;
UniValue error(UniValue::VOBJ);
error.push_back(Pair("code", this->error_code_));
error.push_back(Pair("message", this->error_message_));
return Value(error);
return error;
}
/**
* Return the result of the completed operation as a Value object.
* If the operation did not succeed, return null Value.
* Return the result of the completed operation as a UniValue object.
* If the operation did not succeed, return null UniValue.
*/
Value AsyncRPCOperation::getResult() const {
UniValue AsyncRPCOperation::getResult() const {
if (!isSuccess()) {
return Value::null;
return NullUniValue;
}
std::lock_guard<std::mutex> guard(lock_);
@@ -145,24 +144,24 @@ Value AsyncRPCOperation::getResult() const {
/**
* Returns a status Value object.
* Returns a status UniValue object.
* If the operation has failed, it will include an error object.
* If the operation has succeeded, it will include the result value.
* If the operation was cancelled, there will be no error object or result value.
*/
Value AsyncRPCOperation::getStatus() const {
UniValue AsyncRPCOperation::getStatus() const {
OperationStatus status = this->getState();
Object obj;
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("id", this->id_));
obj.push_back(Pair("status", OperationStatusMap[status]));
obj.push_back(Pair("creation_time", this->creation_time_));
// TODO: Issue #1354: There may be other useful metadata to return to the user.
Value err = this->getError();
if (!err.is_null()) {
UniValue err = this->getError();
if (!err.isNull()) {
obj.push_back(Pair("error", err.get_obj()));
}
Value result = this->getResult();
if (!result.is_null()) {
UniValue result = this->getResult();
if (!result.isNull()) {
obj.push_back(Pair("result", result));
// Include execution time for successful operation
@@ -170,7 +169,7 @@ Value AsyncRPCOperation::getStatus() const {
obj.push_back(Pair("execution_secs", elapsed_seconds.count()));
}
return Value(obj);
return obj;
}
/**

View File

@@ -15,13 +15,9 @@
#include <utility>
#include <future>
#include "json/json_spirit_value.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_writer_template.h"
#include <univalue.h>
using namespace std;
using namespace json_spirit;
/**
* AsyncRPCOperation objects are submitted to the AsyncRPCQueue for processing.
@@ -67,11 +63,11 @@ public:
}
// Override this method to add data to the default status object.
virtual Value getStatus() const;
virtual UniValue getStatus() const;
Value getError() const;
UniValue getError() const;
Value getResult() const;
UniValue getResult() const;
std::string getStateAsString() const;
@@ -114,7 +110,7 @@ protected:
// internal state. Currently, all operations are executed in a single-thread
// by a single worker.
mutable std::mutex lock_; // lock on this when read/writing non-atomics
Value result_;
UniValue result_;
int error_code_;
std::string error_message_;
std::atomic<OperationStatus> state_;
@@ -137,7 +133,7 @@ protected:
this->error_message_ = errorMessage;
}
void set_result(Value v) {
void set_result(UniValue v) {
std::lock_guard<std::mutex> guard(lock_);
this->result_ = v;
}

View File

@@ -171,7 +171,10 @@ public:
K GetKey() {
K ret;
ret.Decode(&vchData[0], &vchData[Size]);
if (vchData.size() == Size) {
//if base58 encouded data not holds a ext key, return a !IsValid() key
ret.Decode(&vchData[0]);
}
return ret;
}
@@ -179,6 +182,10 @@ public:
SetKey(key);
}
CBitcoinExtKeyBase(const std::string& strBase58c) {
SetString(strBase58c.c_str(), Params().Base58Prefix(Type).size());
}
CBitcoinExtKeyBase() {}
};

View File

@@ -11,12 +11,21 @@
#include "utilstrencodings.h"
#include <boost/filesystem/operations.hpp>
#include <stdio.h>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
#include "support/events.h"
#include <univalue.h>
using namespace std;
using namespace json_spirit;
int64_t MAX_MONEY = 200000000 * 100000000LL;
uint64_t komodo_maxallowed(int32_t baseid) { return(100000000LL * 1000000); } // stub
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
std::string HelpMessageCli()
{
string strUsage;
@@ -32,9 +41,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
strUsage += HelpMessageGroup(_("SSL options: (see the Bitcoin Wiki for SSL setup instructions)"));
strUsage += HelpMessageOpt("-rpcssl", _("Use OpenSSL (https) for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
return strUsage;
}
@@ -81,7 +88,7 @@ static bool AppInitRPC(int argc, char* argv[])
ParseParameters(argc, argv);
komodo_args();
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {
std::string strUsage = _("Komodo RPC client version") + " " + FormatFullVersion() + "\n";
std::string strUsage = _("Komodo RPC client version") + " " + FormatFullVersion() + "\n" + PrivacyInfo();
if (!mapArgs.count("-version")) {
strUsage += "\n" + _("Usage:") + "\n" +
" komodo-cli [options] <command> [params] " + _("Send command to Komodo") + "\n" +
@@ -111,32 +118,108 @@ static bool AppInitRPC(int argc, char* argv[])
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
return false;
}
if (GetBoolArg("-rpcssl", false))
{
fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
return false;
}
return true;
}
Object CallRPC(const string& strMethod, const Array& params)
/** Reply structure for request_done to fill in */
struct HTTPReply
{
// Connect to localhost
bool fUseSSL = GetBoolArg("-rpcssl", false);
boost::asio::io_service io_service;
boost::asio::ssl::context context(io_service, boost::asio::ssl::context::sslv23);
context.set_options(boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sslStream(io_service, context);
SSLIOStreamDevice<boost::asio::ip::tcp> d(sslStream, fUseSSL);
boost::iostreams::stream< SSLIOStreamDevice<boost::asio::ip::tcp> > stream(d);
HTTPReply(): status(0), error(-1) {}
const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
if (!fConnected)
throw CConnectionFailed("couldn't connect to server");
int status;
int error;
std::string body;
};
// Find credentials to use
const char *http_errorstring(int code)
{
switch(code) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
case EVREQ_HTTP_TIMEOUT:
return "timeout reached";
case EVREQ_HTTP_EOF:
return "EOF reached";
case EVREQ_HTTP_INVALID_HEADER:
return "error while reading header, or invalid header";
case EVREQ_HTTP_BUFFER_ERROR:
return "error encountered while reading or writing";
case EVREQ_HTTP_REQUEST_CANCEL:
return "request was canceled";
case EVREQ_HTTP_DATA_TOO_LONG:
return "response body is larger than allowed";
#endif
default:
return "unknown";
}
}
static void http_request_done(struct evhttp_request *req, void *ctx)
{
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
if (req == NULL) {
/* If req is NULL, it means an error occurred while connecting: the
* error code will have been passed to http_error_cb.
*/
reply->status = 0;
return;
}
reply->status = evhttp_request_get_response_code(req);
struct evbuffer *buf = evhttp_request_get_input_buffer(req);
if (buf)
{
size_t size = evbuffer_get_length(buf);
const char *data = (const char*)evbuffer_pullup(buf, size);
if (data)
reply->body = std::string(data, size);
evbuffer_drain(buf, size);
}
}
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
static void http_error_cb(enum evhttp_request_error err, void *ctx)
{
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
reply->error = err;
}
#endif
UniValue CallRPC(const string& strMethod, const UniValue& params)
{
std::string host = GetArg("-rpcconnect", "127.0.0.1");
int port = GetArg("-rpcport", BaseParams().RPCPort());
// Obtain event base
raii_event_base base = obtain_event_base();
// Synchronously look up hostname
raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
HTTPReply response;
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
if (req == NULL)
throw runtime_error("create http request failed");
#if LIBEVENT_VERSION_NUMBER >= 0x02010300
evhttp_request_set_error_cb(req.get(), http_error_cb);
#endif
// Get credentials
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."),
_("Could not locate RPC credentials. No authentication cookie could be found,\n"
"and no rpcpassword is set in the configuration file (%s)."),
GetConfigFile().string().c_str()));
}
@@ -144,36 +227,40 @@ Object CallRPC(const string& strMethod, const Array& params)
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
}
// HTTP basic authentication
map<string, string> mapRequestHeaders;
mapRequestHeaders["Authorization"] = string("Basic ") + EncodeBase64(strRPCUserColonPass);
struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
assert(output_headers);
evhttp_add_header(output_headers, "Host", host.c_str());
evhttp_add_header(output_headers, "Connection", "close");
evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
// Send request
string strRequest = JSONRPCRequest(strMethod, params, 1);
string strPost = HTTPPost(strRequest, mapRequestHeaders);
stream << strPost << std::flush;
// Attach request data
std::string strRequest = JSONRPCRequest(strMethod, params, 1);
struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
assert(output_buffer);
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
// Receive HTTP reply status
int nProto = 0;
int nStatus = ReadHTTPStatus(stream, nProto);
int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
req.release(); // ownership moved to evcon in above call
if (r != 0) {
throw CConnectionFailed("send http request failed");
}
// Receive HTTP reply message headers and body
map<string, string> mapHeaders;
string strReply;
ReadHTTPMessage(stream, mapHeaders, strReply, nProto, std::numeric_limits<size_t>::max());
event_base_dispatch(base.get());
if (nStatus == HTTP_UNAUTHORIZED)
if (response.status == 0)
throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
else if (response.status == HTTP_UNAUTHORIZED)
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
else if (strReply.empty())
else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
throw runtime_error(strprintf("server returned HTTP error %d", response.status));
else if (response.body.empty())
throw runtime_error("no response from server");
// Parse reply
Value valReply;
if (!read_string(strReply, valReply))
UniValue valReply(UniValue::VSTR);
if (!valReply.read(response.body))
throw runtime_error("couldn't parse reply from server");
const Object& reply = valReply.get_obj();
const UniValue& reply = valReply.get_obj();
if (reply.empty())
throw runtime_error("expected reply to have result, error and id properties");
@@ -198,35 +285,43 @@ int CommandLineRPC(int argc, char *argv[])
// Parameters default to strings
std::vector<std::string> strParams(&argv[2], &argv[argc]);
Array params = RPCConvertValues(strMethod, strParams);
UniValue params = RPCConvertValues(strMethod, strParams);
// Execute and handle connection failures with -rpcwait
const bool fWait = GetBoolArg("-rpcwait", false);
do {
try {
const Object reply = CallRPC(strMethod, params);
const UniValue reply = CallRPC(strMethod, params);
// Parse reply
const Value& result = find_value(reply, "result");
const Value& error = find_value(reply, "error");
const UniValue& result = find_value(reply, "result");
const UniValue& error = find_value(reply, "error");
if (error.type() != null_type) {
if (!error.isNull()) {
// Error
const int code = find_value(error.get_obj(), "code").get_int();
int code = error["code"].get_int();
if (fWait && code == RPC_IN_WARMUP)
throw CConnectionFailed("server in warmup");
strPrint = "error: " + write_string(error, false);
strPrint = "error: " + error.write();
nRet = abs(code);
if (error.isObject())
{
UniValue errCode = find_value(error, "code");
UniValue errMsg = find_value(error, "message");
strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
if (errMsg.isStr())
strPrint += "error message:\n"+errMsg.get_str();
}
} else {
// Result
if (result.type() == null_type)
if (result.isNull())
strPrint = "";
else if (result.type() == str_type)
else if (result.isStr())
strPrint = result.get_str();
else
strPrint = write_string(result, true);
strPrint = result.write(2);
}
// Connection succeeded, no need to retry.
break;
}
@@ -259,6 +354,10 @@ int CommandLineRPC(int argc, char *argv[])
int main(int argc, char* argv[])
{
SetupEnvironment();
if (!SetupNetworking()) {
fprintf(stderr, "Error: Initializing networking failed\n");
exit(1);
}
try {
if(!AppInitRPC(argc, argv))

View File

@@ -10,11 +10,16 @@
#include "noui.h"
#include "scheduler.h"
#include "util.h"
#include "httpserver.h"
#include "httprpc.h"
#include "rpcserver.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <stdio.h>
/* Introduction text for doxygen: */
/*! \mainpage Developer documentation
@@ -51,7 +56,7 @@ void WaitForShutdown(boost::thread_group* threadGroup)
}
if (threadGroup)
{
threadGroup->interrupt_all();
Interrupt(*threadGroup);
threadGroup->join_all();
}
}
@@ -81,7 +86,7 @@ bool AppInit(int argc, char* argv[])
// Process help and version before taking care about datadir
if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version"))
{
std::string strUsage = _("Komodo Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n";
std::string strUsage = _("Komodo Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n" + PrivacyInfo();
if (mapArgs.count("-version"))
{
@@ -134,7 +139,7 @@ bool AppInit(int argc, char* argv[])
_("- Source code: %s\n"
"- .deb package: %s\n")).c_str(),
GetConfigFile().string().c_str(),
"contrib/DEBIAN/examples/zcash.conf",
"contrib/debian/examples/zcash.conf",
"/usr/share/doc/zcash/examples/zcash.conf");
return false;
} catch (const std::exception& e) {
@@ -194,7 +199,7 @@ bool AppInit(int argc, char* argv[])
}
if (!fRet)
{
threadGroup.interrupt_all();
Interrupt(threadGroup);
// threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of
// the startup-failure cases to make sure they don't result in a hang due to some
// thread-blocking-waiting-for-another-thread-during-startup case

View File

@@ -82,9 +82,10 @@ CBlockIndex* CBlockIndex::GetAncestor(int height)
while (heightWalk > height) {
int heightSkip = GetSkipHeight(heightWalk);
int heightSkipPrev = GetSkipHeight(heightWalk - 1);
if (heightSkip == height ||
(heightSkip > height && !(heightSkipPrev < heightSkip - 2 &&
heightSkipPrev >= height))) {
if (pindexWalk->pskip != NULL &&
(heightSkip == height ||
(heightSkip > height && !(heightSkipPrev < heightSkip - 2 &&
heightSkipPrev >= height)))) {
// Only follow pskip if pprev->pskip isn't better than pskip->pprev.
pindexWalk = pindexWalk->pskip;
heightWalk = heightSkip;

View File

@@ -56,7 +56,7 @@ struct CDiskBlockPos
};
enum BlockStatus {
enum BlockStatus: uint32_t {
//! Unused.
BLOCK_VALID_UNKNOWN = 0,
@@ -141,6 +141,9 @@ public:
//! The anchor for the tree state up to the start of this block
uint256 hashAnchor;
//! (memory only) The anchor for the tree state up to the end of this block
uint256 hashAnchorEnd;
//! block header
int nVersion;
uint256 hashMerkleRoot;
@@ -167,6 +170,7 @@ public:
nChainTx = 0;
nStatus = 0;
hashAnchor = uint256();
hashAnchorEnd = uint256();
nSequenceId = 0;
nVersion = 0;

View File

@@ -111,7 +111,6 @@ public:
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
fRequireRPCPassword = true;
fMiningRequiresPeers = true;
fDefaultConsistencyChecks = false;
fRequireStandard = true;
@@ -127,7 +126,7 @@ public:
1481120910, // * UNIX timestamp of last checkpoint block
110415, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
4240 // * estimated number of transactions per day after checkpoint
2777 // * estimated number of transactions per day after checkpoint
// total number of tx / (checkpoint block height / (24 * 24))
};
if ( pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,chainparams_commandline,(void *)&consensus) != 0 )
@@ -212,10 +211,12 @@ public:
checkpointData = (Checkpoints::CCheckpointData) {
boost::assign::map_list_of
( 0, consensus.hashGenesisBlock),
genesis.nTime,
0,
0
(0, consensus.hashGenesisBlock)
(38000, uint256S("0x001e9a2d2e2892b88e9998cf7b079b41d59dd085423a921fe8386cecc42287b8")),
1486897419, // * UNIX timestamp of last checkpoint block
47163, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
715 // total number of tx / (checkpoint block height / (24 * 24))
};
}
};
@@ -261,7 +262,6 @@ public:
vFixedSeeds.clear(); //! Regtest mode doesn't have any fixed seeds.
vSeeds.clear(); //! Regtest mode doesn't have any DNS seeds.
fRequireRPCPassword = false;
fMiningRequiresPeers = false;
fDefaultConsistencyChecks = true;
fRequireStandard = false;

View File

@@ -58,7 +58,6 @@ public:
/** Used if GenerateBitcoins is called with a negative number of threads */
int DefaultMinerThreads() const { return nMinerThreads; }
const CBlock& GenesisBlock() const { return genesis; }
bool RequireRPCPassword() const { return fRequireRPCPassword; }
/** Make miner wait to have peers to avoid wasting work */
bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
/** Default value for -checkmempool and -checkblockindex argument */
@@ -111,7 +110,6 @@ protected:
std::string strCurrencyUnits;
CBlock genesis;
std::vector<SeedSpec6> vFixedSeeds;
bool fRequireRPCPassword = false;
bool fMiningRequiresPeers = false;
bool fDefaultConsistencyChecks = false;
bool fRequireStandard = false;

View File

@@ -24,15 +24,6 @@ namespace Checkpoints {
*/
static const double SIGCHECK_VERIFICATION_FACTOR = 5.0;
bool CheckBlock(const CCheckpointData& data, int nHeight, const uint256& hash)
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;
MapCheckpoints::const_iterator i = checkpoints.find(nHeight);
if (i == checkpoints.end()) return true;
return hash == i->second;
}
//! Guess how far we are in the verification process at the given block index
double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex *pindex, bool fSigchecks) {
if (pindex==NULL)

View File

@@ -26,9 +26,6 @@ struct CCheckpointData {
double fTransactionsPerDay;
};
//! Returns true if block passes checkpoint checks
bool CheckBlock(const CCheckpointData& data, int nHeight, const uint256& hash);
//! Return conservative estimate of total number of blocks, 0 if unknown
int GetTotalBlocksEstimate(const CCheckpointData& data);

View File

@@ -1,4 +1,5 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2016-2017 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,8 +17,8 @@
//! 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 5
#define CLIENT_VERSION_BUILD 51
#define CLIENT_VERSION_REVISION 8
#define CLIENT_VERSION_BUILD 52
//! Set to true for release, false for prerelease or test build
#define CLIENT_VERSION_IS_RELEASE true
@@ -26,7 +27,7 @@
* Copyright year (2009-this)
* Todo: update this when changing our copyright comments in the source
*/
#define COPYRIGHT_YEAR 2016
#define COPYRIGHT_YEAR 2017
#endif //HAVE_CONFIG_H

View File

@@ -12,6 +12,8 @@ class CCoinControl
{
public:
CTxDestination destChange;
//! If false, allows unselected inputs, but requires all selected inputs be used
bool fAllowOtherInputs;
CCoinControl()
{
@@ -21,6 +23,7 @@ public:
void SetNull()
{
destChange = CNoDestination();
fAllowOtherInputs = false;
setSelected.clear();
}
@@ -50,7 +53,7 @@ public:
setSelected.clear();
}
void ListSelected(std::vector<COutPoint>& vOutpoints)
void ListSelected(std::vector<COutPoint>& vOutpoints) const
{
vOutpoints.assign(setSelected.begin(), setSelected.end());
}

View File

@@ -101,7 +101,7 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
// version as fresh.
ret->second.flags = CCoinsCacheEntry::FRESH;
}
cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins);
cachedCoinsUsage += ret->second.coins.DynamicMemoryUsage();
return ret;
}
@@ -124,7 +124,7 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
ret->second.entered = true;
ret->second.tree = tree;
cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
return true;
}
@@ -163,7 +163,7 @@ void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
if (insertRet.second) {
// An insert took place
cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage();
}
hashAnchor = newrt;
@@ -224,7 +224,7 @@ CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
ret.first->second.flags = CCoinsCacheEntry::FRESH;
}
} else {
cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins);
cachedCoinUsage = ret.first->second.coins.DynamicMemoryUsage();
}
// Assume that whenever ModifyCoins is called, the entry will be modified.
ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
@@ -284,7 +284,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
assert(it->second.flags & CCoinsCacheEntry::FRESH);
CCoinsCacheEntry& entry = cacheCoins[it->first];
entry.coins.swap(it->second.coins);
cachedCoinsUsage += memusage::DynamicUsage(entry.coins);
cachedCoinsUsage += entry.coins.DynamicMemoryUsage();
entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
}
} else {
@@ -292,13 +292,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
// The grandparent does not have an entry, and the child is
// modified and being pruned. This means we can just delete
// it from the parent.
cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage();
cacheCoins.erase(itUs);
} else {
// A normal modification.
cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage();
itUs->second.coins.swap(it->second.coins);
cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins);
cachedCoinsUsage += itUs->second.coins.DynamicMemoryUsage();
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
}
}
@@ -318,7 +318,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
entry.tree = child_it->second.tree;
entry.flags = CAnchorsCacheEntry::DIRTY;
cachedCoinsUsage += memusage::DynamicUsage(entry.tree);
cachedCoinsUsage += entry.tree.DynamicMemoryUsage();
} else {
if (parent_it->second.entered != child_it->second.entered) {
// The parent may have removed the entry.
@@ -525,6 +525,6 @@ CCoinsModifier::~CCoinsModifier()
cache.cacheCoins.erase(it);
} else {
// If the coin still exists after the modification, add the new usage
cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins);
cache.cachedCoinsUsage += it->second.coins.DynamicMemoryUsage();
}
}

View File

@@ -9,6 +9,7 @@
#define KOMODO_ENABLE_INTEREST //enabling this is a hardfork, activate with new RR method
#include "compressor.h"
#include "core_memusage.h"
#include "memusage.h"
#include "serialize.h"
#include "uint256.h"
@@ -262,8 +263,7 @@ public:
size_t DynamicMemoryUsage() const {
size_t ret = memusage::DynamicUsage(vout);
BOOST_FOREACH(const CTxOut &out, vout) {
const std::vector<unsigned char> *script = &out.scriptPubKey;
ret += memusage::DynamicUsage(*script);
ret += RecursiveDynamicUsage(out.scriptPubKey);
}
return ret;
}

View File

@@ -48,7 +48,7 @@ public:
unsigned char _chRejectCode=0, std::string _strRejectReason="") {
return DoS(0, ret, _chRejectCode, _strRejectReason);
}
virtual bool Error(std::string strRejectReasonIn="") {
virtual bool Error(const std::string& strRejectReasonIn) {
if (mode == MODE_VALID)
strRejectReason = strRejectReasonIn;
mode = MODE_ERROR;

View File

@@ -15,7 +15,7 @@ class uint256;
class UniValue;
// core_read.cpp
extern CScript ParseScript(std::string s);
extern CScript ParseScript(const std::string& s);
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);

62
src/core_memusage.h Normal file
View File

@@ -0,0 +1,62 @@
// Copyright (c) 2015 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_CORE_MEMUSAGE_H
#define BITCOIN_CORE_MEMUSAGE_H
#include "primitives/transaction.h"
#include "primitives/block.h"
#include "memusage.h"
static inline size_t RecursiveDynamicUsage(const CScript& script) {
return memusage::DynamicUsage(*static_cast<const std::vector<unsigned char>*>(&script));
}
static inline size_t RecursiveDynamicUsage(const COutPoint& out) {
return 0;
}
static inline size_t RecursiveDynamicUsage(const CTxIn& in) {
return RecursiveDynamicUsage(in.scriptSig) + RecursiveDynamicUsage(in.prevout);
}
static inline size_t RecursiveDynamicUsage(const CTxOut& out) {
return RecursiveDynamicUsage(out.scriptPubKey);
}
static inline size_t RecursiveDynamicUsage(const CTransaction& tx) {
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout);
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
for (std::vector<CTxOut>::const_iterator it = tx.vout.begin(); it != tx.vout.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
return mem;
}
static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) {
size_t mem = memusage::DynamicUsage(tx.vin) + memusage::DynamicUsage(tx.vout);
for (std::vector<CTxIn>::const_iterator it = tx.vin.begin(); it != tx.vin.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
for (std::vector<CTxOut>::const_iterator it = tx.vout.begin(); it != tx.vout.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
return mem;
}
static inline size_t RecursiveDynamicUsage(const CBlock& block) {
size_t mem = memusage::DynamicUsage(block.vtx) + memusage::DynamicUsage(block.vMerkleTree);
for (std::vector<CTransaction>::const_iterator it = block.vtx.begin(); it != block.vtx.end(); it++) {
mem += RecursiveDynamicUsage(*it);
}
return mem;
}
static inline size_t RecursiveDynamicUsage(const CBlockLocator& locator) {
return memusage::DynamicUsage(locator.vHave);
}
#endif // BITCOIN_CORE_MEMUSAGE_H

View File

@@ -9,7 +9,7 @@
#include "script/script.h"
#include "serialize.h"
#include "streams.h"
#include "univalue/univalue.h"
#include <univalue.h>
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"
@@ -22,7 +22,7 @@
using namespace std;
CScript ParseScript(std::string s)
CScript ParseScript(const std::string& s)
{
CScript result;

View File

@@ -10,7 +10,7 @@
#include "script/standard.h"
#include "serialize.h"
#include "streams.h"
#include "univalue/univalue.h"
#include <univalue.h>
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"

View File

@@ -12,6 +12,10 @@
// 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 "crypto/equihash.h"
#include "util.h"
@@ -343,6 +347,7 @@ std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t le
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,
@@ -735,6 +740,7 @@ invalidsolution:
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)
@@ -786,40 +792,48 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
// 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);

View File

@@ -182,12 +182,14 @@ public:
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);
};
@@ -211,6 +213,7 @@ static Equihash<48,5> Eh48_5;
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)
@@ -258,6 +261,7 @@ inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const
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) { \

View File

@@ -1,14 +1,14 @@
#include "json_test_vectors.h"
Array
UniValue
read_json(const std::string& jsondata)
{
Value v;
UniValue v;
if (!read_string(jsondata, v) || v.type() != array_type)
if (!(v.read(jsondata) && v.isArray()))
{
ADD_FAILURE();
return Array();
return UniValue(UniValue::VARR);
}
return v.get_array();
}

View File

@@ -5,12 +5,9 @@
#include "serialize.h"
#include "streams.h"
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
#include "json/json_spirit_writer_template.h"
#include <univalue.h>
using namespace json_spirit;
Array
UniValue
read_json(const std::string& jsondata);
// #define PRINT_JSON 1
@@ -34,7 +31,7 @@ void expect_deser_same(const T& expected)
}
template<typename T, typename U>
void expect_test_vector(T& it, const U& expected)
void expect_test_vector(T& v, const U& expected)
{
expect_deser_same(expected);
@@ -45,7 +42,7 @@ void expect_test_vector(T& it, const U& expected)
std::cout << "\t\"" ;
std::cout << HexStr(ss1.begin(), ss1.end()) << "\",\n";
#else
std::string raw = (it++)->get_str();
std::string raw = v.get_str();
CDataStream ss2(ParseHex(raw), SER_NETWORK, PROTOCOL_VERSION);
ASSERT_TRUE(ss1.size() == ss2.size());

View File

@@ -1,3 +1,7 @@
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include <gtest/gtest.h>
#include <gmock/gmock.h>
@@ -76,6 +80,7 @@ TEST(equihash_tests, is_probably_duplicate) {
ASSERT_TRUE(IsProbablyDuplicate<4>(p3, 4));
}
#ifdef ENABLE_MINING
TEST(equihash_tests, check_basic_solver_cancelled) {
Equihash<48,5> Eh48_5;
crypto_generichash_blake2b_state state;
@@ -283,3 +288,4 @@ TEST(equihash_tests, check_optimised_solver_cancelled) {
}), EhSolverCancelledException);
}
}
#endif // ENABLE_MINING

View File

@@ -44,13 +44,22 @@ TEST(founders_reward_test, create_testnet_2of3multisig) {
for (int i = 0; i < numKeys; i++) {
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[0] = newKey;
pWallet->SetAddressBook(newKey.GetID(), "", "receive");
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[1] = newKey;
pWallet->SetAddressBook(newKey.GetID(), "", "receive");
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[2] = newKey;
pWallet->SetAddressBook(newKey.GetID(), "", "receive");
CScript result = GetScriptForMultisig(2, pubkeys);
ASSERT_FALSE(result.size() > MAX_SCRIPT_ELEMENT_SIZE);
CScriptID innerID(result);
pWallet->AddCScript(result);
pWallet->SetAddressBook(innerID, "", "receive");
std::string address = CBitcoinAddress(innerID).ToString();
addresses.push_back(address);
}
@@ -92,12 +101,16 @@ TEST(founders_reward_test, general) {
CChainParams params = Params();
// First testnet reward:
// address = t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi
// script = OP_HASH160 ef775f1f997f122a062fff1a2d7443abd1f9c642 OP_EQUAL
// raw script = a914ef775f1f997f122a062fff1a2d7443abd1f9c64287
// Fourth testnet reward:
// address = t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy
// script.ToString() = OP_HASH160 55d64928e69829d9376c776550b6cc710d427153 OP_EQUAL
// HexStr(script) = a91455d64928e69829d9376c776550b6cc710d42715387
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(1), ParseHex("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"));
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(1), "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi");
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53126), ParseHex("a914ac67f4c072668138d88a86ff21b27207b283212f87"));
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53126), "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2");
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53127), ParseHex("a91455d64928e69829d9376c776550b6cc710d42715387"));
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53127), "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy");
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();

View File

@@ -1,17 +0,0 @@
#include <gtest/gtest.h>
#include "json/json_spirit_reader_template.h"
using namespace json_spirit;
// This test checks if we have fixed a stack overflow problem with json_spirit.
// It was possible to try and create an unlimited number of nested compound elements.
// Without the fix in json_spirit_reader_template.h, this test will segfault.
TEST(json_spirit_tests, nested_input_segfault) {
std::vector<char> v (100000);
std::fill (v.begin(),v.end(), '[');
std::string s(v.begin(), v.end());
Value value;
bool b = json_spirit::read_string(s, value);
ASSERT_FALSE(b);
}

View File

@@ -57,18 +57,15 @@ void expect_ser_test_vector(B& b, const C& c, const A& tree) {
template<typename Tree, typename Witness>
void test_tree(
Array commitment_tests,
Array root_tests,
Array ser_tests,
Array witness_ser_tests,
Array path_tests
UniValue commitment_tests,
UniValue root_tests,
UniValue ser_tests,
UniValue witness_ser_tests,
UniValue path_tests
)
{
Array::iterator commitment_iterator = commitment_tests.begin();
Array::iterator root_iterator = root_tests.begin();
Array::iterator ser_iterator = ser_tests.begin();
Array::iterator witness_ser_iterator = witness_ser_tests.begin();
Array::iterator path_iterator = path_tests.begin();
size_t witness_ser_i = 0;
size_t path_i = 0;
Tree tree;
@@ -88,7 +85,7 @@ void test_tree(
vector<Witness> witnesses;
for (size_t i = 0; i < 16; i++) {
uint256 test_commitment = uint256S((commitment_iterator++)->get_str());
uint256 test_commitment = uint256S(commitment_tests[i].get_str());
// Witness here
witnesses.push_back(tree.witness());
@@ -103,10 +100,10 @@ void test_tree(
ASSERT_TRUE(tree.last() == test_commitment);
// Check tree root consistency
expect_test_vector(root_iterator, tree.root());
expect_test_vector(root_tests[i], tree.root());
// Check serialization of tree
expect_ser_test_vector(ser_iterator, tree, tree);
expect_ser_test_vector(ser_tests[i], tree, tree);
bool first = true; // The first witness can never form a path
BOOST_FOREACH(Witness& wit, witnesses)
@@ -121,7 +118,7 @@ void test_tree(
auto path = wit.path();
{
expect_test_vector(path_iterator, path);
expect_test_vector(path_tests[path_i++], path);
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
@@ -173,7 +170,7 @@ void test_tree(
}
// Check witness serialization
expect_ser_test_vector(witness_ser_iterator, wit, tree);
expect_ser_test_vector(witness_ser_tests[witness_ser_i++], wit, tree);
ASSERT_TRUE(wit.root() == tree.root());
@@ -192,24 +189,25 @@ void test_tree(
}
}
#define MAKE_STRING(x) std::string((x), (x)+sizeof(x))
TEST(merkletree, vectors) {
Array root_tests = read_json(std::string(json_tests::merkle_roots, json_tests::merkle_roots + sizeof(json_tests::merkle_roots)));
Array ser_tests = read_json(std::string(json_tests::merkle_serialization, json_tests::merkle_serialization + sizeof(json_tests::merkle_serialization)));
Array witness_ser_tests = read_json(std::string(json_tests::merkle_witness_serialization, json_tests::merkle_witness_serialization + sizeof(json_tests::merkle_witness_serialization)));
Array path_tests = read_json(std::string(json_tests::merkle_path, json_tests::merkle_path + sizeof(json_tests::merkle_path)));
Array commitment_tests = read_json(std::string(json_tests::merkle_commitments, json_tests::merkle_commitments + sizeof(json_tests::merkle_commitments)));
UniValue root_tests = read_json(MAKE_STRING(json_tests::merkle_roots));
UniValue ser_tests = read_json(MAKE_STRING(json_tests::merkle_serialization));
UniValue witness_ser_tests = read_json(MAKE_STRING(json_tests::merkle_witness_serialization));
UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path));
UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments));
test_tree<ZCTestingIncrementalMerkleTree, ZCTestingIncrementalWitness>(commitment_tests, root_tests, ser_tests, witness_ser_tests, path_tests);
}
TEST(merkletree, emptyroots) {
Array empty_roots = read_json(std::string(json_tests::merkle_roots_empty, json_tests::merkle_roots_empty + sizeof(json_tests::merkle_roots_empty)));
Array::iterator root_iterator = empty_roots.begin();
UniValue empty_roots = read_json(MAKE_STRING(json_tests::merkle_roots_empty));
libzcash::EmptyMerkleRoots<64, libzcash::SHA256Compress> emptyroots;
for (size_t depth = 0; depth <= 64; depth++) {
expect_test_vector(root_iterator, emptyroots.empty_root(depth));
expect_test_vector(empty_roots[depth], emptyroots.empty_root(depth));
}
// Double check that we're testing (at least) all the empty roots we'll use.

View File

@@ -4,9 +4,57 @@
#include "utiltime.h"
TEST(Metrics, AtomicTimer) {
AtomicTimer t;
SetMockTime(100);
EXPECT_FALSE(t.running());
t.start();
EXPECT_TRUE(t.running());
t.start();
EXPECT_TRUE(t.running());
t.stop();
EXPECT_TRUE(t.running());
t.stop();
EXPECT_FALSE(t.running());
// Additional calls to stop() are ignored.
t.stop();
EXPECT_FALSE(t.running());
t.start();
EXPECT_TRUE(t.running());
AtomicCounter c;
EXPECT_EQ(0, t.rate(c));
c.increment();
EXPECT_EQ(0, t.rate(c));
SetMockTime(101);
EXPECT_EQ(1, t.rate(c));
c.decrement();
EXPECT_EQ(0, t.rate(c));
SetMockTime(102);
EXPECT_EQ(0, t.rate(c));
c.increment();
EXPECT_EQ(0.5, t.rate(c));
t.stop();
EXPECT_FALSE(t.running());
EXPECT_EQ(0.5, t.rate(c));
}
TEST(Metrics, GetLocalSolPS) {
SetMockTime(100);
MarkStartTime();
miningTimer.start();
// No time has passed
EXPECT_EQ(0, GetLocalSolPS());
@@ -27,4 +75,20 @@ TEST(Metrics, GetLocalSolPS) {
solutionTargetChecks.increment();
solutionTargetChecks.increment();
EXPECT_EQ(1.5, GetLocalSolPS());
// Stop timing
miningTimer.stop();
EXPECT_EQ(1.5, GetLocalSolPS());
// Increment time
SetMockTime(103);
EXPECT_EQ(1.5, GetLocalSolPS());
// Start timing again
miningTimer.start();
EXPECT_EQ(1.5, GetLocalSolPS());
// Increment time
SetMockTime(104);
EXPECT_EQ(1, GetLocalSolPS());
}

103
src/gtest/test_miner.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "chainparams.h"
#include "key.h"
#include "miner.h"
#include "util.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#include <boost/optional.hpp>
using ::testing::Return;
#ifdef ENABLE_WALLET
class MockReserveKey : public CReserveKey {
public:
MockReserveKey() : CReserveKey(nullptr) { }
MOCK_METHOD1(GetReservedKey, bool(CPubKey &pubkey));
};
#endif
TEST(Miner, GetMinerScriptPubKey) {
SelectParams(CBaseChainParams::MAIN);
boost::optional<CScript> scriptPubKey;
#ifdef ENABLE_WALLET
MockReserveKey reservekey;
EXPECT_CALL(reservekey, GetReservedKey(::testing::_))
.WillRepeatedly(Return(false));
#endif
// No miner address set
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
mapArgs["-mineraddress"] = "notAnAddress";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
// Partial address
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqq";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
// Typo in address
mapArgs["-mineraddress"] = "t1TByaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_FALSE((bool) scriptPubKey);
// Set up expected scriptPubKey for t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF
CKeyID keyID;
keyID.SetHex("eb88f1c65b39a823479ac9c7db2f4a865960a165");
CScript expectedScriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
// Valid address
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_TRUE((bool) scriptPubKey);
EXPECT_EQ(expectedScriptPubKey, *scriptPubKey);
// Valid address with leading whitespace
mapArgs["-mineraddress"] = " t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_TRUE((bool) scriptPubKey);
EXPECT_EQ(expectedScriptPubKey, *scriptPubKey);
// Valid address with trailing whitespace
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF ";
#ifdef ENABLE_WALLET
scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
scriptPubKey = GetMinerScriptPubKey();
#endif
EXPECT_TRUE((bool) scriptPubKey);
EXPECT_EQ(expectedScriptPubKey, *scriptPubKey);
}

View File

@@ -46,21 +46,25 @@ TEST(noteencryption, api)
ASSERT_TRUE(plaintext == message);
// Test wrong nonce
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), (i == 0) ? 1 : (i - 1)), std::runtime_error);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), (i == 0) ? 1 : (i - 1)),
libzcash::note_decryption_failed);
// Test wrong ephemeral key
{
ZCNoteEncryption c = ZCNoteEncryption(uint256());
ASSERT_THROW(decrypter.decrypt(ciphertext, c.get_epk(), uint256(), i), std::runtime_error);
ASSERT_THROW(decrypter.decrypt(ciphertext, c.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
}
// Test wrong seed
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256S("11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77"), i), std::runtime_error);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256S("11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77"), i),
libzcash::note_decryption_failed);
// Test corrupted ciphertext
ciphertext[10] ^= 0xff;
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
ciphertext[10] ^= 0xff;
}
@@ -69,7 +73,8 @@ TEST(noteencryption, api)
uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint252());
ZCNoteDecryption decrypter(sk_enc_2);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
}
{
@@ -81,7 +86,8 @@ TEST(noteencryption, api)
// Test wrong public key (test of KDF)
decrypter.change_pk_enc(uint256());
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i), std::runtime_error);
ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
libzcash::note_decryption_failed);
}
}

View File

@@ -629,15 +629,14 @@ TEST(proofs, g2_deserialization)
TEST(proofs, g1_test_vectors)
{
Array v = read_json(std::string(json_tests::g1_compressed, json_tests::g1_compressed + sizeof(json_tests::g1_compressed)));
Array::iterator v_iterator = v.begin();
UniValue v = read_json(std::string(json_tests::g1_compressed, json_tests::g1_compressed + sizeof(json_tests::g1_compressed)));
curve_G1 e = curve_Fr("34958239045823") * curve_G1::one();
for (size_t i = 0; i < 10000; i++) {
e = (curve_Fr("34958239045823") ^ i) * e;
auto expected = CompressedG1(e);
expect_test_vector(v_iterator, expected);
expect_test_vector(v[i], expected);
ASSERT_TRUE(expected.to_libsnark_g1<curve_G1>() == e);
}
}
@@ -646,15 +645,14 @@ TEST(proofs, g1_test_vectors)
TEST(proofs, g2_test_vectors)
{
Array v = read_json(std::string(json_tests::g2_compressed, json_tests::g2_compressed + sizeof(json_tests::g2_compressed)));
Array::iterator v_iterator = v.begin();
UniValue v = read_json(std::string(json_tests::g2_compressed, json_tests::g2_compressed + sizeof(json_tests::g2_compressed)));
curve_G2 e = curve_Fr("34958239045823") * curve_G2::one();
for (size_t i = 0; i < 10000; i++) {
e = (curve_Fr("34958239045823") ^ i) * e;
auto expected = CompressedG2(e);
expect_test_vector(v_iterator, expected);
expect_test_vector(v[i], expected);
ASSERT_TRUE(expected.to_libsnark_g2<curve_G2>() == e);
}
}

View File

@@ -1,6 +1,5 @@
#include <gtest/gtest.h>
#include "json/json_spirit_value.h"
#include "json/json_spirit_utils.h"
#include <univalue.h>
#include "chain.h"
#include "chainparams.h"
@@ -10,7 +9,7 @@
#include "streams.h"
#include "utilstrencodings.h"
extern json_spirit::Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
TEST(rpc, check_blockToJSON_returns_minified_solution) {
SelectParams(CBaseChainParams::TESTNET);
@@ -24,6 +23,6 @@ TEST(rpc, check_blockToJSON_returns_minified_solution) {
CBlockIndex index {block};
index.nHeight = 1391;
json_spirit::Object obj = blockToJSON(block, &index);
EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", json_spirit::find_value(obj, "solution").get_str());
UniValue obj = blockToJSON(block, &index);
EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", find_value(obj, "solution").get_str());
}

193
src/httprpc.cpp Normal file
View File

@@ -0,0 +1,193 @@
#include "httprpc.h"
#include "base58.h"
#include "chainparams.h"
#include "httpserver.h"
#include "rpcprotocol.h"
#include "rpcserver.h"
#include "random.h"
#include "sync.h"
#include "util.h"
#include "utilstrencodings.h"
#include "ui_interface.h"
#include <boost/algorithm/string.hpp> // boost::trim
/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
* re-lock the wellet.
*/
class HTTPRPCTimer : public RPCTimerBase
{
public:
HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
ev(eventBase, false, func)
{
struct timeval tv;
tv.tv_sec = millis/1000;
tv.tv_usec = (millis%1000)*1000;
ev.trigger(&tv);
}
private:
HTTPEvent ev;
};
class HTTPRPCTimerInterface : public RPCTimerInterface
{
public:
HTTPRPCTimerInterface(struct event_base* base) : base(base)
{
}
const char* Name()
{
return "HTTP";
}
RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
{
return new HTTPRPCTimer(base, func, millis);
}
private:
struct event_base* base;
};
/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
// Send error reply from json-rpc error object
int nStatus = HTTP_INTERNAL_SERVER_ERROR;
int code = find_value(objError, "code").get_int();
if (code == RPC_INVALID_REQUEST)
nStatus = HTTP_BAD_REQUEST;
else if (code == RPC_METHOD_NOT_FOUND)
nStatus = HTTP_NOT_FOUND;
std::string strReply = JSONRPCReply(NullUniValue, objError, id);
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(nStatus, strReply);
}
static bool RPCAuthorized(const std::string& strAuth)
{
if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
return false;
if (strAuth.substr(0, 6) != "Basic ")
return false;
std::string strUserPass64 = strAuth.substr(6);
boost::trim(strUserPass64);
std::string strUserPass = DecodeBase64(strUserPass64);
return TimingResistantEqual(strUserPass, strRPCUserColonPass);
}
static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
{
// JSONRPC handles only POST
if (req->GetRequestMethod() != HTTPRequest::POST) {
req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
return false;
}
// Check authorization
std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
if (!authHeader.first) {
req->WriteReply(HTTP_UNAUTHORIZED);
return false;
}
if (!RPCAuthorized(authHeader.second)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
/* Deter brute-forcing
If this results in a DoS the user really
shouldn't have their RPC port exposed. */
MilliSleep(250);
req->WriteReply(HTTP_UNAUTHORIZED);
return false;
}
JSONRequest jreq;
try {
// Parse request
UniValue valRequest;
if (!valRequest.read(req->ReadBody()))
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
std::string strReply;
// singleton request
if (valRequest.isObject()) {
jreq.parse(valRequest);
UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
// Send reply
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
// array of requests
} else if (valRequest.isArray())
strReply = JSONRPCExecBatch(valRequest.get_array());
else
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strReply);
} catch (const UniValue& objError) {
JSONErrorReply(req, objError, jreq.id);
return false;
} catch (const std::exception& e) {
JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
return false;
}
return true;
}
static bool InitRPCAuthentication()
{
if (mapArgs["-rpcpassword"] == "")
{
LogPrintf("No rpcpassword set - using random cookie authentication\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
uiInterface.ThreadSafeMessageBox(
_("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
"", CClientUIInterface::MSG_ERROR);
return false;
}
} else {
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
}
return true;
}
bool StartHTTPRPC()
{
LogPrint("rpc", "Starting HTTP RPC server\n");
if (!InitRPCAuthentication())
return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
assert(EventBase());
httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
RPCRegisterTimerInterface(httpRPCTimerInterface);
return true;
}
void InterruptHTTPRPC()
{
LogPrint("rpc", "Interrupting HTTP RPC server\n");
}
void StopHTTPRPC()
{
LogPrint("rpc", "Stopping HTTP RPC server\n");
UnregisterHTTPHandler("/", true);
if (httpRPCTimerInterface) {
RPCUnregisterTimerInterface(httpRPCTimerInterface);
delete httpRPCTimerInterface;
httpRPCTimerInterface = 0;
}
}

37
src/httprpc.h Normal file
View File

@@ -0,0 +1,37 @@
// Copyright (c) 2015 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_HTTPRPC_H
#define BITCOIN_HTTPRPC_H
#include <string>
#include <map>
class HTTPRequest;
/** Start HTTP RPC subsystem.
* Precondition; HTTP and RPC has been started.
*/
bool StartHTTPRPC();
/** Interrupt HTTP RPC subsystem.
*/
void InterruptHTTPRPC();
/** Stop HTTP RPC subsystem.
* Precondition; HTTP and RPC has been stopped.
*/
void StopHTTPRPC();
/** Start HTTP REST subsystem.
* Precondition; HTTP and RPC has been started.
*/
bool StartREST();
/** Interrupt RPC REST subsystem.
*/
void InterruptREST();
/** Stop HTTP REST subsystem.
* Precondition; HTTP and RPC has been stopped.
*/
void StopREST();
#endif

598
src/httpserver.cpp Normal file
View File

@@ -0,0 +1,598 @@
// Copyright (c) 2015 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 "httpserver.h"
#include "chainparamsbase.h"
#include "compat.h"
#include "util.h"
#include "netbase.h"
#include "rpcprotocol.h" // For HTTP status codes
#include "sync.h"
#include "ui_interface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <event2/event.h>
#include <event2/http.h>
#include <event2/thread.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/keyvalq_struct.h>
#ifdef EVENT__HAVE_NETINET_IN_H
#include <netinet/in.h>
#ifdef _XOPEN_SOURCE_EXTENDED
#include <arpa/inet.h>
#endif
#endif
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
/** HTTP request work item */
class HTTPWorkItem : public HTTPClosure
{
public:
HTTPWorkItem(HTTPRequest* req, const std::string &path, const HTTPRequestHandler& func):
req(req), path(path), func(func)
{
}
void operator()()
{
func(req.get(), path);
}
boost::scoped_ptr<HTTPRequest> req;
private:
std::string path;
HTTPRequestHandler func;
};
/** Simple work queue for distributing work over multiple threads.
* Work items are simply callable objects.
*/
template <typename WorkItem>
class WorkQueue
{
private:
/** Mutex protects entire object */
CWaitableCriticalSection cs;
CConditionVariable cond;
/* XXX in C++11 we can use std::unique_ptr here and avoid manual cleanup */
std::deque<WorkItem*> queue;
bool running;
size_t maxDepth;
public:
WorkQueue(size_t maxDepth) : running(true),
maxDepth(maxDepth)
{
}
/* Precondition: worker threads have all stopped */
~WorkQueue()
{
while (!queue.empty()) {
delete queue.front();
queue.pop_front();
}
}
/** Enqueue a work item */
bool Enqueue(WorkItem* item)
{
boost::unique_lock<boost::mutex> lock(cs);
if (queue.size() >= maxDepth) {
return false;
}
queue.push_back(item);
cond.notify_one();
return true;
}
/** Thread function */
void Run()
{
while (running) {
WorkItem* i = 0;
{
boost::unique_lock<boost::mutex> lock(cs);
while (running && queue.empty())
cond.wait(lock);
if (!running)
break;
i = queue.front();
queue.pop_front();
}
(*i)();
delete i;
}
}
/** Interrupt and exit loops */
void Interrupt()
{
boost::unique_lock<boost::mutex> lock(cs);
running = false;
cond.notify_all();
}
/** Return current depth of queue */
size_t Depth()
{
boost::unique_lock<boost::mutex> lock(cs);
return queue.size();
}
};
struct HTTPPathHandler
{
HTTPPathHandler() {}
HTTPPathHandler(std::string prefix, bool exactMatch, HTTPRequestHandler handler):
prefix(prefix), exactMatch(exactMatch), handler(handler)
{
}
std::string prefix;
bool exactMatch;
HTTPRequestHandler handler;
};
/** HTTP module state */
//! libevent event loop
static struct event_base* eventBase = 0;
//! HTTP server
struct evhttp* eventHTTP = 0;
//! List of subnets to allow RPC connections from
static std::vector<CSubNet> rpc_allow_subnets;
//! Work queue for handling longer requests off the event loop thread
static WorkQueue<HTTPClosure>* workQueue = 0;
//! Handlers for (sub)paths
std::vector<HTTPPathHandler> pathHandlers;
/** Check if a network address is allowed to access the HTTP server */
static bool ClientAllowed(const CNetAddr& netaddr)
{
if (!netaddr.IsValid())
return false;
BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets)
if (subnet.Match(netaddr))
return true;
return false;
}
/** Initialize ACL list for HTTP server */
static bool InitHTTPAllowList()
{
rpc_allow_subnets.clear();
rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
if (mapMultiArgs.count("-rpcallowip")) {
const std::vector<std::string>& vAllow = mapMultiArgs["-rpcallowip"];
BOOST_FOREACH (std::string strAllow, vAllow) {
CSubNet subnet(strAllow);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
"", CClientUIInterface::MSG_ERROR);
return false;
}
rpc_allow_subnets.push_back(subnet);
}
}
std::string strAllowed;
BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets)
strAllowed += subnet.ToString() + " ";
LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed);
return true;
}
/** HTTP request method as string - use for logging only */
static std::string RequestMethodString(HTTPRequest::RequestMethod m)
{
switch (m) {
case HTTPRequest::GET:
return "GET";
break;
case HTTPRequest::POST:
return "POST";
break;
case HTTPRequest::HEAD:
return "HEAD";
break;
case HTTPRequest::PUT:
return "PUT";
break;
default:
return "unknown";
}
}
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
LogPrint("http", "Received a %s request for %s from %s\n",
RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString());
// Early address-based allow check
if (!ClientAllowed(hreq->GetPeer())) {
hreq->WriteReply(HTTP_FORBIDDEN);
return;
}
// Early reject unknown HTTP methods
if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
hreq->WriteReply(HTTP_BADMETHOD);
return;
}
// Find registered handler for prefix
std::string strURI = hreq->GetURI();
std::string path;
std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
for (; i != iend; ++i) {
bool match = false;
if (i->exactMatch)
match = (strURI == i->prefix);
else
match = (strURI.substr(0, i->prefix.size()) == i->prefix);
if (match) {
path = strURI.substr(i->prefix.size());
break;
}
}
// Dispatch to worker thread
if (i != iend) {
std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(hreq.release(), path, i->handler));
assert(workQueue);
if (workQueue->Enqueue(item.get()))
item.release(); /* if true, queue took ownership */
else
item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded");
} else {
hreq->WriteReply(HTTP_NOTFOUND);
}
}
/** Event dispatcher thread */
static void ThreadHTTP(struct event_base* base, struct evhttp* http)
{
RenameThread("bitcoin-http");
LogPrint("http", "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
LogPrint("http", "Exited http event loop\n");
}
/** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp* http)
{
int defaultPort = GetArg("-rpcport", BaseParams().RPCPort());
int nBound = 0;
std::vector<std::pair<std::string, uint16_t> > endpoints;
// Determine what addresses to bind to
if (!mapArgs.count("-rpcallowip")) { // Default to loopback if not allowing external IPs
endpoints.push_back(std::make_pair("::1", defaultPort));
endpoints.push_back(std::make_pair("127.0.0.1", defaultPort));
if (mapArgs.count("-rpcbind")) {
LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
}
} else if (mapArgs.count("-rpcbind")) { // Specific bind address
const std::vector<std::string>& vbind = mapMultiArgs["-rpcbind"];
for (std::vector<std::string>::const_iterator i = vbind.begin(); i != vbind.end(); ++i) {
int port = defaultPort;
std::string host;
SplitHostPort(*i, port, host);
endpoints.push_back(std::make_pair(host, port));
}
} else { // No specific bind address specified, bind to any
endpoints.push_back(std::make_pair("::", defaultPort));
endpoints.push_back(std::make_pair("0.0.0.0", defaultPort));
}
// Bind addresses
for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
LogPrint("http", "Binding RPC on address %s port %i\n", i->first, i->second);
if (evhttp_bind_socket(http, i->first.empty() ? NULL : i->first.c_str(), i->second) == 0) {
nBound += 1;
} else {
LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
}
}
return nBound > 0;
}
/** Simple wrapper to set thread name and run work queue */
static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
{
RenameThread("bitcoin-httpworker");
queue->Run();
}
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
#ifndef EVENT_LOG_WARN
// EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed.
# define EVENT_LOG_WARN _EVENT_LOG_WARN
#endif
if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
LogPrintf("libevent: %s\n", msg);
else
LogPrint("libevent", "libevent: %s\n", msg);
}
bool InitHTTPServer()
{
struct evhttp* http = 0;
struct event_base* base = 0;
if (!InitHTTPAllowList())
return false;
if (GetBoolArg("-rpcssl", false)) {
uiInterface.ThreadSafeMessageBox(
"SSL mode for RPC (-rpcssl) is no longer supported.",
"", CClientUIInterface::MSG_ERROR);
return false;
}
// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
#if LIBEVENT_VERSION_NUMBER >= 0x02010100
// If -debug=libevent, set full libevent debugging.
// Otherwise, disable all libevent debugging.
if (LogAcceptCategory("libevent"))
event_enable_debug_logging(EVENT_DBG_ALL);
else
event_enable_debug_logging(EVENT_DBG_NONE);
#endif
#ifdef WIN32
evthread_use_windows_threads();
#else
evthread_use_pthreads();
#endif
base = event_base_new(); // XXX RAII
if (!base) {
LogPrintf("Couldn't create an event_base: exiting\n");
return false;
}
/* Create a new evhttp object to handle requests. */
http = evhttp_new(base); // XXX RAII
if (!http) {
LogPrintf("couldn't create evhttp. Exiting.\n");
event_base_free(base);
return false;
}
evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
evhttp_set_max_body_size(http, MAX_SIZE);
evhttp_set_gencb(http, http_request_cb, NULL);
if (!HTTPBindAddresses(http)) {
LogPrintf("Unable to bind any endpoint for RPC server\n");
evhttp_free(http);
event_base_free(base);
return false;
}
LogPrint("http", "Initialized HTTP server\n");
int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
workQueue = new WorkQueue<HTTPClosure>(workQueueDepth);
eventBase = base;
eventHTTP = http;
return true;
}
bool StartHTTPServer(boost::thread_group& threadGroup)
{
LogPrint("http", "Starting HTTP server\n");
int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
threadGroup.create_thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP));
for (int i = 0; i < rpcThreads; i++)
threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue));
return true;
}
void InterruptHTTPServer()
{
LogPrint("http", "Interrupting HTTP server\n");
if (eventBase)
event_base_loopbreak(eventBase);
if (workQueue)
workQueue->Interrupt();
}
void StopHTTPServer()
{
LogPrint("http", "Stopping HTTP server\n");
delete workQueue;
if (eventHTTP) {
evhttp_free(eventHTTP);
eventHTTP = 0;
}
if (eventBase) {
event_base_free(eventBase);
eventBase = 0;
}
}
struct event_base* EventBase()
{
return eventBase;
}
static void httpevent_callback_fn(evutil_socket_t, short, void* data)
{
// Static handler: simply call inner handler
HTTPEvent *self = ((HTTPEvent*)data);
self->handler();
if (self->deleteWhenTriggered)
delete self;
}
HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler):
deleteWhenTriggered(deleteWhenTriggered), handler(handler)
{
ev = event_new(base, -1, 0, httpevent_callback_fn, this);
assert(ev);
}
HTTPEvent::~HTTPEvent()
{
event_free(ev);
}
void HTTPEvent::trigger(struct timeval* tv)
{
if (tv == NULL)
event_active(ev, 0, 0); // immediately trigger event in main thread
else
evtimer_add(ev, tv); // trigger after timeval passed
}
HTTPRequest::HTTPRequest(struct evhttp_request* req) : req(req),
replySent(false)
{
}
HTTPRequest::~HTTPRequest()
{
if (!replySent) {
// Keep track of whether reply was sent to avoid request leaks
LogPrintf("%s: Unhandled request\n", __func__);
WriteReply(HTTP_INTERNAL, "Unhandled request");
}
// evhttpd cleans up the request, as long as a reply was sent.
}
std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr)
{
const struct evkeyvalq* headers = evhttp_request_get_input_headers(req);
assert(headers);
const char* val = evhttp_find_header(headers, hdr.c_str());
if (val)
return std::make_pair(true, val);
else
return std::make_pair(false, "");
}
std::string HTTPRequest::ReadBody()
{
struct evbuffer* buf = evhttp_request_get_input_buffer(req);
if (!buf)
return "";
size_t size = evbuffer_get_length(buf);
/** Trivial implementation: if this is ever a performance bottleneck,
* internal copying can be avoided in multi-segment buffers by using
* evbuffer_peek and an awkward loop. Though in that case, it'd be even
* better to not copy into an intermediate string but use a stream
* abstraction to consume the evbuffer on the fly in the parsing algorithm.
*/
const char* data = (const char*)evbuffer_pullup(buf, size);
if (!data) // returns NULL in case of empty buffer
return "";
std::string rv(data, size);
evbuffer_drain(buf, size);
return rv;
}
void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
{
struct evkeyvalq* headers = evhttp_request_get_output_headers(req);
assert(headers);
evhttp_add_header(headers, hdr.c_str(), value.c_str());
}
/** Closure sent to main thread to request a reply to be sent to
* a HTTP request.
* Replies must be sent in the main loop in the main http thread,
* this cannot be done from worker threads.
*/
void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
{
assert(!replySent && req);
// Send event to main http thread to send reply message
struct evbuffer* evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent* ev = new HTTPEvent(eventBase, true,
boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL));
ev->trigger(0);
replySent = true;
req = 0; // transferred back to main thread
}
CService HTTPRequest::GetPeer()
{
evhttp_connection* con = evhttp_request_get_connection(req);
CService peer;
if (con) {
// evhttp retains ownership over returned address string
const char* address = "";
uint16_t port = 0;
evhttp_connection_get_peer(con, (char**)&address, &port);
peer = CService(address, port);
}
return peer;
}
std::string HTTPRequest::GetURI()
{
return evhttp_request_get_uri(req);
}
HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod()
{
switch (evhttp_request_get_command(req)) {
case EVHTTP_REQ_GET:
return GET;
break;
case EVHTTP_REQ_POST:
return POST;
break;
case EVHTTP_REQ_HEAD:
return HEAD;
break;
case EVHTTP_REQ_PUT:
return PUT;
break;
default:
return UNKNOWN;
break;
}
}
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
{
LogPrint("http", "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler));
}
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
{
std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
for (; i != iend; ++i)
if (i->prefix == prefix && i->exactMatch == exactMatch)
break;
if (i != iend)
{
LogPrint("http", "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
pathHandlers.erase(i);
}
}

149
src/httpserver.h Normal file
View File

@@ -0,0 +1,149 @@
// Copyright (c) 2015 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_HTTPSERVER_H
#define BITCOIN_HTTPSERVER_H
#include <string>
#include <stdint.h>
#include <boost/thread.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/function.hpp>
static const int DEFAULT_HTTP_THREADS=4;
static const int DEFAULT_HTTP_WORKQUEUE=16;
static const int DEFAULT_HTTP_SERVER_TIMEOUT=30;
struct evhttp_request;
struct event_base;
class CService;
class HTTPRequest;
/** Initialize HTTP server.
* Call this before RegisterHTTPHandler or EventBase().
*/
bool InitHTTPServer();
/** Start HTTP server.
* This is separate from InitHTTPServer to give users race-condition-free time
* to register their handlers between InitHTTPServer and StartHTTPServer.
*/
bool StartHTTPServer(boost::thread_group& threadGroup);
/** Interrupt HTTP server threads */
void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();
/** Handler for requests to a certain HTTP path */
typedef boost::function<void(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
* If multiple handlers match a prefix, the first-registered one will
* be invoked.
*/
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler);
/** Unregister handler for prefix */
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch);
/** Return evhttp event base. This can be used by submodules to
* queue timers or custom events.
*/
struct event_base* EventBase();
/** In-flight HTTP request.
* Thin C++ wrapper around evhttp_request.
*/
class HTTPRequest
{
private:
struct evhttp_request* req;
bool replySent;
public:
HTTPRequest(struct evhttp_request* req);
~HTTPRequest();
enum RequestMethod {
UNKNOWN,
GET,
POST,
HEAD,
PUT
};
/** Get requested URI.
*/
std::string GetURI();
/** Get CService (address:ip) for the origin of the http request.
*/
CService GetPeer();
/** Get request method.
*/
RequestMethod GetRequestMethod();
/**
* Get the request header specified by hdr, or an empty string.
* Return an pair (isPresent,string).
*/
std::pair<bool, std::string> GetHeader(const std::string& hdr);
/**
* Read request body.
*
* @note As this consumes the underlying buffer, call this only once.
* Repeated calls will return an empty string.
*/
std::string ReadBody();
/**
* Write output header.
*
* @note call this before calling WriteErrorReply or Reply.
*/
void WriteHeader(const std::string& hdr, const std::string& value);
/**
* Write HTTP reply.
* nStatus is the HTTP status code to send.
* strReply is the body of the reply. Keep it empty to send a standard message.
*
* @note Can be called only once. As this will give the request back to the
* main thread, do not call any other HTTPRequest methods after calling this.
*/
void WriteReply(int nStatus, const std::string& strReply = "");
};
/** Event handler closure.
*/
class HTTPClosure
{
public:
virtual void operator()() = 0;
virtual ~HTTPClosure() {}
};
/** Event class. This can be used either as an cross-thread trigger or as a timer.
*/
class HTTPEvent
{
public:
/** Create a new event.
* deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
* handler is the handler to call when the event is triggered.
*/
HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function<void(void)>& handler);
~HTTPEvent();
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
* the given time has elapsed.
*/
void trigger(struct timeval* tv);
bool deleteWhenTriggered;
boost::function<void(void)> handler;
private:
struct event* ev;
};
#endif // BITCOIN_HTTPSERVER_H

View File

@@ -11,9 +11,14 @@
#include "crypto/common.h"
#include "addrman.h"
#include "amount.h"
#ifdef ENABLE_MINING
#include "base58.h"
#endif
#include "checkpoints.h"
#include "compat/sanity.h"
#include "consensus/validation.h"
#include "httpserver.h"
#include "httprpc.h"
#include "key.h"
#include "main.h"
#include "metrics.h"
@@ -23,6 +28,7 @@
#include "script/standard.h"
#include "scheduler.h"
#include "txdb.h"
#include "torcontrol.h"
#include "ui_interface.h"
#include "util.h"
#include "utilmoneystr.h"
@@ -31,7 +37,6 @@
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#endif
#include <stdint.h>
#include <stdio.h>
@@ -50,6 +55,10 @@
#include "libsnark/common/profiling.hpp"
#if ENABLE_ZMQ
#include "zmq/zmqnotificationinterface.h"
#endif
using namespace std;
extern void ThreadSendAlert();
@@ -61,6 +70,10 @@ CWallet* pwalletMain = NULL;
#endif
bool fFeeEstimatesInitialized = false;
#if ENABLE_ZMQ
static CZMQNotificationInterface* pzmqNotificationInterface = NULL;
#endif
#ifdef WIN32
// Win32 LevelDB doesn't use file descriptors, and the ones used for
// accessing block files don't count towards the fd_set size limit
@@ -90,7 +103,7 @@ CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group
// created by AppInit() or the Qt main() function.
// created by AppInit().
//
// A clean exit happens when StartShutdown() or the SIGTERM
// signal handler sets fRequestShutdown, which triggers
@@ -106,10 +119,6 @@ CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
// before adding any threads to the threadGroup, so .join_all() returns
// immediately and the parent exits from main().
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// fRequestShutdown getting set, and then does the normal Qt
// shutdown thing.
//
std::atomic<bool> fRequestShutdown(false);
@@ -145,6 +154,16 @@ public:
static CCoinsViewDB *pcoinsdbview = NULL;
static CCoinsViewErrorCatcher *pcoinscatcher = NULL;
void Interrupt(boost::thread_group& threadGroup)
{
InterruptHTTPServer();
InterruptHTTPRPC();
InterruptRPC();
InterruptREST();
InterruptTorControl();
threadGroup.interrupt_all();
}
void Shutdown()
{
LogPrintf("%s: In progress...\n", __func__);
@@ -159,13 +178,24 @@ void Shutdown()
/// module was initialized.
RenameThread("zcash-shutoff");
mempool.AddTransactionsUpdated(1);
StopRPCThreads();
StopHTTPRPC();
StopREST();
StopRPC();
StopHTTPServer();
#ifdef ENABLE_WALLET
if (pwalletMain)
pwalletMain->Flush(false);
#endif
#ifdef ENABLE_MINING
#ifdef ENABLE_WALLET
GenerateBitcoins(false, NULL, 0);
#else
GenerateBitcoins(false, 0);
#endif
#endif
StopNode();
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
if (fFeeEstimatesInitialized)
@@ -197,6 +227,15 @@ void Shutdown()
if (pwalletMain)
pwalletMain->Flush(true);
#endif
#if ENABLE_ZMQ
if (pzmqNotificationInterface) {
UnregisterValidationInterface(pzmqNotificationInterface);
delete pzmqNotificationInterface;
pzmqNotificationInterface = NULL;
}
#endif
#ifndef WIN32
try {
boost::filesystem::remove(GetPidFile());
@@ -319,7 +358,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address"));
strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), 0));
strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"));
strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), 125));
strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION));
strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS));
strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), 5000));
strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), 1000));
strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"));
@@ -330,9 +370,11 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), 1));
strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
strUsage += HelpMessageOpt("-torpassword=<pass>", _("Tor control port password (default: empty)"));
#ifdef USE_UPNP
#if USE_UPNP
strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening)"));
strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)"));
#else
strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0));
#endif
@@ -362,13 +404,20 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));
strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
" " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"));
#endif
#if ENABLE_ZMQ
strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
strUsage += HelpMessageOpt("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"));
strUsage += HelpMessageOpt("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"));
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
if (showDebug)
{
strUsage += HelpMessageOpt("-checkpoints", strprintf("Only accept block chain matching built-in checkpoints (default: %u)", 1));
strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", 1));
strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush database activity from memory pool to disk log every <n> megabytes (default: %u)", 100));
strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", 0));
strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", 0));
@@ -377,17 +426,11 @@ 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, 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";
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, "
"rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these
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 + ".");
#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));
strUsage += HelpMessageOpt("-equihashsolver=<name>", _("Specify the Equihash solver to be used if enabled (default: \"default\")"));
#endif
strUsage += HelpMessageOpt("-experimentalfeatures", _("Enable use of experimental features"));
strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)"));
strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), 0));
strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), 1));
@@ -417,6 +460,24 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-blockminsize=<n>", strprintf(_("Set minimum block size in bytes (default: %u)"), 0));
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));
if (GetBoolArg("-help-debug", false))
strUsage += HelpMessageOpt("-blockversion=<n>", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION));
#ifdef ENABLE_MINING
strUsage += HelpMessageGroup(_("Mining options:"));
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));
strUsage += HelpMessageOpt("-equihashsolver=<name>", _("Specify the Equihash solver to be used if enabled (default: \"default\")"));
strUsage += HelpMessageOpt("-mineraddress=<addr>", _("Send mined coins to a specific single address"));
strUsage += HelpMessageOpt("-minetolocalwallet", strprintf(
_("Require that mined blocks use a coinbase address in the local wallet (default: %u)"),
#ifdef ENABLE_WALLET
1
#else
0
#endif
));
#endif
strUsage += HelpMessageGroup(_("RPC server options:"));
strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands"));
@@ -426,33 +487,16 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), 8232, 18232));
strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"));
strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), 4));
// TODO #1856: Re-enable support for persistent connections.
// Disabled to avoid rpc deadlock #1680, until we backport upstream changes which replace boost::asio with libevent, or another solution is implemented.
//strUsage += HelpMessageOpt("-rpckeepalive", strprintf(_("RPC support for HTTP persistent connections (default: %d)"), 1));
strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS));
if (showDebug) {
strUsage += HelpMessageOpt("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE));
strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT));
}
// Disabled until we can lock notes and also tune performance of libsnark which by default uses multiple threads
//strUsage += HelpMessageOpt("-rpcasyncthreads=<n>", strprintf(_("Set the number of threads to service Async RPC calls (default: %d)"), 1));
strUsage += HelpMessageGroup(_("RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions)"));
strUsage += HelpMessageOpt("-rpcssl", _("Use OpenSSL (https) for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcsslcertificatechainfile=<file.cert>", strprintf(_("Server certificate file (default: %s)"), "server.cert"));
strUsage += HelpMessageOpt("-rpcsslprivatekeyfile=<file.pem>", strprintf(_("Server private key (default: %s)"), "server.pem"));
strUsage += HelpMessageOpt("-rpcsslciphers=<ciphers>", strprintf(_("Acceptable ciphers (default: %s)"), "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"));
if (mode == HMM_BITCOIN_QT)
{
strUsage += HelpMessageGroup(_("UI Options:"));
if (showDebug) {
strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", "Allow self signed root certificates (default: 0)");
}
strUsage += HelpMessageOpt("-choosedatadir", _("Choose data directory on startup (default: 0)"));
strUsage += HelpMessageOpt("-lang=<lang>", _("Set language, for example \"de_DE\" (default: system locale)"));
strUsage += HelpMessageOpt("-min", _("Start minimized"));
strUsage += HelpMessageOpt("-rootcertificates=<file>", _("Set SSL root certificates for payment request (default: -system-)"));
strUsage += HelpMessageOpt("-splash", _("Show splash screen on startup (default: 1)"));
} else if (mode == HMM_BITCOIND) {
if (mode == HMM_BITCOIND) {
strUsage += HelpMessageGroup(_("Metrics Options (only if -daemon and -printtoconsole are not set):"));
strUsage += HelpMessageOpt("-showmetrics", _("Show metrics on stdout (default: 1 if running in a console, 0 otherwise)"));
strUsage += HelpMessageOpt("-metricsui", _("Set to 1 for a persistent metrics screen, 0 for sequential metrics output (default: 1 if running in a console, 0 otherwise)"));
@@ -570,7 +614,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
// -loadblock=
BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) {
BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) {
FILE *file = fopen(path.string().c_str(), "rb");
if (file) {
CImportingNow imp;
@@ -638,6 +682,23 @@ static void ZC_LoadParams()
pzcashParams->setProvingKeyPath(pk_path.string());
}
bool AppInitServers(boost::thread_group& threadGroup)
{
RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer())
return false;
if (!StartRPC())
return false;
if (!StartHTTPRPC())
return false;
if (GetBoolArg("-rest", false) && !StartREST())
return false;
if (!StartHTTPServer(threadGroup))
return false;
return true;
}
/** Initialize bitcoin.
* @pre Parameters should be parsed and config file should be read.
*/
@@ -667,17 +728,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD);
PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE);
// Initialize Windows Sockets
WSADATA wsadata;
int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
{
return InitError(strprintf("Error: Winsock library failed to start (WSAStartup returned error %d)", ret));
}
#endif
#ifndef WIN32
if (!SetupNetworking())
return InitError("Error: Initializing networking failed");
#ifndef WIN32
if (GetBoolArg("-sysperms", false)) {
#ifdef ENABLE_WALLET
if (!GetBoolArg("-disablewallet", false))
@@ -702,20 +758,31 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
sa_hup.sa_flags = 0;
sigaction(SIGHUP, &sa_hup, NULL);
#if defined (__SVR4) && defined (__sun)
// ignore SIGPIPE on Solaris
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#endif
#endif
// ********************************************************* Step 2: parameter interactions
const CChainParams& chainparams = Params();
// Set this early so that experimental features are correctly enabled/disabled
fExperimentalMode = GetBoolArg("-experimentalfeatures", false);
// Fail early if user has set experimental options without the global flag
if (!fExperimentalMode) {
if (mapArgs.count("-developerencryptwallet")) {
return InitError(_("Wallet encryption requires -experimentalfeatures."));
}
}
// Set this early so that parameter interactions go to console
fPrintToConsole = GetBoolArg("-printtoconsole", false);
fLogTimestamps = GetBoolArg("-logtimestamps", true);
fLogIPs = GetBoolArg("-logips", false);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("Zcash version %s (%s)\n", FormatFullVersion(), CLIENT_DATE);
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (mapArgs.count("-bind")) {
@@ -754,6 +821,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
}
if (mapArgs.count("-externalip")) {
@@ -776,7 +845,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
nMaxConnections = GetArg("-maxconnections", 125);
nMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
@@ -807,6 +876,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end())
fDebug = false;
// Special case: if debug=zrpcunsafe, implies debug=zrpc, so add it to debug categories
if (find(categories.begin(), categories.end(), string("zrpcunsafe")) != categories.end()) {
if (find(categories.begin(), categories.end(), string("zrpc")) == categories.end()) {
LogPrintf("%s: parameter interaction: setting -debug=zrpcunsafe -> -debug=zrpc\n", __func__);
vector<string>& v = mapMultiArgs["-debug"];
v.push_back("zrpc");
}
}
// Check for -debugnet
if (GetBoolArg("-debugnet", false))
InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net."));
@@ -922,6 +1000,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS);
// Option to startup with mocktime set (used for regression testing):
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
#ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) {
CBitcoinAddress addr;
if (!addr.SetString(mapArgs["-mineraddress"])) {
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a transparent address)"),
mapArgs["-mineraddress"]));
}
}
#endif
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
// Initialize libsodium
@@ -962,6 +1054,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
ShrinkDebugFile();
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("Komodo version %s (%s)\n", FormatFullVersion(), CLIENT_DATE);
if (fPrintToDebugLog)
OpenDebugLog();
LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION));
#ifdef ENABLE_WALLET
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
@@ -1011,9 +1106,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (fServer)
{
uiInterface.InitMessage.connect(SetRPCWarmupStatus);
RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand);
StartRPCThreads();
if (!AppInitServers(threadGroup))
return InitError(_("Unable to start HTTP server. See debug log for details."));
}
int64_t nStart;
@@ -1026,15 +1120,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::string warningString;
std::string errorString;
if (!CWallet::Verify(strWalletFile, warningString, errorString))
return false;
if (!warningString.empty())
InitWarning(warningString);
if (!errorString.empty())
return InitError(warningString);
} // (!fDisableWallet)
#endif // ENABLE_WALLET
// ********************************************************* Step 6: network initialization
@@ -1043,7 +1137,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (mapArgs.count("-onlynet")) {
std::set<enum Network> nets;
BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) {
BOOST_FOREACH(const std::string& snet, mapMultiArgs["-onlynet"]) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
@@ -1065,31 +1159,37 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
proxyType addrProxy;
bool fProxy = false;
if (mapArgs.count("-proxy")) {
addrProxy = proxyType(CService(mapArgs["-proxy"], 9050), GetBoolArg("-proxyrandomize", true));
bool proxyRandomize = GetBoolArg("-proxyrandomize", true);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
std::string proxyArg = GetArg("-proxy", "");
SetLimited(NET_TOR);
if (proxyArg != "" && proxyArg != "0") {
proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize);
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"]));
return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg));
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_TOR, addrProxy);
SetNameProxy(addrProxy);
fProxy = true;
SetLimited(NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later
}
// -onion can override normal proxy, -noonion disables connecting to .onion entirely
if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") &&
(fProxy || mapArgs.count("-onion"))) {
proxyType addrOnion;
if (!mapArgs.count("-onion"))
addrOnion = addrProxy;
else
addrOnion = proxyType(CService(mapArgs["-onion"], 9050), GetBoolArg("-proxyrandomize", true));
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs["-onion"]));
SetProxy(NET_TOR, addrOnion);
SetReachable(NET_TOR);
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
// -noonion (or -onion=0) disables connecting to .onion entirely
// An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none)
std::string onionArg = GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetLimited(NET_TOR); // set onions as unreachable
} else {
proxyType addrOnion = proxyType(CService(onionArg, 9050), proxyRandomize);
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg));
SetProxy(NET_TOR, addrOnion);
SetLimited(NET_TOR, false);
}
}
// see Step 2: parameter interactions for more information about these
@@ -1100,13 +1200,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
bool fBound = false;
if (fListen) {
if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-bind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) {
BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-whitebind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, 0, false))
return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind));
@@ -1126,7 +1226,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
if (mapArgs.count("-externalip")) {
BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) {
BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-externalip"]) {
CService addrLocal(strAddr, GetListenPort(), fNameLookup);
if (!addrLocal.IsValid())
return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr));
@@ -1134,9 +1234,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
}
BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"])
BOOST_FOREACH(const std::string& strDest, mapMultiArgs["-seednode"])
AddOneShot(strDest);
#if ENABLE_ZMQ
pzmqNotificationInterface = CZMQNotificationInterface::CreateWithArguments(mapArgs);
if (pzmqNotificationInterface) {
RegisterValidationInterface(pzmqNotificationInterface);
}
#endif
// ********************************************************* Step 7: load block chain
fReindex = GetBoolArg("-reindex", false);
@@ -1371,8 +1479,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (fFirstRun)
{
// Create new keyUser and set as default key
RandAddSeedPerfmon();
CPubKey newDefaultKey;
if (pwalletMain->GetKeyFromPool(newDefaultKey)) {
pwalletMain->SetDefaultKey(newDefaultKey);
@@ -1444,6 +1550,33 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("No wallet support compiled in!\n");
#endif // !ENABLE_WALLET
#ifdef ENABLE_MINING
#ifndef ENABLE_WALLET
if (GetBoolArg("-minetolocalwallet", false)) {
return InitError(_("Zcash was not built with wallet support. Set -minetolocalwallet=0 to use -mineraddress, or rebuild Zcash with wallet support."));
}
if (GetArg("-mineraddress", "").empty() && GetBoolArg("-gen", false)) {
return InitError(_("Zcash was not built with wallet support. Set -mineraddress, or rebuild Zcash with wallet support."));
}
#endif // !ENABLE_WALLET
if (mapArgs.count("-mineraddress")) {
#ifdef ENABLE_WALLET
bool minerAddressInLocalWallet = false;
if (pwalletMain) {
// Address has alreday been validated
CBitcoinAddress addr(mapArgs["-mineraddress"]);
CKeyID keyID;
addr.GetKeyID(keyID);
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
}
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
return InitError(_("-mineraddress is not in the local wallet. Either use a local address, or set -minetolocalwallet=0"));
}
#endif // ENABLE_WALLET
}
#endif // ENABLE_MINING
// ********************************************************* Step 9: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore prune
@@ -1472,7 +1605,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
std::vector<boost::filesystem::path> vImportFiles;
if (mapArgs.count("-loadblock"))
{
BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"])
BOOST_FOREACH(const std::string& strFile, mapMultiArgs["-loadblock"])
vImportFiles.push_back(strFile);
}
threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles));
@@ -1490,8 +1623,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!strErrors.str().empty())
return InitError(strErrors.str());
RandAddSeedPerfmon();
//// debug print
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
LogPrintf("nBestHeight = %d\n", chainActive.Height());
@@ -1501,6 +1632,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler);
StartNode(threadGroup, scheduler);
// Monitor the chain, and alert if we get blocks much quicker or slower than expected
@@ -1509,10 +1643,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
boost::ref(cs_main), boost::cref(pindexBestHeader), nPowTargetSpacing);
scheduler.scheduleEvery(f, nPowTargetSpacing);
#ifdef ENABLE_WALLET
#ifdef ENABLE_MINING
// Generate coins in the background
if (pwalletMain)
#ifdef ENABLE_WALLET
if (pwalletMain || !GetArg("-mineraddress", "").empty())
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1));
#else
GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1));
#endif
#endif
// ********************************************************* Step 11: finished

View File

@@ -23,13 +23,14 @@ extern ZCJoinSplit* pzcashParams;
void StartShutdown();
bool ShutdownRequested();
/** Interrupt threads */
void Interrupt(boost::thread_group& threadGroup);
void Shutdown();
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler);
/** The help message mode determines what help message to show */
enum HelpMessageMode {
HMM_BITCOIND,
HMM_BITCOIN_QT
HMM_BITCOIND
};
/** Help for options shared between UI and daemon (for -help) */

View File

@@ -1,24 +0,0 @@
The MIT License
Copyright (c) 2007 - 2009 John W. Wilkinson
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,18 +0,0 @@
#ifndef JSON_SPIRIT
#define JSON_SPIRIT
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_value.h"
#include "json_spirit_reader.h"
#include "json_spirit_writer.h"
#include "json_spirit_utils.h"
#endif

View File

@@ -1,54 +0,0 @@
#ifndef JSON_SPIRIT_ERROR_POSITION
#define JSON_SPIRIT_ERROR_POSITION
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include <string>
namespace json_spirit
{
// An Error_position exception is thrown by the "read_or_throw" functions below on finding an error.
// Note the "read_or_throw" functions are around 3 times slower than the standard functions "read"
// functions that return a bool.
//
struct Error_position
{
Error_position();
Error_position( unsigned int line, unsigned int column, const std::string& reason );
bool operator==( const Error_position& lhs ) const;
unsigned int line_;
unsigned int column_;
std::string reason_;
};
inline Error_position::Error_position()
: line_( 0 )
, column_( 0 )
{
}
inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason )
: line_( line )
, column_( column )
, reason_( reason )
{
}
inline bool Error_position::operator==( const Error_position& lhs ) const
{
if( this == &lhs ) return true;
return ( reason_ == lhs.reason_ ) &&
( line_ == lhs.line_ ) &&
( column_ == lhs.column_ );
}
}
#endif

View File

@@ -1,137 +0,0 @@
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#include "json_spirit_reader.h"
#include "json_spirit_reader_template.h"
using namespace json_spirit;
bool json_spirit::read( const std::string& s, Value& value )
{
return read_string( s, value );
}
void json_spirit::read_or_throw( const std::string& s, Value& value )
{
read_string_or_throw( s, value );
}
bool json_spirit::read( std::istream& is, Value& value )
{
return read_stream( is, value );
}
void json_spirit::read_or_throw( std::istream& is, Value& value )
{
read_stream_or_throw( is, value );
}
bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value )
{
return read_range( begin, end, value );
}
void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value )
{
begin = read_range_or_throw( begin, end, value );
}
#ifndef BOOST_NO_STD_WSTRING
bool json_spirit::read( const std::wstring& s, wValue& value )
{
return read_string( s, value );
}
void json_spirit::read_or_throw( const std::wstring& s, wValue& value )
{
read_string_or_throw( s, value );
}
bool json_spirit::read( std::wistream& is, wValue& value )
{
return read_stream( is, value );
}
void json_spirit::read_or_throw( std::wistream& is, wValue& value )
{
read_stream_or_throw( is, value );
}
bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value )
{
return read_range( begin, end, value );
}
void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value )
{
begin = read_range_or_throw( begin, end, value );
}
#endif
bool json_spirit::read( const std::string& s, mValue& value )
{
return read_string( s, value );
}
void json_spirit::read_or_throw( const std::string& s, mValue& value )
{
read_string_or_throw( s, value );
}
bool json_spirit::read( std::istream& is, mValue& value )
{
return read_stream( is, value );
}
void json_spirit::read_or_throw( std::istream& is, mValue& value )
{
read_stream_or_throw( is, value );
}
bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value )
{
return read_range( begin, end, value );
}
void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value )
{
begin = read_range_or_throw( begin, end, value );
}
#ifndef BOOST_NO_STD_WSTRING
bool json_spirit::read( const std::wstring& s, wmValue& value )
{
return read_string( s, value );
}
void json_spirit::read_or_throw( const std::wstring& s, wmValue& value )
{
read_string_or_throw( s, value );
}
bool json_spirit::read( std::wistream& is, wmValue& value )
{
return read_stream( is, value );
}
void json_spirit::read_or_throw( std::wistream& is, wmValue& value )
{
read_stream_or_throw( is, value );
}
bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value )
{
return read_range( begin, end, value );
}
void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value )
{
begin = read_range_or_throw( begin, end, value );
}
#endif

View File

@@ -1,62 +0,0 @@
#ifndef JSON_SPIRIT_READER
#define JSON_SPIRIT_READER
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_value.h"
#include "json_spirit_error_position.h"
#include <iostream>
namespace json_spirit
{
// functions to reads a JSON values
bool read( const std::string& s, Value& value );
bool read( std::istream& is, Value& value );
bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value );
void read_or_throw( const std::string& s, Value& value );
void read_or_throw( std::istream& is, Value& value );
void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value );
#ifndef BOOST_NO_STD_WSTRING
bool read( const std::wstring& s, wValue& value );
bool read( std::wistream& is, wValue& value );
bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value );
void read_or_throw( const std::wstring& s, wValue& value );
void read_or_throw( std::wistream& is, wValue& value );
void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value );
#endif
bool read( const std::string& s, mValue& value );
bool read( std::istream& is, mValue& value );
bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value );
void read_or_throw( const std::string& s, mValue& value );
void read_or_throw( std::istream& is, mValue& value );
void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value );
#ifndef BOOST_NO_STD_WSTRING
bool read( const std::wstring& s, wmValue& value );
bool read( std::wistream& is, wmValue& value );
bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value );
void read_or_throw( const std::wstring& s, wmValue& value );
void read_or_throw( std::wistream& is, wmValue& value );
void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value );
#endif
}
#endif

View File

@@ -1,618 +0,0 @@
#ifndef JSON_SPIRIT_READER_TEMPLATE
#define JSON_SPIRIT_READER_TEMPLATE
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#include "json_spirit_value.h"
#include "json_spirit_error_position.h"
//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION >= 103800
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/spirit/include/classic_escape_char.hpp>
#include <boost/spirit/include/classic_multi_pass.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#define spirit_namespace boost::spirit::classic
#else
#include <boost/spirit/core.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/utility/escape_char.hpp>
#include <boost/spirit/iterator/multi_pass.hpp>
#include <boost/spirit/iterator/position_iterator.hpp>
#define spirit_namespace boost::spirit
#endif
namespace json_spirit
{
const spirit_namespace::int_parser < int64_t > int64_p = spirit_namespace::int_parser < int64_t >();
const spirit_namespace::uint_parser< uint64_t > uint64_p = spirit_namespace::uint_parser< uint64_t >();
template< class Iter_type >
bool is_eq( Iter_type first, Iter_type last, const char* c_str )
{
for( Iter_type i = first; i != last; ++i, ++c_str )
{
if( *c_str == 0 ) return false;
if( *i != *c_str ) return false;
}
return true;
}
template< class Char_type >
Char_type hex_to_num( const Char_type c )
{
if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0';
if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10;
if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10;
return 0;
}
template< class Char_type, class Iter_type >
Char_type hex_str_to_char( Iter_type& begin )
{
const Char_type c1( *( ++begin ) );
const Char_type c2( *( ++begin ) );
return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 );
}
template< class Char_type, class Iter_type >
Char_type unicode_str_to_char( Iter_type& begin )
{
const Char_type c1( *( ++begin ) );
const Char_type c2( *( ++begin ) );
const Char_type c3( *( ++begin ) );
const Char_type c4( *( ++begin ) );
return ( hex_to_num( c1 ) << 12 ) +
( hex_to_num( c2 ) << 8 ) +
( hex_to_num( c3 ) << 4 ) +
hex_to_num( c4 );
}
template< class String_type >
void append_esc_char_and_incr_iter( String_type& s,
typename String_type::const_iterator& begin,
typename String_type::const_iterator end )
{
typedef typename String_type::value_type Char_type;
const Char_type c2( *begin );
switch( c2 )
{
case 't': s += '\t'; break;
case 'b': s += '\b'; break;
case 'f': s += '\f'; break;
case 'n': s += '\n'; break;
case 'r': s += '\r'; break;
case '\\': s += '\\'; break;
case '/': s += '/'; break;
case '"': s += '"'; break;
case 'x':
{
if( end - begin >= 3 ) // expecting "xHH..."
{
s += hex_str_to_char< Char_type >( begin );
}
break;
}
case 'u':
{
if( end - begin >= 5 ) // expecting "uHHHH..."
{
s += unicode_str_to_char< Char_type >( begin );
}
break;
}
}
}
template< class String_type >
String_type substitute_esc_chars( typename String_type::const_iterator begin,
typename String_type::const_iterator end )
{
typedef typename String_type::const_iterator Iter_type;
if( end - begin < 2 ) return String_type( begin, end );
String_type result;
result.reserve( end - begin );
const Iter_type end_minus_1( end - 1 );
Iter_type substr_start = begin;
Iter_type i = begin;
for( ; i < end_minus_1; ++i )
{
if( *i == '\\' )
{
result.append( substr_start, i );
++i; // skip the '\'
append_esc_char_and_incr_iter( result, i, end );
substr_start = i + 1;
}
}
result.append( substr_start, end );
return result;
}
template< class String_type >
String_type get_str_( typename String_type::const_iterator begin,
typename String_type::const_iterator end )
{
assert( end - begin >= 2 );
typedef typename String_type::const_iterator Iter_type;
Iter_type str_without_quotes( ++begin );
Iter_type end_without_quotes( --end );
return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes );
}
inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end )
{
return get_str_< std::string >( begin, end );
}
inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end )
{
return get_str_< std::wstring >( begin, end );
}
template< class String_type, class Iter_type >
String_type get_str( Iter_type begin, Iter_type end )
{
const String_type tmp( begin, end ); // convert multipass iterators to string iterators
return get_str( tmp.begin(), tmp.end() );
}
// this class's methods get called by the spirit parse resulting
// in the creation of a JSON object or array
//
// NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator
//
template< class Value_type, class Iter_type >
class Semantic_actions
{
public:
typedef typename Value_type::Config_type Config_type;
typedef typename Config_type::String_type String_type;
typedef typename Config_type::Object_type Object_type;
typedef typename Config_type::Array_type Array_type;
typedef typename String_type::value_type Char_type;
Semantic_actions( Value_type& value )
: value_( value )
, current_p_( 0 )
{
}
void begin_obj( Char_type c )
{
assert( c == '{' );
begin_compound< Object_type >();
}
void end_obj( Char_type c )
{
assert( c == '}' );
end_compound();
}
void begin_array( Char_type c )
{
assert( c == '[' );
begin_compound< Array_type >();
}
void end_array( Char_type c )
{
assert( c == ']' );
end_compound();
}
void new_name( Iter_type begin, Iter_type end )
{
assert( current_p_->type() == obj_type );
name_ = get_str< String_type >( begin, end );
}
void new_str( Iter_type begin, Iter_type end )
{
add_to_current( get_str< String_type >( begin, end ) );
}
void new_true( Iter_type begin, Iter_type end )
{
assert( is_eq( begin, end, "true" ) );
add_to_current( true );
}
void new_false( Iter_type begin, Iter_type end )
{
assert( is_eq( begin, end, "false" ) );
add_to_current( false );
}
void new_null( Iter_type begin, Iter_type end )
{
assert( is_eq( begin, end, "null" ) );
add_to_current( Value_type() );
}
void new_int( int64_t i )
{
add_to_current( i );
}
void new_uint64( uint64_t ui )
{
add_to_current( ui );
}
void new_real( double d )
{
add_to_current( d );
}
private:
Semantic_actions& operator=( const Semantic_actions& );
// to prevent "assignment operator could not be generated" warning
Value_type* add_first( const Value_type& value )
{
assert( current_p_ == 0 );
value_ = value;
current_p_ = &value_;
return current_p_;
}
template< class Array_or_obj >
void begin_compound()
{
if( current_p_ == 0 )
{
add_first( Array_or_obj() );
}
else
{
// ZCASH: Prevent potential stack overflow by setting a limit on the number of nested compound elements
if (stack_.size() > 128) {
throw std::domain_error("too many nested elements");
}
// ENDZCASH
stack_.push_back( current_p_ );
Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place
current_p_ = add_to_current( new_array_or_obj );
}
}
void end_compound()
{
if( current_p_ != &value_ )
{
current_p_ = stack_.back();
stack_.pop_back();
}
}
Value_type* add_to_current( const Value_type& value )
{
if( current_p_ == 0 )
{
return add_first( value );
}
else if( current_p_->type() == array_type )
{
current_p_->get_array().push_back( value );
return &current_p_->get_array().back();
}
assert( current_p_->type() == obj_type );
return &Config_type::add( current_p_->get_obj(), name_, value );
}
Value_type& value_; // this is the object or array that is being created
Value_type* current_p_; // the child object or array that is currently being constructed
std::vector< Value_type* > stack_; // previous child objects and arrays
String_type name_; // of current name/value pair
};
template< typename Iter_type >
void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason )
{
throw Error_position( i.get_position().line, i.get_position().column, reason );
}
template< typename Iter_type >
void throw_error( Iter_type i, const std::string& reason )
{
throw reason;
}
// the spirit grammer
//
template< class Value_type, class Iter_type >
class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > >
{
public:
typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t;
Json_grammer( Semantic_actions_t& semantic_actions )
: actions_( semantic_actions )
{
}
static void throw_not_value( Iter_type begin, Iter_type end )
{
throw_error( begin, "not a value" );
}
static void throw_not_array( Iter_type begin, Iter_type end )
{
throw_error( begin, "not an array" );
}
static void throw_not_object( Iter_type begin, Iter_type end )
{
throw_error( begin, "not an object" );
}
static void throw_not_pair( Iter_type begin, Iter_type end )
{
throw_error( begin, "not a pair" );
}
static void throw_not_colon( Iter_type begin, Iter_type end )
{
throw_error( begin, "no colon in pair" );
}
static void throw_not_string( Iter_type begin, Iter_type end )
{
throw_error( begin, "not a string" );
}
template< typename ScannerT >
class definition
{
public:
definition( const Json_grammer& self )
{
using namespace spirit_namespace;
typedef typename Value_type::String_type::value_type Char_type;
// first we convert the semantic action class methods to functors with the
// parameter signature expected by spirit
typedef boost::function< void( Char_type ) > Char_action;
typedef boost::function< void( Iter_type, Iter_type ) > Str_action;
typedef boost::function< void( double ) > Real_action;
typedef boost::function< void( int64_t ) > Int_action;
typedef boost::function< void( uint64_t ) > Uint64_action;
Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) );
Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) );
Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) );
Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) );
Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) );
Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) );
Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) );
Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) );
Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) );
Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) );
Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) );
Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) );
// actual grammer
json_
= value_ | eps_p[ &throw_not_value ]
;
value_
= string_[ new_str ]
| number_
| object_
| array_
| str_p( "true" ) [ new_true ]
| str_p( "false" )[ new_false ]
| str_p( "null" ) [ new_null ]
;
object_
= ch_p('{')[ begin_obj ]
>> !members_
>> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] )
;
members_
= pair_ >> *( ',' >> pair_ )
;
pair_
= string_[ new_name ]
>> ( ':' | eps_p[ &throw_not_colon ] )
>> ( value_ | eps_p[ &throw_not_value ] )
;
array_
= ch_p('[')[ begin_array ]
>> !elements_
>> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] )
;
elements_
= value_ >> *( ',' >> value_ )
;
string_
= lexeme_d // this causes white space inside a string to be retained
[
confix_p
(
'"',
*lex_escape_ch_p,
'"'
)
]
;
number_
= strict_real_p[ new_real ]
| int64_p [ new_int ]
| uint64_p [ new_uint64 ]
;
}
spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_;
const spirit_namespace::rule< ScannerT >& start() const { return json_; }
};
private:
Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning
Semantic_actions_t& actions_;
};
template< class Iter_type, class Value_type >
Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
{
Semantic_actions< Value_type, Iter_type > semantic_actions( value );
const spirit_namespace::parse_info< Iter_type > info =
spirit_namespace::parse( begin, end,
Json_grammer< Value_type, Iter_type >( semantic_actions ) >> spirit_namespace::end_p,
spirit_namespace::space_p );
if( !info.hit )
{
throw_error( info.stop, "error" );
}
return info.stop;
}
template< class Iter_type, class Value_type >
void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
{
typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t;
const Posn_iter_t posn_begin( begin, end );
const Posn_iter_t posn_end( end, end );
read_range_or_throw( posn_begin, posn_end, value );
}
template< class Iter_type, class Value_type >
bool read_range( Iter_type& begin, Iter_type end, Value_type& value )
{
try
{
begin = read_range_or_throw( begin, end, value );
return true;
}
catch( ... )
{
return false;
}
}
template< class String_type, class Value_type >
void read_string_or_throw( const String_type& s, Value_type& value )
{
add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value );
}
template< class String_type, class Value_type >
bool read_string( const String_type& s, Value_type& value )
{
typename String_type::const_iterator begin = s.begin();
bool success = read_range( begin, s.end(), value );
return success && begin == s.end();
}
template< class Istream_type >
struct Multi_pass_iters
{
typedef typename Istream_type::char_type Char_type;
typedef std::istream_iterator< Char_type, Char_type > istream_iter;
typedef spirit_namespace::multi_pass< istream_iter > Mp_iter;
Multi_pass_iters( Istream_type& is )
{
is.unsetf( std::ios::skipws );
begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) );
end_ = spirit_namespace::make_multi_pass( istream_iter() );
}
Mp_iter begin_;
Mp_iter end_;
};
template< class Istream_type, class Value_type >
bool read_stream( Istream_type& is, Value_type& value )
{
Multi_pass_iters< Istream_type > mp_iters( is );
return read_range( mp_iters.begin_, mp_iters.end_, value );
}
template< class Istream_type, class Value_type >
void read_stream_or_throw( Istream_type& is, Value_type& value )
{
const Multi_pass_iters< Istream_type > mp_iters( is );
add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value );
}
}
#endif

View File

@@ -1,70 +0,0 @@
#ifndef JSON_SPIRIT_READ_STREAM
#define JSON_SPIRIT_READ_STREAM
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_reader_template.h"
namespace json_spirit
{
// these classes allows you to read multiple top level contiguous values from a stream,
// the normal stream read functions have a bug that prevent multiple top level values
// from being read unless they are separated by spaces
template< class Istream_type, class Value_type >
class Stream_reader
{
public:
Stream_reader( Istream_type& is )
: iters_( is )
{
}
bool read_next( Value_type& value )
{
return read_range( iters_.begin_, iters_.end_, value );
}
private:
typedef Multi_pass_iters< Istream_type > Mp_iters;
Mp_iters iters_;
};
template< class Istream_type, class Value_type >
class Stream_reader_thrower
{
public:
Stream_reader_thrower( Istream_type& is )
: iters_( is )
, posn_begin_( iters_.begin_, iters_.end_ )
, posn_end_( iters_.end_, iters_.end_ )
{
}
void read_next( Value_type& value )
{
posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value );
}
private:
typedef Multi_pass_iters< Istream_type > Mp_iters;
typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t;
Mp_iters iters_;
Posn_iter_t posn_begin_, posn_end_;
};
}
#endif

View File

@@ -1,61 +0,0 @@
#ifndef JSON_SPIRIT_UTILS
#define JSON_SPIRIT_UTILS
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_value.h"
#include <map>
namespace json_spirit
{
template< class Obj_t, class Map_t >
void obj_to_map( const Obj_t& obj, Map_t& mp_obj )
{
mp_obj.clear();
for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i )
{
mp_obj[ i->name_ ] = i->value_;
}
}
template< class Obj_t, class Map_t >
void map_to_obj( const Map_t& mp_obj, Obj_t& obj )
{
obj.clear();
for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i )
{
obj.push_back( typename Obj_t::value_type( i->first, i->second ) );
}
}
typedef std::map< std::string, Value > Mapped_obj;
#ifndef BOOST_NO_STD_WSTRING
typedef std::map< std::wstring, wValue > wMapped_obj;
#endif
template< class Object_type, class String_type >
const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name )
{
for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i )
{
if( i->name_ == name )
{
return i->value_;
}
}
return Object_type::value_type::Value_type::null;
}
}
#endif

View File

@@ -1,8 +0,0 @@
/* Copyright (c) 2007 John W Wilkinson
This source code can be used for any purpose as long as
this comment is retained. */
// json spirit version 2.00
#include "json_spirit_value.h"

View File

@@ -1,534 +0,0 @@
#ifndef JSON_SPIRIT_VALUE
#define JSON_SPIRIT_VALUE
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include <vector>
#include <map>
#include <string>
#include <cassert>
#include <sstream>
#include <stdexcept>
#include <stdint.h>
#include <boost/config.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/variant.hpp>
namespace json_spirit
{
enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type };
static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"};
template< class Config > // Config determines whether the value uses std::string or std::wstring and
// whether JSON Objects are represented as vectors or maps
class Value_impl
{
public:
typedef Config Config_type;
typedef typename Config::String_type String_type;
typedef typename Config::Object_type Object;
typedef typename Config::Array_type Array;
typedef typename String_type::const_pointer Const_str_ptr; // eg const char*
Value_impl(); // creates null value
Value_impl( Const_str_ptr value );
Value_impl( const String_type& value );
Value_impl( const Object& value );
Value_impl( const Array& value );
Value_impl( bool value );
Value_impl( int value );
Value_impl( int64_t value );
Value_impl( uint64_t value );
Value_impl( double value );
Value_impl( const Value_impl& other );
bool operator==( const Value_impl& lhs ) const;
Value_impl& operator=( const Value_impl& lhs );
Value_type type() const;
bool is_uint64() const;
bool is_null() const;
const String_type& get_str() const;
const Object& get_obj() const;
const Array& get_array() const;
bool get_bool() const;
int get_int() const;
int64_t get_int64() const;
uint64_t get_uint64() const;
double get_real() const;
Object& get_obj();
Array& get_array();
template< typename T > T get_value() const; // example usage: int i = value.get_value< int >();
// or double d = value.get_value< double >();
static const Value_impl null;
private:
void check_type( const Value_type vtype ) const;
typedef boost::variant< String_type,
boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >,
bool, int64_t, double > Variant;
Value_type type_;
Variant v_;
bool is_uint64_;
};
// vector objects
template< class Config >
struct Pair_impl
{
typedef typename Config::String_type String_type;
typedef typename Config::Value_type Value_type;
Pair_impl( const String_type& name, const Value_type& value );
bool operator==( const Pair_impl& lhs ) const;
String_type name_;
Value_type value_;
};
template< class String >
struct Config_vector
{
typedef String String_type;
typedef Value_impl< Config_vector > Value_type;
typedef Pair_impl < Config_vector > Pair_type;
typedef std::vector< Value_type > Array_type;
typedef std::vector< Pair_type > Object_type;
static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
{
obj.push_back( Pair_type( name , value ) );
return obj.back().value_;
}
static String_type get_name( const Pair_type& pair )
{
return pair.name_;
}
static Value_type get_value( const Pair_type& pair )
{
return pair.value_;
}
};
// typedefs for ASCII
typedef Config_vector< std::string > Config;
typedef Config::Value_type Value;
typedef Config::Pair_type Pair;
typedef Config::Object_type Object;
typedef Config::Array_type Array;
// typedefs for Unicode
#ifndef BOOST_NO_STD_WSTRING
typedef Config_vector< std::wstring > wConfig;
typedef wConfig::Value_type wValue;
typedef wConfig::Pair_type wPair;
typedef wConfig::Object_type wObject;
typedef wConfig::Array_type wArray;
#endif
// map objects
template< class String >
struct Config_map
{
typedef String String_type;
typedef Value_impl< Config_map > Value_type;
typedef std::vector< Value_type > Array_type;
typedef std::map< String_type, Value_type > Object_type;
typedef typename Object_type::value_type Pair_type;
static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
{
return obj[ name ] = value;
}
static String_type get_name( const Pair_type& pair )
{
return pair.first;
}
static Value_type get_value( const Pair_type& pair )
{
return pair.second;
}
};
// typedefs for ASCII
typedef Config_map< std::string > mConfig;
typedef mConfig::Value_type mValue;
typedef mConfig::Object_type mObject;
typedef mConfig::Array_type mArray;
// typedefs for Unicode
#ifndef BOOST_NO_STD_WSTRING
typedef Config_map< std::wstring > wmConfig;
typedef wmConfig::Value_type wmValue;
typedef wmConfig::Object_type wmObject;
typedef wmConfig::Array_type wmArray;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////
//
// implementation
template< class Config >
const Value_impl< Config > Value_impl< Config >::null;
template< class Config >
Value_impl< Config >::Value_impl()
: type_( null_type )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Const_str_ptr value )
: type_( str_type )
, v_( String_type( value ) )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const String_type& value )
: type_( str_type )
, v_( value )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Object& value )
: type_( obj_type )
, v_( value )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Array& value )
: type_( array_type )
, v_( value )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( bool value )
: type_( bool_type )
, v_( value )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( int value )
: type_( int_type )
, v_( static_cast< int64_t >( value ) )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( int64_t value )
: type_( int_type )
, v_( value )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( uint64_t value )
: type_( int_type )
, v_( static_cast< int64_t >( value ) )
, is_uint64_( true )
{
}
template< class Config >
Value_impl< Config >::Value_impl( double value )
: type_( real_type )
, v_( value )
, is_uint64_( false )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Value_impl< Config >& other )
: type_( other.type() )
, v_( other.v_ )
, is_uint64_( other.is_uint64_ )
{
}
template< class Config >
Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs )
{
Value_impl tmp( lhs );
std::swap( type_, tmp.type_ );
std::swap( v_, tmp.v_ );
std::swap( is_uint64_, tmp.is_uint64_ );
return *this;
}
template< class Config >
bool Value_impl< Config >::operator==( const Value_impl& lhs ) const
{
if( this == &lhs ) return true;
if( type() != lhs.type() ) return false;
return v_ == lhs.v_;
}
template< class Config >
Value_type Value_impl< Config >::type() const
{
return type_;
}
template< class Config >
bool Value_impl< Config >::is_uint64() const
{
return is_uint64_;
}
template< class Config >
bool Value_impl< Config >::is_null() const
{
return type() == null_type;
}
template< class Config >
void Value_impl< Config >::check_type( const Value_type vtype ) const
{
if( type() != vtype )
{
std::ostringstream os;
///// Bitcoin: Tell the types by name instead of by number
os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype];
throw std::runtime_error( os.str() );
}
}
template< class Config >
const typename Config::String_type& Value_impl< Config >::get_str() const
{
check_type( str_type );
return *boost::get< String_type >( &v_ );
}
template< class Config >
const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const
{
check_type( obj_type );
return *boost::get< Object >( &v_ );
}
template< class Config >
const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const
{
check_type( array_type );
return *boost::get< Array >( &v_ );
}
template< class Config >
bool Value_impl< Config >::get_bool() const
{
check_type( bool_type );
return boost::get< bool >( v_ );
}
template< class Config >
int Value_impl< Config >::get_int() const
{
check_type( int_type );
return static_cast< int >( get_int64() );
}
template< class Config >
int64_t Value_impl< Config >::get_int64() const
{
check_type( int_type );
return boost::get< int64_t >( v_ );
}
template< class Config >
uint64_t Value_impl< Config >::get_uint64() const
{
check_type( int_type );
return static_cast< uint64_t >( get_int64() );
}
template< class Config >
double Value_impl< Config >::get_real() const
{
if( type() == int_type )
{
return is_uint64() ? static_cast< double >( get_uint64() )
: static_cast< double >( get_int64() );
}
check_type( real_type );
return boost::get< double >( v_ );
}
template< class Config >
typename Value_impl< Config >::Object& Value_impl< Config >::get_obj()
{
check_type( obj_type );
return *boost::get< Object >( &v_ );
}
template< class Config >
typename Value_impl< Config >::Array& Value_impl< Config >::get_array()
{
check_type( array_type );
return *boost::get< Array >( &v_ );
}
template< class Config >
Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value )
: name_( name )
, value_( value )
{
}
template< class Config >
bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const
{
if( this == &lhs ) return true;
return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ );
}
// converts a C string, ie. 8 bit char array, to a string object
//
template < class String_type >
String_type to_str( const char* c_str )
{
String_type result;
for( const char* p = c_str; *p != 0; ++p )
{
result += *p;
}
return result;
}
//
namespace internal_
{
template< typename T >
struct Type_to_type
{
};
template< class Value >
int get_value( const Value& value, Type_to_type< int > )
{
return value.get_int();
}
template< class Value >
int64_t get_value( const Value& value, Type_to_type< int64_t > )
{
return value.get_int64();
}
template< class Value >
uint64_t get_value( const Value& value, Type_to_type< uint64_t > )
{
return value.get_uint64();
}
template< class Value >
double get_value( const Value& value, Type_to_type< double > )
{
return value.get_real();
}
template< class Value >
typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > )
{
return value.get_str();
}
template< class Value >
typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > )
{
return value.get_array();
}
template< class Value >
typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > )
{
return value.get_obj();
}
template< class Value >
bool get_value( const Value& value, Type_to_type< bool > )
{
return value.get_bool();
}
}
template< class Config >
template< typename T >
T Value_impl< Config >::get_value() const
{
return internal_::get_value( *this, internal_::Type_to_type< T >() );
}
}
#endif

View File

@@ -1,95 +0,0 @@
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#include "json_spirit_writer.h"
#include "json_spirit_writer_template.h"
void json_spirit::write( const Value& value, std::ostream& os )
{
write_stream( value, os, false );
}
void json_spirit::write_formatted( const Value& value, std::ostream& os )
{
write_stream( value, os, true );
}
std::string json_spirit::write( const Value& value )
{
return write_string( value, false );
}
std::string json_spirit::write_formatted( const Value& value )
{
return write_string( value, true );
}
#ifndef BOOST_NO_STD_WSTRING
void json_spirit::write( const wValue& value, std::wostream& os )
{
write_stream( value, os, false );
}
void json_spirit::write_formatted( const wValue& value, std::wostream& os )
{
write_stream( value, os, true );
}
std::wstring json_spirit::write( const wValue& value )
{
return write_string( value, false );
}
std::wstring json_spirit::write_formatted( const wValue& value )
{
return write_string( value, true );
}
#endif
void json_spirit::write( const mValue& value, std::ostream& os )
{
write_stream( value, os, false );
}
void json_spirit::write_formatted( const mValue& value, std::ostream& os )
{
write_stream( value, os, true );
}
std::string json_spirit::write( const mValue& value )
{
return write_string( value, false );
}
std::string json_spirit::write_formatted( const mValue& value )
{
return write_string( value, true );
}
#ifndef BOOST_NO_STD_WSTRING
void json_spirit::write( const wmValue& value, std::wostream& os )
{
write_stream( value, os, false );
}
void json_spirit::write_formatted( const wmValue& value, std::wostream& os )
{
write_stream( value, os, true );
}
std::wstring json_spirit::write( const wmValue& value )
{
return write_string( value, false );
}
std::wstring json_spirit::write_formatted( const wmValue& value )
{
return write_string( value, true );
}
#endif

View File

@@ -1,50 +0,0 @@
#ifndef JSON_SPIRIT_WRITER
#define JSON_SPIRIT_WRITER
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_value.h"
#include <iostream>
namespace json_spirit
{
// functions to convert JSON Values to text,
// the "formatted" versions add whitespace to format the output nicely
void write ( const Value& value, std::ostream& os );
void write_formatted( const Value& value, std::ostream& os );
std::string write ( const Value& value );
std::string write_formatted( const Value& value );
#ifndef BOOST_NO_STD_WSTRING
void write ( const wValue& value, std::wostream& os );
void write_formatted( const wValue& value, std::wostream& os );
std::wstring write ( const wValue& value );
std::wstring write_formatted( const wValue& value );
#endif
void write ( const mValue& value, std::ostream& os );
void write_formatted( const mValue& value, std::ostream& os );
std::string write ( const mValue& value );
std::string write_formatted( const mValue& value );
#ifndef BOOST_NO_STD_WSTRING
void write ( const wmValue& value, std::wostream& os );
void write_formatted( const wmValue& value, std::wostream& os );
std::wstring write ( const wmValue& value );
std::wstring write_formatted( const wmValue& value );
#endif
}
#endif

View File

@@ -1,249 +0,0 @@
#ifndef JSON_SPIRIT_WRITER_TEMPLATE
#define JSON_SPIRIT_WRITER_TEMPLATE
// Copyright John W. Wilkinson 2007 - 2009.
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.03
#include "json_spirit_value.h"
#include <cassert>
#include <sstream>
#include <iomanip>
namespace json_spirit
{
inline char to_hex_char( unsigned int c )
{
assert( c <= 0xF );
const char ch = static_cast< char >( c );
if( ch < 10 ) return '0' + ch;
return 'A' - 10 + ch;
}
template< class String_type >
String_type non_printable_to_string( unsigned int c )
{
// Silence the warning: typedef Char_type locally defined but not used [-Wunused-local-typedefs]
// typedef typename String_type::value_type Char_type;
String_type result( 6, '\\' );
result[1] = 'u';
result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4;
result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4;
result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4;
result[ 2 ] = to_hex_char( c & 0x000F );
return result;
}
template< typename Char_type, class String_type >
bool add_esc_char( Char_type c, String_type& s )
{
switch( c )
{
case '"': s += to_str< String_type >( "\\\"" ); return true;
case '\\': s += to_str< String_type >( "\\\\" ); return true;
case '\b': s += to_str< String_type >( "\\b" ); return true;
case '\f': s += to_str< String_type >( "\\f" ); return true;
case '\n': s += to_str< String_type >( "\\n" ); return true;
case '\r': s += to_str< String_type >( "\\r" ); return true;
case '\t': s += to_str< String_type >( "\\t" ); return true;
}
return false;
}
template< class String_type >
String_type add_esc_chars( const String_type& s )
{
typedef typename String_type::const_iterator Iter_type;
typedef typename String_type::value_type Char_type;
String_type result;
const Iter_type end( s.end() );
for( Iter_type i = s.begin(); i != end; ++i )
{
const Char_type c( *i );
if( add_esc_char( c, result ) ) continue;
const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );
if( iswprint( unsigned_c ) )
{
result += c;
}
else
{
result += non_printable_to_string< String_type >( unsigned_c );
}
}
return result;
}
// this class generates the JSON text,
// it keeps track of the indentation level etc.
//
template< class Value_type, class Ostream_type >
class Generator
{
typedef typename Value_type::Config_type Config_type;
typedef typename Config_type::String_type String_type;
typedef typename Config_type::Object_type Object_type;
typedef typename Config_type::Array_type Array_type;
typedef typename String_type::value_type Char_type;
typedef typename Object_type::value_type Obj_member_type;
public:
Generator( const Value_type& value, Ostream_type& os, bool pretty )
: os_( os )
, indentation_level_( 0 )
, pretty_( pretty )
{
output( value );
}
private:
void output( const Value_type& value )
{
switch( value.type() )
{
case obj_type: output( value.get_obj() ); break;
case array_type: output( value.get_array() ); break;
case str_type: output( value.get_str() ); break;
case bool_type: output( value.get_bool() ); break;
case int_type: output_int( value ); break;
/// Bitcoin: Added std::fixed and changed precision from 16 to 8
case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8)
<< value.get_real(); break;
case null_type: os_ << "null"; break;
default: assert( false );
}
}
void output( const Object_type& obj )
{
output_array_or_obj( obj, '{', '}' );
}
void output( const Array_type& arr )
{
output_array_or_obj( arr, '[', ']' );
}
void output( const Obj_member_type& member )
{
output( Config_type::get_name( member ) ); space();
os_ << ':'; space();
output( Config_type::get_value( member ) );
}
void output_int( const Value_type& value )
{
if( value.is_uint64() )
{
os_ << value.get_uint64();
}
else
{
os_ << value.get_int64();
}
}
void output( const String_type& s )
{
os_ << '"' << add_esc_chars( s ) << '"';
}
void output( bool b )
{
os_ << to_str< String_type >( b ? "true" : "false" );
}
template< class T >
void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )
{
os_ << start_char; new_line();
++indentation_level_;
for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )
{
indent(); output( *i );
typename T::const_iterator next = i;
if( ++next != t.end())
{
os_ << ',';
}
new_line();
}
--indentation_level_;
indent(); os_ << end_char;
}
void indent()
{
if( !pretty_ ) return;
for( int i = 0; i < indentation_level_; ++i )
{
os_ << " ";
}
}
void space()
{
if( pretty_ ) os_ << ' ';
}
void new_line()
{
if( pretty_ ) os_ << '\n';
}
Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning
Ostream_type& os_;
int indentation_level_;
bool pretty_;
};
template< class Value_type, class Ostream_type >
void write_stream( const Value_type& value, Ostream_type& os, bool pretty )
{
Generator< Value_type, Ostream_type >( value, os, pretty );
}
template< class Value_type >
typename Value_type::String_type write_string( const Value_type& value, bool pretty )
{
typedef typename Value_type::String_type::value_type Char_type;
std::basic_ostringstream< Char_type > os;
write_stream( value, os, pretty );
return os.str();
}
}
#endif

View File

@@ -21,7 +21,6 @@ bool CKey::Check(const unsigned char *vch) {
}
void CKey::MakeNewKey(bool fCompressedIn) {
RandAddSeedPerfmon();
do {
GetRandBytes(vch, sizeof(vch));
} while (!Check(vch));

View File

@@ -11,7 +11,7 @@
#include "primitives/transaction.h"
#include "script/script.h"
#include "script/sign.h"
#include "univalue/univalue.h"
#include <univalue.h>
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
@@ -360,7 +360,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
UniValue keysObj = registers["privatekeys"];
fGivenKeys = true;
for (unsigned int kidx = 0; kidx < keysObj.count(); kidx++) {
for (size_t kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr())
throw runtime_error("privatekey not a string");
CBitcoinSecret vchSecret;
@@ -377,7 +377,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
throw runtime_error("prevtxs register variable must be set.");
UniValue prevtxsObj = registers["prevtxs"];
{
for (unsigned int previdx = 0; previdx < prevtxsObj.count(); previdx++) {
for (size_t previdx = 0; previdx < prevtxsObj.size(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject())
throw runtime_error("expected prevtxs internal object");

View File

@@ -57,6 +57,7 @@ int64_t nTimeBestReceived = 0;
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
bool fExperimentalMode = false;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
@@ -79,9 +80,9 @@ struct COrphanTx {
CTransaction tx;
NodeId fromPeer;
};
map<uint256, COrphanTx> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
void EraseOrphansFor(NodeId peer);
map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);;
void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Returns true if there are nRequired or more blocks of minVersion or above
@@ -573,7 +574,7 @@ CBlockTreeDB *pblocktree = NULL;
// mapOrphanTransactions
//
bool AddOrphanTx(const CTransaction& tx, NodeId peer)
bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
@@ -603,7 +604,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer)
return true;
}
void static EraseOrphanTx(uint256 hash)
void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
if (it == mapOrphanTransactions.end())
@@ -637,7 +638,7 @@ void EraseOrphansFor(NodeId peer)
}
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
unsigned int nEvicted = 0;
while (mapOrphanTransactions.size() > nMaxOrphans)
@@ -776,7 +777,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
/**
* Check transaction inputs to mitigate two
* potential denial-of-service attacks:
*
*
* 1. scriptSigs with extra data stuffed into them,
* not consumed by scriptPubKey (or P2SH script)
* 2. P2SH scripts with a crazy number of expensive
@@ -1753,13 +1754,16 @@ bool CScriptCheck::operator()() {
return true;
}
bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
int GetSpendHeight(const CCoinsViewCache& inputs)
{
if (!tx.IsCoinBase())
{
if (pvChecks)
pvChecks->reserve(tx.vin.size());
LOCK(cs_main);
CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second;
return pindexPrev->nHeight + 1;
}
namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams)
{
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!inputs.HaveInputs(tx))
@@ -1778,6 +1782,13 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
assert(coins);
if (coins->IsCoinBase()) {
// Ensure that coinbases are matured
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) {
return state.Invalid(
error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
}
// Ensure that coinbases cannot be spent to transparent outputs
// Disabled on regtest
if (fCoinbaseEnforcedProtectionEnabled &&
@@ -1830,7 +1841,20 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
if (!MoneyRange(nFees))
return state.DoS(100, error("CheckInputs(): nFees out of range"),
REJECT_INVALID, "bad-txns-fee-outofrange");
//fprintf(stderr,"nFees %.8f\n",(double)nFees/COIN);
return true;
}
}// namespace Consensus
bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
{
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams))
return false;
if (!tx.IsCoinBase())
{
if (pvChecks)
pvChecks->reserve(tx.vin.size());
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
// Helps prevent CPU exhaustion attacks.
@@ -1879,6 +1903,8 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
}
bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
/*bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
{
if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) {
fprintf(stderr,"ContextualCheckInputs failure.0\n");
@@ -1914,7 +1940,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
}
return true;
}
}*/
namespace {
@@ -2207,8 +2233,20 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
{
const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
/*<<<<<<< HEA
// Check it again in case a previous version let a bad block in
bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
=======
*/
bool fExpensiveChecks = true;
if (fCheckpointsEnabled) {
CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints());
if (pindexLastCheckpoint && pindexLastCheckpoint->GetAncestor(pindex->nHeight) == pindex) {
// This block is an ancestor of a checkpoint: disable script checks
fExpensiveChecks = false;
}
}
//>>>>>>> zcash/master
auto verifier = libzcash::ProofVerifier::Strict();
auto disabledVerifier = libzcash::ProofVerifier::Disabled();
@@ -2228,6 +2266,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// Before the genesis block, there was an empty tree
ZCIncrementalMerkleTree tree;
pindex->hashAnchor = tree.root();
// The genesis block contained no JoinSplits
pindex->hashAnchorEnd = pindex->hashAnchor;
}
return true;
}
@@ -2345,6 +2385,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
}
view.PushAnchor(tree);
if (!fJustCheck) {
pindex->hashAnchorEnd = tree.root();
}
blockundo.old_tree_root = old_tree_root;
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
@@ -2560,7 +2603,7 @@ void static UpdateTip(CBlockIndex *pindexNew) {
LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION);
if (nUpgraded > 100/2)
{
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
// strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user:
strMiscWarning = _("Warning: This version is obsolete; upgrade required!");
CAlert::Notify(strMiscWarning, true);
fWarned = true;
@@ -2627,7 +2670,7 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
/**
/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
@@ -2652,7 +2695,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001);
{
CCoinsViewCache view(pcoinsTip);
CInv inv(MSG_BLOCK, pindexNew->GetBlockHash());
bool rv = ConnectBlock(*pblock, state, pindexNew, view);
GetMainSignals().BlockChecked(*pblock, state);
if (!rv) {
@@ -2660,7 +2702,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
InvalidBlockFound(pindexNew, state);
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
mapBlockSource.erase(inv.hash);
mapBlockSource.erase(pindexNew->GetBlockHash());
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001);
assert(view.Flush());
@@ -2893,6 +2935,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
}
// Notify external listeners about the new tip.
GetMainSignals().UpdatedBlockTip(pindexNewTip);
uiInterface.NotifyBlockTip(hashNewTip);
} //else fprintf(stderr,"initial download skips propagation\n");
} while(pindexMostWork != chainActive.Tip());
@@ -3275,7 +3318,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
return state.Invalid(error("%s: block's timestamp is too early", __func__),
REJECT_INVALID, "time-too-old");
if(fCheckpointsEnabled)
if (fCheckpointsEnabled)
{
// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash))
@@ -3794,11 +3837,27 @@ bool static LoadBlockIndexDB()
pblocktree->ReadFlag("txindex", fTxIndex);
LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
// Fill in-memory data
BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
{
CBlockIndex* pindex = item.second;
// - This relationship will always be true even if pprev has multiple
// children, because hashAnchor is technically a property of pprev,
// not its children.
// - This will miss chain tips; we handle the best tip below, and other
// tips will be handled by ConnectTip during a re-org.
if (pindex->pprev) {
pindex->pprev->hashAnchorEnd = pindex->hashAnchor;
}
}
// Load pointer to end of best chain
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
if (it == mapBlockIndex.end())
return true;
chainActive.SetTip(it->second);
// Set hashAnchorEnd for the end of best chain
it->second->hashAnchorEnd = pcoinsTip->GetBestAnchor();
PruneBlockIndexCandidates();
@@ -4285,7 +4344,7 @@ void static CheckBlockIndex()
// CAlert
//
string GetWarnings(string strFor)
std::string GetWarnings(const std::string& strFor)
{
int nPriority = 0;
string strStatusBar;
@@ -4353,7 +4412,7 @@ string GetWarnings(string strFor)
//
bool static AlreadyHave(const CInv& inv)
bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
switch (inv.type)
{
@@ -4522,7 +4581,6 @@ void static ProcessGetData(CNode* pfrom)
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
{
const CChainParams& chainparams = Params();
RandAddSeedPerfmon();
LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
@@ -4608,9 +4666,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CAddress addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
{
LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr);
} else if (IsPeerAddrLocalGood(pfrom)) {
addr.SetIP(pfrom->addrLocal);
LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString());
pfrom->PushAddress(addr);
}
}

View File

@@ -62,7 +62,7 @@ static const unsigned int MAX_P2SH_SIGOPS = 15;
/** The maximum number of sigops we're willing to relay/mine in a single tx */
static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** The maximum size of a blk?????.dat file (since 0.8) */
@@ -117,6 +117,7 @@ extern uint64_t nLastBlockSize;
extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange;
extern bool fExperimentalMode;
extern bool fImporting;
extern bool fReindex;
extern int nScriptCheckThreads;
@@ -145,7 +146,7 @@ extern bool fPruneMode;
/** Number of MiB of block files that we're trying to stay below. */
extern uint64_t nPruneTarget;
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
static const signed int MIN_BLOCKS_TO_KEEP = 288;
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
// Require that user allocate at least 550MB for block & undo files (blk???.dat and rev???.dat)
// At 1MB per block, 288 blocks = 288MB.
@@ -155,7 +156,7 @@ static const signed int MIN_BLOCKS_TO_KEEP = 288;
// full block file chunks, we need the high water mark which triggers the prune to be
// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB
// Setting the target to > than 550MB will make it likely we can respect the target.
static const signed int MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
/** Register with a network node to receive its signals */
void RegisterNodeSignals(CNodeSignals& nodeSignals);
@@ -207,7 +208,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
/** Format a string that describes several potential problems detected by the core */
std::string GetWarnings(std::string strFor);
std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
@@ -332,10 +333,6 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams,
std::vector<CScriptCheck> *pvChecks = NULL);
bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams,
std::vector<CScriptCheck> *pvChecks = NULL);
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight);
@@ -520,4 +517,15 @@ extern CCoinsViewCache *pcoinsTip;
/** Global variable that points to the active block tree (protected by cs_main) */
extern CBlockTreeDB *pblocktree;
/**
* Return the spend height, which is one more than the inputs.GetBestBlock().
* While checking, GetBestBlock() refers to the parent block. (protected by cs_main)
* This is also true for mempool checks.
*/
int GetSpendHeight(const CCoinsViewCache& inputs);
namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams);
}
#endif // BITCOIN_MAIN_H

View File

@@ -11,6 +11,7 @@
#include <set>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
@@ -20,19 +21,27 @@ namespace memusage
/** Compute the total memory used by allocating alloc bytes. */
static size_t MallocUsage(size_t alloc);
/** Dynamic memory usage for built-in types is zero. */
static inline size_t DynamicUsage(const int8_t& v) { return 0; }
static inline size_t DynamicUsage(const uint8_t& v) { return 0; }
static inline size_t DynamicUsage(const int16_t& v) { return 0; }
static inline size_t DynamicUsage(const uint16_t& v) { return 0; }
static inline size_t DynamicUsage(const int32_t& v) { return 0; }
static inline size_t DynamicUsage(const uint32_t& v) { return 0; }
static inline size_t DynamicUsage(const int64_t& v) { return 0; }
static inline size_t DynamicUsage(const uint64_t& v) { return 0; }
static inline size_t DynamicUsage(const float& v) { return 0; }
static inline size_t DynamicUsage(const double& v) { return 0; }
template<typename X> static inline size_t DynamicUsage(X * const &v) { return 0; }
template<typename X> static inline size_t DynamicUsage(const X * const &v) { return 0; }
/** Compute the memory used for dynamically allocated but owned data structures.
* For generic data types, this is *not* recursive. DynamicUsage(vector<vector<int> >)
* will compute the memory used for the vector<int>'s, but not for the ints inside.
* This is for efficiency reasons, as these functions are intended to be fast. If
* application data structures require more accurate inner accounting, they should
* do the recursion themselves, or use more efficient caching + updating on modification.
* iterate themselves, or use more efficient caching + updating on modification.
*/
template<typename X> static size_t DynamicUsage(const std::vector<X>& v);
template<typename X> static size_t DynamicUsage(const std::set<X>& s);
template<typename X, typename Y> static size_t DynamicUsage(const std::map<X, Y>& m);
template<typename X, typename Y> static size_t DynamicUsage(const boost::unordered_set<X, Y>& s);
template<typename X, typename Y, typename Z> static size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& s);
template<typename X> static size_t DynamicUsage(const X& x);
static inline size_t MallocUsage(size_t alloc)
{
@@ -98,14 +107,6 @@ static inline size_t DynamicUsage(const boost::unordered_map<X, Y, Z>& m)
return MallocUsage(sizeof(boost_unordered_node<std::pair<const X, Y> >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
}
// Dispatch to class method as fallback
template<typename X>
static inline size_t DynamicUsage(const X& x)
{
return x.DynamicMemoryUsage();
}
}
#endif

View File

@@ -17,6 +17,45 @@
#include <sys/ioctl.h>
#include <unistd.h>
void AtomicTimer::start()
{
std::unique_lock<std::mutex> lock(mtx);
if (threads < 1) {
start_time = GetTime();
}
++threads;
}
void AtomicTimer::stop()
{
std::unique_lock<std::mutex> lock(mtx);
// Ignore excess calls to stop()
if (threads > 0) {
--threads;
if (threads < 1) {
int64_t time_span = GetTime() - start_time;
total_time += time_span;
}
}
}
bool AtomicTimer::running()
{
std::unique_lock<std::mutex> lock(mtx);
return threads > 0;
}
double AtomicTimer::rate(const AtomicCounter& count)
{
std::unique_lock<std::mutex> lock(mtx);
int64_t duration = total_time;
if (threads > 0) {
// Timer is running, so get the latest count
duration += GetTime() - start_time;
}
return duration > 0 ? (double)count.get() / duration : 0;
}
CCriticalSection cs_metrics;
boost::synchronized_value<int64_t> nNodeStartTime;
@@ -25,6 +64,7 @@ AtomicCounter transactionsValidated;
AtomicCounter ehSolverRuns;
AtomicCounter solutionTargetChecks;
AtomicCounter minedBlocks;
AtomicTimer miningTimer;
boost::synchronized_value<std::list<uint256>> trackedBlocks;
@@ -51,14 +91,9 @@ int64_t GetUptime()
return GetTime() - *nNodeStartTime;
}
double GetLocalSolPS_INTERNAL(int64_t uptime)
{
return uptime > 0 ? (double)solutionTargetChecks.get() / uptime : 0;
}
double GetLocalSolPS()
{
return GetLocalSolPS_INTERNAL(GetUptime());
return miningTimer.rate(solutionTargetChecks);
}
void TriggerRefresh()
@@ -101,6 +136,11 @@ static bool metrics_ThreadSafeMessageBox(const std::string& message,
return false;
}
static bool metrics_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
{
return metrics_ThreadSafeMessageBox(message, caption, style);
}
static void metrics_InitMessage(const std::string& message)
{
*initMessage = message;
@@ -110,24 +150,43 @@ void ConnectMetricsScreen()
{
uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
uiInterface.ThreadSafeQuestion.disconnect_all_slots();
uiInterface.ThreadSafeQuestion.connect(metrics_ThreadSafeQuestion);
uiInterface.InitMessage.disconnect_all_slots();
uiInterface.InitMessage.connect(metrics_InitMessage);
}
int printNetworkStats()
int printStats(bool mining)
{
LOCK2(cs_main, cs_vNodes);
// Number of lines that are always displayed
int lines = 4;
std::cout << " " << _("Block height") << " | " << chainActive.Height() << std::endl;
std::cout << " " << _("Network solution rate") << " | " << GetNetworkHashPS(120, -1) << " Sol/s" << std::endl;
std::cout << " " << _("Connections") << " | " << vNodes.size() << std::endl;
int height;
size_t connections;
int64_t netsolps;
{
LOCK2(cs_main, cs_vNodes);
height = chainActive.Height();
connections = vNodes.size();
netsolps = GetNetworkHashPS(120, -1);
}
auto localsolps = GetLocalSolPS();
std::cout << " " << _("Block height") << " | " << height << std::endl;
std::cout << " " << _("Connections") << " | " << connections << std::endl;
std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl;
if (mining && miningTimer.running()) {
std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl;
lines++;
}
std::cout << std::endl;
return 4;
return lines;
}
int printMiningStatus(bool mining)
{
#ifdef ENABLE_MINING
// Number of lines that are always displayed
int lines = 1;
@@ -140,8 +199,23 @@ int printMiningStatus(bool mining)
else
nThreads = boost::thread::hardware_concurrency();
}
std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
GetArg("-equihashsolver", "default"), nThreads) << std::endl;
if (miningTimer.running()) {
std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
GetArg("-equihashsolver", "default"), nThreads) << std::endl;
} else {
bool fvNodesEmpty;
{
LOCK(cs_vNodes);
fvNodesEmpty = vNodes.empty();
}
if (fvNodesEmpty) {
std::cout << _("Mining is paused while waiting for connections.") << std::endl;
} else if (IsInitialBlockDownload()) {
std::cout << _("Mining is paused while downloading blocks.") << std::endl;
} else {
std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl;
}
}
lines++;
} else {
std::cout << _("You are currently not mining.") << std::endl;
@@ -151,6 +225,9 @@ int printMiningStatus(bool mining)
std::cout << std::endl;
return lines;
#else // ENABLE_MINING
return 0;
#endif // !ENABLE_MINING
}
int printMetrics(size_t cols, bool mining)
@@ -190,11 +267,8 @@ int printMetrics(size_t cols, bool mining)
}
if (mining && loaded) {
double solps = GetLocalSolPS_INTERNAL(uptime);
std::string strSolps = strprintf("%.4f Sol/s", solps);
std::cout << "- " << strprintf(_("You have contributed %s on average to the network solution rate."), strSolps) << std::endl;
std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
lines += 2;
lines++;
int mined = 0;
int orphaned = 0;
@@ -319,6 +393,9 @@ void ThreadShowMetricsScreen()
// Thank you text
std::cout << _("Thank you for running a Zcash node!") << std::endl;
std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
// Privacy notice text
std::cout << PrivacyInfo();
std::cout << std::endl;
}
@@ -342,12 +419,16 @@ void ThreadShowMetricsScreen()
}
// Miner status
#ifdef ENABLE_MINING
bool mining = GetBoolArg("-gen", false);
#else
bool mining = false;
#endif
if (loaded) {
lines += printNetworkStats();
lines += printStats(mining);
lines += printMiningStatus(mining);
}
lines += printMiningStatus(mining);
lines += printMetrics(cols, mining);
lines += printMessageBox(cols);
lines += printInitMessage();

View File

@@ -5,6 +5,7 @@
#include "uint256.h"
#include <atomic>
#include <mutex>
#include <string>
struct AtomicCounter {
@@ -20,14 +21,41 @@ struct AtomicCounter {
--value;
}
int get(){
int get() const {
return value.load();
}
};
class AtomicTimer {
private:
std::mutex mtx;
uint64_t threads;
int64_t start_time;
int64_t total_time;
public:
AtomicTimer() : threads(0), start_time(0), total_time(0) {}
/**
* Starts timing on first call, and counts the number of calls.
*/
void start();
/**
* Counts number of calls, and stops timing after it has been called as
* many times as start().
*/
void stop();
bool running();
double rate(const AtomicCounter& count);
};
extern AtomicCounter transactionsValidated;
extern AtomicCounter ehSolverRuns;
extern AtomicCounter solutionTargetChecks;
extern AtomicTimer miningTimer;
void TrackMinedBlock(uint256 hash);

View File

@@ -4,12 +4,18 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "miner.h"
#ifdef ENABLE_MINING
#include "pow/tromp/equi_miner.h"
#endif
#include "amount.h"
#include "base58.h"
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#ifdef ENABLE_MINING
#include "crypto/equihash.h"
#endif
#include "hash.h"
#include "main.h"
#include "metrics.h"
@@ -18,18 +24,20 @@
#include "primitives/transaction.h"
#include "random.h"
#include "timedata.h"
#include "ui_interface.h"
#include "util.h"
#include "utilmoneystr.h"
#ifdef ENABLE_WALLET
#include "crypto/equihash.h"
#include "wallet/wallet.h"
#include <functional>
#endif
#include "sodium.h"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#ifdef ENABLE_MINING
#include <functional>
#endif
#include <mutex>
using namespace std;
@@ -120,7 +128,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{
uint64_t deposits; int32_t isrealtime,kmdheight; const CChainParams& chainparams = Params();
// Create new block
unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
if(!pblocktemplate.get())
{
fprintf(stderr,"pblocktemplate.get() failure\n");
@@ -443,6 +451,55 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
return pblocktemplate.release();
}
#ifdef ENABLE_WALLET
boost::optional<CScript> GetMinerScriptPubKey(CReserveKey& reservekey)
#else
boost::optional<CScript> GetMinerScriptPubKey()
#endif
{
CKeyID keyID;
CBitcoinAddress addr;
if (addr.SetString(GetArg("-mineraddress", ""))) {
addr.GetKeyID(keyID);
} else {
#ifdef ENABLE_WALLET
CPubKey pubkey;
if (!reservekey.GetReservedKey(pubkey)) {
return boost::optional<CScript>();
}
keyID = pubkey.GetID();
#else
return boost::optional<CScript>();
#endif
}
CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return scriptPubKey;
}
/*#ifdef ENABLE_WALLET
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
{
boost::optional<CScript> scriptPubKey = GetMinerScriptPubKey(reservekey);
#else
CBlockTemplate* CreateNewBlockWithKey()
{
boost::optional<CScript> scriptPubKey = GetMinerScriptPubKey();
#endif
if (!scriptPubKey) {
return NULL;
}
return CreateNewBlock(*scriptPubKey);
}*/
//////////////////////////////////////////////////////////////////////////////
//
// Internal miner
//
#ifdef ENABLE_MINING
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
// Update nExtraNonce
@@ -501,7 +558,11 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
return CreateNewBlock(scriptPubKey);
}
static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
#else
static bool ProcessBlockFound(CBlock* pblock)
#endif // ENABLE_WALLET
{
LogPrintf("%s\n", pblock->ToString());
LogPrintf("generated %s height.%d\n", FormatMoney(pblock->vtx[0].vout[0].nValue),chainActive.Tip()->nHeight+1);
@@ -525,15 +586,21 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
}
}
#ifdef ENABLE_WALLET
// Remove key from key pool
if ( IS_KOMODO_NOTARY == 0 )
reservekey.KeepKey();
{
if (GetArg("-mineraddress", "").empty()) {
// Remove key from key pool
reservekey.KeepKey();
}
}
// Track how many getdata requests this block gets
{
LOCK(wallet.cs_wallet);
wallet.mapRequestCount[pblock->GetHash()] = 0;
}
#endif
// Process this block the same as if we had received it from another node
CValidationState state;
@@ -551,15 +618,23 @@ int32_t FOUND_BLOCK,KOMODO_MAYBEMINED;
extern int32_t KOMODO_LASTMINED;
int32_t roundrobin_delay;
#ifdef ENABLE_WALLET
void static BitcoinMiner(CWallet *pwallet)
#else
void static BitcoinMiner()
#endif
{
LogPrintf("KomodoMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("komodo-miner");
const CChainParams& chainparams = Params();
// Each thread has its own key and counter
#ifdef ENABLE_WALLET
// Each thread has its own key
CReserveKey reservekey(pwallet);
#endif
// Each thread has its own counter
unsigned int nExtraNonce = 0;
unsigned int n = chainparams.EquihashN();
@@ -589,6 +664,7 @@ void static BitcoinMiner(CWallet *pwallet)
cancelSolver = true;
}
);
miningTimer.start();
try {
if ( ASSETCHAINS_SYMBOL[0] != 0 )
@@ -601,7 +677,7 @@ void static BitcoinMiner(CWallet *pwallet)
// break;
// Busy-wait for the network to come online so we don't waste time mining
// on an obsolete chain. In regtest mode we expect to fly solo.
//fprintf(stderr,"Wait for peers...\n");
miningTimer.stop();
do {
bool fvNodesEmpty;
{
@@ -615,6 +691,7 @@ void static BitcoinMiner(CWallet *pwallet)
} while (true);
//fprintf(stderr,"%s Found peers\n",ASSETCHAINS_SYMBOL);
miningTimer.start();
}
/*while ( ASSETCHAINS_SYMBOL[0] != 0 && chainActive.Tip()->nHeight < ASSETCHAINS_MINHEIGHT )
{
@@ -633,7 +710,11 @@ void static BitcoinMiner(CWallet *pwallet)
}
if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 )
fprintf(stderr,"%s create new block ht.%d\n",ASSETCHAINS_SYMBOL,Mining_height);
#ifdef ENABLE_WALLET
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey);
#else
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
if ( ptr == 0 )
{
static uint32_t counter;
@@ -644,7 +725,12 @@ void static BitcoinMiner(CWallet *pwallet)
unique_ptr<CBlockTemplate> pblocktemplate(ptr);
if (!pblocktemplate.get())
{
LogPrintf("Error in KomodoMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
if (GetArg("-mineraddress", "").empty()) {
LogPrintf("Error in KomodoMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
} else {
// Should never reach here, because -mineraddress validity is checked in init.cpp
LogPrintf("Error in KomodoMiner: Invalid -mineraddress\n");
}
return;
}
CBlock *pblock = &pblocktemplate->block;
@@ -752,9 +838,12 @@ void static BitcoinMiner(CWallet *pwallet)
LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n",solver, pblock->nNonce.ToString());
//fprintf(stderr,"running solver\n");
std::function<bool(std::vector<unsigned char>)> validBlock =
#ifdef ENABLE_WALLET
[&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
(std::vector<unsigned char> soln)
{
#else
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams]
#endif
(std::vector<unsigned char> soln) {
// Write the solution to the hash and compute the result.
LogPrint("pow", "- Checking solution against target\n");
pblock->nSolution = soln;
@@ -778,7 +867,11 @@ void static BitcoinMiner(CWallet *pwallet)
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("KomodoMiner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
#ifdef ENABLE_WALLET
if (ProcessBlockFound(pblock, *pwallet, reservekey)) {
#else
if (ProcessBlockFound(pblock)) {
#endif
// Ignore chain updates caused by us
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
@@ -909,20 +1002,27 @@ void static BitcoinMiner(CWallet *pwallet)
}
catch (const boost::thread_interrupted&)
{
miningTimer.stop();
c.disconnect();
LogPrintf("KomodoMiner terminated\n");
throw;
}
catch (const std::runtime_error &e)
{
miningTimer.stop();
c.disconnect();
LogPrintf("KomodoMiner runtime error: %s\n", e.what());
return;
}
miningTimer.stop();
c.disconnect();
}
#ifdef ENABLE_WALLET
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
#else
void GenerateBitcoins(bool fGenerate, int nThreads)
#endif
{
static boost::thread_group* minerThreads = NULL;
@@ -945,8 +1045,13 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
return;
minerThreads = new boost::thread_group();
for (int i = 0; i < nThreads; i++)
for (int i = 0; i < nThreads; i++) {
#ifdef ENABLE_WALLET
minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
#else
minerThreads->create_thread(&BitcoinMiner);
#endif
}
}
#endif // ENABLE_WALLET
#endif // ENABLE_MINING

View File

@@ -8,12 +8,15 @@
#include "primitives/block.h"
#include <boost/optional.hpp>
#include <stdint.h>
class CBlockIndex;
class CReserveKey;
class CScript;
#ifdef ENABLE_WALLET
class CReserveKey;
class CWallet;
#endif
namespace Consensus { struct Params; };
struct CBlockTemplate
@@ -23,13 +26,27 @@ struct CBlockTemplate
std::vector<int64_t> vTxSigOps;
};
/** Run the miner threads */
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads);
/** Generate a new block, without valid proof-of-work */
CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn);
#ifdef ENABLE_WALLET
boost::optional<CScript> GetMinerScriptPubKey(CReserveKey& reservekey);
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey);
#else
boost::optional<CScript> GetMinerScriptPubKey();
CBlockTemplate* CreateNewBlockWithKey();
#endif
#ifdef ENABLE_MINING
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
/** Run the miner threads */
#ifdef ENABLE_WALLET
void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads);
#else
void GenerateBitcoins(bool fGenerate, int nThreads);
#endif
#endif
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
#endif // BITCOIN_MINER_H

View File

@@ -72,13 +72,12 @@ bool fListen = true;
uint64_t nLocalServices = NODE_NETWORK;
CCriticalSection cs_mapLocalHost;
map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {};
static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
uint64_t nLocalHostNonce = 0;
static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
int nMaxConnections = 125;
int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
bool fAddressesInitialized = false;
vector<CNode*> vNodes;
@@ -107,7 +106,7 @@ boost::condition_variable messageHandlerCondition;
static CNodeSignals g_signals;
CNodeSignals& GetNodeSignals() { return g_signals; }
void AddOneShot(string strDest)
void AddOneShot(const std::string& strDest)
{
LOCK(cs_vOneShots);
vOneShots.push_back(strDest);
@@ -213,19 +212,12 @@ void AdvertizeLocal(CNode *pnode)
}
if (addrLocal.IsRoutable())
{
LogPrintf("AdvertizeLocal: advertizing address %s\n", addrLocal.ToString());
pnode->PushAddress(addrLocal);
}
}
}
void SetReachable(enum Network net, bool fFlag)
{
LOCK(cs_mapLocalHost);
vfReachable[net] = fFlag;
if (net == NET_IPV6 && fFlag)
vfReachable[NET_IPV4] = true;
}
// learn a new local address
bool AddLocal(const CService& addr, int nScore)
{
@@ -248,7 +240,6 @@ bool AddLocal(const CService& addr, int nScore)
info.nScore = nScore + (fAlready ? 1 : 0);
info.nPort = addr.GetPort();
}
SetReachable(addr.GetNetwork());
}
return true;
@@ -259,6 +250,14 @@ bool AddLocal(const CNetAddr &addr, int nScore)
return AddLocal(CService(addr, GetListenPort()), nScore);
}
bool RemoveLocal(const CService& addr)
{
LOCK(cs_mapLocalHost);
LogPrintf("RemoveLocal(%s)\n", addr.ToString());
mapLocalHost.erase(addr);
return true;
}
/** Make a particular network entirely off-limits (no automatic connects to it) */
void SetLimited(enum Network net, bool fLimited)
{
@@ -303,7 +302,7 @@ bool IsLocal(const CService& addr)
bool IsReachable(enum Network net)
{
LOCK(cs_mapLocalHost);
return vfReachable[net] && !vfLimited[net];
return !vfLimited[net];
}
/** check whether a given address is in a network we can probably connect to */
@@ -333,6 +332,15 @@ CNode* FindNode(const CNetAddr& ip)
return NULL;
}
CNode* FindNode(const CSubNet& subNet)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
if (subNet.Match((CNetAddr)pnode->addr))
return (pnode);
return NULL;
}
CNode* FindNode(const std::string& addrName)
{
LOCK(cs_vNodes);
@@ -441,11 +449,12 @@ void CNode::PushVersion()
std::map<CNetAddr, int64_t> CNode::setBanned;
std::map<CSubNet, int64_t> CNode::setBanned;
CCriticalSection CNode::cs_setBanned;
void CNode::ClearBanned()
{
LOCK(cs_setBanned);
setBanned.clear();
}
@@ -454,7 +463,24 @@ bool CNode::IsBanned(CNetAddr ip)
bool fResult = false;
{
LOCK(cs_setBanned);
std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip);
for (std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); it != setBanned.end(); it++)
{
CSubNet subNet = (*it).first;
int64_t t = (*it).second;
if(subNet.Match(ip) && GetTime() < t)
fResult = true;
}
}
return fResult;
}
bool CNode::IsBanned(CSubNet subnet)
{
bool fResult = false;
{
LOCK(cs_setBanned);
std::map<CSubNet, int64_t>::iterator i = setBanned.find(subnet);
if (i != setBanned.end())
{
int64_t t = (*i).second;
@@ -465,14 +491,37 @@ bool CNode::IsBanned(CNetAddr ip)
return fResult;
}
bool CNode::Ban(const CNetAddr &addr) {
void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) {
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
Ban(subNet, bantimeoffset, sinceUnixEpoch);
}
void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) {
int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban
{
LOCK(cs_setBanned);
if (setBanned[addr] < banTime)
setBanned[addr] = banTime;
}
return true;
if (bantimeoffset > 0)
banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset;
LOCK(cs_setBanned);
if (setBanned[subNet] < banTime)
setBanned[subNet] = banTime;
}
bool CNode::Unban(const CNetAddr &addr) {
CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128"));
return Unban(subNet);
}
bool CNode::Unban(const CSubNet &subNet) {
LOCK(cs_setBanned);
if (setBanned.erase(subNet))
return true;
return false;
}
void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap)
{
LOCK(cs_setBanned);
banMap = setBanned; //create a thread safe copy
}
@@ -763,8 +812,6 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
continue;
if (node->fDisconnect)
continue;
if (node->addr.IsLocal())
continue;
vEvictionCandidates.push_back(CNodeRef(node));
}
}
@@ -795,15 +842,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
if (vEvictionCandidates.empty()) return false;
// Identify the network group with the most connections
// Identify the network group with the most connections and youngest member.
// (vEvictionCandidates is already sorted by reverse connect time)
std::vector<unsigned char> naMostConnections;
unsigned int nMostConnections = 0;
int64_t nMostConnectionsTime = 0;
std::map<std::vector<unsigned char>, std::vector<CNodeRef> > mapAddrCounts;
BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) {
mapAddrCounts[node->addr.GetGroup()].push_back(node);
int64_t grouptime = mapAddrCounts[node->addr.GetGroup()][0]->nTimeConnected;
size_t groupsize = mapAddrCounts[node->addr.GetGroup()].size();
if (mapAddrCounts[node->addr.GetGroup()].size() > nMostConnections) {
nMostConnections = mapAddrCounts[node->addr.GetGroup()].size();
if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {
nMostConnections = groupsize;
nMostConnectionsTime = grouptime;
naMostConnections = node->addr.GetGroup();
}
}
@@ -811,14 +863,13 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
// Reduce to the network group with the most connections
vEvictionCandidates = mapAddrCounts[naMostConnections];
// Do not disconnect peers if there is only 1 connection from their network group
// Do not disconnect peers if there is only one unprotected connection from their network group.
if (vEvictionCandidates.size() <= 1)
// unless we prefer the new connection (for whitelisted peers)
if (!fPreferNewConnection)
return false;
// Disconnect the most recent connection from the network group with the most connections
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
// Disconnect from the network group with the most connections
vEvictionCandidates[0]->fDisconnect = true;
return true;
@@ -1317,7 +1368,7 @@ void ThreadDNSAddressSeed()
vector<CAddress> vAdd;
if (LookupHost(seed.host.c_str(), vIPs))
{
BOOST_FOREACH(CNetAddr& ip, vIPs)
BOOST_FOREACH(const CNetAddr& ip, vIPs)
{
int nOneDay = 24*3600;
CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()));
@@ -1381,7 +1432,7 @@ void ThreadOpenConnections()
for (int64_t nLoop = 0;; nLoop++)
{
ProcessOneShot();
BOOST_FOREACH(string strAddr, mapMultiArgs["-connect"])
BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect"])
{
CAddress addr;
OpenNetworkConnection(addr, NULL, strAddr.c_str());
@@ -1484,10 +1535,10 @@ void ThreadOpenAddedConnections()
list<string> lAddresses(0);
{
LOCK(cs_vAddedNodes);
BOOST_FOREACH(string& strAddNode, vAddedNodes)
BOOST_FOREACH(const std::string& strAddNode, vAddedNodes)
lAddresses.push_back(strAddNode);
}
BOOST_FOREACH(string& strAddNode, lAddresses) {
BOOST_FOREACH(const std::string& strAddNode, lAddresses) {
CAddress addr;
CSemaphoreGrant grant(*semOutbound);
OpenNetworkConnection(addr, &grant, strAddNode.c_str());
@@ -1502,20 +1553,19 @@ void ThreadOpenAddedConnections()
list<string> lAddresses(0);
{
LOCK(cs_vAddedNodes);
BOOST_FOREACH(string& strAddNode, vAddedNodes)
BOOST_FOREACH(const std::string& strAddNode, vAddedNodes)
lAddresses.push_back(strAddNode);
}
list<vector<CService> > lservAddressesToAdd(0);
BOOST_FOREACH(string& strAddNode, lAddresses)
{
BOOST_FOREACH(const std::string& strAddNode, lAddresses) {
vector<CService> vservNode(0);
if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0))
{
lservAddressesToAdd.push_back(vservNode);
{
LOCK(cs_setservAddNodeAddresses);
BOOST_FOREACH(CService& serv, vservNode)
BOOST_FOREACH(const CService& serv, vservNode)
setservAddNodeAddresses.insert(serv);
}
}
@@ -1526,7 +1576,7 @@ void ThreadOpenAddedConnections()
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++)
BOOST_FOREACH(CService& addrNode, *(it))
BOOST_FOREACH(const CService& addrNode, *(it))
if (pnode->addr == addrNode)
{
it = lservAddressesToAdd.erase(it);
@@ -2111,7 +2161,7 @@ bool CAddrDB::Read(CAddrMan& addr)
unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) :
CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) :
ssSend(SER_NETWORK, INIT_PROTO_VERSION),
addrKnown(5000, 0.001),
setInventoryKnown(SendBufferSize() / 1000)

View File

@@ -61,13 +61,16 @@ static const bool DEFAULT_UPNP = false;
static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
/** The maximum number of entries in setAskFor (larger due to getdata latency)*/
static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
/** The maximum number of peer connections to maintain. */
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
unsigned int ReceiveFloodSize();
unsigned int SendBufferSize();
void AddOneShot(std::string strDest);
void AddOneShot(const std::string& strDest);
void AddressCurrentlyConnected(const CService& addr);
CNode* FindNode(const CNetAddr& ip);
CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& ip);
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
@@ -128,12 +131,12 @@ bool IsLimited(enum Network net);
bool IsLimited(const CNetAddr& addr);
bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
bool RemoveLocal(const CService& addr);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
bool IsReachable(enum Network net);
bool IsReachable(const CNetAddr &addr);
void SetReachable(enum Network net, bool fFlag = true);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
@@ -288,7 +291,7 @@ protected:
// Denial-of-service detection/prevention
// Key is IP address, value is banned-until-time
static std::map<CNetAddr, int64_t> setBanned;
static std::map<CSubNet, int64_t> setBanned;
static CCriticalSection cs_setBanned;
// Whitelisted ranges. Any node connecting from these is automatically
@@ -328,7 +331,7 @@ public:
// Whether a ping is requested.
bool fPingQueued;
CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false);
CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
private:
@@ -613,7 +616,13 @@ public:
// new code.
static void ClearBanned(); // needed for unit testing
static bool IsBanned(CNetAddr ip);
static bool Ban(const CNetAddr &ip);
static bool IsBanned(CSubNet subnet);
static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
static bool Unban(const CNetAddr &ip);
static bool Unban(const CSubNet &ip);
static void GetBanned(std::map<CSubNet, int64_t> &banmap);
void copyStats(CNodeStats &stats);
static bool IsWhitelistedRange(const CNetAddr &ip);

View File

@@ -233,10 +233,7 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
return Lookup(pszName, addr, portDefault, false);
}
/**
* Convert milliseconds to a struct timeval for select.
*/
struct timeval static MillisToTimeval(int64_t nTimeout)
struct timeval MillisToTimeval(int64_t nTimeout)
{
struct timeval timeout;
timeout.tv_sec = nTimeout / 1000;
@@ -1346,6 +1343,11 @@ bool operator!=(const CSubNet& a, const CSubNet& b)
return !(a==b);
}
bool operator<(const CSubNet& a, const CSubNet& b)
{
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
}
#ifdef WIN32
std::string NetworkErrorString(int err)
{

View File

@@ -125,6 +125,7 @@ class CSubNet
friend bool operator==(const CSubNet& a, const CSubNet& b);
friend bool operator!=(const CSubNet& a, const CSubNet& b);
friend bool operator<(const CSubNet& a, const CSubNet& b);
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
@@ -202,5 +203,9 @@ std::string NetworkErrorString(int err);
bool CloseSocket(SOCKET& hSocket);
/** Disable or enable blocking-mode for a socket */
bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
struct timeval MillisToTimeval(int64_t nTimeout);
#endif // BITCOIN_NETBASE_H

View File

@@ -39,6 +39,11 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str
return false;
}
static bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
{
return noui_ThreadSafeMessageBox(message, caption, style);
}
static void noui_InitMessage(const std::string& message)
{
LogPrintf("init message: %s\n", message);
@@ -48,5 +53,6 @@ void noui_connect()
{
// Connect bitcoind signal handlers
uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);
uiInterface.InitMessage.connect(noui_InitMessage);
}

View File

@@ -16,6 +16,10 @@
#include "sodium.h"
#ifdef ENABLE_RUST
#include "librustzcash.h"
#endif // ENABLE_RUST
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();
@@ -95,6 +99,14 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param
// H(I||V||...
crypto_generichash_blake2b_update(&state, (unsigned char*)&ss[0], ss.size());
#ifdef ENABLE_RUST
// Ensure that our Rust interactions are working in production builds. This is
// temporary and should be removed.
{
assert(librustzcash_xor(0x0f0f0f0f0f0f0f0f, 0x1111111111111111) == 0x1e1e1e1e1e1e1e1e);
}
#endif // ENABLE_RUST
bool isValid;
EhIsValidSolution(n, k, state, pblock->nSolution, isValid);
if (!isValid)

View File

@@ -278,10 +278,13 @@ public:
// which has units satoshis-per-kilobyte.
// If you'd pay more than 1/3 in fees
// to spend something, then we consider it dust.
// A typical txout is 34 bytes big, and will
// A typical spendable txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend:
// so dust is a txout less than 546 satoshis
// so dust is a spendable txout less than 54 satoshis
// with default minRelayTxFee.
if (scriptPubKey.IsUnspendable())
return 0;
size_t nSize = GetSerializeSize(SER_DISK,0)+148u;
return 3*minRelayTxFee.GetFee(nSize);
}

View File

@@ -1,313 +0,0 @@
// Copyright (c) 2011-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "addressbookpage.h"
#include "ui_addressbookpage.h"
#include "addresstablemodel.h"
#include "bitcoingui.h"
#include "csvmodelwriter.h"
#include "editaddressdialog.h"
#include "guiutil.h"
#include "scicon.h"
#include <QIcon>
#include <QMenu>
#include <QMessageBox>
#include <QSortFilterProxyModel>
AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
QDialog(parent),
ui(new Ui::AddressBookPage),
model(0),
mode(mode),
tab(tab)
{
ui->setupUi(this);
#ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
ui->newAddress->setIcon(QIcon());
ui->copyAddress->setIcon(QIcon());
ui->deleteAddress->setIcon(QIcon());
ui->exportButton->setIcon(QIcon());
#else
ui->newAddress->setIcon(SingleColorIcon(":/icons/add"));
ui->copyAddress->setIcon(SingleColorIcon(":/icons/editcopy"));
ui->deleteAddress->setIcon(SingleColorIcon(":/icons/remove"));
ui->exportButton->setIcon(SingleColorIcon(":/icons/export"));
#endif
switch(mode)
{
case ForSelection:
switch(tab)
{
case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break;
case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break;
}
connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableView->setFocus();
ui->closeButton->setText(tr("C&hoose"));
ui->exportButton->hide();
break;
case ForEditing:
switch(tab)
{
case SendingTab: setWindowTitle(tr("Sending addresses")); break;
case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break;
}
break;
}
switch(tab)
{
case SendingTab:
ui->labelExplanation->setText(tr("These are your Bitcoin addresses for sending payments. Always check the amount and the receiving address before sending coins."));
ui->deleteAddress->setVisible(true);
break;
case ReceivingTab:
ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction."));
ui->deleteAddress->setVisible(false);
break;
}
// Context menu actions
QAction *copyAddressAction = new QAction(tr("&Copy Address"), this);
QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
QAction *editAction = new QAction(tr("&Edit"), this);
deleteAction = new QAction(ui->deleteAddress->text(), this);
// Build context menu
contextMenu = new QMenu();
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(editAction);
if(tab == SendingTab)
contextMenu->addAction(deleteAction);
contextMenu->addSeparator();
// Connect signals for context menu actions
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction()));
connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction()));
connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteAddress_clicked()));
connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(accept()));
}
AddressBookPage::~AddressBookPage()
{
delete ui;
}
void AddressBookPage::setModel(AddressTableModel *model)
{
this->model = model;
if(!model)
return;
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(model);
proxyModel->setDynamicSortFilter(true);
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
switch(tab)
{
case ReceivingTab:
// Receive filter
proxyModel->setFilterRole(AddressTableModel::TypeRole);
proxyModel->setFilterFixedString(AddressTableModel::Receive);
break;
case SendingTab:
// Send filter
proxyModel->setFilterRole(AddressTableModel::TypeRole);
proxyModel->setFilterFixedString(AddressTableModel::Send);
break;
}
ui->tableView->setModel(proxyModel);
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
// Set column widths
#if QT_VERSION < 0x050000
ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
#else
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
#endif
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(selectionChanged()));
// Select row for newly created address
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(selectNewAddress(QModelIndex,int,int)));
selectionChanged();
}
void AddressBookPage::on_copyAddress_clicked()
{
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
}
void AddressBookPage::onCopyLabelAction()
{
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
}
void AddressBookPage::onEditAction()
{
if(!model)
return;
if(!ui->tableView->selectionModel())
return;
QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
if(indexes.isEmpty())
return;
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::EditSendingAddress :
EditAddressDialog::EditReceivingAddress, this);
dlg.setModel(model);
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
dlg.loadRow(origIndex.row());
dlg.exec();
}
void AddressBookPage::on_newAddress_clicked()
{
if(!model)
return;
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::NewSendingAddress :
EditAddressDialog::NewReceivingAddress, this);
dlg.setModel(model);
if(dlg.exec())
{
newAddressToSelect = dlg.getAddress();
}
}
void AddressBookPage::on_deleteAddress_clicked()
{
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
QModelIndexList indexes = table->selectionModel()->selectedRows();
if(!indexes.isEmpty())
{
table->model()->removeRow(indexes.at(0).row());
}
}
void AddressBookPage::selectionChanged()
{
// Set button states based on selected tab and selection
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
if(table->selectionModel()->hasSelection())
{
switch(tab)
{
case SendingTab:
// In sending tab, allow deletion of selection
ui->deleteAddress->setEnabled(true);
ui->deleteAddress->setVisible(true);
deleteAction->setEnabled(true);
break;
case ReceivingTab:
// Deleting receiving addresses, however, is not allowed
ui->deleteAddress->setEnabled(false);
ui->deleteAddress->setVisible(false);
deleteAction->setEnabled(false);
break;
}
ui->copyAddress->setEnabled(true);
}
else
{
ui->deleteAddress->setEnabled(false);
ui->copyAddress->setEnabled(false);
}
}
void AddressBookPage::done(int retval)
{
QTableView *table = ui->tableView;
if(!table->selectionModel() || !table->model())
return;
// Figure out which address was selected, and return it
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
Q_FOREACH (const QModelIndex& index, indexes) {
QVariant address = table->model()->data(index);
returnValue = address.toString();
}
if(returnValue.isEmpty())
{
// If no address entry selected, return rejected
retval = Rejected;
}
QDialog::done(retval);
}
void AddressBookPage::on_exportButton_clicked()
{
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Address List"), QString(),
tr("Comma separated file (*.csv)"), NULL);
if (filename.isNull())
return;
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(proxyModel);
writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
if(!writer.write()) {
QMessageBox::critical(this, tr("Exporting Failed"),
tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
}
}
void AddressBookPage::contextualMenu(const QPoint &point)
{
QModelIndex index = ui->tableView->indexAt(point);
if(index.isValid())
{
contextMenu->exec(QCursor::pos());
}
}
void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
{
QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
{
// Select row of newly created address, once
ui->tableView->setFocus();
ui->tableView->selectRow(idx.row());
newAddressToSelect.clear();
}
}

View File

@@ -1,87 +0,0 @@
// Copyright (c) 2011-2013 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_QT_ADDRESSBOOKPAGE_H
#define BITCOIN_QT_ADDRESSBOOKPAGE_H
#include <QDialog>
class AddressTableModel;
class OptionsModel;
namespace Ui {
class AddressBookPage;
}
QT_BEGIN_NAMESPACE
class QItemSelection;
class QMenu;
class QModelIndex;
class QSortFilterProxyModel;
class QTableView;
QT_END_NAMESPACE
/** Widget that shows a list of sending or receiving addresses.
*/
class AddressBookPage : public QDialog
{
Q_OBJECT
public:
enum Tabs {
SendingTab = 0,
ReceivingTab = 1
};
enum Mode {
ForSelection, /**< Open address book to pick address */
ForEditing /**< Open address book for editing */
};
explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent);
~AddressBookPage();
void setModel(AddressTableModel *model);
const QString &getReturnValue() const { return returnValue; }
public Q_SLOTS:
void done(int retval);
private:
Ui::AddressBookPage *ui;
AddressTableModel *model;
Mode mode;
Tabs tab;
QString returnValue;
QSortFilterProxyModel *proxyModel;
QMenu *contextMenu;
QAction *deleteAction; // to be able to explicitly disable it
QString newAddressToSelect;
private Q_SLOTS:
/** Delete currently selected address entry */
void on_deleteAddress_clicked();
/** Create a new address for receiving coins and / or add a new address book entry */
void on_newAddress_clicked();
/** Copy address of currently selected address entry to clipboard */
void on_copyAddress_clicked();
/** Copy label of currently selected address entry to clipboard (no button) */
void onCopyLabelAction();
/** Edit currently selected address entry (no button) */
void onEditAction();
/** Export button clicked */
void on_exportButton_clicked();
/** Set button states based on selected tab and selection */
void selectionChanged();
/** Spawn contextual menu (right mouse menu) for address book entry */
void contextualMenu(const QPoint &point);
/** New entry/entries were added to address table */
void selectNewAddress(const QModelIndex &parent, int begin, int /*end*/);
Q_SIGNALS:
void sendCoins(QString addr);
};
#endif // BITCOIN_QT_ADDRESSBOOKPAGE_H

View File

@@ -1,454 +0,0 @@
// Copyright (c) 2011-2013 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 "addresstablemodel.h"
#include "guiutil.h"
#include "walletmodel.h"
#include "base58.h"
#include "wallet/wallet.h"
#include <boost/foreach.hpp>
#include <QFont>
#include <QDebug>
const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";
struct AddressTableEntry
{
enum Type {
Sending,
Receiving,
Hidden /* QSortFilterProxyModel will filter these out */
};
Type type;
QString label;
QString address;
AddressTableEntry() {}
AddressTableEntry(Type type, const QString &label, const QString &address):
type(type), label(label), address(address) {}
};
struct AddressTableEntryLessThan
{
bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
{
return a.address < b.address;
}
bool operator()(const AddressTableEntry &a, const QString &b) const
{
return a.address < b;
}
bool operator()(const QString &a, const AddressTableEntry &b) const
{
return a < b.address;
}
};
/* Determine address type from address purpose */
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
{
AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
// "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
if (strPurpose == "send")
addressType = AddressTableEntry::Sending;
else if (strPurpose == "receive")
addressType = AddressTableEntry::Receiving;
else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
return addressType;
}
// Private implementation
class AddressTablePriv
{
public:
CWallet *wallet;
QList<AddressTableEntry> cachedAddressTable;
AddressTableModel *parent;
AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
wallet(wallet), parent(parent) {}
void refreshAddressTable()
{
cachedAddressTable.clear();
{
LOCK(wallet->cs_wallet);
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
bool fMine = IsMine(*wallet, address.Get());
AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(item.second.purpose), fMine);
const std::string& strName = item.second.name;
cachedAddressTable.append(AddressTableEntry(addressType,
QString::fromStdString(strName),
QString::fromStdString(address.ToString())));
}
}
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
// Even though the map is already sorted this re-sorting step is needed because the originating map
// is sorted by binary address, not by base58() address.
qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
}
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
{
// Find address / label in model
QList<AddressTableEntry>::iterator lower = qLowerBound(
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
QList<AddressTableEntry>::iterator upper = qUpperBound(
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
int lowerIndex = (lower - cachedAddressTable.begin());
int upperIndex = (upper - cachedAddressTable.begin());
bool inModel = (lower != upper);
AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
switch(status)
{
case CT_NEW:
if(inModel)
{
qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
break;
}
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
parent->endInsertRows();
break;
case CT_UPDATED:
if(!inModel)
{
qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
break;
}
lower->type = newEntryType;
lower->label = label;
parent->emitDataChanged(lowerIndex);
break;
case CT_DELETED:
if(!inModel)
{
qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
break;
}
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
cachedAddressTable.erase(lower, upper);
parent->endRemoveRows();
break;
}
}
int size()
{
return cachedAddressTable.size();
}
AddressTableEntry *index(int idx)
{
if(idx >= 0 && idx < cachedAddressTable.size())
{
return &cachedAddressTable[idx];
}
else
{
return 0;
}
}
};
AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(wallet, this);
priv->refreshAddressTable();
}
AddressTableModel::~AddressTableModel()
{
delete priv;
}
int AddressTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return priv->size();
}
int AddressTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return columns.length();
}
QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
if(role == Qt::DisplayRole || role == Qt::EditRole)
{
switch(index.column())
{
case Label:
if(rec->label.isEmpty() && role == Qt::DisplayRole)
{
return tr("(no label)");
}
else
{
return rec->label;
}
case Address:
return rec->address;
}
}
else if (role == Qt::FontRole)
{
QFont font;
if(index.column() == Address)
{
font = GUIUtil::bitcoinAddressFont();
}
return font;
}
else if (role == TypeRole)
{
switch(rec->type)
{
case AddressTableEntry::Sending:
return Send;
case AddressTableEntry::Receiving:
return Receive;
default: break;
}
}
return QVariant();
}
bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(!index.isValid())
return false;
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
editStatus = OK;
if(role == Qt::EditRole)
{
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
if(index.column() == Label)
{
// Do nothing, if old label == new label
if(rec->label == value.toString())
{
editStatus = NO_CHANGES;
return false;
}
wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} else if(index.column() == Address) {
CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
// Refuse to set invalid address, set error status and return false
if(boost::get<CNoDestination>(&newAddress))
{
editStatus = INVALID_ADDRESS;
return false;
}
// Do nothing, if old address == new address
else if(newAddress == curAddress)
{
editStatus = NO_CHANGES;
return false;
}
// Check for duplicate addresses to prevent accidental deletion of addresses, if you try
// to paste an existing address over another address (with a different label)
else if(wallet->mapAddressBook.count(newAddress))
{
editStatus = DUPLICATE_ADDRESS;
return false;
}
// Double-check that we're not overwriting a receiving address
else if(rec->type == AddressTableEntry::Sending)
{
// Remove old entry
wallet->DelAddressBook(curAddress);
// Add new entry with new address
wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose);
}
}
return true;
}
return false;
}
QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal)
{
if(role == Qt::DisplayRole && section < columns.size())
{
return columns[section];
}
}
return QVariant();
}
Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
{
if(!index.isValid())
return 0;
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
// Can edit address and label for sending addresses,
// and only label for receiving addresses.
if(rec->type == AddressTableEntry::Sending ||
(rec->type == AddressTableEntry::Receiving && index.column()==Label))
{
retval |= Qt::ItemIsEditable;
}
return retval;
}
QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
AddressTableEntry *data = priv->index(row);
if(data)
{
return createIndex(row, column, priv->index(row));
}
else
{
return QModelIndex();
}
}
void AddressTableModel::updateEntry(const QString &address,
const QString &label, bool isMine, const QString &purpose, int status)
{
// Update address book model from Bitcoin core
priv->updateEntry(address, label, isMine, purpose, status);
}
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
{
std::string strLabel = label.toStdString();
std::string strAddress = address.toStdString();
editStatus = OK;
if(type == Send)
{
if(!walletModel->validateAddress(address))
{
editStatus = INVALID_ADDRESS;
return QString();
}
// Check for duplicate addresses
{
LOCK(wallet->cs_wallet);
if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
}
}
}
else if(type == Receive)
{
// Generate a new address to associate with given label
CPubKey newKey;
if(!wallet->GetKeyFromPool(newKey))
{
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
{
// Unlock wallet failed or was cancelled
editStatus = WALLET_UNLOCK_FAILURE;
return QString();
}
if(!wallet->GetKeyFromPool(newKey))
{
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
}
strAddress = CBitcoinAddress(newKey.GetID()).ToString();
}
else
{
return QString();
}
// Add entry
{
LOCK(wallet->cs_wallet);
wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
(type == Send ? "send" : "receive"));
}
return QString::fromStdString(strAddress);
}
bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);
AddressTableEntry *rec = priv->index(row);
if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
{
// Can only remove one row at a time, and cannot remove rows not in model.
// Also refuse to remove receiving addresses.
return false;
}
{
LOCK(wallet->cs_wallet);
wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
}
return true;
}
/* Look up label for address in address book, if not found return empty string.
*/
QString AddressTableModel::labelForAddress(const QString &address) const
{
{
LOCK(wallet->cs_wallet);
CBitcoinAddress address_parsed(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
if (mi != wallet->mapAddressBook.end())
{
return QString::fromStdString(mi->second.name);
}
}
return QString();
}
int AddressTableModel::lookupAddress(const QString &address) const
{
QModelIndexList lst = match(index(0, Address, QModelIndex()),
Qt::EditRole, address, 1, Qt::MatchExactly);
if(lst.isEmpty())
{
return -1;
}
else
{
return lst.at(0).row();
}
}
void AddressTableModel::emitDataChanged(int idx)
{
Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}

View File

@@ -1,95 +0,0 @@
// Copyright (c) 2011-2013 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_QT_ADDRESSTABLEMODEL_H
#define BITCOIN_QT_ADDRESSTABLEMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
class AddressTablePriv;
class WalletModel;
class CWallet;
/**
Qt model of the address book in the core. This allows views to access and modify the address book.
*/
class AddressTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0);
~AddressTableModel();
enum ColumnIndex {
Label = 0, /**< User specified label */
Address = 1 /**< Bitcoin address */
};
enum RoleIndex {
TypeRole = Qt::UserRole /**< Type of address (#Send or #Receive) */
};
/** Return status of edit/insert operation */
enum EditStatus {
OK, /**< Everything ok */
NO_CHANGES, /**< No changes were made during edit operation */
INVALID_ADDRESS, /**< Unparseable address */
DUPLICATE_ADDRESS, /**< Address already in address book */
WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */
KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */
};
static const QString Send; /**< Specifies send address */
static const QString Receive; /**< Specifies receive address */
/** @name Methods overridden from QAbstractTableModel
@{*/
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
Qt::ItemFlags flags(const QModelIndex &index) const;
/*@}*/
/* Add an address to the model.
Returns the added address on success, and an empty string otherwise.
*/
QString addRow(const QString &type, const QString &label, const QString &address);
/* Look up label for address in address book, if not found return empty string.
*/
QString labelForAddress(const QString &address) const;
/* Look up row index of an address in the model.
Return -1 if not found.
*/
int lookupAddress(const QString &address) const;
EditStatus getEditStatus() const { return editStatus; }
private:
WalletModel *walletModel;
CWallet *wallet;
AddressTablePriv *priv;
QStringList columns;
EditStatus editStatus;
/** Notify listeners that data changed. */
void emitDataChanged(int index);
public Q_SLOTS:
/* Update address list from core.
*/
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
friend class AddressTablePriv;
};
#endif // BITCOIN_QT_ADDRESSTABLEMODEL_H

View File

@@ -1,258 +0,0 @@
// Copyright (c) 2011-2013 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 "askpassphrasedialog.h"
#include "ui_askpassphrasedialog.h"
#include "guiconstants.h"
#include "walletmodel.h"
#include "support/allocators/secure.h"
#include <QKeyEvent>
#include <QMessageBox>
#include <QPushButton>
AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) :
QDialog(parent),
ui(new Ui::AskPassphraseDialog),
mode(mode),
model(0),
fCapsLock(false)
{
ui->setupUi(this);
ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());
ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);
// Setup Caps Lock detection.
ui->passEdit1->installEventFilter(this);
ui->passEdit2->installEventFilter(this);
ui->passEdit3->installEventFilter(this);
switch(mode)
{
case Encrypt: // Ask passphrase x2
ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
ui->passLabel1->hide();
ui->passEdit1->hide();
setWindowTitle(tr("Encrypt wallet"));
break;
case Unlock: // Ask passphrase
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
ui->passLabel2->hide();
ui->passEdit2->hide();
ui->passLabel3->hide();
ui->passEdit3->hide();
setWindowTitle(tr("Unlock wallet"));
break;
case Decrypt: // Ask passphrase
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
ui->passLabel2->hide();
ui->passEdit2->hide();
ui->passLabel3->hide();
ui->passEdit3->hide();
setWindowTitle(tr("Decrypt wallet"));
break;
case ChangePass: // Ask old passphrase + new passphrase x2
setWindowTitle(tr("Change passphrase"));
ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase to the wallet."));
break;
}
textChanged();
connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
}
AskPassphraseDialog::~AskPassphraseDialog()
{
// Attempt to overwrite text so that they do not linger around in memory
ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
delete ui;
}
void AskPassphraseDialog::setModel(WalletModel *model)
{
this->model = model;
}
void AskPassphraseDialog::accept()
{
SecureString oldpass, newpass1, newpass2;
if(!model)
return;
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
newpass2.reserve(MAX_PASSPHRASE_SIZE);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make this input mlock()'d to begin with.
oldpass.assign(ui->passEdit1->text().toStdString().c_str());
newpass1.assign(ui->passEdit2->text().toStdString().c_str());
newpass2.assign(ui->passEdit3->text().toStdString().c_str());
switch(mode)
{
case Encrypt: {
if(newpass1.empty() || newpass2.empty())
{
// Cannot encrypt with empty passphrase
break;
}
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
tr("Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!") + "<br><br>" + tr("Are you sure you wish to encrypt your wallet?"),
QMessageBox::Yes|QMessageBox::Cancel,
QMessageBox::Cancel);
if(retval == QMessageBox::Yes)
{
if(newpass1 == newpass2)
{
if(model->setWalletEncrypted(true, newpass1))
{
QMessageBox::warning(this, tr("Wallet encrypted"),
"<qt>" +
tr("Bitcoin Core will close now to finish the encryption process. "
"Remember that encrypting your wallet cannot fully protect "
"your bitcoins from being stolen by malware infecting your computer.") +
"<br><br><b>" +
tr("IMPORTANT: Any previous backups you have made of your wallet file "
"should be replaced with the newly generated, encrypted wallet file. "
"For security reasons, previous backups of the unencrypted wallet file "
"will become useless as soon as you start using the new, encrypted wallet.") +
"</b></qt>");
QApplication::quit();
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
}
QDialog::accept(); // Success
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The supplied passphrases do not match."));
}
}
else
{
QDialog::reject(); // Cancelled
}
} break;
case Unlock:
if(!model->setWalletLocked(false, oldpass))
{
QMessageBox::critical(this, tr("Wallet unlock failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
else
{
QDialog::accept(); // Success
}
break;
case Decrypt:
if(!model->setWalletEncrypted(false, oldpass))
{
QMessageBox::critical(this, tr("Wallet decryption failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
else
{
QDialog::accept(); // Success
}
break;
case ChangePass:
if(newpass1 == newpass2)
{
if(model->changePassphrase(oldpass, newpass1))
{
QMessageBox::information(this, tr("Wallet encrypted"),
tr("Wallet passphrase was successfully changed."));
QDialog::accept(); // Success
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The supplied passphrases do not match."));
}
break;
}
}
void AskPassphraseDialog::textChanged()
{
// Validate input, set Ok button to enabled when acceptable
bool acceptable = false;
switch(mode)
{
case Encrypt: // New passphrase x2
acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
case Unlock: // Old passphrase x1
case Decrypt:
acceptable = !ui->passEdit1->text().isEmpty();
break;
case ChangePass: // Old passphrase x1, new passphrase x2
acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
}
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
}
bool AskPassphraseDialog::event(QEvent *event)
{
// Detect Caps Lock key press.
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_CapsLock) {
fCapsLock = !fCapsLock;
}
if (fCapsLock) {
ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
} else {
ui->capsLabel->clear();
}
}
return QWidget::event(event);
}
bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event)
{
/* Detect Caps Lock.
* There is no good OS-independent way to check a key state in Qt, but we
* can detect Caps Lock by checking for the following condition:
* Shift key is down and the result is a lower case character, or
* Shift key is not down and the result is an upper case character.
*/
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
QString str = ke->text();
if (str.length() != 0) {
const QChar *psz = str.unicode();
bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
fCapsLock = true;
ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
} else if (psz->isLetter()) {
fCapsLock = false;
ui->capsLabel->clear();
}
}
}
return QDialog::eventFilter(object, event);
}

View File

@@ -1,51 +0,0 @@
// Copyright (c) 2011-2013 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_QT_ASKPASSPHRASEDIALOG_H
#define BITCOIN_QT_ASKPASSPHRASEDIALOG_H
#include <QDialog>
class WalletModel;
namespace Ui {
class AskPassphraseDialog;
}
/** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase.
*/
class AskPassphraseDialog : public QDialog
{
Q_OBJECT
public:
enum Mode {
Encrypt, /**< Ask passphrase twice and encrypt */
Unlock, /**< Ask passphrase and unlock */
ChangePass, /**< Ask old passphrase + new passphrase twice */
Decrypt /**< Ask passphrase and decrypt wallet */
};
explicit AskPassphraseDialog(Mode mode, QWidget *parent);
~AskPassphraseDialog();
void accept();
void setModel(WalletModel *model);
private:
Ui::AskPassphraseDialog *ui;
Mode mode;
WalletModel *model;
bool fCapsLock;
private Q_SLOTS:
void textChanged();
protected:
bool event(QEvent *event);
bool eventFilter(QObject *object, QEvent *event);
};
#endif // BITCOIN_QT_ASKPASSPHRASEDIALOG_H

View File

@@ -1,88 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/icons">
<file alias="bitcoin">res/icons/bitcoin.png</file>
<file alias="address-book">res/icons/address-book.png</file>
<file alias="quit">res/icons/quit.png</file>
<file alias="send">res/icons/send.png</file>
<file alias="connect_0">res/icons/connect0.png</file>
<file alias="connect_1">res/icons/connect1.png</file>
<file alias="connect_2">res/icons/connect2.png</file>
<file alias="connect_3">res/icons/connect3.png</file>
<file alias="connect_4">res/icons/connect4.png</file>
<file alias="transaction_0">res/icons/transaction0.png</file>
<file alias="transaction_confirmed">res/icons/transaction2.png</file>
<file alias="transaction_conflicted">res/icons/transaction_conflicted.png</file>
<file alias="transaction_1">res/icons/clock1.png</file>
<file alias="transaction_2">res/icons/clock2.png</file>
<file alias="transaction_3">res/icons/clock3.png</file>
<file alias="transaction_4">res/icons/clock4.png</file>
<file alias="transaction_5">res/icons/clock5.png</file>
<file alias="eye">res/icons/eye.png</file>
<file alias="eye_minus">res/icons/eye_minus.png</file>
<file alias="eye_plus">res/icons/eye_plus.png</file>
<file alias="options">res/icons/configure.png</file>
<file alias="receiving_addresses">res/icons/receive.png</file>
<file alias="editpaste">res/icons/editpaste.png</file>
<file alias="editcopy">res/icons/editcopy.png</file>
<file alias="add">res/icons/add.png</file>
<file alias="edit">res/icons/edit.png</file>
<file alias="history">res/icons/history.png</file>
<file alias="overview">res/icons/overview.png</file>
<file alias="export">res/icons/export.png</file>
<file alias="synced">res/icons/synced.png</file>
<file alias="remove">res/icons/remove.png</file>
<file alias="tx_mined">res/icons/tx_mined.png</file>
<file alias="tx_input">res/icons/tx_input.png</file>
<file alias="tx_output">res/icons/tx_output.png</file>
<file alias="tx_inout">res/icons/tx_inout.png</file>
<file alias="lock_closed">res/icons/lock_closed.png</file>
<file alias="lock_open">res/icons/lock_open.png</file>
<file alias="key">res/icons/key.png</file>
<file alias="filesave">res/icons/filesave.png</file>
<file alias="debugwindow">res/icons/debugwindow.png</file>
<file alias="open">res/icons/open.png</file>
<file alias="info">res/icons/info.png</file>
<file alias="about">res/icons/about.png</file>
<file alias="about_qt">res/icons/about_qt.png</file>
<file alias="verify">res/icons/verify.png</file>
<file alias="warning">res/icons/warning.png</file>
</qresource>
<qresource prefix="/movies">
<file alias="spinner-000">res/movies/spinner-000.png</file>
<file alias="spinner-001">res/movies/spinner-001.png</file>
<file alias="spinner-002">res/movies/spinner-002.png</file>
<file alias="spinner-003">res/movies/spinner-003.png</file>
<file alias="spinner-004">res/movies/spinner-004.png</file>
<file alias="spinner-005">res/movies/spinner-005.png</file>
<file alias="spinner-006">res/movies/spinner-006.png</file>
<file alias="spinner-007">res/movies/spinner-007.png</file>
<file alias="spinner-008">res/movies/spinner-008.png</file>
<file alias="spinner-009">res/movies/spinner-009.png</file>
<file alias="spinner-010">res/movies/spinner-010.png</file>
<file alias="spinner-011">res/movies/spinner-011.png</file>
<file alias="spinner-012">res/movies/spinner-012.png</file>
<file alias="spinner-013">res/movies/spinner-013.png</file>
<file alias="spinner-014">res/movies/spinner-014.png</file>
<file alias="spinner-015">res/movies/spinner-015.png</file>
<file alias="spinner-016">res/movies/spinner-016.png</file>
<file alias="spinner-017">res/movies/spinner-017.png</file>
<file alias="spinner-018">res/movies/spinner-018.png</file>
<file alias="spinner-019">res/movies/spinner-019.png</file>
<file alias="spinner-020">res/movies/spinner-020.png</file>
<file alias="spinner-021">res/movies/spinner-021.png</file>
<file alias="spinner-022">res/movies/spinner-022.png</file>
<file alias="spinner-023">res/movies/spinner-023.png</file>
<file alias="spinner-024">res/movies/spinner-024.png</file>
<file alias="spinner-025">res/movies/spinner-025.png</file>
<file alias="spinner-026">res/movies/spinner-026.png</file>
<file alias="spinner-027">res/movies/spinner-027.png</file>
<file alias="spinner-028">res/movies/spinner-028.png</file>
<file alias="spinner-029">res/movies/spinner-029.png</file>
<file alias="spinner-030">res/movies/spinner-030.png</file>
<file alias="spinner-031">res/movies/spinner-031.png</file>
<file alias="spinner-032">res/movies/spinner-032.png</file>
<file alias="spinner-033">res/movies/spinner-033.png</file>
<file alias="spinner-034">res/movies/spinner-034.png</file>
<file alias="spinner-035">res/movies/spinner-035.png</file>
</qresource>
</RCC>

View File

@@ -1,75 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/translations">
<file alias="ach">locale/bitcoin_ach.qm</file>
<file alias="af_ZA">locale/bitcoin_af_ZA.qm</file>
<file alias="ar">locale/bitcoin_ar.qm</file>
<file alias="be_BY">locale/bitcoin_be_BY.qm</file>
<file alias="bg">locale/bitcoin_bg.qm</file>
<file alias="bs">locale/bitcoin_bs.qm</file>
<file alias="ca_ES">locale/bitcoin_ca_ES.qm</file>
<file alias="ca">locale/bitcoin_ca.qm</file>
<file alias="ca@valencia">locale/bitcoin_ca@valencia.qm</file>
<file alias="cmn">locale/bitcoin_cmn.qm</file>
<file alias="cs">locale/bitcoin_cs.qm</file>
<file alias="cy">locale/bitcoin_cy.qm</file>
<file alias="da">locale/bitcoin_da.qm</file>
<file alias="de">locale/bitcoin_de.qm</file>
<file alias="el_GR">locale/bitcoin_el_GR.qm</file>
<file alias="en">locale/bitcoin_en.qm</file>
<file alias="eo">locale/bitcoin_eo.qm</file>
<file alias="es_CL">locale/bitcoin_es_CL.qm</file>
<file alias="es_DO">locale/bitcoin_es_DO.qm</file>
<file alias="es_MX">locale/bitcoin_es_MX.qm</file>
<file alias="es">locale/bitcoin_es.qm</file>
<file alias="es_UY">locale/bitcoin_es_UY.qm</file>
<file alias="et">locale/bitcoin_et.qm</file>
<file alias="eu_ES">locale/bitcoin_eu_ES.qm</file>
<file alias="fa_IR">locale/bitcoin_fa_IR.qm</file>
<file alias="fa">locale/bitcoin_fa.qm</file>
<file alias="fi">locale/bitcoin_fi.qm</file>
<file alias="fr_CA">locale/bitcoin_fr_CA.qm</file>
<file alias="fr">locale/bitcoin_fr.qm</file>
<file alias="gl">locale/bitcoin_gl.qm</file>
<file alias="gu_IN">locale/bitcoin_gu_IN.qm</file>
<file alias="he">locale/bitcoin_he.qm</file>
<file alias="hi_IN">locale/bitcoin_hi_IN.qm</file>
<file alias="hr">locale/bitcoin_hr.qm</file>
<file alias="hu">locale/bitcoin_hu.qm</file>
<file alias="id_ID">locale/bitcoin_id_ID.qm</file>
<file alias="it">locale/bitcoin_it.qm</file>
<file alias="ja">locale/bitcoin_ja.qm</file>
<file alias="ka">locale/bitcoin_ka.qm</file>
<file alias="kk_KZ">locale/bitcoin_kk_KZ.qm</file>
<file alias="ko_KR">locale/bitcoin_ko_KR.qm</file>
<file alias="ky">locale/bitcoin_ky.qm</file>
<file alias="la">locale/bitcoin_la.qm</file>
<file alias="lt">locale/bitcoin_lt.qm</file>
<file alias="lv_LV">locale/bitcoin_lv_LV.qm</file>
<file alias="mn">locale/bitcoin_mn.qm</file>
<file alias="ms_MY">locale/bitcoin_ms_MY.qm</file>
<file alias="nb">locale/bitcoin_nb.qm</file>
<file alias="nl">locale/bitcoin_nl.qm</file>
<file alias="pam">locale/bitcoin_pam.qm</file>
<file alias="pl">locale/bitcoin_pl.qm</file>
<file alias="pt_BR">locale/bitcoin_pt_BR.qm</file>
<file alias="pt_PT">locale/bitcoin_pt_PT.qm</file>
<file alias="ro_RO">locale/bitcoin_ro_RO.qm</file>
<file alias="ru">locale/bitcoin_ru.qm</file>
<file alias="sah">locale/bitcoin_sah.qm</file>
<file alias="sk">locale/bitcoin_sk.qm</file>
<file alias="sl_SI">locale/bitcoin_sl_SI.qm</file>
<file alias="sq">locale/bitcoin_sq.qm</file>
<file alias="sr">locale/bitcoin_sr.qm</file>
<file alias="sv">locale/bitcoin_sv.qm</file>
<file alias="th_TH">locale/bitcoin_th_TH.qm</file>
<file alias="tr">locale/bitcoin_tr.qm</file>
<file alias="uk">locale/bitcoin_uk.qm</file>
<file alias="ur_PK">locale/bitcoin_ur_PK.qm</file>
<file alias="uz@Cyrl">locale/bitcoin_uz@Cyrl.qm</file>
<file alias="vi">locale/bitcoin_vi.qm</file>
<file alias="vi_VN">locale/bitcoin_vi_VN.qm</file>
<file alias="zh_CN">locale/bitcoin_zh_CN.qm</file>
<file alias="zh_HK">locale/bitcoin_zh_HK.qm</file>
<file alias="zh_TW">locale/bitcoin_zh_TW.qm</file>
</qresource>
</RCC>

View File

@@ -1,97 +0,0 @@
// Copyright (c) 2011-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 "bitcoinaddressvalidator.h"
#include "base58.h"
/* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
This is:
- All numbers except for '0'
- All upper-case letters except for 'I' and 'O'
- All lower-case letters except for 'l'
*/
BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
// Empty address is "intermediate" input
if (input.isEmpty())
return QValidator::Intermediate;
// Correction
for (int idx = 0; idx < input.size();)
{
bool removeChar = false;
QChar ch = input.at(idx);
// Corrections made are very conservative on purpose, to avoid
// users unexpectedly getting away with typos that would normally
// be detected, and thus sending to the wrong address.
switch(ch.unicode())
{
// Qt categorizes these as "Other_Format" not "Separator_Space"
case 0x200B: // ZERO WIDTH SPACE
case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
removeChar = true;
break;
default:
break;
}
// Remove whitespace
if (ch.isSpace())
removeChar = true;
// To next character
if (removeChar)
input.remove(idx, 1);
else
++idx;
}
// Validation
QValidator::State state = QValidator::Acceptable;
for (int idx = 0; idx < input.size(); ++idx)
{
int ch = input.at(idx).unicode();
if (((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') ||
(ch >= 'A' && ch<='Z')) &&
ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
{
// Alphanumeric and not a 'forbidden' character
}
else
{
state = QValidator::Invalid;
}
}
return state;
}
BitcoinAddressCheckValidator::BitcoinAddressCheckValidator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
// Validate the passed Bitcoin address
CBitcoinAddress addr(input.toStdString());
if (addr.IsValid())
return QValidator::Acceptable;
return QValidator::Invalid;
}

View File

@@ -1,35 +0,0 @@
// Copyright (c) 2011-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_QT_BITCOINADDRESSVALIDATOR_H
#define BITCOIN_QT_BITCOINADDRESSVALIDATOR_H
#include <QValidator>
/** Base58 entry widget validator, checks for valid characters and
* removes some whitespace.
*/
class BitcoinAddressEntryValidator : public QValidator
{
Q_OBJECT
public:
explicit BitcoinAddressEntryValidator(QObject *parent);
State validate(QString &input, int &pos) const;
};
/** Bitcoin address widget validator, checks for a valid bitcoin address.
*/
class BitcoinAddressCheckValidator : public QValidator
{
Q_OBJECT
public:
explicit BitcoinAddressCheckValidator(QObject *parent);
State validate(QString &input, int &pos) const;
};
#endif // BITCOIN_QT_BITCOINADDRESSVALIDATOR_H

View File

@@ -1,302 +0,0 @@
// Copyright (c) 2011-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 "bitcoinamountfield.h"
#include "bitcoinunits.h"
#include "guiconstants.h"
#include "qvaluecombobox.h"
#include <QApplication>
#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>
/** QSpinBox that uses fixed-point numbers internally and uses our own
* formatting/parsing functions.
*/
class AmountSpinBox: public QAbstractSpinBox
{
Q_OBJECT
public:
explicit AmountSpinBox(QWidget *parent):
QAbstractSpinBox(parent),
currentUnit(BitcoinUnits::BTC),
singleStep(100000) // satoshis
{
setAlignment(Qt::AlignRight);
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
}
QValidator::State validate(QString &text, int &pos) const
{
if(text.isEmpty())
return QValidator::Intermediate;
bool valid = false;
parse(text, &valid);
/* Make sure we return Intermediate so that fixup() is called on defocus */
return valid ? QValidator::Intermediate : QValidator::Invalid;
}
void fixup(QString &input) const
{
bool valid = false;
CAmount val = parse(input, &valid);
if(valid)
{
input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
lineEdit()->setText(input);
}
}
CAmount value(bool *valid_out=0) const
{
return parse(text(), valid_out);
}
void setValue(const CAmount& value)
{
lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
Q_EMIT valueChanged();
}
void stepBy(int steps)
{
bool valid = false;
CAmount val = value(&valid);
val = val + steps * singleStep;
val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney());
setValue(val);
}
void setDisplayUnit(int unit)
{
bool valid = false;
CAmount val = value(&valid);
currentUnit = unit;
if(valid)
setValue(val);
else
clear();
}
void setSingleStep(const CAmount& step)
{
singleStep = step;
}
QSize minimumSizeHint() const
{
if(cachedMinimumSizeHint.isEmpty())
{
ensurePolished();
const QFontMetrics fm(fontMetrics());
int h = lineEdit()->minimumSizeHint().height();
int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
initStyleOption(&opt);
QSize hint(w, h);
QSize extra(35, 6);
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
// get closer to final result by repeating the calculation
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
hint += extra;
hint.setHeight(h);
opt.rect = rect();
cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
.expandedTo(QApplication::globalStrut());
}
return cachedMinimumSizeHint;
}
private:
int currentUnit;
CAmount singleStep;
mutable QSize cachedMinimumSizeHint;
/**
* Parse a string into a number of base monetary units and
* return validity.
* @note Must return 0 if !valid.
*/
CAmount parse(const QString &text, bool *valid_out=0) const
{
CAmount val = 0;
bool valid = BitcoinUnits::parse(currentUnit, text, &val);
if(valid)
{
if(val < 0 || val > BitcoinUnits::maxMoney())
valid = false;
}
if(valid_out)
*valid_out = valid;
return valid ? val : 0;
}
protected:
bool event(QEvent *event)
{
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Comma)
{
// Translate a comma into a period
QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
return QAbstractSpinBox::event(&periodKeyEvent);
}
}
return QAbstractSpinBox::event(event);
}
StepEnabled stepEnabled() const
{
if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
return StepNone;
if (text().isEmpty()) // Allow step-up with empty field
return StepUpEnabled;
StepEnabled rv = 0;
bool valid = false;
CAmount val = value(&valid);
if(valid)
{
if(val > 0)
rv |= StepDownEnabled;
if(val < BitcoinUnits::maxMoney())
rv |= StepUpEnabled;
}
return rv;
}
Q_SIGNALS:
void valueChanged();
};
#include "bitcoinamountfield.moc"
BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
QWidget(parent),
amount(0)
{
amount = new AmountSpinBox(this);
amount->setLocale(QLocale::c());
amount->installEventFilter(this);
amount->setMaximumWidth(170);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(amount);
unit = new QValueComboBox(this);
unit->setModel(new BitcoinUnits(this));
layout->addWidget(unit);
layout->addStretch(1);
layout->setContentsMargins(0,0,0,0);
setLayout(layout);
setFocusPolicy(Qt::TabFocus);
setFocusProxy(amount);
// If one if the widgets changes, the combined content changes as well
connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
// Set default based on configuration
unitChanged(unit->currentIndex());
}
void BitcoinAmountField::clear()
{
amount->clear();
unit->setCurrentIndex(0);
}
void BitcoinAmountField::setEnabled(bool fEnabled)
{
amount->setEnabled(fEnabled);
unit->setEnabled(fEnabled);
}
bool BitcoinAmountField::validate()
{
bool valid = false;
value(&valid);
setValid(valid);
return valid;
}
void BitcoinAmountField::setValid(bool valid)
{
if (valid)
amount->setStyleSheet("");
else
amount->setStyleSheet(STYLE_INVALID);
}
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn)
{
// Clear invalid flag on focus
setValid(true);
}
return QWidget::eventFilter(object, event);
}
QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
{
QWidget::setTabOrder(prev, amount);
QWidget::setTabOrder(amount, unit);
return unit;
}
CAmount BitcoinAmountField::value(bool *valid_out) const
{
return amount->value(valid_out);
}
void BitcoinAmountField::setValue(const CAmount& value)
{
amount->setValue(value);
}
void BitcoinAmountField::setReadOnly(bool fReadOnly)
{
amount->setReadOnly(fReadOnly);
}
void BitcoinAmountField::unitChanged(int idx)
{
// Use description tooltip for current unit for the combobox
unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
// Determine new unit ID
int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
amount->setDisplayUnit(newUnit);
}
void BitcoinAmountField::setDisplayUnit(int newUnit)
{
unit->setValue(newUnit);
}
void BitcoinAmountField::setSingleStep(const CAmount& step)
{
amount->setSingleStep(step);
}

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2011-2013 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_QT_BITCOINAMOUNTFIELD_H
#define BITCOIN_QT_BITCOINAMOUNTFIELD_H
#include "amount.h"
#include <QWidget>
class AmountSpinBox;
QT_BEGIN_NAMESPACE
class QValueComboBox;
QT_END_NAMESPACE
/** Widget for entering bitcoin amounts.
*/
class BitcoinAmountField: public QWidget
{
Q_OBJECT
// ugly hack: for some unknown reason CAmount (instead of qint64) does not work here as expected
// discussion: https://github.com/bitcoin/bitcoin/pull/5117
Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY valueChanged USER true)
public:
explicit BitcoinAmountField(QWidget *parent = 0);
CAmount value(bool *value=0) const;
void setValue(const CAmount& value);
/** Set single step in satoshis **/
void setSingleStep(const CAmount& step);
/** Make read-only **/
void setReadOnly(bool fReadOnly);
/** Mark current value as invalid in UI. */
void setValid(bool valid);
/** Perform input validation, mark field as invalid if entered value is not valid. */
bool validate();
/** Change unit used to display amount. */
void setDisplayUnit(int unit);
/** Make field empty and ready for new input. */
void clear();
/** Enable/Disable. */
void setEnabled(bool fEnabled);
/** Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907),
in these cases we have to set it up manually.
*/
QWidget *setupTabChain(QWidget *prev);
Q_SIGNALS:
void valueChanged();
protected:
/** Intercept focus-in event and ',' key presses */
bool eventFilter(QObject *object, QEvent *event);
private:
AmountSpinBox *amount;
QValueComboBox *unit;
private Q_SLOTS:
void unitChanged(int idx);
};
#endif // BITCOIN_QT_BITCOINAMOUNTFIELD_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,244 +0,0 @@
// Copyright (c) 2011-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_QT_BITCOINGUI_H
#define BITCOIN_QT_BITCOINGUI_H
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "amount.h"
#include <QLabel>
#include <QMainWindow>
#include <QMap>
#include <QMenu>
#include <QPoint>
#include <QSystemTrayIcon>
class ClientModel;
class NetworkStyle;
class Notificator;
class OptionsModel;
class RPCConsole;
class SendCoinsRecipient;
class UnitDisplayStatusBarControl;
class WalletFrame;
class WalletModel;
class CWallet;
QT_BEGIN_NAMESPACE
class QAction;
class QProgressBar;
class QProgressDialog;
QT_END_NAMESPACE
/**
Bitcoin GUI main class. This class represents the main window of the Bitcoin UI. It communicates with both the client and
wallet models to give the user an up-to-date view of the current core state.
*/
class BitcoinGUI : public QMainWindow
{
Q_OBJECT
public:
static const QString DEFAULT_WALLET;
explicit BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent = 0);
~BitcoinGUI();
/** Set the client model.
The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic.
*/
void setClientModel(ClientModel *clientModel);
#ifdef ENABLE_WALLET
/** Set the wallet model.
The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending
functionality.
*/
bool addWallet(const QString& name, WalletModel *walletModel);
bool setCurrentWallet(const QString& name);
void removeAllWallets();
#endif // ENABLE_WALLET
bool enableWallet;
protected:
void changeEvent(QEvent *e);
void closeEvent(QCloseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
bool eventFilter(QObject *object, QEvent *event);
private:
ClientModel *clientModel;
WalletFrame *walletFrame;
UnitDisplayStatusBarControl *unitDisplayControl;
QLabel *labelEncryptionIcon;
QLabel *labelConnectionsIcon;
QLabel *labelBlocksIcon;
QLabel *progressBarLabel;
QProgressBar *progressBar;
QProgressDialog *progressDialog;
QMenuBar *appMenuBar;
QAction *overviewAction;
QAction *historyAction;
QAction *quitAction;
QAction *sendCoinsAction;
QAction *sendCoinsMenuAction;
QAction *usedSendingAddressesAction;
QAction *usedReceivingAddressesAction;
QAction *signMessageAction;
QAction *verifyMessageAction;
QAction *aboutAction;
QAction *receiveCoinsAction;
QAction *receiveCoinsMenuAction;
QAction *optionsAction;
QAction *toggleHideAction;
QAction *encryptWalletAction;
QAction *backupWalletAction;
QAction *changePassphraseAction;
QAction *aboutQtAction;
QAction *openRPCConsoleAction;
QAction *openAction;
QAction *showHelpMessageAction;
QSystemTrayIcon *trayIcon;
QMenu *trayIconMenu;
Notificator *notificator;
RPCConsole *rpcConsole;
/** Keep track of previous number of blocks, to detect progress */
int prevBlocks;
int spinnerFrame;
/** Create the main UI actions. */
void createActions();
/** Create the menu bar and sub-menus. */
void createMenuBar();
/** Create the toolbars */
void createToolBars();
/** Create system tray icon and notification */
void createTrayIcon(const NetworkStyle *networkStyle);
/** Create system tray menu (or setup the dock menu) */
void createTrayIconMenu();
/** Enable or disable all wallet-related actions */
void setWalletActionsEnabled(bool enabled);
/** Connect core signals to GUI client */
void subscribeToCoreSignals();
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
Q_SIGNALS:
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate);
/** Notify the user of an event from the core network or transaction handling code.
@param[in] title the message box / notification title
@param[in] message the displayed text
@param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes)
@see CClientUIInterface::MessageBoxFlags
@param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only)
*/
void message(const QString &title, const QString &message, unsigned int style, bool *ret = NULL);
#ifdef ENABLE_WALLET
/** Set the encryption status as shown in the UI.
@param[in] status current encryption status
@see WalletModel::EncryptionStatus
*/
void setEncryptionStatus(int status);
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
/** Show incoming transaction notification for new transactions. */
void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label);
#endif // ENABLE_WALLET
private Q_SLOTS:
#ifdef ENABLE_WALLET
/** Switch to overview (home) page */
void gotoOverviewPage();
/** Switch to history (transactions) page */
void gotoHistoryPage();
/** Switch to receive coins page */
void gotoReceiveCoinsPage();
/** Switch to send coins page */
void gotoSendCoinsPage(QString addr = "");
/** Show Sign/Verify Message dialog and switch to sign message tab */
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Show open dialog */
void openClicked();
#endif // ENABLE_WALLET
/** Show configuration dialog */
void optionsClicked();
/** Show about dialog */
void aboutClicked();
/** Show debug window */
void showDebugWindow();
/** Show help message dialog */
void showHelpMessageClicked();
#ifndef Q_OS_MAC
/** Handle tray icon clicked */
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
#endif
/** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */
void showNormalIfMinimized(bool fToggleHidden = false);
/** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */
void toggleHidden();
/** called by a timer to check if fRequestShutdown has been set **/
void detectShutdown();
/** Show progress dialog e.g. for verifychain */
void showProgress(const QString &title, int nProgress);
};
class UnitDisplayStatusBarControl : public QLabel
{
Q_OBJECT
public:
explicit UnitDisplayStatusBarControl();
/** Lets the control know about the Options Model (and its signals) */
void setOptionsModel(OptionsModel *optionsModel);
protected:
/** So that it responds to left-button clicks */
void mousePressEvent(QMouseEvent *event);
private:
OptionsModel *optionsModel;
QMenu* menu;
/** Shows context menu with Display Unit options by the mouse coordinates */
void onDisplayUnitsClicked(const QPoint& point);
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
void createContextMenu();
private Q_SLOTS:
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
void updateDisplayUnit(int newUnits);
/** Tells underlying optionsModel to update its current display unit. */
void onMenuSelection(QAction* action);
};
#endif // BITCOIN_QT_BITCOINGUI_H

View File

@@ -1,220 +0,0 @@
// Copyright (c) 2011-2013 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 "bitcoinunits.h"
#include "primitives/transaction.h"
#include <QStringList>
BitcoinUnits::BitcoinUnits(QObject *parent):
QAbstractListModel(parent),
unitlist(availableUnits())
{
}
QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits()
{
QList<BitcoinUnits::Unit> unitlist;
unitlist.append(BTC);
unitlist.append(mBTC);
unitlist.append(uBTC);
return unitlist;
}
bool BitcoinUnits::valid(int unit)
{
switch(unit)
{
case BTC:
case mBTC:
case uBTC:
return true;
default:
return false;
}
}
QString BitcoinUnits::name(int unit)
{
switch(unit)
{
case BTC: return QString("BTC");
case mBTC: return QString("mBTC");
case uBTC: return QString::fromUtf8("μBTC");
default: return QString("???");
}
}
QString BitcoinUnits::description(int unit)
{
switch(unit)
{
case BTC: return QString("Bitcoins");
case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)");
case uBTC: return QString("Micro-Bitcoins (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
default: return QString("???");
}
}
qint64 BitcoinUnits::factor(int unit)
{
switch(unit)
{
case BTC: return 100000000;
case mBTC: return 100000;
case uBTC: return 100;
default: return 100000000;
}
}
int BitcoinUnits::decimals(int unit)
{
switch(unit)
{
case BTC: return 8;
case mBTC: return 5;
case uBTC: return 2;
default: return 0;
}
}
QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
if(!valid(unit))
return QString(); // Refuse to format invalid unit
qint64 n = (qint64)nIn;
qint64 coin = factor(unit);
int num_decimals = decimals(unit);
qint64 n_abs = (n > 0 ? n : -n);
qint64 quotient = n_abs / coin;
qint64 remainder = n_abs % coin;
QString quotient_str = QString::number(quotient);
QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
// Use SI-style thin space separators as these are locale independent and can't be
// confused with the decimal marker.
QChar thin_sp(THIN_SP_CP);
int q_size = quotient_str.size();
if (separators == separatorAlways || (separators == separatorStandard && q_size > 4))
for (int i = 3; i < q_size; i += 3)
quotient_str.insert(q_size - i, thin_sp);
if (n < 0)
quotient_str.insert(0, '-');
else if (fPlus && n > 0)
quotient_str.insert(0, '+');
return quotient_str + QString(".") + remainder_str;
}
// TODO: Review all remaining calls to BitcoinUnits::formatWithUnit to
// TODO: determine whether the output is used in a plain text context
// TODO: or an HTML context (and replace with
// TODO: BtcoinUnits::formatHtmlWithUnit in the latter case). Hopefully
// TODO: there aren't instances where the result could be used in
// TODO: either context.
// NOTE: Using formatWithUnit in an HTML context risks wrapping
// quantities at the thousands separator. More subtly, it also results
// in a standard space rather than a thin space, due to a bug in Qt's
// XML whitespace canonicalisation
//
// Please take care to use formatHtmlWithUnit instead, when
// appropriate.
QString BitcoinUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
return format(unit, amount, plussign, separators) + QString(" ") + name(unit);
}
QString BitcoinUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
QString str(formatWithUnit(unit, amount, plussign, separators));
str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
}
bool BitcoinUnits::parse(int unit, const QString &value, CAmount *val_out)
{
if(!valid(unit) || value.isEmpty())
return false; // Refuse to parse invalid unit or empty string
int num_decimals = decimals(unit);
// Ignore spaces and thin spaces when parsing
QStringList parts = removeSpaces(value).split(".");
if(parts.size() > 2)
{
return false; // More than one dot
}
QString whole = parts[0];
QString decimals;
if(parts.size() > 1)
{
decimals = parts[1];
}
if(decimals.size() > num_decimals)
{
return false; // Exceeds max precision
}
bool ok = false;
QString str = whole + decimals.leftJustified(num_decimals, '0');
if(str.size() > 18)
{
return false; // Longer numbers will exceed 63 bits
}
CAmount retvalue(str.toLongLong(&ok));
if(val_out)
{
*val_out = retvalue;
}
return ok;
}
QString BitcoinUnits::getAmountColumnTitle(int unit)
{
QString amountTitle = QObject::tr("Amount");
if (BitcoinUnits::valid(unit))
{
amountTitle += " ("+BitcoinUnits::name(unit) + ")";
}
return amountTitle;
}
int BitcoinUnits::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return unitlist.size();
}
QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
{
int row = index.row();
if(row >= 0 && row < unitlist.size())
{
Unit unit = unitlist.at(row);
switch(role)
{
case Qt::EditRole:
case Qt::DisplayRole:
return QVariant(name(unit));
case Qt::ToolTipRole:
return QVariant(description(unit));
case UnitRole:
return QVariant(static_cast<int>(unit));
}
}
return QVariant();
}
CAmount BitcoinUnits::maxMoney()
{
return MAX_MONEY;
}

View File

@@ -1,127 +0,0 @@
// Copyright (c) 2011-2013 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_QT_BITCOINUNITS_H
#define BITCOIN_QT_BITCOINUNITS_H
#include "amount.h"
#include <QAbstractListModel>
#include <QString>
// U+2009 THIN SPACE = UTF-8 E2 80 89
#define REAL_THIN_SP_CP 0x2009
#define REAL_THIN_SP_UTF8 "\xE2\x80\x89"
#define REAL_THIN_SP_HTML "&thinsp;"
// U+200A HAIR SPACE = UTF-8 E2 80 8A
#define HAIR_SP_CP 0x200A
#define HAIR_SP_UTF8 "\xE2\x80\x8A"
#define HAIR_SP_HTML "&#8202;"
// U+2006 SIX-PER-EM SPACE = UTF-8 E2 80 86
#define SIXPEREM_SP_CP 0x2006
#define SIXPEREM_SP_UTF8 "\xE2\x80\x86"
#define SIXPEREM_SP_HTML "&#8198;"
// U+2007 FIGURE SPACE = UTF-8 E2 80 87
#define FIGURE_SP_CP 0x2007
#define FIGURE_SP_UTF8 "\xE2\x80\x87"
#define FIGURE_SP_HTML "&#8199;"
// QMessageBox seems to have a bug whereby it doesn't display thin/hair spaces
// correctly. Workaround is to display a space in a small font. If you
// change this, please test that it doesn't cause the parent span to start
// wrapping.
#define HTML_HACK_SP "<span style='white-space: nowrap; font-size: 6pt'> </span>"
// Define THIN_SP_* variables to be our preferred type of thin space
#define THIN_SP_CP REAL_THIN_SP_CP
#define THIN_SP_UTF8 REAL_THIN_SP_UTF8
#define THIN_SP_HTML HTML_HACK_SP
/** Bitcoin unit definitions. Encapsulates parsing and formatting
and serves as list model for drop-down selection boxes.
*/
class BitcoinUnits: public QAbstractListModel
{
Q_OBJECT
public:
explicit BitcoinUnits(QObject *parent);
/** Bitcoin units.
@note Source: https://en.bitcoin.it/wiki/Units . Please add only sensible ones
*/
enum Unit
{
BTC,
mBTC,
uBTC
};
enum SeparatorStyle
{
separatorNever,
separatorStandard,
separatorAlways
};
//! @name Static API
//! Unit conversion and formatting
///@{
//! Get list of units, for drop-down box
static QList<Unit> availableUnits();
//! Is unit ID valid?
static bool valid(int unit);
//! Short name
static QString name(int unit);
//! Longer description
static QString description(int unit);
//! Number of Satoshis (1e-8) per unit
static qint64 factor(int unit);
//! Number of decimals left
static int decimals(int unit);
//! Format as string
static QString format(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
//! Format as string (with unit)
static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
//! Parse string to coin amount
static bool parse(int unit, const QString &value, CAmount *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
static QString getAmountColumnTitle(int unit);
///@}
//! @name AbstractListModel implementation
//! List model for unit drop-down selection box.
///@{
enum RoleIndex {
/** Unit identifier */
UnitRole = Qt::UserRole
};
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
///@}
static QString removeSpaces(QString text)
{
text.remove(' ');
text.remove(QChar(THIN_SP_CP));
#if (THIN_SP_CP != REAL_THIN_SP_CP)
text.remove(QChar(REAL_THIN_SP_CP));
#endif
return text;
}
//! Return maximum number of base units (Satoshis)
static CAmount maxMoney();
private:
QList<BitcoinUnits::Unit> unitlist;
};
typedef BitcoinUnits::Unit BitcoinUnit;
#endif // BITCOIN_QT_BITCOINUNITS_H

View File

@@ -1,243 +0,0 @@
// Copyright (c) 2011-2013 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 "clientmodel.h"
#include "guiconstants.h"
#include "peertablemodel.h"
#include "alert.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "clientversion.h"
#include "main.h"
#include "net.h"
#include "ui_interface.h"
#include "util.h"
#include <stdint.h>
#include <QDebug>
#include <QTimer>
static const int64_t nClientStartupTime = GetTime();
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
QObject(parent),
optionsModel(optionsModel),
peerTableModel(0),
cachedNumBlocks(0),
cachedBlockDate(QDateTime()),
cachedReindexing(0),
cachedImporting(0),
pollTimer(0)
{
peerTableModel = new PeerTableModel(this);
pollTimer = new QTimer(this);
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
pollTimer->start(MODEL_UPDATE_DELAY);
subscribeToCoreSignals();
}
ClientModel::~ClientModel()
{
unsubscribeFromCoreSignals();
}
int ClientModel::getNumConnections(unsigned int flags) const
{
LOCK(cs_vNodes);
if (flags == CONNECTIONS_ALL) // Shortcut if we want total
return vNodes.size();
int nNum = 0;
BOOST_FOREACH(CNode* pnode, vNodes)
if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
nNum++;
return nNum;
}
int ClientModel::getNumBlocks() const
{
LOCK(cs_main);
return chainActive.Height();
}
quint64 ClientModel::getTotalBytesRecv() const
{
return CNode::GetTotalBytesRecv();
}
quint64 ClientModel::getTotalBytesSent() const
{
return CNode::GetTotalBytesSent();
}
QDateTime ClientModel::getLastBlockDate() const
{
LOCK(cs_main);
if (chainActive.Tip())
return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network
}
double ClientModel::getVerificationProgress() const
{
LOCK(cs_main);
return Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip());
}
void ClientModel::updateTimer()
{
// Get required lock upfront. This avoids the GUI from getting stuck on
// periodical polls if the core is holding the locks for a longer time -
// for example, during a wallet rescan.
TRY_LOCK(cs_main, lockMain);
if (!lockMain)
return;
// Some quantities (such as number of blocks) change so fast that we don't want to be notified for each change.
// Periodically check and update with a timer.
int newNumBlocks = getNumBlocks();
QDateTime newBlockDate = getLastBlockDate();
// check for changed number of blocks we have, number of blocks peers claim to have, reindexing state and importing state
if (cachedNumBlocks != newNumBlocks ||
cachedBlockDate != newBlockDate ||
cachedReindexing != fReindex ||
cachedImporting != fImporting)
{
cachedNumBlocks = newNumBlocks;
cachedBlockDate = newBlockDate;
cachedReindexing = fReindex;
cachedImporting = fImporting;
Q_EMIT numBlocksChanged(newNumBlocks, newBlockDate);
}
Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent());
}
void ClientModel::updateNumConnections(int numConnections)
{
Q_EMIT numConnectionsChanged(numConnections);
}
void ClientModel::updateAlert(const QString &hash, int status)
{
// Show error message notification for new alert
if(status == CT_NEW)
{
uint256 hash_256;
hash_256.SetHex(hash.toStdString());
CAlert alert = CAlert::getAlertByHash(hash_256);
if(!alert.IsNull())
{
Q_EMIT message(tr("Network Alert"), QString::fromStdString(alert.strStatusBar), CClientUIInterface::ICON_ERROR);
}
}
Q_EMIT alertsChanged(getStatusBarWarnings());
}
bool ClientModel::inInitialBlockDownload() const
{
return IsInitialBlockDownload();
}
enum BlockSource ClientModel::getBlockSource() const
{
if (fReindex)
return BLOCK_SOURCE_REINDEX;
else if (fImporting)
return BLOCK_SOURCE_DISK;
else if (getNumConnections() > 0)
return BLOCK_SOURCE_NETWORK;
return BLOCK_SOURCE_NONE;
}
QString ClientModel::getStatusBarWarnings() const
{
return QString::fromStdString(GetWarnings("statusbar"));
}
OptionsModel *ClientModel::getOptionsModel()
{
return optionsModel;
}
PeerTableModel *ClientModel::getPeerTableModel()
{
return peerTableModel;
}
QString ClientModel::formatFullVersion() const
{
return QString::fromStdString(FormatFullVersion());
}
QString ClientModel::formatBuildDate() const
{
return QString::fromStdString(CLIENT_DATE);
}
bool ClientModel::isReleaseVersion() const
{
return CLIENT_VERSION_IS_RELEASE;
}
QString ClientModel::clientName() const
{
return QString::fromStdString(CLIENT_NAME);
}
QString ClientModel::formatClientStartupTime() const
{
return QDateTime::fromTime_t(nClientStartupTime).toString();
}
// Handlers for core signals
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
{
// emits signal "showProgress"
QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(title)),
Q_ARG(int, nProgress));
}
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
{
// Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
Q_ARG(int, newNumConnections));
}
static void NotifyAlertChanged(ClientModel *clientmodel, const uint256 &hash, ChangeType status)
{
qDebug() << "NotifyAlertChanged: " + QString::fromStdString(hash.GetHex()) + " status=" + QString::number(status);
QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(hash.GetHex())),
Q_ARG(int, status));
}
void ClientModel::subscribeToCoreSignals()
{
// Connect signals to client
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this, _1, _2));
}
void ClientModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this, _1, _2));
}

View File

@@ -1,103 +0,0 @@
// Copyright (c) 2011-2013 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_QT_CLIENTMODEL_H
#define BITCOIN_QT_CLIENTMODEL_H
#include <QObject>
#include <QDateTime>
class AddressTableModel;
class OptionsModel;
class PeerTableModel;
class TransactionTableModel;
class CWallet;
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
enum BlockSource {
BLOCK_SOURCE_NONE,
BLOCK_SOURCE_REINDEX,
BLOCK_SOURCE_DISK,
BLOCK_SOURCE_NETWORK
};
enum NumConnections {
CONNECTIONS_NONE = 0,
CONNECTIONS_IN = (1U << 0),
CONNECTIONS_OUT = (1U << 1),
CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT),
};
/** Model for Bitcoin network client. */
class ClientModel : public QObject
{
Q_OBJECT
public:
explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
~ClientModel();
OptionsModel *getOptionsModel();
PeerTableModel *getPeerTableModel();
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const;
quint64 getTotalBytesRecv() const;
quint64 getTotalBytesSent() const;
double getVerificationProgress() const;
QDateTime getLastBlockDate() const;
//! Return true if core is doing initial block download
bool inInitialBlockDownload() const;
//! Return true if core is importing blocks
enum BlockSource getBlockSource() const;
//! Return warnings to be displayed in status bar
QString getStatusBarWarnings() const;
QString formatFullVersion() const;
QString formatBuildDate() const;
bool isReleaseVersion() const;
QString clientName() const;
QString formatClientStartupTime() const;
private:
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
int cachedNumBlocks;
QDateTime cachedBlockDate;
bool cachedReindexing;
bool cachedImporting;
QTimer *pollTimer;
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
Q_SIGNALS:
void numConnectionsChanged(int count);
void numBlocksChanged(int count, const QDateTime& blockDate);
void alertsChanged(const QString &warnings);
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
//! Fired when a message should be reported to the user
void message(const QString &title, const QString &message, unsigned int style);
// Show progress dialog e.g. for verifychain
void showProgress(const QString &title, int nProgress);
public Q_SLOTS:
void updateTimer();
void updateNumConnections(int numConnections);
void updateAlert(const QString &hash, int status);
};
#endif // BITCOIN_QT_CLIENTMODEL_H

View File

@@ -1,828 +0,0 @@
// Copyright (c) 2011-2013 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 "coincontroldialog.h"
#include "ui_coincontroldialog.h"
#include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "scicon.h"
#include "walletmodel.h"
#include "coincontrol.h"
#include "init.h"
#include "main.h"
#include "wallet/wallet.h"
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
#include <QApplication>
#include <QCheckBox>
#include <QCursor>
#include <QDialogButtonBox>
#include <QFlags>
#include <QIcon>
#include <QSettings>
#include <QString>
#include <QTreeWidget>
#include <QTreeWidgetItem>
using namespace std;
QList<CAmount> CoinControlDialog::payAmounts;
CCoinControl* CoinControlDialog::coinControl = new CCoinControl();
bool CoinControlDialog::fSubtractFeeFromAmount = false;
CoinControlDialog::CoinControlDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::CoinControlDialog),
model(0)
{
ui->setupUi(this);
// context menu actions
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
// context menu
contextMenu = new QMenu();
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
contextMenu->addAction(copyTransactionHashAction);
contextMenu->addSeparator();
contextMenu->addAction(lockAction);
contextMenu->addAction(unlockAction);
// context menu signals
connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash()));
connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin()));
connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin()));
// clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this);
QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity()));
connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount()));
connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee()));
connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee()));
connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes()));
connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority()));
connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput()));
connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange()));
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
ui->labelCoinControlAmount->addAction(clipboardAmountAction);
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
// toggle tree/list mode
connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool)));
connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool)));
// click on checkbox
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int)));
// click on header
#if QT_VERSION < 0x050000
ui->treeWidget->header()->setClickable(true);
#else
ui->treeWidget->header()->setSectionsClickable(true);
#endif
connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int)));
// ok button
connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*)));
// (un)select all
connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
// change coin control first column label due Qt4 bug.
// see https://github.com/bitcoin/bitcoin/issues/5716
ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 290);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 110);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100);
ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100);
ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transacton hash in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but don't show it
ui->treeWidget->setColumnHidden(COLUMN_DATE_INT64, true); // store date int64 in this column, but don't show it
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder);
// restore list mode and sortorder as a convenience feature
QSettings settings;
if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool())
ui->radioTreeMode->click();
if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
sortView(settings.value("nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt()));
}
CoinControlDialog::~CoinControlDialog()
{
QSettings settings;
settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
settings.setValue("nCoinControlSortColumn", sortColumn);
settings.setValue("nCoinControlSortOrder", (int)sortOrder);
delete ui;
}
void CoinControlDialog::setModel(WalletModel *model)
{
this->model = model;
if(model && model->getOptionsModel() && model->getAddressTableModel())
{
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(model, this);
}
}
// helper function str_pad
QString CoinControlDialog::strPad(QString s, int nPadLength, QString sPadding)
{
while (s.length() < nPadLength)
s = sPadding + s;
return s;
}
// ok button
void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
done(QDialog::Accepted); // closes the dialog
}
// (un)select all
void CoinControlDialog::buttonSelectAllClicked()
{
Qt::CheckState state = Qt::Checked;
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
{
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
{
state = Qt::Unchecked;
break;
}
}
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
ui->treeWidget->setEnabled(true);
if (state == Qt::Unchecked)
coinControl->UnSelectAll(); // just to be sure
CoinControlDialog::updateLabels(model, this);
}
// context menu
void CoinControlDialog::showMenu(const QPoint &point)
{
QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
if(item)
{
contextMenuItem = item;
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
{
copyTransactionHashAction->setEnabled(true);
if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))
{
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
}
else
{
lockAction->setEnabled(true);
unlockAction->setEnabled(false);
}
}
else // this means click on parent node in tree mode -> disable all
{
copyTransactionHashAction->setEnabled(false);
lockAction->setEnabled(false);
unlockAction->setEnabled(false);
}
// show context menu
contextMenu->exec(QCursor::pos());
}
}
// context menu action: copy amount
void CoinControlDialog::copyAmount()
{
GUIUtil::setClipboard(BitcoinUnits::removeSpaces(contextMenuItem->text(COLUMN_AMOUNT)));
}
// context menu action: copy label
void CoinControlDialog::copyLabel()
{
if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_LABEL));
else
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_LABEL));
}
// context menu action: copy address
void CoinControlDialog::copyAddress()
{
if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_ADDRESS));
else
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS));
}
// context menu action: copy transaction id
void CoinControlDialog::copyTransactionHash()
{
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_TXHASH));
}
// context menu action: lock coin
void CoinControlDialog::lockCoin()
{
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
model->lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
}
// context menu action: unlock coin
void CoinControlDialog::unlockCoin()
{
COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt());
model->unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
updateLabelLocked();
}
// copy label "Quantity" to clipboard
void CoinControlDialog::clipboardQuantity()
{
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
}
// copy label "Amount" to clipboard
void CoinControlDialog::clipboardAmount()
{
GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
}
// copy label "Fee" to clipboard
void CoinControlDialog::clipboardFee()
{
GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
}
// copy label "After fee" to clipboard
void CoinControlDialog::clipboardAfterFee()
{
GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
}
// copy label "Bytes" to clipboard
void CoinControlDialog::clipboardBytes()
{
GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
// copy label "Priority" to clipboard
void CoinControlDialog::clipboardPriority()
{
GUIUtil::setClipboard(ui->labelCoinControlPriority->text());
}
// copy label "Dust" to clipboard
void CoinControlDialog::clipboardLowOutput()
{
GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
}
// copy label "Change" to clipboard
void CoinControlDialog::clipboardChange()
{
GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
}
// treeview: sort
void CoinControlDialog::sortView(int column, Qt::SortOrder order)
{
sortColumn = column;
sortOrder = order;
ui->treeWidget->sortItems(column, order);
ui->treeWidget->header()->setSortIndicator(getMappedColumn(sortColumn), sortOrder);
}
// treeview: clicked on header
void CoinControlDialog::headerSectionClicked(int logicalIndex)
{
if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
{
ui->treeWidget->header()->setSortIndicator(getMappedColumn(sortColumn), sortOrder);
}
else
{
logicalIndex = getMappedColumn(logicalIndex, false);
if (sortColumn == logicalIndex)
sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
else
{
sortColumn = logicalIndex;
sortOrder = ((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS) ? Qt::AscendingOrder : Qt::DescendingOrder); // if label or address then default => asc, else default => desc
}
sortView(sortColumn, sortOrder);
}
}
// toggle tree mode
void CoinControlDialog::radioTreeMode(bool checked)
{
if (checked && model)
updateView();
}
// toggle list mode
void CoinControlDialog::radioListMode(bool checked)
{
if (checked && model)
updateView();
}
// checkbox clicked by user
void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
{
if (column == COLUMN_CHECKBOX && item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
{
COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
coinControl->UnSelect(outpt);
else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
else
coinControl->Select(outpt);
// selection changed -> update labels
if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
CoinControlDialog::updateLabels(model, this);
}
// todo: this is a temporary qt5 fix: when clicking a parent node in tree mode, the parent node
// including all children are partially selected. But the parent node should be fully selected
// as well as the children. Children should never be partially selected in the first place.
// Please remove this ugly fix, once the bug is solved upstream.
#if QT_VERSION >= 0x050000
else if (column == COLUMN_CHECKBOX && item->childCount() > 0)
{
if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
item->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
#endif
}
// return human readable label for priority number
QString CoinControlDialog::getPriorityLabel(double dPriority, double mempoolEstimatePriority)
{
double dPriorityMedium = mempoolEstimatePriority;
if (dPriorityMedium <= 0)
dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded
if (dPriority / 1000000 > dPriorityMedium) return tr("highest");
else if (dPriority / 100000 > dPriorityMedium) return tr("higher");
else if (dPriority / 10000 > dPriorityMedium) return tr("high");
else if (dPriority / 1000 > dPriorityMedium) return tr("medium-high");
else if (dPriority > dPriorityMedium) return tr("medium");
else if (dPriority * 10 > dPriorityMedium) return tr("low-medium");
else if (dPriority * 100 > dPriorityMedium) return tr("low");
else if (dPriority * 1000 > dPriorityMedium) return tr("lower");
else return tr("lowest");
}
// shows count of locked unspent outputs
void CoinControlDialog::updateLabelLocked()
{
vector<COutPoint> vOutpts;
model->listLockedCoins(vOutpts);
if (vOutpts.size() > 0)
{
ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
ui->labelLocked->setVisible(true);
}
else ui->labelLocked->setVisible(false);
}
void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
if (!model)
return;
// nPayAmount
CAmount nPayAmount = 0;
bool fDust = false;
CMutableTransaction txDummy;
Q_FOREACH(const CAmount &amount, CoinControlDialog::payAmounts)
{
nPayAmount += amount;
if (amount > 0)
{
CTxOut txout(amount, (CScript)vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout);
if (txout.IsDust(::minRelayTxFee))
fDust = true;
}
}
QString sPriorityLabel = tr("none");
CAmount nAmount = 0;
CAmount nPayFee = 0;
CAmount nAfterFee = 0;
CAmount nChange = 0;
unsigned int nBytes = 0;
unsigned int nBytesInputs = 0;
double dPriority = 0;
double dPriorityInputs = 0;
unsigned int nQuantity = 0;
int nQuantityUncompressed = 0;
bool fAllowFree = false;
vector<COutPoint> vCoinControl;
vector<COutput> vOutputs;
coinControl->ListSelected(vCoinControl);
model->getOutputs(vCoinControl, vOutputs);
BOOST_FOREACH(const COutput& out, vOutputs)
{
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
uint256 txhash = out.tx->GetHash();
COutPoint outpt(txhash, out.i);
if (model->isSpent(outpt))
{
coinControl->UnSelect(outpt);
continue;
}
// Quantity
nQuantity++;
// Amount
nAmount += out.tx->vout[out.i].nValue;
// Priority
dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
// Bytes
CTxDestination address;
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
{
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&address);
if (keyid && model->getPubKey(*keyid, pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
if (!pubkey.IsCompressed())
nQuantityUncompressed++;
}
else
nBytesInputs += 148; // in all error cases, simply assume 148 here
}
else nBytesInputs += 148;
}
// calculation
if (nQuantity > 0)
{
// Bytes
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
// Priority
double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority);
// in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate
if (CoinControlDialog::fSubtractFeeFromAmount)
if (nAmount - nPayAmount == 0)
nBytes -= 34;
// Fee
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
// Allow free?
double dPriorityNeeded = mempoolEstimatePriority;
if (dPriorityNeeded <= 0)
dPriorityNeeded = AllowFreeThreshold(); // not enough data, back to hard-coded
fAllowFree = (dPriority >= dPriorityNeeded);
if (fSendFreeTransactions)
if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
nPayFee = 0;
if (nPayAmount > 0)
{
nChange = nAmount - nPayAmount;
if (!CoinControlDialog::fSubtractFeeFromAmount)
nChange -= nPayFee;
// Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < CENT)
{
CTxOut txout(nChange, (CScript)vector<unsigned char>(24, 0));
if (txout.IsDust(::minRelayTxFee))
{
if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust
nChange = txout.GetDustThreshold(::minRelayTxFee);
else
{
nPayFee += nChange;
nChange = 0;
}
}
}
if (nChange == 0 && !CoinControlDialog::fSubtractFeeFromAmount)
nBytes -= 34;
}
// after fee
nAfterFee = nAmount - nPayFee;
if (nAfterFee < 0)
nAfterFee = 0;
}
// actually update labels
int nDisplayUnit = BitcoinUnits::BTC;
if (model && model->getOptionsModel())
nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
QLabel *l6 = dialog->findChild<QLabel *>("labelCoinControlPriority");
QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
// enable/disable "dust" and "change"
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
// stats
l1->setText(QString::number(nQuantity)); // Quantity
l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes
l6->setText(sPriorityLabel); // Priority
l7->setText(fDust ? tr("yes") : tr("no")); // Dust
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
if (nPayFee > 0 && !(payTxFee.GetFeePerK() > 0 && fPayAtLeastCustomFee && nBytes < 1000))
{
l3->setText(ASYMP_UTF8 + l3->text());
l4->setText(ASYMP_UTF8 + l4->text());
if (nChange > 0 && !CoinControlDialog::fSubtractFeeFromAmount)
l8->setText(ASYMP_UTF8 + l8->text());
}
// turn labels "red"
l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000
l6->setStyleSheet((dPriority > 0 && !fAllowFree) ? "color:red;" : ""); // Priority < "medium"
l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes"
// tool tips
QString toolTip1 = tr("This label turns red if the transaction size is greater than 1000 bytes.") + "<br /><br />";
toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK())) + "<br /><br />";
toolTip1 += tr("Can vary +/- 1 byte per input.");
QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "<br /><br />";
toolTip2 += tr("This label turns red if the priority is smaller than \"medium\".") + "<br /><br />";
toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::minTxFee.GetFeePerK()));
QString toolTip3 = tr("This label turns red if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546)));
// how many satoshis the estimated fee can vary per byte we guess wrong
double dFeeVary;
if (payTxFee.GetFeePerK() > 0)
dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), payTxFee.GetFeePerK()) / 1000;
else
dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), mempool.estimateFee(nTxConfirmTarget).GetFeePerK()) / 1000;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
l4->setToolTip(toolTip4);
l5->setToolTip(toolTip1);
l6->setToolTip(toolTip2);
l7->setToolTip(toolTip3);
l8->setToolTip(toolTip4);
dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip());
dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip());
dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
dialog->findChild<QLabel *>("labelCoinControlPriorityText") ->setToolTip(l6->toolTip());
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
// Insufficient funds
QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
if (label)
label->setVisible(nChange < 0);
}
void CoinControlDialog::updateView()
{
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())
return;
bool treeMode = ui->radioTreeMode->isChecked();
ui->treeWidget->clear();
ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
ui->treeWidget->setAlternatingRowColors(!treeMode);
QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
map<QString, vector<COutput> > mapCoins;
model->listCoins(mapCoins);
BOOST_FOREACH(PAIRTYPE(QString, vector<COutput>) coins, mapCoins)
{
QTreeWidgetItem *itemWalletAddress = new QTreeWidgetItem();
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
QString sWalletAddress = coins.first;
QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
if (sWalletLabel.isEmpty())
sWalletLabel = tr("(no label)");
if (treeMode)
{
// wallet address
ui->treeWidget->addTopLevelItem(itemWalletAddress);
itemWalletAddress->setFlags(flgTristate);
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
// label
itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
// address
itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
}
CAmount nSum = 0;
double dPrioritySum = 0;
int nChildren = 0;
int nInputSum = 0;
BOOST_FOREACH(const COutput& out, coins.second)
{
int nInputSize = 0;
nSum += out.tx->vout[out.i].nValue;
nChildren++;
QTreeWidgetItem *itemOutput;
if (treeMode) itemOutput = new QTreeWidgetItem(itemWalletAddress);
else itemOutput = new QTreeWidgetItem(ui->treeWidget);
itemOutput->setFlags(flgCheckbox);
itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
// address
CTxDestination outputAddress;
QString sAddress = "";
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
// if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress)))
itemOutput->setText(COLUMN_ADDRESS, sAddress);
CPubKey pubkey;
CKeyID *keyid = boost::get<CKeyID>(&outputAddress);
if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed())
nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes)
}
// label
if (!(sAddress == sWalletAddress)) // change
{
// tooltip from where the change comes from
itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
itemOutput->setText(COLUMN_LABEL, tr("(change)"));
}
else if (!treeMode)
{
QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
if (sLabel.isEmpty())
sLabel = tr("(no label)");
itemOutput->setText(COLUMN_LABEL, sLabel);
}
// amount
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue));
itemOutput->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(out.tx->vout[out.i].nValue), 15, " ")); // padding so that sorting works correctly
// date
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
itemOutput->setText(COLUMN_DATE_INT64, strPad(QString::number(out.tx->GetTxTime()), 20, " "));
// confirmations
itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " "));
// priority
double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority));
itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
nInputSum += nInputSize;
// transaction hash
uint256 txhash = out.tx->GetHash();
itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex()));
// vout index
itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i));
// disable locked coins
if (model->isLockedCoin(txhash, out.i))
{
COutPoint outpt(txhash, out.i);
coinControl->UnSelect(outpt); // just to be sure
itemOutput->setDisabled(true);
itemOutput->setIcon(COLUMN_CHECKBOX, SingleColorIcon(":/icons/lock_closed"));
}
// set checkbox
if (coinControl->IsSelected(txhash, out.i))
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
// amount
if (treeMode)
{
dPrioritySum = dPrioritySum / (nInputSum + 78);
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum, mempoolEstimatePriority));
itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
}
}
// expand all partially selected
if (treeMode)
{
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
ui->treeWidget->topLevelItem(i)->setExpanded(true);
}
// sort view
sortView(sortColumn, sortOrder);
ui->treeWidget->setEnabled(true);
}

View File

@@ -1,130 +0,0 @@
// Copyright (c) 2011-2013 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_QT_COINCONTROLDIALOG_H
#define BITCOIN_QT_COINCONTROLDIALOG_H
#include "amount.h"
#include <QAbstractButton>
#include <QAction>
#include <QDialog>
#include <QList>
#include <QMenu>
#include <QPoint>
#include <QString>
#include <QTreeWidgetItem>
class WalletModel;
class CCoinControl;
class CTxMemPool;
namespace Ui {
class CoinControlDialog;
}
#define ASYMP_UTF8 "\xE2\x89\x88"
class CoinControlDialog : public QDialog
{
Q_OBJECT
public:
explicit CoinControlDialog(QWidget *parent = 0);
~CoinControlDialog();
void setModel(WalletModel *model);
// static because also called from sendcoinsdialog
static void updateLabels(WalletModel*, QDialog*);
static QString getPriorityLabel(double dPriority, double mempoolEstimatePriority);
static QList<CAmount> payAmounts;
static CCoinControl *coinControl;
static bool fSubtractFeeFromAmount;
private:
Ui::CoinControlDialog *ui;
WalletModel *model;
int sortColumn;
Qt::SortOrder sortOrder;
QMenu *contextMenu;
QTreeWidgetItem *contextMenuItem;
QAction *copyTransactionHashAction;
QAction *lockAction;
QAction *unlockAction;
QString strPad(QString, int, QString);
void sortView(int, Qt::SortOrder);
void updateView();
enum
{
COLUMN_CHECKBOX,
COLUMN_AMOUNT,
COLUMN_LABEL,
COLUMN_ADDRESS,
COLUMN_DATE,
COLUMN_CONFIRMATIONS,
COLUMN_PRIORITY,
COLUMN_TXHASH,
COLUMN_VOUT_INDEX,
COLUMN_AMOUNT_INT64,
COLUMN_PRIORITY_INT64,
COLUMN_DATE_INT64
};
// some columns have a hidden column containing the value used for sorting
int getMappedColumn(int column, bool fVisibleColumn = true)
{
if (fVisibleColumn)
{
if (column == COLUMN_AMOUNT_INT64)
return COLUMN_AMOUNT;
else if (column == COLUMN_PRIORITY_INT64)
return COLUMN_PRIORITY;
else if (column == COLUMN_DATE_INT64)
return COLUMN_DATE;
}
else
{
if (column == COLUMN_AMOUNT)
return COLUMN_AMOUNT_INT64;
else if (column == COLUMN_PRIORITY)
return COLUMN_PRIORITY_INT64;
else if (column == COLUMN_DATE)
return COLUMN_DATE_INT64;
}
return column;
}
private Q_SLOTS:
void showMenu(const QPoint &);
void copyAmount();
void copyLabel();
void copyAddress();
void copyTransactionHash();
void lockCoin();
void unlockCoin();
void clipboardQuantity();
void clipboardAmount();
void clipboardFee();
void clipboardAfterFee();
void clipboardBytes();
void clipboardPriority();
void clipboardLowOutput();
void clipboardChange();
void radioTreeMode(bool);
void radioListMode(bool);
void viewItemChanged(QTreeWidgetItem*, int);
void headerSectionClicked(int);
void buttonBoxClicked(QAbstractButton*);
void buttonSelectAllClicked();
void updateLabelLocked();
};
#endif // BITCOIN_QT_COINCONTROLDIALOG_H

View File

@@ -1,33 +0,0 @@
// Copyright (c) 2011-2013 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 "coincontroltreewidget.h"
#include "coincontroldialog.h"
CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) :
QTreeWidget(parent)
{
}
void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox
{
event->ignore();
int COLUMN_CHECKBOX = 0;
if(this->currentItem())
this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked));
}
else if (event->key() == Qt::Key_Escape) // press esc -> close dialog
{
event->ignore();
CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget();
coinControlDialog->done(QDialog::Accepted);
}
else
{
this->QTreeWidget::keyPressEvent(event);
}
}

View File

@@ -1,22 +0,0 @@
// Copyright (c) 2011-2013 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_QT_COINCONTROLTREEWIDGET_H
#define BITCOIN_QT_COINCONTROLTREEWIDGET_H
#include <QKeyEvent>
#include <QTreeWidget>
class CoinControlTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
explicit CoinControlTreeWidget(QWidget *parent = 0);
protected:
virtual void keyPressEvent(QKeyEvent *event);
};
#endif // BITCOIN_QT_COINCONTROLTREEWIDGET_H

View File

@@ -1,91 +0,0 @@
// Copyright (c) 2011-2013 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 "csvmodelwriter.h"
#include <QAbstractItemModel>
#include <QFile>
#include <QTextStream>
CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) :
QObject(parent),
filename(filename), model(0)
{
}
void CSVModelWriter::setModel(const QAbstractItemModel *model)
{
this->model = model;
}
void CSVModelWriter::addColumn(const QString &title, int column, int role)
{
Column col;
col.title = title;
col.column = column;
col.role = role;
columns.append(col);
}
static void writeValue(QTextStream &f, const QString &value)
{
QString escaped = value;
escaped.replace('"', "\"\"");
f << "\"" << escaped << "\"";
}
static void writeSep(QTextStream &f)
{
f << ",";
}
static void writeNewline(QTextStream &f)
{
f << "\n";
}
bool CSVModelWriter::write()
{
QFile file(filename);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QTextStream out(&file);
int numRows = 0;
if(model)
{
numRows = model->rowCount();
}
// Header row
for(int i=0; i<columns.size(); ++i)
{
if(i!=0)
{
writeSep(out);
}
writeValue(out, columns[i].title);
}
writeNewline(out);
// Data rows
for(int j=0; j<numRows; ++j)
{
for(int i=0; i<columns.size(); ++i)
{
if(i!=0)
{
writeSep(out);
}
QVariant data = model->index(j, columns[i].column).data(columns[i].role);
writeValue(out, data.toString());
}
writeNewline(out);
}
file.close();
return file.error() == QFile::NoError;
}

View File

@@ -1,46 +0,0 @@
// Copyright (c) 2011-2013 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_QT_CSVMODELWRITER_H
#define BITCOIN_QT_CSVMODELWRITER_H
#include <QList>
#include <QObject>
QT_BEGIN_NAMESPACE
class QAbstractItemModel;
QT_END_NAMESPACE
/** Export a Qt table model to a CSV file. This is useful for analyzing or post-processing the data in
a spreadsheet.
*/
class CSVModelWriter : public QObject
{
Q_OBJECT
public:
explicit CSVModelWriter(const QString &filename, QObject *parent = 0);
void setModel(const QAbstractItemModel *model);
void addColumn(const QString &title, int column, int role=Qt::EditRole);
/** Perform export of the model to CSV.
@returns true on success, false otherwise
*/
bool write();
private:
QString filename;
const QAbstractItemModel *model;
struct Column
{
QString title;
int column;
int role;
};
QList<Column> columns;
};
#endif // BITCOIN_QT_CSVMODELWRITER_H

Some files were not shown because too many files have changed in this diff Show More