Initial merge
This commit is contained in:
114
src/Makefile.am
114
src/Makefile.am
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -470,7 +470,7 @@ public:
|
||||
}
|
||||
|
||||
//! Return the number of (unique) addresses in all tables.
|
||||
int size()
|
||||
size_t size() const
|
||||
{
|
||||
return vRandom.size();
|
||||
}
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -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;
|
||||
|
||||
12
src/amount.h
12
src/amount.h
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
};
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
62
src/core_memusage.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) { \
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
103
src/gtest/test_miner.cpp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
193
src/httprpc.cpp
Normal 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
37
src/httprpc.h
Normal 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
598
src/httpserver.cpp
Normal 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
149
src/httpserver.h
Normal 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
|
||||
324
src/init.cpp
324
src/init.cpp
@@ -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
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 ¤t_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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
|
||||
104
src/main.cpp
104
src/main.cpp
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
24
src/main.h
24
src/main.h
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
121
src/metrics.cpp
121
src/metrics.cpp
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
129
src/miner.cpp
129
src/miner.cpp
@@ -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
|
||||
|
||||
23
src/miner.h
23
src/miner.h
@@ -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
|
||||
|
||||
130
src/net.cpp
130
src/net.cpp
@@ -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)
|
||||
|
||||
19
src/net.h
19
src/net.h
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
12
src/pow.cpp
12
src/pow.cpp
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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()));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 " "
|
||||
|
||||
// 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 " "
|
||||
|
||||
// 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 " "
|
||||
|
||||
// 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 " "
|
||||
|
||||
// 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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user