diff options
Diffstat (limited to 'src')
458 files changed, 8729 insertions, 4925 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index ebae53a8c1..4b65774fc6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ else LIBUNIVALUE = $(UNIVALUE_LIBS) endif -BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) +BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) @@ -78,6 +78,7 @@ BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ + bech32.h \ bloom.h \ blockencodings.h \ chain.h \ @@ -167,6 +168,7 @@ BITCOIN_CORE_H = \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ + wallet/walletutil.h \ warnings.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ @@ -248,6 +250,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ + wallet/walletutil.cpp \ $(BITCOIN_CORE_H) # crypto primitives library @@ -316,6 +319,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ + bech32.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ @@ -474,8 +478,7 @@ CLEANFILES += univalue/*.gcda univalue/*.gcno CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno - -DISTCLEANFILES = obj/build.h +CLEANFILES += obj/build.h EXTRA_DIST = $(CTAES_DIST) @@ -499,10 +502,6 @@ clean-local: ## FIXME: How to get the appropriate modulename_CPPFLAGS in here? $(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@ -.mm.o: - $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< - check-symbols: $(bin_PROGRAMS) if GLIBC_BACK_COMPAT @echo "Checking glibc back compat..." diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 2b1f70b25b..8e2e587d32 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -6,11 +6,12 @@ bin_PROGRAMS += bench/bench_bitcoin BENCH_SRCDIR = bench BENCH_BINARY = bench/bench_bitcoin$(EXEEXT) -RAW_TEST_FILES = \ +RAW_BENCH_FILES = \ bench/data/block413567.raw -GENERATED_TEST_FILES = $(RAW_TEST_FILES:.raw=.raw.h) +GENERATED_BENCH_FILES = $(RAW_BENCH_FILES:.raw=.raw.h) bench_bench_bitcoin_SOURCES = \ + $(RAW_BENCH_FILES) \ bench/bench_bitcoin.cpp \ bench/bench.cpp \ bench/bench.h \ @@ -28,7 +29,7 @@ bench_bench_bitcoin_SOURCES = \ bench/perf.h \ bench/prevector_destructor.cpp -nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES) +nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -56,7 +57,7 @@ endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_TEST_FILES) +CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) CLEANFILES += $(CLEAN_BITCOIN_BENCH) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index e4b64c1ca7..0bdde06772 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -362,12 +362,12 @@ RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png) BITCOIN_RC = qt/res/bitcoin-qt-res.rc -BITCOIN_QT_INCLUDES = -I$(builddir)/qt -I$(srcdir)/qt -I$(srcdir)/qt/forms \ - -I$(builddir)/qt/forms -DQT_NO_KEYWORDS +BITCOIN_QT_INCLUDES = -DQT_NO_KEYWORDS qt_libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ $(QT_INCLUDES) $(QT_DBUS_INCLUDES) $(PROTOBUF_CFLAGS) $(QR_CFLAGS) qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) +qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS) qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) @@ -438,7 +438,7 @@ $(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< | \ $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ -CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno +CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_bitcoin_locale.qrc CLEANFILES += $(CLEAN_QT) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 01ab0134fe..06175be3fc 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -31,6 +31,7 @@ BITCOIN_TESTS =\ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ + test/bech32_tests.cpp \ test/bip32_tests.cpp \ test/blockencodings_tests.cpp \ test/bloom_tests.cpp \ @@ -49,6 +50,7 @@ BITCOIN_TESTS =\ test/main_tests.cpp \ test/mempool_tests.cpp \ test/merkle_tests.cpp \ + test/merkleblock_tests.cpp \ test/miner_tests.cpp \ test/multisig_tests.cpp \ test/net_tests.cpp \ @@ -65,6 +67,7 @@ BITCOIN_TESTS =\ test/scheduler_tests.cpp \ test/script_P2SH_tests.cpp \ test/script_tests.cpp \ + test/script_standard_tests.cpp \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ test/sighash_tests.cpp \ @@ -77,6 +80,7 @@ BITCOIN_TESTS =\ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ + test/txvalidation_tests.cpp \ test/txvalidationcache_tests.cpp \ test/versionbits_tests.cpp \ test/uint256_tests.cpp \ @@ -93,7 +97,7 @@ BITCOIN_TESTS += \ endif test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) -test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) +test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS) test_test_bitcoin_LDADD = if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) @@ -146,7 +150,7 @@ bitcoin_test_clean : FORCE check-local: @echo "Running test/util/bitcoin-util-test.py..." - $(top_builddir)/test/util/bitcoin-util-test.py + $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check if EMBEDDED_UNIVALUE $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 7f85c16585..0f9ccf1e9a 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -3,17 +3,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "addrdb.h" - -#include "addrman.h" -#include "chainparams.h" -#include "clientversion.h" -#include "fs.h" -#include "hash.h" -#include "random.h" -#include "streams.h" -#include "tinyformat.h" -#include "util.h" +#include <addrdb.h> + +#include <addrman.h> +#include <chainparams.h> +#include <clientversion.h> +#include <fs.h> +#include <hash.h> +#include <random.h> +#include <streams.h> +#include <tinyformat.h> +#include <util.h> namespace { diff --git a/src/addrdb.h b/src/addrdb.h index d930de204d..098da4240e 100644 --- a/src/addrdb.h +++ b/src/addrdb.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_ADDRDB_H #define BITCOIN_ADDRDB_H -#include "fs.h" -#include "serialize.h" +#include <fs.h> +#include <serialize.h> #include <string> #include <map> diff --git a/src/addrman.cpp b/src/addrman.cpp index a56bb4f9c1..372cac8483 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -3,11 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "addrman.h" +#include <addrman.h> -#include "hash.h" -#include "serialize.h" -#include "streams.h" +#include <hash.h> +#include <serialize.h> +#include <streams.h> int CAddrInfo::GetTriedBucket(const uint256& nKey) const { @@ -390,9 +390,9 @@ int CAddrMan::Check_() if (vRandom.size() != nTried + nNew) return -7; - for (std::map<int, CAddrInfo>::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { - int n = (*it).first; - CAddrInfo& info = (*it).second; + for (const auto& entry : mapInfo) { + int n = entry.first; + const CAddrInfo& info = entry.second; if (info.fInTried) { if (!info.nLastSuccess) return -1; diff --git a/src/addrman.h b/src/addrman.h index 18f3062287..ea289d508c 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -6,12 +6,12 @@ #ifndef BITCOIN_ADDRMAN_H #define BITCOIN_ADDRMAN_H -#include "netaddress.h" -#include "protocol.h" -#include "random.h" -#include "sync.h" -#include "timedata.h" -#include "util.h" +#include <netaddress.h> +#include <protocol.h> +#include <random.h> +#include <sync.h> +#include <timedata.h> +#include <util.h> #include <map> #include <set> @@ -313,9 +313,9 @@ public: s << nUBuckets; std::map<int, int> mapUnkIds; int nIds = 0; - for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { - mapUnkIds[(*it).first] = nIds; - const CAddrInfo &info = (*it).second; + for (const auto& entry : mapInfo) { + mapUnkIds[entry.first] = nIds; + const CAddrInfo &info = entry.second; if (info.nRefCount) { assert(nIds != nNew); // this means nNew was wrong, oh ow s << info; @@ -323,8 +323,8 @@ public: } } nIds = 0; - for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { - const CAddrInfo &info = (*it).second; + for (const auto& entry : mapInfo) { + const CAddrInfo &info = entry.second; if (info.fInTried) { assert(nIds != nTried); // this means nTried was wrong, oh ow s << info; @@ -455,6 +455,7 @@ public: void Clear() { + LOCK(cs); std::vector<int>().swap(vRandom); nKey = GetRandHash(); for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) { diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index b4952af6f4..738224fb70 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -3,11 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "arith_uint256.h" +#include <arith_uint256.h> -#include "uint256.h" -#include "utilstrencodings.h" -#include "crypto/common.h" +#include <uint256.h> +#include <utilstrencodings.h> +#include <crypto/common.h> #include <stdio.h> #include <string.h> diff --git a/src/arith_uint256.h b/src/arith_uint256.h index 5fd4fe96cf..1009fe1cc8 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -25,7 +25,7 @@ template<unsigned int BITS> class base_uint { protected: - enum { WIDTH=BITS/32 }; + static constexpr int WIDTH = BITS / 32; uint32_t pn[WIDTH]; public: diff --git a/src/base58.cpp b/src/base58.cpp index 3b907c20d6..cd087e7997 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -2,19 +2,22 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "base58.h" +#include <base58.h> -#include "hash.h" -#include "uint256.h" +#include <bech32.h> +#include <hash.h> +#include <script/script.h> +#include <uint256.h> +#include <utilstrencodings.h> -#include <assert.h> -#include <stdint.h> -#include <string.h> -#include <vector> -#include <string> #include <boost/variant/apply_visitor.hpp> #include <boost/variant/static_visitor.hpp> +#include <algorithm> +#include <assert.h> +#include <string.h> + + /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -136,7 +139,7 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) } // re-calculate the checksum, ensure it matches the included 4-byte checksum uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4); - if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) { + if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) { vchRet.clear(); return false; } @@ -212,86 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const namespace { -/** base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } - CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; -}; - -class CBitcoinAddressVisitor : public boost::static_visitor<bool> +class DestinationEncoder : public boost::static_visitor<std::string> { private: - CBitcoinAddress* addr; + const CChainParams& m_params; public: - explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} + DestinationEncoder(const CChainParams& params) : m_params(params) {} - bool operator()(const CKeyID& id) const { return addr->Set(id); } - bool operator()(const CScriptID& id) const { return addr->Set(id); } - bool operator()(const CNoDestination& no) const { return false; } -}; - -} // namespace + std::string operator()(const CKeyID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::Set(const CKeyID& id) -{ - SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); - return true; -} + std::string operator()(const CScriptID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::Set(const CScriptID& id) -{ - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); - return true; -} + std::string operator()(const WitnessV0KeyHash& id) const + { + std::vector<unsigned char> data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::Set(const CTxDestination& dest) -{ - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); -} + std::string operator()(const WitnessV0ScriptHash& id) const + { + std::vector<unsigned char> data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::IsValid() const -{ - return IsValid(Params()); -} + std::string operator()(const WitnessUnknown& id) const + { + if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { + return {}; + } + std::vector<unsigned char> data = {(unsigned char)id.version}; + ConvertBits<8, 5, true>(data, id.program, id.program + id.length); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::IsValid(const CChainParams& params) const -{ - bool fCorrectSize = vchData.size() == 20; - bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - return fCorrectSize && fKnownVersion; -} + std::string operator()(const CNoDestination& no) const { return {}; } +}; -CTxDestination CBitcoinAddress::Get() const +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) { - if (!IsValid()) - return CNoDestination(); - uint160 id; - memcpy(&id, vchData.data(), 20); - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) - return CKeyID(id); - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) - return CScriptID(id); - else - return CNoDestination(); + std::vector<unsigned char> data; + uint160 hash; + if (DecodeBase58Check(str, data)) { + // base58-encoded Bitcoin addresses. + // Public-key-hash-addresses have version 0 (or 111 testnet). + // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { + std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); + return CKeyID(hash); + } + // Script-hash-addresses have version 5 (or 196 testnet). + // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { + std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); + return CScriptID(hash); + } + } + data.clear(); + auto bech = bech32::Decode(str); + if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { + // Bech32 decoding + int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) + // The rest of the symbols are converted witness program bytes. + if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) { + if (version == 0) { + { + WitnessV0KeyHash keyid; + if (data.size() == keyid.size()) { + std::copy(data.begin(), data.end(), keyid.begin()); + return keyid; + } + } + { + WitnessV0ScriptHash scriptid; + if (data.size() == scriptid.size()) { + std::copy(data.begin(), data.end(), scriptid.begin()); + return scriptid; + } + } + return CNoDestination(); + } + if (version > 16 || data.size() < 2 || data.size() > 40) { + return CNoDestination(); + } + WitnessUnknown unk; + unk.version = version; + std::copy(data.begin(), data.end(), unk.program); + unk.length = data.size(); + return unk; + } + } + return CNoDestination(); } +} // namespace void CBitcoinSecret::SetKey(const CKey& vchSecret) { @@ -328,22 +358,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) std::string EncodeDestination(const CTxDestination& dest) { - CBitcoinAddress addr(dest); - if (!addr.IsValid()) return ""; - return addr.ToString(); + return boost::apply_visitor(DestinationEncoder(Params()), dest); } CTxDestination DecodeDestination(const std::string& str) { - return CBitcoinAddress(str).Get(); + return DecodeDestination(str, Params()); } bool IsValidDestinationString(const std::string& str, const CChainParams& params) { - return CBitcoinAddress(str).IsValid(params); + return IsValidDestination(DecodeDestination(str, params)); } bool IsValidDestinationString(const std::string& str) { - return CBitcoinAddress(str).IsValid(); + return IsValidDestinationString(str, Params()); } diff --git a/src/base58.h b/src/base58.h index 4b895ca022..fee79f02bf 100644 --- a/src/base58.h +++ b/src/base58.h @@ -14,12 +14,11 @@ #ifndef BITCOIN_BASE58_H #define BITCOIN_BASE58_H -#include "chainparams.h" -#include "key.h" -#include "pubkey.h" -#include "script/script.h" -#include "script/standard.h" -#include "support/allocators/zeroafterfree.h" +#include <chainparams.h> +#include <key.h> +#include <pubkey.h> +#include <script/standard.h> +#include <support/allocators/zeroafterfree.h> #include <string> #include <vector> diff --git a/src/bech32.cpp b/src/bech32.cpp new file mode 100644 index 0000000000..274782e467 --- /dev/null +++ b/src/bech32.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bech32.h> + +namespace +{ + +typedef std::vector<uint8_t> data; + +/** The Bech32 character set for encoding. */ +const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** The Bech32 character set for decoding. */ +const int8_t CHARSET_REV[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + +/** Concatenate two byte arrays. */ +data Cat(data x, const data& y) +{ + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to + * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher + * bits correspond to earlier values. */ +uint32_t PolyMod(const data& v) +{ + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an + // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = + // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that + // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. + + // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of + // v(x) mod g(x), where g(x) is the Bech32 generator, + // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way + // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a + // window of 1023 characters. Among the various possible BCH codes, one was selected to in + // fact guarantee detection of up to 4 errors within a window of 89 characters. + + // Note that the coefficients are elements of GF(32), here represented as decimal numbers + // between {}. In this finite field, addition is just XOR of the corresponding numbers. For + // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires + // treating the bits of values themselves as coefficients of a polynomial over a smaller field, + // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = + // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a + // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. + + // During the course of the loop below, `c` contains the bitpacked coefficients of the + // polynomial constructed from just the values of v that were processed so far, mod g(x). In + // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value + // for `c`. + uint32_t c = 1; + for (auto v_i : v) { + // We want to update `c` to correspond to a polynomial with one extra term. If the initial + // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to + // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to + // process. Simplifying: + // c'(x) = (f(x) * x + v_i) mod g(x) + // ((f(x) mod g(x)) * x + v_i) mod g(x) + // (c(x) * x + v_i) mod g(x) + // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute + // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) + // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) + // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i + // If we call (x^6 mod g(x)) = k(x), this can be written as + // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) + + // First, determine the value of c0: + uint8_t c0 = c >> 25; + + // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: + c = ((c & 0x1ffffff) << 5) ^ v_i; + + // Finally, for each set bit n in c0, conditionally add {2^n}k(x): + if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18} + if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13} + if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26} + if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29} + if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19} + } + return c; +} + +/** Convert to lower case. */ +inline unsigned char LowerCase(unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; +} + +/** Expand a HRP for use in checksum computation. */ +data ExpandHRP(const std::string& hrp) +{ + data ret; + ret.reserve(hrp.size() + 90); + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Verify a checksum. */ +bool VerifyChecksum(const std::string& hrp, const data& values) +{ + // PolyMod computes what value to xor into the final values to make the checksum 0. However, + // if we required that the checksum was 0, it would be the case that appending a 0 to a valid + // list of values would result in a new valid list. For that reason, Bech32 requires the + // resulting checksum to be 1 instead. + return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; +} + +/** Create a checksum. */ +data CreateChecksum(const std::string& hrp, const data& values) +{ + data enc = Cat(ExpandHRP(hrp), values); + enc.resize(enc.size() + 6); // Append 6 zeroes + uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + data ret(6); + for (size_t i = 0; i < 6; ++i) { + // Convert the 5-bit groups in mod to checksum values. + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; +} + +} // namespace + +namespace bech32 +{ + +/** Encode a Bech32 string. */ +std::string Encode(const std::string& hrp, const data& values) { + data checksum = CreateChecksum(hrp, values); + data combined = Cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (auto c : combined) { + ret += CHARSET[c]; + } + return ret; +} + +/** Decode a Bech32 string. */ +std::pair<std::string, data> Decode(const std::string& str) { + bool lower = false, upper = false; + for (size_t i = 0; i < str.size(); ++i) { + unsigned char c = str[i]; + if (c < 33 || c > 126) return {}; + if (c >= 'a' && c <= 'z') lower = true; + if (c >= 'A' && c <= 'Z') upper = true; + } + if (lower && upper) return {}; + size_t pos = str.rfind('1'); + if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + return {}; + } + data values(str.size() - 1 - pos); + for (size_t i = 0; i < str.size() - 1 - pos; ++i) { + unsigned char c = str[i + pos + 1]; + int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; + if (rev == -1) { + return {}; + } + values[i] = rev; + } + std::string hrp; + for (size_t i = 0; i < pos; ++i) { + hrp += LowerCase(str[i]); + } + if (!VerifyChecksum(hrp, values)) { + return {}; + } + return {hrp, data(values.begin(), values.end() - 6)}; +} + +} // namespace bech32 diff --git a/src/bech32.h b/src/bech32.h new file mode 100644 index 0000000000..7ef7b22213 --- /dev/null +++ b/src/bech32.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Bech32 is a string encoding format used in newer address types. +// The output consists of a human-readable part (alphanumeric), a +// separator character (1), and a base32 data section, the last +// 6 characters of which are a checksum. +// +// For more information, see BIP 173. + +#include <stdint.h> +#include <string> +#include <vector> + +namespace bech32 +{ + +/** Encode a Bech32 string. Returns the empty string in case of failure. */ +std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str); + +} // namespace bech32 diff --git a/src/bench/Examples.cpp b/src/bench/Examples.cpp index 314947d48c..536e450940 100644 --- a/src/bench/Examples.cpp +++ b/src/bench/Examples.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "validation.h" -#include "utiltime.h" +#include <bench/bench.h> +#include <validation.h> +#include <utiltime.h> // Sanity test: this should loop ten times, and // min/max/average should be close to 100ms. diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp index 65e27a615d..8f6d07ac16 100644 --- a/src/bench/base58.cpp +++ b/src/bench/base58.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" +#include <bench/bench.h> -#include "validation.h" -#include "base58.h" +#include <validation.h> +#include <base58.h> #include <array> #include <vector> diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 849d924af2..1482452814 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -2,35 +2,31 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "perf.h" +#include <bench/bench.h> +#include <bench/perf.h> #include <assert.h> #include <iostream> #include <iomanip> -#include <sys/time.h> benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() { static std::map<std::string, benchmark::BenchFunction> benchmarks_map; return benchmarks_map; } -static double gettimedouble(void) { - struct timeval tv; - gettimeofday(&tv, nullptr); - return tv.tv_usec * 0.000001 + tv.tv_sec; -} - benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func) { benchmarks().insert(std::make_pair(name, func)); } void -benchmark::BenchRunner::RunAll(double elapsedTimeForOne) +benchmark::BenchRunner::RunAll(benchmark::duration elapsedTimeForOne) { perf_init(); - std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "," + if (std::ratio_less_equal<benchmark::clock::period, std::micro>::value) { + std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n"; + } + std::cout << "#Benchmark" << "," << "count" << "," << "min(ns)" << "," << "max(ns)" << "," << "average(ns)" << "," << "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n"; for (const auto &p: benchmarks()) { @@ -46,22 +42,23 @@ bool benchmark::State::KeepRunning() ++count; return true; } - double now; + time_point now; + uint64_t nowCycles; if (count == 0) { - lastTime = beginTime = now = gettimedouble(); + lastTime = beginTime = now = clock::now(); lastCycles = beginCycles = nowCycles = perf_cpucycles(); } else { - now = gettimedouble(); - double elapsed = now - lastTime; - double elapsedOne = elapsed * countMaskInv; + now = clock::now(); + auto elapsed = now - lastTime; + auto elapsedOne = elapsed / (countMask + 1); if (elapsedOne < minTime) minTime = elapsedOne; if (elapsedOne > maxTime) maxTime = elapsedOne; // We only use relative values, so don't have to handle 64-bit wrap-around specially nowCycles = perf_cpucycles(); - uint64_t elapsedOneCycles = (nowCycles - lastCycles) * countMaskInv; + uint64_t elapsedOneCycles = (nowCycles - lastCycles) / (countMask + 1); if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles; if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles; @@ -69,10 +66,9 @@ bool benchmark::State::KeepRunning() // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing. // The restart avoids including the overhead of this code in the measurement. countMask = ((countMask<<3)|7) & ((1LL<<60)-1); - countMaskInv = 1./(countMask+1); count = 0; - minTime = std::numeric_limits<double>::max(); - maxTime = std::numeric_limits<double>::min(); + minTime = duration::max(); + maxTime = duration::zero(); minCycles = std::numeric_limits<uint64_t>::max(); maxCycles = std::numeric_limits<uint64_t>::min(); return true; @@ -81,7 +77,6 @@ bool benchmark::State::KeepRunning() uint64_t newCountMask = ((countMask<<1)|1) & ((1LL<<60)-1); if ((count & newCountMask)==0) { countMask = newCountMask; - countMaskInv = 1./(countMask+1); } } } @@ -96,9 +91,13 @@ bool benchmark::State::KeepRunning() assert(count != 0 && "count == 0 => (now == 0 && beginTime == 0) => return above"); // Output results - double average = (now-beginTime)/count; + // Duration casts are only necessary here because hardware with sub-nanosecond clocks + // will lose precision. + int64_t min_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(minTime).count(); + int64_t max_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(maxTime).count(); + int64_t avg_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>((now-beginTime)/count).count(); int64_t averageCycles = (nowCycles-beginCycles)/count; - std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "," + std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << min_elapsed << "," << max_elapsed << "," << avg_elapsed << "," << minCycles << "," << maxCycles << "," << averageCycles << "\n"; std::cout.copyfmt(std::ios(nullptr)); diff --git a/src/bench/bench.h b/src/bench/bench.h index 1f36f2a4bc..071a5dc9c7 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -9,6 +9,7 @@ #include <limits> #include <map> #include <string> +#include <chrono> #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/stringize.hpp> @@ -36,12 +37,21 @@ BENCHMARK(CODE_TO_TIME); */ namespace benchmark { + // In case high_resolution_clock is steady, prefer that, otherwise use steady_clock. + struct best_clock { + using hi_res_clock = std::chrono::high_resolution_clock; + using steady_clock = std::chrono::steady_clock; + using type = std::conditional<hi_res_clock::is_steady, hi_res_clock, steady_clock>::type; + }; + using clock = best_clock::type; + using time_point = clock::time_point; + using duration = clock::duration; class State { std::string name; - double maxElapsed; - double beginTime; - double lastTime, minTime, maxTime, countMaskInv; + duration maxElapsed; + time_point beginTime, lastTime; + duration minTime, maxTime; uint64_t count; uint64_t countMask; uint64_t beginCycles; @@ -49,13 +59,17 @@ namespace benchmark { uint64_t minCycles; uint64_t maxCycles; public: - State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) { - minTime = std::numeric_limits<double>::max(); - maxTime = std::numeric_limits<double>::min(); - minCycles = std::numeric_limits<uint64_t>::max(); - maxCycles = std::numeric_limits<uint64_t>::min(); - countMask = 1; - countMaskInv = 1./(countMask + 1); + State(std::string _name, duration _maxElapsed) : + name(_name), + maxElapsed(_maxElapsed), + minTime(duration::max()), + maxTime(duration::zero()), + count(0), + countMask(1), + beginCycles(0), + lastCycles(0), + minCycles(std::numeric_limits<uint64_t>::max()), + maxCycles(std::numeric_limits<uint64_t>::min()) { } bool KeepRunning(); }; @@ -70,7 +84,7 @@ namespace benchmark { public: BenchRunner(std::string name, BenchFunction func); - static void RunAll(double elapsedTimeForOne=1.0); + static void RunAll(duration elapsedTimeForOne = std::chrono::seconds(1)); }; } diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 37fd772435..84e51d809a 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" +#include <bench/bench.h> -#include "crypto/sha256.h" -#include "key.h" -#include "validation.h" -#include "util.h" -#include "random.h" +#include <crypto/sha256.h> +#include <key.h> +#include <validation.h> +#include <util.h> +#include <random.h> int main(int argc, char** argv) diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp index 5aab3381fd..89ba3d3d21 100644 --- a/src/bench/ccoins_caching.cpp +++ b/src/bench/ccoins_caching.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "coins.h" -#include "policy/policy.h" -#include "wallet/crypter.h" +#include <bench/bench.h> +#include <coins.h> +#include <policy/policy.h> +#include <wallet/crypter.h> #include <vector> diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 7bb1b93668..9533b3c711 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -2,15 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" +#include <bench/bench.h> -#include "chainparams.h" -#include "validation.h" -#include "streams.h" -#include "consensus/validation.h" +#include <chainparams.h> +#include <validation.h> +#include <streams.h> +#include <consensus/validation.h> namespace block_bench { -#include "bench/data/block413567.raw.h" +#include <bench/data/block413567.raw.h> } // namespace block_bench // These are the two major time-sinks which happen after we have fully received diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index b7ae5c2d57..7e63e820db 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "util.h" -#include "validation.h" -#include "checkqueue.h" -#include "prevector.h" +#include <bench/bench.h> +#include <util.h> +#include <validation.h> +#include <checkqueue.h> +#include <prevector.h> #include <vector> #include <boost/thread/thread.hpp> -#include "random.h" +#include <random.h> // This Benchmark tests the CheckQueue with the lightest diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index f8956508f6..ff57f88170 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "wallet/wallet.h" +#include <bench/bench.h> +#include <wallet/wallet.h> #include <set> diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 410a08e512..b37b5cad62 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -4,16 +4,16 @@ #include <iostream> -#include "bench.h" -#include "bloom.h" -#include "hash.h" -#include "random.h" -#include "uint256.h" -#include "utiltime.h" -#include "crypto/ripemd160.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/sha512.h" +#include <bench/bench.h> +#include <bloom.h> +#include <hash.h> +#include <random.h> +#include <uint256.h> +#include <utiltime.h> +#include <crypto/ripemd160.h> +#include <crypto/sha1.h> +#include <crypto/sha256.h> +#include <crypto/sha512.h> /* Number of bytes to hash per iteration */ static const uint64_t BUFFER_SIZE = 1000*1000; diff --git a/src/bench/lockedpool.cpp b/src/bench/lockedpool.cpp index c6a05567be..b0bfa95144 100644 --- a/src/bench/lockedpool.cpp +++ b/src/bench/lockedpool.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" +#include <bench/bench.h> -#include "support/lockedpool.h" +#include <support/lockedpool.h> #include <iostream> #include <vector> diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index 073bbde016..eda6edbb23 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "policy/policy.h" -#include "txmempool.h" +#include <bench/bench.h> +#include <policy/policy.h> +#include <txmempool.h> #include <list> #include <vector> diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp index a549ec29ea..4045670a31 100644 --- a/src/bench/perf.cpp +++ b/src/bench/perf.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "perf.h" +#include <bench/perf.h> #if defined(__i386__) || defined(__x86_64__) diff --git a/src/bench/prevector_destructor.cpp b/src/bench/prevector_destructor.cpp index 55af3de4fe..de7ecab737 100644 --- a/src/bench/prevector_destructor.cpp +++ b/src/bench/prevector_destructor.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "prevector.h" +#include <bench/bench.h> +#include <prevector.h> static void PrevectorDestructor(benchmark::State& state) { diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp index 73c02cf718..452099b800 100644 --- a/src/bench/rollingbloom.cpp +++ b/src/bench/rollingbloom.cpp @@ -4,9 +4,8 @@ #include <iostream> -#include "bench.h" -#include "bloom.h" -#include "utiltime.h" +#include <bench/bench.h> +#include <bloom.h> static void RollingBloom(benchmark::State& state) { @@ -23,10 +22,10 @@ static void RollingBloom(benchmark::State& state) data[2] = count >> 16; data[3] = count >> 24; if (countnow == nEntriesPerGeneration) { - int64_t b = GetTimeMicros(); + auto b = benchmark::clock::now(); filter.insert(data); - int64_t e = GetTimeMicros(); - std::cout << "RollingBloom-refresh,1," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "\n"; + auto total = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark::clock::now() - b).count(); + std::cout << "RollingBloom-refresh,1," << total << "," << total << "," << total << "\n"; countnow = 0; } else { filter.insert(data); diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index ef7381c120..bfa5806c9d 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bench.h" -#include "key.h" +#include <bench/bench.h> +#include <key.h> #if defined(HAVE_CONSENSUS_LIB) -#include "script/bitcoinconsensus.h" +#include <script/bitcoinconsensus.h> #endif -#include "script/script.h" -#include "script/sign.h" -#include "streams.h" +#include <script/script.h> +#include <script/sign.h> +#include <streams.h> #include <array> diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 3c94c99b3e..136981b709 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -4,22 +4,22 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "chainparamsbase.h" -#include "clientversion.h" -#include "fs.h" -#include "rpc/client.h" -#include "rpc/protocol.h" -#include "util.h" -#include "utilstrencodings.h" +#include <chainparamsbase.h> +#include <clientversion.h> +#include <fs.h> +#include <rpc/client.h> +#include <rpc/protocol.h> +#include <util.h> +#include <utilstrencodings.h> #include <stdio.h> #include <event2/buffer.h> #include <event2/keyvalq_struct.h> -#include "support/events.h" +#include <support/events.h> #include <univalue.h> @@ -37,6 +37,7 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-?", _("This help message")); strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); + strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)")); AppendParamsHelpMessages(strUsage); strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT)); @@ -191,7 +192,99 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -static UniValue CallRPC(const std::string& strMethod, const UniValue& params) +/** Class that handles the conversion from a command-line to a JSON-RPC request, + * as well as converting back to a JSON object that can be shown as result. + */ +class BaseRequestHandler +{ +public: + virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0; + virtual UniValue ProcessReply(const UniValue &batch_in) = 0; +}; + +/** Process getinfo requests */ +class GetinfoRequestHandler: public BaseRequestHandler +{ +public: + const int ID_NETWORKINFO = 0; + const int ID_BLOCKCHAININFO = 1; + const int ID_WALLETINFO = 2; + + /** Create a simulated `getinfo` request. */ + UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override + { + if (!args.empty()) { + throw std::runtime_error("-getinfo takes no arguments"); + } + UniValue result(UniValue::VARR); + result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); + result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO)); + result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO)); + return result; + } + + /** Collect values from the batch and form a simulated `getinfo` reply. */ + UniValue ProcessReply(const UniValue &batch_in) override + { + UniValue result(UniValue::VOBJ); + std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in, 3); + // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on + // getwalletinfo() is allowed to fail in case there is no wallet. + if (!batch[ID_NETWORKINFO]["error"].isNull()) { + return batch[ID_NETWORKINFO]; + } + if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) { + return batch[ID_BLOCKCHAININFO]; + } + result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); + result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]); + if (!batch[ID_WALLETINFO].isNull()) { + result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); + result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); + } + result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); + result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]); + result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]); + result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]); + result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); + result.pushKV("testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test")); + if (!batch[ID_WALLETINFO].isNull()) { + result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); + result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); + result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]); + result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); + if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { + result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); + } + result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]); + } + result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]); + result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]); + return JSONRPCReplyObj(result, NullUniValue, 1); + } +}; + +/** Process default single requests */ +class DefaultRequestHandler: public BaseRequestHandler { +public: + UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override + { + UniValue params; + if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { + params = RPCConvertNamedValues(method, args); + } else { + params = RPCConvertValues(method, args); + } + return JSONRPCRequestObj(method, params, 1); + } + + UniValue ProcessReply(const UniValue &reply) override + { + return reply.get_obj(); + } +}; + +static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, const std::vector<std::string>& args) { std::string host; // In preference order, we choose the following for the port: @@ -238,7 +331,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params) evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); // Attach request data - std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; + std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n"; struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); @@ -277,7 +370,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params) UniValue valReply(UniValue::VSTR); if (!valReply.read(response.body)) throw std::runtime_error("couldn't parse reply from server"); - const UniValue& reply = valReply.get_obj(); + const UniValue reply = rh->ProcessReply(valReply); if (reply.empty()) throw std::runtime_error("expected reply to have result, error and id properties"); @@ -309,24 +402,25 @@ int CommandLineRPC(int argc, char *argv[]) args.push_back(line); } } - if (args.size() < 1) { - throw std::runtime_error("too few parameters (need at least command)"); - } - std::string strMethod = args[0]; - args.erase(args.begin()); // Remove trailing method name from arguments vector - - UniValue params; - if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { - params = RPCConvertNamedValues(strMethod, args); + std::unique_ptr<BaseRequestHandler> rh; + std::string method; + if (gArgs.GetBoolArg("-getinfo", false)) { + rh.reset(new GetinfoRequestHandler()); + method = ""; } else { - params = RPCConvertValues(strMethod, args); + rh.reset(new DefaultRequestHandler()); + if (args.size() < 1) { + throw std::runtime_error("too few parameters (need at least command)"); + } + method = args[0]; + args.erase(args.begin()); // Remove trailing method name from arguments vector } // Execute and handle connection failures with -rpcwait const bool fWait = gArgs.GetBoolArg("-rpcwait", false); do { try { - const UniValue reply = CallRPC(strMethod, params); + const UniValue reply = CallRPC(rh.get(), method, args); // Parse reply const UniValue& result = find_value(reply, "result"); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index d8d7934bf6..c9223dbf10 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -3,24 +3,24 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "base58.h" -#include "clientversion.h" -#include "coins.h" -#include "consensus/consensus.h" -#include "core_io.h" -#include "keystore.h" -#include "policy/policy.h" -#include "policy/rbf.h" -#include "primitives/transaction.h" -#include "script/script.h" -#include "script/sign.h" +#include <base58.h> +#include <clientversion.h> +#include <coins.h> +#include <consensus/consensus.h> +#include <core_io.h> +#include <keystore.h> +#include <policy/policy.h> +#include <policy/rbf.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <script/sign.h> #include <univalue.h> -#include "util.h" -#include "utilmoneystr.h" -#include "utilstrencodings.h" +#include <util.h> +#include <utilmoneystr.h> +#include <utilstrencodings.h> #include <stdio.h> @@ -310,6 +310,9 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str } if (bSegWit) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } @@ -375,10 +378,19 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bSegWit) { + for (CPubKey& pubkey : pubkeys) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { + if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) { + throw std::runtime_error(strprintf( + "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE)); + } // Get the ID for the script, and then construct a P2SH destination for it. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } @@ -439,10 +451,19 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str bScriptHash = (flags.find("S") != std::string::npos); } + if (scriptPubKey.size() > MAX_SCRIPT_SIZE) { + throw std::runtime_error(strprintf( + "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE)); + } + if (bSegWit) { scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { + if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) { + throw std::runtime_error(strprintf( + "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE)); + } scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } @@ -682,10 +703,10 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, else if (command == "outaddr") MutateTxAddOutAddr(tx, commandVal); else if (command == "outpubkey") { - if (!ecc) { ecc.reset(new Secp256k1Init()); } + ecc.reset(new Secp256k1Init()); MutateTxAddOutPubKey(tx, commandVal); } else if (command == "outmultisig") { - if (!ecc) { ecc.reset(new Secp256k1Init()); } + ecc.reset(new Secp256k1Init()); MutateTxAddOutMultiSig(tx, commandVal); } else if (command == "outscript") MutateTxAddOutScript(tx, commandVal); @@ -693,7 +714,7 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command, MutateTxAddOutData(tx, commandVal); else if (command == "sign") { - if (!ecc) { ecc.reset(new Secp256k1Init()); } + ecc.reset(new Secp256k1Init()); MutateTxSign(tx, commandVal); } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 543eba0e69..bc23912d2b 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -4,21 +4,21 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "chainparams.h" -#include "clientversion.h" -#include "compat.h" -#include "fs.h" -#include "rpc/server.h" -#include "init.h" -#include "noui.h" -#include "scheduler.h" -#include "util.h" -#include "httpserver.h" -#include "httprpc.h" -#include "utilstrencodings.h" +#include <chainparams.h> +#include <clientversion.h> +#include <compat.h> +#include <fs.h> +#include <rpc/server.h> +#include <init.h> +#include <noui.h> +#include <scheduler.h> +#include <util.h> +#include <httpserver.h> +#include <httprpc.h> +#include <utilstrencodings.h> #include <boost/thread.hpp> @@ -120,7 +120,7 @@ bool AppInit(int argc, char* argv[]) for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]); - exit(EXIT_FAILURE); + return false; } } @@ -132,17 +132,17 @@ bool AppInit(int argc, char* argv[]) if (!AppInitBasicSetup()) { // InitError will have been called with detailed error, which ends up on console - exit(EXIT_FAILURE); + return false; } if (!AppInitParameterInteraction()) { // InitError will have been called with detailed error, which ends up on console - exit(EXIT_FAILURE); + return false; } if (!AppInitSanityChecks()) { // InitError will have been called with detailed error, which ends up on console - exit(EXIT_FAILURE); + return false; } if (gArgs.GetBoolArg("-daemon", false)) { @@ -163,7 +163,7 @@ bool AppInit(int argc, char* argv[]) if (!AppInitLockDataDirectory()) { // If locking the data directory failed, exit immediately - exit(EXIT_FAILURE); + return false; } fRet = AppInitMain(threadGroup, scheduler); } diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 6f27b7b9dc..6ff4d97bf4 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -2,16 +2,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "blockencodings.h" -#include "consensus/consensus.h" -#include "consensus/validation.h" -#include "chainparams.h" -#include "hash.h" -#include "random.h" -#include "streams.h" -#include "txmempool.h" -#include "validation.h" -#include "util.h" +#include <blockencodings.h> +#include <consensus/consensus.h> +#include <consensus/validation.h> +#include <chainparams.h> +#include <hash.h> +#include <random.h> +#include <streams.h> +#include <txmempool.h> +#include <validation.h> +#include <util.h> #include <unordered_map> diff --git a/src/blockencodings.h b/src/blockencodings.h index 50478f9f32..d034bb3080 100644 --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_BLOCK_ENCODINGS_H #define BITCOIN_BLOCK_ENCODINGS_H -#include "primitives/block.h" +#include <primitives/block.h> #include <memory> diff --git a/src/bloom.cpp b/src/bloom.cpp index fa884f0bf3..11ea099637 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bloom.h" - -#include "primitives/transaction.h" -#include "hash.h" -#include "script/script.h" -#include "script/standard.h" -#include "random.h" -#include "streams.h" +#include <bloom.h> + +#include <primitives/transaction.h> +#include <hash.h> +#include <script/script.h> +#include <script/standard.h> +#include <random.h> +#include <streams.h> #include <math.h> #include <stdlib.h> diff --git a/src/bloom.h b/src/bloom.h index 7ca9682239..d29a8dcd6f 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_BLOOM_H #define BITCOIN_BLOOM_H -#include "serialize.h" +#include <serialize.h> #include <vector> diff --git a/src/chain.cpp b/src/chain.cpp index 63e75ccecf..7ebc08a50b 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -3,7 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chain.h" +#include <chain.h> /** * CChain implementation @@ -129,7 +129,7 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) // We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256 // as it's too large for an arith_uint256. However, as 2**256 is at least as large // as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1, - // or ~bnTarget / (nTarget+1) + 1. + // or ~bnTarget / (bnTarget+1) + 1. return (~bnTarget / (bnTarget + 1)) + 1; } diff --git a/src/chain.h b/src/chain.h index f1036e5d92..ca1900c22b 100644 --- a/src/chain.h +++ b/src/chain.h @@ -6,11 +6,11 @@ #ifndef BITCOIN_CHAIN_H #define BITCOIN_CHAIN_H -#include "arith_uint256.h" -#include "primitives/block.h" -#include "pow.h" -#include "tinyformat.h" -#include "uint256.h" +#include <arith_uint256.h> +#include <primitives/block.h> +#include <pow.h> +#include <tinyformat.h> +#include <uint256.h> #include <vector> @@ -304,7 +304,7 @@ public: return (int64_t)nTimeMax; } - enum { nMedianTimeSpan=11 }; + static constexpr int nMedianTimeSpan = 11; int64_t GetMedianTimePast() const { diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2021ec51db..e753c5ccff 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -3,16 +3,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chainparams.h" -#include "consensus/merkle.h" +#include <chainparams.h> +#include <consensus/merkle.h> -#include "tinyformat.h" -#include "util.h" -#include "utilstrencodings.h" +#include <tinyformat.h> +#include <util.h> +#include <utilstrencodings.h> #include <assert.h> -#include "chainparamsseeds.h" +#include <chainparamsseeds.h> static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { @@ -75,6 +75,7 @@ public: CMainParams() { strNetworkID = "main"; consensus.nSubsidyHalvingInterval = 210000; + consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012 consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 @@ -137,6 +138,8 @@ public: base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + bech32_hrp = "bc"; + vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); fDefaultConsistencyChecks = false; @@ -179,6 +182,7 @@ public: CTestNetParams() { strNetworkID = "test"; consensus.nSubsidyHalvingInterval = 210000; + consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65 consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 @@ -228,7 +232,6 @@ public: vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch", true); vSeeds.emplace_back("seed.tbtc.petertodd.org", true); vSeeds.emplace_back("testnet-seed.bluematt.me", false); - vSeeds.emplace_back("testnet-seed.bitcoin.schildbach.de", false); base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); @@ -236,6 +239,8 @@ public: base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + bech32_hrp = "tb"; + vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fDefaultConsistencyChecks = false; @@ -267,6 +272,7 @@ public: CRegTestParams() { strNetworkID = "regtest"; consensus.nSubsidyHalvingInterval = 150; + consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests) consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests) @@ -280,13 +286,13 @@ public: consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); @@ -330,6 +336,8 @@ public: base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "bcrt"; } }; diff --git a/src/chainparams.h b/src/chainparams.h index f55ae4cf7f..0d82a998b1 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -6,10 +6,10 @@ #ifndef BITCOIN_CHAINPARAMS_H #define BITCOIN_CHAINPARAMS_H -#include "chainparamsbase.h" -#include "consensus/params.h" -#include "primitives/block.h" -#include "protocol.h" +#include <chainparamsbase.h> +#include <consensus/params.h> +#include <primitives/block.h> +#include <protocol.h> #include <memory> #include <vector> @@ -73,6 +73,7 @@ public: std::string NetworkIDString() const { return strNetworkID; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::string& Bech32HRP() const { return bech32_hrp; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } @@ -86,6 +87,7 @@ protected: uint64_t nPruneAfterHeight; std::vector<CDNSSeedData> vSeeds; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; + std::string bech32_hrp; std::string strNetworkID; CBlock genesis; std::vector<SeedSpec6> vFixedSeeds; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index c966683b72..a7e87faee3 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -3,10 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chainparamsbase.h" +#include <chainparamsbase.h> -#include "tinyformat.h" -#include "util.h" +#include <tinyformat.h> +#include <util.h> #include <assert.h> diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 5afe3e66b6..2ef4dd8330 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "checkpoints.h" +#include <checkpoints.h> -#include "chain.h" -#include "chainparams.h" -#include "reverse_iterator.h" -#include "validation.h" -#include "uint256.h" +#include <chain.h> +#include <chainparams.h> +#include <reverse_iterator.h> +#include <validation.h> +#include <uint256.h> #include <stdint.h> diff --git a/src/checkpoints.h b/src/checkpoints.h index 7449d1063b..df97a674a8 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_CHECKPOINTS_H #define BITCOIN_CHECKPOINTS_H -#include "uint256.h" +#include <uint256.h> #include <map> diff --git a/src/checkqueue.h b/src/checkqueue.h index 6377fbe942..08de336079 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_CHECKQUEUE_H #define BITCOIN_CHECKQUEUE_H -#include "sync.h" +#include <sync.h> #include <algorithm> #include <vector> diff --git a/src/clientversion.cpp b/src/clientversion.cpp index d2344ded09..f7ded63e6a 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -2,15 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "clientversion.h" +#include <clientversion.h> -#include "tinyformat.h" +#include <tinyformat.h> #include <string> /** * Name of client reported in the 'version' message. Report the same name - * for both bitcoind and bitcoin-core, to make it harder for attackers to + * for both bitcoind and bitcoin-qt, to make it harder for attackers to * target servers or GUI users specifically. */ const std::string CLIENT_NAME("Satoshi"); @@ -39,7 +39,7 @@ const std::string CLIENT_NAME("Satoshi"); //! First, include build.h if requested #ifdef HAVE_BUILD_INFO -#include "build.h" +#include <obj/build.h> #endif //! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ diff --git a/src/clientversion.h b/src/clientversion.h index 3d5392619b..3a3d117d53 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -6,7 +6,7 @@ #define BITCOIN_CLIENTVERSION_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif //HAVE_CONFIG_H // Check that required client information is defined diff --git a/src/coins.cpp b/src/coins.cpp index b45dfc3342..03138ef878 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "coins.h" +#include <coins.h> -#include "consensus/consensus.h" -#include "memusage.h" -#include "random.h" +#include <consensus/consensus.h> +#include <memusage.h> +#include <random.h> #include <assert.h> @@ -146,56 +146,58 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { } bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) { - for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { - if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization). - CCoinsMap::iterator itUs = cacheCoins.find(it->first); - if (itUs == cacheCoins.end()) { - // The parent cache does not have an entry, while the child does - // We can ignore it if it's both FRESH and pruned in the child - if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) { - // Otherwise we will need to create it in the parent - // and move the data up and mark it as dirty - CCoinsCacheEntry& entry = cacheCoins[it->first]; - entry.coin = std::move(it->second.coin); - cachedCoinsUsage += entry.coin.DynamicMemoryUsage(); - entry.flags = CCoinsCacheEntry::DIRTY; - // We can mark it FRESH in the parent if it was FRESH in the child - // Otherwise it might have just been flushed from the parent's cache - // and already exist in the grandparent - if (it->second.flags & CCoinsCacheEntry::FRESH) - entry.flags |= CCoinsCacheEntry::FRESH; + for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) { + // Ignore non-dirty entries (optimization). + if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) { + continue; + } + CCoinsMap::iterator itUs = cacheCoins.find(it->first); + if (itUs == cacheCoins.end()) { + // The parent cache does not have an entry, while the child does + // We can ignore it if it's both FRESH and pruned in the child + if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) { + // Otherwise we will need to create it in the parent + // and move the data up and mark it as dirty + CCoinsCacheEntry& entry = cacheCoins[it->first]; + entry.coin = std::move(it->second.coin); + cachedCoinsUsage += entry.coin.DynamicMemoryUsage(); + entry.flags = CCoinsCacheEntry::DIRTY; + // We can mark it FRESH in the parent if it was FRESH in the child + // Otherwise it might have just been flushed from the parent's cache + // and already exist in the grandparent + if (it->second.flags & CCoinsCacheEntry::FRESH) { + entry.flags |= CCoinsCacheEntry::FRESH; } - } else { - // Assert that the child cache entry was not marked FRESH if the - // parent cache entry has unspent outputs. If this ever happens, - // it means the FRESH flag was misapplied and there is a logic - // error in the calling code. - if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent()) - throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs"); + } + } else { + // Assert that the child cache entry was not marked FRESH if the + // parent cache entry has unspent outputs. If this ever happens, + // it means the FRESH flag was misapplied and there is a logic + // error in the calling code. + if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent()) { + throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs"); + } - // Found the entry in the parent cache - if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) { - // 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 -= itUs->second.coin.DynamicMemoryUsage(); - cacheCoins.erase(itUs); - } else { - // A normal modification. - cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage(); - itUs->second.coin = std::move(it->second.coin); - cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage(); - itUs->second.flags |= CCoinsCacheEntry::DIRTY; - // NOTE: It is possible the child has a FRESH flag here in - // the event the entry we found in the parent is pruned. But - // we must not copy that FRESH flag to the parent as that - // pruned state likely still needs to be communicated to the - // grandparent. - } + // Found the entry in the parent cache + if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) { + // 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 -= itUs->second.coin.DynamicMemoryUsage(); + cacheCoins.erase(itUs); + } else { + // A normal modification. + cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage(); + itUs->second.coin = std::move(it->second.coin); + cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage(); + itUs->second.flags |= CCoinsCacheEntry::DIRTY; + // NOTE: It is possible the child has a FRESH flag here in + // the event the entry we found in the parent is pruned. But + // we must not copy that FRESH flag to the parent as that + // pruned state likely still needs to be communicated to the + // grandparent. } } - CCoinsMap::iterator itOld = it++; - mapCoins.erase(itOld); } hashBlock = hashBlockIn; return true; diff --git a/src/coins.h b/src/coins.h index efb5ce869c..0280abb56c 100644 --- a/src/coins.h +++ b/src/coins.h @@ -6,13 +6,13 @@ #ifndef BITCOIN_COINS_H #define BITCOIN_COINS_H -#include "primitives/transaction.h" -#include "compressor.h" -#include "core_memusage.h" -#include "hash.h" -#include "memusage.h" -#include "serialize.h" -#include "uint256.h" +#include <primitives/transaction.h> +#include <compressor.h> +#include <core_memusage.h> +#include <hash.h> +#include <memusage.h> +#include <serialize.h> +#include <uint256.h> #include <assert.h> #include <stdint.h> @@ -214,6 +214,11 @@ protected: public: CCoinsViewCache(CCoinsView *baseIn); + /** + * By deleting the copy constructor, we prevent accidentally using it when one intends to create a cache on top of a base cache. + */ + CCoinsViewCache(const CCoinsViewCache &) = delete; + // Standard CCoinsView methods bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; @@ -290,11 +295,6 @@ public: private: CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const; - - /** - * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache. - */ - CCoinsViewCache(const CCoinsViewCache &); }; //! Utility function to add all of a transaction's outputs to a cache. diff --git a/src/compat.h b/src/compat.h index e022659c01..2d699159b7 100644 --- a/src/compat.h +++ b/src/compat.h @@ -7,7 +7,7 @@ #define BITCOIN_COMPAT_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #ifdef WIN32 @@ -49,7 +49,7 @@ #ifndef WIN32 typedef unsigned int SOCKET; -#include "errno.h" +#include <errno.h> #define WSAGetLastError() errno #define WSAEINVAL EINVAL #define WSAEALREADY EALREADY diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h index d93ff7413a..a6df6ded7a 100644 --- a/src/compat/byteswap.h +++ b/src/compat/byteswap.h @@ -6,7 +6,7 @@ #define BITCOIN_COMPAT_BYTESWAP_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <stdint.h> @@ -37,7 +37,7 @@ inline uint16_t bswap_16(uint16_t x) { return (x >> 8) | (x << 8); } -#endif // HAVE_DECL_BSWAP16 +#endif // HAVE_DECL_BSWAP16 == 0 #if HAVE_DECL_BSWAP_32 == 0 inline uint32_t bswap_32(uint32_t x) @@ -45,7 +45,7 @@ inline uint32_t bswap_32(uint32_t x) return (((x & 0xff000000U) >> 24) | ((x & 0x00ff0000U) >> 8) | ((x & 0x0000ff00U) << 8) | ((x & 0x000000ffU) << 24)); } -#endif // HAVE_DECL_BSWAP32 +#endif // HAVE_DECL_BSWAP32 == 0 #if HAVE_DECL_BSWAP_64 == 0 inline uint64_t bswap_64(uint64_t x) @@ -59,7 +59,7 @@ inline uint64_t bswap_64(uint64_t x) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56)); } -#endif // HAVE_DECL_BSWAP64 +#endif // HAVE_DECL_BSWAP64 == 0 #endif // defined(__APPLE__) diff --git a/src/compat/endian.h b/src/compat/endian.h index dbf178f53c..9fabbd0bfb 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -6,10 +6,10 @@ #define BITCOIN_COMPAT_ENDIAN_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "compat/byteswap.h" +#include <compat/byteswap.h> #include <stdint.h> diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index 3b9c70df7f..6633658c16 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <cstddef> diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index b4d1c90992..20d2ad3cb6 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <cstddef> diff --git a/src/compat/strnlen.cpp b/src/compat/strnlen.cpp index 1ac266c2d1..313435a222 100644 --- a/src/compat/strnlen.cpp +++ b/src/compat/strnlen.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <cstring> diff --git a/src/compressor.cpp b/src/compressor.cpp index f4c12f38d2..a729e28f07 100644 --- a/src/compressor.cpp +++ b/src/compressor.cpp @@ -3,11 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "compressor.h" +#include <compressor.h> -#include "hash.h" -#include "pubkey.h" -#include "script/standard.h" +#include <hash.h> +#include <pubkey.h> +#include <script/standard.h> bool CScriptCompressor::IsToKeyID(CKeyID &hash) const { diff --git a/src/compressor.h b/src/compressor.h index 094c1bcfe1..8731eef9f7 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_COMPRESSOR_H #define BITCOIN_COMPRESSOR_H -#include "primitives/transaction.h" -#include "script/script.h" -#include "serialize.h" +#include <primitives/transaction.h> +#include <script/script.h> +#include <serialize.h> class CKeyID; class CPubKey; diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index ddd4ee9fab..6e3bac2d0e 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -24,12 +24,9 @@ static const size_t MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60; // 60 is static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 10; // 10 is the lower bound for the size of a serialized CTransaction /** Flags for nSequence and nLockTime locks */ -enum { - /* Interpret sequence numbers as relative lock-time constraints. */ - LOCKTIME_VERIFY_SEQUENCE = (1 << 0), - - /* Use GetMedianTimePast() instead of nTime for end point timestamp. */ - LOCKTIME_MEDIAN_TIME_PAST = (1 << 1), -}; +/** Interpret sequence numbers as relative lock-time constraints. */ +static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0); +/** Use GetMedianTimePast() instead of nTime for end point timestamp. */ +static constexpr unsigned int LOCKTIME_MEDIAN_TIME_PAST = (1 << 1); #endif // BITCOIN_CONSENSUS_CONSENSUS_H diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 798ce4b5fd..fef4a5d560 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "merkle.h" -#include "hash.h" -#include "utilstrencodings.h" +#include <consensus/merkle.h> +#include <hash.h> +#include <utilstrencodings.h> /* WARNING! If you're reading this because you're learning about crypto and/or designing a new system that will use merkle trees, keep in mind diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 33764c7460..c1573cc214 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -8,9 +8,9 @@ #include <stdint.h> #include <vector> -#include "primitives/transaction.h" -#include "primitives/block.h" -#include "uint256.h" +#include <primitives/transaction.h> +#include <primitives/block.h> +#include <uint256.h> uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated = nullptr); std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position); diff --git a/src/consensus/params.h b/src/consensus/params.h index 6240e82857..c9fbba12a2 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -6,7 +6,8 @@ #ifndef BITCOIN_CONSENSUS_PARAMS_H #define BITCOIN_CONSENSUS_PARAMS_H -#include "uint256.h" +#include <uint256.h> +#include <limits> #include <map> #include <string> @@ -31,6 +32,15 @@ struct BIP9Deployment { int64_t nStartTime; /** Timeout/expiry MedianTime for the deployment attempt. */ int64_t nTimeout; + + /** Constant for nTimeout very far in the future. */ + static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max(); + + /** Special value for nStartTime indicating that the deployment is always active. + * This is useful for testing, as it means tests don't need to deal with the activation + * process (which takes at least 3 BIP9 intervals). Only tests that specifically test the + * behaviour during activation cannot use this. */ + static constexpr int64_t ALWAYS_ACTIVE = -1; }; /** @@ -39,6 +49,8 @@ struct BIP9Deployment { struct Params { uint256 hashGenesisBlock; int nSubsidyHalvingInterval; + /** Block height at which BIP16 becomes active */ + int BIP16Height; /** Block height and hash at which BIP34 becomes active */ int BIP34Height; uint256 BIP34Hash; diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 0a71915d1d..be73d0a2f9 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "tx_verify.h" +#include <consensus/tx_verify.h> -#include "consensus.h" -#include "primitives/transaction.h" -#include "script/interpreter.h" -#include "validation.h" +#include <consensus/consensus.h> +#include <primitives/transaction.h> +#include <script/interpreter.h> +#include <consensus/validation.h> // TODO remove the following dependencies -#include "chain.h" -#include "coins.h" -#include "utilmoneystr.h" - +#include <chain.h> +#include <coins.h> +#include <utilmoneystr.h> + bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if (tx.nLockTime == 0) @@ -205,46 +205,46 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe return true; } -bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) +bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) { - // 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)) - return state.Invalid(false, 0, "", "Inputs unavailable"); - - CAmount nValueIn = 0; - CAmount nFees = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const Coin& coin = inputs.AccessCoin(prevout); - assert(!coin.IsSpent()); - - // If prev is coinbase, check that it's matured - if (coin.IsCoinBase()) { - if (nSpendHeight - coin.nHeight < COINBASE_MATURITY) - return state.Invalid(false, - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", - strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); - } - - // Check for negative or overflow input values - nValueIn += coin.out.nValue; - if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + // are the actual inputs available? + if (!inputs.HaveInputs(tx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false, + strprintf("%s: inputs missing/spent", __func__)); + } + + CAmount nValueIn = 0; + for (unsigned int i = 0; i < tx.vin.size(); ++i) { + const COutPoint &prevout = tx.vin[i].prevout; + const Coin& coin = inputs.AccessCoin(prevout); + assert(!coin.IsSpent()); + + // If prev is coinbase, check that it's matured + if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); + } + // Check for negative or overflow input values + nValueIn += coin.out.nValue; + if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } + } + + const CAmount value_out = tx.GetValueOut(); + if (nValueIn < value_out) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); + } + + // Tally transaction fees + const CAmount txfee_aux = nValueIn - value_out; + if (!MoneyRange(txfee_aux)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + } - if (nValueIn < tx.GetValueOut()) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, - strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); - - // Tally transaction fees - CAmount nTxFee = nValueIn - tx.GetValueOut(); - if (nTxFee < 0) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + txfee = txfee_aux; return true; } diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index d46d3294ca..a4836ceda4 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_CONSENSUS_TX_VERIFY_H #define BITCOIN_CONSENSUS_TX_VERIFY_H +#include <amount.h> + #include <stdint.h> #include <vector> @@ -22,9 +24,10 @@ namespace Consensus { /** * Check whether all inputs of this transaction are valid (no double spends and amounts) * This does not modify the UTXO set. This does not check scripts and sigs. + * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight); +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 5494ce40ea..55f6c363fc 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -7,10 +7,10 @@ #define BITCOIN_CONSENSUS_VALIDATION_H #include <string> -#include "version.h" -#include "consensus/consensus.h" -#include "primitives/transaction.h" -#include "primitives/block.h" +#include <version.h> +#include <consensus/consensus.h> +#include <primitives/transaction.h> +#include <primitives/block.h> /** "reject" message codes */ static const unsigned char REJECT_MALFORMED = 0x01; @@ -89,17 +89,16 @@ public: std::string GetDebugMessage() const { return strDebugMessage; } }; +// These implement the weight = (stripped_size * 4) + witness_size formula, +// using only serialization with and without witness data. As witness_size +// is equal to total_size - stripped_size, this formula is identical to: +// weight = (stripped_size * 3) + total_size. static inline int64_t GetTransactionWeight(const CTransaction& tx) { - return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); } - static inline int64_t GetBlockWeight(const CBlock& block) { - // This implements the weight = (stripped_size * 4) + witness_size formula, - // using only serialization with and without witness data. As witness_size - // is equal to total_size - stripped_size, this formula is identical to: - // weight = (stripped_size * 3) + total_size. return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); } diff --git a/src/core_io.h b/src/core_io.h index ccc72ebb32..7ed84d6665 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_CORE_IO_H #define BITCOIN_CORE_IO_H -#include "amount.h" +#include <amount.h> #include <string> #include <vector> diff --git a/src/core_memusage.h b/src/core_memusage.h index f038e7b154..d234f0a9d0 100644 --- a/src/core_memusage.h +++ b/src/core_memusage.h @@ -5,9 +5,9 @@ #ifndef BITCOIN_CORE_MEMUSAGE_H #define BITCOIN_CORE_MEMUSAGE_H -#include "primitives/transaction.h" -#include "primitives/block.h" -#include "memusage.h" +#include <primitives/transaction.h> +#include <primitives/block.h> +#include <memusage.h> static inline size_t RecursiveDynamicUsage(const CScript& script) { return memusage::DynamicUsage(script); diff --git a/src/core_read.cpp b/src/core_read.cpp index 7018131a13..2f5b67b5c7 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -2,17 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "core_io.h" +#include <core_io.h> -#include "primitives/block.h" -#include "primitives/transaction.h" -#include "script/script.h" -#include "serialize.h" -#include "streams.h" +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <serialize.h> +#include <streams.h> #include <univalue.h> -#include "util.h" -#include "utilstrencodings.h" -#include "version.h" +#include <util.h> +#include <utilstrencodings.h> +#include <version.h> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/predicate.hpp> diff --git a/src/core_write.cpp b/src/core_write.cpp index e16db13650..e97876bef3 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -2,19 +2,19 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "core_io.h" - -#include "base58.h" -#include "consensus/consensus.h" -#include "consensus/validation.h" -#include "script/script.h" -#include "script/standard.h" -#include "serialize.h" -#include "streams.h" +#include <core_io.h> + +#include <base58.h> +#include <consensus/consensus.h> +#include <consensus/validation.h> +#include <script/script.h> +#include <script/standard.h> +#include <serialize.h> +#include <streams.h> #include <univalue.h> -#include "util.h" -#include "utilmoneystr.h" -#include "utilstrencodings.h" +#include <util.h> +#include <utilmoneystr.h> +#include <utilstrencodings.h> UniValue ValueFromAmount(const CAmount& amount) { diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp index 5e70d25eee..baba8bcad0 100644 --- a/src/crypto/aes.cpp +++ b/src/crypto/aes.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "aes.h" -#include "crypto/common.h" +#include <crypto/aes.h> +#include <crypto/common.h> #include <assert.h> #include <string.h> extern "C" { -#include "crypto/ctaes/ctaes.c" +#include <crypto/ctaes/ctaes.c> } AES128Encrypt::AES128Encrypt(const unsigned char key[16]) diff --git a/src/crypto/aes.h b/src/crypto/aes.h index a7b63b19df..7dda94d291 100644 --- a/src/crypto/aes.h +++ b/src/crypto/aes.h @@ -8,7 +8,7 @@ #define BITCOIN_CRYPTO_AES_H extern "C" { -#include "crypto/ctaes/ctaes.h" +#include <crypto/ctaes/ctaes.h> } static const int AES_BLOCKSIZE = 16; diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp index 4038ae9f86..ac4470f04f 100644 --- a/src/crypto/chacha20.cpp +++ b/src/crypto/chacha20.cpp @@ -5,8 +5,8 @@ // Based on the public domain implementation 'merged' by D. J. Bernstein // See https://cr.yp.to/chacha.html. -#include "crypto/common.h" -#include "crypto/chacha20.h" +#include <crypto/common.h> +#include <crypto/chacha20.h> #include <string.h> diff --git a/src/crypto/common.h b/src/crypto/common.h index bd9bc9420b..5a193a4e64 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -6,13 +6,13 @@ #define BITCOIN_CRYPTO_COMMON_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <stdint.h> #include <string.h> -#include "compat/endian.h" +#include <compat/endian.h> uint16_t static inline ReadLE16(const unsigned char* ptr) { diff --git a/src/crypto/hmac_sha256.cpp b/src/crypto/hmac_sha256.cpp index 3c791625d0..9366e216c2 100644 --- a/src/crypto/hmac_sha256.cpp +++ b/src/crypto/hmac_sha256.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/hmac_sha256.h" +#include <crypto/hmac_sha256.h> #include <string.h> diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h index 8c42fcfe14..40d40f9fb2 100644 --- a/src/crypto/hmac_sha256.h +++ b/src/crypto/hmac_sha256.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_CRYPTO_HMAC_SHA256_H #define BITCOIN_CRYPTO_HMAC_SHA256_H -#include "crypto/sha256.h" +#include <crypto/sha256.h> #include <stdint.h> #include <stdlib.h> diff --git a/src/crypto/hmac_sha512.cpp b/src/crypto/hmac_sha512.cpp index 5939c6ec47..f984201f6a 100644 --- a/src/crypto/hmac_sha512.cpp +++ b/src/crypto/hmac_sha512.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/hmac_sha512.h" +#include <crypto/hmac_sha512.h> #include <string.h> diff --git a/src/crypto/hmac_sha512.h b/src/crypto/hmac_sha512.h index 17dee61ea8..6d3fd59f27 100644 --- a/src/crypto/hmac_sha512.h +++ b/src/crypto/hmac_sha512.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_CRYPTO_HMAC_SHA512_H #define BITCOIN_CRYPTO_HMAC_SHA512_H -#include "crypto/sha512.h" +#include <crypto/sha512.h> #include <stdint.h> #include <stdlib.h> diff --git a/src/crypto/ripemd160.cpp b/src/crypto/ripemd160.cpp index 77c9acfc26..4afa9abd1e 100644 --- a/src/crypto/ripemd160.cpp +++ b/src/crypto/ripemd160.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/ripemd160.h" +#include <crypto/ripemd160.h> -#include "crypto/common.h" +#include <crypto/common.h> #include <string.h> diff --git a/src/crypto/sha1.cpp b/src/crypto/sha1.cpp index 0b895b33a2..8ccc5e112a 100644 --- a/src/crypto/sha1.cpp +++ b/src/crypto/sha1.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/sha1.h" +#include <crypto/sha1.h> -#include "crypto/common.h" +#include <crypto/common.h> #include <string.h> diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 29afe86ec7..9a21aec20e 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/sha256.h" -#include "crypto/common.h" +#include <crypto/sha256.h> +#include <crypto/common.h> #include <assert.h> #include <string.h> diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp index 564127cc31..aa46e7ba86 100644 --- a/src/crypto/sha512.cpp +++ b/src/crypto/sha512.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/sha512.h" +#include <crypto/sha512.h> -#include "crypto/common.h" +#include <crypto/common.h> #include <string.h> diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index dfc90f3ab9..eb29be05c5 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "dbwrapper.h" +#include <dbwrapper.h> -#include "fs.h" -#include "util.h" -#include "random.h" +#include <fs.h> +#include <util.h> +#include <random.h> #include <leveldb/cache.h> #include <leveldb/env.h> @@ -64,7 +64,7 @@ public: assert(p <= limit); base[std::min(bufsize - 1, (int)(p - base))] = '\0'; - LogPrintStr(base); + LogPrintf("leveldb: %s", base); if (base != buffer) { delete[] base; } diff --git a/src/dbwrapper.h b/src/dbwrapper.h index e19fde51c1..9f4d4814eb 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -5,13 +5,13 @@ #ifndef BITCOIN_DBWRAPPER_H #define BITCOIN_DBWRAPPER_H -#include "clientversion.h" -#include "fs.h" -#include "serialize.h" -#include "streams.h" -#include "util.h" -#include "utilstrencodings.h" -#include "version.h" +#include <clientversion.h> +#include <fs.h> +#include <serialize.h> +#include <streams.h> +#include <util.h> +#include <utilstrencodings.h> +#include <version.h> #include <leveldb/db.h> #include <leveldb/write_batch.h> diff --git a/src/fs.cpp b/src/fs.cpp index a5e12f1cfc..570ed3e2ee 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,4 +1,4 @@ -#include "fs.h" +#include <fs.h> namespace fsbridge { diff --git a/src/hash.cpp b/src/hash.cpp index 5a15600be5..1363d32cf4 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "hash.h" -#include "crypto/common.h" -#include "crypto/hmac_sha512.h" -#include "pubkey.h" +#include <hash.h> +#include <crypto/common.h> +#include <crypto/hmac_sha512.h> +#include <pubkey.h> inline uint32_t ROTL32(uint32_t x, int8_t r) diff --git a/src/hash.h b/src/hash.h index ad59bb1817..55724fb636 100644 --- a/src/hash.h +++ b/src/hash.h @@ -6,12 +6,12 @@ #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H -#include "crypto/ripemd160.h" -#include "crypto/sha256.h" -#include "prevector.h" -#include "serialize.h" -#include "uint256.h" -#include "version.h" +#include <crypto/ripemd160.h> +#include <crypto/sha256.h> +#include <prevector.h> +#include <serialize.h> +#include <uint256.h> +#include <version.h> #include <vector> @@ -88,20 +88,6 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, return result; } -/** Compute the 256-bit hash of the concatenation of three objects. */ -template<typename T1, typename T2, typename T3> -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end, - const T3 p3begin, const T3 p3end) { - static const unsigned char pblank[1] = {}; - uint256 result; - CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])) - .Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])) - .Write(p3begin == p3end ? pblank : (const unsigned char*)&p3begin[0], (p3end - p3begin) * sizeof(p3begin[0])) - .Finalize((unsigned char*)&result); - return result; -} - /** Compute the 160-bit hash an object. */ template<typename T1> inline uint160 Hash160(const T1 pbegin, const T1 pend) diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 91f96ef207..6b6849e59b 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -2,19 +2,19 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "httprpc.h" - -#include "base58.h" -#include "chainparams.h" -#include "httpserver.h" -#include "rpc/protocol.h" -#include "rpc/server.h" -#include "random.h" -#include "sync.h" -#include "util.h" -#include "utilstrencodings.h" -#include "ui_interface.h" -#include "crypto/hmac_sha256.h" +#include <httprpc.h> + +#include <base58.h> +#include <chainparams.h> +#include <httpserver.h> +#include <rpc/protocol.h> +#include <rpc/server.h> +#include <random.h> +#include <sync.h> +#include <util.h> +#include <utilstrencodings.h> +#include <ui_interface.h> +#include <crypto/hmac_sha256.h> #include <stdio.h> #include <boost/algorithm/string.hpp> // boost::trim @@ -62,7 +62,7 @@ private: /* Pre-base64-encoded authentication token */ static std::string strRPCUserColonPass; /* Stored RPC timer interface (for unregistration) */ -static HTTPRPCTimerInterface* httpRPCTimerInterface = nullptr; +static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface; static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id) { @@ -192,7 +192,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) // array of requests } else if (valRequest.isArray()) - strReply = JSONRPCExecBatch(valRequest.get_array()); + strReply = JSONRPCExecBatch(jreq, valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); @@ -238,8 +238,8 @@ bool StartHTTPRPC() RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC); #endif assert(EventBase()); - httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase()); - RPCSetTimerInterface(httpRPCTimerInterface); + httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(EventBase()); + RPCSetTimerInterface(httpRPCTimerInterface.get()); return true; } @@ -253,8 +253,7 @@ void StopHTTPRPC() LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n"); UnregisterHTTPHandler("/", true); if (httpRPCTimerInterface) { - RPCUnsetTimerInterface(httpRPCTimerInterface); - delete httpRPCTimerInterface; - httpRPCTimerInterface = nullptr; + RPCUnsetTimerInterface(httpRPCTimerInterface.get()); + httpRPCTimerInterface.reset(); } } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 5923871691..7d590f8e1f 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -2,16 +2,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "httpserver.h" +#include <httpserver.h> -#include "chainparamsbase.h" -#include "compat.h" -#include "util.h" -#include "utilstrencodings.h" -#include "netbase.h" -#include "rpc/protocol.h" // For HTTP status codes -#include "sync.h" -#include "ui_interface.h" +#include <chainparamsbase.h> +#include <compat.h> +#include <util.h> +#include <utilstrencodings.h> +#include <netbase.h> +#include <rpc/protocol.h> // For HTTP status codes +#include <sync.h> +#include <ui_interface.h> #include <stdio.h> #include <stdlib.h> @@ -24,10 +24,11 @@ #include <event2/thread.h> #include <event2/buffer.h> +#include <event2/bufferevent.h> #include <event2/util.h> #include <event2/keyvalq_struct.h> -#include "support/events.h" +#include <support/events.h> #ifdef EVENT__HAVE_NETINET_IN_H #include <netinet/in.h> @@ -239,6 +240,16 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m) /** HTTP request callback */ static void http_request_cb(struct evhttp_request* req, void* arg) { + // Disable reading to work around a libevent bug, fixed in 2.2.0. + if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { + evhttp_connection* conn = evhttp_request_get_connection(req); + if (conn) { + bufferevent* bev = evhttp_connection_get_bufferevent(conn); + if (bev) { + bufferevent_disable(bev, EV_READ); + } + } + } std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req)); LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", @@ -481,6 +492,8 @@ void StopHTTPServer() } if (eventBase) { LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); + // Exit the event loop as soon as there are no active events. + event_base_loopexit(eventBase, nullptr); // Give event loop a few seconds to exit (to send back last RPC responses), then break it // Before this was solved with event_base_loopexit, but that didn't work as expected in // at least libevent 2.0.21 and always introduced a delay. In libevent @@ -599,8 +612,21 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) struct evbuffer* evb = evhttp_request_get_output_buffer(req); assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); - HTTPEvent* ev = new HTTPEvent(eventBase, true, - std::bind(evhttp_send_reply, req, nStatus, (const char*)nullptr, (struct evbuffer *)nullptr)); + auto req_copy = req; + HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{ + evhttp_send_reply(req_copy, nStatus, nullptr, nullptr); + // Re-enable reading from the socket. This is the second part of the libevent + // workaround above. + if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { + evhttp_connection* conn = evhttp_request_get_connection(req_copy); + if (conn) { + bufferevent* bev = evhttp_connection_get_bufferevent(conn); + if (bev) { + bufferevent_enable(bev, EV_READ | EV_WRITE); + } + } + } + }); ev->trigger(nullptr); replySent = true; req = nullptr; // transferred back to main thread diff --git a/src/init.cpp b/src/init.cpp index 6b9699213f..9356862999 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -4,49 +4,49 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "init.h" - -#include "addrman.h" -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "compat/sanity.h" -#include "consensus/validation.h" -#include "fs.h" -#include "httpserver.h" -#include "httprpc.h" -#include "key.h" -#include "validation.h" -#include "miner.h" -#include "netbase.h" -#include "net.h" -#include "net_processing.h" -#include "policy/feerate.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "rpc/server.h" -#include "rpc/register.h" -#include "rpc/safemode.h" -#include "rpc/blockchain.h" -#include "script/standard.h" -#include "script/sigcache.h" -#include "scheduler.h" -#include "timedata.h" -#include "txdb.h" -#include "txmempool.h" -#include "torcontrol.h" -#include "ui_interface.h" -#include "util.h" -#include "utilmoneystr.h" -#include "validationinterface.h" +#include <init.h> + +#include <addrman.h> +#include <amount.h> +#include <chain.h> +#include <chainparams.h> +#include <checkpoints.h> +#include <compat/sanity.h> +#include <consensus/validation.h> +#include <fs.h> +#include <httpserver.h> +#include <httprpc.h> +#include <key.h> +#include <validation.h> +#include <miner.h> +#include <netbase.h> +#include <net.h> +#include <net_processing.h> +#include <policy/feerate.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <rpc/server.h> +#include <rpc/register.h> +#include <rpc/safemode.h> +#include <rpc/blockchain.h> +#include <script/standard.h> +#include <script/sigcache.h> +#include <scheduler.h> +#include <timedata.h> +#include <txdb.h> +#include <txmempool.h> +#include <torcontrol.h> +#include <ui_interface.h> +#include <util.h> +#include <utilmoneystr.h> +#include <validationinterface.h> #ifdef ENABLE_WALLET -#include "wallet/init.h" +#include <wallet/init.h> #endif -#include "warnings.h" +#include <warnings.h> #include <stdint.h> #include <stdio.h> #include <memory> @@ -64,7 +64,7 @@ #include <openssl/crypto.h> #if ENABLE_ZMQ -#include "zmq/zmqnotificationinterface.h" +#include <zmq/zmqnotificationinterface.h> #endif bool fFeeEstimatesInitialized = false; @@ -152,7 +152,7 @@ public: // Writes do not need similar protection, as failure to write is handled by the caller. }; -static CCoinsViewErrorCatcher *pcoinscatcher = nullptr; +static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher; static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; void Interrupt(boost::thread_group& threadGroup) @@ -193,8 +193,8 @@ void Shutdown() // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. - UnregisterValidationInterface(peerLogic.get()); - if(g_connman) g_connman->Stop(); + if (peerLogic) UnregisterValidationInterface(peerLogic.get()); + if (g_connman) g_connman->Stop(); peerLogic.reset(); g_connman.reset(); @@ -235,14 +235,10 @@ void Shutdown() if (pcoinsTip != nullptr) { FlushStateToDisk(); } - delete pcoinsTip; - pcoinsTip = nullptr; - delete pcoinscatcher; - pcoinscatcher = nullptr; - delete pcoinsdbview; - pcoinsdbview = nullptr; - delete pblocktree; - pblocktree = nullptr; + pcoinsTip.reset(); + pcoinscatcher.reset(); + pcoinsdbview.reset(); + pblocktree.reset(); } #ifdef ENABLE_WALLET StopWallets(); @@ -265,6 +261,7 @@ void Shutdown() #endif UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); + GetMainSignals().UnregisterWithMempoolSignals(mempool); #ifdef ENABLE_WALLET CloseWallets(); #endif @@ -369,11 +366,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX)); strUsage += HelpMessageGroup(_("Connection options:")); - strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open")); + strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)")); strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD)); strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME)); strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6")); - strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections")); + strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)")); strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)")); strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP)); strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)")); @@ -430,6 +427,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE)); + strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used"); strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE)); strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages"); strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages"); @@ -536,20 +534,21 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex return; std::string strCmd = gArgs.GetArg("-blocknotify", ""); - - boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + if (!strCmd.empty()) { + boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } } static bool fHaveGenesis = false; -static boost::mutex cs_GenesisWait; +static CWaitableCriticalSection cs_GenesisWait; static CConditionVariable condvar_GenesisWait; static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { if (pBlockIndex != nullptr) { { - boost::unique_lock<boost::mutex> lock_GenesisWait(cs_GenesisWait); + WaitableLock lock_GenesisWait(cs_GenesisWait); fHaveGenesis = true; } condvar_GenesisWait.notify_all(); @@ -586,7 +585,7 @@ void CleanupBlockRevFiles() LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); fs::path blocksdir = GetDataDir() / "blocks"; for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) { - if (is_regular_file(*it) && + if (fs::is_regular_file(*it) && it->path().filename().string().length() == 12 && it->path().filename().string().substr(8,4) == ".dat") { @@ -813,7 +812,6 @@ void InitLogging() namespace { // Variables internal to initialization process only -ServiceFlags nRelevantServices = NODE_NETWORK; int nMaxConnections; int nUserMaxConnections; int nFD; @@ -921,7 +919,8 @@ bool AppInitParameterInteraction() // Special-case: if -debug=0/-nodebug is set, turn off debugging messages const std::vector<std::string> categories = gArgs.GetArgs("-debug"); - if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) { + if (std::none_of(categories.begin(), categories.end(), + [](std::string cat){return cat == "0" || cat == "none";})) { for (const auto& cat : categories) { uint32_t flag = 0; if (!GetLogCategory(&flag, &cat)) { @@ -1032,11 +1031,6 @@ bool AppInitParameterInteraction() fPruneMode = true; } - RegisterAllCoreRPCCommands(tableRPC); -#ifdef ENABLE_WALLET - RegisterWalletRPC(tableRPC); -#endif - nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; @@ -1239,6 +1233,15 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); + GetMainSignals().RegisterWithMempoolSignals(mempool); + + /* Register RPC commands regardless of -server setting so they will be + * available in the GUI RPC console even if external calls are disabled. + */ + RegisterAllCoreRPCCommands(tableRPC); +#ifdef ENABLE_WALLET + RegisterWalletRPC(tableRPC); +#endif /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections @@ -1269,7 +1272,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()))); CConnman& connman = *g_connman; - peerLogic.reset(new PeerLogicValidation(&connman)); + peerLogic.reset(new PeerLogicValidation(&connman, scheduler)); RegisterValidationInterface(peerLogic.get()); // sanitize comments per BIP-0014, format user agent and check total size @@ -1405,12 +1408,10 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) do { try { UnloadBlockIndex(); - delete pcoinsTip; - delete pcoinsdbview; - delete pcoinscatcher; - delete pblocktree; - - pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReset); + pcoinsTip.reset(); + pcoinsdbview.reset(); + pcoinscatcher.reset(); + pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, false, fReset)); if (fReset) { pblocktree->WriteReindexing(true); @@ -1461,8 +1462,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // At this point we're either in reindex or we've loaded a useful // block tree into mapBlockIndex! - pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState); - pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); + pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState)); + pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get())); // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate @@ -1472,13 +1473,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate - if (!ReplayBlocks(chainparams, pcoinsdbview)) { + if (!ReplayBlocks(chainparams, pcoinsdbview.get())) { strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate."); break; } // The on-disk coinsdb is now in a good state, create the cache - pcoinsTip = new CCoinsViewCache(pcoinscatcher); + pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get())); bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull(); if (!is_coinsview_empty) { @@ -1520,7 +1521,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } } - if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview, gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), + if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { strLoadError = _("Corrupted block database detected"); break; @@ -1602,9 +1603,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Note that setting NODE_WITNESS is never required: the only downside from not // doing so is that after activation, no upgraded nodes will fetch from you. nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS); - // Only care about others providing witness capabilities if there is a softfork - // defined. - nRelevantServices = ServiceFlags(nRelevantServices | NODE_WITNESS); } // ********************************************************* Step 10: import blocks @@ -1632,7 +1630,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // Wait for genesis block to be processed { - boost::unique_lock<boost::mutex> lock(cs_GenesisWait); + WaitableLock lock(cs_GenesisWait); while (!fHaveGenesis) { condvar_GenesisWait.wait(lock); } @@ -1641,9 +1639,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 11: start node + int chain_active_height; + //// debug print - LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", chainActive.Height()); + { + LOCK(cs_main); + LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); + chain_active_height = chainActive.Height(); + } + LogPrintf("nBestHeight = %d\n", chain_active_height); + if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); @@ -1654,16 +1659,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; - connOptions.nRelevantServices = nRelevantServices; connOptions.nMaxConnections = nMaxConnections; connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = 1; - connOptions.nBestHeight = chainActive.Height(); + connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; connOptions.m_msgproc = peerLogic.get(); connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); + connOptions.m_added_nodes = gArgs.GetArgs("-addnode"); connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; @@ -1694,9 +1699,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.vWhitelistedRange.push_back(subnet); } - if (gArgs.IsArgSet("-seednode")) { - connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); - } + connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); + // Initiate outbound connections unless connect=0 connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect"); if (!connOptions.m_use_addrman_outgoing) { diff --git a/src/key.cpp b/src/key.cpp index 315a3978c8..73983caf41 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "key.h" +#include <key.h> -#include "arith_uint256.h" -#include "crypto/common.h" -#include "crypto/hmac_sha512.h" -#include "pubkey.h" -#include "random.h" +#include <arith_uint256.h> +#include <crypto/common.h> +#include <crypto/hmac_sha512.h> +#include <pubkey.h> +#include <random.h> #include <secp256k1.h> #include <secp256k1_recovery.h> @@ -6,10 +6,10 @@ #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H -#include "pubkey.h" -#include "serialize.h" -#include "support/allocators/secure.h" -#include "uint256.h" +#include <pubkey.h> +#include <serialize.h> +#include <support/allocators/secure.h> +#include <uint256.h> #include <stdexcept> #include <vector> @@ -56,11 +56,6 @@ public: keydata.resize(32); } - //! Destructor (again necessary because of memlocking). - ~CKey() - { - } - friend bool operator==(const CKey& a, const CKey& b) { return a.fCompressed == b.fCompressed && diff --git a/src/keystore.cpp b/src/keystore.cpp index 8454175ca8..5718a14ca2 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -3,11 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "keystore.h" +#include <keystore.h> -#include "key.h" -#include "pubkey.h" -#include "util.h" +#include <key.h> +#include <pubkey.h> +#include <util.h> bool CKeyStore::AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); @@ -36,6 +36,33 @@ bool CBasicKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) return true; } +bool CBasicKeyStore::HaveKey(const CKeyID &address) const +{ + LOCK(cs_KeyStore); + return mapKeys.count(address) > 0; +} + +std::set<CKeyID> CBasicKeyStore::GetKeys() const +{ + LOCK(cs_KeyStore); + std::set<CKeyID> set_address; + for (const auto& mi : mapKeys) { + set_address.insert(mi.first); + } + return set_address; +} + +bool CBasicKeyStore::GetKey(const CKeyID &address, CKey &keyOut) const +{ + LOCK(cs_KeyStore); + KeyMap::const_iterator mi = mapKeys.find(address); + if (mi != mapKeys.end()) { + keyOut = mi->second; + return true; + } + return false; +} + bool CBasicKeyStore::AddCScript(const CScript& redeemScript) { if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) diff --git a/src/keystore.h b/src/keystore.h index 9b85ddb0ec..4e6d8e8a27 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -6,11 +6,11 @@ #ifndef BITCOIN_KEYSTORE_H #define BITCOIN_KEYSTORE_H -#include "key.h" -#include "pubkey.h" -#include "script/script.h" -#include "script/standard.h" -#include "sync.h" +#include <key.h> +#include <pubkey.h> +#include <script/script.h> +#include <script/standard.h> +#include <sync.h> #include <boost/signals2/signal.hpp> @@ -62,37 +62,9 @@ protected: public: bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; - bool HaveKey(const CKeyID &address) const override - { - bool result; - { - LOCK(cs_KeyStore); - result = (mapKeys.count(address) > 0); - } - return result; - } - std::set<CKeyID> GetKeys() const override - { - LOCK(cs_KeyStore); - std::set<CKeyID> set_address; - for (const auto& mi : mapKeys) { - set_address.insert(mi.first); - } - return set_address; - } - bool GetKey(const CKeyID &address, CKey &keyOut) const override - { - { - LOCK(cs_KeyStore); - KeyMap::const_iterator mi = mapKeys.find(address); - if (mi != mapKeys.end()) - { - keyOut = mi->second; - return true; - } - } - return false; - } + bool HaveKey(const CKeyID &address) const override; + std::set<CKeyID> GetKeys() const override; + bool GetKey(const CKeyID &address, CKey &keyOut) const override; bool AddCScript(const CScript& redeemScript) override; bool HaveCScript(const CScriptID &hash) const override; bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override; diff --git a/src/memusage.h b/src/memusage.h index 93fd6a0eb5..d931171d35 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_MEMUSAGE_H #define BITCOIN_MEMUSAGE_H -#include "indirectmap.h" +#include <indirectmap.h> #include <stdlib.h> diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index f0abea0611..f4db9dd57e 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -3,39 +3,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "merkleblock.h" +#include <merkleblock.h> -#include "hash.h" -#include "consensus/consensus.h" -#include "utilstrencodings.h" +#include <hash.h> +#include <consensus/consensus.h> +#include <utilstrencodings.h> -CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) -{ - header = block.GetBlockHeader(); - std::vector<bool> vMatch; - std::vector<uint256> vHashes; - - vMatch.reserve(block.vtx.size()); - vHashes.reserve(block.vtx.size()); - - for (unsigned int i = 0; i < block.vtx.size(); i++) - { - const uint256& hash = block.vtx[i]->GetHash(); - if (filter.IsRelevantAndUpdate(*block.vtx[i])) - { - vMatch.push_back(true); - vMatchedTxn.push_back(std::make_pair(i, hash)); - } - else - vMatch.push_back(false); - vHashes.push_back(hash); - } - - txn = CPartialMerkleTree(vHashes, vMatch); -} - -CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids) { header = block.GetBlockHeader(); @@ -48,10 +23,14 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) for (unsigned int i = 0; i < block.vtx.size(); i++) { const uint256& hash = block.vtx[i]->GetHash(); - if (txids.count(hash)) + if (txids && txids->count(hash)) { vMatch.push_back(true); - else + } else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) { + vMatch.push_back(true); + vMatchedTxn.emplace_back(i, hash); + } else { vMatch.push_back(false); + } vHashes.push_back(hash); } diff --git a/src/merkleblock.h b/src/merkleblock.h index 20f2b36886..b64c34370e 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -6,10 +6,10 @@ #ifndef BITCOIN_MERKLEBLOCK_H #define BITCOIN_MERKLEBLOCK_H -#include "serialize.h" -#include "uint256.h" -#include "primitives/block.h" -#include "bloom.h" +#include <serialize.h> +#include <uint256.h> +#include <primitives/block.h> +#include <bloom.h> #include <vector> @@ -131,8 +131,12 @@ public: CBlockHeader header; CPartialMerkleTree txn; -public: - /** Public only for unit testing and relay testing (not relayed) */ + /** + * Public only for unit testing and relay testing (not relayed). + * + * Used only when a bloom filter is specified to allow + * testing the transactions which matched the bloom filter. + */ std::vector<std::pair<unsigned int, uint256> > vMatchedTxn; /** @@ -140,10 +144,10 @@ public: * Note that this will call IsRelevantAndUpdate on the filter for each transaction, * thus the filter will likely be modified. */ - CMerkleBlock(const CBlock& block, CBloomFilter& filter); + CMerkleBlock(const CBlock& block, CBloomFilter& filter) : CMerkleBlock(block, &filter, nullptr) { } // Create from a CBlock, matching the txids in the set - CMerkleBlock(const CBlock& block, const std::set<uint256>& txids); + CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) : CMerkleBlock(block, nullptr, &txids) { } CMerkleBlock() {} @@ -154,6 +158,10 @@ public: READWRITE(header); READWRITE(txn); } + +private: + // Combined constructor to consolidate code + CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids); }; #endif // BITCOIN_MERKLEBLOCK_H diff --git a/src/miner.cpp b/src/miner.cpp index a9989f4b17..397d99fc9a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -3,29 +3,29 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "miner.h" - -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "coins.h" -#include "consensus/consensus.h" -#include "consensus/tx_verify.h" -#include "consensus/merkle.h" -#include "consensus/validation.h" -#include "hash.h" -#include "validation.h" -#include "net.h" -#include "policy/feerate.h" -#include "policy/policy.h" -#include "pow.h" -#include "primitives/transaction.h" -#include "script/standard.h" -#include "timedata.h" -#include "txmempool.h" -#include "util.h" -#include "utilmoneystr.h" -#include "validationinterface.h" +#include <miner.h> + +#include <amount.h> +#include <chain.h> +#include <chainparams.h> +#include <coins.h> +#include <consensus/consensus.h> +#include <consensus/tx_verify.h> +#include <consensus/merkle.h> +#include <consensus/validation.h> +#include <hash.h> +#include <validation.h> +#include <net.h> +#include <policy/feerate.h> +#include <policy/policy.h> +#include <pow.h> +#include <primitives/transaction.h> +#include <script/standard.h> +#include <timedata.h> +#include <txmempool.h> +#include <util.h> +#include <utilmoneystr.h> +#include <validationinterface.h> #include <algorithm> #include <queue> diff --git a/src/miner.h b/src/miner.h index 683f4fe085..36276dc362 100644 --- a/src/miner.h +++ b/src/miner.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_MINER_H #define BITCOIN_MINER_H -#include "primitives/block.h" -#include "txmempool.h" +#include <primitives/block.h> +#include <txmempool.h> #include <stdint.h> #include <memory> @@ -158,7 +158,6 @@ public: struct Options { Options(); size_t nBlockMaxWeight; - size_t nBlockMaxSize; CFeeRate blockMinFeeRate; }; diff --git a/src/net.cpp b/src/net.cpp index 587c9e5110..7ac64ead90 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -4,23 +4,23 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "net.h" - -#include "addrman.h" -#include "chainparams.h" -#include "clientversion.h" -#include "consensus/consensus.h" -#include "crypto/common.h" -#include "crypto/sha256.h" -#include "hash.h" -#include "primitives/transaction.h" -#include "netbase.h" -#include "scheduler.h" -#include "ui_interface.h" -#include "utilstrencodings.h" +#include <net.h> + +#include <addrman.h> +#include <chainparams.h> +#include <clientversion.h> +#include <consensus/consensus.h> +#include <crypto/common.h> +#include <crypto/sha256.h> +#include <hash.h> +#include <primitives/transaction.h> +#include <netbase.h> +#include <scheduler.h> +#include <ui_interface.h> +#include <utilstrencodings.h> #ifdef WIN32 #include <string.h> @@ -110,13 +110,13 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) int nBestReachability = -1; { LOCK(cs_mapLocalHost); - for (std::map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + for (const auto& entry : mapLocalHost) { - int nScore = (*it).second.nScore; - int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); + int nScore = entry.second.nScore; + int nReachability = entry.first.GetReachabilityFrom(paddrPeer); if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { - addr = CService((*it).first, (*it).second.nPort); + addr = CService(entry.first, entry.second.nPort); nBestReachability = nReachability; nBestScore = nScore; } @@ -135,11 +135,10 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn const int64_t nOneWeek = 7*24*60*60; std::vector<CAddress> vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); - for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) - { + for (const auto& seed_in : vSeedsIn) { struct in6_addr ip; - memcpy(&ip, i->addr, sizeof(ip)); - CAddress addr(CService(ip, i->port), NODE_NETWORK); + memcpy(&ip, seed_in.addr, sizeof(ip)); + CAddress addr(CService(ip, seed_in.port), NODE_NETWORK); addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } @@ -299,18 +298,22 @@ bool IsReachable(const CNetAddr& addr) CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if ((CNetAddr)pnode->addr == ip) - return (pnode); + for (CNode* pnode : vNodes) { + if ((CNetAddr)pnode->addr == ip) { + return pnode; + } + } return nullptr; } CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (subNet.Match((CNetAddr)pnode->addr)) - return (pnode); + for (CNode* pnode : vNodes) { + if (subNet.Match((CNetAddr)pnode->addr)) { + return pnode; + } + } return nullptr; } @@ -319,7 +322,7 @@ CNode* CConnman::FindNode(const std::string& addrName) LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (pnode->GetAddrName() == addrName) { - return (pnode); + return pnode; } } return nullptr; @@ -328,9 +331,11 @@ CNode* CConnman::FindNode(const std::string& addrName) CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if ((CService)pnode->addr == addr) - return (pnode); + for (CNode* pnode : vNodes) { + if ((CService)pnode->addr == addr) { + return pnode; + } + } return nullptr; } @@ -380,19 +385,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); - // Connect - SOCKET hSocket; - bool proxyConnectionFailed = false; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : - ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) - { - if (!IsSelectableSocket(hSocket)) { - LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); - CloseSocket(hSocket); - return nullptr; - } - - if (pszDest && addrConnect.IsValid()) { + // Resolve + const int default_port = Params().GetDefaultPort(); + if (pszDest) { + std::vector<CService> resolved; + if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { + addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE); + if (!addrConnect.IsValid()) { + LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest); + return nullptr; + } // It is possible that we already have a connection to the IP/port pszDest resolved to. // In that case, drop the connection that was just created, and return the existing CNode instead. // Also store the name we used to connect in that CNode, so that future FindNode() calls to that @@ -402,27 +404,49 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); - CloseSocket(hSocket); LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } + } - addrman.Attempt(addrConnect, fCountFailure); + // Connect + bool connected = false; + SOCKET hSocket; + proxyType proxy; + if (addrConnect.IsValid()) { + bool proxyConnectionFailed = false; + + if (GetProxy(addrConnect.GetNetwork(), proxy)) + connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed); + else // no proxy needed (none set for target network) + connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); + if (!proxyConnectionFailed) { + // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to + // the proxy, mark this as an attempt. + addrman.Attempt(addrConnect, fCountFailure); + } + } else if (pszDest && GetNameProxy(proxy)) { + std::string host; + int port = default_port; + SplitHostPort(std::string(pszDest), port, host); + connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr); + } + if (connected) { + if (!IsSelectableSocket(hSocket)) { + LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); + CloseSocket(hSocket); + return nullptr; + } // Add node NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); CAddress addr_bind = GetBindAddress(hSocket); CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false); - pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices); pnode->AddRef(); return pnode; - } else if (!proxyConnectionFailed) { - // If connecting to the node failed, and failure is not caused by a problem connecting to - // the proxy, mark this as an attempt. - addrman.Attempt(addrConnect, fCountFailure); } return nullptr; @@ -474,10 +498,9 @@ void CConnman::ClearBanned() bool CConnman::IsBanned(CNetAddr ip) { LOCK(cs_setBanned); - for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) - { - CSubNet subNet = (*it).first; - CBanEntry banEntry = (*it).second; + for (const auto& it : setBanned) { + CSubNet subNet = it.first; + CBanEntry banEntry = it.second; if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) { return true; @@ -661,7 +684,7 @@ void CNode::copyStats(CNodeStats &stats) X(cleanSubVer); } X(fInbound); - X(fAddnode); + X(m_manual_connection); X(nStartingHeight); { LOCK(cs_vSend); @@ -938,6 +961,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction return a.nTimeConnected > b.nTimeConnected; } + +//! Sort an array by the specified comparator, then erase the last K elements. +template<typename T, typename Comparator> +static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k) +{ + std::sort(elements.begin(), elements.end(), comparator); + size_t eraseSize = std::min(k, elements.size()); + elements.erase(elements.end() - eraseSize, elements.end()); +} + /** Try to find a connection to evict when the node is full. * Extreme care must be taken to avoid opening the node to attacker * triggered network partitioning. @@ -952,7 +985,7 @@ bool CConnman::AttemptToEvictConnection() { LOCK(cs_vNodes); - for (CNode *node : vNodes) { + for (const CNode* node : vNodes) { if (node->fWhitelisted) continue; if (!node->fInbound) @@ -961,48 +994,29 @@ bool CConnman::AttemptToEvictConnection() continue; NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, - (node->nServices & nRelevantServices) == nRelevantServices, + HasAllDesirableServiceFlags(node->nServices), node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup}; vEvictionCandidates.push_back(candidate); } } - if (vEvictionCandidates.empty()) return false; - // Protect connections with certain characteristics // Deterministically select 4 peers to protect by netgroup. // An attacker cannot predict which netgroups will be protected - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); - - if (vEvictionCandidates.empty()) return false; - + EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); // Protect the 8 nodes with the lowest minimum ping time. // An attacker cannot manipulate this metric without physically moving nodes closer to the target. - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); - - if (vEvictionCandidates.empty()) return false; - + EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); // Protect 4 nodes that most recently sent us transactions. // An attacker cannot manipulate this metric without performing useful work. - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); - - if (vEvictionCandidates.empty()) return false; - + EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); // Protect 4 nodes that most recently sent us blocks. // An attacker cannot manipulate this metric without performing useful work. - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); - - if (vEvictionCandidates.empty()) return false; - + EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); // Protect the half of the remaining nodes which have been connected the longest. // This replicates the non-eviction implicit behavior, and precludes attacks that start later. - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); - vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end()); + EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2); if (vEvictionCandidates.empty()) return false; @@ -1013,12 +1027,12 @@ bool CConnman::AttemptToEvictConnection() int64_t nMostConnectionsTime = 0; std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes; for (const NodeEvictionCandidate &node : vEvictionCandidates) { - mapNetGroupNodes[node.nKeyedNetGroup].push_back(node); - int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected; - size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size(); + std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup]; + group.push_back(node); + int64_t grouptime = group[0].nTimeConnected; - if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { - nMostConnections = groupsize; + if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) { + nMostConnections = group.size(); nMostConnectionsTime = grouptime; naMostConnections = node.nKeyedNetGroup; } @@ -1030,9 +1044,9 @@ bool CConnman::AttemptToEvictConnection() // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); - for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { - if ((*it)->GetId() == evicted) { - (*it)->fDisconnect = true; + for (CNode* pnode : vNodes) { + if (pnode->GetId() == evicted) { + pnode->fDisconnect = true; return true; } } @@ -1056,9 +1070,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (pnode->fInbound) - nInbound++; + for (const CNode* pnode : vNodes) { + if (pnode->fInbound) nInbound++; + } } if (hSocket == INVALID_SOCKET) @@ -1520,22 +1534,20 @@ void ThreadMapPort() void MapPort(bool fUseUPnP) { - static boost::thread* upnp_thread = nullptr; + static std::unique_ptr<boost::thread> upnp_thread; if (fUseUPnP) { if (upnp_thread) { upnp_thread->interrupt(); upnp_thread->join(); - delete upnp_thread; } - upnp_thread = new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)); + upnp_thread.reset(new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); } else if (upnp_thread) { upnp_thread->interrupt(); upnp_thread->join(); - delete upnp_thread; - upnp_thread = nullptr; + upnp_thread.reset(); } } @@ -1578,7 +1590,7 @@ void CConnman::ThreadDNSAddressSeed() LOCK(cs_vNodes); int nRelevant = 0; for (auto pnode : vNodes) { - nRelevant += pnode->fSuccessfullyConnected && ((pnode->nServices & nRelevantServices) == nRelevantServices); + nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound; } if (nRelevant >= 2) { LogPrintf("P2P peers available. Skipped DNS seeding.\n"); @@ -1600,7 +1612,7 @@ void CConnman::ThreadDNSAddressSeed() } else { std::vector<CNetAddr> vIPs; std::vector<CAddress> vAdd; - ServiceFlags requiredServiceBits = nRelevantServices; + ServiceFlags requiredServiceBits = GetDesirableServiceFlags(NODE_NONE); std::string host = GetDNSHost(seed, &requiredServiceBits); CNetAddr resolveSource; if (!resolveSource.SetInternal(host)) { @@ -1670,6 +1682,37 @@ void CConnman::ProcessOneShot() } } +bool CConnman::GetTryNewOutboundPeer() +{ + return m_try_another_outbound_peer; +} + +void CConnman::SetTryNewOutboundPeer(bool flag) +{ + m_try_another_outbound_peer = flag; + LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false"); +} + +// Return the number of peers we have over our outbound connection limit +// Exclude peers that are marked for disconnect, or are going to be +// disconnected soon (eg one-shots and feelers) +// Also exclude peers that haven't finished initial connection handshake yet +// (so that we don't decide we're over our desired connection limit, and then +// evict some peer that has finished the handshake) +int CConnman::GetExtraOutboundCount() +{ + int nOutbound = 0; + { + LOCK(cs_vNodes); + for (CNode* pnode : vNodes) { + if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) { + ++nOutbound; + } + } + } + return std::max(nOutbound - nMaxOutbound, 0); +} + void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) { // Connect to specific addresses @@ -1681,7 +1724,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) for (const std::string& strAddr : connect) { CAddress addr(CService(), NODE_NONE); - OpenNetworkConnection(addr, false, nullptr, strAddr.c_str()); + OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), false, false, true); for (int i = 0; i < 10 && i < nLoop; i++) { if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) @@ -1729,17 +1772,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // Only connect out to one peer per network group (/16 for IPv4). // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. int nOutbound = 0; - int nOutboundRelevant = 0; std::set<std::vector<unsigned char> > setConnected; { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if (!pnode->fInbound && !pnode->fAddnode) { - - // Count the peers that have all relevant services - if (pnode->fSuccessfullyConnected && !pnode->fFeeler && ((pnode->nServices & nRelevantServices) == nRelevantServices)) { - nOutboundRelevant++; - } + if (!pnode->fInbound && !pnode->m_manual_connection) { // Netgroups for inbound and addnode peers are not excluded because our goal here // is to not use multiple of our limited outbound slots on a single netgroup // but inbound and addnode peers do not use our outbound slots. Inbound peers @@ -1764,7 +1801,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // * Only make a feeler connection once every few minutes. // bool fFeeler = false; - if (nOutbound >= nMaxOutbound) { + + if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) { int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds). if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); @@ -1794,21 +1832,16 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) if (IsLimited(addr)) continue; - // only connect to full nodes - if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) - continue; - // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) continue; - // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up. - ServiceFlags nRequiredServices = nRelevantServices; - if (nTries >= 40 && nOutbound < (nMaxOutbound >> 1)) { - nRequiredServices = REQUIRED_SERVICES; - } - - if ((addr.nServices & nRequiredServices) != nRequiredServices) { + // for non-feelers, require all the services we'll want, + // for feelers, only require they be a full node (only because most + // SPV clients don't have a good address DB available) + if (!fFeeler && !HasAllDesirableServiceFlags(addr.nServices)) { + continue; + } else if (fFeeler && !MayHaveUsefulAddressDB(addr.nServices)) { continue; } @@ -1817,13 +1850,6 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) continue; addrConnect = addr; - - // regardless of the services assumed to be available, only require the minimum if half or more outbound have relevant services - if (nOutboundRelevant >= (nMaxOutbound >> 1)) { - addrConnect.nServices = REQUIRED_SERVICES; - } else { - addrConnect.nServices = nRequiredServices; - } break; } @@ -1850,8 +1876,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); - for (const std::string& strAddNode : vAddedNodes) - lAddresses.push_back(strAddNode); + std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses)); } @@ -1897,11 +1922,6 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() void CConnman::ThreadOpenAddedConnections() { - { - LOCK(cs_vAddedNodes); - vAddedNodes = gArgs.GetArgs("-addnode"); - } - while (true) { CSemaphoreGrant grant(*semAddnode); @@ -1914,11 +1934,9 @@ void CConnman::ThreadOpenAddedConnections() // the addednodeinfo state might change. break; } - // If strAddedNode is an IP/port, decode it immediately, so - // OpenNetworkConnection can detect existing connections to that IP/port. tried = true; - CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort())); - OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true); + CAddress addr(CService(), NODE_NONE); + OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } @@ -1930,7 +1948,7 @@ void CConnman::ThreadOpenAddedConnections() } // if successful, this moves the passed grant to the constructed node -bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode) +bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection) { // // Initiate outbound network connection @@ -1959,8 +1977,8 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai pnode->fOneShot = true; if (fFeeler) pnode->fFeeler = true; - if (fAddnode) - pnode->fAddnode = true; + if (manual_connection) + pnode->m_manual_connection = true; m_msgproc->InitializeNode(pnode); { @@ -2204,9 +2222,8 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe nLastNodeId = 0; nSendBufferMaxSize = 0; nReceiveFloodSize = 0; - semOutbound = nullptr; - semAddnode = nullptr; flagInterruptMsgProc = false; + SetTryNewOutboundPeer(false); Options connOptions; Init(connOptions); @@ -2252,10 +2269,16 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) { Init(connOptions); - nTotalBytesRecv = 0; - nTotalBytesSent = 0; - nMaxOutboundTotalBytesSentInCycle = 0; - nMaxOutboundCycleStartTime = 0; + { + LOCK(cs_totalBytesRecv); + nTotalBytesRecv = 0; + } + { + LOCK(cs_totalBytesSent); + nTotalBytesSent = 0; + nMaxOutboundTotalBytesSentInCycle = 0; + nMaxOutboundCycleStartTime = 0; + } if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) { if (clientInterface) { @@ -2310,11 +2333,11 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) if (semOutbound == nullptr) { // initialize semaphore - semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); + semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); } if (semAddnode == nullptr) { // initialize semaphore - semAddnode = new CSemaphore(nMaxAddnode); + semAddnode = MakeUnique<CSemaphore>(nMaxAddnode); } // @@ -2437,10 +2460,8 @@ void CConnman::Stop() vNodes.clear(); vNodesDisconnected.clear(); vhListenSocket.clear(); - delete semOutbound; - semOutbound = nullptr; - delete semAddnode; - semAddnode = nullptr; + semOutbound.reset(); + semAddnode.reset(); } void CConnman::DeleteNode(CNode* pnode) @@ -2488,9 +2509,8 @@ std::vector<CAddress> CConnman::GetAddresses() bool CConnman::AddNode(const std::string& strNode) { LOCK(cs_vAddedNodes); - for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { - if (strNode == *it) - return false; + for (const std::string& it : vAddedNodes) { + if (strNode == it) return false; } vAddedNodes.push_back(strNode); @@ -2516,9 +2536,11 @@ size_t CConnman::GetNodeCount(NumConnections flags) return vNodes.size(); int nNum = 0; - for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it) - if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) + for (const auto& pnode : vNodes) { + if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) { nNum++; + } + } return nNum; } @@ -2528,8 +2550,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) vstats.clear(); LOCK(cs_vNodes); vstats.reserve(vNodes.size()); - for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) { - CNode* pnode = *it; + for (CNode* pnode : vNodes) { vstats.emplace_back(); pnode->copyStats(vstats.back()); } @@ -2696,7 +2717,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn nSendVersion(0) { nServices = NODE_NONE; - nServicesExpected = NODE_NONE; hSocket = hSocketIn; nRecvVersion = INIT_PROTO_VERSION; nLastSend = 0; @@ -2709,7 +2729,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn strSubVer = ""; fWhitelisted = false; fOneShot = false; - fAddnode = false; + m_manual_connection = false; fClient = false; // set by version message fFeeler = false; fSuccessfullyConnected = false; @@ -2727,7 +2747,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn nNextInvSend = 0; fRelayTxes = false; fSentAddr = false; - pfilter = new CBloomFilter(); + pfilter = MakeUnique<CBloomFilter>(); timeLastMempoolReq = 0; nLastBlockTime = 0; nLastTXTime = 0; @@ -2757,9 +2777,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn CNode::~CNode() { CloseSocket(hSocket); - - if (pfilter) - delete pfilter; } void CNode::AskFor(const CInv& inv) @@ -6,21 +6,21 @@ #ifndef BITCOIN_NET_H #define BITCOIN_NET_H -#include "addrdb.h" -#include "addrman.h" -#include "amount.h" -#include "bloom.h" -#include "compat.h" -#include "hash.h" -#include "limitedmap.h" -#include "netaddress.h" -#include "policy/feerate.h" -#include "protocol.h" -#include "random.h" -#include "streams.h" -#include "sync.h" -#include "uint256.h" -#include "threadinterrupt.h" +#include <addrdb.h> +#include <addrman.h> +#include <amount.h> +#include <bloom.h> +#include <compat.h> +#include <hash.h> +#include <limitedmap.h> +#include <netaddress.h> +#include <policy/feerate.h> +#include <protocol.h> +#include <random.h> +#include <streams.h> +#include <sync.h> +#include <uint256.h> +#include <threadinterrupt.h> #include <atomic> #include <deque> @@ -84,8 +84,6 @@ static const bool DEFAULT_FORCEDNSSEED = false; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; -static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK; - // NOTE: When adjusting this, update rpcnet:setban's help ("24h") static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban @@ -130,7 +128,6 @@ public: struct Options { ServiceFlags nLocalServices = NODE_NONE; - ServiceFlags nRelevantServices = NODE_NONE; int nMaxConnections = 0; int nMaxOutbound = 0; int nMaxAddnode = 0; @@ -147,11 +144,11 @@ public: std::vector<CService> vBinds, vWhiteBinds; bool m_use_addrman_outgoing = true; std::vector<std::string> m_specified_outgoing; + std::vector<std::string> m_added_nodes; }; void Init(const Options& connOptions) { nLocalServices = connOptions.nLocalServices; - nRelevantServices = connOptions.nRelevantServices; nMaxConnections = connOptions.nMaxConnections; nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections); nMaxAddnode = connOptions.nMaxAddnode; @@ -161,9 +158,16 @@ public: m_msgproc = connOptions.m_msgproc; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; nReceiveFloodSize = connOptions.nReceiveFloodSize; - nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; - nMaxOutboundLimit = connOptions.nMaxOutboundLimit; + { + LOCK(cs_totalBytesSent); + nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; + nMaxOutboundLimit = connOptions.nMaxOutboundLimit; + } vWhitelistedRange = connOptions.vWhitelistedRange; + { + LOCK(cs_vAddedNodes); + vAddedNodes = connOptions.m_added_nodes; + } } CConnman(uint64_t seed0, uint64_t seed1); @@ -173,7 +177,7 @@ public: void Interrupt(); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); - bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false); + bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func); @@ -253,6 +257,19 @@ public: void GetBanned(banmap_t &banmap); void SetBanned(const banmap_t &banmap); + // This allows temporarily exceeding nMaxOutbound, with the goal of finding + // a peer that is better than all our current peers. + void SetTryNewOutboundPeer(bool flag); + bool GetTryNewOutboundPeer(); + + // Return the number of outbound peers we have in excess of our target (eg, + // if we previously called SetTryNewOutboundPeer(true), and have since set + // to false, we may have extra peers that we wish to disconnect). This may + // return a value less than (num_outbound_connections - num_outbound_slots) + // in cases where some outbound connections are not yet fully connected, or + // not yet fully disconnected. + int GetExtraOutboundCount(); + bool AddNode(const std::string& node); bool RemoveAddedNode(const std::string& node); std::vector<AddedNodeInfo> GetAddedNodeInfo(); @@ -353,14 +370,14 @@ private: // Network usage totals CCriticalSection cs_totalBytesRecv; CCriticalSection cs_totalBytesSent; - uint64_t nTotalBytesRecv; - uint64_t nTotalBytesSent; + uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv); + uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent); // outbound limit & stats - uint64_t nMaxOutboundTotalBytesSentInCycle; - uint64_t nMaxOutboundCycleStartTime; - uint64_t nMaxOutboundLimit; - uint64_t nMaxOutboundTimeframe; + uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); + uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent); // Whitelisted ranges. Any node connecting from these is automatically // whitelisted (as well as those connecting to whitelisted binds). @@ -378,7 +395,7 @@ private: CAddrMan addrman; std::deque<std::string> vOneShots; CCriticalSection cs_vOneShots; - std::vector<std::string> vAddedNodes; + std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes); CCriticalSection cs_vAddedNodes; std::vector<CNode*> vNodes; std::list<CNode*> vNodesDisconnected; @@ -388,11 +405,8 @@ private: /** Services this instance offers */ ServiceFlags nLocalServices; - /** Services this instance cares about */ - ServiceFlags nRelevantServices; - - CSemaphore *semOutbound; - CSemaphore *semAddnode; + std::unique_ptr<CSemaphore> semOutbound; + std::unique_ptr<CSemaphore> semAddnode; int nMaxConnections; int nMaxOutbound; int nMaxAddnode; @@ -418,6 +432,13 @@ private: std::thread threadOpenAddedConnections; std::thread threadOpenConnections; std::thread threadMessageHandler; + + /** flag for deciding to connect to an extra outbound peer, + * in excess of nMaxOutbound + * This takes the place of a feeler connection */ + std::atomic_bool m_try_another_outbound_peer; + + friend struct CConnmanTest; }; extern std::unique_ptr<CConnman> g_connman; void Discover(boost::thread_group& threadGroup); @@ -511,7 +532,7 @@ public: int nVersion; std::string cleanSubVer; bool fInbound; - bool fAddnode; + bool m_manual_connection; int nStartingHeight; uint64_t nSendBytes; mapMsgCmdSize mapSendBytesPerMsgCmd; @@ -583,7 +604,6 @@ class CNode public: // socket std::atomic<ServiceFlags> nServices; - ServiceFlags nServicesExpected; SOCKET hSocket; size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent @@ -621,7 +641,7 @@ public: bool fWhitelisted; // This peer can bypass DoS banning. bool fFeeler; // If true this node is being used as a short lived feeler. bool fOneShot; - bool fAddnode; + bool m_manual_connection; bool fClient; const bool fInbound; std::atomic_bool fSuccessfullyConnected; @@ -634,7 +654,7 @@ public: bool fSentAddr; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; - CBloomFilter* pfilter; + std::unique_ptr<CBloomFilter> pfilter; std::atomic<int> nRefCount; const uint64_t nKeyedNetGroup; @@ -702,13 +722,11 @@ public: CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); + CNode(const CNode&) = delete; + CNode& operator=(const CNode&) = delete; private: - CNode(const CNode&); - void operator=(const CNode&); const NodeId id; - - const uint64_t nLocalHostNonce; // Services offered to this peer const ServiceFlags nLocalServices; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index b8900d9888..442cd00c9b 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3,33 +3,34 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "net_processing.h" - -#include "addrman.h" -#include "arith_uint256.h" -#include "blockencodings.h" -#include "chainparams.h" -#include "consensus/validation.h" -#include "hash.h" -#include "init.h" -#include "validation.h" -#include "merkleblock.h" -#include "net.h" -#include "netmessagemaker.h" -#include "netbase.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "primitives/block.h" -#include "primitives/transaction.h" -#include "random.h" -#include "reverse_iterator.h" -#include "tinyformat.h" -#include "txmempool.h" -#include "ui_interface.h" -#include "util.h" -#include "utilmoneystr.h" -#include "utilstrencodings.h" -#include "validationinterface.h" +#include <net_processing.h> + +#include <addrman.h> +#include <arith_uint256.h> +#include <blockencodings.h> +#include <chainparams.h> +#include <consensus/validation.h> +#include <hash.h> +#include <init.h> +#include <validation.h> +#include <merkleblock.h> +#include <net.h> +#include <netmessagemaker.h> +#include <netbase.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <random.h> +#include <reverse_iterator.h> +#include <scheduler.h> +#include <tinyformat.h> +#include <txmempool.h> +#include <ui_interface.h> +#include <util.h> +#include <utilmoneystr.h> +#include <utilstrencodings.h> +#include <validationinterface.h> #if defined(NDEBUG) # error "Bitcoin cannot be compiled without assertions." @@ -61,6 +62,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] +/// Age after which a stale block will no longer be served if requested as +/// protection against fingerprinting. Set to one month, denominated in seconds. +static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; + +/// Age after which a block is considered historical for purposes of rate +/// limiting block relay. Set to one week, denominated in seconds. +static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; + // Internal stuff namespace { /** Number of nodes with fSyncStarted. */ @@ -116,6 +125,12 @@ namespace { /** Number of peers from which we're downloading blocks. */ int nPeersWithValidatedDownloads = 0; + /** Number of outbound peers with m_chain_sync.m_protect. */ + int g_outbound_peers_with_protect_from_disconnect = 0; + + /** When our tip was last updated. */ + int64_t g_last_tip_update = 0; + /** Relay map, protected by cs_main. */ typedef std::map<uint256, CTransactionRef> MapRelay; MapRelay mapRelay; @@ -193,6 +208,36 @@ struct CNodeState { */ bool fSupportsDesiredCmpctVersion; + /** State used to enforce CHAIN_SYNC_TIMEOUT + * Only in effect for outbound, non-manual connections, with + * m_protect == false + * Algorithm: if a peer's best known block has less work than our tip, + * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future: + * - If at timeout their best known block now has more work than our tip + * when the timeout was set, then either reset the timeout or clear it + * (after comparing against our current tip's work) + * - If at timeout their best known block still has less work than our + * tip did when the timeout was set, then send a getheaders message, + * and set a shorter timeout, HEADERS_RESPONSE_TIME seconds in future. + * If their best known block is still behind when that new timeout is + * reached, disconnect. + */ + struct ChainSyncTimeoutState { + //! A timeout used for checking whether our peer has sufficiently synced + int64_t m_timeout; + //! A header with the work we require on our peer's chain + const CBlockIndex * m_work_header; + //! After timeout is reached, set to true after sending getheaders + bool m_sent_getheaders; + //! Whether this peer is protected from disconnection due to a bad/slow chain + bool m_protect; + }; + + ChainSyncTimeoutState m_chain_sync; + + //! Time of last new block announcement + int64_t m_last_block_announcement; + CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) { fCurrentlyConnected = false; nMisbehavior = 0; @@ -215,6 +260,8 @@ struct CNodeState { fHaveWitness = false; fWantsCmpctWitness = false; fSupportsDesiredCmpctVersion = false; + m_chain_sync = { 0, nullptr, false, false }; + m_last_block_announcement = 0; } }; @@ -371,25 +418,32 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) { } } connman->ForNode(nodeid, [connman](CNode* pfrom){ - bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { // As per BIP152, we only get 3 of our peers to announce // blocks using compact encodings. - connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ - connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, nCMPCTBLOCKVersion](CNode* pnodeStop){ + connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion)); return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } - fAnnounceUsingCMPCTBLOCK = true; - connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); } } +bool TipMayBeStale(const Consensus::Params &consensusParams) +{ + AssertLockHeld(cs_main); + if (g_last_tip_update == 0) { + g_last_tip_update = GetTime(); + } + return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty(); +} + // Requires cs_main bool CanDirectFetch(const Consensus::Params &consensusParams) { @@ -496,6 +550,22 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con } // namespace +// This function is used for testing the stale tip eviction logic, see +// DoS_tests.cpp +void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) +{ + LOCK(cs_main); + CNodeState *state = State(node); + if (state) state->m_last_block_announcement = time_in_seconds; +} + +// Returns true for outbound peers, excluding manual connections, feelers, and +// one-shots +bool IsOutboundDisconnectionCandidate(const CNode *node) +{ + return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot); +} + void PeerLogicValidation::InitializeNode(CNode *pnode) { CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); @@ -528,6 +598,8 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim nPreferredDownload -= state->fPreferredDownload; nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); assert(nPeersWithValidatedDownloads >= 0); + g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect; + assert(g_outbound_peers_with_protect_from_disconnect >= 0); mapNodeState.erase(nodeid); @@ -536,6 +608,7 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim assert(mapBlocksInFlight.empty()); assert(nPreferredDownload == 0); assert(nPeersWithValidatedDownloads == 0); + assert(g_outbound_peers_with_protect_from_disconnect == 0); } LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); } @@ -560,7 +633,7 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { // mapOrphanTransactions // -void AddToCompactExtraTransactions(const CTransactionRef& tx) +void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); if (max_extra_txn <= 0) @@ -706,9 +779,30 @@ void Misbehaving(NodeId pnode, int howmuch) // blockchain -> download logic notification // -PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) { +// To prevent fingerprinting attacks, only send blocks/headers outside of the +// active chain if they are no more than a month older (both in time, and in +// best equivalent proof of work) than the best header chain we know about and +// we fully-validated them at some point. +static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + AssertLockHeld(cs_main); + if (chainActive.Contains(pindex)) return true; + return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && + (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); +} + +PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) { // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); + + const Consensus::Params& consensusParams = Params().GetConsensus(); + // Stale tip checking and peer eviction are on two different timers, but we + // don't want them to get out of sync due to drift in the scheduler, so we + // combine them in one function and schedule at the quicker (peer-eviction) + // timer. + static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer"); + scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000); } void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) { @@ -739,6 +833,8 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb } LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased); } + + g_last_tip_update = GetTime(); } // All of the following cache a recent block, and are protected by cs_most_recent_block @@ -980,25 +1076,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam CValidationState dummy; ActivateBestChain(dummy, Params(), a_recent_block); } - if (chainActive.Contains(mi->second)) { - send = true; - } else { - static const int nOneMonth = 30 * 24 * 60 * 60; - // To prevent fingerprinting attacks, only send blocks outside of the active - // chain if they are valid, and no more than a month older (both in time, and in - // best equivalent proof of work) than the best header chain we know about. - send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth); - if (!send) { - LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); - } + send = BlockRequestAllowed(mi->second, consensusParams); + if (!send) { + LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } } // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes - static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical - if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -1153,6 +1238,225 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } +bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool punish_duplicate_invalid) +{ + const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + size_t nCount = headers.size(); + + if (nCount == 0) { + // Nothing interesting. Stop asking this peers for more headers. + return true; + } + + bool received_new_header = false; + const CBlockIndex *pindexLast = nullptr; + { + LOCK(cs_main); + CNodeState *nodestate = State(pfrom->GetId()); + + // If this looks like it could be a block announcement (nCount < + // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that + // don't connect: + // - Send a getheaders message in response to try to connect the chain. + // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that + // don't connect before giving DoS points + // - Once a headers message is received that is valid and does connect, + // nUnconnectingHeaders gets reset back to 0. + if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + nodestate->nUnconnectingHeaders++; + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", + headers[0].GetHash().ToString(), + headers[0].hashPrevBlock.ToString(), + pindexBestHeader->nHeight, + pfrom->GetId(), nodestate->nUnconnectingHeaders); + // Set hashLastUnknownBlock for this peer, so that if we + // eventually get the headers - even from a different peer - + // we can use this peer to download. + UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); + + if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) { + Misbehaving(pfrom->GetId(), 20); + } + return true; + } + + uint256 hashLastBlock; + for (const CBlockHeader& header : headers) { + if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) { + Misbehaving(pfrom->GetId(), 20); + return error("non-continuous headers sequence"); + } + hashLastBlock = header.GetHash(); + } + + // If we don't have the last header, then they'll have given us + // something new (if these headers are valid). + if (mapBlockIndex.find(hashLastBlock) == mapBlockIndex.end()) { + received_new_header = true; + } + } + + CValidationState state; + CBlockHeader first_invalid_header; + if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) { + int nDoS; + if (state.IsInvalid(nDoS)) { + LOCK(cs_main); + if (nDoS > 0) { + Misbehaving(pfrom->GetId(), nDoS); + } + if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) { + // Goal: don't allow outbound peers to use up our outbound + // connection slots if they are on incompatible chains. + // + // We ask the caller to set punish_invalid appropriately based + // on the peer and the method of header delivery (compact + // blocks are allowed to be invalid in some circumstances, + // under BIP 152). + // Here, we try to detect the narrow situation that we have a + // valid block header (ie it was valid at the time the header + // was received, and hence stored in mapBlockIndex) but know the + // block is invalid, and that a peer has announced that same + // block as being on its active chain. + // Disconnect the peer in such a situation. + // + // Note: if the header that is invalid was not accepted to our + // mapBlockIndex at all, that may also be grounds for + // disconnecting the peer, as the chain they are on is likely + // to be incompatible. However, there is a circumstance where + // that does not hold: if the header's timestamp is more than + // 2 hours ahead of our current time. In that case, the header + // may become valid in the future, and we don't want to + // disconnect a peer merely for serving us one too-far-ahead + // block header, to prevent an attacker from splitting the + // network by mining a block right at the 2 hour boundary. + // + // TODO: update the DoS logic (or, rather, rewrite the + // DoS-interface between validation and net_processing) so that + // the interface is cleaner, and so that we disconnect on all the + // reasons that a peer's headers chain is incompatible + // with ours (eg block->nVersion softforks, MTP violations, + // etc), and not just the duplicate-invalid case. + pfrom->fDisconnect = true; + } + return error("invalid header received"); + } + } + + { + LOCK(cs_main); + CNodeState *nodestate = State(pfrom->GetId()); + if (nodestate->nUnconnectingHeaders > 0) { + LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders); + } + nodestate->nUnconnectingHeaders = 0; + + assert(pindexLast); + UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); + + // From here, pindexBestKnownBlock should be guaranteed to be non-null, + // because it is set in UpdateBlockAvailability. Some nullptr checks + // are still present, however, as belt-and-suspenders. + + if (received_new_header && pindexLast->nChainWork > chainActive.Tip()->nChainWork) { + nodestate->m_last_block_announcement = GetTime(); + } + + if (nCount == MAX_HEADERS_RESULTS) { + // Headers message had its maximum size; the peer may have more headers. + // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue + // from there instead. + LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); + } + + bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); + // If this set of headers is valid and ends in a block with at least as + // much work as our tip, download as much as possible. + if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { + std::vector<const CBlockIndex*> vToFetch; + const CBlockIndex *pindexWalk = pindexLast; + // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. + while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && + !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && + (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { + // We don't have this block, and it's not yet in flight. + vToFetch.push_back(pindexWalk); + } + pindexWalk = pindexWalk->pprev; + } + // If pindexWalk still isn't on our main chain, we're looking at a + // very large reorg at a time we think we're close to caught up to + // the main chain -- this shouldn't really happen. Bail out on the + // direct fetch and rely on parallel download instead. + if (!chainActive.Contains(pindexWalk)) { + LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", + pindexLast->GetBlockHash().ToString(), + pindexLast->nHeight); + } else { + std::vector<CInv> vGetData; + // Download as much as possible, from earliest to latest. + for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { + if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + // Can't download any more from this peer + break; + } + uint32_t nFetchFlags = GetFetchFlags(pfrom); + vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); + MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex); + LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", + pindex->GetBlockHash().ToString(), pfrom->GetId()); + } + if (vGetData.size() > 1) { + LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n", + pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); + } + if (vGetData.size() > 0) { + if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { + // In any case, we want to download using a compact block, not a regular one + vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); + } + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + } + } + } + // If we're in IBD, we want outbound peers that will serve us a useful + // chain. Disconnect peers that are on chains with insufficient work. + if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) { + // When nCount < MAX_HEADERS_RESULTS, we know we have no more + // headers to fetch from this peer. + if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { + // This peer has too little work on their headers chain to help + // us sync -- disconnect if using an outbound slot (unless + // whitelisted or addnode). + // Note: We compare their tip to nMinimumChainWork (rather than + // chainActive.Tip()) because we won't start block download + // until we have a headers chain that has at least + // nMinimumChainWork, even if a peer has a chain past our tip, + // as an anti-DoS measure. + if (IsOutboundDisconnectionCandidate(pfrom)) { + LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId()); + pfrom->fDisconnect = true; + } + } + } + + if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) { + // If this is an outbound peer, check to see if we should protect + // it from the bad/lagging chain logic. + if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) { + LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId()); + nodestate->m_chain_sync.m_protect = true; + ++g_outbound_peers_with_protect_from_disconnect; + } + } + } + + return true; +} + bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); @@ -1232,11 +1536,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { connman->SetServices(pfrom->addr, nServices); } - if (pfrom->nServicesExpected & ~nServices) + if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices)) { - LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, pfrom->nServicesExpected); + LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, - strprintf("Expected to offer services %08x", pfrom->nServicesExpected))); + strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices)))); pfrom->fDisconnect = true; return false; } @@ -1455,7 +1759,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (interruptMsgProc) return true; - if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) + // We only bother storing full nodes, though this may include + // things which we would not make an outbound connection to, in + // part because we may make feeler connections to them. + if (!MayHaveUsefulAddressDB(addr.nServices)) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) @@ -1723,6 +2030,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (mi == mapBlockIndex.end()) return true; pindex = (*mi).second; + + if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) { + LogPrintf("%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId()); + return true; + } } else { @@ -1788,8 +2100,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr std::list<CTransactionRef> lRemovedTxn; - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, &lRemovedTxn)) { - mempool.check(pcoinsTip); + if (!AlreadyHave(inv) && + AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { + mempool.check(pcoinsTip.get()); RelayTransaction(tx, connman); for (unsigned int i = 0; i < tx.vout.size(); i++) { vWorkQueue.emplace_back(inv.hash, i); @@ -1826,7 +2139,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2, &lRemovedTxn)) { + if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx, connman); for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { @@ -1856,7 +2169,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr recentRejects->insert(orphanHash); } } - mempool.check(pcoinsTip); + mempool.check(pcoinsTip.get()); } } @@ -1950,6 +2263,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr CBlockHeaderAndShortTxIDs cmpctblock; vRecv >> cmpctblock; + bool received_new_header = false; + { LOCK(cs_main); @@ -1959,6 +2274,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); return true; } + + if (mapBlockIndex.find(cmpctblock.header.GetHash()) == mapBlockIndex.end()) { + received_new_header = true; + } } const CBlockIndex *pindex = nullptr; @@ -1985,7 +2304,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // If we end up treating this as a plain headers message, call that as well // without cs_main. bool fRevertToHeaderProcessing = false; - CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION); // Keep a CBlock for "optimistic" compactblock reconstructions (see // below) @@ -1998,6 +2316,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr assert(pindex); UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); + CNodeState *nodestate = State(pfrom->GetId()); + + // If this was a new header with more work than our tip, update the + // peer's last block announcement time + if (received_new_header && pindex->nChainWork > chainActive.Tip()->nChainWork) { + nodestate->m_last_block_announcement = GetTime(); + } + std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator blockInFlightIt = mapBlocksInFlight.find(pindex->GetBlockHash()); bool fAlreadyInFlight = blockInFlightIt != mapBlocksInFlight.end(); @@ -2020,8 +2346,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus())) return true; - CNodeState *nodestate = State(pfrom->GetId()); - if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) { // Don't bother trying to process compact blocks from v1 peers // after segwit activates. @@ -2102,10 +2426,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } else { // If this was an announce-cmpctblock, we want the same treatment as a header message - // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions) - std::vector<CBlock> headers; - headers.push_back(cmpctblock.header); - vHeadersMsg << headers; fRevertToHeaderProcessing = true; } } @@ -2114,8 +2434,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (fProcessBLOCKTXN) return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc); - if (fRevertToHeaderProcessing) - return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman, interruptMsgProc); + if (fRevertToHeaderProcessing) { + // Headers received from HB compact block peers are permitted to be + // relayed before full validation (see BIP 152), so we don't want to disconnect + // the peer if the header turns out to be for an invalid block. + // Note that if a peer tries to build on an invalid chain, that + // will be detected and the peer will be banned. + return ProcessHeadersMessage(pfrom, connman, {cmpctblock.header}, chainparams, /*punish_duplicate_invalid=*/false); + } if (fBlockReconstructed) { // If we got here, we were able to optimistically reconstruct a @@ -2125,7 +2451,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom->GetId(), false)); } bool fNewBlock = false; - ProcessNewBlock(chainparams, pblock, true, &fNewBlock); + // Setting fForceProcessing to true means that we bypass some of + // our anti-DoS protections in AcceptBlock, which filters + // unrequested blocks that might be trying to waste our resources + // (eg disk space). Because we only try to reconstruct blocks when + // we're close to caught up (via the CanDirectFetch() requirement + // above, combined with the behavior of not requesting blocks until + // we have a chain with at least nMinimumChainWork), and we ignore + // compact blocks with less work than our tip, it is safe to treat + // reconstructed compact blocks as having been requested. + ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { @@ -2205,7 +2540,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fNewBlock = false; // Since we requested this block (it was in mapBlocksInFlight), force it to be processed, // even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc) - ProcessNewBlock(chainparams, pblock, true, &fNewBlock); + // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent + // disk-space attacks), but this should be safe due to the + // protections in the compact block handler -- see related comment + // in compact block optimistic reconstruction handling. + ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom->nLastBlockTime = GetTime(); } else { @@ -2233,136 +2572,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - if (nCount == 0) { - // Nothing interesting. Stop asking this peers for more headers. - return true; - } - - const CBlockIndex *pindexLast = nullptr; - { - LOCK(cs_main); - CNodeState *nodestate = State(pfrom->GetId()); - - // If this looks like it could be a block announcement (nCount < - // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that - // don't connect: - // - Send a getheaders message in response to try to connect the chain. - // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that - // don't connect before giving DoS points - // - Once a headers message is received that is valid and does connect, - // nUnconnectingHeaders gets reset back to 0. - if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { - nodestate->nUnconnectingHeaders++; - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); - LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", - headers[0].GetHash().ToString(), - headers[0].hashPrevBlock.ToString(), - pindexBestHeader->nHeight, - pfrom->GetId(), nodestate->nUnconnectingHeaders); - // Set hashLastUnknownBlock for this peer, so that if we - // eventually get the headers - even from a different peer - - // we can use this peer to download. - UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); - - if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) { - Misbehaving(pfrom->GetId(), 20); - } - return true; - } - - uint256 hashLastBlock; - for (const CBlockHeader& header : headers) { - if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) { - Misbehaving(pfrom->GetId(), 20); - return error("non-continuous headers sequence"); - } - hashLastBlock = header.GetHash(); - } - } - - CValidationState state; - if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) { - int nDoS; - if (state.IsInvalid(nDoS)) { - if (nDoS > 0) { - LOCK(cs_main); - Misbehaving(pfrom->GetId(), nDoS); - } - return error("invalid header received"); - } - } - - { - LOCK(cs_main); - CNodeState *nodestate = State(pfrom->GetId()); - if (nodestate->nUnconnectingHeaders > 0) { - LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders); - } - nodestate->nUnconnectingHeaders = 0; - - assert(pindexLast); - UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - - if (nCount == MAX_HEADERS_RESULTS) { - // Headers message had its maximum size; the peer may have more headers. - // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue - // from there instead. - LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); - } - - bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); - // If this set of headers is valid and ends in a block with at least as - // much work as our tip, download as much as possible. - if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { - std::vector<const CBlockIndex*> vToFetch; - const CBlockIndex *pindexWalk = pindexLast; - // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. - while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && - !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && - (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { - // We don't have this block, and it's not yet in flight. - vToFetch.push_back(pindexWalk); - } - pindexWalk = pindexWalk->pprev; - } - // If pindexWalk still isn't on our main chain, we're looking at a - // very large reorg at a time we think we're close to caught up to - // the main chain -- this shouldn't really happen. Bail out on the - // direct fetch and rely on parallel download instead. - if (!chainActive.Contains(pindexWalk)) { - LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", - pindexLast->GetBlockHash().ToString(), - pindexLast->nHeight); - } else { - std::vector<CInv> vGetData; - // Download as much as possible, from earliest to latest. - for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { - if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - // Can't download any more from this peer - break; - } - uint32_t nFetchFlags = GetFetchFlags(pfrom); - vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); - MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex); - LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", - pindex->GetBlockHash().ToString(), pfrom->GetId()); - } - if (vGetData.size() > 1) { - LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n", - pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); - } - if (vGetData.size() > 0) { - if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) { - // In any case, we want to download using a compact block, not a regular one - vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); - } - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); - } - } - } - } + // Headers received via a HEADERS message should be valid, and reflect + // the chain the peer is on. If we receive a known-invalid header, + // disconnect the peer if it is using one of our outbound connection + // slots. + bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection; + return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish); } else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing @@ -2372,11 +2587,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock->GetHash().ToString(), pfrom->GetId()); - // Process all blocks from whitelisted peers, even if not requested, - // unless we're still syncing with the network. - // Such an unrequested block may still be processed, subject to the - // conditions in AcceptBlock(). - bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); + bool forceProcessing = false; const uint256 hash(pblock->GetHash()); { LOCK(cs_main); @@ -2540,8 +2751,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr else { LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(filter); + pfrom->pfilter.reset(new CBloomFilter(filter)); pfrom->pfilter->UpdateEmptyFull(); pfrom->fRelayTxes = true; } @@ -2577,8 +2787,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { LOCK(pfrom->cs_filter); if (pfrom->GetLocalServices() & NODE_BLOOM) { - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(); + pfrom->pfilter.reset(new CBloomFilter()); } pfrom->fRelayTxes = true; } @@ -2624,8 +2833,8 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) state.fShouldBan = false; if (pnode->fWhitelisted) LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString()); - else if (pnode->fAddnode) - LogPrintf("Warning: not punishing addnoded peer %s!\n", pnode->addr.ToString()); + else if (pnode->m_manual_connection) + LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString()); else { pnode->fDisconnect = true; if (pnode->addr.IsLocal()) @@ -2760,6 +2969,136 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter return fMoreWork; } +void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds) +{ + AssertLockHeld(cs_main); + + CNodeState &state = *State(pto->GetId()); + const CNetMsgMaker msgMaker(pto->GetSendVersion()); + + if (!state.m_chain_sync.m_protect && IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) { + // This is an outbound peer subject to disconnection if they don't + // announce a block with as much work as the current tip within + // CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if + // their chain has more work than ours, we should sync to it, + // unless it's invalid, in which case we should find that out and + // disconnect from them elsewhere). + if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork) { + if (state.m_chain_sync.m_timeout != 0) { + state.m_chain_sync.m_timeout = 0; + state.m_chain_sync.m_work_header = nullptr; + state.m_chain_sync.m_sent_getheaders = false; + } + } else if (state.m_chain_sync.m_timeout == 0 || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainWork >= state.m_chain_sync.m_work_header->nChainWork)) { + // Our best block known by this peer is behind our tip, and we're either noticing + // that for the first time, OR this peer was able to catch up to some earlier point + // where we checked against our tip. + // Either way, set a new timeout based on current tip. + state.m_chain_sync.m_timeout = time_in_seconds + CHAIN_SYNC_TIMEOUT; + state.m_chain_sync.m_work_header = chainActive.Tip(); + state.m_chain_sync.m_sent_getheaders = false; + } else if (state.m_chain_sync.m_timeout > 0 && time_in_seconds > state.m_chain_sync.m_timeout) { + // No evidence yet that our peer has synced to a chain with work equal to that + // of our tip, when we first detected it was behind. Send a single getheaders + // message to give the peer a chance to update us. + if (state.m_chain_sync.m_sent_getheaders) { + // They've run out of time to catch up! + LogPrintf("Disconnecting outbound peer %d for old chain, best known block = %s\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>"); + pto->fDisconnect = true; + } else { + assert(state.m_chain_sync.m_work_header); + LogPrint(BCLog::NET, "sending getheaders to outbound peer=%d to verify chain work (current best known block:%s, benchmark blockhash: %s)\n", pto->GetId(), state.pindexBestKnownBlock != nullptr ? state.pindexBestKnownBlock->GetBlockHash().ToString() : "<none>", state.m_chain_sync.m_work_header->GetBlockHash().ToString()); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(state.m_chain_sync.m_work_header->pprev), uint256())); + state.m_chain_sync.m_sent_getheaders = true; + constexpr int64_t HEADERS_RESPONSE_TIME = 120; // 2 minutes + // Bump the timeout to allow a response, which could clear the timeout + // (if the response shows the peer has synced), reset the timeout (if + // the peer syncs to the required work but not to our tip), or result + // in disconnect (if we advance to the timeout and pindexBestKnownBlock + // has not sufficiently progressed) + state.m_chain_sync.m_timeout = time_in_seconds + HEADERS_RESPONSE_TIME; + } + } + } +} + +void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) +{ + // Check whether we have too many outbound peers + int extra_peers = connman->GetExtraOutboundCount(); + if (extra_peers > 0) { + // If we have more outbound peers than we target, disconnect one. + // Pick the outbound peer that least recently announced + // us a new block, with ties broken by choosing the more recent + // connection (higher node id) + NodeId worst_peer = -1; + int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); + + LOCK(cs_main); + + connman->ForEachNode([&](CNode* pnode) { + // Ignore non-outbound peers, or nodes marked for disconnect already + if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return; + CNodeState *state = State(pnode->GetId()); + if (state == nullptr) return; // shouldn't be possible, but just in case + // Don't evict our protected peers + if (state->m_chain_sync.m_protect) return; + if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) { + worst_peer = pnode->GetId(); + oldest_block_announcement = state->m_last_block_announcement; + } + }); + if (worst_peer != -1) { + bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) { + // Only disconnect a peer that has been connected to us for + // some reasonable fraction of our check-frequency, to give + // it time for new information to have arrived. + // Also don't disconnect any peer we're trying to download a + // block from. + CNodeState &state = *State(pnode->GetId()); + if (time_in_seconds - pnode->nTimeConnected > MINIMUM_CONNECT_TIME && state.nBlocksInFlight == 0) { + LogPrint(BCLog::NET, "disconnecting extra outbound peer=%d (last block announcement received at time %d)\n", pnode->GetId(), oldest_block_announcement); + pnode->fDisconnect = true; + return true; + } else { + LogPrint(BCLog::NET, "keeping outbound peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n", pnode->GetId(), pnode->nTimeConnected, state.nBlocksInFlight); + return false; + } + }); + if (disconnected) { + // If we disconnected an extra peer, that means we successfully + // connected to at least one peer after the last time we + // detected a stale tip. Don't try any more extra peers until + // we next detect a stale tip, to limit the load we put on the + // network from these extra connections. + connman->SetTryNewOutboundPeer(false); + } + } + } +} + +void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams) +{ + if (connman == nullptr) return; + + int64_t time_in_seconds = GetTime(); + + EvictExtraOutboundPeers(time_in_seconds); + + if (time_in_seconds > m_stale_tip_check_time) { + LOCK(cs_main); + // Check whether our tip is stale, and if so, allow using an extra + // outbound peer + if (TipMayBeStale(consensusParams)) { + LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update); + connman->SetTryNewOutboundPeer(true); + } else if (connman->GetTryNewOutboundPeer()) { + connman->SetTryNewOutboundPeer(false); + } + m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; + } +} + class CompareInvMempoolOrder { CTxMemPool *mp; @@ -3226,6 +3565,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM } } + // Check that outbound peers have reasonable chains + // GetTime() is used by this anti-DoS logic so we can test this using mocktime + ConsiderEviction(pto, GetTime()); // // Message: getdata (blocks) diff --git a/src/net_processing.h b/src/net_processing.h index 79745cdd42..3fc1e05835 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -6,8 +6,9 @@ #ifndef BITCOIN_NET_PROCESSING_H #define BITCOIN_NET_PROCESSING_H -#include "net.h" -#include "validationinterface.h" +#include <net.h> +#include <validationinterface.h> +#include <consensus/params.h> /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; @@ -21,13 +22,25 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; * Timeout = base + per_header * (expected number of headers) */ static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header +/** Protect at least this many outbound peers from disconnection due to slow/ + * behind headers chain. + */ +static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4; +/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */ +static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes +/** How frequently to check for stale tips, in seconds */ +static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes +/** How frequently to check for extra outbound peers and disconnect, in seconds */ +static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45; +/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */ +static constexpr int64_t MINIMUM_CONNECT_TIME = 30; class PeerLogicValidation : public CValidationInterface, public NetEventsInterface { private: - CConnman* connman; + CConnman* const connman; public: - explicit PeerLogicValidation(CConnman* connman); + explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler); void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; @@ -47,6 +60,13 @@ public: * @return True if there is more work to be done */ bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override; + + void ConsiderEviction(CNode *pto, int64_t time_in_seconds); + void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams); + void EvictExtraOutboundPeers(int64_t time_in_seconds); + +private: + int64_t m_stale_tip_check_time; //! Next time to check for stale tip }; struct CNodeStateStats { diff --git a/src/netaddress.cpp b/src/netaddress.cpp index b8a261c921..3ef3c67ae4 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -4,13 +4,13 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifdef HAVE_CONFIG_H -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "netaddress.h" -#include "hash.h" -#include "utilstrencodings.h" -#include "tinyformat.h" +#include <netaddress.h> +#include <hash.h> +#include <utilstrencodings.h> +#include <tinyformat.h> static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; diff --git a/src/netaddress.h b/src/netaddress.h index 6ca99b36b5..3b542b4748 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -6,11 +6,11 @@ #define BITCOIN_NETADDRESS_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "compat.h" -#include "serialize.h" +#include <compat.h> +#include <serialize.h> #include <stdint.h> #include <string> diff --git a/src/netbase.cpp b/src/netbase.cpp index 05f9f6961c..bd1eaf062f 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -4,17 +4,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifdef HAVE_CONFIG_H -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "netbase.h" +#include <netbase.h> -#include "hash.h" -#include "sync.h" -#include "uint256.h" -#include "random.h" -#include "util.h" -#include "utilstrencodings.h" +#include <hash.h> +#include <sync.h> +#include <uint256.h> +#include <random.h> +#include <util.h> +#include <utilstrencodings.h> #include <atomic> @@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout) return timeout; } +/** SOCKS version */ +enum SOCKSVersion: uint8_t { + SOCKS4 = 0x04, + SOCKS5 = 0x05 +}; + +/** Values defined for METHOD in RFC1928 */ +enum SOCKS5Method: uint8_t { + NOAUTH = 0x00, //! No authentication required + GSSAPI = 0x01, //! GSSAPI + USER_PASS = 0x02, //! Username/password + NO_ACCEPTABLE = 0xff, //! No acceptable methods +}; + +/** Values defined for CMD in RFC1928 */ +enum SOCKS5Command: uint8_t { + CONNECT = 0x01, + BIND = 0x02, + UDP_ASSOCIATE = 0x03 +}; + +/** Values defined for REP in RFC1928 */ +enum SOCKS5Reply: uint8_t { + SUCCEEDED = 0x00, //! Succeeded + GENFAILURE = 0x01, //! General failure + NOTALLOWED = 0x02, //! Connection not allowed by ruleset + NETUNREACHABLE = 0x03, //! Network unreachable + HOSTUNREACHABLE = 0x04, //! Network unreachable + CONNREFUSED = 0x05, //! Connection refused + TTLEXPIRED = 0x06, //! TTL expired + CMDUNSUPPORTED = 0x07, //! Command not supported + ATYPEUNSUPPORTED = 0x08, //! Address type not supported +}; + +/** Values defined for ATYPE in RFC1928 */ +enum SOCKS5Atyp: uint8_t { + IPV4 = 0x01, + DOMAINNAME = 0x03, + IPV6 = 0x04, +}; + +/** Status codes that can be returned by InterruptibleRecv */ enum class IntrRecvError { OK, Timeout, @@ -203,7 +245,7 @@ enum class IntrRecvError { * * @note This function requires that hSocket is in non-blocking mode. */ -static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket) +static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket) { int64_t curTime = GetTimeMillis(); int64_t endTime = curTime + timeout; @@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons // to break off in case of an interruption. const int64_t maxWait = 1000; while (len > 0 && curTime < endTime) { - ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first + ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first if (ret > 0) { len -= ret; data += ret; @@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; } +/** Credentials for proxy authentication */ struct ProxyCredentials { std::string username; std::string password; }; -std::string Socks5ErrorString(int err) +/** Convert SOCKS5 reply to an error message */ +std::string Socks5ErrorString(uint8_t err) { switch(err) { - case 0x01: return "general failure"; - case 0x02: return "connection not allowed"; - case 0x03: return "network unreachable"; - case 0x04: return "host unreachable"; - case 0x05: return "connection refused"; - case 0x06: return "TTL expired"; - case 0x07: return "protocol error"; - case 0x08: return "address type not supported"; - default: return "unknown"; + case SOCKS5Reply::GENFAILURE: + return "general failure"; + case SOCKS5Reply::NOTALLOWED: + return "connection not allowed"; + case SOCKS5Reply::NETUNREACHABLE: + return "network unreachable"; + case SOCKS5Reply::HOSTUNREACHABLE: + return "host unreachable"; + case SOCKS5Reply::CONNREFUSED: + return "connection refused"; + case SOCKS5Reply::TTLEXPIRED: + return "TTL expired"; + case SOCKS5Reply::CMDUNSUPPORTED: + return "protocol error"; + case SOCKS5Reply::ATYPEUNSUPPORTED: + return "address type not supported"; + default: + return "unknown"; } } @@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials } // Accepted authentication methods std::vector<uint8_t> vSocks5Init; - vSocks5Init.push_back(0x05); + vSocks5Init.push_back(SOCKSVersion::SOCKS5); if (auth) { - vSocks5Init.push_back(0x02); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED - vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929) + vSocks5Init.push_back(0x02); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); + vSocks5Init.push_back(SOCKS5Method::USER_PASS); } else { - vSocks5Init.push_back(0x01); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED + vSocks5Init.push_back(0x01); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); } ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL); if (ret != (ssize_t)vSocks5Init.size()) { CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet1[2]; + uint8_t pchRet1[2]; if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); return false; } - if (pchRet1[0] != 0x05) { + if (pchRet1[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to initialize"); } - if (pchRet1[1] == 0x02 && auth) { + if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) { // Perform username/password authentication (as described in RFC1929) std::vector<uint8_t> vAuth; - vAuth.push_back(0x01); + vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation if (auth->username.size() > 255 || auth->password.size() > 255) return error("Proxy username or password too long"); vAuth.push_back(auth->username.size()); @@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error sending authentication to proxy"); } LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); - char pchRetA[2]; + uint8_t pchRetA[2]; if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); return error("Error reading proxy authentication response"); @@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Proxy authentication unsuccessful"); } - } else if (pchRet1[1] == 0x00) { + } else if (pchRet1[1] == SOCKS5Method::NOAUTH) { // Perform no authentication } else { CloseSocket(hSocket); return error("Proxy requested wrong authentication method %02x", pchRet1[1]); } std::vector<uint8_t> vSocks5; - vSocks5.push_back(0x05); // VER protocol version - vSocks5.push_back(0x01); // CMD CONNECT - vSocks5.push_back(0x00); // RSV Reserved - vSocks5.push_back(0x03); // ATYP DOMAINNAME + vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version + vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT + vSocks5.push_back(0x00); // RSV Reserved must be 0 + vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end()); vSocks5.push_back((port >> 8) & 0xFF); @@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet2[4]; + uint8_t pchRet2[4]; if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); if (recvr == IntrRecvError::Timeout) { @@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error while reading proxy response"); } } - if (pchRet2[0] != 0x05) { + if (pchRet2[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to accept request"); } - if (pchRet2[1] != 0x00) { + if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) { // Failures to connect to a peer that are not proxy errors CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1])); return false; } - if (pchRet2[2] != 0x00) { + if (pchRet2[2] != 0x00) { // Reserved field must be 0 CloseSocket(hSocket); return error("Error: malformed proxy response"); } - char pchRet3[256]; + uint8_t pchRet3[256]; switch (pchRet2[3]) { - case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x03: + case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::DOMAINNAME: { recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); if (recvr != IntrRecvError::OK) { @@ -399,7 +452,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return true; } -bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) +bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) { hSocketRet = INVALID_SOCKET; @@ -534,7 +587,7 @@ bool IsProxy(const CNetAddr &addr) { return false; } -static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) +bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) { SOCKET hSocket = INVALID_SOCKET; // first connect to proxy server @@ -558,47 +611,6 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe hSocketRet = hSocket; return true; } - -bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) -{ - proxyType proxy; - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - - if (GetProxy(addrDest.GetNetwork(), proxy)) - return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed); - else // no proxy needed (none set for target network) - return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); -} - -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed) -{ - std::string strDest; - int port = portDefault; - - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - - SplitHostPort(std::string(pszDest), port, strDest); - - proxyType proxy; - GetNameProxy(proxy); - - std::vector<CService> addrResolved; - if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) { - if (addrResolved.size() > 0) { - addr = addrResolved[GetRand(addrResolved.size())]; - return ConnectSocket(addr, hSocketRet, nTimeout); - } - } - - addr = CService(); - - if (!HaveNameProxy()) - return false; - return ConnectThroughProxy(proxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed); -} - bool LookupSubNet(const char* pszName, CSubNet& ret) { std::string strSubnet(pszName); diff --git a/src/netbase.h b/src/netbase.h index 6572f0a12e..0a07be87b7 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -6,12 +6,12 @@ #define BITCOIN_NETBASE_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "compat.h" -#include "netaddress.h" -#include "serialize.h" +#include <compat.h> +#include <netaddress.h> +#include <serialize.h> #include <stdint.h> #include <string> @@ -44,14 +44,15 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); +bool GetNameProxy(proxyType &nameProxyOut); bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup); bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup); bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup); bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); -bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr); -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr); +bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout); +bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); /** Close socket and set hSocket to INVALID_SOCKET */ diff --git a/src/netmessagemaker.h b/src/netmessagemaker.h index 79b2501c5d..ea7fccacb2 100644 --- a/src/netmessagemaker.h +++ b/src/netmessagemaker.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_NETMESSAGEMAKER_H #define BITCOIN_NETMESSAGEMAKER_H -#include "net.h" -#include "serialize.h" +#include <net.h> +#include <serialize.h> class CNetMsgMaker { diff --git a/src/noui.cpp b/src/noui.cpp index 4bfb75fa29..feb3464a56 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -3,10 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "noui.h" +#include <noui.h> -#include "ui_interface.h" -#include "util.h" +#include <ui_interface.h> +#include <util.h> #include <cstdio> #include <stdint.h> diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp index a089c02284..26c44ee0cf 100644 --- a/src/policy/feerate.cpp +++ b/src/policy/feerate.cpp @@ -3,9 +3,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "feerate.h" +#include <policy/feerate.h> -#include "tinyformat.h" +#include <tinyformat.h> const std::string CURRENCY_UNIT = "BTC"; diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 7e519e3efa..35b1b23786 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_POLICY_FEERATE_H #define BITCOIN_POLICY_FEERATE_H -#include "amount.h" -#include "serialize.h" +#include <amount.h> +#include <serialize.h> #include <string> @@ -20,10 +20,15 @@ class CFeeRate { private: CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes + public: /** Fee rate of 0 satoshis per kB */ CFeeRate() : nSatoshisPerK(0) { } - explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } + template<typename I> + CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { + // We've previously had bugs creep in from silent double->int conversion... + static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats"); + } /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/ CFeeRate(const CAmount& nFeePaid, size_t nBytes); /** diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index b9476407cf..013116318b 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -3,16 +3,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "policy/fees.h" -#include "policy/policy.h" +#include <policy/fees.h> +#include <policy/policy.h> -#include "amount.h" -#include "clientversion.h" -#include "primitives/transaction.h" -#include "random.h" -#include "streams.h" -#include "txmempool.h" -#include "util.h" +#include <amount.h> +#include <clientversion.h> +#include <primitives/transaction.h> +#include <random.h> +#include <streams.h> +#include <txmempool.h> +#include <util.h> static constexpr double INF_FEERATE = 1e99; @@ -180,6 +180,7 @@ TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets, : buckets(defaultBuckets), bucketMap(defaultBucketMap) { decay = _decay; + assert(_scale != 0 && "_scale must be non-zero"); scale = _scale; confAvg.resize(maxPeriods); for (unsigned int i = 0; i < maxPeriods; i++) { @@ -418,6 +419,9 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)"); } filein >> scale; + if (scale == 0) { + throw std::runtime_error("Corrupt estimates file. Scale must be non-zero"); + } } filein >> avg; @@ -503,6 +507,7 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe } } if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period + assert(scale != 0); unsigned int periodsAgo = blocksAgo / scale; for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) { failAvg[i][bucketindex]++; @@ -543,16 +548,13 @@ CBlockPolicyEstimator::CBlockPolicyEstimator() bucketMap[INF_FEERATE] = bucketIndex; assert(bucketMap.size() == buckets.size()); - feeStats = new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE); - shortStats = new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE); - longStats = new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE); + feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE)); + shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE)); + longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE)); } CBlockPolicyEstimator::~CBlockPolicyEstimator() { - delete feeStats; - delete shortStats; - delete longStats; } void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate) @@ -685,16 +687,16 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr double sufficientTxs = SUFFICIENT_FEETXS; switch (horizon) { case FeeEstimateHorizon::SHORT_HALFLIFE: { - stats = shortStats; + stats = shortStats.get(); sufficientTxs = SUFFICIENT_TXS_SHORT; break; } case FeeEstimateHorizon::MED_HALFLIFE: { - stats = feeStats; + stats = feeStats.get(); break; } case FeeEstimateHorizon::LONG_HALFLIFE: { - stats = longStats; + stats = longStats.get(); break; } default: { @@ -714,7 +716,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr if (median < 0) return CFeeRate(0); - return CFeeRate(median); + return CFeeRate(llround(median)); } unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const @@ -901,7 +903,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation if (median < 0) return CFeeRate(0); // error condition - return CFeeRate(median); + return CFeeRate(llround(median)); } @@ -997,12 +999,9 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) } // Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap - delete feeStats; - delete shortStats; - delete longStats; - feeStats = fileFeeStats.release(); - shortStats = fileShortStats.release(); - longStats = fileLongStats.release(); + feeStats = std::move(fileFeeStats); + shortStats = std::move(fileShortStats); + longStats = std::move(fileLongStats); nBestSeenHeight = nFileBestSeenHeight; historicalFirst = nFileHistoricalFirst; @@ -1043,5 +1042,5 @@ CAmount FeeFilterRounder::round(CAmount currentMinFee) if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) { it--; } - return *it; + return static_cast<CAmount>(*it); } diff --git a/src/policy/fees.h b/src/policy/fees.h index 6edaf28714..6528560f52 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -5,11 +5,11 @@ #ifndef BITCOIN_POLICYESTIMATOR_H #define BITCOIN_POLICYESTIMATOR_H -#include "amount.h" -#include "feerate.h" -#include "uint256.h" -#include "random.h" -#include "sync.h" +#include <amount.h> +#include <policy/feerate.h> +#include <uint256.h> +#include <random.h> +#include <sync.h> #include <map> #include <string> @@ -245,9 +245,9 @@ private: std::map<uint256, TxStatsInfo> mapMemPoolTxs; /** Classes to track historical data on transaction confirmations */ - TxConfirmStats* feeStats; - TxConfirmStats* shortStats; - TxConfirmStats* longStats; + std::unique_ptr<TxConfirmStats> feeStats; + std::unique_ptr<TxConfirmStats> shortStats; + std::unique_ptr<TxConfirmStats> longStats; unsigned int trackedTxs; unsigned int untrackedTxs; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 56912d0375..b69bed5d6f 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -5,14 +5,14 @@ // NOTE: This file is intended to be customised by the end user, and includes only local node policy logic -#include "policy/policy.h" +#include <policy/policy.h> -#include "consensus/validation.h" -#include "validation.h" -#include "coins.h" -#include "tinyformat.h" -#include "util.h" -#include "utilstrencodings.h" +#include <consensus/validation.h> +#include <validation.h> +#include <coins.h> +#include <tinyformat.h> +#include <util.h> +#include <utilstrencodings.h> CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) @@ -76,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH)) return false; - return whichType != TX_NONSTANDARD; + return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN; } bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled) diff --git a/src/policy/policy.h b/src/policy/policy.h index ef71dd73bc..f3f8ebbbb4 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -6,10 +6,10 @@ #ifndef BITCOIN_POLICY_POLICY_H #define BITCOIN_POLICY_POLICY_H -#include "consensus/consensus.h" -#include "feerate.h" -#include "script/interpreter.h" -#include "script/standard.h" +#include <consensus/consensus.h> +#include <policy/feerate.h> +#include <script/interpreter.h> +#include <script/standard.h> #include <string> diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp index 755ef83c9a..e9692d4b48 100644 --- a/src/policy/rbf.cpp +++ b/src/policy/rbf.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "policy/rbf.h" +#include <policy/rbf.h> bool SignalsOptInRBF(const CTransaction &tx) { diff --git a/src/policy/rbf.h b/src/policy/rbf.h index 22c73f3319..1a5218e120 100644 --- a/src/policy/rbf.h +++ b/src/policy/rbf.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_POLICY_RBF_H #define BITCOIN_POLICY_RBF_H -#include "txmempool.h" +#include <txmempool.h> static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd; diff --git a/src/pow.cpp b/src/pow.cpp index 7d87c6bbb7..cecb54ca7b 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -3,12 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "pow.h" +#include <pow.h> -#include "arith_uint256.h" -#include "chain.h" -#include "primitives/block.h" -#include "uint256.h" +#include <arith_uint256.h> +#include <chain.h> +#include <primitives/block.h> +#include <uint256.h> unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { @@ -6,7 +6,7 @@ #ifndef BITCOIN_POW_H #define BITCOIN_POW_H -#include "consensus/params.h" +#include <consensus/params.h> #include <stdint.h> diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 3774ac3e4b..9e20565511 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -3,12 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "primitives/block.h" +#include <primitives/block.h> -#include "hash.h" -#include "tinyformat.h" -#include "utilstrencodings.h" -#include "crypto/common.h" +#include <hash.h> +#include <tinyformat.h> +#include <utilstrencodings.h> +#include <crypto/common.h> uint256 CBlockHeader::GetHash() const { diff --git a/src/primitives/block.h b/src/primitives/block.h index 292df40896..4957958c6a 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_PRIMITIVES_BLOCK_H #define BITCOIN_PRIMITIVES_BLOCK_H -#include "primitives/transaction.h" -#include "serialize.h" -#include "uint256.h" +#include <primitives/transaction.h> +#include <serialize.h> +#include <uint256.h> /** Nodes collect new transactions into a block, hash them into a hash tree, * and scan through nonce values to make the block's hash satisfy proof-of-work diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index e0a106adb9..d8230d1423 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -3,11 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "primitives/transaction.h" +#include <primitives/transaction.h> -#include "hash.h" -#include "tinyformat.h" -#include "utilstrencodings.h" +#include <hash.h> +#include <tinyformat.h> +#include <utilstrencodings.h> std::string COutPoint::ToString() const { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 18d524e23d..32f45e5748 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -7,10 +7,10 @@ #define BITCOIN_PRIMITIVES_TRANSACTION_H #include <stdint.h> -#include "amount.h" -#include "script/script.h" -#include "serialize.h" -#include "uint256.h" +#include <amount.h> +#include <script/script.h> +#include <serialize.h> +#include <uint256.h> static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; diff --git a/src/protocol.cpp b/src/protocol.cpp index da87e40091..8c34c4196e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -3,10 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "protocol.h" +#include <protocol.h> -#include "util.h" -#include "utilstrencodings.h" +#include <util.h> +#include <utilstrencodings.h> #ifndef WIN32 # include <arpa/inet.h> diff --git a/src/protocol.h b/src/protocol.h index 67e01d9606..bc31434515 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -10,10 +10,10 @@ #ifndef BITCOIN_PROTOCOL_H #define BITCOIN_PROTOCOL_H -#include "netaddress.h" -#include "serialize.h" -#include "uint256.h" -#include "version.h" +#include <netaddress.h> +#include <serialize.h> +#include <uint256.h> +#include <version.h> #include <stdint.h> #include <string> @@ -27,16 +27,13 @@ class CMessageHeader { public: - enum { - MESSAGE_START_SIZE = 4, - COMMAND_SIZE = 12, - MESSAGE_SIZE_SIZE = 4, - CHECKSUM_SIZE = 4, - - MESSAGE_SIZE_OFFSET = MESSAGE_START_SIZE + COMMAND_SIZE, - CHECKSUM_OFFSET = MESSAGE_SIZE_OFFSET + MESSAGE_SIZE_SIZE, - HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE - }; + static constexpr size_t MESSAGE_START_SIZE = 4; + static constexpr size_t COMMAND_SIZE = 12; + static constexpr size_t MESSAGE_SIZE_SIZE = 4; + static constexpr size_t CHECKSUM_SIZE = 4; + static constexpr size_t MESSAGE_SIZE_OFFSET = MESSAGE_START_SIZE + COMMAND_SIZE; + static constexpr size_t CHECKSUM_OFFSET = MESSAGE_SIZE_OFFSET + MESSAGE_SIZE_SIZE; + static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE; typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; explicit CMessageHeader(const MessageStartChars& pchMessageStartIn); @@ -277,6 +274,43 @@ enum ServiceFlags : uint64_t { // BIP process. }; +/** + * Gets the set of service flags which are "desirable" for a given peer. + * + * These are the flags which are required for a peer to support for them + * to be "interesting" to us, ie for us to wish to use one of our few + * outbound connection slots for or for us to wish to prioritize keeping + * their connection around. + * + * Relevant service flags may be peer- and state-specific in that the + * version of the peer may determine which flags are required (eg in the + * case of NODE_NETWORK_LIMITED where we seek out NODE_NETWORK peers + * unless they set NODE_NETWORK_LIMITED and we are out of IBD, in which + * case NODE_NETWORK_LIMITED suffices). + * + * Thus, generally, avoid calling with peerServices == NODE_NONE. + */ +static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) { + return ServiceFlags(NODE_NETWORK | NODE_WITNESS); +} + +/** + * A shortcut for (services & GetDesirableServiceFlags(services)) + * == GetDesirableServiceFlags(services), ie determines whether the given + * set of service flags are sufficient for a peer to be "relevant". + */ +static inline bool HasAllDesirableServiceFlags(ServiceFlags services) { + return !(GetDesirableServiceFlags(services) & (~services)); +} + +/** + * Checks if a peer with the given service flags may be capable of having a + * robust address-storage DB. Currently an alias for checking NODE_NETWORK. + */ +static inline bool MayHaveUsefulAddressDB(ServiceFlags services) { + return services & NODE_NETWORK; +} + /** A CService with information about it as peer */ class CAddress : public CService { diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 2da7be783f..7f5ec1e8de 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "pubkey.h" +#include <pubkey.h> #include <secp256k1.h> #include <secp256k1_recovery.h> @@ -126,7 +126,6 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1 return 0; } spos = pos; - pos += slen; /* Ignore leading zeroes in R */ while (rlen > 0 && input[rpos] == 0) { diff --git a/src/pubkey.h b/src/pubkey.h index 65738d8fe2..c9f3c18eb3 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_PUBKEY_H #define BITCOIN_PUBKEY_H -#include "hash.h" -#include "serialize.h" -#include "uint256.h" +#include <hash.h> +#include <serialize.h> +#include <uint256.h> #include <stdexcept> #include <vector> diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index f295bd4689..dda8dce391 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -3,18 +3,18 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "addressbookpage.h" -#include "ui_addressbookpage.h" +#include <qt/addressbookpage.h> +#include <qt/forms/ui_addressbookpage.h> -#include "addresstablemodel.h" -#include "bitcoingui.h" -#include "csvmodelwriter.h" -#include "editaddressdialog.h" -#include "guiutil.h" -#include "platformstyle.h" +#include <qt/addresstablemodel.h> +#include <qt/bitcoingui.h> +#include <qt/csvmodelwriter.h> +#include <qt/editaddressdialog.h> +#include <qt/guiutil.h> +#include <qt/platformstyle.h> #include <QIcon> #include <QMenu> diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 0eb7ec4306..1d16940acb 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "addresstablemodel.h" +#include <qt/addresstablemodel.h> -#include "guiutil.h" -#include "walletmodel.h" +#include <qt/guiutil.h> +#include <qt/walletmodel.h> -#include "base58.h" -#include "wallet/wallet.h" +#include <base58.h> +#include <wallet/wallet.h> #include <QFont> diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index e9f5c77a5b..a720ac956b 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -3,16 +3,16 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "askpassphrasedialog.h" -#include "ui_askpassphrasedialog.h" +#include <qt/askpassphrasedialog.h> +#include <qt/forms/ui_askpassphrasedialog.h> -#include "guiconstants.h" -#include "walletmodel.h" +#include <qt/guiconstants.h> +#include <qt/walletmodel.h> -#include "support/allocators/secure.h" +#include <support/allocators/secure.h> #include <QKeyEvent> #include <QMessageBox> @@ -70,6 +70,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) : break; } textChanged(); + connect(ui->toggleShowPasswordButton, SIGNAL(toggled(bool)), this, SLOT(toggleShowPassword(bool))); 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())); @@ -234,6 +235,15 @@ bool AskPassphraseDialog::event(QEvent *event) return QWidget::event(event); } +void AskPassphraseDialog::toggleShowPassword(bool show) +{ + ui->toggleShowPasswordButton->setDown(show); + const auto mode = show ? QLineEdit::Normal : QLineEdit::Password; + ui->passEdit1->setEchoMode(mode); + ui->passEdit2->setEchoMode(mode); + ui->passEdit3->setEchoMode(mode); +} + bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) { /* Detect Caps Lock. diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 34bf7ccb31..7c6acc4650 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -43,6 +43,7 @@ private: private Q_SLOTS: void textChanged(); void secureClearPassFields(); + void toggleShowPassword(bool); protected: bool event(QEvent *event); diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index f8a99506c1..c96bdfd5d7 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bantablemodel.h" +#include <qt/bantablemodel.h> -#include "clientmodel.h" -#include "guiconstants.h" -#include "guiutil.h" +#include <qt/clientmodel.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> -#include "sync.h" -#include "utiltime.h" +#include <sync.h> +#include <utiltime.h> #include <QDebug> #include <QList> @@ -55,11 +55,11 @@ public: #if QT_VERSION >= 0x040700 cachedBanlist.reserve(banMap.size()); #endif - for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) + for (const auto& entry : banMap) { CCombinedBan banEntry; - banEntry.subnet = (*it).first; - banEntry.banEntry = (*it).second; + banEntry.subnet = entry.first; + banEntry.banEntry = entry.second; cachedBanlist.append(banEntry); } diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index 062cfdc931..dc69dee20a 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_BANTABLEMODEL_H #define BITCOIN_QT_BANTABLEMODEL_H -#include "net.h" +#include <net.h> #include <QAbstractTableModel> #include <QStringList> diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 3fd58a2f9a..557d7efcec 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -3,38 +3,38 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "bitcoingui.h" - -#include "chainparams.h" -#include "clientmodel.h" -#include "fs.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "intro.h" -#include "networkstyle.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "splashscreen.h" -#include "utilitydialog.h" -#include "winshutdownmonitor.h" +#include <qt/bitcoingui.h> + +#include <chainparams.h> +#include <qt/clientmodel.h> +#include <fs.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/intro.h> +#include <qt/networkstyle.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/splashscreen.h> +#include <qt/utilitydialog.h> +#include <qt/winshutdownmonitor.h> #ifdef ENABLE_WALLET -#include "paymentserver.h" -#include "walletmodel.h" +#include <qt/paymentserver.h> +#include <qt/walletmodel.h> #endif -#include "init.h" -#include "rpc/server.h" -#include "scheduler.h" -#include "ui_interface.h" -#include "util.h" -#include "warnings.h" +#include <init.h> +#include <rpc/server.h> +#include <scheduler.h> +#include <ui_interface.h> +#include <util.h> +#include <warnings.h> #ifdef ENABLE_WALLET -#include "wallet/wallet.h" +#include <wallet/wallet.h> #endif #include <stdint.h> @@ -261,7 +261,7 @@ private: void startThread(); }; -#include "bitcoin.moc" +#include <qt/bitcoin.moc> BitcoinCore::BitcoinCore(): QObject() diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 4dd1092806..e8d174bf29 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinaddressvalidator.h" +#include <qt/bitcoinaddressvalidator.h> -#include "base58.h" +#include <base58.h> /* Base58 characters are: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" @@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po if (((ch >= '0' && ch<='9') || (ch >= 'a' && ch<='z') || (ch >= 'A' && ch<='Z')) && - ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32 { // Alphanumeric and not a 'forbidden' character } diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 73eb35a54e..0b21349c54 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinamountfield.h" +#include <qt/bitcoinamountfield.h> -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "qvaluecombobox.h" +#include <qt/bitcoinunits.h> +#include <qt/guiconstants.h> +#include <qt/qvaluecombobox.h> #include <QApplication> #include <QAbstractSpinBox> @@ -188,7 +188,7 @@ Q_SIGNALS: void valueChanged(); }; -#include "bitcoinamountfield.moc" +#include <qt/bitcoinamountfield.moc> BitcoinAmountField::BitcoinAmountField(QWidget *parent) : QWidget(parent), diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 2f03a3d171..659ecb416b 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_BITCOINAMOUNTFIELD_H #define BITCOIN_QT_BITCOINAMOUNTFIELD_H -#include "amount.h" +#include <amount.h> #include <QWidget> diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index be2d21daee..cd3234c564 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -3,38 +3,38 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "bitcoingui.h" - -#include "bitcoinunits.h" -#include "clientmodel.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "modaloverlay.h" -#include "networkstyle.h" -#include "notificator.h" -#include "openuridialog.h" -#include "optionsdialog.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "rpcconsole.h" -#include "utilitydialog.h" +#include <qt/bitcoingui.h> + +#include <qt/bitcoinunits.h> +#include <qt/clientmodel.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/modaloverlay.h> +#include <qt/networkstyle.h> +#include <qt/notificator.h> +#include <qt/openuridialog.h> +#include <qt/optionsdialog.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/rpcconsole.h> +#include <qt/utilitydialog.h> #ifdef ENABLE_WALLET -#include "walletframe.h" -#include "walletmodel.h" +#include <qt/walletframe.h> +#include <qt/walletmodel.h> #endif // ENABLE_WALLET #ifdef Q_OS_MAC -#include "macdockiconhandler.h" +#include <qt/macdockiconhandler.h> #endif -#include "chainparams.h" -#include "init.h" -#include "ui_interface.h" -#include "util.h" +#include <chainparams.h> +#include <init.h> +#include <ui_interface.h> +#include <util.h> #include <iostream> @@ -123,7 +123,11 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); + QSettings settings; + if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } QString windowTitle = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET @@ -261,7 +265,8 @@ BitcoinGUI::~BitcoinGUI() // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); - GUIUtil::saveWindowGeometry("nWindow", this); + QSettings settings; + settings.setValue("MainWindowGeometry", saveGeometry()); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); #ifdef Q_OS_MAC diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index aa45ea1f0a..1647975fec 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -6,10 +6,10 @@ #define BITCOIN_QT_BITCOINGUI_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "amount.h" +#include <amount.h> #include <QLabel> #include <QMainWindow> diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 4da414a4e3..c8614fdfe0 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinunits.h" +#include <qt/bitcoinunits.h> -#include "primitives/transaction.h" +#include <primitives/transaction.h> #include <QStringList> diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 6ef37de380..3f5a7fd32d 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_BITCOINUNITS_H #define BITCOIN_QT_BITCOINUNITS_H -#include "amount.h" +#include <amount.h> #include <QAbstractListModel> #include <QString> diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 52ce11cefd..78174b90a1 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -2,23 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "clientmodel.h" - -#include "bantablemodel.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "peertablemodel.h" - -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "clientversion.h" -#include "validation.h" -#include "net.h" -#include "txmempool.h" -#include "ui_interface.h" -#include "util.h" -#include "warnings.h" +#include <qt/clientmodel.h> + +#include <qt/bantablemodel.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/peertablemodel.h> + +#include <chain.h> +#include <chainparams.h> +#include <checkpoints.h> +#include <clientversion.h> +#include <validation.h> +#include <net.h> +#include <txmempool.h> +#include <ui_interface.h> +#include <util.h> +#include <warnings.h> #include <stdint.h> diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 3ca43eae22..03e633e375 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -2,24 +2,24 @@ // 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 "platformstyle.h" -#include "txmempool.h" -#include "walletmodel.h" - -#include "wallet/coincontrol.h" -#include "init.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "validation.h" // For mempool -#include "wallet/fees.h" -#include "wallet/wallet.h" +#include <qt/coincontroldialog.h> +#include <qt/forms/ui_coincontroldialog.h> + +#include <qt/addresstablemodel.h> +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <txmempool.h> +#include <qt/walletmodel.h> + +#include <wallet/coincontrol.h> +#include <init.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <validation.h> // For mempool +#include <wallet/fees.h> +#include <wallet/wallet.h> #include <QApplication> #include <QCheckBox> @@ -582,7 +582,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); // how many satoshis the estimated fee can vary per byte we guess wrong - double dFeeVary = (double)nPayFee / nBytes; + double dFeeVary = (nBytes != 0) ? (double)nPayFee / nBytes : 0; QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 4949c91771..51b0a42907 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_COINCONTROLDIALOG_H #define BITCOIN_QT_COINCONTROLDIALOG_H -#include "amount.h" +#include <amount.h> #include <QAbstractButton> #include <QAction> diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp index 88510b6168..b628824793 100644 --- a/src/qt/coincontroltreewidget.cpp +++ b/src/qt/coincontroltreewidget.cpp @@ -2,8 +2,8 @@ // 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" +#include <qt/coincontroltreewidget.h> +#include <qt/coincontroldialog.h> CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) : QTreeWidget(parent) diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index df59927782..702757147b 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "csvmodelwriter.h" +#include <qt/csvmodelwriter.h> #include <QAbstractItemModel> #include <QFile> diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index bb03c12f2d..0437f81a7e 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "editaddressdialog.h" -#include "ui_editaddressdialog.h" +#include <qt/editaddressdialog.h> +#include <qt/forms/ui_editaddressdialog.h> -#include "addresstablemodel.h" -#include "guiutil.h" +#include <qt/addresstablemodel.h> +#include <qt/guiutil.h> #include <QDataWidgetMapper> #include <QMessageBox> diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index a2105ecd0a..69803989cd 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -93,6 +93,13 @@ </widget> </item> <item row="3" column="1"> + <widget class="QCheckBox" name="toggleShowPasswordButton"> + <property name="text"> + <string>Show password</string> + </property> + </widget> + </item> + <item row="4" column="1"> <widget class="QLabel" name="capsLabel"> <property name="font"> <font> diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index 1ea00eb5c3..d1237ad283 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -487,7 +487,7 @@ <customwidget> <class>CoinControlTreeWidget</class> <extends>QTreeWidget</extends> - <header>coincontroltreewidget.h</header> + <header>qt/coincontroltreewidget.h</header> </customwidget> </customwidgets> <resources/> diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 093e644bdc..bba822882e 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1424,7 +1424,7 @@ <customwidget> <class>TrafficGraphWidget</class> <extends>QWidget</extends> - <header>trafficgraphwidget.h</header> + <header>qt/trafficgraphwidget.h</header> <container>1</container> <slots> <slot>clear()</slot> diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index c1aea36338..e968271107 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -71,7 +71,7 @@ <customwidget> <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>qt/qvalidatedlineedit.h</header> </customwidget> </customwidgets> <resources/> diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui index 65a7a6c77e..fdc52dc455 100644 --- a/src/qt/forms/modaloverlay.ui +++ b/src/qt/forms/modaloverlay.ui @@ -367,7 +367,7 @@ QLabel { color: rgb(40,40,40); }</string> <customwidget> <class>ModalOverlay</class> <extends>QWidget</extends> - <header>modaloverlay.h</header> + <header>qt/modaloverlay.h</header> <container>1</container> </customwidget> </customwidgets> diff --git a/src/qt/forms/openuridialog.ui b/src/qt/forms/openuridialog.ui index 7fce858bd2..0e1048bc07 100644 --- a/src/qt/forms/openuridialog.ui +++ b/src/qt/forms/openuridialog.ui @@ -77,7 +77,7 @@ <customwidget> <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>qt/qvalidatedlineedit.h</header> </customwidget> </customwidgets> <resources/> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index e31bfee05e..a3721991ee 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -824,12 +824,12 @@ <customwidget> <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>qt/qvalidatedlineedit.h</header> </customwidget> <customwidget> <class>QValueComboBox</class> <extends>QComboBox</extends> - <header>qvaluecombobox.h</header> + <header>qt/qvaluecombobox.h</header> </customwidget> </customwidgets> <resources/> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 03fcb2fb50..58f030ebf0 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -28,23 +28,6 @@ <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <layout class="QGridLayout" name="gridLayout"> - <item row="7" column="2"> - <widget class="QCheckBox" name="reuseAddress"> - <property name="toolTip"> - <string>Reuse one of the previously used receiving addresses. Reusing addresses has security and privacy issues. Do not use this unless re-generating a payment request made before.</string> - </property> - <property name="text"> - <string>R&euse an existing receiving address (not recommended)</string> - </property> - </widget> - </item> - <item row="7" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string/> - </property> - </widget> - </item> <item row="6" column="0"> <widget class="QLabel" name="label_3"> <property name="toolTip"> @@ -127,7 +110,7 @@ </property> </widget> </item> - <item row="8" column="2"> + <item row="7" column="2"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPushButton" name="receiveButton"> @@ -184,7 +167,7 @@ </item> </layout> </item> - <item row="8" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_7"> <property name="text"> <string/> @@ -316,7 +299,7 @@ <customwidget> <class>BitcoinAmountField</class> <extends>QLineEdit</extends> - <header>bitcoinamountfield.h</header> + <header>qt/bitcoinamountfield.h</header> <container>1</container> </customwidget> </customwidgets> @@ -324,7 +307,6 @@ <tabstop>reqLabel</tabstop> <tabstop>reqAmount</tabstop> <tabstop>reqMessage</tabstop> - <tabstop>reuseAddress</tabstop> <tabstop>receiveButton</tabstop> <tabstop>clearButton</tabstop> <tabstop>recentRequestsView</tabstop> diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index 4163f4189c..dbe966b241 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -127,7 +127,7 @@ <customwidget> <class>QRImageWidget</class> <extends>QLabel</extends> - <header>receiverequestdialog.h</header> + <header>qt/receiverequestdialog.h</header> </customwidget> </customwidgets> <resources/> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 1e2f2302b9..9c89741afe 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -846,19 +846,13 @@ <item> <layout class="QHBoxLayout" name="horizontalLayoutFee13"> <item> - <widget class="QRadioButton" name="radioCustomPerKilobyte"> + <widget class="QLabel" name="labelCustomPerKilobyte"> <property name="toolTip"> <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string> </property> <property name="text"> <string>per kilobyte</string> </property> - <property name="checked"> - <bool>true</bool> - </property> - <attribute name="buttonGroup"> - <string notr="true">groupCustomFee</string> - </attribute> </widget> </item> <item> @@ -1270,12 +1264,12 @@ <customwidget> <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>qt/qvalidatedlineedit.h</header> </customwidget> <customwidget> <class>BitcoinAmountField</class> <extends>QLineEdit</extends> - <header>bitcoinamountfield.h</header> + <header>qt/bitcoinamountfield.h</header> <container>1</container> </customwidget> </customwidgets> @@ -1285,6 +1279,5 @@ <connections/> <buttongroups> <buttongroup name="groupFee"/> - <buttongroup name="groupCustomFee"/> </buttongroups> </ui> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index df06f36833..3c699abc6a 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -163,7 +163,7 @@ </widget> </item> <item row="2" column="1"> - <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1"> + <layout class="QHBoxLayout" name="horizontalLayoutAmount" stretch="0,1,0"> <item> <widget class="BitcoinAmountField" name="payAmount"/> </item> @@ -177,6 +177,13 @@ </property> </widget> </item> + <item> + <widget class="QPushButton" name="useAvailableBalanceButton"> + <property name="text"> + <string>Use available balance</string> + </property> + </widget> + </item> </layout> </item> <item row="3" column="0"> @@ -1248,12 +1255,12 @@ <customwidget> <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>qt/qvalidatedlineedit.h</header> </customwidget> <customwidget> <class>BitcoinAmountField</class> <extends>QLineEdit</extends> - <header>bitcoinamountfield.h</header> + <header>qt/bitcoinamountfield.h</header> </customwidget> </customwidgets> <tabstops> diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 92f6430c51..c9ddd757c1 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -380,7 +380,7 @@ <customwidget> <class>QValidatedLineEdit</class> <extends>QLineEdit</extends> - <header>qvalidatedlineedit.h</header> + <header>qt/qvalidatedlineedit.h</header> </customwidget> </customwidgets> <resources> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c3f98f764b..80ee35a341 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -2,21 +2,21 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "guiutil.h" - -#include "bitcoinaddressvalidator.h" -#include "bitcoinunits.h" -#include "qvalidatedlineedit.h" -#include "walletmodel.h" - -#include "fs.h" -#include "primitives/transaction.h" -#include "init.h" -#include "policy/policy.h" -#include "protocol.h" -#include "script/script.h" -#include "script/standard.h" -#include "util.h" +#include <qt/guiutil.h> + +#include <qt/bitcoinaddressvalidator.h> +#include <qt/bitcoinunits.h> +#include <qt/qvalidatedlineedit.h> +#include <qt/walletmodel.h> + +#include <fs.h> +#include <primitives/transaction.h> +#include <init.h> +#include <policy/policy.h> +#include <protocol.h> +#include <script/script.h> +#include <script/standard.h> +#include <util.h> #ifdef WIN32 #ifdef _WIN32_WINNT @@ -31,9 +31,9 @@ #ifndef NOMINMAX #define NOMINMAX #endif -#include "shellapi.h" -#include "shlobj.h" -#include "shlwapi.h" +#include <shellapi.h> +#include <shlobj.h> +#include <shlwapi.h> #endif #include <boost/scoped_array.hpp> @@ -744,9 +744,10 @@ bool SetStartOnSystemStartup(bool fAutoStart) else { char pszExePath[MAX_PATH+1]; - memset(pszExePath, 0, sizeof(pszExePath)); - if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1); + if (r == -1) return false; + pszExePath[r] = '\0'; fs::create_directories(GetAutostartDir()); @@ -862,32 +863,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; } #endif -void saveWindowGeometry(const QString& strSetting, QWidget *parent) -{ - QSettings settings; - settings.setValue(strSetting + "Pos", parent->pos()); - settings.setValue(strSetting + "Size", parent->size()); -} - -void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent) -{ - QSettings settings; - QPoint pos = settings.value(strSetting + "Pos").toPoint(); - QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); - - parent->resize(size); - parent->move(pos); - - if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1)) - { - QRect screen = QApplication::desktop()->screenGeometry(); - QPoint defaultPos((screen.width() - defaultSize.width()) / 2, - (screen.height() - defaultSize.height()) / 2); - parent->resize(defaultSize); - parent->move(defaultPos); - } -} - void setClipboard(const QString& str) { QApplication::clipboard()->setText(str, QClipboard::Clipboard); @@ -1009,6 +984,18 @@ QString formatNiceTimeOffset(qint64 secs) return timeBehindText; } +QString formatBytes(uint64_t bytes) +{ + if(bytes < 1024) + return QString(QObject::tr("%1 B")).arg(bytes); + if(bytes < 1024 * 1024) + return QString(QObject::tr("%1 KB")).arg(bytes / 1024); + if(bytes < 1024 * 1024 * 1024) + return QString(QObject::tr("%1 MB")).arg(bytes / 1024 / 1024); + + return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); +} + void ClickableLabel::mouseReleaseEvent(QMouseEvent *event) { Q_EMIT clicked(event->pos()); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d6aa8c4ea6..d0ab491952 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_QT_GUIUTIL_H #define BITCOIN_QT_GUIUTIL_H -#include "amount.h" -#include "fs.h" +#include <amount.h> +#include <fs.h> #include <QEvent> #include <QHeaderView> @@ -179,11 +179,6 @@ namespace GUIUtil bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); - /** Save window size and position */ - void saveWindowGeometry(const QString& strSetting, QWidget *parent); - /** Restore window size and position */ - void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent); - /* Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); @@ -204,6 +199,8 @@ namespace GUIUtil QString formatNiceTimeOffset(qint64 secs); + QString formatBytes(uint64_t bytes); + class ClickableLabel : public QLabel { Q_OBJECT diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 0ff95d8502..7f8a8394e6 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -3,16 +3,16 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "fs.h" -#include "intro.h" -#include "ui_intro.h" +#include <fs.h> +#include <qt/intro.h> +#include <qt/forms/ui_intro.h> -#include "guiutil.h" +#include <qt/guiutil.h> -#include "util.h" +#include <util.h> #include <QFileDialog> #include <QSettings> @@ -60,7 +60,7 @@ private: Intro *intro; }; -#include "intro.moc" +#include <qt/intro.moc> FreespaceChecker::FreespaceChecker(Intro *_intro) { @@ -214,7 +214,10 @@ bool Intro::pickDataDirectory() } dataDir = intro.getDataDirectory(); try { - TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir)); + if (TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir))) { + // If a new data directory has been created, make wallets subdirectory too + TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir) / "wallets"); + } break; } catch (const fs::filesystem_error&) { QMessageBox::critical(0, tr(PACKAGE_NAME), diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index e32a0bdda8..b707b0dda9 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "modaloverlay.h" -#include "ui_modaloverlay.h" +#include <qt/modaloverlay.h> +#include <qt/forms/ui_modaloverlay.h> -#include "guiutil.h" +#include <qt/guiutil.h> -#include "chainparams.h" +#include <chainparams.h> #include <QResizeEvent> #include <QPropertyAnimation> diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 4b81c54d36..42ae3ae566 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "networkstyle.h" +#include <qt/networkstyle.h> -#include "guiconstants.h" +#include <qt/guiconstants.h> #include <QApplication> diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index 937928315b..4f8732a9ce 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "notificator.h" +#include <qt/notificator.h> #include <QApplication> #include <QByteArray> @@ -24,7 +24,7 @@ // #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 #ifdef Q_OS_MAC #include <ApplicationServices/ApplicationServices.h> -#include "macnotificationhandler.h" +#include <qt/macnotificationhandler.h> #endif diff --git a/src/qt/notificator.h b/src/qt/notificator.h index 67f2b1df69..ef6f5c7ec5 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -6,7 +6,7 @@ #define BITCOIN_QT_NOTIFICATOR_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <QIcon> diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 3ee656d470..40671922f4 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "openuridialog.h" -#include "ui_openuridialog.h" +#include <qt/openuridialog.h> +#include <qt/forms/ui_openuridialog.h> -#include "guiutil.h" -#include "walletmodel.h" +#include <qt/guiutil.h> +#include <qt/walletmodel.h> #include <QUrl> diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 53e2e5053c..d7aa8bc38b 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -3,19 +3,19 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "optionsdialog.h" -#include "ui_optionsdialog.h" +#include <qt/optionsdialog.h> +#include <qt/forms/ui_optionsdialog.h> -#include "bitcoinunits.h" -#include "guiutil.h" -#include "optionsmodel.h" +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> -#include "validation.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS -#include "netbase.h" -#include "txdb.h" // for -dbcache defaults +#include <validation.h> // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS +#include <netbase.h> +#include <txdb.h> // for -dbcache defaults #include <QDataWidgetMapper> #include <QDir> diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index e9960a01b1..a0645d9a74 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -3,25 +3,25 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "optionsmodel.h" +#include <qt/optionsmodel.h> -#include "bitcoinunits.h" -#include "guiutil.h" +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> -#include "amount.h" -#include "init.h" -#include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS -#include "net.h" -#include "netbase.h" -#include "txdb.h" // for -dbcache defaults -#include "intro.h" +#include <amount.h> +#include <init.h> +#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS +#include <net.h> +#include <netbase.h> +#include <txdb.h> // for -dbcache defaults +#include <qt/intro.h> #ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#include "wallet/walletdb.h" +#include <wallet/wallet.h> +#include <wallet/walletdb.h> #endif #include <QNetworkProxy> @@ -124,7 +124,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseProxy")) settings.setValue("fUseProxy", false); - if (!settings.contains("addrProxy")) + if (!settings.contains("addrProxy") || !settings.value("addrProxy").toString().contains(':')) settings.setValue("addrProxy", "127.0.0.1:9050"); // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) @@ -134,7 +134,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseSeparateProxyTor")) settings.setValue("fUseSeparateProxyTor", false); - if (!settings.contains("addrSeparateProxyTor")) + if (!settings.contains("addrSeparateProxyTor") || !settings.value("addrSeparateProxyTor").toString().contains(':')) settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); // Only try to set -onion, if user has enabled fUseSeparateProxyTor if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) @@ -151,10 +151,32 @@ void OptionsModel::Init(bool resetSettings) language = settings.value("language").toString(); } +/** Helper function to copy contents from one QSettings to another. + * By using allKeys this also covers nested settings in a hierarchy. + */ +static void CopySettings(QSettings& dst, const QSettings& src) +{ + for (const QString& key : src.allKeys()) { + dst.setValue(key, src.value(key)); + } +} + +/** Back up a QSettings to an ini-formatted file. */ +static void BackupSettings(const fs::path& filename, const QSettings& src) +{ + qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename); + QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat); + dst.clear(); + CopySettings(dst, src); +} + void OptionsModel::Reset() { QSettings settings; + // Backup old settings to chain-specific datadir for troubleshooting + BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); + // Save the strDataDir setting QString dataDir = Intro::getDefaultDataDirectory(); dataDir = settings.value("strDataDir", dataDir).toString(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 0ac82a4148..b6e8fdef68 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_OPTIONSMODEL_H #define BITCOIN_QT_OPTIONSMODEL_H -#include "amount.h" +#include <amount.h> #include <QAbstractListModel> diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index ba1839e7b4..7818607d30 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "overviewpage.h" -#include "ui_overviewpage.h" - -#include "bitcoinunits.h" -#include "clientmodel.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "transactionfilterproxy.h" -#include "transactiontablemodel.h" -#include "walletmodel.h" +#include <qt/overviewpage.h> +#include <qt/forms/ui_overviewpage.h> + +#include <qt/bitcoinunits.h> +#include <qt/clientmodel.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/transactionfilterproxy.h> +#include <qt/transactiontablemodel.h> +#include <qt/walletmodel.h> #include <QAbstractItemDelegate> #include <QPainter> @@ -106,7 +106,7 @@ public: const PlatformStyle *platformStyle; }; -#include "overviewpage.moc" +#include <qt/overviewpage.moc> OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index d76b651ce6..1e09d907b0 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_OVERVIEWPAGE_H #define BITCOIN_QT_OVERVIEWPAGE_H -#include "amount.h" +#include <amount.h> #include <QWidget> #include <memory> diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index d3799f59ab..4503728882 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -7,9 +7,9 @@ // with some extra methods // -#include "paymentrequestplus.h" +#include <qt/paymentrequestplus.h> -#include "util.h" +#include <util.h> #include <stdexcept> @@ -194,8 +194,7 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c qWarning() << "PaymentRequestPlus::getMerchant: SSL error: " << err.what(); } - if (website) - delete[] website; + delete[] website; X509_STORE_CTX_free(store_ctx); for (unsigned int i = 0; i < certs.size(); i++) X509_free(certs[i]); diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h index a2fea3fdc6..e1388f2b5d 100644 --- a/src/qt/paymentrequestplus.h +++ b/src/qt/paymentrequestplus.h @@ -7,10 +7,10 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include "paymentrequest.pb.h" +#include <qt/paymentrequest.pb.h> #pragma GCC diagnostic pop -#include "base58.h" +#include <base58.h> #include <openssl/x509.h> diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 169684cf6d..510a3783ae 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "paymentserver.h" +#include <qt/paymentserver.h> -#include "bitcoinunits.h" -#include "guiutil.h" -#include "optionsmodel.h" +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> -#include "base58.h" -#include "chainparams.h" -#include "policy/policy.h" -#include "ui_interface.h" -#include "util.h" -#include "wallet/wallet.h" +#include <base58.h> +#include <chainparams.h> +#include <policy/policy.h> +#include <ui_interface.h> +#include <util.h> +#include <wallet/wallet.h> #include <cstdlib> @@ -362,8 +362,7 @@ void PaymentServer::initNetManager() { if (!optionsModel) return; - if (netManager != nullptr) - delete netManager; + delete netManager; // netManager is used to fetch paymentrequests given in bitcoin: URIs netManager = new QNetworkAccessManager(this); diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 98b2364b92..9adef9743d 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -32,8 +32,8 @@ // sends them to the server. // -#include "paymentrequestplus.h" -#include "walletmodel.h" +#include <qt/paymentrequestplus.h> +#include <qt/walletmodel.h> #include <QObject> #include <QString> diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 42934f8055..cfafab2c8a 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "peertablemodel.h" +#include <qt/peertablemodel.h> -#include "clientmodel.h" -#include "guiconstants.h" -#include "guiutil.h" +#include <qt/clientmodel.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> -#include "validation.h" // for cs_main -#include "sync.h" +#include <validation.h> // for cs_main +#include <sync.h> #include <QDebug> #include <QList> @@ -33,6 +33,10 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: return pLeft->dMinPing < pRight->dMinPing; + case PeerTableModel::Sent: + return pLeft->nSendBytes < pRight->nSendBytes; + case PeerTableModel::Received: + return pLeft->nRecvBytes < pRight->nRecvBytes; } return false; @@ -114,7 +118,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : clientModel(parent), timer(0) { - columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping"); + columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); priv.reset(new PeerTablePriv()); // default to unsorted priv->sortColumn = -1; @@ -173,10 +177,20 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: return GUIUtil::formatPingTime(rec->nodeStats.dMinPing); + case Sent: + return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); + case Received: + return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); } } else if (role == Qt::TextAlignmentRole) { - if (index.column() == Ping) - return (QVariant)(Qt::AlignRight | Qt::AlignVCenter); + switch (index.column()) { + case Ping: + case Sent: + case Received: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + default: + return QVariant(); + } } return QVariant(); diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index cc47b67ec9..e41fe4bb03 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_QT_PEERTABLEMODEL_H #define BITCOIN_QT_PEERTABLEMODEL_H -#include "net_processing.h" // For CNodeStateStats -#include "net.h" +#include <net_processing.h> // For CNodeStateStats +#include <net.h> #include <QAbstractTableModel> #include <QStringList> @@ -55,8 +55,10 @@ public: enum ColumnIndex { NetNodeId = 0, Address = 1, - Subversion = 2, - Ping = 3 + Ping = 2, + Sent = 3, + Received = 4, + Subversion = 5 }; /** @name Methods overridden from QAbstractTableModel diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp index 1f4e1a442f..cc8ea3622b 100644 --- a/src/qt/platformstyle.cpp +++ b/src/qt/platformstyle.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "platformstyle.h" +#include <qt/platformstyle.h> -#include "guiconstants.h" +#include <qt/guiconstants.h> #include <QApplication> #include <QColor> diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index 179ecdc8b3..4d6d5891c9 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "qvalidatedlineedit.h" +#include <qt/qvalidatedlineedit.h> -#include "bitcoinaddressvalidator.h" -#include "guiconstants.h" +#include <qt/bitcoinaddressvalidator.h> +#include <qt/guiconstants.h> QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : QLineEdit(parent), diff --git a/src/qt/qvaluecombobox.cpp b/src/qt/qvaluecombobox.cpp index a3b9b994ec..af5f6f8fd0 100644 --- a/src/qt/qvaluecombobox.cpp +++ b/src/qt/qvaluecombobox.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "qvaluecombobox.h" +#include <qt/qvaluecombobox.h> QValueComboBox::QValueComboBox(QWidget *parent) : QComboBox(parent), role(Qt::UserRole) diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 84f43266e1..d6e2beb312 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "receivecoinsdialog.h" -#include "ui_receivecoinsdialog.h" - -#include "addressbookpage.h" -#include "addresstablemodel.h" -#include "bitcoinunits.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "receiverequestdialog.h" -#include "recentrequeststablemodel.h" -#include "walletmodel.h" +#include <qt/receivecoinsdialog.h> +#include <qt/forms/ui_receivecoinsdialog.h> + +#include <qt/addressbookpage.h> +#include <qt/addresstablemodel.h> +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/receiverequestdialog.h> +#include <qt/recentrequeststablemodel.h> +#include <qt/walletmodel.h> #include <QAction> #include <QCursor> @@ -106,7 +106,6 @@ void ReceiveCoinsDialog::clear() ui->reqAmount->clear(); ui->reqLabel->setText(""); ui->reqMessage->setText(""); - ui->reuseAddress->setChecked(false); updateDisplayUnit(); } @@ -135,25 +134,8 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() QString address; QString label = ui->reqLabel->text(); - if(ui->reuseAddress->isChecked()) - { - /* Choose existing receiving address */ - AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this); - dlg.setModel(model->getAddressTableModel()); - if(dlg.exec()) - { - address = dlg.getReturnValue(); - if(label.isEmpty()) /* If no label provided, use the previously used label */ - { - label = model->getAddressTableModel()->labelForAddress(address); - } - } else { - return; - } - } else { - /* Generate new receiving address */ - address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, ""); - } + /* Generate new receiving address */ + address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, ""); SendCoinsRecipient info(address, label, ui->reqAmount->value(), ui->reqMessage->text()); ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this); diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 385f98565c..3687cca87b 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_RECEIVECOINSDIALOG_H #define BITCOIN_QT_RECEIVECOINSDIALOG_H -#include "guiutil.h" +#include <qt/guiutil.h> #include <QDialog> #include <QHeaderView> diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index 4aa6375d8a..c69fea1c93 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "receiverequestdialog.h" -#include "ui_receiverequestdialog.h" +#include <qt/receiverequestdialog.h> +#include <qt/forms/ui_receiverequestdialog.h> -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "walletmodel.h" +#include <qt/bitcoinunits.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/walletmodel.h> #include <QClipboard> #include <QDrag> @@ -22,7 +22,7 @@ #endif #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" /* for USE_QRCODE */ +#include <config/bitcoin-config.h> /* for USE_QRCODE */ #endif #ifdef USE_QRCODE diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index 1a9b165237..23b81135e2 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H #define BITCOIN_QT_RECEIVEREQUESTDIALOG_H -#include "walletmodel.h" +#include <qt/walletmodel.h> #include <QDialog> #include <QImage> diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 1c4f7aca86..4d6e401d0d 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "recentrequeststablemodel.h" +#include <qt/recentrequeststablemodel.h> -#include "bitcoinunits.h" -#include "guiutil.h" -#include "optionsmodel.h" +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> -#include "clientversion.h" -#include "streams.h" +#include <clientversion.h> +#include <streams.h> RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) : diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 0c02968f92..c88b8c4426 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H #define BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H -#include "walletmodel.h" +#include <qt/walletmodel.h> #include <QAbstractTableModel> #include <QStringList> diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3590a98efa..54a6e837c1 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -3,21 +3,21 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "rpcconsole.h" -#include "ui_debugwindow.h" +#include <qt/rpcconsole.h> +#include <qt/forms/ui_debugwindow.h> -#include "bantablemodel.h" -#include "clientmodel.h" -#include "guiutil.h" -#include "platformstyle.h" -#include "chainparams.h" -#include "netbase.h" -#include "rpc/server.h" -#include "rpc/client.h" -#include "util.h" +#include <qt/bantablemodel.h> +#include <qt/clientmodel.h> +#include <qt/guiutil.h> +#include <qt/platformstyle.h> +#include <chainparams.h> +#include <netbase.h> +#include <rpc/server.h> +#include <rpc/client.h> +#include <util.h> #include <openssl/crypto.h> @@ -28,6 +28,7 @@ #include <wallet/wallet.h> #endif +#include <QDesktopWidget> #include <QKeyEvent> #include <QMenu> #include <QMessageBox> @@ -124,7 +125,7 @@ public: }; -#include "rpcconsole.moc" +#include <qt/rpcconsole.moc> /** * Split shell command line into a list of arguments and optionally execute the command(s). @@ -391,11 +392,37 @@ void RPCExecutor::request(const QString &command) { std::string result; std::string executableCommand = command.toStdString() + "\n"; + + // Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply. + if(executableCommand == "help-console\n") + { + Q_EMIT reply(RPCConsole::CMD_REPLY, QString(("\n" + "This console accepts RPC commands using the standard syntax.\n" + " example: getblockhash 0\n\n" + + "This console can also accept RPC commands using parenthesized syntax.\n" + " example: getblockhash(0)\n\n" + + "Commands may be nested when specified with the parenthesized syntax.\n" + " example: getblock(getblockhash(0) 1)\n\n" + + "A space or a comma can be used to delimit arguments for either syntax.\n" + " example: getblockhash 0\n" + " getblockhash,0\n\n" + + "Named results can be queried with a non-quoted key string in brackets.\n" + " example: getblock(getblockhash(0) true)[tx]\n\n" + + "Results without keys can be queried using an integer in brackets.\n" + " example: getblock(getblockhash(0),true)[tx][0]\n\n"))); + return; + } if(!RPCConsole::RPCExecuteCommandLine(result, executableCommand)) { Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); return; } + Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result)); } catch (UniValue& objError) @@ -428,7 +455,11 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : consoleFontSize(0) { ui->setupUi(this); - GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); + QSettings settings; + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME))); @@ -466,14 +497,14 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); - QSettings settings; consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt(); clear(); } RPCConsole::~RPCConsole() { - GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); + QSettings settings; + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; @@ -640,6 +671,7 @@ void RPCConsole::setClientModel(ClientModel *model) wordList << ("help " + commandList[i]).c_str(); } + wordList << "help-console"; wordList.sort(); autoCompleter = new QCompleter(wordList, this); autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); @@ -745,10 +777,11 @@ void RPCConsole::clear(bool clearHistory) message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) + "<br>" + tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" + - tr("Type <b>help</b> for an overview of available commands.")) + - "<br><span class=\"secwarning\">" + + tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" + + tr("For more information on using this console type %1.").arg("<b>help-console</b>") + + "<br><span class=\"secwarning\"><br>" + tr("WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramifications of a command.") + - "</span>", + "</span>"), true); } @@ -930,18 +963,6 @@ void RPCConsole::on_sldGraphRange_valueChanged(int value) setTrafficGraphRange(mins); } -QString RPCConsole::FormatBytes(quint64 bytes) -{ - if(bytes < 1024) - return QString(tr("%1 B")).arg(bytes); - if(bytes < 1024 * 1024) - return QString(tr("%1 KB")).arg(bytes / 1024); - if(bytes < 1024 * 1024 * 1024) - return QString(tr("%1 MB")).arg(bytes / 1024 / 1024); - - return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024); -} - void RPCConsole::setTrafficGraphRange(int mins) { ui->trafficGraph->setGraphRangeMins(mins); @@ -950,8 +971,8 @@ void RPCConsole::setTrafficGraphRange(int mins) void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) { - ui->lblBytesIn->setText(FormatBytes(totalBytesIn)); - ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); + ui->lblBytesIn->setText(GUIUtil::formatBytes(totalBytesIn)); + ui->lblBytesOut->setText(GUIUtil::formatBytes(totalBytesOut)); } void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected) @@ -1045,8 +1066,8 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices)); ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastSend) : tr("never")); ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nLastRecv) : tr("never")); - ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes)); - ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes)); + ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes)); + ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes)); ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected)); ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait)); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index da06818f87..5960410cdd 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -5,10 +5,10 @@ #ifndef BITCOIN_QT_RPCCONSOLE_H #define BITCOIN_QT_RPCCONSOLE_H -#include "guiutil.h" -#include "peertablemodel.h" +#include <qt/guiutil.h> +#include <qt/peertablemodel.h> -#include "net.h" +#include <net.h> #include <QWidget> #include <QCompleter> @@ -123,7 +123,6 @@ Q_SIGNALS: void cmdRequest(const QString &command); private: - static QString FormatBytes(quint64 bytes); void startExecutor(); void setTrafficGraphRange(int mins); /** show detailed information on ui about selected node */ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 05c5ccbfe2..036b6ebcc0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -2,27 +2,27 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "sendcoinsdialog.h" -#include "ui_sendcoinsdialog.h" - -#include "addresstablemodel.h" -#include "bitcoinunits.h" -#include "clientmodel.h" -#include "coincontroldialog.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "sendcoinsentry.h" -#include "walletmodel.h" - -#include "base58.h" -#include "chainparams.h" -#include "wallet/coincontrol.h" -#include "validation.h" // mempool and minRelayTxFee -#include "ui_interface.h" -#include "txmempool.h" -#include "policy/fees.h" -#include "wallet/fees.h" +#include <qt/sendcoinsdialog.h> +#include <qt/forms/ui_sendcoinsdialog.h> + +#include <qt/addresstablemodel.h> +#include <qt/bitcoinunits.h> +#include <qt/clientmodel.h> +#include <qt/coincontroldialog.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/sendcoinsentry.h> +#include <qt/walletmodel.h> + +#include <base58.h> +#include <chainparams.h> +#include <wallet/coincontrol.h> +#include <validation.h> // mempool and minRelayTxFee +#include <ui_interface.h> +#include <txmempool.h> +#include <policy/fees.h> +#include <wallet/fees.h> #include <QFontMetrics> #include <QMessageBox> @@ -114,10 +114,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p settings.setValue("nFeeRadio", 1); // custom if (!settings.contains("nFeeRadio")) settings.setValue("nFeeRadio", 0); // recommended - if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility - settings.setValue("nCustomFeeRadio", 1); // total at least - if (!settings.contains("nCustomFeeRadio")) - settings.setValue("nCustomFeeRadio", 0); // per kilobyte if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) @@ -127,8 +123,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupFee->setId(ui->radioSmartFee, 0); ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true); - ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); - ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); @@ -178,7 +172,6 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); @@ -214,7 +207,6 @@ SendCoinsDialog::~SendCoinsDialog() QSettings settings; settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); - settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex())); settings.setValue("nTransactionFee", (qint64)ui->customFee->value()); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); @@ -410,6 +402,7 @@ SendCoinsEntry *SendCoinsDialog::addEntry() entry->setModel(model); ui->entries->addWidget(entry); connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*))); + connect(entry, SIGNAL(useAvailableBalance(SendCoinsEntry*)), this, SLOT(useAvailableBalance(SendCoinsEntry*))); connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels())); connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels())); @@ -607,9 +600,33 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() minimizeFeeSection(true); } +void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) +{ + // Get CCoinControl instance if CoinControl is enabled or create a new one. + CCoinControl coin_control; + if (model->getOptionsModel()->getCoinControlFeatures()) { + coin_control = *CoinControlDialog::coinControl; + } + + // Calculate available amount to send. + CAmount amount = model->getBalance(&coin_control); + for (int i = 0; i < ui->entries->count(); ++i) { + SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); + if (e && !e->isHidden() && e != entry) { + amount -= e->getValue().amount; + } + } + + if (amount > 0) { + entry->checkSubtractFeeFromAmount(); + entry->setAmount(amount); + } else { + entry->setAmount(0); + } +} + void SendCoinsDialog::setMinimumFee() { - ui->radioCustomPerKilobyte->setChecked(true); ui->customFee->setValue(GetRequiredFee(1000)); } @@ -622,7 +639,7 @@ void SendCoinsDialog::updateFeeSectionControls() ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); - ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); + ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } @@ -634,8 +651,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel() if (ui->radioSmartFee->isChecked()) ui->labelFeeMinimized->setText(ui->labelSmartFee->text()); else { - ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + - ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : "")); + ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB"); } } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 70b4aa5a03..300116fd9f 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_SENDCOINSDIALOG_H #define BITCOIN_QT_SENDCOINSDIALOG_H -#include "walletmodel.h" +#include <qt/walletmodel.h> #include <QDialog> #include <QMessageBox> @@ -76,6 +76,7 @@ private Q_SLOTS: void on_buttonChooseFee_clicked(); void on_buttonMinimizeFee_clicked(); void removeEntry(SendCoinsEntry* entry); + void useAvailableBalance(SendCoinsEntry* entry); void updateDisplayUnit(); void coinControlFeatureChanged(bool); void coinControlButtonClicked(); diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index bb0f47b21c..20e39bdeba 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -2,15 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "sendcoinsentry.h" -#include "ui_sendcoinsentry.h" +#include <qt/sendcoinsentry.h> +#include <qt/forms/ui_sendcoinsentry.h> -#include "addressbookpage.h" -#include "addresstablemodel.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "walletmodel.h" +#include <qt/addressbookpage.h> +#include <qt/addresstablemodel.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/walletmodel.h> #include <QApplication> #include <QClipboard> @@ -48,6 +48,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->useAvailableBalanceButton, SIGNAL(clicked()), this, SLOT(useAvailableBalanceClicked())); } SendCoinsEntry::~SendCoinsEntry() @@ -112,11 +113,21 @@ void SendCoinsEntry::clear() updateDisplayUnit(); } +void SendCoinsEntry::checkSubtractFeeFromAmount() +{ + ui->checkboxSubtractFeeFromAmount->setChecked(true); +} + void SendCoinsEntry::deleteClicked() { Q_EMIT removeEntry(this); } +void SendCoinsEntry::useAvailableBalanceClicked() +{ + Q_EMIT useAvailableBalance(this); +} + bool SendCoinsEntry::validate() { if (!model) @@ -228,6 +239,11 @@ void SendCoinsEntry::setAddress(const QString &address) ui->payAmount->setFocus(); } +void SendCoinsEntry::setAmount(const CAmount &amount) +{ + ui->payAmount->setValue(amount); +} + bool SendCoinsEntry::isClear() { return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() && ui->payTo_s->text().isEmpty(); diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index a8be670c2a..b470a0d72e 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_SENDCOINSENTRY_H #define BITCOIN_QT_SENDCOINSENTRY_H -#include "walletmodel.h" +#include <qt/walletmodel.h> #include <QStackedWidget> @@ -38,6 +38,7 @@ public: void setValue(const SendCoinsRecipient &value); void setAddress(const QString &address); + void setAmount(const CAmount &amount); /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases * (issue https://bugreports.qt-project.org/browse/QTBUG-10907). @@ -48,14 +49,17 @@ public: public Q_SLOTS: void clear(); + void checkSubtractFeeFromAmount(); Q_SIGNALS: void removeEntry(SendCoinsEntry *entry); + void useAvailableBalance(SendCoinsEntry* entry); void payAmountChanged(); void subtractFeeFromAmountChanged(); private Q_SLOTS: void deleteClicked(); + void useAvailableBalanceClicked(); void on_payTo_textChanged(const QString &address); void on_addressBookButton_clicked(); void on_pasteButton_clicked(); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index cba9d4da38..4cd0c76b16 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "signverifymessagedialog.h" -#include "ui_signverifymessagedialog.h" - -#include "addressbookpage.h" -#include "guiutil.h" -#include "platformstyle.h" -#include "walletmodel.h" - -#include "base58.h" -#include "init.h" -#include "validation.h" // For strMessageMagic -#include "wallet/wallet.h" +#include <qt/signverifymessagedialog.h> +#include <qt/forms/ui_signverifymessagedialog.h> + +#include <qt/addressbookpage.h> +#include <qt/guiutil.h> +#include <qt/platformstyle.h> +#include <qt/walletmodel.h> + +#include <base58.h> +#include <init.h> +#include <validation.h> // For strMessageMagic +#include <wallet/wallet.h> #include <string> #include <vector> diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index a1fbba963c..5aa1de5553 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -3,21 +3,21 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "splashscreen.h" +#include <qt/splashscreen.h> -#include "networkstyle.h" +#include <qt/networkstyle.h> -#include "clientversion.h" -#include "init.h" -#include "util.h" -#include "ui_interface.h" -#include "version.h" +#include <clientversion.h> +#include <init.h> +#include <util.h> +#include <ui_interface.h> +#include <version.h> #ifdef ENABLE_WALLET -#include "wallet/wallet.h" +#include <wallet/wallet.h> #endif #include <QApplication> diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp index 2a7284b5b2..7a4388e940 100644 --- a/src/qt/test/compattests.cpp +++ b/src/qt/test/compattests.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "paymentrequestplus.h" // this includes protobuf's port.h which defines its own bswap macos +#include <qt/paymentrequestplus.h> // this includes protobuf's port.h which defines its own bswap macos -#include "compattests.h" +#include <qt/test/compattests.h> -#include "compat/byteswap.h" +#include <compat/byteswap.h> void CompatTests::bswapTests() { diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 273bd10487..1864604372 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -2,17 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "paymentservertests.h" +#include <qt/test/paymentservertests.h> -#include "optionsmodel.h" -#include "paymentrequestdata.h" +#include <qt/optionsmodel.h> +#include <qt/test/paymentrequestdata.h> -#include "amount.h" -#include "random.h" -#include "script/script.h" -#include "script/standard.h" -#include "util.h" -#include "utilstrencodings.h" +#include <amount.h> +#include <random.h> +#include <script/script.h> +#include <script/standard.h> +#include <util.h> +#include <utilstrencodings.h> #include <openssl/x509.h> #include <openssl/x509_vfy.h> diff --git a/src/qt/test/paymentservertests.h b/src/qt/test/paymentservertests.h index faf167f2c6..6d84c38287 100644 --- a/src/qt/test/paymentservertests.h +++ b/src/qt/test/paymentservertests.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_TEST_PAYMENTSERVERTESTS_H #define BITCOIN_QT_TEST_PAYMENTSERVERTESTS_H -#include "../paymentserver.h" +#include <qt/paymentserver.h> #include <QObject> #include <QTest> diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 70fdd4bf58..0596ccf90e 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpcnestedtests.h" - -#include "chainparams.h" -#include "consensus/validation.h" -#include "fs.h" -#include "validation.h" -#include "rpc/register.h" -#include "rpc/server.h" -#include "rpcconsole.h" -#include "test/test_bitcoin.h" -#include "univalue.h" -#include "util.h" +#include <qt/test/rpcnestedtests.h> + +#include <chainparams.h> +#include <consensus/validation.h> +#include <fs.h> +#include <validation.h> +#include <rpc/register.h> +#include <rpc/server.h> +#include <qt/rpcconsole.h> +#include <test/test_bitcoin.h> +#include <univalue.h> +#include <util.h> #include <QDir> #include <QtGlobal> diff --git a/src/qt/test/rpcnestedtests.h b/src/qt/test/rpcnestedtests.h index 9ad409019f..04a9d124aa 100644 --- a/src/qt/test/rpcnestedtests.h +++ b/src/qt/test/rpcnestedtests.h @@ -8,8 +8,8 @@ #include <QObject> #include <QTest> -#include "txdb.h" -#include "txmempool.h" +#include <txdb.h> +#include <txmempool.h> class RPCNestedTests : public QObject { @@ -17,9 +17,6 @@ class RPCNestedTests : public QObject private Q_SLOTS: void rpcNestedTests(); - -private: - CCoinsViewDB *pcoinsdbview; }; #endif // BITCOIN_QT_TEST_RPC_NESTED_TESTS_H diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 4c04e67ccc..2e11b4f4d6 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -3,18 +3,18 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "chainparams.h" -#include "rpcnestedtests.h" -#include "util.h" -#include "uritests.h" -#include "compattests.h" +#include <chainparams.h> +#include <qt/test/rpcnestedtests.h> +#include <util.h> +#include <qt/test/uritests.h> +#include <qt/test/compattests.h> #ifdef ENABLE_WALLET -#include "paymentservertests.h" -#include "wallettests.h" +#include <qt/test/paymentservertests.h> +#include <qt/test/wallettests.h> #endif #include <QApplication> diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index 8b53c0d5c7..e47aa8b546 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "uritests.h" +#include <qt/test/uritests.h> -#include "guiutil.h" -#include "walletmodel.h" +#include <qt/guiutil.h> +#include <qt/walletmodel.h> #include <QUrl> diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 5031d7651d..4b7c3bd726 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -1,18 +1,22 @@ -#include "wallettests.h" - -#include "qt/bitcoinamountfield.h" -#include "qt/callback.h" -#include "qt/optionsmodel.h" -#include "qt/platformstyle.h" -#include "qt/qvalidatedlineedit.h" -#include "qt/sendcoinsdialog.h" -#include "qt/sendcoinsentry.h" -#include "qt/transactiontablemodel.h" -#include "qt/transactionview.h" -#include "qt/walletmodel.h" -#include "test/test_bitcoin.h" -#include "validation.h" -#include "wallet/wallet.h" +#include <qt/test/wallettests.h> + +#include <qt/bitcoinamountfield.h> +#include <qt/callback.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/qvalidatedlineedit.h> +#include <qt/sendcoinsdialog.h> +#include <qt/sendcoinsentry.h> +#include <qt/transactiontablemodel.h> +#include <qt/transactionview.h> +#include <qt/walletmodel.h> +#include <test/test_bitcoin.h> +#include <validation.h> +#include <wallet/wallet.h> +#include <qt/overviewpage.h> +#include <qt/receivecoinsdialog.h> +#include <qt/recentrequeststablemodel.h> +#include <qt/receiverequestdialog.h> #include <QAbstractButton> #include <QAction> @@ -21,6 +25,9 @@ #include <QPushButton> #include <QTimer> #include <QVBoxLayout> +#include <QTextEdit> +#include <QListView> +#include <QDialogButtonBox> namespace { @@ -140,7 +147,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st // src/qt/test/test_bitcoin-qt -platform xcb # Linux // src/qt/test/test_bitcoin-qt -platform windows # Windows // src/qt/test/test_bitcoin-qt -platform cocoa # macOS -void TestSendCoins() +void TestGUI() { // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; @@ -157,7 +164,10 @@ void TestSendCoins() wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive"); wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); } - wallet.ScanForWalletTransactions(chainActive.Genesis(), true); + { + LOCK(cs_main); + wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, true); + } wallet.SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. @@ -184,6 +194,68 @@ void TestSendCoins() BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */); BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */); + // Check current balance on OverviewPage + OverviewPage overviewPage(platformStyle.get()); + overviewPage.setWalletModel(&walletModel); + QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance"); + QString balanceText = balanceLabel->text(); + int unit = walletModel.getOptionsModel()->getDisplayUnit(); + CAmount balance = walletModel.getBalance(); + QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); + QCOMPARE(balanceText, balanceComparison); + + // Check Request Payment button + ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get()); + receiveCoinsDialog.setModel(&walletModel); + RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel(); + + // Label input + QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel"); + labelInput->setText("TEST_LABEL_1"); + + // Amount input + BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount"); + amountInput->setValue(1); + + // Message input + QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage"); + messageInput->setText("TEST_MESSAGE_1"); + int initialRowCount = requestTableModel->rowCount({}); + QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton"); + requestPaymentButton->click(); + for (QWidget* widget : QApplication::topLevelWidgets()) { + if (widget->inherits("ReceiveRequestDialog")) { + ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget); + QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri"); + QString paymentText = rlist->toPlainText(); + QStringList paymentTextList = paymentText.split('\n'); + QCOMPARE(paymentTextList.at(0), QString("Payment information")); + QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1); + QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); + QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); + QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); + QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); + } + } + + // Clear button + QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton"); + clearButton->click(); + QCOMPARE(labelInput->text(), QString("")); + QCOMPARE(amountInput->value(), CAmount(0)); + QCOMPARE(messageInput->text(), QString("")); + + // Check addition to history + int currentRowCount = requestTableModel->rowCount({}); + QCOMPARE(currentRowCount, initialRowCount+1); + + // Check Remove button + QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView"); + table->selectRow(currentRowCount-1); + QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton"); + removeRequestButton->click(); + QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); + bitdb.Flush(true); bitdb.Reset(); } @@ -192,5 +264,5 @@ void TestSendCoins() void WalletTests::walletTests() { - TestSendCoins(); + TestGUI(); } diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 5bb863451f..b5e4ccb406 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "trafficgraphwidget.h" -#include "clientmodel.h" +#include <qt/trafficgraphwidget.h> +#include <qt/clientmodel.h> #include <QPainter> #include <QColor> diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 74f5c774a0..3f245e6c33 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -2,21 +2,21 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "transactiondesc.h" - -#include "bitcoinunits.h" -#include "guiutil.h" -#include "paymentserver.h" -#include "transactionrecord.h" - -#include "base58.h" -#include "consensus/consensus.h" -#include "validation.h" -#include "script/script.h" -#include "timedata.h" -#include "util.h" -#include "wallet/db.h" -#include "wallet/wallet.h" +#include <qt/transactiondesc.h> + +#include <qt/bitcoinunits.h> +#include <qt/guiutil.h> +#include <qt/paymentserver.h> +#include <qt/transactionrecord.h> + +#include <base58.h> +#include <consensus/consensus.h> +#include <validation.h> +#include <script/script.h> +#include <timedata.h> +#include <util.h> +#include <wallet/db.h> +#include <wallet/wallet.h> #include <stdint.h> #include <string> @@ -24,7 +24,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) { AssertLockHeld(cs_main); - if (!CheckFinalTx(wtx)) + if (!CheckFinalTx(*wtx.tx)) { if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height()); diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp index 65f163deb2..74d7a8f525 100644 --- a/src/qt/transactiondescdialog.cpp +++ b/src/qt/transactiondescdialog.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "transactiondescdialog.h" -#include "ui_transactiondescdialog.h" +#include <qt/transactiondescdialog.h> +#include <qt/forms/ui_transactiondescdialog.h> -#include "transactiontablemodel.h" +#include <qt/transactiontablemodel.h> #include <QModelIndex> diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 44e4ef8238..ada73a73df 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "transactionfilterproxy.h" +#include <qt/transactionfilterproxy.h> -#include "transactiontablemodel.h" -#include "transactionrecord.h" +#include <qt/transactiontablemodel.h> +#include <qt/transactionrecord.h> #include <cstdlib> @@ -20,7 +20,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : QSortFilterProxyModel(parent), dateFrom(MIN_DATE), dateTo(MAX_DATE), - addrPrefix(), + m_search_string(), typeFilter(ALL_TYPES), watchOnlyFilter(WatchOnlyFilter_All), minAmount(0), @@ -38,6 +38,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool(); QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); + QString txid = index.data(TransactionTableModel::TxIDRole).toString(); qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); int status = index.data(TransactionTableModel::StatusRole).toInt(); @@ -51,8 +52,11 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & return false; if(datetime < dateFrom || datetime > dateTo) return false; - if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive)) + if (!address.contains(m_search_string, Qt::CaseInsensitive) && + ! label.contains(m_search_string, Qt::CaseInsensitive) && + ! txid.contains(m_search_string, Qt::CaseInsensitive)) { return false; + } if(amount < minAmount) return false; @@ -66,9 +70,10 @@ void TransactionFilterProxy::setDateRange(const QDateTime &from, const QDateTime invalidateFilter(); } -void TransactionFilterProxy::setAddressPrefix(const QString &_addrPrefix) +void TransactionFilterProxy::setSearchString(const QString &search_string) { - this->addrPrefix = _addrPrefix; + if (m_search_string == search_string) return; + m_search_string = search_string; invalidateFilter(); } diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 7db02cd61f..fea7502b26 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_TRANSACTIONFILTERPROXY_H #define BITCOIN_QT_TRANSACTIONFILTERPROXY_H -#include "amount.h" +#include <amount.h> #include <QDateTime> #include <QSortFilterProxyModel> @@ -35,7 +35,7 @@ public: }; void setDateRange(const QDateTime &from, const QDateTime &to); - void setAddressPrefix(const QString &addrPrefix); + void setSearchString(const QString &); /** @note Type filter takes a bit field created with TYPE() or ALL_TYPES */ @@ -57,7 +57,7 @@ protected: private: QDateTime dateFrom; QDateTime dateTo; - QString addrPrefix; + QString m_search_string; quint32 typeFilter; WatchOnlyFilter watchOnlyFilter; CAmount minAmount; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index d40ffd22cd..e3e8377cb9 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "transactionrecord.h" +#include <qt/transactionrecord.h> -#include "base58.h" -#include "consensus/consensus.h" -#include "validation.h" -#include "timedata.h" -#include "wallet/wallet.h" +#include <base58.h> +#include <consensus/consensus.h> +#include <validation.h> +#include <timedata.h> +#include <wallet/wallet.h> #include <stdint.h> @@ -182,7 +182,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); - if (!CheckFinalTx(wtx)) + if (!CheckFinalTx(*wtx.tx)) { if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) { diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index a26e676142..ee85acf4ac 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -5,8 +5,8 @@ #ifndef BITCOIN_QT_TRANSACTIONRECORD_H #define BITCOIN_QT_TRANSACTIONRECORD_H -#include "amount.h" -#include "uint256.h" +#include <amount.h> +#include <uint256.h> #include <QList> #include <QString> diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 59cef555b1..67580f11ae 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -2,23 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "transactiontablemodel.h" - -#include "addresstablemodel.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "transactiondesc.h" -#include "transactionrecord.h" -#include "walletmodel.h" - -#include "core_io.h" -#include "validation.h" -#include "sync.h" -#include "uint256.h" -#include "util.h" -#include "wallet/wallet.h" +#include <qt/transactiontablemodel.h> + +#include <qt/addresstablemodel.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/transactiondesc.h> +#include <qt/transactionrecord.h> +#include <qt/walletmodel.h> + +#include <core_io.h> +#include <validation.h> +#include <sync.h> +#include <uint256.h> +#include <util.h> +#include <wallet/wallet.h> #include <QColor> #include <QDateTime> @@ -80,10 +80,10 @@ public: cachedWallet.clear(); { LOCK2(cs_main, wallet->cs_wallet); - for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) + for (const auto& entry : wallet->mapWallet) { - if(TransactionRecord::showTransaction(it->second)) - cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); + if (TransactionRecord::showTransaction(entry.second)) + cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, entry.second)); } } } @@ -230,7 +230,7 @@ public: std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { - std::string strHex = EncodeHexTx(static_cast<CTransaction>(mi->second)); + std::string strHex = EncodeHexTx(*mi->second.tx); return QString::fromStdString(strHex); } return QString(); diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index b1f81498b2..a769707163 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_TRANSACTIONTABLEMODEL_H #define BITCOIN_QT_TRANSACTIONTABLEMODEL_H -#include "bitcoinunits.h" +#include <qt/bitcoinunits.h> #include <QAbstractTableModel> #include <QStringList> diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c38da9db..4d2aac12f0 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -2,23 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "transactionview.h" - -#include "addresstablemodel.h" -#include "bitcoinunits.h" -#include "csvmodelwriter.h" -#include "editaddressdialog.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "platformstyle.h" -#include "sendcoinsdialog.h" -#include "transactiondescdialog.h" -#include "transactionfilterproxy.h" -#include "transactionrecord.h" -#include "transactiontablemodel.h" -#include "walletmodel.h" - -#include "ui_interface.h" +#include <qt/transactionview.h> + +#include <qt/addresstablemodel.h> +#include <qt/bitcoinunits.h> +#include <qt/csvmodelwriter.h> +#include <qt/editaddressdialog.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/platformstyle.h> +#include <qt/sendcoinsdialog.h> +#include <qt/transactiondescdialog.h> +#include <qt/transactionfilterproxy.h> +#include <qt/transactionrecord.h> +#include <qt/transactiontablemodel.h> +#include <qt/walletmodel.h> + +#include <ui_interface.h> #include <QComboBox> #include <QDateTimeEdit> @@ -33,6 +33,7 @@ #include <QScrollBar> #include <QSignalMapper> #include <QTableView> +#include <QTimer> #include <QUrl> #include <QVBoxLayout> @@ -94,11 +95,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa hlayout->addWidget(typeWidget); - addressWidget = new QLineEdit(this); + search_widget = new QLineEdit(this); #if QT_VERSION >= 0x040700 - addressWidget->setPlaceholderText(tr("Enter address or label to search")); + search_widget->setPlaceholderText(tr("Enter address, transaction id, or label to search")); #endif - hlayout->addWidget(addressWidget); + hlayout->addWidget(search_widget); amountWidget = new QLineEdit(this); #if QT_VERSION >= 0x040700 @@ -112,6 +113,17 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); + // Delay before filtering transactions in ms + static const int input_filter_delay = 200; + + QTimer* amount_typing_delay = new QTimer(this); + amount_typing_delay->setSingleShot(true); + amount_typing_delay->setInterval(input_filter_delay); + + QTimer* prefix_typing_delay = new QTimer(this); + prefix_typing_delay->setSingleShot(true); + prefix_typing_delay->setInterval(input_filter_delay); + QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); vlayout->setSpacing(0); @@ -173,8 +185,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); - connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start())); + connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount())); + connect(search_widget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start())); + connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedSearch())); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); @@ -312,20 +326,19 @@ void TransactionView::chooseWatchonly(int idx) (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); } -void TransactionView::changedPrefix(const QString &prefix) +void TransactionView::changedSearch() { if(!transactionProxyModel) return; - transactionProxyModel->setAddressPrefix(prefix); + transactionProxyModel->setSearchString(search_widget->text()); } -void TransactionView::changedAmount(const QString &amount) +void TransactionView::changedAmount() { if(!transactionProxyModel) return; CAmount amount_parsed = 0; - if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) - { + if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } else diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 52e57cae4c..f72a828b00 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_TRANSACTIONVIEW_H #define BITCOIN_QT_TRANSACTIONVIEW_H -#include "guiutil.h" +#include <qt/guiutil.h> #include <QWidget> #include <QKeyEvent> @@ -66,7 +66,7 @@ private: QComboBox *dateWidget; QComboBox *typeWidget; QComboBox *watchOnlyWidget; - QLineEdit *addressWidget; + QLineEdit *search_widget; QLineEdit *amountWidget; QMenu *contextMenu; @@ -112,8 +112,8 @@ public Q_SLOTS: void chooseDate(int idx); void chooseType(int idx); void chooseWatchonly(int idx); - void changedPrefix(const QString &prefix); - void changedAmount(const QString &amount); + void changedAmount(); + void changedSearch(); void exportClicked(); void focusTransaction(const QModelIndex&); diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 5d8c23d13c..6d5a2f3101 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -3,23 +3,23 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "utilitydialog.h" +#include <qt/utilitydialog.h> -#include "ui_helpmessagedialog.h" +#include <qt/forms/ui_helpmessagedialog.h> -#include "bitcoingui.h" -#include "clientmodel.h" -#include "guiconstants.h" -#include "intro.h" -#include "paymentrequestplus.h" -#include "guiutil.h" +#include <qt/bitcoingui.h> +#include <qt/clientmodel.h> +#include <qt/guiconstants.h> +#include <qt/intro.h> +#include <qt/paymentrequestplus.h> +#include <qt/guiutil.h> -#include "clientversion.h" -#include "init.h" -#include "util.h" +#include <clientversion.h> +#include <init.h> +#include <util.h> #include <stdio.h> diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index f3183320f0..573b3f1412 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -2,11 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "walletframe.h" +#include <qt/walletframe.h> -#include "bitcoingui.h" -#include "walletview.h" +#include <qt/bitcoingui.h> +#include <qt/walletview.h> +#include <cassert> #include <cstdio> #include <QHBoxLayout> @@ -69,6 +70,7 @@ bool WalletFrame::setCurrentWallet(const QString& name) WalletView *walletView = mapWalletViews.value(name); walletStack->setCurrentWidget(walletView); + assert(walletView); walletView->updateEncryptionStatus(); return true; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 53b1c2967c..a38e233608 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -2,32 +2,32 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "walletmodel.h" - -#include "addresstablemodel.h" -#include "consensus/validation.h" -#include "guiconstants.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "paymentserver.h" -#include "recentrequeststablemodel.h" -#include "sendcoinsdialog.h" -#include "transactiontablemodel.h" - -#include "base58.h" -#include "chain.h" -#include "keystore.h" -#include "validation.h" -#include "net.h" // for g_connman -#include "policy/fees.h" -#include "policy/rbf.h" -#include "sync.h" -#include "ui_interface.h" -#include "util.h" // for GetBoolArg -#include "wallet/coincontrol.h" -#include "wallet/feebumper.h" -#include "wallet/wallet.h" -#include "wallet/walletdb.h" // for BackupWallet +#include <qt/walletmodel.h> + +#include <qt/addresstablemodel.h> +#include <consensus/validation.h> +#include <qt/guiconstants.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/paymentserver.h> +#include <qt/recentrequeststablemodel.h> +#include <qt/sendcoinsdialog.h> +#include <qt/transactiontablemodel.h> + +#include <base58.h> +#include <chain.h> +#include <keystore.h> +#include <validation.h> +#include <net.h> // for g_connman +#include <policy/fees.h> +#include <policy/rbf.h> +#include <sync.h> +#include <ui_interface.h> +#include <util.h> // for GetBoolArg +#include <wallet/coincontrol.h> +#include <wallet/feebumper.h> +#include <wallet/wallet.h> +#include <wallet/walletdb.h> // for BackupWallet #include <stdint.h> @@ -659,45 +659,39 @@ bool WalletModel::abandonTransaction(uint256 hash) const bool WalletModel::transactionCanBeBumped(uint256 hash) const { - LOCK2(cs_main, wallet->cs_wallet); - const CWalletTx *wtx = wallet->GetWalletTx(hash); - return wtx && SignalsOptInRBF(*wtx) && !wtx->mapValue.count("replaced_by_txid"); + return feebumper::TransactionCanBeBumped(wallet, hash); } bool WalletModel::bumpFee(uint256 hash) { - std::unique_ptr<CFeeBumper> feeBump; - { - CCoinControl coin_control; - coin_control.signalRbf = true; - LOCK2(cs_main, wallet->cs_wallet); - feeBump.reset(new CFeeBumper(wallet, hash, coin_control, 0)); - } - if (feeBump->getResult() != BumpFeeResult::OK) - { + CCoinControl coin_control; + coin_control.signalRbf = true; + std::vector<std::string> errors; + CAmount old_fee; + CAmount new_fee; + CMutableTransaction mtx; + if (feebumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, old_fee, new_fee, mtx) != feebumper::Result::OK) { QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" + - (feeBump->getErrors().size() ? QString::fromStdString(feeBump->getErrors()[0]) : "") +")"); + (errors.size() ? QString::fromStdString(errors[0]) : "") +")"); return false; } // allow a user based fee verification QString questionString = tr("Do you want to increase the fee?"); questionString.append("<br />"); - CAmount oldFee = feeBump->getOldFee(); - CAmount newFee = feeBump->getNewFee(); questionString.append("<table style=\"text-align: left;\">"); questionString.append("<tr><td>"); questionString.append(tr("Current fee:")); questionString.append("</td><td>"); - questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), oldFee)); + questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), old_fee)); questionString.append("</td></tr><tr><td>"); questionString.append(tr("Increase:")); questionString.append("</td><td>"); - questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee - oldFee)); + questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee - old_fee)); questionString.append("</td></tr><tr><td>"); questionString.append(tr("New fee:")); questionString.append("</td><td>"); - questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), newFee)); + questionString.append(BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), new_fee)); questionString.append("</td></tr></table>"); SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString); confirmationDialog.exec(); @@ -715,23 +709,15 @@ bool WalletModel::bumpFee(uint256 hash) } // sign bumped transaction - bool res = false; - { - LOCK2(cs_main, wallet->cs_wallet); - res = feeBump->signTransaction(wallet); - } - if (!res) { + if (!feebumper::SignTransaction(wallet, mtx)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction.")); return false; } // commit the bumped transaction - { - LOCK2(cs_main, wallet->cs_wallet); - res = feeBump->commit(wallet); - } - if(!res) { + uint256 txid; + if (feebumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) { QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" + - QString::fromStdString(feeBump->getErrors()[0])+")"); + QString::fromStdString(errors[0])+")"); return false; } return true; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 05733f8272..028146c187 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -5,10 +5,10 @@ #ifndef BITCOIN_QT_WALLETMODEL_H #define BITCOIN_QT_WALLETMODEL_H -#include "paymentrequestplus.h" -#include "walletmodeltransaction.h" +#include <qt/paymentrequestplus.h> +#include <qt/walletmodeltransaction.h> -#include "support/allocators/secure.h" +#include <support/allocators/secure.h> #include <map> #include <vector> diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index eae2c27f8a..2188b39b92 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -2,15 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "walletmodeltransaction.h" +#include <qt/walletmodeltransaction.h> -#include "policy/policy.h" -#include "wallet/wallet.h" +#include <policy/policy.h> +#include <wallet/wallet.h> WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &_recipients) : recipients(_recipients), walletTransaction(0), - keyChange(0), fee(0) { walletTransaction = new CWalletTx(); @@ -18,7 +17,6 @@ WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> & WalletModelTransaction::~WalletModelTransaction() { - delete keyChange; delete walletTransaction; } @@ -34,7 +32,7 @@ CWalletTx *WalletModelTransaction::getTransaction() const unsigned int WalletModelTransaction::getTransactionSize() { - return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction)); + return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction->tx)); } CAmount WalletModelTransaction::getTransactionFee() const @@ -91,10 +89,10 @@ CAmount WalletModelTransaction::getTotalTransactionAmount() const void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet) { - keyChange = new CReserveKey(wallet); + keyChange.reset(new CReserveKey(wallet)); } CReserveKey *WalletModelTransaction::getPossibleKeyChange() { - return keyChange; + return keyChange.get(); } diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index d7ecd7aa8c..519cf9df99 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H #define BITCOIN_QT_WALLETMODELTRANSACTION_H -#include "walletmodel.h" +#include <qt/walletmodel.h> #include <QObject> @@ -40,7 +40,7 @@ public: private: QList<SendCoinsRecipient> recipients; CWalletTx *walletTransaction; - CReserveKey *keyChange; + std::unique_ptr<CReserveKey> keyChange; CAmount fee; }; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 971f5e0e1a..a589fad9bd 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -2,24 +2,24 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "walletview.h" - -#include "addressbookpage.h" -#include "askpassphrasedialog.h" -#include "bitcoingui.h" -#include "clientmodel.h" -#include "guiutil.h" -#include "optionsmodel.h" -#include "overviewpage.h" -#include "platformstyle.h" -#include "receivecoinsdialog.h" -#include "sendcoinsdialog.h" -#include "signverifymessagedialog.h" -#include "transactiontablemodel.h" -#include "transactionview.h" -#include "walletmodel.h" - -#include "ui_interface.h" +#include <qt/walletview.h> + +#include <qt/addressbookpage.h> +#include <qt/askpassphrasedialog.h> +#include <qt/bitcoingui.h> +#include <qt/clientmodel.h> +#include <qt/guiutil.h> +#include <qt/optionsmodel.h> +#include <qt/overviewpage.h> +#include <qt/platformstyle.h> +#include <qt/receivecoinsdialog.h> +#include <qt/sendcoinsdialog.h> +#include <qt/signverifymessagedialog.h> +#include <qt/transactiontablemodel.h> +#include <qt/transactionview.h> +#include <qt/walletmodel.h> + +#include <ui_interface.h> #include <QAction> #include <QActionGroup> @@ -122,8 +122,8 @@ void WalletView::setWalletModel(WalletModel *_walletModel) overviewPage->setWalletModel(_walletModel); receiveCoinsPage->setModel(_walletModel); sendCoinsPage->setModel(_walletModel); - usedReceivingAddressesPage->setModel(_walletModel->getAddressTableModel()); - usedSendingAddressesPage->setModel(_walletModel->getAddressTableModel()); + usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); + usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); if (_walletModel) { diff --git a/src/qt/walletview.h b/src/qt/walletview.h index c1f8422f0c..4c90874135 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_WALLETVIEW_H #define BITCOIN_QT_WALLETVIEW_H -#include "amount.h" +#include <amount.h> #include <QStackedWidget> diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp index d78d9a2358..aa6c273431 100644 --- a/src/qt/winshutdownmonitor.cpp +++ b/src/qt/winshutdownmonitor.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "winshutdownmonitor.h" +#include <qt/winshutdownmonitor.h> #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 -#include "init.h" -#include "util.h" +#include <init.h> +#include <util.h> #include <windows.h> diff --git a/src/random.cpp b/src/random.cpp index 7e0e94439e..3075a77182 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -3,16 +3,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "random.h" +#include <random.h> -#include "crypto/sha512.h" -#include "support/cleanse.h" +#include <crypto/sha512.h> +#include <support/cleanse.h> #ifdef WIN32 -#include "compat.h" // for Windows API +#include <compat.h> // for Windows API #include <wincrypt.h> #endif -#include "util.h" // for LogPrint() -#include "utilstrencodings.h" // for GetTime() +#include <util.h> // for LogPrint() +#include <utilstrencodings.h> // for GetTime() #include <stdlib.h> #include <limits> diff --git a/src/random.h b/src/random.h index c60ab36179..9309917689 100644 --- a/src/random.h +++ b/src/random.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_RANDOM_H #define BITCOIN_RANDOM_H -#include "crypto/chacha20.h" -#include "crypto/common.h" -#include "uint256.h" +#include <crypto/chacha20.h> +#include <crypto/common.h> +#include <uint256.h> #include <stdint.h> diff --git a/src/rest.cpp b/src/rest.cpp index 0b2c843d5f..86ee7804a3 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -3,20 +3,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chain.h" -#include "chainparams.h" -#include "core_io.h" -#include "primitives/block.h" -#include "primitives/transaction.h" -#include "validation.h" -#include "httpserver.h" -#include "rpc/blockchain.h" -#include "rpc/server.h" -#include "streams.h" -#include "sync.h" -#include "txmempool.h" -#include "utilstrencodings.h" -#include "version.h" +#include <chain.h> +#include <chainparams.h> +#include <core_io.h> +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <validation.h> +#include <httpserver.h> +#include <rpc/blockchain.h> +#include <rpc/server.h> +#include <streams.h> +#include <sync.h> +#include <txmempool.h> +#include <utilstrencodings.h> +#include <version.h> #include <boost/algorithm/string.hpp> @@ -178,8 +178,11 @@ static bool rest_headers(HTTPRequest* req, } case RF_JSON: { UniValue jsonHeaders(UniValue::VARR); - for (const CBlockIndex *pindex : headers) { - jsonHeaders.push_back(blockheaderToJSON(pindex)); + { + LOCK(cs_main); + for (const CBlockIndex *pindex : headers) { + jsonHeaders.push_back(blockheaderToJSON(pindex)); + } } std::string strJSON = jsonHeaders.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -239,7 +242,11 @@ static bool rest_block(HTTPRequest* req, } case RF_JSON: { - UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails); + UniValue objBlock; + { + LOCK(cs_main); + objBlock = blockToJSON(block, pblockindex, showTxDetails); + } std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); @@ -409,10 +416,8 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) if (uriParts.size() > 0) { - //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...) - if (uriParts.size() > 0 && uriParts[0] == "checkmempool") - fCheckMemPool = true; + if (uriParts[0] == "checkmempool") fCheckMemPool = true; for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 46f4f16321..b7895b86f7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -3,27 +3,28 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpc/blockchain.h" - -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "coins.h" -#include "consensus/validation.h" -#include "validation.h" -#include "core_io.h" -#include "policy/feerate.h" -#include "policy/policy.h" -#include "primitives/transaction.h" -#include "rpc/server.h" -#include "streams.h" -#include "sync.h" -#include "txdb.h" -#include "txmempool.h" -#include "util.h" -#include "utilstrencodings.h" -#include "hash.h" +#include <rpc/blockchain.h> + +#include <amount.h> +#include <chain.h> +#include <chainparams.h> +#include <checkpoints.h> +#include <coins.h> +#include <consensus/validation.h> +#include <validation.h> +#include <core_io.h> +#include <policy/feerate.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <rpc/server.h> +#include <streams.h> +#include <sync.h> +#include <txdb.h> +#include <txmempool.h> +#include <util.h> +#include <utilstrencodings.h> +#include <hash.h> +#include <warnings.h> #include <stdint.h> @@ -77,6 +78,7 @@ double GetDifficulty(const CBlockIndex* blockindex) UniValue blockheaderToJSON(const CBlockIndex* blockindex) { + AssertLockHeld(cs_main); UniValue result(UniValue::VOBJ); result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); int confirmations = -1; @@ -105,6 +107,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails) { + AssertLockHeld(cs_main); UniValue result(UniValue::VOBJ); result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); int confirmations = -1; @@ -925,7 +928,7 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request) CCoinsStats stats; FlushStateToDisk(); - if (GetUTXOStats(pcoinsdbview, stats)) { + if (GetUTXOStats(pcoinsdbview.get(), stats)) { ret.push_back(Pair("height", (int64_t)stats.nHeight)); ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); @@ -993,7 +996,7 @@ UniValue gettxout(const JSONRPCRequest& request) Coin coin; if (fMempool) { LOCK(mempool.cs); - CCoinsViewMemPool view(pcoinsTip, mempool); + CCoinsViewMemPool view(pcoinsTip.get(), mempool); if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { return NullUniValue; } @@ -1045,7 +1048,7 @@ UniValue verifychain(const JSONRPCRequest& request) if (!request.params[1].isNull()) nCheckDepth = request.params[1].get_int(); - return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); + return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth); } /** Implementation of IsSuperMajority with better feedback */ @@ -1110,13 +1113,13 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse return rv; } -void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { // Deployments with timeout value of 0 are hidden. // A timeout value of 0 guarantees a softfork will never be activated. // This is used when softfork codes are merged without specifying the deployment schedule. if (consensusParams.vDeployments[id].nTimeout > 0) - bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id))); + bip9_softforks.push_back(Pair(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id))); } UniValue getblockchaininfo(const JSONRPCRequest& request) @@ -1127,41 +1130,46 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) "Returns an object containing various state info regarding blockchain processing.\n" "\nResult:\n" "{\n" - " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" + " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored\n" - " \"softforks\": [ (array) status of softforks in progress\n" + " \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\n" + " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" + " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" + " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" + " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" + " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" + " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" + " \"softforks\": [ (array) status of softforks in progress\n" " {\n" - " \"id\": \"xxxx\", (string) name of softfork\n" - " \"version\": xx, (numeric) block version\n" - " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n" - " \"status\": xx, (boolean) true if threshold reached\n" + " \"id\": \"xxxx\", (string) name of softfork\n" + " \"version\": xx, (numeric) block version\n" + " \"reject\": { (object) progress toward rejecting pre-softfork blocks\n" + " \"status\": xx, (boolean) true if threshold reached\n" " },\n" " }, ...\n" " ],\n" - " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n" - " \"xxxx\" : { (string) name of the softfork\n" - " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" - " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" - " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" - " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" - " \"since\": xx, (numeric) height of the first block to which the status applies\n" - " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n" - " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" - " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" - " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" - " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" - " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" + " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n" + " \"xxxx\" : { (string) name of the softfork\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" + " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" + " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" + " \"since\": xx, (numeric) height of the first block to which the status applies\n" + " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n" + " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" + " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" + " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" + " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" + " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" " }\n" " }\n" " }\n" + " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") @@ -1178,8 +1186,26 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); + obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload())); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); obj.push_back(Pair("pruned", fPruneMode)); + if (fPruneMode) { + CBlockIndex* block = chainActive.Tip(); + assert(block); + while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { + block = block->pprev; + } + + obj.push_back(Pair("pruneheight", block->nHeight)); + + // if 0, execution bypasses the whole if block. + bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); + obj.push_back(Pair("automatic_pruning", automatic_pruning)); + if (automatic_pruning) { + obj.push_back(Pair("prune_target_size", nPruneTarget)); + } + } const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); @@ -1188,19 +1214,13 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV); - BIP9SoftForkDescPushBack(bip9_softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT); + for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { + BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos)); + } obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); - if (fPruneMode) - { - CBlockIndex *block = chainActive.Tip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) - block = block->pprev; - - obj.push_back(Pair("pruneheight", block->nHeight)); - } + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); return obj; } @@ -1481,9 +1501,12 @@ UniValue getchaintxstats(const JSONRPCRequest& request) "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n" "\nResult:\n" "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the statistics in UNIX format.\n" - " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" - " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window.\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" + " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" + " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getchaintxstats", "") @@ -1493,10 +1516,6 @@ UniValue getchaintxstats(const JSONRPCRequest& request) const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month - if (!request.params[0].isNull()) { - blockcount = request.params[0].get_int(); - } - bool havehash = !request.params[1].isNull(); uint256 hash; if (havehash) { @@ -1521,8 +1540,14 @@ UniValue getchaintxstats(const JSONRPCRequest& request) assert(pindex != nullptr); - if (blockcount < 1 || blockcount >= pindex->nHeight) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height"); + if (request.params[0].isNull()) { + blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); + } else { + blockcount = request.params[0].get_int(); + + if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1"); + } } const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); @@ -1532,7 +1557,14 @@ UniValue getchaintxstats(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); ret.push_back(Pair("time", (int64_t)pindex->nTime)); ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx)); - ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + ret.push_back(Pair("window_block_count", blockcount)); + if (blockcount > 0) { + ret.push_back(Pair("window_tx_count", nTxDiff)); + ret.push_back(Pair("window_interval", nTimeDiff)); + if (nTimeDiff > 0) { + ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + } + } return ret; } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 406ad2f6ec..a595b6ec27 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -3,9 +3,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpc/client.h" -#include "rpc/protocol.h" -#include "util.h" +#include <rpc/client.h> +#include <rpc/protocol.h> +#include <util.h> #include <set> #include <stdint.h> @@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "logging", 0, "include" }, { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, + { "addwitnessaddress", 1, "p2sh" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, @@ -140,6 +141,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "echojson", 7, "arg7" }, { "echojson", 8, "arg8" }, { "echojson", 9, "arg9" }, + { "rescanblockchain", 0, "start_height"}, + { "rescanblockchain", 1, "stop_height"}, }; class CRPCConvertTable diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f0ffa07e12..1753ecd3fc 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -3,28 +3,28 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "base58.h" -#include "amount.h" -#include "chain.h" -#include "chainparams.h" -#include "consensus/consensus.h" -#include "consensus/params.h" -#include "consensus/validation.h" -#include "core_io.h" -#include "init.h" -#include "validation.h" -#include "miner.h" -#include "net.h" -#include "policy/fees.h" -#include "pow.h" -#include "rpc/blockchain.h" -#include "rpc/mining.h" -#include "rpc/server.h" -#include "txmempool.h" -#include "util.h" -#include "utilstrencodings.h" -#include "validationinterface.h" -#include "warnings.h" +#include <base58.h> +#include <amount.h> +#include <chain.h> +#include <chainparams.h> +#include <consensus/consensus.h> +#include <consensus/params.h> +#include <consensus/validation.h> +#include <core_io.h> +#include <init.h> +#include <validation.h> +#include <miner.h> +#include <net.h> +#include <policy/fees.h> +#include <pow.h> +#include <rpc/blockchain.h> +#include <rpc/mining.h> +#include <rpc/server.h> +#include <txmempool.h> +#include <util.h> +#include <utilstrencodings.h> +#include <validationinterface.h> +#include <warnings.h> #include <memory> #include <stdint.h> @@ -199,10 +199,11 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"currentblockweight\": nnn, (numeric) The last block weight\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" + " \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when bitcoind is started with -deprecatedrpc=getmininginfo\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -217,10 +218,14 @@ UniValue getmininginfo(const JSONRPCRequest& request) obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("networkhashps", getnetworkhashps(request))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("chain", Params().NetworkIDString())); + if (IsDeprecatedRPCEnabled("getmininginfo")) { + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + } else { + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + } return obj; } @@ -450,7 +455,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) { // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions uint256 hashWatchedChain; - boost::system_time checktxtime; + std::chrono::steady_clock::time_point checktxtime; unsigned int nTransactionsUpdatedLastLP; if (lpval.isStr()) @@ -471,17 +476,17 @@ UniValue getblocktemplate(const JSONRPCRequest& request) // Release the wallet and main lock while waiting LEAVE_CRITICAL_SECTION(cs_main); { - checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); + checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - boost::unique_lock<boost::mutex> lock(csBestBlock); + WaitableLock lock(csBestBlock); while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) { - if (!cvBlockChange.timed_wait(lock, checktxtime)) + if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) break; - checktxtime += boost::posix_time::seconds(10); + checktxtime += std::chrono::seconds(10); } } } @@ -789,6 +794,12 @@ UniValue estimatefee(const JSONRPCRequest& request) + HelpExampleCli("estimatefee", "6") ); + if (!IsDeprecatedRPCEnabled("estimatefee")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee is deprecated and will be fully removed in v0.17. " + "To use estimatefee in v0.16, restart bitcoind with -deprecatedrpc=estimatefee.\n" + "Projects should transition to using estimatesmartfee before upgrading to v0.17"); + } + RPCTypeCheck(request.params, {UniValue::VNUM}); int nBlocks = request.params[0].get_int(); diff --git a/src/rpc/mining.h b/src/rpc/mining.h index 868d7002b5..8d46273159 100644 --- a/src/rpc/mining.h +++ b/src/rpc/mining.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_RPC_MINING_H #define BITCOIN_RPC_MINING_H -#include "script/script.h" +#include <script/script.h> #include <univalue.h> diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index f8cdf57d14..c511aa0eb2 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -3,26 +3,27 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "base58.h" -#include "chain.h" -#include "clientversion.h" -#include "core_io.h" -#include "init.h" -#include "validation.h" -#include "httpserver.h" -#include "net.h" -#include "netbase.h" -#include "rpc/blockchain.h" -#include "rpc/server.h" -#include "timedata.h" -#include "util.h" -#include "utilstrencodings.h" +#include <base58.h> +#include <chain.h> +#include <clientversion.h> +#include <core_io.h> +#include <crypto/ripemd160.h> +#include <init.h> +#include <validation.h> +#include <httpserver.h> +#include <net.h> +#include <netbase.h> +#include <rpc/blockchain.h> +#include <rpc/server.h> +#include <timedata.h> +#include <util.h> +#include <utilstrencodings.h> #ifdef ENABLE_WALLET -#include "wallet/rpcwallet.h" -#include "wallet/wallet.h" -#include "wallet/walletdb.h" +#include <wallet/rpcwallet.h> +#include <wallet/wallet.h> +#include <wallet/walletdb.h> #endif -#include "warnings.h" +#include <warnings.h> #include <stdint.h> #ifdef HAVE_MALLOC_INFO @@ -45,6 +46,7 @@ public: UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); @@ -56,6 +58,7 @@ public: UniValue obj(UniValue::VOBJ); CScript subscript; obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetCScript(scriptID, subscript)) { std::vector<CTxDestination> addresses; txnouttype whichType; @@ -73,6 +76,47 @@ public: } return obj; } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + obj.push_back(Pair("pubkey", HexStr(pubkey))); + } + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + } + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", (int)id.version)); + obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length))); + return obj; + } }; #endif @@ -489,6 +533,9 @@ uint32_t getCategoryMask(UniValue cats) { if (!GetLogCategory(&flag, &cat)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); } + if (flag == BCLog::NONE) { + return 0; + } mask |= flag; } return mask; @@ -498,16 +545,32 @@ UniValue logging(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( - "logging [include,...] <exclude>\n" + "logging ( <include> <exclude> )\n" "Gets and sets the logging configuration.\n" - "When called without an argument, returns the list of categories that are currently being debug logged.\n" - "When called with arguments, adds or removes categories from debug logging.\n" + "When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n" + "When called with arguments, adds or removes categories from debug logging and return the lists above.\n" + "The arguments are evaluated in order \"include\", \"exclude\".\n" + "If an item is both included and excluded, it will thus end up being excluded.\n" "The valid logging categories are: " + ListLogCategories() + "\n" - "libevent logging is configured on startup and cannot be modified by this RPC during runtime." - "Arguments:\n" - "1. \"include\" (array of strings) add debug logging for these categories.\n" - "2. \"exclude\" (array of strings) remove debug logging for these categories.\n" - "\nResult: <categories> (string): a list of the logging categories that are active.\n" + "In addition, the following are available as category names with special meanings:\n" + " - \"all\", \"1\" : represent all logging categories.\n" + " - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n" + "\nArguments:\n" + "1. \"include\" (array of strings, optional) A json array of categories to add debug logging\n" + " [\n" + " \"category\" (string) the valid logging category\n" + " ,...\n" + " ]\n" + "2. \"exclude\" (array of strings, optional) A json array of categories to remove debug logging\n" + " [\n" + " \"category\" (string) the valid logging category\n" + " ,...\n" + " ]\n" + "\nResult:\n" + "{ (json object where keys are the logging categories, and values indicates its status\n" + " \"category\": 0|1, (numeric) if being debug logged or not. 0:inactive, 1:active\n" + " ...\n" + "}\n" "\nExamples:\n" + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"") @@ -564,6 +627,7 @@ static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, + { "control", "logging", &logging, {"include", "exclude"}}, { "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */ { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, @@ -573,7 +637,6 @@ static const CRPCCommand commands[] = { "hidden", "setmocktime", &setmocktime, {"timestamp"}}, { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "logging", &logging, {"include", "exclude"}}, }; void RegisterMiscRPCCommands(CRPCTable &t) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7faf216047..9de249855c 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -2,24 +2,24 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpc/server.h" - -#include "chainparams.h" -#include "clientversion.h" -#include "core_io.h" -#include "validation.h" -#include "net.h" -#include "net_processing.h" -#include "netbase.h" -#include "policy/policy.h" -#include "protocol.h" -#include "sync.h" -#include "timedata.h" -#include "ui_interface.h" -#include "util.h" -#include "utilstrencodings.h" -#include "version.h" -#include "warnings.h" +#include <rpc/server.h> + +#include <chainparams.h> +#include <clientversion.h> +#include <core_io.h> +#include <validation.h> +#include <net.h> +#include <net_processing.h> +#include <netbase.h> +#include <policy/policy.h> +#include <rpc/protocol.h> +#include <sync.h> +#include <timedata.h> +#include <ui_interface.h> +#include <util.h> +#include <utilstrencodings.h> +#include <version.h> +#include <warnings.h> #include <univalue.h> @@ -92,7 +92,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) " \"version\": v, (numeric) The peer version, such as 7001\n" " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" - " \"addnode\": true|false, (boolean) Whether connection was due to addnode and is using an addnode slot\n" + " \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" " \"banscore\": n, (numeric) The ban score\n" " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" @@ -146,7 +146,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) obj.push_back(Pair("timeoffset", stats.nTimeOffset)); if (stats.dPingTime > 0.0) obj.push_back(Pair("pingtime", stats.dPingTime)); - if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6) + if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6) obj.push_back(Pair("minping", stats.dMinPing)); if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); @@ -156,7 +156,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) // their ver message. obj.push_back(Pair("subver", stats.cleanSubVer)); obj.push_back(Pair("inbound", stats.fInbound)); - obj.push_back(Pair("addnode", stats.fAddnode)); + obj.push_back(Pair("addnode", stats.m_manual_connection)); obj.push_back(Pair("startingheight", stats.nStartingHeight)); if (fStateStats) { obj.push_back(Pair("banscore", statestats.nMisbehavior)); @@ -201,6 +201,8 @@ UniValue addnode(const JSONRPCRequest& request) "addnode \"node\" \"add|remove|onetry\"\n" "\nAttempts to add or remove a node from the addnode list.\n" "Or try a connection to a node once.\n" + "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n" + "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n" "\nArguments:\n" "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n" @@ -217,7 +219,7 @@ UniValue addnode(const JSONRPCRequest& request) if (strCommand == "onetry") { CAddress addr; - g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str()); + g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true); return NullUniValue; } @@ -447,7 +449,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" - " \"warnings\": \"...\" (string) any network warnings\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") @@ -569,11 +571,11 @@ UniValue listbanned(const JSONRPCRequest& request) g_connman->GetBanned(banMap); UniValue bannedAddresses(UniValue::VARR); - for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) + for (const auto& entry : banMap) { - CBanEntry banEntry = (*it).second; + const CBanEntry& banEntry = entry.second; UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", (*it).first.ToString())); + rec.push_back(Pair("address", entry.first.ToString())); rec.push_back(Pair("banned_until", banEntry.nBanUntil)); rec.push_back(Pair("ban_created", banEntry.nCreateTime)); rec.push_back(Pair("ban_reason", banEntry.banReasonToString())); diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index dc6bcec382..4cb28c2104 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -3,14 +3,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpc/protocol.h" +#include <rpc/protocol.h> -#include "random.h" -#include "tinyformat.h" -#include "util.h" -#include "utilstrencodings.h" -#include "utiltime.h" -#include "version.h" +#include <random.h> +#include <tinyformat.h> +#include <util.h> +#include <utilstrencodings.h> +#include <utiltime.h> +#include <version.h> #include <stdint.h> #include <fstream> @@ -19,7 +19,7 @@ * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were * unspecified (HTTP errors and contents of 'error'). - * + * * 1.0 spec: http://json-rpc.org/wiki/specification * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html */ @@ -135,3 +135,22 @@ void DeleteAuthCookie() } } +std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num) +{ + if (!in.isArray()) { + throw std::runtime_error("Batch must be an array"); + } + std::vector<UniValue> batch(num); + for (size_t i=0; i<in.size(); ++i) { + const UniValue &rec = in[i]; + if (!rec.isObject()) { + throw std::runtime_error("Batch member must be object"); + } + size_t id = rec["id"].get_int(); + if (id >= num) { + throw std::runtime_error("Batch member id larger than size"); + } + batch[id] = rec; + } + return batch; +} diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 5c9c64f67d..4b9bd41994 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_RPCPROTOCOL_H #define BITCOIN_RPCPROTOCOL_H -#include "fs.h" +#include <fs.h> #include <list> #include <map> @@ -57,6 +57,7 @@ enum RPCErrorCode RPC_VERIFY_REJECTED = -26, //!< Transaction or block was rejected by network rules RPC_VERIFY_ALREADY_IN_CHAIN = -27, //!< Transaction already in chain RPC_IN_WARMUP = -28, //!< Client still warming up + RPC_METHOD_DEPRECATED = -32, //!< RPC method is deprecated //! Aliases for backward compatibility RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR, @@ -97,5 +98,7 @@ bool GenerateAuthCookie(std::string *cookie_out); bool GetAuthCookie(std::string *cookie_out); /** Delete RPC authentication cookie from disk */ void DeleteAuthCookie(); +/** Parse JSON-RPC batch reply into a vector */ +std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num); #endif // BITCOIN_RPCPROTOCOL_H diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a0322f67b4..3aff1e9fbf 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -3,33 +3,35 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "base58.h" -#include "chain.h" -#include "coins.h" -#include "consensus/validation.h" -#include "core_io.h" -#include "init.h" -#include "keystore.h" -#include "validation.h" -#include "merkleblock.h" -#include "net.h" -#include "policy/policy.h" -#include "policy/rbf.h" -#include "primitives/transaction.h" -#include "rpc/safemode.h" -#include "rpc/server.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "script/standard.h" -#include "txmempool.h" -#include "uint256.h" -#include "utilstrencodings.h" +#include <base58.h> +#include <chain.h> +#include <coins.h> +#include <consensus/validation.h> +#include <core_io.h> +#include <init.h> +#include <keystore.h> +#include <validation.h> +#include <validationinterface.h> +#include <merkleblock.h> +#include <net.h> +#include <policy/policy.h> +#include <policy/rbf.h> +#include <primitives/transaction.h> +#include <rpc/safemode.h> +#include <rpc/server.h> +#include <script/script.h> +#include <script/script_error.h> +#include <script/sign.h> +#include <script/standard.h> +#include <txmempool.h> +#include <uint256.h> +#include <utilstrencodings.h> #ifdef ENABLE_WALLET -#include "wallet/rpcwallet.h" -#include "wallet/wallet.h" +#include <wallet/rpcwallet.h> +#include <wallet/wallet.h> #endif +#include <future> #include <stdint.h> #include <univalue.h> @@ -873,7 +875,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request) ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { + // Unable to sign input and verification failed (possible attempt to partially sign). + TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)"); + } else { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } } } bool fComplete = vErrors.empty(); @@ -912,7 +919,9 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) ); ObserveSafeMode(); - LOCK(cs_main); + + std::promise<void> promise; + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); // parse hex string from parameter @@ -926,6 +935,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) if (!request.params[1].isNull() && request.params[1].get_bool()) nMaxRawTxFee = 0; + { // cs_main scope + LOCK(cs_main); CCoinsViewCache &view = *pcoinsTip; bool fHaveChain = false; for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { @@ -937,8 +948,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - bool fLimitFree = true; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { @@ -947,10 +958,28 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) } throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } + } else { + // If wallet is enabled, ensure that the wallet has been made aware + // of the new transaction prior to returning. This prevents a race + // where a user might call sendrawtransaction with a transaction + // to/from their wallet, immediately call some wallet RPC, and get + // a stale result because callbacks have not yet been processed. + CallFunctionInValidationInterfaceQueue([&promise] { + promise.set_value(); + }); } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); + } else { + // Make sure we don't block forever if re-sending + // a transaction already in mempool. + promise.set_value(); } + + } // cs_main + + promise.get_future().wait(); + if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); @@ -959,6 +988,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) { pnode->PushInventory(inv); }); + return hashTx.GetHex(); } diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp index 24770ad47f..9f3a9d30b8 100644 --- a/src/rpc/safemode.cpp +++ b/src/rpc/safemode.cpp @@ -1,8 +1,8 @@ -#include "safemode.h" +#include <rpc/safemode.h> -#include "rpc/protocol.h" -#include "util.h" -#include "warnings.h" +#include <rpc/protocol.h> +#include <util.h> +#include <warnings.h> void ObserveSafeMode() { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 428ab3b9b0..0e4cd41689 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -3,16 +3,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpc/server.h" +#include <rpc/server.h> -#include "base58.h" -#include "fs.h" -#include "init.h" -#include "random.h" -#include "sync.h" -#include "ui_interface.h" -#include "util.h" -#include "utilstrencodings.h" +#include <base58.h> +#include <fs.h> +#include <init.h> +#include <random.h> +#include <sync.h> +#include <ui_interface.h> +#include <util.h> +#include <utilstrencodings.h> #include <univalue.h> @@ -160,8 +160,8 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& std::set<rpcfn_type> setDone; std::vector<std::pair<std::string, const CRPCCommand*> > vCommands; - for (std::map<std::string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) - vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); + for (const auto& entry : mapCommands) + vCommands.push_back(make_pair(entry.second->category + entry.first, entry.second)); sort(vCommands.begin(), vCommands.end()); JSONRPCRequest jreq(helpreq); @@ -382,11 +382,17 @@ void JSONRPCRequest::parse(const UniValue& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object"); } -static UniValue JSONRPCExecOne(const UniValue& req) +bool IsDeprecatedRPCEnabled(const std::string& method) +{ + const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc"); + + return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end(); +} + +static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req) { UniValue rpc_result(UniValue::VOBJ); - JSONRPCRequest jreq; try { jreq.parse(req); @@ -406,11 +412,11 @@ static UniValue JSONRPCExecOne(const UniValue& req) return rpc_result; } -std::string JSONRPCExecBatch(const UniValue& vReq) +std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq) { UniValue ret(UniValue::VARR); for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) - ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + ret.push_back(JSONRPCExecOne(jreq, vReq[reqIdx])); return ret.write() + "\n"; } diff --git a/src/rpc/server.h b/src/rpc/server.h index 777acbcb94..cd201bf316 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_RPCSERVER_H #define BITCOIN_RPCSERVER_H -#include "amount.h" -#include "rpc/protocol.h" -#include "uint256.h" +#include <amount.h> +#include <rpc/protocol.h> +#include <uint256.h> #include <list> #include <map> @@ -171,6 +171,8 @@ public: bool appendCommand(const std::string& name, const CRPCCommand* pcmd); }; +bool IsDeprecatedRPCEnabled(const std::string& method); + extern CRPCTable tableRPC; /** @@ -189,7 +191,7 @@ extern std::string HelpExampleRpc(const std::string& methodname, const std::stri bool StartRPC(); void InterruptRPC(); void StopRPC(); -std::string JSONRPCExecBatch(const UniValue& vReq); +std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq); // Retrieves any serialization flags requested in command line argument int RPCSerializationFlags(); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 4edb2c6d9b..260f6fa60e 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "scheduler.h" +#include <scheduler.h> -#include "random.h" -#include "reverselock.h" +#include <random.h> +#include <reverselock.h> #include <assert.h> #include <boost/bind.hpp> diff --git a/src/scheduler.h b/src/scheduler.h index db93bcb21e..b99f165343 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -14,7 +14,7 @@ #include <boost/thread.hpp> #include <map> -#include "sync.h" +#include <sync.h> // // Simple class for background tasks that should be run diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 03128917fd..77edc85b8c 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -3,12 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bitcoinconsensus.h" +#include <script/bitcoinconsensus.h> -#include "primitives/transaction.h" -#include "pubkey.h" -#include "script/interpreter.h" -#include "version.h" +#include <primitives/transaction.h> +#include <pubkey.h> +#include <script/interpreter.h> +#include <version.h> namespace { diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 33bf80e5a7..f09cd59b92 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -9,7 +9,7 @@ #include <stdint.h> #if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #if defined(_WIN32) #if defined(DLL_EXPORT) #if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 7315500e3b..d6348f17d8 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -3,15 +3,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "interpreter.h" +#include <script/interpreter.h> -#include "primitives/transaction.h" -#include "crypto/ripemd160.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "pubkey.h" -#include "script/script.h" -#include "uint256.h" +#include <primitives/transaction.h> +#include <crypto/ripemd160.h> +#include <crypto/sha1.h> +#include <crypto/sha256.h> +#include <pubkey.h> +#include <script/script.h> +#include <uint256.h> typedef std::vector<unsigned char> valtype; @@ -1168,29 +1168,36 @@ uint256 GetOutputsHash(const CTransaction& txTo) { PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) { - hashPrevouts = GetPrevoutHash(txTo); - hashSequence = GetSequenceHash(txTo); - hashOutputs = GetOutputsHash(txTo); + // Cache is calculated only for transactions with witness + if (txTo.HasWitness()) { + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); + ready = true; + } } uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { + assert(nIn < txTo.vin.size()); + if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; + const bool cacheready = cache && cache->ready; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); + hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); + hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo); } if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); + hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; @@ -1221,10 +1228,6 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig } static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); - if (nIn >= txTo.vin.size()) { - // nIn out of range - return one; - } // Check for invalid use of SIGHASH_SINGLE if ((nHashType & 0x1f) == SIGHASH_SINGLE) { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index f845e1943b..2eae68179e 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_SCRIPT_INTERPRETER_H #define BITCOIN_SCRIPT_INTERPRETER_H -#include "script_error.h" -#include "primitives/transaction.h" +#include <script/script_error.h> +#include <primitives/transaction.h> #include <vector> #include <stdint.h> @@ -113,6 +113,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i struct PrecomputedTransactionData { uint256 hashPrevouts, hashSequence, hashOutputs; + bool ready = false; explicit PrecomputedTransactionData(const CTransaction& tx); }; diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 0a39619734..60bef3b701 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -3,13 +3,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "ismine.h" +#include <script/ismine.h> -#include "key.h" -#include "keystore.h" -#include "script/script.h" -#include "script/standard.h" -#include "script/sign.h" +#include <key.h> +#include <keystore.h> +#include <script/script.h> +#include <script/standard.h> +#include <script/sign.h> typedef std::vector<unsigned char> valtype; @@ -46,6 +46,8 @@ isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& i isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { + isInvalid = false; + std::vector<valtype> vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { @@ -59,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); diff --git a/src/script/ismine.h b/src/script/ismine.h index 1aa5937b34..77ffa82432 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SCRIPT_ISMINE_H #define BITCOIN_SCRIPT_ISMINE_H -#include "script/standard.h" +#include <script/standard.h> #include <stdint.h> diff --git a/src/script/script.cpp b/src/script/script.cpp index a10b619f7d..a7bfb170eb 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -3,10 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script.h" +#include <script/script.h> -#include "tinyformat.h" -#include "utilstrencodings.h" +#include <tinyformat.h> +#include <utilstrencodings.h> const char* GetOpName(opcodetype opcode) { diff --git a/src/script/script.h b/src/script/script.h index 587f2d26eb..263db62edf 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_SCRIPT_SCRIPT_H #define BITCOIN_SCRIPT_SCRIPT_H -#include "crypto/common.h" -#include "prevector.h" -#include "serialize.h" +#include <crypto/common.h> +#include <prevector.h> +#include <serialize.h> #include <assert.h> #include <climits> @@ -420,6 +420,7 @@ public: CScript& operator+=(const CScript& b) { + reserve(size() + b.size()); insert(end(), b.begin(), b.end()); return *this; } diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index c9d13c92a8..32aa639ac4 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -3,7 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script_error.h" +#include <script/script_error.h> const char* ScriptErrorString(const ScriptError serror) { @@ -73,6 +73,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness version reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_CLEANSTACK: + return "Extra items left on stack after execution"; case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH: return "Witness program has incorrect length"; case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY: diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 4cc7afa2f5..88d1d67559 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -3,15 +3,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "sigcache.h" +#include <script/sigcache.h> -#include "memusage.h" -#include "pubkey.h" -#include "random.h" -#include "uint256.h" -#include "util.h" +#include <memusage.h> +#include <pubkey.h> +#include <random.h> +#include <uint256.h> +#include <util.h> -#include "cuckoocache.h" +#include <cuckoocache.h> #include <boost/thread.hpp> namespace { diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 5832b264b3..1019061fbb 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SCRIPT_SIGCACHE_H #define BITCOIN_SCRIPT_SIGCACHE_H -#include "script/interpreter.h" +#include <script/interpreter.h> #include <vector> diff --git a/src/script/sign.cpp b/src/script/sign.cpp index dc50467d3f..117a4d8a52 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -3,14 +3,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script/sign.h" +#include <script/sign.h> -#include "key.h" -#include "keystore.h" -#include "policy/policy.h" -#include "primitives/transaction.h" -#include "script/standard.h" -#include "uint256.h" +#include <key.h> +#include <keystore.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <script/standard.h> +#include <uint256.h> typedef std::vector<unsigned char> valtype; @@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: // Don't know anything about this, assume bigger one is correct: if (sigs1.script.size() >= sigs2.script.size()) return sigs1; diff --git a/src/script/sign.h b/src/script/sign.h index a0d8ee4ff9..400c0c0865 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SCRIPT_SIGN_H #define BITCOIN_SCRIPT_SIGN_H -#include "script/interpreter.h" +#include <script/interpreter.h> class CKeyID; class CKeyStore; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index b6e2232ab4..b7b33fade6 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -3,12 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "script/standard.h" +#include <script/standard.h> -#include "pubkey.h" -#include "script/script.h" -#include "util.h" -#include "utilstrencodings.h" +#include <pubkey.h> +#include <script/script.h> +#include <util.h> +#include <utilstrencodings.h> typedef std::vector<unsigned char> valtype; @@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_NULL_DATA: return "nulldata"; case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; + case TX_WITNESS_UNKNOWN: return "witness_unknown"; } return nullptr; } @@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v vSolutionsRet.push_back(witnessprogram); return true; } + if (witnessversion != 0) { + typeRet = TX_WITNESS_UNKNOWN; + vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); + vSolutionsRet.push_back(std::move(witnessprogram)); + return true; + } return false; } @@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { addressRet = CScriptID(uint160(vSolutions[0])); return true; + } else if (whichType == TX_WITNESS_V0_KEYHASH) { + WitnessV0KeyHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) { + WitnessV0ScriptHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_UNKNOWN) { + WitnessUnknown unk; + unk.version = vSolutions[0][0]; + std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); + unk.length = vSolutions[1].size(); + addressRet = unk; + return true; } // Multisig txns have more than one address... return false; @@ -268,6 +292,27 @@ public: *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return true; } + + bool operator()(const WitnessV0KeyHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessUnknown& id) const + { + script->clear(); + *script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length); + return true; + } }; } // namespace diff --git a/src/script/standard.h b/src/script/standard.h index 8df143a3a3..3eeeabdc15 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_SCRIPT_STANDARD_H #define BITCOIN_SCRIPT_STANDARD_H -#include "script/interpreter.h" -#include "uint256.h" +#include <script/interpreter.h> +#include <uint256.h> #include <boost/variant.hpp> @@ -64,6 +64,7 @@ enum txnouttype TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_KEYHASH, + TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; class CNoDestination { @@ -72,14 +73,42 @@ public: friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; +struct WitnessV0ScriptHash : public uint256 {}; +struct WitnessV0KeyHash : public uint160 {}; + +//! CTxDestination subtype to encode any future Witness version +struct WitnessUnknown +{ + unsigned int version; + unsigned int length; + unsigned char program[40]; + + friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version != w2.version) return false; + if (w1.length != w2.length) return false; + return std::equal(w1.program, w1.program + w1.length, w2.program); + } + + friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version < w2.version) return true; + if (w1.version > w2.version) return false; + if (w1.length < w2.length) return true; + if (w1.length > w2.length) return false; + return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length); + } +}; + /** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set - * * CKeyID: TX_PUBKEYHASH destination - * * CScriptID: TX_SCRIPTHASH destination + * * CKeyID: TX_PUBKEYHASH destination (P2PKH) + * * CScriptID: TX_SCRIPTHASH destination (P2SH) + * * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH) + * * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH) + * * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???) * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination; +typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); @@ -104,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v * Parse a standard scriptPubKey for the destination address. Assigns result to * the addressRet parameter and returns true if successful. For multisig * scripts, instead use ExtractDestinations. Currently only works for P2PK, - * P2PKH, and P2SH scripts. + * P2PKH, P2SH, P2WPKH, and P2WSH scripts. */ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h index 6d27871a7c..7eaf63bf6a 100644 --- a/src/secp256k1/contrib/lax_der_parsing.h +++ b/src/secp256k1/contrib/lax_der_parsing.h @@ -48,14 +48,14 @@ * 8.3.1. */ -#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ -#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ +#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H +#define SECP256K1_CONTRIB_LAX_DER_PARSING_H #include <secp256k1.h> -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Parse a signature in "lax DER" format * @@ -88,4 +88,4 @@ int ecdsa_signature_parse_der_lax( } #endif -#endif +#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */ diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h index 2fd088f8ab..fece261fb9 100644 --- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h +++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h @@ -25,14 +25,14 @@ * library are sufficient. */ -#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ -#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ +#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H +#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H #include <secp256k1.h> -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Export a private key in DER format. * @@ -87,4 +87,4 @@ SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( } #endif -#endif +#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */ diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index fc4c5cefbb..3e9c098d19 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -1,9 +1,9 @@ -#ifndef _SECP256K1_ -# define _SECP256K1_ +#ifndef SECP256K1_H +#define SECP256K1_H -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif #include <stddef.h> @@ -61,7 +61,7 @@ typedef struct { * however guaranteed to be 64 bytes in size, and can be safely copied/moved. * If you need to convert to a format suitable for storage, transmission, or * comparison, use the secp256k1_ecdsa_signature_serialize_* and - * secp256k1_ecdsa_signature_serialize_* functions. + * secp256k1_ecdsa_signature_parse_* functions. */ typedef struct { unsigned char data[64]; @@ -159,6 +159,13 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) #define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) +/** Prefix byte used to tag various encoded curvepoints for specific purposes */ +#define SECP256K1_TAG_PUBKEY_EVEN 0x02 +#define SECP256K1_TAG_PUBKEY_ODD 0x03 +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04 +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 + /** Create a secp256k1 context object. * * Returns: a newly created context object. @@ -607,8 +614,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( size_t n ) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_H */ diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h index 4b84d7a963..88492dc1a4 100644 --- a/src/secp256k1/include/secp256k1_ecdh.h +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -1,11 +1,11 @@ -#ifndef _SECP256K1_ECDH_ -# define _SECP256K1_ECDH_ +#ifndef SECP256K1_ECDH_H +#define SECP256K1_ECDH_H -# include "secp256k1.h" +#include "secp256k1.h" -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Compute an EC Diffie-Hellman secret in constant time * Returns: 1: exponentiation was successful @@ -24,8 +24,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( const unsigned char *privkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_ECDH_H */ diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h index 0553797253..cf6c5ed7f5 100644 --- a/src/secp256k1/include/secp256k1_recovery.h +++ b/src/secp256k1/include/secp256k1_recovery.h @@ -1,11 +1,11 @@ -#ifndef _SECP256K1_RECOVERY_ -# define _SECP256K1_RECOVERY_ +#ifndef SECP256K1_RECOVERY_H +#define SECP256K1_RECOVERY_H -# include "secp256k1.h" +#include "secp256k1.h" -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Opaque data structured that holds a parsed ECDSA signature, * supporting pubkey recovery. @@ -103,8 +103,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( const unsigned char *msg32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_RECOVERY_H */ diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage index 5198724bea..8521f07999 100644 --- a/src/secp256k1/sage/group_prover.sage +++ b/src/secp256k1/sage/group_prover.sage @@ -17,7 +17,7 @@ # - A constraint describing the requirements of the law, called "require" # * Implementations are transliterated into functions that operate as well on # algebraic input points, and are called once per combination of branches -# exectured. Each execution returns: +# executed. Each execution returns: # - A constraint describing the assumptions this implementation requires # (such as Z1=1), called "assumeFormula" # - A constraint describing the assumptions this specific branch requires, diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s index bd2b629e1c..5a9cc3ffcf 100644 --- a/src/secp256k1/src/asm/field_10x26_arm.s +++ b/src/secp256k1/src/asm/field_10x26_arm.s @@ -23,7 +23,7 @@ Note: .eabi_attribute 10, 0 @ Tag_FP_arch = none .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP - .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 .text diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h index c4c16eb7ca..fc588061ca 100644 --- a/src/secp256k1/src/basic-config.h +++ b/src/secp256k1/src/basic-config.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BASIC_CONFIG_ -#define _SECP256K1_BASIC_CONFIG_ +#ifndef SECP256K1_BASIC_CONFIG_H +#define SECP256K1_BASIC_CONFIG_H #ifdef USE_BASIC_CONFIG @@ -28,5 +28,6 @@ #define USE_FIELD_10X26 1 #define USE_SCALAR_8X32 1 -#endif // USE_BASIC_CONFIG -#endif // _SECP256K1_BASIC_CONFIG_ +#endif /* USE_BASIC_CONFIG */ + +#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h index d67f08a426..d5ebe01301 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BENCH_H_ -#define _SECP256K1_BENCH_H_ +#ifndef SECP256K1_BENCH_H +#define SECP256K1_BENCH_H #include <stdio.h> #include <math.h> @@ -63,4 +63,4 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v printf("us\n"); } -#endif +#endif /* SECP256K1_BENCH_H */ diff --git a/src/secp256k1/src/ecdsa.h b/src/secp256k1/src/ecdsa.h index 54ae101b92..80590c7cc8 100644 --- a/src/secp256k1/src/ecdsa.h +++ b/src/secp256k1/src/ecdsa.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECDSA_ -#define _SECP256K1_ECDSA_ +#ifndef SECP256K1_ECDSA_H +#define SECP256K1_ECDSA_H #include <stddef.h> @@ -18,4 +18,4 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); -#endif +#endif /* SECP256K1_ECDSA_H */ diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h index 453bb11880..c3400042d8 100644 --- a/src/secp256k1/src/ecdsa_impl.h +++ b/src/secp256k1/src/ecdsa_impl.h @@ -5,8 +5,8 @@ **********************************************************************/ -#ifndef _SECP256K1_ECDSA_IMPL_H_ -#define _SECP256K1_ECDSA_IMPL_H_ +#ifndef SECP256K1_ECDSA_IMPL_H +#define SECP256K1_ECDSA_IMPL_H #include "scalar.h" #include "field.h" @@ -81,8 +81,6 @@ static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned cha return -1; } while (lenleft > 0) { - if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) { - } ret = (ret << 8) | **sigp; if (ret + lenleft > (size_t)(sigend - *sigp)) { /* Result exceeds the length of the passed array. */ @@ -312,4 +310,4 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec return 1; } -#endif +#endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h index 42739a3bea..b621f1e6c3 100644 --- a/src/secp256k1/src/eckey.h +++ b/src/secp256k1/src/eckey.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_ -#define _SECP256K1_ECKEY_ +#ifndef SECP256K1_ECKEY_H +#define SECP256K1_ECKEY_H #include <stddef.h> @@ -22,4 +22,4 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); -#endif +#endif /* SECP256K1_ECKEY_H */ diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h index ce38071ac2..1ab9a68ec0 100644 --- a/src/secp256k1/src/eckey_impl.h +++ b/src/secp256k1/src/eckey_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_IMPL_H_ -#define _SECP256K1_ECKEY_IMPL_H_ +#ifndef SECP256K1_ECKEY_IMPL_H +#define SECP256K1_ECKEY_IMPL_H #include "eckey.h" @@ -15,16 +15,17 @@ #include "ecmult_gen.h" static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { - if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { + if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { secp256k1_fe x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); + return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { secp256k1_fe x, y; if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { return 0; } secp256k1_ge_set_xy(elem, &x, &y); - if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) && + secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { return 0; } return secp256k1_ge_is_valid_var(elem); @@ -42,10 +43,10 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p secp256k1_fe_get_b32(&pub[1], &elem->x); if (compressed) { *size = 33; - pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; } else { *size = 65; - pub[0] = 0x04; + pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED; secp256k1_fe_get_b32(&pub[33], &elem->y); } return 1; @@ -96,4 +97,4 @@ static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, return 1; } -#endif +#endif /* SECP256K1_ECKEY_IMPL_H */ diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index 20484134f5..6d44aba60b 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_ -#define _SECP256K1_ECMULT_ +#ifndef SECP256K1_ECMULT_H +#define SECP256K1_ECMULT_H #include "num.h" #include "group.h" @@ -28,4 +28,4 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx /** Double multiply: R = na*A + ng*G */ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); -#endif +#endif /* SECP256K1_ECMULT_H */ diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h index 2b0097655c..72bf7d7582 100644 --- a/src/secp256k1/src/ecmult_const.h +++ b/src/secp256k1/src/ecmult_const.h @@ -4,12 +4,12 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_CONST_ -#define _SECP256K1_ECMULT_CONST_ +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H #include "scalar.h" #include "group.h" static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); -#endif +#endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index 0db314c48e..7d7a172b7b 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_CONST_IMPL_ -#define _SECP256K1_ECMULT_CONST_IMPL_ +#ifndef SECP256K1_ECMULT_CONST_IMPL_H +#define SECP256K1_ECMULT_CONST_IMPL_H #include "scalar.h" #include "group.h" @@ -42,11 +42,12 @@ } while(0) -/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val) - * with the following guarantees: +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) * - each wnaf[i] is nonzero - * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w + * - the number of words set is always WNAF_SIZE(w) + 1 * * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) @@ -236,4 +237,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons } } -#endif +#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h index eb2cc9ead6..7564b7015f 100644 --- a/src/secp256k1/src/ecmult_gen.h +++ b/src/secp256k1/src/ecmult_gen.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_ -#define _SECP256K1_ECMULT_GEN_ +#ifndef SECP256K1_ECMULT_GEN_H +#define SECP256K1_ECMULT_GEN_H #include "scalar.h" #include "group.h" @@ -40,4 +40,4 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp25 static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); -#endif +#endif /* SECP256K1_ECMULT_GEN_H */ diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h index 35f2546077..9615b932dd 100644 --- a/src/secp256k1/src/ecmult_gen_impl.h +++ b/src/secp256k1/src/ecmult_gen_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ -#define _SECP256K1_ECMULT_GEN_IMPL_H_ +#ifndef SECP256K1_ECMULT_GEN_IMPL_H +#define SECP256K1_ECMULT_GEN_IMPL_H #include "scalar.h" #include "group.h" @@ -207,4 +207,4 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_gej_clear(&gb); } -#endif +#endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index 4e40104ad4..93d3794cb4 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_IMPL_H_ -#define _SECP256K1_ECMULT_IMPL_H_ +#ifndef SECP256K1_ECMULT_IMPL_H +#define SECP256K1_ECMULT_IMPL_H #include <string.h> @@ -403,4 +403,4 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej } } -#endif +#endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index bbb1ee866c..bb6692ad57 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_ -#define _SECP256K1_FIELD_ +#ifndef SECP256K1_FIELD_H +#define SECP256K1_FIELD_H /** Field element module. * @@ -129,4 +129,4 @@ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_f /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); -#endif +#endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h index 61ee1e0965..727c5267fb 100644 --- a/src/secp256k1/src/field_10x26.h +++ b/src/secp256k1/src/field_10x26.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include <stdint.h> @@ -44,4 +44,5 @@ typedef struct { #define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} #define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] -#endif + +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 234c13a644..94f8132fc8 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #include "util.h" #include "num.h" @@ -1158,4 +1158,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h index 8e69a560dc..bccd8feb4d 100644 --- a/src/secp256k1/src/field_5x52.h +++ b/src/secp256k1/src/field_5x52.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include <stdint.h> @@ -44,4 +44,4 @@ typedef struct { (d6) | (((uint64_t)(d7)) << 32) \ }} -#endif +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h index 98cc004bf0..1fc3171f6b 100644 --- a/src/secp256k1/src/field_5x52_asm_impl.h +++ b/src/secp256k1/src/field_5x52_asm_impl.h @@ -11,8 +11,8 @@ * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly */ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { /** @@ -499,4 +499,4 @@ __asm__ __volatile__( ); } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index 8e8b286baf..957c61b014 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -493,4 +493,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h index 0bf22bdd3e..95a0d1791c 100644 --- a/src/secp256k1/src/field_5x52_int128_impl.h +++ b/src/secp256k1/src/field_5x52_int128_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H #include <stdint.h> @@ -274,4 +274,4 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index 5127b279bc..20428648af 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_IMPL_H_ -#define _SECP256K1_FIELD_IMPL_H_ +#ifndef SECP256K1_FIELD_IMPL_H +#define SECP256K1_FIELD_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -312,4 +312,4 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { #endif } -#endif +#endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h index 4957b248fe..ea1302deb8 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_ -#define _SECP256K1_GROUP_ +#ifndef SECP256K1_GROUP_H +#define SECP256K1_GROUP_H #include "num.h" #include "field.h" @@ -141,4 +141,4 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); -#endif +#endif /* SECP256K1_GROUP_H */ diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index 7d723532ff..b31b6c12ef 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_IMPL_H_ -#define _SECP256K1_GROUP_IMPL_H_ +#ifndef SECP256K1_GROUP_IMPL_H +#define SECP256K1_GROUP_IMPL_H #include "num.h" #include "field.h" @@ -697,4 +697,4 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { return secp256k1_fe_is_quad_var(&yz); } -#endif +#endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h index fca98cab9f..e08d25d225 100644 --- a/src/secp256k1/src/hash.h +++ b/src/secp256k1/src/hash.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_ -#define _SECP256K1_HASH_ +#ifndef SECP256K1_HASH_H +#define SECP256K1_HASH_H #include <stdlib.h> #include <stdint.h> @@ -38,4 +38,4 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); -#endif +#endif /* SECP256K1_HASH_H */ diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h index b47e65f830..4c9964ee06 100644 --- a/src/secp256k1/src/hash_impl.h +++ b/src/secp256k1/src/hash_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_IMPL_H_ -#define _SECP256K1_HASH_IMPL_H_ +#ifndef SECP256K1_HASH_IMPL_H +#define SECP256K1_HASH_IMPL_H #include "hash.h" @@ -278,4 +278,4 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 #undef Maj #undef Ch -#endif +#endif /* SECP256K1_HASH_IMPL_H */ diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h index 9e30fb73dd..01ecba4d53 100644 --- a/src/secp256k1/src/modules/ecdh/main_impl.h +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_ECDH_MAIN_ -#define _SECP256K1_MODULE_ECDH_MAIN_ +#ifndef SECP256K1_MODULE_ECDH_MAIN_H +#define SECP256K1_MODULE_ECDH_MAIN_H #include "include/secp256k1_ecdh.h" #include "ecmult_const_impl.h" @@ -51,4 +51,4 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se return ret; } -#endif +#endif /* SECP256K1_MODULE_ECDH_MAIN_H */ diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h index 85a5d0a9a6..cec30b67c6 100644 --- a/src/secp256k1/src/modules/ecdh/tests_impl.h +++ b/src/secp256k1/src/modules/ecdh/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_ECDH_TESTS_ -#define _SECP256K1_MODULE_ECDH_TESTS_ +#ifndef SECP256K1_MODULE_ECDH_TESTS_H +#define SECP256K1_MODULE_ECDH_TESTS_H void test_ecdh_api(void) { /* Setup context that just counts errors */ @@ -102,4 +102,4 @@ void run_ecdh_tests(void) { test_bad_scalar(); } -#endif +#endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h index c6fbe23981..2f6691c5a1 100755 --- a/src/secp256k1/src/modules/recovery/main_impl.h +++ b/src/secp256k1/src/modules/recovery/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_ -#define _SECP256K1_MODULE_RECOVERY_MAIN_ +#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H +#define SECP256K1_MODULE_RECOVERY_MAIN_H #include "include/secp256k1_recovery.h" @@ -190,4 +190,4 @@ int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubk } } -#endif +#endif /* SECP256K1_MODULE_RECOVERY_MAIN_H */ diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h index 765c7dd81e..5c9bbe8610 100644 --- a/src/secp256k1/src/modules/recovery/tests_impl.h +++ b/src/secp256k1/src/modules/recovery/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_ -#define _SECP256K1_MODULE_RECOVERY_TESTS_ +#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H +#define SECP256K1_MODULE_RECOVERY_TESTS_H static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { (void) msg32; @@ -390,4 +390,4 @@ void run_recovery_tests(void) { test_ecdsa_recovery_edge_cases(); } -#endif +#endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */ diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h index 7bb9c5be8c..49f2dd791d 100644 --- a/src/secp256k1/src/num.h +++ b/src/secp256k1/src/num.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_ -#define _SECP256K1_NUM_ +#ifndef SECP256K1_NUM_H +#define SECP256K1_NUM_H #ifndef USE_NUM_NONE @@ -71,4 +71,4 @@ static void secp256k1_num_negate(secp256k1_num *r); #endif -#endif +#endif /* SECP256K1_NUM_H */ diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h index 7dd813088a..3619844bd5 100644 --- a/src/secp256k1/src/num_gmp.h +++ b/src/secp256k1/src/num_gmp.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_ -#define _SECP256K1_NUM_REPR_ +#ifndef SECP256K1_NUM_REPR_H +#define SECP256K1_NUM_REPR_H #include <gmp.h> @@ -17,4 +17,4 @@ typedef struct { int limbs; } secp256k1_num; -#endif +#endif /* SECP256K1_NUM_REPR_H */ diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h index 3a46495eea..0ae2a8ba0e 100644 --- a/src/secp256k1/src/num_gmp_impl.h +++ b/src/secp256k1/src/num_gmp_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_IMPL_H_ -#define _SECP256K1_NUM_REPR_IMPL_H_ +#ifndef SECP256K1_NUM_REPR_IMPL_H +#define SECP256K1_NUM_REPR_IMPL_H #include <string.h> #include <stdlib.h> @@ -285,4 +285,4 @@ static void secp256k1_num_negate(secp256k1_num *r) { r->neg ^= 1; } -#endif +#endif /* SECP256K1_NUM_REPR_IMPL_H */ diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h index 0b0e3a072a..c45193b033 100644 --- a/src/secp256k1/src/num_impl.h +++ b/src/secp256k1/src/num_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_IMPL_H_ -#define _SECP256K1_NUM_IMPL_H_ +#ifndef SECP256K1_NUM_IMPL_H +#define SECP256K1_NUM_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -21,4 +21,4 @@ #error "Please select num implementation" #endif -#endif +#endif /* SECP256K1_NUM_IMPL_H */ diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h index 27e9d8375e..59304cb66e 100644 --- a/src/secp256k1/src/scalar.h +++ b/src/secp256k1/src/scalar.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_ -#define _SECP256K1_SCALAR_ +#ifndef SECP256K1_SCALAR_H +#define SECP256K1_SCALAR_H #include "num.h" @@ -103,4 +103,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); -#endif +#endif /* SECP256K1_SCALAR_H */ diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h index cff406038f..19c7495d1c 100644 --- a/src/secp256k1/src/scalar_4x64.h +++ b/src/secp256k1/src/scalar_4x64.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include <stdint.h> @@ -16,4 +16,4 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index 56e7bd82af..db1ebf94be 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) @@ -946,4 +946,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h index 1319664f65..2c9a348e24 100644 --- a/src/secp256k1/src/scalar_8x32.h +++ b/src/secp256k1/src/scalar_8x32.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include <stdint.h> @@ -16,4 +16,4 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index aae4f35c08..4f9ed61fea 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) @@ -718,4 +718,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index 2690d86558..fa790570ff 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_IMPL_H_ -#define _SECP256K1_SCALAR_IMPL_H_ +#ifndef SECP256K1_SCALAR_IMPL_H +#define SECP256K1_SCALAR_IMPL_H #include "group.h" #include "scalar.h" @@ -330,4 +330,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar #endif #endif -#endif +#endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h index 5574c44c7a..5836febc5b 100644 --- a/src/secp256k1/src/scalar_low.h +++ b/src/secp256k1/src/scalar_low.h @@ -4,12 +4,12 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include <stdint.h> /** A scalar modulo the group order of the secp256k1 curve. */ typedef uint32_t secp256k1_scalar; -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h index 4f94441f49..c80e70c5a2 100644 --- a/src/secp256k1/src/scalar_low_impl.h +++ b/src/secp256k1/src/scalar_low_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H #include "scalar.h" @@ -111,4 +111,4 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h index f8efa93c7c..f1f9be077e 100644 --- a/src/secp256k1/src/testrand.h +++ b/src/secp256k1/src/testrand.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_H_ -#define _SECP256K1_TESTRAND_H_ +#ifndef SECP256K1_TESTRAND_H +#define SECP256K1_TESTRAND_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -35,4 +35,4 @@ static void secp256k1_rand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); -#endif +#endif /* SECP256K1_TESTRAND_H */ diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h index 15c7b9f12d..1255574209 100644 --- a/src/secp256k1/src/testrand_impl.h +++ b/src/secp256k1/src/testrand_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_IMPL_H_ -#define _SECP256K1_TESTRAND_IMPL_H_ +#ifndef SECP256K1_TESTRAND_IMPL_H +#define SECP256K1_TESTRAND_IMPL_H #include <stdint.h> #include <string.h> @@ -107,4 +107,4 @@ static void secp256k1_rand256_test(unsigned char *b32) { secp256k1_rand_bytes_test(b32, 32); } -#endif +#endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index 4092a86c91..b0441d8e30 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_UTIL_H_ -#define _SECP256K1_UTIL_H_ +#ifndef SECP256K1_UTIL_H +#define SECP256K1_UTIL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -110,4 +110,4 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; #endif -#endif +#endif /* SECP256K1_UTIL_H */ diff --git a/src/serialize.h b/src/serialize.h index eeb05fa76c..02d3e4f7c6 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SERIALIZE_H #define BITCOIN_SERIALIZE_H -#include "compat/endian.h" +#include <compat/endian.h> #include <algorithm> #include <assert.h> @@ -21,7 +21,7 @@ #include <utility> #include <vector> -#include "prevector.h" +#include <prevector.h> static const unsigned int MAX_SIZE = 0x02000000; @@ -732,8 +732,8 @@ template<typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream& os, const std::map<K, T, Pred, A>& m) { WriteCompactSize(os, m.size()); - for (typename std::map<K, T, Pred, A>::const_iterator mi = m.begin(); mi != m.end(); ++mi) - Serialize(os, (*mi)); + for (const auto& entry : m) + Serialize(os, entry); } template<typename Stream, typename K, typename T, typename Pred, typename A> diff --git a/src/streams.h b/src/streams.h index 159847279d..a212691f6d 100644 --- a/src/streams.h +++ b/src/streams.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_STREAMS_H #define BITCOIN_STREAMS_H -#include "support/allocators/zeroafterfree.h" -#include "serialize.h" +#include <support/allocators/zeroafterfree.h> +#include <serialize.h> #include <algorithm> #include <assert.h> @@ -345,18 +345,16 @@ public: // Read from the beginning of the buffer unsigned int nReadPosNext = nReadPos + nSize; - if (nReadPosNext >= vch.size()) + if (nReadPosNext > vch.size()) { + throw std::ios_base::failure("CDataStream::read(): end of data"); + } + memcpy(pch, &vch[nReadPos], nSize); + if (nReadPosNext == vch.size()) { - if (nReadPosNext > vch.size()) - { - throw std::ios_base::failure("CDataStream::read(): end of data"); - } - memcpy(pch, &vch[nReadPos], nSize); nReadPos = 0; vch.clear(); return; } - memcpy(pch, &vch[nReadPos], nSize); nReadPos = nReadPosNext; } @@ -455,10 +453,6 @@ public: class CAutoFile { private: - // Disallow copies - CAutoFile(const CAutoFile&); - CAutoFile& operator=(const CAutoFile&); - const int nType; const int nVersion; @@ -475,6 +469,10 @@ public: fclose(); } + // Disallow copies + CAutoFile(const CAutoFile&) = delete; + CAutoFile& operator=(const CAutoFile&) = delete; + void fclose() { if (file) { @@ -564,10 +562,6 @@ public: class CBufferedFile { private: - // Disallow copies - CBufferedFile(const CBufferedFile&); - CBufferedFile& operator=(const CBufferedFile&); - const int nType; const int nVersion; @@ -609,6 +603,10 @@ public: fclose(); } + // Disallow copies + CBufferedFile(const CBufferedFile&) = delete; + CBufferedFile& operator=(const CBufferedFile&) = delete; + int GetVersion() const { return nVersion; } int GetType() const { return nType; } diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h index 39347c73bb..7cd031bfe8 100644 --- a/src/support/allocators/secure.h +++ b/src/support/allocators/secure.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_SUPPORT_ALLOCATORS_SECURE_H #define BITCOIN_SUPPORT_ALLOCATORS_SECURE_H -#include "support/lockedpool.h" -#include "support/cleanse.h" +#include <support/lockedpool.h> +#include <support/cleanse.h> #include <string> diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h index 618874ceee..23ef96c13f 100644 --- a/src/support/allocators/zeroafterfree.h +++ b/src/support/allocators/zeroafterfree.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H #define BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H -#include "support/cleanse.h" +#include <support/cleanse.h> #include <memory> #include <vector> diff --git a/src/support/cleanse.cpp b/src/support/cleanse.cpp index a2141b2449..82cdfe707b 100644 --- a/src/support/cleanse.cpp +++ b/src/support/cleanse.cpp @@ -3,11 +3,37 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "cleanse.h" +#include <support/cleanse.h> -#include <openssl/crypto.h> +#include <cstring> +/* Compilers have a bad habit of removing "superfluous" memset calls that + * are trying to zero memory. For example, when memset()ing a buffer and + * then free()ing it, the compiler might decide that the memset is + * unobservable and thus can be removed. + * + * Previously we used OpenSSL which tried to stop this by a) implementing + * memset in assembly on x86 and b) putting the function in its own file + * for other platforms. + * + * This change removes those tricks in favour of using asm directives to + * scare the compiler away. As best as our compiler folks can tell, this is + * sufficient and will continue to be so. + * + * Adam Langley <agl@google.com> + * Commit: ad1907fe73334d6c696c8539646c21b11178f20f + * BoringSSL (LICENSE: ISC) + */ void memory_cleanse(void *ptr, size_t len) { - OPENSSL_cleanse(ptr, len); + std::memset(ptr, 0, len); + + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ +#if defined(_MSC_VER) + __asm; +#else + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif } diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 2ead72185f..98e8694181 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "support/lockedpool.h" -#include "support/cleanse.h" +#include <support/lockedpool.h> +#include <support/cleanse.h> #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #ifdef WIN32 diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index cecbdec1aa..834f0371e2 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -50,6 +50,9 @@ public: Arena(void *base, size_t size, size_t alignment); virtual ~Arena(); + Arena(const Arena& other) = delete; // non construction-copyable + Arena& operator=(const Arena&) = delete; // non copyable + /** Memory statistics. */ struct Stats { @@ -85,9 +88,6 @@ public: */ bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; } private: - Arena(const Arena& other) = delete; // non construction-copyable - Arena& operator=(const Arena&) = delete; // non copyable - /** Map of chunk address to chunk information. This class makes use of the * sorted order to merge previous and next chunks during deallocation. */ @@ -153,6 +153,9 @@ public: explicit LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); + LockedPool(const LockedPool& other) = delete; // non construction-copyable + LockedPool& operator=(const LockedPool&) = delete; // non copyable + /** Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or * the application tried to allocate 0 bytes. @@ -168,9 +171,6 @@ public: /** Get pool usage statistics */ Stats stats() const; private: - LockedPool(const LockedPool& other) = delete; // non construction-copyable - LockedPool& operator=(const LockedPool&) = delete; // non copyable - std::unique_ptr<LockedPageAllocator> allocator; /** Create an arena from locked pages */ diff --git a/src/sync.cpp b/src/sync.cpp index 9c351ea487..3f51383ea2 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -2,16 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "sync.h" +#include <sync.h> -#include "util.h" -#include "utilstrencodings.h" +#include <set> +#include <util.h> +#include <utilstrencodings.h> #include <stdio.h> -#include <boost/thread.hpp> - #ifdef DEBUG_LOCKCONTENTION +#if !defined(HAVE_THREAD_LOCAL) +static_assert(false, "thread_local is not supported"); +#endif void PrintLockContention(const char* pszName, const char* pszFile, int nLine) { LogPrintf("LOCKCONTENTION: %s\n", pszName); @@ -45,8 +47,8 @@ struct CLockLocation { return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : ""); } - bool fTry; private: + bool fTry; std::string mutexName; std::string sourceFile; int sourceLine; @@ -67,10 +69,10 @@ struct LockData { LockOrders lockorders; InvLockOrders invlockorders; - boost::mutex dd_mutex; + std::mutex dd_mutex; } static lockdata; -boost::thread_specific_ptr<LockStack> lockstack; +static thread_local std::unique_ptr<LockStack> lockstack; static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) { @@ -98,14 +100,14 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, assert(false); } -static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +static void push_lock(void* c, const CLockLocation& locklocation) { - if (lockstack.get() == nullptr) + if (!lockstack) lockstack.reset(new LockStack); - boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex); + std::lock_guard<std::mutex> lock(lockdata.dd_mutex); - (*lockstack).push_back(std::make_pair(c, locklocation)); + lockstack->push_back(std::make_pair(c, locklocation)); for (const std::pair<void*, CLockLocation> & i : (*lockstack)) { if (i.first == c) @@ -130,7 +132,7 @@ static void pop_lock() void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) { - push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry), fTry); + push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry)); } void LeaveCritical() @@ -155,13 +157,23 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, abort(); } +void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) +{ + for (const std::pair<void*, CLockLocation>& i : *lockstack) { + if (i.first == cs) { + fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); + abort(); + } + } +} + void DeleteLock(void* cs) { if (!lockdata.available) { // We're already shutting down. return; } - boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex); + std::lock_guard<std::mutex> lock(lockdata.dd_mutex); std::pair<void*, void*> item = std::make_pair(cs, nullptr); LockOrders::iterator it = lockdata.lockorders.lower_bound(item); while (it != lockdata.lockorders.end() && it->first.first == cs) { diff --git a/src/sync.h b/src/sync.h index 0871c5fb4d..3af27c65d0 100644 --- a/src/sync.h +++ b/src/sync.h @@ -6,11 +6,11 @@ #ifndef BITCOIN_SYNC_H #define BITCOIN_SYNC_H -#include "threadsafety.h" +#include <threadsafety.h> -#include <boost/thread/condition_variable.hpp> -#include <boost/thread/mutex.hpp> -#include <boost/thread/recursive_mutex.hpp> +#include <condition_variable> +#include <thread> +#include <mutex> //////////////////////////////////////////////// @@ -21,17 +21,17 @@ /* CCriticalSection mutex; - boost::recursive_mutex mutex; + std::recursive_mutex mutex; LOCK(mutex); - boost::unique_lock<boost::recursive_mutex> criticalblock(mutex); + std::unique_lock<std::recursive_mutex> criticalblock(mutex); LOCK2(mutex1, mutex2); - boost::unique_lock<boost::recursive_mutex> criticalblock1(mutex1); - boost::unique_lock<boost::recursive_mutex> criticalblock2(mutex2); + std::unique_lock<std::recursive_mutex> criticalblock1(mutex1); + std::unique_lock<std::recursive_mutex> criticalblock2(mutex2); TRY_LOCK(mutex, name); - boost::unique_lock<boost::recursive_mutex> name(mutex, boost::try_to_lock_t); + std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t); ENTER_CRITICAL_SECTION(mutex); // no RAII mutex.lock(); @@ -75,20 +75,23 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs void LeaveCritical(); std::string LocksHeld(); void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); +void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); void DeleteLock(void* cs); #else void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} +void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} void static inline DeleteLock(void* cs) {} #endif #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) +#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs) /** - * Wrapped boost mutex: supports recursive locking, but no waiting + * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ -class CCriticalSection : public AnnotatedMixin<boost::recursive_mutex> +class CCriticalSection : public AnnotatedMixin<std::recursive_mutex> { public: ~CCriticalSection() { @@ -96,22 +99,24 @@ public: } }; -/** Wrapped boost mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection; +/** Wrapped mutex: supports waiting but not recursive locking */ +typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection; -/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ -typedef boost::condition_variable CConditionVariable; +/** Just a typedef for std::condition_variable, can be wrapped later if desired */ +typedef std::condition_variable CConditionVariable; + +/** Just a typedef for std::unique_lock, can be wrapped later if desired */ +typedef std::unique_lock<std::mutex> WaitableLock; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif -/** Wrapper around boost::unique_lock<Mutex> */ -template <typename Mutex> -class SCOPED_LOCKABLE CMutexLock +/** Wrapper around std::unique_lock<CCriticalSection> */ +class SCOPED_LOCKABLE CCriticalBlock { private: - boost::unique_lock<Mutex> lock; + std::unique_lock<CCriticalSection> lock; void Enter(const char* pszName, const char* pszFile, int nLine) { @@ -136,7 +141,7 @@ private: } public: - CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock) + CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock) { if (fTry) TryEnter(pszName, pszFile, nLine); @@ -144,18 +149,18 @@ public: Enter(pszName, pszFile, nLine); } - CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) + CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) return; - lock = boost::unique_lock<Mutex>(*pmutexIn, boost::defer_lock); + lock = std::unique_lock<CCriticalSection>(*pmutexIn, std::defer_lock); if (fTry) TryEnter(pszName, pszFile, nLine); else Enter(pszName, pszFile, nLine); } - ~CMutexLock() UNLOCK_FUNCTION() + ~CCriticalBlock() UNLOCK_FUNCTION() { if (lock.owns_lock()) LeaveCritical(); @@ -167,8 +172,6 @@ public: } }; -typedef CMutexLock<CCriticalSection> CCriticalBlock; - #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) @@ -191,8 +194,8 @@ typedef CMutexLock<CCriticalSection> CCriticalBlock; class CSemaphore { private: - boost::condition_variable condition; - boost::mutex mutex; + std::condition_variable condition; + std::mutex mutex; int value; public: @@ -200,16 +203,14 @@ public: void wait() { - boost::unique_lock<boost::mutex> lock(mutex); - while (value < 1) { - condition.wait(lock); - } + std::unique_lock<std::mutex> lock(mutex); + condition.wait(lock, [&]() { return value >= 1; }); value--; } bool try_wait() { - boost::unique_lock<boost::mutex> lock(mutex); + std::lock_guard<std::mutex> lock(mutex); if (value < 1) return false; value--; @@ -219,7 +220,7 @@ public: void post() { { - boost::unique_lock<boost::mutex> lock(mutex); + std::lock_guard<std::mutex> lock(mutex); value++; } condition.notify_one(); diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index b88ad5ed1b..6be176a1ad 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -4,17 +4,17 @@ // Unit tests for denial-of-service detection/prevention code -#include "chainparams.h" -#include "keystore.h" -#include "net.h" -#include "net_processing.h" -#include "pow.h" -#include "script/sign.h" -#include "serialize.h" -#include "util.h" -#include "validation.h" - -#include "test/test_bitcoin.h" +#include <chainparams.h> +#include <keystore.h> +#include <net.h> +#include <net_processing.h> +#include <pow.h> +#include <script/sign.h> +#include <serialize.h> +#include <util.h> +#include <validation.h> + +#include <test/test_bitcoin.h> #include <stdint.h> @@ -40,8 +40,141 @@ CService ip(uint32_t i) static NodeId id = 0; +void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds); + BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup) +// Test eviction of an outbound peer whose chain never advances +// Mock a node connection, and use mocktime to simulate a peer +// which never sends any headers messages. PeerLogic should +// decide to evict that outbound peer, after the appropriate timeouts. +// Note that we protect 4 outbound nodes from being subject to +// this logic; this test takes advantage of that protection only +// being applied to nodes which send headers with sufficient +// work. +BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) +{ + std::atomic<bool> interruptDummy(false); + + // Mock an outbound peer + CAddress addr1(ip(0xa0b0c001), NODE_NONE); + CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", /*fInboundIn=*/ false); + dummyNode1.SetSendVersion(PROTOCOL_VERSION); + + peerLogic->InitializeNode(&dummyNode1); + dummyNode1.nVersion = 1; + dummyNode1.fSuccessfullyConnected = true; + + // This test requires that we have a chain with non-zero work. + LOCK(cs_main); + BOOST_CHECK(chainActive.Tip() != nullptr); + BOOST_CHECK(chainActive.Tip()->nChainWork > 0); + + // Test starts here + LOCK(dummyNode1.cs_sendProcessing); + peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders + LOCK(dummyNode1.cs_vSend); + BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + dummyNode1.vSendMsg.clear(); + + int64_t nStartTime = GetTime(); + // Wait 21 minutes + SetMockTime(nStartTime+21*60); + peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in getheaders + BOOST_CHECK(dummyNode1.vSendMsg.size() > 0); + // Wait 3 more minutes + SetMockTime(nStartTime+24*60); + peerLogic->SendMessages(&dummyNode1, interruptDummy); // should result in disconnect + BOOST_CHECK(dummyNode1.fDisconnect == true); + SetMockTime(0); + + bool dummy; + peerLogic->FinalizeNode(dummyNode1.GetId(), dummy); +} + +void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic) +{ + CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE); + vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false)); + CNode &node = *vNodes.back(); + node.SetSendVersion(PROTOCOL_VERSION); + + peerLogic.InitializeNode(&node); + node.nVersion = 1; + node.fSuccessfullyConnected = true; + + CConnmanTest::AddNode(node); +} + +BOOST_AUTO_TEST_CASE(stale_tip_peer_management) +{ + const Consensus::Params& consensusParams = Params().GetConsensus(); + constexpr int nMaxOutbound = 8; + CConnman::Options options; + options.nMaxConnections = 125; + options.nMaxOutbound = nMaxOutbound; + options.nMaxFeeler = 1; + + connman->Init(options); + std::vector<CNode *> vNodes; + + // Mock some outbound peers + for (int i=0; i<nMaxOutbound; ++i) { + AddRandomOutboundPeer(vNodes, *peerLogic); + } + + peerLogic->CheckForStaleTipAndEvictPeers(consensusParams); + + // No nodes should be marked for disconnection while we have no extra peers + for (const CNode *node : vNodes) { + BOOST_CHECK(node->fDisconnect == false); + } + + SetMockTime(GetTime() + 3*consensusParams.nPowTargetSpacing + 1); + + // Now tip should definitely be stale, and we should look for an extra + // outbound peer + peerLogic->CheckForStaleTipAndEvictPeers(consensusParams); + BOOST_CHECK(connman->GetTryNewOutboundPeer()); + + // Still no peers should be marked for disconnection + for (const CNode *node : vNodes) { + BOOST_CHECK(node->fDisconnect == false); + } + + // If we add one more peer, something should get marked for eviction + // on the next check (since we're mocking the time to be in the future, the + // required time connected check should be satisfied). + AddRandomOutboundPeer(vNodes, *peerLogic); + + peerLogic->CheckForStaleTipAndEvictPeers(consensusParams); + for (int i=0; i<nMaxOutbound; ++i) { + BOOST_CHECK(vNodes[i]->fDisconnect == false); + } + // Last added node should get marked for eviction + BOOST_CHECK(vNodes.back()->fDisconnect == true); + + vNodes.back()->fDisconnect = false; + + // Update the last announced block time for the last + // peer, and check that the next newest node gets evicted. + UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime()); + + peerLogic->CheckForStaleTipAndEvictPeers(consensusParams); + for (int i=0; i<nMaxOutbound-1; ++i) { + BOOST_CHECK(vNodes[i]->fDisconnect == false); + } + BOOST_CHECK(vNodes[nMaxOutbound-1]->fDisconnect == true); + BOOST_CHECK(vNodes.back()->fDisconnect == false); + + bool dummy; + for (const CNode *node : vNodes) { + peerLogic->FinalizeNode(node->GetId(), dummy); + } + + CConnmanTest::ClearNodes(); +} + BOOST_AUTO_TEST_CASE(DoS_banning) { std::atomic<bool> interruptDummy(false); @@ -53,7 +186,11 @@ BOOST_AUTO_TEST_CASE(DoS_banning) peerLogic->InitializeNode(&dummyNode1); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; - Misbehaving(dummyNode1.GetId(), 100); // Should get banned + { + LOCK(cs_main); + Misbehaving(dummyNode1.GetId(), 100); // Should get banned + } + LOCK(dummyNode1.cs_sendProcessing); peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned @@ -64,13 +201,24 @@ BOOST_AUTO_TEST_CASE(DoS_banning) peerLogic->InitializeNode(&dummyNode2); dummyNode2.nVersion = 1; dummyNode2.fSuccessfullyConnected = true; - Misbehaving(dummyNode2.GetId(), 50); + { + LOCK(cs_main); + Misbehaving(dummyNode2.GetId(), 50); + } + LOCK(dummyNode2.cs_sendProcessing); peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be - Misbehaving(dummyNode2.GetId(), 50); + { + LOCK(cs_main); + Misbehaving(dummyNode2.GetId(), 50); + } peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(connman->IsBanned(addr2)); + + bool dummy; + peerLogic->FinalizeNode(dummyNode1.GetId(), dummy); + peerLogic->FinalizeNode(dummyNode2.GetId(), dummy); } BOOST_AUTO_TEST_CASE(DoS_banscore) @@ -85,16 +233,29 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) peerLogic->InitializeNode(&dummyNode1); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; - Misbehaving(dummyNode1.GetId(), 100); + { + LOCK(cs_main); + Misbehaving(dummyNode1.GetId(), 100); + } + LOCK(dummyNode1.cs_sendProcessing); peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); - Misbehaving(dummyNode1.GetId(), 10); + { + LOCK(cs_main); + Misbehaving(dummyNode1.GetId(), 10); + } peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); - Misbehaving(dummyNode1.GetId(), 1); + { + LOCK(cs_main); + Misbehaving(dummyNode1.GetId(), 1); + } peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); + + bool dummy; + peerLogic->FinalizeNode(dummyNode1.GetId(), dummy); } BOOST_AUTO_TEST_CASE(DoS_bantime) @@ -112,7 +273,11 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) dummyNode.nVersion = 1; dummyNode.fSuccessfullyConnected = true; - Misbehaving(dummyNode.GetId(), 100); + { + LOCK(cs_main); + Misbehaving(dummyNode.GetId(), 100); + } + LOCK(dummyNode.cs_sendProcessing); peerLogic->SendMessages(&dummyNode, interruptDummy); BOOST_CHECK(connman->IsBanned(addr)); @@ -121,11 +286,15 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) SetMockTime(nStartTime+60*60*24+1); BOOST_CHECK(!connman->IsBanned(addr)); + + bool dummy; + peerLogic->FinalizeNode(dummyNode.GetId(), dummy); } CTransactionRef RandomOrphan() { std::map<uint256, COrphanTx>::iterator it; + LOCK(cs_main); it = mapOrphanTransactions.lower_bound(InsecureRand256()); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); @@ -195,6 +364,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i)); } + LOCK(cs_main); // Test EraseOrphansFor: for (NodeId i = 0; i < 3; i++) { diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 7be29c6d6b..609b1a05d3 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -1,14 +1,14 @@ // Copyright (c) 2012-2016 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 "addrman.h" -#include "test/test_bitcoin.h" +#include <addrman.h> +#include <test/test_bitcoin.h> #include <string> #include <boost/test/unit_test.hpp> -#include "hash.h" -#include "netbase.h" -#include "random.h" +#include <hash.h> +#include <netbase.h> +#include <random.h> class CAddrManTest : public CAddrMan { diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index 4a533b5bf2..83fa3b76be 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "util.h" +#include <util.h> -#include "support/allocators/secure.h" -#include "test/test_bitcoin.h" +#include <support/allocators/secure.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp index 952cf901f0..7ebbd22c5d 100644 --- a/src/test/amount_tests.cpp +++ b/src/test/amount_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "amount.h" -#include "policy/feerate.h" -#include "test/test_bitcoin.h" +#include <amount.h> +#include <policy/feerate.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp index 2c98fbcfd6..eb3148a1de 100644 --- a/src/test/arith_uint256_tests.cpp +++ b/src/test/arith_uint256_tests.cpp @@ -8,11 +8,11 @@ #include <iomanip> #include <limits> #include <cmath> -#include "uint256.h" -#include "arith_uint256.h" +#include <uint256.h> +#include <arith_uint256.h> #include <string> -#include "version.h" -#include "test/test_bitcoin.h" +#include <version.h> +#include <test/test_bitcoin.h> BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup) diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp index 6422b3a88f..abf236b953 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 4829590c54..3156cc53d0 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -2,22 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "base58.h" +#include <base58.h> -#include "data/base58_encode_decode.json.h" -#include "data/base58_keys_invalid.json.h" -#include "data/base58_keys_valid.json.h" +#include <test/data/base58_encode_decode.json.h> +#include <test/data/base58_keys_invalid.json.h> +#include <test/data/base58_keys_valid.json.h> -#include "key.h" -#include "script/script.h" -#include "uint256.h" -#include "util.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <key.h> +#include <script/script.h> +#include <test/test_bitcoin.h> +#include <uint256.h> +#include <util.h> +#include <utilstrencodings.h> + +#include <univalue.h> #include <boost/test/unit_test.hpp> -#include <univalue.h> extern UniValue read_json(const std::string& jsondata); @@ -72,50 +73,6 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); } -// Visitor to check address type -class TestAddrTypeVisitor : public boost::static_visitor<bool> -{ -private: - std::string exp_addrType; -public: - explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } - bool operator()(const CKeyID &id) const - { - return (exp_addrType == "pubkey"); - } - bool operator()(const CScriptID &id) const - { - return (exp_addrType == "script"); - } - bool operator()(const CNoDestination &no) const - { - return (exp_addrType == "none"); - } -}; - -// Visitor to check address payload -class TestPayloadVisitor : public boost::static_visitor<bool> -{ -private: - std::vector<unsigned char> exp_payload; -public: - explicit TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { } - bool operator()(const CKeyID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CScriptID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CNoDestination &no) const - { - return exp_payload.size() == 0; - } -}; - // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { @@ -127,8 +84,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; std::string strTest = test.write(); - if (test.size() < 3) // Allow for extra stuff (useful for comments) - { + if (test.size() < 3) { // Allow for extra stuff (useful for comments) BOOST_ERROR("Bad test: " << strTest); continue; } @@ -136,13 +92,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) - SelectParams(CBaseChainParams::TESTNET); - else - SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + SelectParams(find_value(metadata, "chain").get_str()); + bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool(); + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); @@ -154,15 +106,27 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) // Private key must be invalid public key destination = DecodeDestination(exp_base58string); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" + } else { // Must be valid public key destination = DecodeDestination(exp_base58string); + CScript script = GetScriptForDestination(destination); BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); - BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + + // Try flipped case version + for (char& c : exp_base58string) { + if (c >= 'a' && c <= 'z') { + c = (c - 'a') + 'A'; + } else if (c >= 'A' && c <= 'Z') { + c = (c - 'A') + 'a'; + } + } + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); + if (IsValidDestination(destination)) { + script = GetScriptForDestination(destination); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + } // Public key must be invalid private key secret.SetString(exp_base58string); @@ -188,13 +152,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) - SelectParams(CBaseChainParams::TESTNET); - else - SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + SelectParams(find_value(metadata, "chain").get_str()); + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); CKey key; key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); @@ -202,36 +161,20 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CBitcoinSecret secret; secret.SetKey(key); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); + } else { CTxDestination dest; - if(exp_addrType == "pubkey") - { - dest = CKeyID(uint160(exp_payload)); - } - else if(exp_addrType == "script") - { - dest = CScriptID(uint160(exp_payload)); - } - else if(exp_addrType == "none") - { - dest = CNoDestination(); - } - else - { - BOOST_ERROR("Bad addrtype: " << strTest); - continue; - } + CScript exp_script(exp_payload.begin(), exp_payload.end()); + ExtractDestination(exp_script, dest); std::string address = EncodeDestination(dest); - BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest); + + BOOST_CHECK_EQUAL(address, exp_base58string); } } SelectParams(CBaseChainParams::MAIN); } + // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { @@ -250,13 +193,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - destination = DecodeDestination(exp_base58string); - BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); - secret.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); + for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { + SelectParams(chain); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); + secret.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest); + } } } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp index ccad94d946..46b3a04f35 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp new file mode 100644 index 0000000000..495290c8d9 --- /dev/null +++ b/src/test/bech32_tests.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bech32.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup) + +bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2) +{ + if (s1.size() != s2.size()) return false; + for (size_t i = 0; i < s1.size(); ++i) { + char c1 = s1[i]; + if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a'); + char c2 = s2[i]; + if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a'); + if (c1 != c2) return false; + } + return true; +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_valid) +{ + static const std::string CASES[] = { + "A12UEL5L", + "a12uel5l", + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + "?1ezyfcl", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(!ret.first.empty()); + std::string recode = bech32::Encode(ret.first, ret.second); + BOOST_CHECK(!recode.empty()); + BOOST_CHECK(CaseInsensitiveEqual(str, recode)); + } +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) +{ + static const std::string CASES[] = { + " 1nwldj5", + "\x7f""1axkwrx", + "\x80""1eym55h", + "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", + "pzry9x0s0muk", + "1pzry9x0s0muk", + "x1b4n0q5v", + "li1dgmt3", + "de1lg7wt\xff", + "A1G7SGD8", + "10a06t8", + "1qzzfhee", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(ret.first.empty()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index e123c26ad0..e3e727439b 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -4,12 +4,12 @@ #include <boost/test/unit_test.hpp> -#include "base58.h" -#include "key.h" -#include "uint256.h" -#include "util.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <base58.h> +#include <key.h> +#include <uint256.h> +#include <util.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <string> #include <vector> diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index f2d5b385d0..718b5bdfb9 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "blockencodings.h" -#include "consensus/merkle.h" -#include "chainparams.h" -#include "random.h" +#include <blockencodings.h> +#include <consensus/merkle.h> +#include <chainparams.h> +#include <random.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> @@ -62,6 +62,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) CBlock block(BuildBlockTestCase()); pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2])); + LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); // Do a simple ShortTxIDs RT @@ -161,6 +162,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) CBlock block(BuildBlockTestCase()); pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2])); + LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); uint256 txhash; @@ -227,6 +229,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) CBlock block(BuildBlockTestCase()); pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1])); + LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); uint256 txhash; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 9274ceefcb..fdd30be0b1 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -2,19 +2,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "bloom.h" - -#include "base58.h" -#include "clientversion.h" -#include "key.h" -#include "merkleblock.h" -#include "random.h" -#include "serialize.h" -#include "streams.h" -#include "uint256.h" -#include "util.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <bloom.h> + +#include <base58.h> +#include <clientversion.h> +#include <key.h> +#include <merkleblock.h> +#include <primitives/block.h> +#include <random.h> +#include <serialize.h> +#include <streams.h> +#include <uint256.h> +#include <util.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <vector> @@ -179,20 +180,15 @@ BOOST_AUTO_TEST_CASE(bloom_match) BOOST_AUTO_TEST_CASE(merkle_block_1) { - // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) - // With 9 txes - CBlock block; - CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); - stream >> block; - + CBlock block = getBlock13b8a(); CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); // Match the last transaction filter.insert(uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); CMerkleBlock merkleBlock(block, filter); - BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); - BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1); std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0]; BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp index 7b3134d327..ab4f08b999 100644 --- a/src/test/bswap_tests.cpp +++ b/src/test/bswap_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "compat/byteswap.h" -#include "test/test_bitcoin.h" +#include <compat/byteswap.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 6ae0bcadd0..d8ad0d9165 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "util.h" -#include "utiltime.h" -#include "validation.h" +#include <util.h> +#include <utiltime.h> +#include <validation.h> -#include "test/test_bitcoin.h" -#include "checkqueue.h" +#include <test/test_bitcoin.h> +#include <checkqueue.h> #include <boost/test/unit_test.hpp> #include <boost/thread.hpp> #include <atomic> @@ -18,7 +18,7 @@ #include <unordered_set> #include <memory> -#include "random.h" +#include <random.h> // BasicTestingSetup not sufficient because nScriptCheckThreads is not set // otherwise. @@ -38,7 +38,7 @@ struct FakeCheckCheckCompletion { static std::atomic<size_t> n_calls; bool operator()() { - ++n_calls; + n_calls.fetch_add(1, std::memory_order_relaxed); return true; } void swap(FakeCheckCheckCompletion& x){}; @@ -88,15 +88,15 @@ struct MemoryCheck { // // Really, copy constructor should be deletable, but CCheckQueue breaks // if it is deleted because of internal push_back. - fake_allocated_memory += b; + fake_allocated_memory.fetch_add(b, std::memory_order_relaxed); }; MemoryCheck(bool b_) : b(b_) { - fake_allocated_memory += b; + fake_allocated_memory.fetch_add(b, std::memory_order_relaxed); }; - ~MemoryCheck(){ - fake_allocated_memory -= b; - + ~MemoryCheck() + { + fake_allocated_memory.fetch_sub(b, std::memory_order_relaxed); }; void swap(MemoryCheck& x) { std::swap(b, x.b); }; }; @@ -117,9 +117,9 @@ struct FrozenCleanupCheck { { if (should_freeze) { std::unique_lock<std::mutex> l(m); - nFrozen = 1; + nFrozen.store(1, std::memory_order_relaxed); cv.notify_one(); - cv.wait(l, []{ return nFrozen == 0;}); + cv.wait(l, []{ return nFrozen.load(std::memory_order_relaxed) == 0;}); } } void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);}; @@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) control.Add(vChecks); } bool r =control.Wait(); - BOOST_REQUIRE(r || end_fails); + BOOST_REQUIRE(r != end_fails); } } tg.interrupt_all(); @@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) tg.join_all(); } -// Test that a new verification cannot occur until all checks +// Test that a new verification cannot occur until all checks // have been destructed BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) { @@ -361,11 +361,14 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) std::unique_lock<std::mutex> l(FrozenCleanupCheck::m); // Wait until the queue has finished all jobs and frozen FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;}); - // Try to get control of the queue a bunch of times - for (auto x = 0; x < 100 && !fails; ++x) { - fails = queue->ControlMutex.try_lock(); - } - // Unfreeze + } + // Try to get control of the queue a bunch of times + for (auto x = 0; x < 100 && !fails; ++x) { + fails = queue->ControlMutex.try_lock(); + } + { + // Unfreeze (we need lock n case of spurious wakeup) + std::unique_lock<std::mutex> l(FrozenCleanupCheck::m); FrozenCleanupCheck::nFrozen = 0; } // Awaken frozen destructor diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index dc358bff95..ed556c07f4 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "coins.h" -#include "script/standard.h" -#include "uint256.h" -#include "undo.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" -#include "validation.h" -#include "consensus/validation.h" +#include <coins.h> +#include <script/standard.h> +#include <uint256.h> +#include <undo.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> +#include <validation.h> +#include <consensus/validation.h> #include <vector> #include <map> @@ -81,8 +81,8 @@ public: // Manually recompute the dynamic usage of the whole data, and compare it. size_t ret = memusage::DynamicUsage(cacheCoins); size_t count = 0; - for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) { - ret += it->second.coin.DynamicMemoryUsage(); + for (const auto& entry : cacheCoins) { + ret += entry.second.coin.DynamicMemoryUsage(); ++count; } BOOST_CHECK_EQUAL(GetCacheSize(), count); @@ -189,15 +189,15 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) // Once every 1000 iterations and at the end, verify the full cache. if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { - for (auto it = result.begin(); it != result.end(); it++) { - bool have = stack.back()->HaveCoin(it->first); - const Coin& coin = stack.back()->AccessCoin(it->first); + for (const auto& entry : result) { + bool have = stack.back()->HaveCoin(entry.first); + const Coin& coin = stack.back()->AccessCoin(entry.first); BOOST_CHECK(have == !coin.IsSpent()); - BOOST_CHECK(coin == it->second); + BOOST_CHECK(coin == entry.second); if (coin.IsSpent()) { missed_an_entry = true; } else { - BOOST_CHECK(stack.back()->HaveCoinInCache(it->first)); + BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first)); found_an_entry = true; } } @@ -420,11 +420,11 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) // Once every 1000 iterations and at the end, verify the full cache. if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) { - for (auto it = result.begin(); it != result.end(); it++) { - bool have = stack.back()->HaveCoin(it->first); - const Coin& coin = stack.back()->AccessCoin(it->first); + for (const auto& entry : result) { + bool have = stack.back()->HaveCoin(entry.first); + const Coin& coin = stack.back()->AccessCoin(entry.first); BOOST_CHECK(have == !coin.IsSpent()); - BOOST_CHECK(coin == it->second); + BOOST_CHECK(coin == entry.second); } } diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp index 35e4458bba..87e75d8020 100644 --- a/src/test/compress_tests.cpp +++ b/src/test/compress_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "compressor.h" -#include "util.h" -#include "test/test_bitcoin.h" +#include <compressor.h> +#include <util.h> +#include <test/test_bitcoin.h> #include <stdint.h> diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index c748b2448c..a63d62bb37 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -2,17 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypto/aes.h" -#include "crypto/chacha20.h" -#include "crypto/ripemd160.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/sha512.h" -#include "crypto/hmac_sha256.h" -#include "crypto/hmac_sha512.h" -#include "random.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <crypto/aes.h> +#include <crypto/chacha20.h> +#include <crypto/ripemd160.h> +#include <crypto/sha1.h> +#include <crypto/sha256.h> +#include <crypto/sha512.h> +#include <crypto/hmac_sha256.h> +#include <crypto/hmac_sha512.h> +#include <random.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <vector> diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 1004482224..32d46da984 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <boost/test/unit_test.hpp> -#include "cuckoocache.h" -#include "script/sigcache.h" -#include "test/test_bitcoin.h" -#include "random.h" +#include <cuckoocache.h> +#include <script/sigcache.h> +#include <test/test_bitcoin.h> +#include <random.h> #include <thread> /** Test Suite for CuckooCache diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/base58_keys_invalid.json index a088620f1b..2056c7491c 100644 --- a/src/test/data/base58_keys_invalid.json +++ b/src/test/data/base58_keys_invalid.json @@ -148,5 +148,35 @@ ], [ "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" + ], + [ + "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty" + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5" + ], + [ + "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2" + ], + [ + "bc1rw5uspcuh" + ], + [ + "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90" + ], + [ + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv" + ], + [ + "bc1gmk9yu" ] ] diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json index e1e252e22d..8418a6002d 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/base58_keys_valid.json @@ -1,452 +1,533 @@ [ [ - "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", - "65a16059864a2fdbc7c99a4723a8395bc6f188eb", + "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", + "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", - "74f209f6ea907e2ea48f74fae05782ae8a665257", + "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", + "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", - "53c0307d6851aa0ce7825ba883c6bd9ad242b486", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", - "6349a418fc4578d10a372b54b45c280cc8c4382f", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "regtest" } - ], + ], [ - "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", - "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", + "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", + "a9146349a418fc4578d10a372b54b45c280cc8c4382f87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", - "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", + "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", + "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", - "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", + "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", - "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", - "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", - "fcc5460dd6e2487c7d75b1963625da0e8f4c5975", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", - "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - "c579342c2c4c9220205e2cdc285617040c924a0a", + "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", + "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", - "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", + "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", + "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", - "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", + "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", + "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", - "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", + "a914c579342c2c4c9220205e2cdc285617040c924a0a87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", - "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", + "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", + "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", - "7987ccaa53d02c8873487ef919677cd3db7a6912", + "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", + "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", - "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", + "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", + "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", - "ef66444b5b17f14e8fae6e7e19b045a78c54fd79", + "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", + "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", - "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", + "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", + "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", - "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", + "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", + "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", - "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", + "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", + "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", - "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", + "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", - "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", + "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", + "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", - "adc1cc2081a27206fae25792f28bbc55b831549d", + "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", + "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", - "188f91a931947eddd7432d6e614387e32b244709", + "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", + "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", - "1694f5bc1a7295b600f40018a618a6ea48eeb498", + "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", + "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", - "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", + "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", + "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", - "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", + "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", + "a914188f91a931947eddd7432d6e614387e32b24470987", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", - "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", + "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", + "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", - "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", + "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", - "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", + "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", + "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", - "c4c1b72491ede1eedaca00618407ee0b772cad0d", + "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", + "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", - "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", - "261f83568a098a8638844bd7aeca039d5f2352c0", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", - "e930e1834a4d234702773951d627cce82fbb5d2e", + "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", + "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", - "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", + "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", + "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", - "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", + "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", + "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", - "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", + "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", - "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", + "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", + "a914e930e1834a4d234702773951d627cce82fbb5d2e87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", - "5eadaf9bb7121f0f192561a5a62f5e5f54210292", + "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", + "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", - "3f210e7277c899c3a155cc1c90f4106cbddeec6e", + "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", + "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", - "c8a3c2a09a298592c3e180f02487cd91ba3400b5", + "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", + "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", - "99b31df7c9068d1481b596578ddbb4d3bd90baeb", + "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", + "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", - "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", + "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", + "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", - "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", + "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", + "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", - "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", + "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", - "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", + "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", - "1ed467017f043e91ed4c44b4e8dd674db211c4e6", + "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", + "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", - "5ece0cadddc415b1980f001785947120acdb36fc", + "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", + "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" + } + ], + [ + "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", + "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + { + "isCompressed": false, + "isPrivkey": true, + "chain": "test" + } + ], + [ + "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", + "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + { + "isCompressed": true, + "isPrivkey": true, + "chain": "test" + } + ], + [ + "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", + "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac", + { + "isPrivkey": false, + "chain": "main" + } + ], + [ + "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", + "a9145ece0cadddc415b1980f001785947120acdb36fc87", + { + "isPrivkey": false, + "chain": "main" + } + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true + } + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", + "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1sw50qa3jx3s", + "6002751e", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", + "5210751e76e8199196d454941c45d1b3a323", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true } ] ] diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 251d5a7142..2e1e4da72f 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "dbwrapper.h" -#include "uint256.h" -#include "random.h" -#include "test/test_bitcoin.h" +#include <dbwrapper.h> +#include <uint256.h> +#include <random.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> @@ -125,7 +125,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. - CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); + std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; @@ -135,8 +135,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); // Call the destructor to free leveldb LOCK - delete dbw; - dbw = nullptr; + dbw.reset(); // Now, set up another wrapper that wants to obfuscate the same directory CDBWrapper odbw(ph, (1 << 10), false, false, true); @@ -167,7 +166,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) create_directories(ph); // Set up a non-obfuscated wrapper to write some initial data. - CDBWrapper* dbw = new CDBWrapper(ph, (1 << 10), false, false, false); + std::unique_ptr<CDBWrapper> dbw = MakeUnique<CDBWrapper>(ph, (1 << 10), false, false, false); char key = 'k'; uint256 in = InsecureRand256(); uint256 res; @@ -177,8 +176,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) BOOST_CHECK_EQUAL(res.ToString(), in.ToString()); // Call the destructor to free leveldb LOCK - delete dbw; - dbw = nullptr; + dbw.reset(); // Simulate a -reindex by wiping the existing data store CDBWrapper odbw(ph, (1 << 10), false, true, true); @@ -204,19 +202,31 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) for (int x=0x00; x<256; ++x) { uint8_t key = x; uint32_t value = x*x; - BOOST_CHECK(dbw.Write(key, value)); + if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value)); } + // Check that creating an iterator creates a snapshot std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); + + for (int x=0x00; x<256; ++x) { + uint8_t key = x; + uint32_t value = x*x; + if (x & 1) BOOST_CHECK(dbw.Write(key, value)); + } + for (int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); - for (int x=seek_start; x<256; ++x) { + for (int x=seek_start; x<255; ++x) { uint8_t key; uint32_t value; BOOST_CHECK(it->Valid()); if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure break; BOOST_CHECK(it->GetKey(key)); + if (x & 1) { + BOOST_CHECK_EQUAL(key, x + 1); + continue; + } BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key, x); BOOST_CHECK_EQUAL(value, x*x); diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 40f0ecd5f1..3c39258693 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "util.h" -#include "test/test_bitcoin.h" +#include <util.h> +#include <test/test_bitcoin.h> #include <string> #include <vector> diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index 05b6b3b1e6..68f445d142 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "hash.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <hash.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <vector> diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 91c0175412..7b9d7a6398 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "key.h" - -#include "base58.h" -#include "script/script.h" -#include "uint256.h" -#include "util.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" +#include <key.h> + +#include <base58.h> +#include <script/script.h> +#include <uint256.h> +#include <util.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> #include <string> #include <vector> diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp index b071ab117b..90730cd7b5 100644 --- a/src/test/limitedmap_tests.cpp +++ b/src/test/limitedmap_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "limitedmap.h" +#include <limitedmap.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp index 656aec606b..4be32f7628 100644 --- a/src/test/main_tests.cpp +++ b/src/test/main_tests.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chainparams.h" -#include "validation.h" -#include "net.h" +#include <chainparams.h> +#include <validation.h> +#include <net.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/signals2/signal.hpp> #include <boost/test/unit_test.hpp> diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 51b28d09fa..a694b870fe 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -2,11 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "policy/policy.h" -#include "txmempool.h" -#include "util.h" +#include <policy/policy.h> +#include <txmempool.h> +#include <util.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> #include <list> @@ -165,6 +165,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder[2] = tx1.GetHash().ToString(); // 10000 sortedOrder[3] = tx4.GetHash().ToString(); // 15000 sortedOrder[4] = tx2.GetHash().ToString(); // 20000 + LOCK(pool.cs); CheckSort<descendant_score>(pool, sortedOrder); /* low fee but with high fee child */ @@ -375,6 +376,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) } sortedOrder[4] = tx3.GetHash().ToString(); // 0 + LOCK(pool.cs); CheckSort<ancestor_score>(pool, sortedOrder); /* low fee parent with high fee child */ @@ -559,15 +561,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) // ... we should keep the same min fee until we get a block pool.removeForBlock(vtx, 1); SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0)); // ... then feerate should drop 1/2 each halflife SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0)); // ... with a 1/2 halflife when mempool is < 1/2 its target size SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0)); // ... with a 1/4 halflife when mempool is < 1/4 its target size SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 1a1cf4399c..ee48d96ad9 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/merkle.h" -#include "test/test_bitcoin.h" +#include <consensus/merkle.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp new file mode 100644 index 0000000000..37a1a84136 --- /dev/null +++ b/src/test/merkleblock_tests.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2017 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 <merkleblock.h> +#include <uint256.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + + +BOOST_FIXTURE_TEST_SUITE(merkleblock_tests, BasicTestingSetup) + +/** + * Create a CMerkleBlock using a list of txids which will be found in the + * given block. + */ +BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found) +{ + CBlock block = getBlock13b8a(); + + std::set<uint256> txids; + + // Last txn in block. + uint256 txhash1 = uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"); + + // Second txn in block. + uint256 txhash2 = uint256S("0xf9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07"); + + txids.insert(txhash1); + txids.insert(txhash2); + + CMerkleBlock merkleBlock(block, txids); + + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); + + // vMatchedTxn is only used when bloom filter is specified. + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + + std::vector<uint256> vMatched; + std::vector<unsigned int> vIndex; + + BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); + BOOST_CHECK_EQUAL(vMatched.size(), 2); + + // Ordered by occurrence in depth-first tree traversal. + BOOST_CHECK_EQUAL(vMatched[0].ToString(), txhash2.ToString()); + BOOST_CHECK_EQUAL(vIndex[0], 1); + + BOOST_CHECK_EQUAL(vMatched[1].ToString(), txhash1.ToString()); + BOOST_CHECK_EQUAL(vIndex[1], 8); +} + + +/** + * Create a CMerkleBlock using a list of txids which will not be found in the + * given block. + */ +BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found) +{ + CBlock block = getBlock13b8a(); + + std::set<uint256> txids2; + txids2.insert(uint256S("0xc0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); + CMerkleBlock merkleBlock(block, txids2); + + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + + std::vector<uint256> vMatched; + std::vector<unsigned int> vIndex; + + BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); + BOOST_CHECK_EQUAL(vMatched.size(), 0); + BOOST_CHECK_EQUAL(vIndex.size(), 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9fa9a8509c..b1a2032ea8 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -2,23 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chainparams.h" -#include "coins.h" -#include "consensus/consensus.h" -#include "consensus/merkle.h" -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "validation.h" -#include "miner.h" -#include "policy/policy.h" -#include "pubkey.h" -#include "script/standard.h" -#include "txmempool.h" -#include "uint256.h" -#include "util.h" -#include "utilstrencodings.h" - -#include "test/test_bitcoin.h" +#include <chainparams.h> +#include <coins.h> +#include <consensus/consensus.h> +#include <consensus/merkle.h> +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <validation.h> +#include <miner.h> +#include <policy/policy.h> +#include <pubkey.h> +#include <script/standard.h> +#include <txmempool.h> +#include <uint256.h> +#include <util.h> +#include <utilstrencodings.h> + +#include <test/test_bitcoin.h> #include <memory> @@ -32,7 +32,6 @@ static BlockAssembler AssemblerForTest(const CChainParams& params) { BlockAssembler::Options options; options.nBlockMaxWeight = MAX_BLOCK_WEIGHT; - options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; options.blockMinFeeRate = blockMinFeeRate; return BlockAssembler(params, options); } @@ -336,23 +335,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); - // invalid (pre-p2sh) txn in mempool, template creation fails - tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vin[0].prevout.n = 0; - tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; - script = CScript() << OP_0; - tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); - hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - tx.vin[0].prevout.hash = hash; - tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end()); - tx.vout[0].nValue -= LOWFEE; - hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); - mempool.clear(); - // double spend txn pair in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; @@ -392,6 +374,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.SetTip(next); } BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + + // invalid p2sh txn in mempool, template creation fails + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.n = 0; + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; + script = CScript() << OP_0; + tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); + hash = tx.GetHash(); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + tx.vin[0].prevout.hash = hash; + tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end()); + tx.vout[0].nValue -= LOWFEE; + hash = tx.GetHash(); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); + mempool.clear(); + // Delete the dummy blocks again. while (chainActive.Tip()->nHeight > nHeight) { CBlockIndex* del = chainActive.Tip(); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 5e89ef60d2..ffba7d6d22 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -2,22 +2,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "key.h" -#include "keystore.h" -#include "policy/policy.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/interpreter.h" -#include "script/sign.h" -#include "script/ismine.h" -#include "uint256.h" -#include "test/test_bitcoin.h" +#include <key.h> +#include <keystore.h> +#include <policy/policy.h> +#include <script/script.h> +#include <script/script_error.h> +#include <script/interpreter.h> +#include <script/sign.h> +#include <script/ismine.h> +#include <uint256.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> -typedef std::vector<unsigned char> valtype; - BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript @@ -173,95 +171,6 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) BOOST_CHECK(!::IsStandard(malformed[i], whichType)); } -BOOST_AUTO_TEST_CASE(multisig_Solver1) -{ - // Tests Solver() that returns lists of keys that are - // required to satisfy a ScriptPubKey - // - // Also tests IsMine() and ExtractDestination() - // - // Note: ExtractDestination for the multisignature transactions - // always returns false for this release, even if you have - // one key that would satisfy an (a|b) or 2-of-3 keys needed - // to spend an escrow transaction. - // - CBasicKeyStore keystore, emptykeystore, partialkeystore; - CKey key[3]; - CTxDestination keyaddr[3]; - for (int i = 0; i < 3; i++) - { - key[i].MakeNewKey(true); - keystore.AddKey(key[i]); - keyaddr[i] = key[i].GetPubKey().GetID(); - } - partialkeystore.AddKey(key[0]); - - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 1); - CTxDestination addr; - BOOST_CHECK(ExtractDestination(s, addr)); - BOOST_CHECK(addr == keyaddr[0]); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_DUP << OP_HASH160 << ToByteVector(key[0].GetPubKey().GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 1); - CTxDestination addr; - BOOST_CHECK(ExtractDestination(s, addr)); - BOOST_CHECK(addr == keyaddr[0]); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4U); - CTxDestination addr; - BOOST_CHECK(!ExtractDestination(s, addr)); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - BOOST_CHECK(!IsMine(partialkeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4U); - std::vector<CTxDestination> addrs; - int nRequired; - BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired)); - BOOST_CHECK(addrs[0] == keyaddr[0]); - BOOST_CHECK(addrs[1] == keyaddr[1]); - BOOST_CHECK(nRequired == 1); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - BOOST_CHECK(!IsMine(partialkeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 5); - } -} - BOOST_AUTO_TEST_CASE(multisig_Sign) { // Test SignSignature() (and therefore the version of Solver() that signs transactions) diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 31b05d868b..b4176a2b85 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -1,17 +1,17 @@ // Copyright (c) 2012-2016 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 "addrman.h" -#include "test/test_bitcoin.h" +#include <addrman.h> +#include <test/test_bitcoin.h> #include <string> #include <boost/test/unit_test.hpp> -#include "hash.h" -#include "serialize.h" -#include "streams.h" -#include "net.h" -#include "netbase.h" -#include "chainparams.h" -#include "util.h" +#include <hash.h> +#include <serialize.h> +#include <streams.h> +#include <net.h> +#include <netbase.h> +#include <chainparams.h> +#include <util.h> class CAddrManSerializationMock : public CAddrMan { diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 1baf7643e5..f3a9b1fc65 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "netbase.h" -#include "test/test_bitcoin.h" -#include "utilstrencodings.h" +#include <netbase.h> +#include <test/test_bitcoin.h> +#include <utilstrencodings.h> #include <string> diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index c1d216d094..b7b632be3a 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/merkle.h" -#include "merkleblock.h" -#include "serialize.h" -#include "streams.h" -#include "uint256.h" -#include "arith_uint256.h" -#include "version.h" -#include "test/test_bitcoin.h" +#include <consensus/merkle.h> +#include <merkleblock.h> +#include <serialize.h> +#include <streams.h> +#include <uint256.h> +#include <arith_uint256.h> +#include <version.h> +#include <test/test_bitcoin.h> #include <vector> diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index fd8f7191f4..d8026e4468 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "policy/policy.h" -#include "policy/fees.h" -#include "txmempool.h" -#include "uint256.h" -#include "util.h" +#include <policy/policy.h> +#include <policy/fees.h> +#include <txmempool.h> +#include <uint256.h> +#include <util.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index b13f2625aa..2837537c9a 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chain.h" -#include "chainparams.h" -#include "pow.h" -#include "random.h" -#include "util.h" -#include "test/test_bitcoin.h" +#include <chain.h> +#include <chainparams.h> +#include <pow.h> +#include <random.h> +#include <util.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 841282873f..1327d67650 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -3,13 +3,13 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <vector> -#include "prevector.h" +#include <prevector.h> -#include "reverse_iterator.h" -#include "serialize.h" -#include "streams.h" +#include <reverse_iterator.h> +#include <serialize.h> +#include <streams.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp index 0d541ec7d4..781b1e242d 100644 --- a/src/test/raii_event_tests.cpp +++ b/src/test/raii_event_tests.cpp @@ -10,9 +10,9 @@ #include <map> #include <stdlib.h> -#include "support/events.h" +#include <support/events.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <vector> diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 132e190051..1ca5a53d72 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "random.h" +#include <random.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp index 00dc47e13e..86d6161e20 100644 --- a/src/test/reverselock_tests.cpp +++ b/src/test/reverselock_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "reverselock.h" -#include "test/test_bitcoin.h" +#include <reverselock.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index c6643be7a7..710d09ead6 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "rpc/server.h" -#include "rpc/client.h" +#include <rpc/server.h> +#include <rpc/client.h> -#include "base58.h" -#include "core_io.h" -#include "netbase.h" +#include <base58.h> +#include <core_io.h> +#include <netbase.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/algorithm/string.hpp> #include <boost/test/unit_test.hpp> diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp index 51f9e9f39f..b3ec6d34c7 100644 --- a/src/test/sanity_tests.cpp +++ b/src/test/sanity_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "compat/sanity.h" -#include "key.h" -#include "test/test_bitcoin.h" +#include <compat/sanity.h> +#include <key.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 1de865776e..7e86eed1c5 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "random.h" -#include "scheduler.h" +#include <random.h> +#include <scheduler.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/bind.hpp> #include <boost/thread.hpp> diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index efd0f77d9f..5a64aa3959 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -2,17 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/tx_verify.h" -#include "core_io.h" -#include "key.h" -#include "keystore.h" -#include "validation.h" -#include "policy/policy.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "script/ismine.h" -#include "test/test_bitcoin.h" +#include <consensus/tx_verify.h> +#include <core_io.h> +#include <key.h> +#include <keystore.h> +#include <validation.h> +#include <policy/policy.h> +#include <script/script.h> +#include <script/script_error.h> +#include <script/sign.h> +#include <script/ismine.h> +#include <test/test_bitcoin.h> #include <vector> @@ -112,8 +112,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - const CTxOut& output = txFrom.vout[txTo[i].vin[0].prevout.n]; - bool sigOK = CScriptCheck(output.scriptPubKey, output.nValue, txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); + bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp new file mode 100644 index 0000000000..19060eccc9 --- /dev/null +++ b/src/test/script_standard_tests.cpp @@ -0,0 +1,741 @@ +// Copyright (c) 2017 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 <key.h> +#include <keystore.h> +#include <script/ismine.h> +#include <script/script.h> +#include <script/script_error.h> +#include <script/standard.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + + +BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(script_standard_Solver_success) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript s; + txnouttype whichType; + std::vector<std::vector<unsigned char> > solutions; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); + + // TX_MULTISIG + s.clear(); + s << OP_1 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(solutions.size(), 4); + BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1})); + BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); + BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); + BOOST_CHECK(solutions[3] == std::vector<unsigned char>({2})); + + s.clear(); + s << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + ToByteVector(pubkeys[2]) << + OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(solutions.size(), 5); + BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2})); + BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); + BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); + BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2])); + BOOST_CHECK(solutions[4] == std::vector<unsigned char>({3})); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << + std::vector<unsigned char>({0}) << + std::vector<unsigned char>({75}) << + std::vector<unsigned char>({255}); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + // TX_WITNESS_V0_KEYHASH + s.clear(); + s << OP_0 << ToByteVector(pubkeys[0].GetID()); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); + + // TX_WITNESS_V0_SCRIPTHASH + uint256 scriptHash; + CSHA256().Write(&redeemScript[0], redeemScript.size()) + .Finalize(scriptHash.begin()); + + s.clear(); + s << OP_0 << ToByteVector(scriptHash); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); + + // TX_NONSTANDARD + s.clear(); + s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); +} + +BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) +{ + CKey key; + CPubKey pubkey; + key.MakeNewKey(true); + pubkey = key.GetPubKey(); + + CScript s; + txnouttype whichType; + std::vector<std::vector<unsigned char> > solutions; + + // TX_PUBKEY with incorrectly sized pubkey + s.clear(); + s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_PUBKEYHASH with incorrectly sized key hash + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_SCRIPTHASH with incorrectly sized script hash + s.clear(); + s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG 0/2 + s.clear(); + s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG 2/1 + s.clear(); + s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG n = 2 with 1 pubkey + s.clear(); + s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG n = 1 with 0 pubkeys + s.clear(); + s << OP_1 << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_NULL_DATA with other opcodes + s.clear(); + s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_WITNESS with incorrect program size + s.clear(); + s << OP_0 << std::vector<unsigned char>(19, 0x01); + BOOST_CHECK(!Solver(s, whichType, solutions)); +} + +BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) +{ + CKey key; + CPubKey pubkey; + key.MakeNewKey(true); + pubkey = key.GetPubKey(); + + CScript s; + CTxDestination address; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkey) << OP_CHECKSIG; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<CKeyID>(&address) && + *boost::get<CKeyID>(&address) == pubkey.GetID()); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<CKeyID>(&address) && + *boost::get<CKeyID>(&address) == pubkey.GetID()); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<CScriptID>(&address) && + *boost::get<CScriptID>(&address) == CScriptID(redeemScript)); + + // TX_MULTISIG + s.clear(); + s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << std::vector<unsigned char>({75}); + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_WITNESS_V0_KEYHASH + s.clear(); + s << OP_0 << ToByteVector(pubkey.GetID()); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessV0KeyHash keyhash; + CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin()); + BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash); + + // TX_WITNESS_V0_SCRIPTHASH + s.clear(); + WitnessV0ScriptHash scripthash; + CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin()); + s << OP_0 << ToByteVector(scripthash); + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash); + + // TX_WITNESS with unknown version + s.clear(); + s << OP_1 << ToByteVector(pubkey); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessUnknown unk; + unk.length = 33; + unk.version = 1; + std::copy(pubkey.begin(), pubkey.end(), unk.program); + BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk); +} + +BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript s; + txnouttype whichType; + std::vector<CTxDestination> addresses; + int nRequired; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && + *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && + *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) && + *boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript)); + + // TX_MULTISIG + s.clear(); + s << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(addresses.size(), 2); + BOOST_CHECK_EQUAL(nRequired, 2); + BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && + *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); + BOOST_CHECK(boost::get<CKeyID>(&addresses[1]) && + *boost::get<CKeyID>(&addresses[1]) == pubkeys[1].GetID()); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << std::vector<unsigned char>({75}); + BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); +} + +BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript expected, result; + + // CKeyID + expected.clear(); + expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForDestination(pubkeys[0].GetID()); + BOOST_CHECK(result == expected); + + // CScriptID + CScript redeemScript(result); + expected.clear(); + expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + result = GetScriptForDestination(CScriptID(redeemScript)); + BOOST_CHECK(result == expected); + + // CNoDestination + expected.clear(); + result = GetScriptForDestination(CNoDestination()); + BOOST_CHECK(result == expected); + + // GetScriptForRawPubKey + expected.clear(); + expected << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + result = GetScriptForRawPubKey(pubkeys[0]); + BOOST_CHECK(result == expected); + + // GetScriptForMultisig + expected.clear(); + expected << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + ToByteVector(pubkeys[2]) << + OP_3 << OP_CHECKMULTISIG; + result = GetScriptForMultisig(2, std::vector<CPubKey>(pubkeys, pubkeys + 3)); + BOOST_CHECK(result == expected); + + // GetScriptForWitness + CScript witnessScript; + + witnessScript << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + expected.clear(); + expected << OP_0 << ToByteVector(pubkeys[0].GetID()); + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); + + witnessScript.clear(); + witnessScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); + + witnessScript.clear(); + witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + expected.clear(); + expected << OP_0 << ToByteVector(scriptHash); + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); +} + +BOOST_AUTO_TEST_CASE(script_standard_IsMine) +{ + CKey keys[2]; + CPubKey pubkeys[2]; + for (int i = 0; i < 2; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CKey uncompressedKey; + uncompressedKey.MakeNewKey(false); + CPubKey uncompressedPubkey = uncompressedKey.GetPubKey(); + + CScript scriptPubKey; + isminetype result; + bool isInvalid; + + // P2PK compressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PK uncompressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(uncompressedKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PKH compressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PKH uncompressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(uncompressedKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2SH + { + CBasicKeyStore keystore; + + CScript redeemScript; + redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore does not have redeemScript or key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript but no key + keystore.AddCScript(redeemScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript and key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WPKH compressed + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID()); + + // Keystore has key, but no P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key and P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WPKH uncompressed + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID()); + + // Keystore has key, but no P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key and P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(isInvalid); + } + + // scriptPubKey multisig + { + CBasicKeyStore keystore; + + scriptPubKey.clear(); + scriptPubKey << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + // Keystore does not have any keys + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has 1/2 keys + keystore.AddKey(uncompressedKey); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has 2/2 keys + keystore.AddKey(keys[1]); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2SH multisig + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + keystore.AddKey(keys[1]); + + CScript redeemScript; + redeemScript << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore has no redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript + keystore.AddCScript(redeemScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WSH multisig with compressed keys + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + keystore.AddKey(keys[1]); + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(scriptHash); + + // Keystore has keys, but no witnessScript or P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys and witnessScript, but no P2SH redeemScript + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WSH multisig with uncompressed key + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + keystore.AddKey(keys[1]); + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(scriptHash); + + // Keystore has keys, but no witnessScript or P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys and witnessScript, but no P2SH redeemScript + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(isInvalid); + } + + // P2WSH multisig wrapped in P2SH + { + CBasicKeyStore keystore; + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + CScript redeemScript; + redeemScript << OP_0 << ToByteVector(scriptHash); + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore has no witnessScript, P2SH redeemScript, or keys + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has witnessScript and P2SH redeemScript, but no keys + keystore.AddCScript(redeemScript); + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddKey(keys[0]); + keystore.AddKey(keys[1]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // OP_RETURN + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + } + + // Nonstandard + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 17374edcc4..57b3e501af 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2,21 +2,21 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/script_tests.json.h" - -#include "core_io.h" -#include "key.h" -#include "keystore.h" -#include "script/script.h" -#include "script/script_error.h" -#include "script/sign.h" -#include "util.h" -#include "utilstrencodings.h" -#include "test/test_bitcoin.h" -#include "rpc/server.h" +#include <test/data/script_tests.json.h> + +#include <core_io.h> +#include <key.h> +#include <keystore.h> +#include <script/script.h> +#include <script/script_error.h> +#include <script/sign.h> +#include <util.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> +#include <rpc/server.h> #if defined(HAVE_CONSENSUS_LIB) -#include "script/bitcoinconsensus.h" +#include <script/bitcoinconsensus.h> #endif #include <fstream> @@ -1451,4 +1451,21 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps) BOOST_CHECK(!script.HasValidOps()); } +BOOST_AUTO_TEST_CASE(script_can_append_self) +{ + CScript s, d; + + s = ScriptFromHex("00"); + s += s; + d = ScriptFromHex("0000"); + BOOST_CHECK(s == d); + + // check doubling a script that's large enough to require reallocation + static const char hex[] = "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"; + s = CScript() << ParseHex(hex) << OP_CHECKSIG; + d = CScript() << ParseHex(hex) << OP_CHECKSIG << ParseHex(hex) << OP_CHECKSIG; + s += s; + BOOST_CHECK(s == d); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/scriptnum10.h b/src/test/scriptnum10.h index 94dd58526c..ad15427a9b 100644 --- a/src/test/scriptnum10.h +++ b/src/test/scriptnum10.h @@ -12,7 +12,7 @@ #include <stdint.h> #include <string> #include <vector> -#include "assert.h" +#include <assert.h> class scriptnum10_error : public std::runtime_error { diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp index 280eb59ce8..aef500174a 100644 --- a/src/test/scriptnum_tests.cpp +++ b/src/test/scriptnum_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "scriptnum10.h" -#include "script/script.h" -#include "test/test_bitcoin.h" +#include <test/scriptnum10.h> +#include <script/script.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> #include <limits.h> diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 9661a66514..28ed2bfeb0 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -2,10 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "serialize.h" -#include "streams.h" -#include "hash.h" -#include "test/test_bitcoin.h" +#include <serialize.h> +#include <streams.h> +#include <hash.h> +#include <test/test_bitcoin.h> #include <stdint.h> diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index ecbdf57788..d97e106287 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -2,18 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "data/sighash.json.h" -#include "hash.h" -#include "script/interpreter.h" -#include "script/script.h" -#include "serialize.h" -#include "streams.h" -#include "test/test_bitcoin.h" -#include "util.h" -#include "utilstrencodings.h" -#include "version.h" +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <test/data/sighash.json.h> +#include <hash.h> +#include <script/interpreter.h> +#include <script/script.h> +#include <serialize.h> +#include <streams.h> +#include <test/test_bitcoin.h> +#include <util.h> +#include <utilstrencodings.h> +#include <version.h> #include <iostream> diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index d3b8b07228..891cd1e1d8 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "pubkey.h" -#include "key.h" -#include "script/script.h" -#include "script/standard.h" -#include "uint256.h" -#include "test/test_bitcoin.h" +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <pubkey.h> +#include <key.h> +#include <script/script.h> +#include <script/standard.h> +#include <uint256.h> +#include <test/test_bitcoin.h> #include <vector> diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index 164cbd873f..1ffd9f2b42 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chain.h" -#include "util.h" -#include "test/test_bitcoin.h" +#include <chain.h> +#include <util.h> +#include <test/test_bitcoin.h> #include <vector> diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index af2a152aa5..3636e6c332 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "streams.h" -#include "support/allocators/zeroafterfree.h" -#include "test/test_bitcoin.h" +#include <streams.h> +#include <support/allocators/zeroafterfree.h> +#include <test/test_bitcoin.h> #include <boost/assign/std/vector.hpp> // for 'operator+=()' #include <boost/test/unit_test.hpp> diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 045655983c..419106194e 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -2,28 +2,41 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "test_bitcoin.h" - -#include "chainparams.h" -#include "consensus/consensus.h" -#include "consensus/validation.h" -#include "crypto/sha256.h" -#include "fs.h" -#include "key.h" -#include "validation.h" -#include "miner.h" -#include "net_processing.h" -#include "pubkey.h" -#include "random.h" -#include "txdb.h" -#include "txmempool.h" -#include "ui_interface.h" -#include "rpc/server.h" -#include "rpc/register.h" -#include "script/sigcache.h" +#include <test/test_bitcoin.h> + +#include <chainparams.h> +#include <consensus/consensus.h> +#include <consensus/validation.h> +#include <crypto/sha256.h> +#include <fs.h> +#include <key.h> +#include <validation.h> +#include <miner.h> +#include <net_processing.h> +#include <pubkey.h> +#include <random.h> +#include <txdb.h> +#include <txmempool.h> +#include <ui_interface.h> +#include <streams.h> +#include <rpc/server.h> +#include <rpc/register.h> +#include <script/sigcache.h> #include <memory> +void CConnmanTest::AddNode(CNode& node) +{ + LOCK(g_connman->cs_vNodes); + g_connman->vNodes.push_back(&node); +} + +void CConnmanTest::ClearNodes() +{ + LOCK(g_connman->cs_vNodes); + g_connman->vNodes.clear(); +} + uint256 insecure_rand_seed = GetRandHash(); FastRandomContext insecure_rand_ctx(insecure_rand_seed); @@ -68,9 +81,9 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); mempool.setSanityCheck(1.0); - pblocktree = new CBlockTreeDB(1 << 20, true); - pcoinsdbview = new CCoinsViewDB(1 << 23, true); - pcoinsTip = new CCoinsViewCache(pcoinsdbview); + pblocktree.reset(new CBlockTreeDB(1 << 20, true)); + pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true)); + pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get())); if (!LoadGenesisBlock(chainparams)) { throw std::runtime_error("LoadGenesisBlock failed."); } @@ -85,7 +98,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha threadGroup.create_thread(&ThreadScriptCheck); g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests. connman = g_connman.get(); - peerLogic.reset(new PeerLogicValidation(connman)); + peerLogic.reset(new PeerLogicValidation(connman, scheduler)); } TestingSetup::~TestingSetup() @@ -97,14 +110,17 @@ TestingSetup::~TestingSetup() g_connman.reset(); peerLogic.reset(); UnloadBlockIndex(); - delete pcoinsTip; - delete pcoinsdbview; - delete pblocktree; + pcoinsTip.reset(); + pcoinsdbview.reset(); + pblocktree.reset(); fs::remove_all(pathTemp); } TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { + // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests. + // TODO: fix the code to support SegWit blocks. + UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, Consensus::BIP9Deployment::NO_TIMEOUT); // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; @@ -133,7 +149,10 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& block.vtx.push_back(MakeTransactionRef(tx)); // IncrementExtraNonce creates a valid coinbase and merkleRoot unsigned int extraNonce = 0; - IncrementExtraNonce(&block, chainActive.Tip(), extraNonce); + { + LOCK(cs_main); + IncrementExtraNonce(&block, chainActive.Tip(), extraNonce); + } while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; @@ -158,3 +177,15 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) { return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp); } + +/** + * @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) + * with 9 txs. + */ +CBlock getBlock13b8a() +{ + CBlock block; + CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + return block; +} diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 6ada96f887..80a9cbaeea 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -5,14 +5,14 @@ #ifndef BITCOIN_TEST_TEST_BITCOIN_H #define BITCOIN_TEST_TEST_BITCOIN_H -#include "chainparamsbase.h" -#include "fs.h" -#include "key.h" -#include "pubkey.h" -#include "random.h" -#include "scheduler.h" -#include "txdb.h" -#include "txmempool.h" +#include <chainparamsbase.h> +#include <fs.h> +#include <key.h> +#include <pubkey.h> +#include <random.h> +#include <scheduler.h> +#include <txdb.h> +#include <txmempool.h> #include <boost/thread.hpp> @@ -49,9 +49,14 @@ struct BasicTestingSetup { * Included are data directory, coins database, script check threads setup. */ class CConnman; +class CNode; +struct CConnmanTest { + static void AddNode(CNode& node); + static void ClearNodes(); +}; + class PeerLogicValidation; struct TestingSetup: public BasicTestingSetup { - CCoinsViewDB *pcoinsdbview; fs::path pathTemp; boost::thread_group threadGroup; CConnman* connman; @@ -99,7 +104,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper() : nFee(0), nTime(0), nHeight(1), spendsCoinbase(false), sigOpCost(4) { } - + CTxMemPoolEntry FromTx(const CMutableTransaction &tx); CTxMemPoolEntry FromTx(const CTransaction &tx); @@ -110,4 +115,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; } }; + +CBlock getBlock13b8a(); + #endif diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index 50e4076d07..319d81187a 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -3,22 +3,23 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "consensus/merkle.h" -#include "primitives/block.h" -#include "script/script.h" -#include "addrman.h" -#include "chain.h" -#include "coins.h" -#include "compressor.h" -#include "net.h" -#include "protocol.h" -#include "streams.h" -#include "undo.h" -#include "version.h" -#include "pubkey.h" +#include <consensus/merkle.h> +#include <primitives/block.h> +#include <script/script.h> +#include <addrman.h> +#include <chain.h> +#include <coins.h> +#include <compressor.h> +#include <net.h> +#include <protocol.h> +#include <streams.h> +#include <undo.h> +#include <version.h> +#include <pubkey.h> +#include <blockencodings.h> #include <stdint.h> #include <unistd.h> @@ -45,11 +46,13 @@ enum TEST_ID { CBLOOMFILTER_DESERIALIZE, CDISKBLOCKINDEX_DESERIALIZE, CTXOUTCOMPRESSOR_DESERIALIZE, + BLOCKTRANSACTIONS_DESERIALIZE, + BLOCKTRANSACTIONSREQUEST_DESERIALIZE, TEST_ID_END }; -bool read_stdin(std::vector<char> &data) { - char buffer[1024]; +bool read_stdin(std::vector<uint8_t> &data) { + uint8_t buffer[1024]; ssize_t length=0; while((length = read(STDIN_FILENO, buffer, 1024)) > 0) { data.insert(data.end(), buffer, buffer+length); @@ -59,11 +62,7 @@ bool read_stdin(std::vector<char> &data) { return length==0; } -int do_fuzz() -{ - std::vector<char> buffer; - if (!read_stdin(buffer)) return 0; - +int test_one_input(std::vector<uint8_t> buffer) { if (buffer.size() < sizeof(uint32_t)) return 0; uint32_t test_id = 0xffffffff; @@ -249,15 +248,58 @@ int do_fuzz() break; } + case BLOCKTRANSACTIONS_DESERIALIZE: + { + try + { + BlockTransactions bt; + ds >> bt; + } catch (const std::ios_base::failure& e) {return 0;} + + break; + } + case BLOCKTRANSACTIONSREQUEST_DESERIALIZE: + { + try + { + BlockTransactionsRequest btr; + ds >> btr; + } catch (const std::ios_base::failure& e) {return 0;} + + break; + } default: return 0; } return 0; } +static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; +void initialize() { + globalVerifyHandle = std::unique_ptr<ECCVerifyHandle>(new ECCVerifyHandle()); +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + test_one_input(std::vector<uint8_t>(data, data + size)); + return 0; +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + initialize(); + return 0; +} + +// Disabled under WIN32 due to clash with Cygwin's WinMain. +#ifndef WIN32 +// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides +// the main(...) function. +__attribute__((weak)) +#endif int main(int argc, char **argv) { - ECCVerifyHandle globalVerifyHandle; + initialize(); #ifdef __AFL_INIT // Enable AFL deferred forkserver mode. Requires compilation using // afl-clang-fast++. See fuzzing.md for details. @@ -267,11 +309,20 @@ int main(int argc, char **argv) #ifdef __AFL_LOOP // Enable AFL persistent mode. Requires compilation using afl-clang-fast++. // See fuzzing.md for details. + int ret = 0; while (__AFL_LOOP(1000)) { - do_fuzz(); + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + continue; + } + ret = test_one_input(buffer); } - return 0; + return ret; #else - return do_fuzz(); + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + return 0; + } + return test_one_input(buffer); #endif } diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp index b556c953b9..8d81ff6fc6 100644 --- a/src/test/test_bitcoin_main.cpp +++ b/src/test/test_bitcoin_main.cpp @@ -4,7 +4,7 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite -#include "net.h" +#include <net.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp index 34863fd9d0..9ff95bd2b0 100644 --- a/src/test/timedata_tests.cpp +++ b/src/test/timedata_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // -#include "timedata.h" -#include "test/test_bitcoin.h" +#include <timedata.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp index b7affaacde..d0aa8659c2 100644 --- a/src/test/torcontrol_tests.cpp +++ b/src/test/torcontrol_tests.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. // -#include "test/test_bitcoin.h" -#include "torcontrol.cpp" +#include <test/test_bitcoin.h> +#include <torcontrol.cpp> #include <boost/test/unit_test.hpp> diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 6654634bf1..34a182722d 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -2,24 +2,24 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "data/tx_invalid.json.h" -#include "data/tx_valid.json.h" -#include "test/test_bitcoin.h" - -#include "clientversion.h" -#include "checkqueue.h" -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "core_io.h" -#include "key.h" -#include "keystore.h" -#include "validation.h" -#include "policy/policy.h" -#include "script/script.h" -#include "script/sign.h" -#include "script/script_error.h" -#include "script/standard.h" -#include "utilstrencodings.h" +#include <test/data/tx_invalid.json.h> +#include <test/data/tx_valid.json.h> +#include <test/test_bitcoin.h> + +#include <clientversion.h> +#include <checkqueue.h> +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <core_io.h> +#include <key.h> +#include <keystore.h> +#include <validation.h> +#include <policy/policy.h> +#include <script/script.h> +#include <script/sign.h> +#include <script/script_error.h> +#include <script/standard.h> +#include <utilstrencodings.h> #include <map> #include <string> @@ -480,8 +480,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { for(uint32_t i = 0; i < mtx.vin.size(); i++) { std::vector<CScriptCheck> vChecks; - const CTxOut& output = coins[tx.vin[i].prevout.n].out; - CScriptCheck check(output.scriptPubKey, output.nValue, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); + CScriptCheck check(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); vChecks.push_back(CScriptCheck()); check.swap(vChecks.back()); control.Add(vChecks); diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp new file mode 100644 index 0000000000..2d1eb7b772 --- /dev/null +++ b/src/test/txvalidation_tests.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2017 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 <validation.h> +#include <txmempool.h> +#include <amount.h> +#include <consensus/validation.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + + +BOOST_AUTO_TEST_SUITE(txvalidation_tests) + +/** + * Ensure that the mempool won't accept coinbase transactions. + */ +BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) +{ + CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + CMutableTransaction coinbaseTx; + + coinbaseTx.nVersion = 1; + coinbaseTx.vin.resize(1); + coinbaseTx.vout.resize(1); + coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL; + coinbaseTx.vout[0].nValue = 1 * CENT; + coinbaseTx.vout[0].scriptPubKey = scriptPubKey; + + assert(CTransaction(coinbaseTx).IsCoinBase()); + + CValidationState state; + + LOCK(cs_main); + + unsigned int initialPoolSize = mempool.size(); + + BOOST_CHECK_EQUAL( + false, + AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx), + nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, + true /* bypass_limits */, + 0 /* nAbsurdFee */)); + + // Check that the transaction hasn't been added to mempool. + BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize); + + // Check that the validation state reflects the unsuccessful attempt. + BOOST_CHECK(state.IsInvalid()); + BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase"); + + int nDoS; + BOOST_CHECK_EQUAL(state.IsInvalid(nDoS), true); + BOOST_CHECK_EQUAL(nDoS, 100); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 2d25cb96c8..fa49b9c33b 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -2,20 +2,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/validation.h" -#include "key.h" -#include "validation.h" -#include "miner.h" -#include "pubkey.h" -#include "txmempool.h" -#include "random.h" -#include "script/standard.h" -#include "script/sign.h" -#include "test/test_bitcoin.h" -#include "utiltime.h" -#include "core_io.h" -#include "keystore.h" -#include "policy/policy.h" +#include <consensus/validation.h> +#include <key.h> +#include <validation.h> +#include <miner.h> +#include <pubkey.h> +#include <txmempool.h> +#include <random.h> +#include <script/standard.h> +#include <script/sign.h> +#include <test/test_bitcoin.h> +#include <utiltime.h> +#include <core_io.h> +#include <keystore.h> +#include <policy/policy.h> #include <boost/test/unit_test.hpp> @@ -29,7 +29,8 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, nullptr, nullptr, true, 0); + return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) @@ -65,6 +66,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) // Test 1: block with both of those transactions should be rejected. block = CreateAndProcessBlock(spends, scriptPubKey); + LOCK(cs_main); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); // Test 2: ... and should be rejected if spend1 is in the memory pool @@ -118,7 +120,7 @@ void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_fl // WITNESS requires P2SH test_flags |= SCRIPT_VERIFY_P2SH; } - bool ret = CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, nullptr); + bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr); // CheckInputs should succeed iff test_flags doesn't intersect with // failing_flags bool expected_return_value = !(test_flags & failing_flags); @@ -134,13 +136,13 @@ void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_fl if (ret && add_to_cache) { // Check that we get a cache hit if the tx was valid std::vector<CScriptCheck> scriptchecks; - BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK(scriptchecks.empty()); } else { // Check that we get script executions to check, if the transaction // was invalid, or we didn't add to cache. std::vector<CScriptCheck> scriptchecks; - BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); } } @@ -150,7 +152,10 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { // Test that passing CheckInputs with one set of script flags doesn't imply // that we would pass again with a different set of flags. - InitScriptExecutionCache(); + { + LOCK(cs_main); + InitScriptExecutionCache(); + } CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey)); @@ -200,13 +205,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) CValidationState state; PrecomputedTransactionData ptd_spend_tx(spend_tx); - BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); + BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); // If we call again asking for scriptchecks (as happens in // ConnectBlock), we should add a script check object for this -- we're // not caching invalidity (if that changes, delete this test case). std::vector<CScriptCheck> scriptchecks; - BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); + BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), 1); // Test that CheckInputs returns true iff DERSIG-enforcing flags are @@ -267,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata(invalid_with_cltv_tx); - BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); + BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); } // TEST CHECKSEQUENCEVERIFY @@ -295,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata(invalid_with_csv_tx); - BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); + BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); } // TODO: add tests for remaining script flags @@ -357,12 +362,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) CValidationState state; PrecomputedTransactionData txdata(tx); // This transaction is now invalid under segwit, because of the second input. - BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); + BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); std::vector<CScriptCheck> scriptchecks; // Make sure this transaction was not cached (ie because the first // input was valid) - BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); // Should get 2 script checks back -- caching is on a whole-transaction basis. BOOST_CHECK_EQUAL(scriptchecks.size(), 2); } diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index 70d83a2e54..40ef9adb2e 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -1,10 +1,10 @@ // Copyright (c) 2011-2016 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 "arith_uint256.h" -#include "uint256.h" -#include "version.h" -#include "test/test_bitcoin.h" +#include <arith_uint256.h> +#include <uint256.h> +#include <version.h> +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> #include <stdint.h> diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp index dffe8e55a8..7386204437 100644 --- a/src/test/univalue_tests.cpp +++ b/src/test/univalue_tests.cpp @@ -8,7 +8,7 @@ #include <string> #include <map> #include <univalue.h> -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 6ec544290d..998090cd9b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "util.h" - -#include "clientversion.h" -#include "primitives/transaction.h" -#include "sync.h" -#include "utilstrencodings.h" -#include "utilmoneystr.h" -#include "test/test_bitcoin.h" +#include <util.h> + +#include <clientversion.h> +#include <primitives/transaction.h> +#include <sync.h> +#include <utilstrencodings.h> +#include <utilmoneystr.h> +#include <test/test_bitcoin.h> #include <stdint.h> #include <vector> diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 882afb2e20..7029b347dd 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chain.h" -#include "versionbits.h" -#include "test/test_bitcoin.h" -#include "chainparams.h" -#include "validation.h" -#include "consensus/params.h" +#include <chain.h> +#include <versionbits.h> +#include <test/test_bitcoin.h> +#include <chainparams.h> +#include <validation.h> +#include <consensus/params.h> #include <boost/test/unit_test.hpp> @@ -32,6 +32,12 @@ public: int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); } }; +class TestAlwaysActiveConditionChecker : public TestConditionChecker +{ +public: + int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::ALWAYS_ACTIVE; } +}; + #define CHECKERS 6 class VersionBitsTester @@ -43,6 +49,8 @@ class VersionBitsTester // The first one performs all checks, the second only 50%, the third only 25%, etc... // This is to test whether lack of cached information leads to the same results. TestConditionChecker checker[CHECKERS]; + // Another 6 that assume always active activation + TestAlwaysActiveConditionChecker checker_always[CHECKERS]; // Test counter (to identify failures) int num; @@ -56,6 +64,7 @@ public: } for (unsigned int i = 0; i < CHECKERS; i++) { checker[i] = TestConditionChecker(); + checker_always[i] = TestAlwaysActiveConditionChecker(); } vpblock.clear(); return *this; @@ -82,6 +91,7 @@ public: for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back()) == 0, strprintf("Test %i for StateSinceHeight (always active)", num)); } } num++; @@ -92,6 +102,7 @@ public: for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -102,6 +113,7 @@ public: for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -112,6 +124,7 @@ public: for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -122,6 +135,7 @@ public: for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; @@ -132,6 +146,7 @@ public: for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num)); + BOOST_CHECK_MESSAGE(checker_always[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE (always active)", num)); } } num++; diff --git a/src/threadinterrupt.cpp b/src/threadinterrupt.cpp index 9d691079ed..7bcacc3014 100644 --- a/src/threadinterrupt.cpp +++ b/src/threadinterrupt.cpp @@ -3,7 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "threadinterrupt.h" +#include <threadinterrupt.h> CThreadInterrupt::operator bool() const { diff --git a/src/timedata.cpp b/src/timedata.cpp index 5113bb60db..b91cf01723 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -3,17 +3,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "timedata.h" +#include <timedata.h> -#include "netaddress.h" -#include "sync.h" -#include "ui_interface.h" -#include "util.h" -#include "utilstrencodings.h" -#include "warnings.h" +#include <netaddress.h> +#include <sync.h> +#include <ui_interface.h> +#include <util.h> +#include <utilstrencodings.h> +#include <warnings.h> static CCriticalSection cs_nTimeOffset; diff --git a/src/tinyformat.h b/src/tinyformat.h index 2e453e56bb..d34cfaa94f 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -495,7 +495,11 @@ namespace detail { class FormatArg { public: - FormatArg() {} + FormatArg() + : m_value(nullptr), + m_formatImpl(nullptr), + m_toIntImpl(nullptr) + { } template<typename T> explicit FormatArg(const T& value) @@ -507,11 +511,15 @@ class FormatArg void format(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc) const { + assert(m_value); + assert(m_formatImpl); m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } int toInt() const { + assert(m_value); + assert(m_toIntImpl); return m_toIntImpl(m_value); } @@ -712,23 +720,27 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi break; case 'X': out.setf(std::ios::uppercase); + // Falls through case 'x': case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); + // Falls through case 'e': out.setf(std::ios::scientific, std::ios::floatfield); out.setf(std::ios::dec, std::ios::basefield); break; case 'F': out.setf(std::ios::uppercase); + // Falls through case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; case 'G': out.setf(std::ios::uppercase); + // Falls through case 'g': out.setf(std::ios::dec, std::ios::basefield); // As in boost::format, let stream decide float format. diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 1cea197666..46f39d65a0 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -3,12 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "torcontrol.h" -#include "utilstrencodings.h" -#include "netbase.h" -#include "net.h" -#include "util.h" -#include "crypto/hmac_sha256.h" +#include <torcontrol.h> +#include <utilstrencodings.h> +#include <netbase.h> +#include <net.h> +#include <util.h> +#include <crypto/hmac_sha256.h> #include <vector> #include <deque> diff --git a/src/torcontrol.h b/src/torcontrol.h index 72dc82c5b1..a01eeefeb8 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -8,7 +8,7 @@ #ifndef BITCOIN_TORCONTROL_H #define BITCOIN_TORCONTROL_H -#include "scheduler.h" +#include <scheduler.h> extern const std::string DEFAULT_TOR_CONTROL; static const bool DEFAULT_LISTEN_ONION = true; diff --git a/src/txdb.cpp b/src/txdb.cpp index 134bb8721b..7a8e854b55 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -3,16 +3,16 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "txdb.h" - -#include "chainparams.h" -#include "hash.h" -#include "random.h" -#include "pow.h" -#include "uint256.h" -#include "util.h" -#include "ui_interface.h" -#include "init.h" +#include <txdb.h> + +#include <chainparams.h> +#include <hash.h> +#include <random.h> +#include <pow.h> +#include <uint256.h> +#include <util.h> +#include <ui_interface.h> +#include <init.h> #include <stdint.h> diff --git a/src/txdb.h b/src/txdb.h index d1cd5a4250..2e8095e3cd 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -6,9 +6,9 @@ #ifndef BITCOIN_TXDB_H #define BITCOIN_TXDB_H -#include "coins.h" -#include "dbwrapper.h" -#include "chain.h" +#include <coins.h> +#include <dbwrapper.h> +#include <chain.h> #include <map> #include <string> @@ -110,17 +110,17 @@ class CBlockTreeDB : public CDBWrapper { public: explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); -private: - CBlockTreeDB(const CBlockTreeDB&); - void operator=(const CBlockTreeDB&); -public: + + CBlockTreeDB(const CBlockTreeDB&) = delete; + CBlockTreeDB& operator=(const CBlockTreeDB&) = delete; + bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo); - bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); + bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info); bool ReadLastBlockFile(int &nFile); - bool WriteReindexing(bool fReindex); - bool ReadReindexing(bool &fReindex); + bool WriteReindexing(bool fReindexing); + bool ReadReindexing(bool &fReindexing); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); - bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list); + bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &vect); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f68d677646..392323808f 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -3,20 +3,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "txmempool.h" - -#include "consensus/consensus.h" -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "validation.h" -#include "policy/policy.h" -#include "policy/fees.h" -#include "reverse_iterator.h" -#include "streams.h" -#include "timedata.h" -#include "util.h" -#include "utilmoneystr.h" -#include "utiltime.h" +#include <txmempool.h> + +#include <consensus/consensus.h> +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <validation.h> +#include <policy/policy.h> +#include <policy/fees.h> +#include <reverse_iterator.h> +#include <streams.h> +#include <timedata.h> +#include <util.h> +#include <utilmoneystr.h> +#include <utiltime.h> CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _entryHeight, @@ -317,7 +317,7 @@ void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFe assert(int64_t(nCountWithDescendants) > 0); } -void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps) +void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps) { nSizeWithAncestors += modifySize; assert(int64_t(nSizeWithAncestors) > 0); @@ -607,6 +607,15 @@ void CTxMemPool::clear() _clear(); } +static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight) +{ + CValidationState state; + CAmount txfee = 0; + bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee); + assert(fCheckResult); + UpdateCoins(tx, mempoolDuplicate, 1000000); +} + void CTxMemPool::check(const CCoinsViewCache *pcoins) const { if (nCheckFrequency == 0) @@ -621,7 +630,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins)); - const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); + const int64_t spendheight = GetSpendHeight(mempoolDuplicate); LOCK(cs); std::list<const CTxMemPoolEntry*> waitingOnDependants; @@ -700,11 +709,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const if (fDependsWait) waitingOnDependants.push_back(&(*it)); else { - CValidationState state; - bool fCheckResult = tx.IsCoinBase() || - Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight); - assert(fCheckResult); - UpdateCoins(tx, mempoolDuplicate, 1000000); + CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); } } unsigned int stepsSinceLastRemove = 0; @@ -717,10 +722,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { - bool fCheckResult = entry->GetTx().IsCoinBase() || - Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight); - assert(fCheckResult); - UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); + CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight); stepsSinceLastRemove = 0; } } @@ -981,7 +983,7 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { LOCK(cs); if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) - return CFeeRate(rollingMinimumFeeRate); + return CFeeRate(llround(rollingMinimumFeeRate)); int64_t time = GetTime(); if (time > lastRollingFeeUpdate + 10) { @@ -999,7 +1001,7 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { return CFeeRate(0); } } - return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee); + return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee); } void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { diff --git a/src/txmempool.h b/src/txmempool.h index b07886579c..346585ab11 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -13,13 +13,13 @@ #include <utility> #include <string> -#include "amount.h" -#include "coins.h" -#include "indirectmap.h" -#include "policy/feerate.h" -#include "primitives/transaction.h" -#include "sync.h" -#include "random.h" +#include <amount.h> +#include <coins.h> +#include <indirectmap.h> +#include <policy/feerate.h> +#include <primitives/transaction.h> +#include <sync.h> +#include <random.h> #include <boost/multi_index_container.hpp> #include <boost/multi_index/hashed_index.hpp> @@ -109,7 +109,7 @@ public: // Adjusts the descendant state. void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); // Adjusts the ancestor state - void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps); + void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps); // Updates the fee delta used for mining priority score, and the // modified fees with descendants. void UpdateFeeDelta(int64_t feeDelta); @@ -507,12 +507,15 @@ public: * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967295.0; } + void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); } // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of // addUnchecked can be used to have it call CalculateMemPoolAncestors(), and // then invoke the second version. + // Note that addUnchecked is ONLY called from ATMP outside of tests + // and any other callers may break wallet's in-mempool tracking (due to + // lack of CValidationInterface::TransactionAddedToMempool callbacks). bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool validFeeEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate = true); diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp index 74a13e0e05..c0f7defa46 100644 --- a/src/ui_interface.cpp +++ b/src/ui_interface.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "ui_interface.h" -#include "util.h" +#include <ui_interface.h> +#include <util.h> CClientUIInterface uiInterface; diff --git a/src/uint256.cpp b/src/uint256.cpp index 736a0d4fe2..b7255231c1 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -3,9 +3,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "uint256.h" +#include <uint256.h> -#include "utilstrencodings.h" +#include <utilstrencodings.h> #include <stdio.h> #include <string.h> diff --git a/src/uint256.h b/src/uint256.h index 3ed694d723..e45c990dae 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -12,14 +12,14 @@ #include <stdint.h> #include <string> #include <vector> -#include "crypto/common.h" +#include <crypto/common.h> /** Template base class for fixed-sized opaque blobs. */ template<unsigned int BITS> class base_blob { protected: - enum { WIDTH=BITS/8 }; + static constexpr int WIDTH = BITS / 8; uint8_t data[WIDTH]; public: base_blob() @@ -111,7 +111,6 @@ public: class uint160 : public base_blob<160> { public: uint160() {} - explicit uint160(const base_blob<160>& b) : base_blob<160>(b) {} explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {} }; @@ -123,7 +122,6 @@ public: class uint256 : public base_blob<256> { public: uint256() {} - explicit uint256(const base_blob<256>& b) : base_blob<256>(b) {} explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {} /** A cheap hash function that just returns 64 bits from the result, it can be diff --git a/src/undo.h b/src/undo.h index a720de4ac5..b482fd3089 100644 --- a/src/undo.h +++ b/src/undo.h @@ -6,10 +6,10 @@ #ifndef BITCOIN_UNDO_H #define BITCOIN_UNDO_H -#include "compressor.h" -#include "consensus/consensus.h" -#include "primitives/transaction.h" -#include "serialize.h" +#include <compressor.h> +#include <consensus/consensus.h> +#include <primitives/transaction.h> +#include <serialize.h> /** Undo information for a CTxIn * diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am index 6c1ec81e63..e283fc890e 100644 --- a/src/univalue/Makefile.am +++ b/src/univalue/Makefile.am @@ -12,6 +12,7 @@ pkgconfig_DATA = pc/libunivalue.pc libunivalue_la_SOURCES = \ lib/univalue.cpp \ + lib/univalue_get.cpp \ lib/univalue_read.cpp \ lib/univalue_write.cpp @@ -20,7 +21,7 @@ libunivalue_la_LDFLAGS = \ -no-undefined libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include -TESTS = test/unitester +TESTS = test/object test/unitester test/no_nul GENBIN = gen/gen$(BUILD_EXEEXT) GEN_SRCS = gen/gen.cpp @@ -33,7 +34,7 @@ gen: lib/univalue_escapes.h $(GENBIN) @echo Updating $< $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h -noinst_PROGRAMS = $(TESTS) +noinst_PROGRAMS = $(TESTS) test/test_json TEST_DATA_DIR=test @@ -42,6 +43,21 @@ test_unitester_LDADD = libunivalue.la test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\" test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) +test_test_json_SOURCES = test/test_json.cpp +test_test_json_LDADD = libunivalue.la +test_test_json_CXXFLAGS = -I$(top_srcdir)/include +test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_no_nul_SOURCES = test/no_nul.cpp +test_no_nul_LDADD = libunivalue.la +test_no_nul_CXXFLAGS = -I$(top_srcdir)/include +test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_object_SOURCES = test/object.cpp +test_object_LDADD = libunivalue.la +test_object_CXXFLAGS = -I$(top_srcdir)/include +test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + TEST_FILES = \ $(TEST_DATA_DIR)/fail10.json \ $(TEST_DATA_DIR)/fail11.json \ @@ -77,6 +93,8 @@ TEST_FILES = \ $(TEST_DATA_DIR)/fail39.json \ $(TEST_DATA_DIR)/fail40.json \ $(TEST_DATA_DIR)/fail41.json \ + $(TEST_DATA_DIR)/fail42.json \ + $(TEST_DATA_DIR)/fail44.json \ $(TEST_DATA_DIR)/fail3.json \ $(TEST_DATA_DIR)/fail4.json \ $(TEST_DATA_DIR)/fail5.json \ @@ -88,6 +106,11 @@ TEST_FILES = \ $(TEST_DATA_DIR)/pass2.json \ $(TEST_DATA_DIR)/pass3.json \ $(TEST_DATA_DIR)/round1.json \ - $(TEST_DATA_DIR)/round2.json + $(TEST_DATA_DIR)/round2.json \ + $(TEST_DATA_DIR)/round3.json \ + $(TEST_DATA_DIR)/round4.json \ + $(TEST_DATA_DIR)/round5.json \ + $(TEST_DATA_DIR)/round6.json \ + $(TEST_DATA_DIR)/round7.json EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) diff --git a/src/univalue/README b/src/univalue/README deleted file mode 100644 index 48167b083b..0000000000 --- a/src/univalue/README +++ /dev/null @@ -1,7 +0,0 @@ - - UniValue - -A universal value object, with JSON encoding (output) and decoding (input). - -Built as a single dynamic RAII C++ object class, and no templates. - diff --git a/src/univalue/README.md b/src/univalue/README.md new file mode 100644 index 0000000000..36aa786a4c --- /dev/null +++ b/src/univalue/README.md @@ -0,0 +1,32 @@ + +# UniValue + +## Summary + +A universal value class, with JSON encoding and decoding. + +UniValue is an abstract data type that may be a null, boolean, string, +number, array container, or a key/value dictionary container, nested to +an arbitrary depth. + +This class is aligned with the JSON standard, [RFC +7159](https://tools.ietf.org/html/rfc7159.html). + +## Installation + +This project is a standard GNU +[autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) +project. Build and install instructions are available in the `INSTALL` +file provided with GNU autotools. + +``` +$ ./autogen.sh +$ ./configure +$ make +``` + +## Design + +UniValue provides a single dynamic RAII C++ object class, +and minimizes template use (contra json_spirit). + diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac index 93d3ba945d..8298332ac1 100644 --- a/src/univalue/configure.ac +++ b/src/univalue/configure.ac @@ -1,7 +1,7 @@ m4_define([libunivalue_major_version], [1]) m4_define([libunivalue_minor_version], [1]) -m4_define([libunivalue_micro_version], [2]) -m4_define([libunivalue_interface_age], [2]) +m4_define([libunivalue_micro_version], [3]) +m4_define([libunivalue_interface_age], [3]) # If you need a modifier for the version number. # Normally empty, but can be used to make "fixup" releases. m4_define([libunivalue_extraversion], []) @@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) -AC_INIT([univalue], [1.0.2], +AC_INIT([univalue], [1.0.3], [http://github.com/jgarzik/univalue/]) dnl make the compilation flags quiet unless V=1 is used diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index e8ce283519..4fd2223b30 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -7,6 +7,7 @@ #define __UNIVALUE_H__ #include <stdint.h> +#include <string.h> #include <string> #include <vector> @@ -69,10 +70,11 @@ public: size_t size() const { return values.size(); } bool getBool() const { return isTrue(); } - bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes); + void getObjMap(std::map<std::string,UniValue>& kv) const; + bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes) const; const UniValue& operator[](const std::string& key) const; - const UniValue& operator[](unsigned int index) const; - bool exists(const std::string& key) const { return (findKey(key) >= 0); } + const UniValue& operator[](size_t index) const; + bool exists(const std::string& key) const { size_t i; return findKey(key, i); } bool isNull() const { return (typ == VNULL); } bool isTrue() const { return (typ == VBOOL) && (val == "1"); } @@ -92,8 +94,25 @@ public: std::string s(val_); return push_back(s); } + bool push_back(uint64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(double val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } bool push_backV(const std::vector<UniValue>& vec); + void __pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const std::string& val_) { UniValue tmpVal(VSTR, val_); @@ -124,9 +143,10 @@ public: std::string write(unsigned int prettyIndent = 0, unsigned int indentLevel = 0) const; - bool read(const char *raw); + bool read(const char *raw, size_t len); + bool read(const char *raw) { return read(raw, strlen(raw)); } bool read(const std::string& rawStr) { - return read(rawStr.c_str()); + return read(rawStr.data(), rawStr.size()); } private: @@ -135,7 +155,7 @@ private: std::vector<std::string> keys; std::vector<UniValue> values; - int findKey(const std::string& key) const; + bool findKey(const std::string& key, size_t& retIdx) const; void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; @@ -240,7 +260,7 @@ enum jtokentype { }; extern enum jtokentype getJsonToken(std::string& tokenVal, - unsigned int& consumed, const char *raw); + unsigned int& consumed, const char *raw, const char *end); extern const char *uvTypeName(UniValue::VType t); static inline bool jsonTokenIsValue(enum jtokentype jtt) diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index 5a2860c13f..d8ad7c4b90 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -4,75 +4,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <stdint.h> -#include <errno.h> #include <iomanip> -#include <limits> #include <sstream> -#include <stdexcept> #include <stdlib.h> -#include <string.h> #include "univalue.h" -namespace -{ -static bool ParsePrechecks(const std::string& str) -{ - if (str.empty()) // No empty string allowed - return false; - if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed - return false; - if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed - return false; - return true; -} - -bool ParseInt32(const std::string& str, int32_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtol will not set errno if valid - long int n = strtol(str.c_str(), &endp, 10); - if(out) *out = (int32_t)n; - // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit - // platforms the size of these types may be different. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits<int32_t>::min() && - n <= std::numeric_limits<int32_t>::max(); -} - -bool ParseInt64(const std::string& str, int64_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtoll will not set errno if valid - long long int n = strtoll(str.c_str(), &endp, 10); - if(out) *out = (int64_t)n; - // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int64_t*. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits<int64_t>::min() && - n <= std::numeric_limits<int64_t>::max(); -} - -bool ParseDouble(const std::string& str, double *out) -{ - if (!ParsePrechecks(str)) - return false; - if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed - return false; - std::istringstream text(str); - text.imbue(std::locale::classic()); - double result; - text >> result; - if(out) *out = result; - return text.eof() && !text.fail(); -} -} - using namespace std; const UniValue NullUniValue; @@ -104,7 +41,7 @@ static bool validNumStr(const string& s) { string tokenVal; unsigned int consumed; - enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str()); + enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } @@ -189,13 +126,22 @@ bool UniValue::push_backV(const std::vector<UniValue>& vec) return true; } +void UniValue::__pushKV(const std::string& key, const UniValue& val_) +{ + keys.push_back(key); + values.push_back(val_); +} + bool UniValue::pushKV(const std::string& key, const UniValue& val_) { if (typ != VOBJ) return false; - keys.push_back(key); - values.push_back(val_); + size_t idx; + if (findKey(key, idx)) + values[idx] = val_; + else + __pushKV(key, val_); return true; } @@ -204,30 +150,43 @@ bool UniValue::pushKVs(const UniValue& obj) if (typ != VOBJ || obj.typ != VOBJ) return false; - for (unsigned int i = 0; i < obj.keys.size(); i++) { - keys.push_back(obj.keys[i]); - values.push_back(obj.values.at(i)); - } + for (size_t i = 0; i < obj.keys.size(); i++) + __pushKV(obj.keys[i], obj.values.at(i)); return true; } -int UniValue::findKey(const std::string& key) const +void UniValue::getObjMap(std::map<std::string,UniValue>& kv) const +{ + if (typ != VOBJ) + return; + + kv.clear(); + for (size_t i = 0; i < keys.size(); i++) + kv[keys[i]] = values[i]; +} + +bool UniValue::findKey(const std::string& key, size_t& retIdx) const { - for (unsigned int i = 0; i < keys.size(); i++) { - if (keys[i] == key) - return (int) i; + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i] == key) { + retIdx = i; + return true; + } } - return -1; + return false; } -bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) +bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const { + if (typ != VOBJ) + return false; + for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin(); it != t.end(); ++it) { - int idx = findKey(it->first); - if (idx < 0) + size_t idx = 0; + if (!findKey(it->first, idx)) return false; if (values.at(idx).getType() != it->second) @@ -242,14 +201,14 @@ const UniValue& UniValue::operator[](const std::string& key) const if (typ != VOBJ) return NullUniValue; - int index = findKey(key); - if (index < 0) + size_t index = 0; + if (!findKey(key, index)) return NullUniValue; return values.at(index); } -const UniValue& UniValue::operator[](unsigned int index) const +const UniValue& UniValue::operator[](size_t index) const { if (typ != VOBJ && typ != VARR) return NullUniValue; @@ -283,75 +242,3 @@ const UniValue& find_value(const UniValue& obj, const std::string& name) return NullUniValue; } -const std::vector<std::string>& UniValue::getKeys() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return keys; -} - -const std::vector<UniValue>& UniValue::getValues() const -{ - if (typ != VOBJ && typ != VARR) - throw std::runtime_error("JSON value is not an object or array as expected"); - return values; -} - -bool UniValue::get_bool() const -{ - if (typ != VBOOL) - throw std::runtime_error("JSON value is not a boolean as expected"); - return getBool(); -} - -const std::string& UniValue::get_str() const -{ - if (typ != VSTR) - throw std::runtime_error("JSON value is not a string as expected"); - return getValStr(); -} - -int UniValue::get_int() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int32_t retval; - if (!ParseInt32(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -int64_t UniValue::get_int64() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int64_t retval; - if (!ParseInt64(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -double UniValue::get_real() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not a number as expected"); - double retval; - if (!ParseDouble(getValStr(), &retval)) - throw std::runtime_error("JSON double out of range"); - return retval; -} - -const UniValue& UniValue::get_obj() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return *this; -} - -const UniValue& UniValue::get_array() const -{ - if (typ != VARR) - throw std::runtime_error("JSON value is not an array as expected"); - return *this; -} - diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp new file mode 100644 index 0000000000..eabcf2dad1 --- /dev/null +++ b/src/univalue/lib/univalue_get.cpp @@ -0,0 +1,147 @@ +// Copyright 2014 BitPay Inc. +// Copyright 2015 Bitcoin Core Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdexcept> +#include <vector> +#include <limits> +#include <string> + +#include "univalue.h" + +namespace +{ +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + +bool ParseInt32(const std::string& str, int32_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtol will not set errno if valid + long int n = strtol(str.c_str(), &endp, 10); + if(out) *out = (int32_t)n; + // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits<int32_t>::min() && + n <= std::numeric_limits<int32_t>::max(); +} + +bool ParseInt64(const std::string& str, int64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtoll will not set errno if valid + long long int n = strtoll(str.c_str(), &endp, 10); + if(out) *out = (int64_t)n; + // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int64_t*. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits<int64_t>::min() && + n <= std::numeric_limits<int64_t>::max(); +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); +} +} + +const std::vector<std::string>& UniValue::getKeys() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return keys; +} + +const std::vector<UniValue>& UniValue::getValues() const +{ + if (typ != VOBJ && typ != VARR) + throw std::runtime_error("JSON value is not an object or array as expected"); + return values; +} + +bool UniValue::get_bool() const +{ + if (typ != VBOOL) + throw std::runtime_error("JSON value is not a boolean as expected"); + return getBool(); +} + +const std::string& UniValue::get_str() const +{ + if (typ != VSTR) + throw std::runtime_error("JSON value is not a string as expected"); + return getValStr(); +} + +int UniValue::get_int() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int32_t retval; + if (!ParseInt32(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +int64_t UniValue::get_int64() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int64_t retval; + if (!ParseInt64(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +double UniValue::get_real() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not a number as expected"); + double retval; + if (!ParseDouble(getValStr(), &retval)) + throw std::runtime_error("JSON double out of range"); + return retval; +} + +const UniValue& UniValue::get_obj() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return *this; +} + +const UniValue& UniValue::get_array() const +{ + if (typ != VARR) + throw std::runtime_error("JSON value is not an array as expected"); + return *this; +} + diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index 95bac6958d..ae75cb462a 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last, } enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, - const char *raw) + const char *raw, const char *end) { tokenVal.clear(); consumed = 0; const char *rawStart = raw; - while ((*raw) && (json_isspace(*raw))) // skip whitespace + while (raw < end && (json_isspace(*raw))) // skip whitespace raw++; - switch (*raw) { - - case 0: + if (raw >= end) return JTOK_NONE; + switch (*raw) { + case '{': raw++; consumed = (raw - rawStart); @@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, numStr += *raw; // copy first char raw++; - if ((*first == '-') && (!json_isdigit(*raw))) + if ((*first == '-') && (raw < end) && (!json_isdigit(*raw))) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } // part 2: frac - if (*raw == '.') { + if (raw < end && *raw == '.') { numStr += *raw; // copy . raw++; - if (!json_isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } // part 3: exp - if (*raw == 'e' || *raw == 'E') { + if (raw < end && (*raw == 'e' || *raw == 'E')) { numStr += *raw; // copy E raw++; - if (*raw == '-' || *raw == '+') { // copy +/- + if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/- numStr += *raw; raw++; } - if (!json_isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } @@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, string valStr; JSONUTF8StringFilter writer(valStr); - while (*raw) { - if ((unsigned char)*raw < 0x20) + while (true) { + if (raw >= end || (unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash + if (raw >= end) + return JTOK_ERR; + switch (*raw) { case '"': writer.push_back('\"'); break; case '\\': writer.push_back('\\'); break; @@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case 'u': { unsigned int codepoint; - if (hatoui(raw + 1, raw + 1 + 4, codepoint) != + if (raw + 1 + 4 >= end || + hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; writer.push_back_u(codepoint); @@ -246,7 +250,7 @@ enum expect_bits { #define setExpect(bit) (expectMask |= EXP_##bit) #define clearExpect(bit) (expectMask &= ~EXP_##bit) -bool UniValue::read(const char *raw) +bool UniValue::read(const char *raw, size_t size) { clear(); @@ -257,10 +261,11 @@ bool UniValue::read(const char *raw) unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; + const char* end = raw + size; do { last_tok = tok; - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok == JTOK_NONE || tok == JTOK_ERR) return false; raw += consumed; @@ -371,9 +376,6 @@ bool UniValue::read(const char *raw) case JTOK_KW_NULL: case JTOK_KW_TRUE: case JTOK_KW_FALSE: { - if (!stack.size()) - return false; - UniValue tmpVal; switch (tok) { case JTOK_KW_NULL: @@ -388,6 +390,11 @@ bool UniValue::read(const char *raw) default: /* impossible */ break; } + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); @@ -396,10 +403,12 @@ bool UniValue::read(const char *raw) } case JTOK_NUMBER: { - if (!stack.size()) - return false; - UniValue tmpVal(VNUM, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); @@ -408,17 +417,18 @@ bool UniValue::read(const char *raw) } case JTOK_STRING: { - if (!stack.size()) - return false; - - UniValue *top = stack.back(); - if (expect(OBJ_NAME)) { + UniValue *top = stack.back(); top->keys.push_back(tokenVal); clearExpect(OBJ_NAME); setExpect(COLON); } else { UniValue tmpVal(VSTR, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); } @@ -432,7 +442,7 @@ bool UniValue::read(const char *raw) } while (!stack.empty ()); /* Check that nothing follows the initial construct (parsed above). */ - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok != JTOK_NONE) return false; diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h index 2fb6a492d1..20d4043009 100644 --- a/src/univalue/lib/univalue_utffilter.h +++ b/src/univalue/lib/univalue_utffilter.h @@ -46,19 +46,19 @@ public: } } // Write codepoint directly, possibly collating surrogate pairs - void push_back_u(unsigned int codepoint) + void push_back_u(unsigned int codepoint_) { if (state) // Only accept full codepoints in open state is_valid = false; - if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair + if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair if (surpair) // Two subsequent surrogate pair openers - fail is_valid = false; else - surpair = codepoint; - } else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair + surpair = codepoint_; + } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair if (surpair) { // Open surrogate pair, expect second half // Compute code point from UTF-16 surrogate pair - append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00)); + append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00)); surpair = 0; } else // Second half doesn't follow a first half - fail is_valid = false; @@ -66,7 +66,7 @@ public: if (surpair) // First half of surrogate pair not followed by second - fail is_valid = false; else - append_codepoint(codepoint); + append_codepoint(codepoint_); } } // Check that we're in a state where the string can be ended @@ -96,22 +96,22 @@ private: // Two subsequent \u.... may have to be replaced with one actual codepoint. unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0 - void append_codepoint(unsigned int codepoint) + void append_codepoint(unsigned int codepoint_) { - if (codepoint <= 0x7f) - str.push_back((char)codepoint); - else if (codepoint <= 0x7FF) { - str.push_back((char)(0xC0 | (codepoint >> 6))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0xFFFF) { - str.push_back((char)(0xE0 | (codepoint >> 12))); - str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0x1FFFFF) { - str.push_back((char)(0xF0 | (codepoint >> 18))); - str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F))); - str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); + if (codepoint_ <= 0x7f) + str.push_back((char)codepoint_); + else if (codepoint_ <= 0x7FF) { + str.push_back((char)(0xC0 | (codepoint_ >> 6))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0xFFFF) { + str.push_back((char)(0xE0 | (codepoint_ >> 12))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0x1FFFFF) { + str.push_back((char)(0xF0 | (codepoint_ >> 18))); + str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); } } }; diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index cfbdad3284..cf27835991 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -79,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += values[i].write(prettyIndent, indentLevel + 1); if (i != (values.size() - 1)) { s += ","; - if (prettyIndent) - s += " "; } if (prettyIndent) s += "\n"; diff --git a/src/univalue/test/.gitignore b/src/univalue/test/.gitignore index 3d9347fe7e..7b27cf0da2 100644 --- a/src/univalue/test/.gitignore +++ b/src/univalue/test/.gitignore @@ -1,4 +1,8 @@ + +object unitester +test_json +no_nul *.trs *.log diff --git a/src/univalue/test/fail1.json b/src/univalue/test/fail1.json index 6216b865f1..8feb01a6d0 100644 --- a/src/univalue/test/fail1.json +++ b/src/univalue/test/fail1.json @@ -1 +1 @@ -"A JSON payload should be an object or array, not a string."
\ No newline at end of file +"This is a string that never ends, yes it goes on and on, my friends. diff --git a/src/univalue/test/fail42.json b/src/univalue/test/fail42.json Binary files differnew file mode 100644 index 0000000000..9c7565adbd --- /dev/null +++ b/src/univalue/test/fail42.json diff --git a/src/univalue/test/fail44.json b/src/univalue/test/fail44.json new file mode 100644 index 0000000000..80edceddf1 --- /dev/null +++ b/src/univalue/test/fail44.json @@ -0,0 +1 @@ +"This file ends without a newline or close-quote.
\ No newline at end of file diff --git a/src/univalue/test/no_nul.cpp b/src/univalue/test/no_nul.cpp new file mode 100644 index 0000000000..83d292200b --- /dev/null +++ b/src/univalue/test/no_nul.cpp @@ -0,0 +1,8 @@ +#include "univalue.h" + +int main (int argc, char *argv[]) +{ + char buf[] = "___[1,2,3]___"; + UniValue val; + return val.read(buf + 3, 7) ? 0 : 1; +} diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp new file mode 100644 index 0000000000..02446292a1 --- /dev/null +++ b/src/univalue/test/object.cpp @@ -0,0 +1,395 @@ +// Copyright (c) 2014 BitPay Inc. +// Copyright (c) 2014-2016 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 <stdint.h> +#include <vector> +#include <string> +#include <map> +#include <cassert> +#include <stdexcept> +#include <univalue.h> + +#define BOOST_FIXTURE_TEST_SUITE(a, b) +#define BOOST_AUTO_TEST_CASE(funcName) void funcName() +#define BOOST_AUTO_TEST_SUITE_END() +#define BOOST_CHECK(expr) assert(expr) +#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2)) +#define BOOST_CHECK_THROW(stmt, excMatch) { \ + try { \ + (stmt); \ + } catch (excMatch & e) { \ + } catch (...) { \ + assert(0); \ + } \ + } +#define BOOST_CHECK_NO_THROW(stmt) { \ + try { \ + (stmt); \ + } catch (...) { \ + assert(0); \ + } \ + } + +BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(univalue_constructor) +{ + UniValue v1; + BOOST_CHECK(v1.isNull()); + + UniValue v2(UniValue::VSTR); + BOOST_CHECK(v2.isStr()); + + UniValue v3(UniValue::VSTR, "foo"); + BOOST_CHECK(v3.isStr()); + BOOST_CHECK_EQUAL(v3.getValStr(), "foo"); + + UniValue numTest; + BOOST_CHECK(numTest.setNumStr("82")); + BOOST_CHECK(numTest.isNum()); + BOOST_CHECK_EQUAL(numTest.getValStr(), "82"); + + uint64_t vu64 = 82; + UniValue v4(vu64); + BOOST_CHECK(v4.isNum()); + BOOST_CHECK_EQUAL(v4.getValStr(), "82"); + + int64_t vi64 = -82; + UniValue v5(vi64); + BOOST_CHECK(v5.isNum()); + BOOST_CHECK_EQUAL(v5.getValStr(), "-82"); + + int vi = -688; + UniValue v6(vi); + BOOST_CHECK(v6.isNum()); + BOOST_CHECK_EQUAL(v6.getValStr(), "-688"); + + double vd = -7.21; + UniValue v7(vd); + BOOST_CHECK(v7.isNum()); + BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); + + std::string vs("yawn"); + UniValue v8(vs); + BOOST_CHECK(v8.isStr()); + BOOST_CHECK_EQUAL(v8.getValStr(), "yawn"); + + const char *vcs = "zappa"; + UniValue v9(vcs); + BOOST_CHECK(v9.isStr()); + BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); +} + +BOOST_AUTO_TEST_CASE(univalue_typecheck) +{ + UniValue v1; + BOOST_CHECK(v1.setNumStr("1")); + BOOST_CHECK(v1.isNum()); + BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error); + + UniValue v2; + BOOST_CHECK(v2.setBool(true)); + BOOST_CHECK_EQUAL(v2.get_bool(), true); + BOOST_CHECK_THROW(v2.get_int(), std::runtime_error); + + UniValue v3; + BOOST_CHECK(v3.setNumStr("32482348723847471234")); + BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error); + BOOST_CHECK(v3.setNumStr("1000")); + BOOST_CHECK_EQUAL(v3.get_int64(), 1000); + + UniValue v4; + BOOST_CHECK(v4.setNumStr("2147483648")); + BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648); + BOOST_CHECK_THROW(v4.get_int(), std::runtime_error); + BOOST_CHECK(v4.setNumStr("1000")); + BOOST_CHECK_EQUAL(v4.get_int(), 1000); + BOOST_CHECK_THROW(v4.get_str(), std::runtime_error); + BOOST_CHECK_EQUAL(v4.get_real(), 1000); + BOOST_CHECK_THROW(v4.get_array(), std::runtime_error); + BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error); + BOOST_CHECK_THROW(v4.getValues(), std::runtime_error); + BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error); + + UniValue v5; + BOOST_CHECK(v5.read("[true, 10]")); + BOOST_CHECK_NO_THROW(v5.get_array()); + std::vector<UniValue> vals = v5.getValues(); + BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error); + BOOST_CHECK_EQUAL(vals[0].get_bool(), true); + + BOOST_CHECK_EQUAL(vals[1].get_int(), 10); + BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(univalue_set) +{ + UniValue v(UniValue::VSTR, "foo"); + v.clear(); + BOOST_CHECK(v.isNull()); + BOOST_CHECK_EQUAL(v.getValStr(), ""); + + BOOST_CHECK(v.setObject()); + BOOST_CHECK(v.isObject()); + BOOST_CHECK_EQUAL(v.size(), 0); + BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); + BOOST_CHECK(v.empty()); + + BOOST_CHECK(v.setArray()); + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 0); + + BOOST_CHECK(v.setStr("zum")); + BOOST_CHECK(v.isStr()); + BOOST_CHECK_EQUAL(v.getValStr(), "zum"); + + BOOST_CHECK(v.setFloat(-1.01)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); + + BOOST_CHECK(v.setInt((int)1023)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + BOOST_CHECK(v.setInt((int64_t)-1023LL)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1023"); + + BOOST_CHECK(v.setInt((uint64_t)1023ULL)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + BOOST_CHECK(v.setNumStr("-688")); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-688"); + + BOOST_CHECK(v.setBool(false)); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), false); + BOOST_CHECK_EQUAL(v.isFalse(), true); + BOOST_CHECK_EQUAL(v.getBool(), false); + + BOOST_CHECK(v.setBool(true)); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), true); + BOOST_CHECK_EQUAL(v.isFalse(), false); + BOOST_CHECK_EQUAL(v.getBool(), true); + + BOOST_CHECK(!v.setNumStr("zombocom")); + + BOOST_CHECK(v.setNull()); + BOOST_CHECK(v.isNull()); +} + +BOOST_AUTO_TEST_CASE(univalue_array) +{ + UniValue arr(UniValue::VARR); + + UniValue v((int64_t)1023LL); + BOOST_CHECK(arr.push_back(v)); + + std::string vStr("zippy"); + BOOST_CHECK(arr.push_back(vStr)); + + const char *s = "pippy"; + BOOST_CHECK(arr.push_back(s)); + + std::vector<UniValue> vec; + v.setStr("boing"); + vec.push_back(v); + + v.setStr("going"); + vec.push_back(v); + + BOOST_CHECK(arr.push_backV(vec)); + + BOOST_CHECK(arr.push_back((uint64_t) 400ULL)); + BOOST_CHECK(arr.push_back((int64_t) -400LL)); + BOOST_CHECK(arr.push_back((int) -401)); + BOOST_CHECK(arr.push_back(-40.1)); + + BOOST_CHECK_EQUAL(arr.empty(), false); + BOOST_CHECK_EQUAL(arr.size(), 9); + + BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); + BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); + BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); + BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); + BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); + BOOST_CHECK_EQUAL(arr[5].getValStr(), "400"); + BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400"); + BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401"); + BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1"); + + BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); + + arr.clear(); + BOOST_CHECK(arr.empty()); + BOOST_CHECK_EQUAL(arr.size(), 0); +} + +BOOST_AUTO_TEST_CASE(univalue_object) +{ + UniValue obj(UniValue::VOBJ); + std::string strKey, strVal; + UniValue v; + + strKey = "age"; + v.setInt(100); + BOOST_CHECK(obj.pushKV(strKey, v)); + + strKey = "first"; + strVal = "John"; + BOOST_CHECK(obj.pushKV(strKey, strVal)); + + strKey = "last"; + const char *cVal = "Smith"; + BOOST_CHECK(obj.pushKV(strKey, cVal)); + + strKey = "distance"; + BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25)); + + strKey = "time"; + BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600)); + + strKey = "calories"; + BOOST_CHECK(obj.pushKV(strKey, (int) 12)); + + strKey = "temperature"; + BOOST_CHECK(obj.pushKV(strKey, (double) 90.012)); + + UniValue obj2(UniValue::VOBJ); + BOOST_CHECK(obj2.pushKV("cat1", 9000)); + BOOST_CHECK(obj2.pushKV("cat2", 12345)); + + BOOST_CHECK(obj.pushKVs(obj2)); + + BOOST_CHECK_EQUAL(obj.empty(), false); + BOOST_CHECK_EQUAL(obj.size(), 9); + + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); + BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); + BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith"); + BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25"); + BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); + BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); + BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); + BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); + BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); + + BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), ""); + + BOOST_CHECK(obj.exists("age")); + BOOST_CHECK(obj.exists("first")); + BOOST_CHECK(obj.exists("last")); + BOOST_CHECK(obj.exists("distance")); + BOOST_CHECK(obj.exists("time")); + BOOST_CHECK(obj.exists("calories")); + BOOST_CHECK(obj.exists("temperature")); + BOOST_CHECK(obj.exists("cat1")); + BOOST_CHECK(obj.exists("cat2")); + + BOOST_CHECK(!obj.exists("nyuknyuknyuk")); + + std::map<std::string, UniValue::VType> objTypes; + objTypes["age"] = UniValue::VNUM; + objTypes["first"] = UniValue::VSTR; + objTypes["last"] = UniValue::VSTR; + objTypes["distance"] = UniValue::VNUM; + objTypes["time"] = UniValue::VNUM; + objTypes["calories"] = UniValue::VNUM; + objTypes["temperature"] = UniValue::VNUM; + objTypes["cat1"] = UniValue::VNUM; + objTypes["cat2"] = UniValue::VNUM; + BOOST_CHECK(obj.checkObject(objTypes)); + + objTypes["cat2"] = UniValue::VSTR; + BOOST_CHECK(!obj.checkObject(objTypes)); + + obj.clear(); + BOOST_CHECK(obj.empty()); + BOOST_CHECK_EQUAL(obj.size(), 0); + BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL); + + BOOST_CHECK_EQUAL(obj.setObject(), true); + UniValue uv; + uv.setInt(42); + obj.__pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42"); + + uv.setInt(43); + obj.pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43"); + + obj.pushKV("name", "foo bar"); + + std::map<std::string,UniValue> kv; + obj.getObjMap(kv); + BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43"); + BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar"); + +} + +static const char *json1 = +"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]"; + +BOOST_AUTO_TEST_CASE(univalue_readwrite) +{ + UniValue v; + BOOST_CHECK(v.read(json1)); + + std::string strJson1(json1); + BOOST_CHECK(v.read(strJson1)); + + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 2); + + BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); + + UniValue obj = v[1]; + BOOST_CHECK(obj.isObject()); + BOOST_CHECK_EQUAL(obj.size(), 3); + + BOOST_CHECK(obj["key1"].isStr()); + std::string correctValue("str"); + correctValue.push_back('\0'); + BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); + BOOST_CHECK(obj["key2"].isNum()); + BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); + BOOST_CHECK(obj["key3"].isObject()); + + BOOST_CHECK_EQUAL(strJson1, v.write()); + + /* Check for (correctly reporting) a parsing error if the initial + JSON construct is followed by more stuff. Note that whitespace + is, of course, exempt. */ + + BOOST_CHECK(v.read(" {}\n ")); + BOOST_CHECK(v.isObject()); + BOOST_CHECK(v.read(" []\n ")); + BOOST_CHECK(v.isArray()); + + BOOST_CHECK(!v.read("@{}")); + BOOST_CHECK(!v.read("{} garbage")); + BOOST_CHECK(!v.read("[]{}")); + BOOST_CHECK(!v.read("{}[]")); + BOOST_CHECK(!v.read("{} 42")); +} + +BOOST_AUTO_TEST_SUITE_END() + +int main (int argc, char *argv[]) +{ + univalue_constructor(); + univalue_typecheck(); + univalue_set(); + univalue_array(); + univalue_object(); + univalue_readwrite(); + return 0; +} + diff --git a/src/univalue/test/round3.json b/src/univalue/test/round3.json new file mode 100644 index 0000000000..7182dc2f9b --- /dev/null +++ b/src/univalue/test/round3.json @@ -0,0 +1 @@ +"abcdefghijklmnopqrstuvwxyz" diff --git a/src/univalue/test/round4.json b/src/univalue/test/round4.json new file mode 100644 index 0000000000..7f8f011eb7 --- /dev/null +++ b/src/univalue/test/round4.json @@ -0,0 +1 @@ +7 diff --git a/src/univalue/test/round5.json b/src/univalue/test/round5.json new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/src/univalue/test/round5.json @@ -0,0 +1 @@ +true diff --git a/src/univalue/test/round6.json b/src/univalue/test/round6.json new file mode 100644 index 0000000000..c508d5366f --- /dev/null +++ b/src/univalue/test/round6.json @@ -0,0 +1 @@ +false diff --git a/src/univalue/test/round7.json b/src/univalue/test/round7.json new file mode 100644 index 0000000000..19765bd501 --- /dev/null +++ b/src/univalue/test/round7.json @@ -0,0 +1 @@ +null diff --git a/src/univalue/test/test_json.cpp b/src/univalue/test/test_json.cpp new file mode 100644 index 0000000000..2943bae2b1 --- /dev/null +++ b/src/univalue/test/test_json.cpp @@ -0,0 +1,24 @@ +// Test program that can be called by the JSON test suite at +// https://github.com/nst/JSONTestSuite. +// +// It reads JSON input from stdin and exits with code 0 if it can be parsed +// successfully. It also pretty prints the parsed JSON value to stdout. + +#include <iostream> +#include <string> +#include "univalue.h" + +using namespace std; + +int main (int argc, char *argv[]) +{ + UniValue val; + if (val.read(string(istreambuf_iterator<char>(cin), + istreambuf_iterator<char>()))) { + cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl; + return 0; + } else { + cerr << "JSON Parse Error." << endl; + return 1; + } +} diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp index 05f3842cd1..2c37794a4b 100644 --- a/src/univalue/test/unitester.cpp +++ b/src/univalue/test/unitester.cpp @@ -113,6 +113,8 @@ static const char *filenames[] = { "fail39.json", // invalid unicode: only second half of surrogate pair "fail40.json", // invalid unicode: broken UTF-8 "fail41.json", // invalid unicode: unfinished UTF-8 + "fail42.json", // valid json with garbage following a nul byte + "fail44.json", // unterminated string "fail3.json", "fail4.json", // extra comma "fail5.json", @@ -125,6 +127,11 @@ static const char *filenames[] = { "pass3.json", "round1.json", // round-trip test "round2.json", // unicode + "round3.json", // bare string + "round4.json", // bare number + "round5.json", // bare true + "round6.json", // bare false + "round7.json", // bare null }; // Test \u handling diff --git a/src/util.cpp b/src/util.cpp index 4659ff73c6..b2023b8322 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -4,17 +4,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "util.h" +#include <util.h> -#include "chainparamsbase.h" -#include "fs.h" -#include "random.h" -#include "serialize.h" -#include "utilstrencodings.h" -#include "utiltime.h" +#include <chainparamsbase.h> +#include <fs.h> +#include <random.h> +#include <serialize.h> +#include <utilstrencodings.h> +#include <utiltime.h> #include <stdarg.h> @@ -220,6 +220,7 @@ struct CLogCategoryDesc const CLogCategoryDesc LogCategories[] = { {BCLog::NONE, "0"}, + {BCLog::NONE, "none"}, {BCLog::NET, "net"}, {BCLog::TOR, "tor"}, {BCLog::MEMPOOL, "mempool"}, @@ -574,7 +575,10 @@ const fs::path &GetDataDir(bool fNetSpecific) if (fNetSpecific) path /= BaseParams().DataDir(); - fs::create_directories(path); + if (fs::create_directories(path)) { + // This is the first run, create wallets subdirectory too + fs::create_directories(path / "wallets"); + } return path; } @@ -810,6 +814,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate) void runCommand(const std::string& strCommand) { + if (strCommand.empty()) return; int nErr = ::system(strCommand.c_str()); if (nErr) LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); diff --git a/src/util.h b/src/util.h index 480a80c0a3..08de43d29f 100644 --- a/src/util.h +++ b/src/util.h @@ -11,14 +11,14 @@ #define BITCOIN_UTIL_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "compat.h" -#include "fs.h" -#include "sync.h" -#include "tinyformat.h" -#include "utiltime.h" +#include <compat.h> +#include <fs.h> +#include <sync.h> +#include <tinyformat.h> +#include <utiltime.h> #include <atomic> #include <exception> @@ -28,6 +28,7 @@ #include <vector> #include <boost/signals2/signal.hpp> +#include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted // Application startup time (used for uptime calculation) int64_t GetStartupTime(); @@ -326,4 +327,11 @@ template <typename Callable> void TraceThread(const char* name, Callable func) std::string CopyrightHolders(const std::string& strPrefix); +//! Substitute for C++14 std::make_unique. +template <typename T, typename... Args> +std::unique_ptr<T> MakeUnique(Args&&... args) +{ + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + #endif // BITCOIN_UTIL_H diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp index 6e6e33184e..656d7c51b7 100644 --- a/src/utilmoneystr.cpp +++ b/src/utilmoneystr.cpp @@ -3,11 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "utilmoneystr.h" +#include <utilmoneystr.h> -#include "primitives/transaction.h" -#include "tinyformat.h" -#include "utilstrencodings.h" +#include <primitives/transaction.h> +#include <tinyformat.h> +#include <utilstrencodings.h> std::string FormatMoney(const CAmount& n) { diff --git a/src/utilmoneystr.h b/src/utilmoneystr.h index bc885ee167..b1a4499e90 100644 --- a/src/utilmoneystr.h +++ b/src/utilmoneystr.h @@ -12,7 +12,7 @@ #include <stdint.h> #include <string> -#include "amount.h" +#include <amount.h> /* Do not use these functions to represent or parse monetary amounts to or from * JSON but use AmountFromValue and ValueFromAmount for that. diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 741680e93f..d90713ed6a 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -3,9 +3,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "utilstrencodings.h" +#include <utilstrencodings.h> -#include "tinyformat.h" +#include <tinyformat.h> #include <cstdlib> #include <cstring> diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 192f33fb29..af33f0e5f8 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -149,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b) */ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +/** Convert from one power-of-2 number base to another. */ +template<int frombits, int tobits, bool pad, typename O, typename I> +bool ConvertBits(O& out, I it, I end) { + size_t acc = 0; + size_t bits = 0; + constexpr size_t maxv = (1 << tobits) - 1; + constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; + while (it != end) { + acc = ((acc << frombits) | *it) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + ++it; + } + if (pad) { + if (bits) out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 4cc77dbfeb..ca11cf341f 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -4,10 +4,10 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "utiltime.h" +#include <utiltime.h> #include <atomic> diff --git a/src/validation.cpp b/src/validation.cpp index 0bd1ec672b..98db699566 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3,44 +3,44 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "validation.h" - -#include "arith_uint256.h" -#include "chain.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "checkqueue.h" -#include "consensus/consensus.h" -#include "consensus/merkle.h" -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "cuckoocache.h" -#include "fs.h" -#include "hash.h" -#include "init.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "policy/rbf.h" -#include "pow.h" -#include "primitives/block.h" -#include "primitives/transaction.h" -#include "random.h" -#include "reverse_iterator.h" -#include "script/script.h" -#include "script/sigcache.h" -#include "script/standard.h" -#include "timedata.h" -#include "tinyformat.h" -#include "txdb.h" -#include "txmempool.h" -#include "ui_interface.h" -#include "undo.h" -#include "util.h" -#include "utilmoneystr.h" -#include "utilstrencodings.h" -#include "validationinterface.h" -#include "versionbits.h" -#include "warnings.h" +#include <validation.h> + +#include <arith_uint256.h> +#include <chain.h> +#include <chainparams.h> +#include <checkpoints.h> +#include <checkqueue.h> +#include <consensus/consensus.h> +#include <consensus/merkle.h> +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <cuckoocache.h> +#include <fs.h> +#include <hash.h> +#include <init.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <policy/rbf.h> +#include <pow.h> +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <random.h> +#include <reverse_iterator.h> +#include <script/script.h> +#include <script/sigcache.h> +#include <script/standard.h> +#include <timedata.h> +#include <tinyformat.h> +#include <txdb.h> +#include <txmempool.h> +#include <ui_interface.h> +#include <undo.h> +#include <util.h> +#include <utilmoneystr.h> +#include <utilstrencodings.h> +#include <validationinterface.h> +#include <versionbits.h> +#include <warnings.h> #include <atomic> #include <sstream> @@ -69,7 +69,7 @@ CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); -bool fReindex = false; +std::atomic_bool fReindex(false); bool fTxIndex = false; bool fHavePruned = false; bool fPruneMode = false; @@ -156,6 +156,26 @@ namespace { /** chainwork for the last block that preciousblock has been applied to. */ arith_uint256 nLastPreciousChainwork = 0; + /** In order to efficiently track invalidity of headers, we keep the set of + * blocks which we tried to connect and found to be invalid here (ie which + * were set to BLOCK_FAILED_VALID since the last restart). We can then + * walk this set and check if a new header is a descendant of something in + * this set, preventing us from having to walk mapBlockIndex when we try + * to connect a bad block and fail. + * + * While this is more complicated than marking everything which descends + * from an invalid block as invalid at the time we discover it to be + * invalid, doing so would require walking all of mapBlockIndex to find all + * descendants. Since this case should be very rare, keeping track of all + * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as + * well. + * + * Because we already walk mapBlockIndex in height-order at startup, we go + * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, + * instead of putting things in this set. + */ + std::set<CBlockIndex*> g_failed_blocks; + /** Dirty block index entries. */ std::set<CBlockIndex*> setDirtyBlockIndex; @@ -181,9 +201,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc return chain.Genesis(); } -CCoinsViewDB *pcoinsdbview = nullptr; -CCoinsViewCache *pcoinsTip = nullptr; -CBlockTreeDB *pblocktree = nullptr; +std::unique_ptr<CCoinsViewDB> pcoinsdbview; +std::unique_ptr<CCoinsViewCache> pcoinsTip; +std::unique_ptr<CBlockTreeDB> pblocktree; enum FlushStateMode { FLUSH_STATE_NONE, @@ -219,7 +239,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - // BIP113 will require that time-locked transactions have nLockTime set to + // BIP113 requires that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. // When the next block is created its previous block will be the current // chain tip, so we use that to calculate the median time passed to @@ -275,7 +295,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool } else { // pcoinsTip contains the UTXO set for chainActive.Tip() - CCoinsViewMemPool viewMemPool(pcoinsTip, mempool); + CCoinsViewMemPool viewMemPool(pcoinsTip.get(), mempool); std::vector<int> prevheights; prevheights.resize(tx.vin.size()); for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { @@ -384,7 +404,9 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, nullptr, nullptr, true)) { + if (!fAddToMempool || (*it)->IsCoinBase() || + !AcceptToMemoryPool(mempool, stateDummy, *it, nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); @@ -402,7 +424,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f mempool.UpdateTransactionsFromBlock(vHashUpdate); // We also need to remove any now-immature transactions - mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + mempool.removeForReorg(pcoinsTip.get(), chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); // Re-limit mempool size, in case we added any transactions LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); } @@ -443,9 +465,9 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); } -static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, +static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache) + bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache) { const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -532,11 +554,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CCoinsView dummy; CCoinsViewCache view(&dummy); - CAmount nValueIn = 0; LockPoints lp; { LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); view.SetBackend(viewMemPool); // do all inputs exist? @@ -563,8 +584,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Bring the best block into scope view.GetBestBlock(); - nValueIn = view.GetValueIn(tx); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); @@ -575,6 +594,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // CoinsViewCache instead of create its own if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); + + } // end LOCK(pool.cs) + + CAmount nFees = 0; + if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); } // Check for non-standard pay-to-script-hash in inputs @@ -587,8 +612,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); - CAmount nValueOut = tx.GetValueOut(); - CAmount nFees = nValueIn-nValueOut; // nModifiedFees includes any fee deltas from PrioritiseTransaction CAmount nModifiedFees = nFees; pool.ApplyDelta(hash, nModifiedFees); @@ -618,12 +641,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool strprintf("%d", nSigOpsCost)); CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { + if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); } // No transactions are allowed below minRelayTxFee except from disconnected blocks - if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { + if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); } @@ -855,17 +878,18 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); - // This transaction should only count for fee estimation if it isn't a - // BIP 125 replacement transaction (may not be widely supported), the - // node is not behind, and the transaction is not dependent on any other - // transactions in the mempool. - bool validForFeeEstimation = !fReplacementTransaction && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + // This transaction should only count for fee estimation if: + // - it isn't a BIP 125 replacement transaction (may not be widely supported) + // - it's not being readded during a reorg which bypasses typical mempool fee limits + // - the node is not behind + // - the transaction is not dependent on any other transactions in the mempool + bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed - if (!fOverrideMempoolLimit) { + if (!bypass_limits) { LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); if (!pool.exists(hash)) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); @@ -878,12 +902,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } /** (try to) add transaction to memory pool with a specified acceptance time **/ -static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, +static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool bypass_limits, const CAmount nAbsurdFee) { std::vector<COutPoint> coins_to_uncache; - bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache); + bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache); if (!res) { for (const COutPoint& hashTx : coins_to_uncache) pcoinsTip->Uncache(hashTx); @@ -894,12 +918,12 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo return res; } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool bypass_limits, const CAmount nAbsurdFee) { const CChainParams& chainparams = Params(); - return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee); + return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee); } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ @@ -935,6 +959,9 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus return error("%s: txid mismatch", __func__); return true; } + + // transaction not found in index, nothing more can be done + return false; } if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it @@ -1173,6 +1200,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; + g_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); @@ -1203,7 +1231,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; - return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error); + return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error); } int GetSpendHeight(const CCoinsViewCache& inputs) @@ -1244,9 +1272,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi { if (!tx.IsCoinBase()) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) - return false; - if (pvChecks) pvChecks->reserve(tx.vin.size()); @@ -1285,11 +1310,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // a sanity check that our caching is not introducing consensus // failures through additional data in, eg, the coins being // spent being checked as a part of CScriptCheck. - const CScript& scriptPubKey = coin.out.scriptPubKey; - const CAmount amount = coin.out.nValue; // Verify signature - CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheSigStore, &txdata); + CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1301,7 +1324,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // arguments; if so, don't trigger DoS protection to // avoid splitting the network between upgraded and // non-upgraded nodes. - CScriptCheck check2(scriptPubKey, amount, tx, i, + CScriptCheck check2(coin.out, tx, i, flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); @@ -1588,11 +1611,12 @@ static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { AssertLockHeld(cs_main); - // BIP16 didn't become active until Apr 1 2012 - int64_t nBIP16SwitchTime = 1333238400; - bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); + unsigned int flags = SCRIPT_VERIFY_NONE; - unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; + // Start enforcing P2SH (BIP16) + if (pindex->nHeight >= consensusparams.BIP16Height) { + flags |= SCRIPT_VERIFY_P2SH; + } // Start enforcing the DERSIG (BIP66) rule if (pindex->nHeight >= consensusparams.BIP66Height) { @@ -1643,6 +1667,18 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd int64_t nTimeStart = GetTimeMicros(); // Check it again in case a previous version let a bad block in + // NOTE: We don't currently (re-)invoke ContextualCheckBlock() or + // ContextualCheckBlockHeader() here. This means that if we add a new + // consensus rule that is enforced in one of those two functions, then we + // may have let in a block that violates the rule prior to updating the + // software, and we would NOT be enforcing the rule here. Fully solving + // upgrade from one software version to the next after a consensus rule + // change is potentially tricky and issue-specific (see RewindBlockIndex() + // for one general approach that was used for BIP 141 deployment). + // Also, currently the rule against blocks more than 2 hours in the future + // is enforced in ContextualCheckBlockHeader(); we wouldn't want to + // re-enforce that rule here (at least until we make it impossible for + // GetAdjustedTime() to go backward). if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); @@ -1711,6 +1747,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd // before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further // duplicate transactions descending from the known pairs either. // If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check. + assert(pindex->pprev); CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height); //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond. fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash)); @@ -1760,9 +1797,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!tx.IsCoinBase()) { - if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), - REJECT_INVALID, "bad-txns-inputs-missingorspent"); + CAmount txfee = 0; + if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + } + nFees += txfee; + if (!MoneyRange(nFees)) { + return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__), + REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); + } // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must @@ -1790,8 +1833,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd txdata.emplace_back(tx); if (!tx.IsCoinBase()) { - nFees += view.GetValueIn(tx)-tx.GetValueOut(); - std::vector<CScriptCheck> vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) @@ -1850,6 +1891,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); + assert(pindex->phashBlock); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -2075,7 +2117,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); { - CCoinsViewCache view(pcoinsTip); + CCoinsViewCache view(pcoinsTip.get()); assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); @@ -2205,7 +2247,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, int64_t nTime3; LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); { - CCoinsViewCache view(pcoinsTip); + CCoinsViewCache view(pcoinsTip.get()); bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); GetMainSignals().BlockChecked(blockConnecting, state); if (!rv) { @@ -2383,7 +2425,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c // any disconnected transactions back to the mempool. UpdateMempoolForReorg(disconnectpool, true); } - mempool.check(pcoinsTip); + mempool.check(pcoinsTip.get()); // Callbacks/notifications for a new best chain. if (fInvalidFound) @@ -2464,7 +2506,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) { assert(trace.pblock && trace.pindex); - GetMainSignals().BlockConnected(trace.pblock, trace.pindex, *trace.conflictedTxs); + GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); } } // When we reach this point, we switched to a new tip (stored in pindexNewTip). @@ -2525,17 +2567,18 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C { AssertLockHeld(cs_main); - // Mark the block itself as invalid. - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - setBlockIndexCandidates.erase(pindex); + // We first disconnect backwards and then mark the blocks as invalid. + // This prevents a case where pruned nodes may fail to invalidateblock + // and be left unable to start as they have no tip candidates (as there + // are no blocks that meet the "have data and are not invalid per + // nStatus" criteria for inclusion in setBlockIndexCandidates). + + bool pindex_was_in_chain = false; + CBlockIndex *invalid_walk_tip = chainActive.Tip(); DisconnectedBlockTransactions disconnectpool; while (chainActive.Contains(pindex)) { - CBlockIndex *pindexWalk = chainActive.Tip(); - pindexWalk->nStatus |= BLOCK_FAILED_CHILD; - setDirtyBlockIndex.insert(pindexWalk); - setBlockIndexCandidates.erase(pindexWalk); + pindex_was_in_chain = true; // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(state, chainparams, &disconnectpool)) { @@ -2546,6 +2589,21 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C } } + // Now mark the blocks we just disconnected as descendants invalid + // (note this may not be all descendants). + while (pindex_was_in_chain && invalid_walk_tip != pindex) { + invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(invalid_walk_tip); + setBlockIndexCandidates.erase(invalid_walk_tip); + invalid_walk_tip = invalid_walk_tip->pprev; + } + + // Mark the block itself as invalid. + pindex->nStatus |= BLOCK_FAILED_VALID; + setDirtyBlockIndex.insert(pindex); + setBlockIndexCandidates.erase(pindex); + g_failed_blocks.insert(pindex); + // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. UpdateMempoolForReorg(disconnectpool, true); @@ -2583,6 +2641,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) { // Reset invalid block marker if it was pointing to one of those. pindexBestInvalid = nullptr; } + g_failed_blocks.erase(it->second); } it++; } @@ -2608,7 +2667,6 @@ static CBlockIndex* AddToBlockIndex(const CBlockHeader& block) // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); - assert(pindexNew); // We assign the sequence id to blocks only when the full data is available, // to avoid miners withholding blocks but broadcasting headers, to get a // competitive advantage. @@ -2906,7 +2964,13 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc /** Context-dependent validity checks. * By "context", we mean only the previous block headers, but not the UTXO - * set; UTXO-related validity checks are done in ConnectBlock(). */ + * set; UTXO-related validity checks are done in ConnectBlock(). + * NOTE: This function is not currently invoked by ConnectBlock(), so we + * should consider upgrade issues if we change which consensus rules are + * enforced in this function (eg by adding a new consensus rule). See comment + * in ConnectBlock(). + * Note that -reindex-chainstate skips the validation that happens here! + */ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) { assert(pindexPrev != nullptr); @@ -2946,6 +3010,12 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta return true; } +/** NOTE: This function is not currently invoked by ConnectBlock(), so we + * should consider upgrade issues if we change which consensus rules are + * enforced in this function (eg by adding a new consensus rule). See comment + * in ConnectBlock(). + * Note that -reindex-chainstate skips the validation that happens here! + */ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; @@ -3059,6 +3129,21 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + + if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { + for (const CBlockIndex* failedit : g_failed_blocks) { + if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { + assert(failedit->nStatus & BLOCK_FAILED_VALID); + CBlockIndex* invalid_walk = pindexPrev; + while (invalid_walk != failedit) { + invalid_walk->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(invalid_walk); + invalid_walk = invalid_walk->pprev; + } + return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); + } + } + } } if (pindex == nullptr) pindex = AddToBlockIndex(block); @@ -3072,13 +3157,15 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } // Exposed wrapper for AcceptBlockHeader -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid) { + if (first_invalid != nullptr) first_invalid->SetNull(); { LOCK(cs_main); for (const CBlockHeader& header : headers) { CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast if (!AcceptBlockHeader(header, state, chainparams, &pindex)) { + if (first_invalid) *first_invalid = header; return false; } if (ppindex) { @@ -3108,7 +3195,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation // process an unrequested block if it's new and has enough work to // advance our tip, and isn't too many blocks ahead. bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; - bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); + bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true); // Blocks that are too out-of-order needlessly limit the effectiveness of // pruning, because pruning will not delete block files that contain any // blocks which are too close in height to the tip. Apply this test @@ -3125,9 +3212,15 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation // and unrequested blocks. if (fAlreadyHave) return true; if (!fRequested) { // If we didn't ask for it: - if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned - if (!fHasMoreWork) return true; // Don't process less-work chains - if (fTooFarAhead) return true; // Block height is too high + if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned + if (!fHasMoreOrSameWork) return true; // Don't process less-work chains + if (fTooFarAhead) return true; // Block height is too high + + // Protect against DoS attacks from low-work chains. + // If our tip is behind, a peer could try to send us + // low-work blocks on a fake chain that we would never + // request; don't process these. + if (pindex->nChainWork < nMinimumChainWork) return true; } if (fNewBlock) *fNewBlock = true; @@ -3189,7 +3282,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CheckBlockIndex(chainparams.GetConsensus()); if (!ret) { GetMainSignals().BlockChecked(*pblock, state); - return error("%s: AcceptBlock FAILED", __func__); + return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage()); } } @@ -3206,7 +3299,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, { AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainActive.Tip()); - CCoinsViewCache viewNew(pcoinsTip); + CCoinsViewCache viewNew(pcoinsTip.get()); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; @@ -3230,8 +3323,10 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, */ /* Calculate the amount of disk space the block & undo files currently use */ -static uint64_t CalculateCurrentUsage() +uint64_t CalculateCurrentUsage() { + LOCK(cs_LastBlockFile); + uint64_t retval = 0; for (const CBlockFileInfo &file : vinfoBlockFile) { retval += file.nSize + file.nUndoSize; @@ -3242,8 +3337,10 @@ static uint64_t CalculateCurrentUsage() /* Prune a block file (modify associated database entries)*/ void PruneOneBlockFile(const int fileNumber) { - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { - CBlockIndex* pindex = it->second; + LOCK(cs_LastBlockFile); + + for (const auto& entry : mapBlockIndex) { + CBlockIndex* pindex = entry.second; if (pindex->nFile == fileNumber) { pindex->nStatus &= ~BLOCK_HAVE_DATA; pindex->nStatus &= ~BLOCK_HAVE_UNDO; @@ -3391,7 +3488,7 @@ static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fRe return nullptr; fs::path path = GetBlockPosFilename(pos, prefix); fs::create_directories(path.parent_path()); - FILE* file = fsbridge::fopen(path, "rb+"); + FILE* file = fsbridge::fopen(path, fReadOnly ? "rb": "rb+"); if (!file && !fReadOnly) file = fsbridge::fopen(path, "wb+"); if (!file) { @@ -3434,8 +3531,6 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); - if (!pindexNew) - throw std::runtime_error(std::string(__func__) + ": new CBlockIndex failed"); mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); @@ -3477,6 +3572,10 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) pindex->nChainTx = pindex->nTx; } } + if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) { + pindex->nStatus |= BLOCK_FAILED_CHILD; + setDirtyBlockIndex.insert(pindex); + } if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr)) setBlockIndexCandidates.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) @@ -3530,7 +3629,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); - fReindex |= fReindexing; + if(fReindexing) fReindex = true; // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); @@ -3795,8 +3894,8 @@ bool RewindBlockIndex(const CChainParams& params) // Reduce validity flag and have-data flags. // We do this after actual disconnecting, otherwise we'll end up writing the lack of data // to disk before writing the chainstate, resulting in a failure to continue if interrupted. - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - CBlockIndex* pindexIter = it->second; + for (const auto& entry : mapBlockIndex) { + CBlockIndex* pindexIter = entry.second; // Note: If we encounter an insufficiently validated block that // is on chainActive, it must be because we are a pruning node, and @@ -3867,6 +3966,7 @@ void UnloadBlockIndex() nLastBlockFile = 0; nBlockSequenceId = 1; setDirtyBlockIndex.clear(); + g_failed_blocks.clear(); setDirtyFileInfo.clear(); versionbitscache.Clear(); for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { @@ -4071,8 +4171,8 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams) // Build forward-pointing map of the entire block tree. std::multimap<CBlockIndex*,CBlockIndex*> forward; - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - forward.insert(std::make_pair(it->second->pprev, it->second)); + for (auto& entry : mapBlockIndex) { + forward.insert(std::make_pair(entry.second->pprev, entry.second)); } assert(forward.size() == mapBlockIndex.size()); @@ -4244,6 +4344,8 @@ std::string CBlockFileInfo::ToString() const CBlockFileInfo* GetBlockFileInfo(size_t n) { + LOCK(cs_LastBlockFile); + return &vinfoBlockFile.at(n); } @@ -4279,8 +4381,9 @@ bool LoadMempool(void) } int64_t count = 0; - int64_t skipped = 0; + int64_t expired = 0; int64_t failed = 0; + int64_t already_there = 0; int64_t nNow = GetTime(); try { @@ -4306,14 +4409,23 @@ bool LoadMempool(void) CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); - AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, nullptr, nTime, nullptr, false, 0); + AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime, + nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */); if (state.IsValid()) { ++count; } else { - ++failed; + // mempool may contain the transaction already, e.g. from + // wallet(s) having loaded it while we were processing + // mempool transactions; consider these as valid, instead of + // failed, but mark them as 'already there' + if (mempool.exists(tx->GetHash())) { + ++already_there; + } else { + ++failed; + } } } else { - ++skipped; + ++expired; } if (ShutdownRequested()) return false; @@ -4329,7 +4441,7 @@ bool LoadMempool(void) return false; } - LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped); + LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there\n", count, failed, expired, already_there); return true; } diff --git a/src/validation.h b/src/validation.h index aa4d7abb4e..254f3e0754 100644 --- a/src/validation.h +++ b/src/validation.h @@ -7,17 +7,17 @@ #define BITCOIN_VALIDATION_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif -#include "amount.h" -#include "coins.h" -#include "fs.h" -#include "protocol.h" // For CMessageHeader::MessageStartChars -#include "policy/feerate.h" -#include "script/script_error.h" -#include "sync.h" -#include "versionbits.h" +#include <amount.h> +#include <coins.h> +#include <fs.h> +#include <protocol.h> // For CMessageHeader::MessageStartChars +#include <policy/feerate.h> +#include <script/script_error.h> +#include <sync.h> +#include <versionbits.h> #include <algorithm> #include <exception> @@ -45,9 +45,9 @@ struct ChainTxData; struct PrecomputedTransactionData; struct LockPoints; -/** Default for DEFAULT_WHITELISTRELAY. */ +/** Default for -whitelistrelay. */ static const bool DEFAULT_WHITELISTRELAY = true; -/** Default for DEFAULT_WHITELISTFORCERELAY. */ +/** Default for -whitelistforcerelay. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; @@ -94,8 +94,8 @@ static const int MAX_CMPCTBLOCK_DEPTH = 5; static const int MAX_BLOCKTXN_DEPTH = 10; /** Size of the "block download window": how far ahead of our current height do we fetch? * Larger windows tolerate larger download speed differences between peer, but increase the potential - * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning - * harder). We'll probably want to make this a per-peer adaptive value at some point. */ + * degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably + * want to make this a per-peer adaptive value at some point. */ static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** Time to wait (in seconds) between writing blocks/block index to disk. */ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; @@ -166,7 +166,7 @@ extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; extern std::atomic_bool fImporting; -extern bool fReindex; +extern std::atomic_bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; extern bool fIsBareMultisigStd; @@ -247,8 +247,9 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons * @param[out] state This may be set to an Error state if any error occurred processing them * @param[in] chainparams The params for the chain we want to connect to * @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers + * @param[out] first_invalid First header that fails validation, if one exists */ -bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr); +bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); @@ -280,6 +281,9 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); +/** Calculate the amount of disk space the block & undo files currently use */ +uint64_t CalculateCurrentUsage(); + /** * Mark one block file as pruned. */ @@ -301,9 +305,9 @@ void PruneBlockFilesManual(int nManualPruneHeight); /** (try to) add transaction to memory pool * plTxnReplaced will be appended to with all transactions replaced from mempool **/ -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced = nullptr, - bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, + bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, + bool bypass_limits, const CAmount nAbsurdFee); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); @@ -357,8 +361,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null class CScriptCheck { private: - CScript scriptPubKey; - CAmount amount; + CTxOut m_tx_out; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -367,17 +370,15 @@ private: PrecomputedTransactionData *txdata; public: - CScriptCheck(): amount(0), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} - CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : - scriptPubKey(scriptPubKeyIn), amount(amountIn), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } + CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } bool operator()(); void swap(CScriptCheck &check) { - scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); - std::swap(amount, check.amount); + std::swap(m_tx_out, check.m_tx_out); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); @@ -443,13 +444,13 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex); extern CChain chainActive; /** Global variable that points to the coins database (protected by cs_main) */ -extern CCoinsViewDB *pcoinsdbview; +extern std::unique_ptr<CCoinsViewDB> pcoinsdbview; /** Global variable that points to the active CCoinsView (protected by cs_main) */ -extern CCoinsViewCache *pcoinsTip; +extern std::unique_ptr<CCoinsViewCache> pcoinsTip; /** Global variable that points to the active block tree (protected by cs_main) */ -extern CBlockTreeDB *pblocktree; +extern std::unique_ptr<CBlockTreeDB> pblocktree; /** * Return the spend height, which is one more than the inputs.GetBestBlock(). diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index be5029dec3..abbd8cc4d2 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -3,11 +3,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "validationinterface.h" -#include "init.h" -#include "scheduler.h" -#include "sync.h" -#include "util.h" +#include <validationinterface.h> + +#include <init.h> +#include <primitives/block.h> +#include <scheduler.h> +#include <sync.h> +#include <txmempool.h> +#include <util.h> #include <list> #include <atomic> @@ -19,6 +22,7 @@ struct MainSignalsInstance { boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool; boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected; boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected; + boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool; boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; boost::signals2::signal<void (const uint256 &)> Inventory; boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast; @@ -45,7 +49,17 @@ void CMainSignals::UnregisterBackgroundSignalScheduler() { } void CMainSignals::FlushBackgroundCallbacks() { - m_internals->m_schedulerClient.EmptyQueue(); + if (m_internals) { + m_internals->m_schedulerClient.EmptyQueue(); + } +} + +void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) { + pool.NotifyEntryRemoved.connect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2)); +} + +void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) { + pool.NotifyEntryRemoved.disconnect(boost::bind(&CMainSignals::MempoolEntryRemoved, this, _1, _2)); } CMainSignals& GetMainSignals() @@ -58,6 +72,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.m_internals->TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); + g_signals.m_internals->TransactionRemovedFromMempool.connect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1)); g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); @@ -73,11 +88,15 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1)); g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3)); g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1)); + g_signals.m_internals->TransactionRemovedFromMempool.disconnect(boost::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, _1)); g_signals.m_internals->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3)); g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2)); } void UnregisterAllValidationInterfaces() { + if (!g_signals.m_internals) { + return; + } g_signals.m_internals->BlockChecked.disconnect_all_slots(); g_signals.m_internals->Broadcast.disconnect_all_slots(); g_signals.m_internals->Inventory.disconnect_all_slots(); @@ -85,32 +104,57 @@ void UnregisterAllValidationInterfaces() { g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots(); g_signals.m_internals->BlockConnected.disconnect_all_slots(); g_signals.m_internals->BlockDisconnected.disconnect_all_slots(); + g_signals.m_internals->TransactionRemovedFromMempool.disconnect_all_slots(); g_signals.m_internals->UpdatedBlockTip.disconnect_all_slots(); g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots(); } +void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) { + g_signals.m_internals->m_schedulerClient.AddToProcessQueue(std::move(func)); +} + +void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason reason) { + if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) { + m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] { + m_internals->TransactionRemovedFromMempool(ptx); + }); + } +} + void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { - m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); + m_internals->m_schedulerClient.AddToProcessQueue([pindexNew, pindexFork, fInitialDownload, this] { + m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); + }); } void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) { - m_internals->TransactionAddedToMempool(ptx); + m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] { + m_internals->TransactionAddedToMempool(ptx); + }); } -void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) { - m_internals->BlockConnected(pblock, pindex, vtxConflicted); +void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>>& pvtxConflicted) { + m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, pvtxConflicted, this] { + m_internals->BlockConnected(pblock, pindex, *pvtxConflicted); + }); } void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) { - m_internals->BlockDisconnected(pblock); + m_internals->m_schedulerClient.AddToProcessQueue([pblock, this] { + m_internals->BlockDisconnected(pblock); + }); } void CMainSignals::SetBestChain(const CBlockLocator &locator) { - m_internals->SetBestChain(locator); + m_internals->m_schedulerClient.AddToProcessQueue([locator, this] { + m_internals->SetBestChain(locator); + }); } void CMainSignals::Inventory(const uint256 &hash) { - m_internals->Inventory(hash); + m_internals->m_schedulerClient.AddToProcessQueue([hash, this] { + m_internals->Inventory(hash); + }); } void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) { diff --git a/src/validationinterface.h b/src/validationinterface.h index d6da2bc1fd..7b5d138414 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -6,9 +6,10 @@ #ifndef BITCOIN_VALIDATIONINTERFACE_H #define BITCOIN_VALIDATIONINTERFACE_H -#include <memory> +#include <primitives/transaction.h> // CTransaction(Ref) -#include "primitives/transaction.h" // CTransaction(Ref) +#include <functional> +#include <memory> class CBlock; class CBlockIndex; @@ -20,6 +21,8 @@ class CValidationInterface; class CValidationState; class uint256; class CScheduler; +class CTxMemPool; +enum class MemPoolRemovalReason; // These functions dispatch to one or all registered wallets @@ -29,23 +32,66 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn); void UnregisterValidationInterface(CValidationInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllValidationInterfaces(); +/** + * Pushes a function to callback onto the notification queue, guaranteeing any + * callbacks generated prior to now are finished when the function is called. + * + * Be very careful blocking on func to be called if any locks are held - + * validation interface clients may not be able to make progress as they often + * wait for things like cs_main, so blocking until func is called with cs_main + * will result in a deadlock (that DEBUG_LOCKORDER will miss). + */ +void CallFunctionInValidationInterfaceQueue(std::function<void ()> func); class CValidationInterface { protected: - /** Notifies listeners of updated block chain tip */ + /** + * Notifies listeners of updated block chain tip + * + * Called on a background thread. + */ virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {} - /** Notifies listeners of a transaction having been added to mempool. */ + /** + * Notifies listeners of a transaction having been added to mempool. + * + * Called on a background thread. + */ virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {} /** + * Notifies listeners of a transaction leaving mempool. + * + * This only fires for transactions which leave mempool because of expiry, + * size limiting, reorg (changes in lock times/coinbase maturity), or + * replacement. This does not include any transactions which are included + * in BlockConnectedDisconnected either in block->vtx or in txnConflicted. + * + * Called on a background thread. + */ + virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) {} + /** * Notifies listeners of a block being connected. * Provides a vector of transactions evicted from the mempool as a result. + * + * Called on a background thread. */ virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {} - /** Notifies listeners of a block being disconnected */ + /** + * Notifies listeners of a block being disconnected + * + * Called on a background thread. + */ virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {} - /** Notifies listeners of the new active block chain on-disk. */ + /** + * Notifies listeners of the new active block chain on-disk. + * + * Called on a background thread. + */ virtual void SetBestChain(const CBlockLocator &locator) {} - /** Notifies listeners about an inventory item being seen on the network. */ + /** + * Notifies listeners about an inventory item being seen on the network. + * + * Called on a background thread. + */ virtual void Inventory(const uint256 &hash) {} /** Tells listeners to broadcast their data. */ virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {} @@ -73,6 +119,9 @@ private: friend void ::RegisterValidationInterface(CValidationInterface*); friend void ::UnregisterValidationInterface(CValidationInterface*); friend void ::UnregisterAllValidationInterfaces(); + friend void ::CallFunctionInValidationInterfaceQueue(std::function<void ()> func); + + void MempoolEntryRemoved(CTransactionRef tx, MemPoolRemovalReason reason); public: /** Register a CScheduler to give callbacks which should run in the background (may only be called once) */ @@ -82,9 +131,14 @@ public: /** Call any remaining callbacks on the calling thread */ void FlushBackgroundCallbacks(); + /** Register with mempool to call TransactionRemovedFromMempool callbacks */ + void RegisterWithMempoolSignals(CTxMemPool& pool); + /** Unregister with mempool */ + void UnregisterWithMempoolSignals(CTxMemPool& pool); + void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload); void TransactionAddedToMempool(const CTransactionRef &); - void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &); + void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &); void BlockDisconnected(const std::shared_ptr<const CBlock> &); void SetBestChain(const CBlockLocator &); void Inventory(const uint256 &); diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 64ae939672..3bd00ccb23 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "versionbits.h" -#include "consensus/params.h" +#include <versionbits.h> +#include <consensus/params.h> const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { { @@ -27,6 +27,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* int64_t nTimeStart = BeginTime(params); int64_t nTimeTimeout = EndTime(params); + // Check if this deployment is always active. + if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return THRESHOLD_ACTIVE; + } + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. if (pindexPrev != nullptr) { pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); @@ -136,6 +141,11 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const { + int64_t start_time = BeginTime(params); + if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return 0; + } + const ThresholdState initialState = GetStateFor(pindexPrev, params, cache); // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment." diff --git a/src/versionbits.h b/src/versionbits.h index 71dc0c8500..0e111ed7a1 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_CONSENSUS_VERSIONBITS #define BITCOIN_CONSENSUS_VERSIONBITS -#include "chain.h" +#include <chain.h> #include <map> /** What block version to use for new blocks (pre versionbits) */ diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index fc0e7c519e..15fd105779 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -5,10 +5,10 @@ #ifndef BITCOIN_WALLET_COINCONTROL_H #define BITCOIN_WALLET_COINCONTROL_H -#include "policy/feerate.h" -#include "policy/fees.h" -#include "primitives/transaction.h" -#include "wallet/wallet.h" +#include <policy/feerate.h> +#include <policy/fees.h> +#include <primitives/transaction.h> +#include <wallet/wallet.h> #include <boost/optional.hpp> diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 8db3bfd69c..4cd7db048b 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "crypter.h" +#include <wallet/crypter.h> -#include "crypto/aes.h" -#include "crypto/sha512.h" -#include "script/script.h" -#include "script/standard.h" -#include "util.h" +#include <crypto/aes.h> +#include <crypto/sha512.h> +#include <script/script.h> +#include <script/standard.h> +#include <util.h> #include <string> #include <vector> @@ -152,6 +152,15 @@ bool CCryptoKeyStore::SetCrypted() return true; } +bool CCryptoKeyStore::IsLocked() const +{ + if (!IsCrypted()) { + return false; + } + LOCK(cs_KeyStore); + return vMasterKey.empty(); +} + bool CCryptoKeyStore::Lock() { if (!SetCrypted()) @@ -206,21 +215,23 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn) bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) { - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::AddKeyPubKey(key, pubkey); + LOCK(cs_KeyStore); + if (!IsCrypted()) { + return CBasicKeyStore::AddKeyPubKey(key, pubkey); + } - if (IsLocked()) - return false; + if (IsLocked()) { + return false; + } - std::vector<unsigned char> vchCryptedSecret; - CKeyingMaterial vchSecret(key.begin(), key.end()); - if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) - return false; + std::vector<unsigned char> vchCryptedSecret; + CKeyingMaterial vchSecret(key.begin(), key.end()); + if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) { + return false; + } - if (!AddCryptedKey(pubkey, vchCryptedSecret)) - return false; + if (!AddCryptedKey(pubkey, vchCryptedSecret)) { + return false; } return true; } @@ -228,72 +239,88 @@ bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey) bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { - { - LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; - - mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); + LOCK(cs_KeyStore); + if (!SetCrypted()) { + return false; } + + mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); return true; } +bool CCryptoKeyStore::HaveKey(const CKeyID &address) const +{ + LOCK(cs_KeyStore); + if (!IsCrypted()) { + return CBasicKeyStore::HaveKey(address); + } + return mapCryptedKeys.count(address) > 0; +} + bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const { - { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetKey(address, keyOut); + LOCK(cs_KeyStore); + if (!IsCrypted()) { + return CBasicKeyStore::GetKey(address, keyOut); + } - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - const CPubKey &vchPubKey = (*mi).second.first; - const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); - } + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) + { + const CPubKey &vchPubKey = (*mi).second.first; + const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; + return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); } return false; } bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const { + LOCK(cs_KeyStore); + if (!IsCrypted()) + return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); + + CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); + if (mi != mapCryptedKeys.end()) { - LOCK(cs_KeyStore); - if (!IsCrypted()) - return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); + vchPubKeyOut = (*mi).second.first; + return true; + } + // Check for watch-only pubkeys + return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); +} - CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); - if (mi != mapCryptedKeys.end()) - { - vchPubKeyOut = (*mi).second.first; - return true; - } - // Check for watch-only pubkeys - return CBasicKeyStore::GetPubKey(address, vchPubKeyOut); +std::set<CKeyID> CCryptoKeyStore::GetKeys() const +{ + LOCK(cs_KeyStore); + if (!IsCrypted()) { + return CBasicKeyStore::GetKeys(); } + std::set<CKeyID> set_address; + for (const auto& mi : mapCryptedKeys) { + set_address.insert(mi.first); + } + return set_address; } bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) { + LOCK(cs_KeyStore); + if (!mapCryptedKeys.empty() || IsCrypted()) + return false; + + fUseCrypto = true; + for (KeyMap::value_type& mKey : mapKeys) { - LOCK(cs_KeyStore); - if (!mapCryptedKeys.empty() || IsCrypted()) + const CKey &key = mKey.second; + CPubKey vchPubKey = key.GetPubKey(); + CKeyingMaterial vchSecret(key.begin(), key.end()); + std::vector<unsigned char> vchCryptedSecret; + if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) + return false; + if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; - - fUseCrypto = true; - for (KeyMap::value_type& mKey : mapKeys) - { - const CKey &key = mKey.second; - CPubKey vchPubKey = key.GetPubKey(); - CKeyingMaterial vchSecret(key.begin(), key.end()); - std::vector<unsigned char> vchCryptedSecret; - if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) - return false; - if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) - return false; - } - mapKeys.clear(); } + mapKeys.clear(); return true; } diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index eac258b287..7b0936ba0d 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -5,9 +5,11 @@ #ifndef BITCOIN_WALLET_CRYPTER_H #define BITCOIN_WALLET_CRYPTER_H -#include "keystore.h" -#include "serialize.h" -#include "support/allocators/secure.h" +#include <keystore.h> +#include <serialize.h> +#include <support/allocators/secure.h> + +#include <atomic> const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; @@ -118,7 +120,7 @@ private: //! if fUseCrypto is true, mapKeys must be empty //! if fUseCrypto is false, vMasterKey must be empty - bool fUseCrypto; + std::atomic<bool> fUseCrypto; //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; @@ -137,52 +139,16 @@ public: { } - bool IsCrypted() const - { - return fUseCrypto; - } - - bool IsLocked() const - { - if (!IsCrypted()) - return false; - bool result; - { - LOCK(cs_KeyStore); - result = vMasterKey.empty(); - } - return result; - } - + bool IsCrypted() const { return fUseCrypto; } + bool IsLocked() const; bool Lock(); virtual bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; - bool HaveKey(const CKeyID &address) const override - { - { - LOCK(cs_KeyStore); - if (!IsCrypted()) { - return CBasicKeyStore::HaveKey(address); - } - return mapCryptedKeys.count(address) > 0; - } - return false; - } + bool HaveKey(const CKeyID &address) const override; bool GetKey(const CKeyID &address, CKey& keyOut) const override; bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; - std::set<CKeyID> GetKeys() const override - { - LOCK(cs_KeyStore); - if (!IsCrypted()) { - return CBasicKeyStore::GetKeys(); - } - std::set<CKeyID> set_address; - for (const auto& mi : mapCryptedKeys) { - set_address.insert(mi.first); - } - return set_address; - } + std::set<CKeyID> GetKeys() const override; /** * Wallet status (encrypted, locked) changed. diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index d66ba48421..79ff27279c 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -3,14 +3,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "db.h" +#include <wallet/db.h> -#include "addrman.h" -#include "fs.h" -#include "hash.h" -#include "protocol.h" -#include "util.h" -#include "utilstrencodings.h" +#include <addrman.h> +#include <fs.h> +#include <hash.h> +#include <protocol.h> +#include <util.h> +#include <utilstrencodings.h> +#include <wallet/walletutil.h> #include <stdint.h> @@ -20,6 +21,40 @@ #include <boost/thread.hpp> +namespace { +//! Make sure database has a unique fileid within the environment. If it +//! doesn't, throw an error. BDB caches do not work properly when more than one +//! open database has the same fileid (values written to one database may show +//! up in reads to other databases). +//! +//! BerkeleyDB generates unique fileids by default +//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), +//! so bitcoin should never create different databases with the same fileid, but +//! this error can be triggered if users manually copy database files. +void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) +{ + if (env.IsMock()) return; + + u_int8_t fileid[DB_FILE_ID_LEN]; + int ret = db.get_mpf()->get_fileid(fileid); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret)); + } + + for (const auto& item : env.mapDb) { + u_int8_t item_fileid[DB_FILE_ID_LEN]; + if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 && + memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { + const char* item_filename = nullptr; + item.second->get_dbname(&item_filename, nullptr); + throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename, + HexStr(std::begin(item_fileid), std::end(item_fileid)), + item_filename ? item_filename : "(unknown database)")); + } + } +} +} // namespace + // // CDB // @@ -41,13 +76,12 @@ void CDBEnv::EnvShutdown() void CDBEnv::Reset() { - delete dbenv; - dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS); + dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS)); fDbEnvInit = false; fMockDb = false; } -CDBEnv::CDBEnv() : dbenv(nullptr) +CDBEnv::CDBEnv() { Reset(); } @@ -55,8 +89,6 @@ CDBEnv::CDBEnv() : dbenv(nullptr) CDBEnv::~CDBEnv() { EnvShutdown(); - delete dbenv; - dbenv = nullptr; } void CDBEnv::Close() @@ -148,7 +180,7 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); - Db db(dbenv, 0); + Db db(dbenv.get(), 0); int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); if (result == 0) return VERIFY_OK; @@ -191,7 +223,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco } LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); - std::unique_ptr<Db> pdbCopy(new Db(bitdb.dbenv, 0)); + std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(bitdb.dbenv.get(), 0); int ret = pdbCopy->open(nullptr, // Txn pointer filename.c_str(), // Filename "main", // Logical db name @@ -226,7 +258,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco return fSuccess; } -bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr) +bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr) { LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); LogPrintf("Using wallet %s\n", walletFile); @@ -234,15 +266,15 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD // Wallet file must be a plain filename without a directory if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) { - errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string()); + errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), walletFile, walletDir.string()); return false; } - if (!bitdb.Open(dataDir)) + if (!bitdb.Open(walletDir)) { // try moving the database env out of the way - fs::path pathDatabase = dataDir / "database"; - fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime()); + fs::path pathDatabase = walletDir / "database"; + fs::path pathDatabaseBak = walletDir / strprintf("database.%d.bak", GetTime()); try { fs::rename(pathDatabase, pathDatabaseBak); LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); @@ -251,18 +283,18 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD } // try again - if (!bitdb.Open(dataDir)) { + if (!bitdb.Open(walletDir)) { // if it still fails, it probably means we can't even create the database env - errorStr = strprintf(_("Error initializing wallet database environment %s!"), GetDataDir()); + errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir); return false; } } return true; } -bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) +bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) { - if (fs::exists(dataDir / walletFile)) + if (fs::exists(walletDir / walletFile)) { std::string backup_filename; CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename); @@ -272,7 +304,7 @@ bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& data " Original %s saved as %s in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), - walletFile, backup_filename, dataDir); + walletFile, backup_filename, walletDir); } if (r == CDBEnv::RECOVER_FAIL) { @@ -300,7 +332,7 @@ bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<C std::stringstream strDump; - Db db(dbenv, 0); + Db db(dbenv.get(), 0); int result = db.verify(strFile.c_str(), nullptr, &strDump, flags); if (result == DB_VERIFY_BAD) { LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data may not be recoverable.\n"); @@ -376,38 +408,37 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb { LOCK(env->cs_db); - if (!env->Open(GetDataDir())) + if (!env->Open(GetWalletDir())) throw std::runtime_error("CDB: Failed to open database environment."); - strFile = strFilename; - ++env->mapFileUseCount[strFile]; - pdb = env->mapDb[strFile]; + pdb = env->mapDb[strFilename]; if (pdb == nullptr) { int ret; - pdb = new Db(env->dbenv, 0); + std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0); bool fMockDb = env->IsMock(); if (fMockDb) { - DbMpoolFile* mpf = pdb->get_mpf(); + DbMpoolFile* mpf = pdb_temp->get_mpf(); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); - if (ret != 0) - throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile)); + if (ret != 0) { + throw std::runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFilename)); + } } - ret = pdb->open(nullptr, // Txn pointer - fMockDb ? nullptr : strFile.c_str(), // Filename - fMockDb ? strFile.c_str() : "main", // Logical db name - DB_BTREE, // Database type - nFlags, // Flags + ret = pdb_temp->open(nullptr, // Txn pointer + fMockDb ? nullptr : strFilename.c_str(), // Filename + fMockDb ? strFilename.c_str() : "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags 0); if (ret != 0) { - delete pdb; - pdb = nullptr; - --env->mapFileUseCount[strFile]; - strFile = ""; throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } + CheckUniqueFileid(*env, strFilename, *pdb_temp); + + pdb = pdb_temp.release(); + env->mapDb[strFilename] = pdb; if (fCreate && !Exists(std::string("version"))) { bool fTmp = fReadOnly; @@ -415,9 +446,9 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb WriteVersion(CLIENT_VERSION); fReadOnly = fTmp; } - - env->mapDb[strFile] = pdb; } + ++env->mapFileUseCount[strFilename]; + strFile = strFilename; } } @@ -492,7 +523,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) std::string strFileRes = strFile + ".rewrite"; { // surround usage of db with extra {} CDB db(dbw, "r"); - Db* pdbCopy = new Db(env->dbenv, 0); + std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0); int ret = pdbCopy->open(nullptr, // Txn pointer strFileRes.c_str(), // Filename @@ -541,13 +572,12 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) } else { pdbCopy->close(0); } - delete pdbCopy; } if (fSuccess) { - Db dbA(env->dbenv, 0); + Db dbA(env->dbenv.get(), 0); if (dbA.remove(strFile.c_str(), nullptr, 0)) fSuccess = false; - Db dbB(env->dbenv, 0); + Db dbB(env->dbenv.get(), 0); if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0)) fSuccess = false; } @@ -666,12 +696,17 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) env->mapFileUseCount.erase(strFile); // Copy wallet file - fs::path pathSrc = GetDataDir() / strFile; + fs::path pathSrc = GetWalletDir() / strFile; fs::path pathDest(strDest); if (fs::is_directory(pathDest)) pathDest /= strFile; try { + if (fs::equivalent(pathSrc, pathDest)) { + LogPrintf("cannot backup to wallet source file %s\n", pathDest.string()); + return false; + } + fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists); LogPrintf("copied %s to %s\n", strFile, pathDest.string()); return true; diff --git a/src/wallet/db.h b/src/wallet/db.h index 6f3cfe9557..ed2ee65cac 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -6,12 +6,12 @@ #ifndef BITCOIN_WALLET_DB_H #define BITCOIN_WALLET_DB_H -#include "clientversion.h" -#include "fs.h" -#include "serialize.h" -#include "streams.h" -#include "sync.h" -#include "version.h" +#include <clientversion.h> +#include <fs.h> +#include <serialize.h> +#include <streams.h> +#include <sync.h> +#include <version.h> #include <atomic> #include <map> @@ -36,7 +36,7 @@ private: public: mutable CCriticalSection cs_db; - DbEnv *dbenv; + std::unique_ptr<DbEnv> dbenv; std::map<std::string, int> mapFileUseCount; std::map<std::string, Db*> mapDb; @@ -156,6 +156,9 @@ public: explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true); ~CDB() { Close(); } + CDB(const CDB&) = delete; + CDB& operator=(const CDB&) = delete; + void Flush(); void Close(); static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); @@ -164,13 +167,9 @@ public: ideal to be called periodically */ static bool PeriodicFlush(CWalletDBWrapper& dbw); /* verifies the database environment */ - static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr); + static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); - -private: - CDB(const CDB&); - void operator=(const CDB&); + static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); public: template <typename K, typename T> diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 6abd060714..9bfcab54a5 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -2,19 +2,19 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/validation.h" -#include "wallet/coincontrol.h" -#include "wallet/feebumper.h" -#include "wallet/fees.h" -#include "wallet/wallet.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "policy/rbf.h" -#include "validation.h" //for mempool access -#include "txmempool.h" -#include "utilmoneystr.h" -#include "util.h" -#include "net.h" +#include <consensus/validation.h> +#include <wallet/coincontrol.h> +#include <wallet/feebumper.h> +#include <wallet/fees.h> +#include <wallet/wallet.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <policy/rbf.h> +#include <validation.h> //for mempool access +#include <txmempool.h> +#include <utilmoneystr.h> +#include <util.h> +#include <net.h> // Calculate the size of the transaction assuming all signatures are max size // Use DummySignatureCreator, which inserts 72 byte signatures everywhere. @@ -23,7 +23,7 @@ // calculation, but we should be able to refactor after priority is removed). // NOTE: this requires that all inputs must be in mapWallet (eg the tx should // be IsAllFromMe). -int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWallet) +static int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet) { CMutableTransaction txNew(tx); std::vector<CInputCoin> vCoins; @@ -31,11 +31,11 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our // wallet, with a valid index into the vout array. for (auto& input : tx.vin) { - const auto mi = pWallet->mapWallet.find(input.prevout.hash); - assert(mi != pWallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); + const auto mi = wallet->mapWallet.find(input.prevout.hash); + assert(mi != wallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); vCoins.emplace_back(CInputCoin(&(mi->second), input.prevout.n)); } - if (!pWallet->DummySignTx(txNew, vCoins)) { + if (!wallet->DummySignTx(txNew, vCoins)) { // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) // implies that we can sign for every input. return -1; @@ -43,103 +43,102 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal return GetVirtualTransactionSize(txNew); } -bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx) { - if (pWallet->HasWalletSpend(wtx.GetHash())) { - vErrors.push_back("Transaction has descendants in the wallet"); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return false; +//! Check whether transaction has descendant in wallet or mempool, or has been +//! mined, or conflicts with a mined transaction. Return a feebumper::Result. +static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) +{ + if (wallet->HasWalletSpend(wtx.GetHash())) { + errors.push_back("Transaction has descendants in the wallet"); + return feebumper::Result::INVALID_PARAMETER; } { LOCK(mempool.cs); auto it_mp = mempool.mapTx.find(wtx.GetHash()); if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) { - vErrors.push_back("Transaction has descendants in the mempool"); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return false; + errors.push_back("Transaction has descendants in the mempool"); + return feebumper::Result::INVALID_PARAMETER; } } if (wtx.GetDepthInMainChain() != 0) { - vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); - currentResult = BumpFeeResult::WALLET_ERROR; - return false; + errors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); + return feebumper::Result::WALLET_ERROR; } - return true; + return feebumper::Result::OK; } -CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee) - : - txid(std::move(txidIn)), - nOldFee(0), - nNewFee(0) +namespace feebumper { + +bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid) { - vErrors.clear(); - bumpedTxid.SetNull(); - AssertLockHeld(pWallet->cs_wallet); - auto it = pWallet->mapWallet.find(txid); - if (it == pWallet->mapWallet.end()) { - vErrors.push_back("Invalid or non-wallet transaction id"); - currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY; - return; + LOCK2(cs_main, wallet->cs_wallet); + const CWalletTx* wtx = wallet->GetWalletTx(txid); + return wtx && SignalsOptInRBF(*wtx->tx) && !wtx->mapValue.count("replaced_by_txid"); +} + +Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors, + CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx) +{ + LOCK2(cs_main, wallet->cs_wallet); + errors.clear(); + auto it = wallet->mapWallet.find(txid); + if (it == wallet->mapWallet.end()) { + errors.push_back("Invalid or non-wallet transaction id"); + return Result::INVALID_ADDRESS_OR_KEY; } const CWalletTx& wtx = it->second; - if (!preconditionChecks(pWallet, wtx)) { - return; + Result result = PreconditionChecks(wallet, wtx, errors); + if (result != Result::OK) { + return result; } - if (!SignalsOptInRBF(wtx)) { - vErrors.push_back("Transaction is not BIP 125 replaceable"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + if (!SignalsOptInRBF(*wtx.tx)) { + errors.push_back("Transaction is not BIP 125 replaceable"); + return Result::WALLET_ERROR; } if (wtx.mapValue.count("replaced_by_txid")) { - vErrors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid"))); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid"))); + return Result::WALLET_ERROR; } // check that original tx consists entirely of our inputs // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) - if (!pWallet->IsAllFromMe(wtx, ISMINE_SPENDABLE)) { - vErrors.push_back("Transaction contains inputs that don't belong to this wallet"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) { + errors.push_back("Transaction contains inputs that don't belong to this wallet"); + return Result::WALLET_ERROR; } // figure out which output was change // if there was no change output or multiple change outputs, fail int nOutput = -1; for (size_t i = 0; i < wtx.tx->vout.size(); ++i) { - if (pWallet->IsChange(wtx.tx->vout[i])) { + if (wallet->IsChange(wtx.tx->vout[i])) { if (nOutput != -1) { - vErrors.push_back("Transaction has multiple change outputs"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + errors.push_back("Transaction has multiple change outputs"); + return Result::WALLET_ERROR; } nOutput = i; } } if (nOutput == -1) { - vErrors.push_back("Transaction does not have a change output"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + errors.push_back("Transaction does not have a change output"); + return Result::WALLET_ERROR; } // Calculate the expected size of the new transaction. int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); - const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, pWallet); + const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, wallet); if (maxNewTxSize < 0) { - vErrors.push_back("Transaction contains inputs that cannot be signed"); - currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY; - return; + errors.push_back("Transaction contains inputs that cannot be signed"); + return Result::INVALID_ADDRESS_OR_KEY; } // calculate the old fee and fee-rate - nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); - CFeeRate nOldFeeRate(nOldFee, txSize); + old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + CFeeRate nOldFeeRate(old_fee, txSize); CFeeRate nNewFeeRate; // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to // future proof against changes to network wide policy for incremental relay @@ -149,26 +148,24 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin walletIncrementalRelayFee = ::incrementalRelayFee; } - if (totalFee > 0) { + if (total_fee > 0) { CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize); - if (totalFee < minTotalFee) { - vErrors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)", + if (total_fee < minTotalFee) { + errors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)", FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize)))); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return; + return Result::INVALID_PARAMETER; } CAmount requiredFee = GetRequiredFee(maxNewTxSize); - if (totalFee < requiredFee) { - vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", + if (total_fee < requiredFee) { + errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", FormatMoney(requiredFee))); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return; + return Result::INVALID_PARAMETER; } - nNewFee = totalFee; - nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); + new_fee = total_fee; + nNewFeeRate = CFeeRate(total_fee, maxNewTxSize); } else { - nNewFee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); - nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); + new_fee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nNewFeeRate = CFeeRate(new_fee, maxNewTxSize); // New fee rate must be at least old rate + minimum incremental relay rate // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized @@ -177,47 +174,50 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin // add 1 satoshi to the result, because it may have been rounded down. if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) { nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()); - nNewFee = nNewFeeRate.GetFee(maxNewTxSize); + new_fee = nNewFeeRate.GetFee(maxNewTxSize); } } // Check that in all cases the new fee doesn't violate maxTxFee - if (nNewFee > maxTxFee) { - vErrors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", - FormatMoney(nNewFee), FormatMoney(maxTxFee))); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + if (new_fee > maxTxFee) { + errors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", + FormatMoney(new_fee), FormatMoney(maxTxFee))); + return Result::WALLET_ERROR; } // check that fee rate is higher than mempool's minimum fee // (no point in bumping fee if we know that the new tx won't be accepted to the mempool) // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps, // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a - // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment. + // moment earlier. In this case, we report an error to the user, who may use total_fee to make an adjustment. CFeeRate minMempoolFeeRate = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { - vErrors.push_back(strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK()))); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + errors.push_back(strprintf( + "New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- " + "the totalFee value should be at least %s or the settxfee value should be at least %s to add transaction", + FormatMoney(nNewFeeRate.GetFeePerK()), + FormatMoney(minMempoolFeeRate.GetFeePerK()), + FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), + FormatMoney(minMempoolFeeRate.GetFeePerK()))); + return Result::WALLET_ERROR; } // Now modify the output to increase the fee. // If the output is not large enough to pay the fee, fail. - CAmount nDelta = nNewFee - nOldFee; + CAmount nDelta = new_fee - old_fee; assert(nDelta > 0); mtx = *wtx.tx; CTxOut* poutput = &(mtx.vout[nOutput]); if (poutput->nValue < nDelta) { - vErrors.push_back("Change output is too small to bump the fee"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + errors.push_back("Change output is too small to bump the fee"); + return Result::WALLET_ERROR; } // If the output would become dust, discard it (converting the dust to fee) poutput->nValue -= nDelta; if (poutput->nValue <= GetDustThreshold(*poutput, ::dustRelayFee)) { LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n"); - nNewFee += poutput->nValue; + new_fee += poutput->nValue; mtx.vout.erase(mtx.vout.begin() + nOutput); } @@ -228,36 +228,36 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin } } - currentResult = BumpFeeResult::OK; + return Result::OK; } -bool CFeeBumper::signTransaction(CWallet *pWallet) -{ - return pWallet->SignTransaction(mtx); +bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx) { + LOCK2(cs_main, wallet->cs_wallet); + return wallet->SignTransaction(mtx); } -bool CFeeBumper::commit(CWallet *pWallet) +Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid) { - AssertLockHeld(pWallet->cs_wallet); - if (!vErrors.empty() || currentResult != BumpFeeResult::OK) { - return false; + LOCK2(cs_main, wallet->cs_wallet); + if (!errors.empty()) { + return Result::MISC_ERROR; } - auto it = txid.IsNull() ? pWallet->mapWallet.end() : pWallet->mapWallet.find(txid); - if (it == pWallet->mapWallet.end()) { - vErrors.push_back("Invalid or non-wallet transaction id"); - currentResult = BumpFeeResult::MISC_ERROR; - return false; + auto it = txid.IsNull() ? wallet->mapWallet.end() : wallet->mapWallet.find(txid); + if (it == wallet->mapWallet.end()) { + errors.push_back("Invalid or non-wallet transaction id"); + return Result::MISC_ERROR; } CWalletTx& oldWtx = it->second; // make sure the transaction still has no descendants and hasn't been mined in the meantime - if (!preconditionChecks(pWallet, oldWtx)) { - return false; + Result result = PreconditionChecks(wallet, oldWtx, errors); + if (result != Result::OK) { + return result; } - CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx))); + CWalletTx wtxBumped(wallet, MakeTransactionRef(std::move(mtx))); // commit/broadcast the tx - CReserveKey reservekey(pWallet); + CReserveKey reservekey(wallet); wtxBumped.mapValue = oldWtx.mapValue; wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); wtxBumped.vOrderForm = oldWtx.vOrderForm; @@ -265,27 +265,29 @@ bool CFeeBumper::commit(CWallet *pWallet) wtxBumped.fTimeReceivedIsTxTime = true; wtxBumped.fFromMe = true; CValidationState state; - if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { + if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. - vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); - return false; + errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason())); + return Result::WALLET_ERROR; } - bumpedTxid = wtxBumped.GetHash(); + bumped_txid = wtxBumped.GetHash(); if (state.IsInvalid()) { // This can happen if the mempool rejected the transaction. Report // what happened in the "errors" response. - vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); + errors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); } // mark the original tx as bumped - if (!pWallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { + if (!wallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { // TODO: see if JSON-RPC has a standard way of returning a response // along with an exception. It would be good to return information about // wtxBumped to the caller even if marking the original transaction // replaced does not succeed for some reason. - vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); + errors.push_back("Created new bumpfee transaction but could not mark the original transaction as replaced"); } - return true; + return Result::OK; } +} // namespace feebumper + diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 3d64e53c15..8eec30440c 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -13,7 +13,9 @@ class uint256; class CCoinControl; enum class FeeEstimateMode; -enum class BumpFeeResult +namespace feebumper { + +enum class Result { OK, INVALID_ADDRESS_OR_KEY, @@ -23,39 +25,34 @@ enum class BumpFeeResult MISC_ERROR, }; -class CFeeBumper -{ -public: - CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee); - BumpFeeResult getResult() const { return currentResult; } - const std::vector<std::string>& getErrors() const { return vErrors; } - CAmount getOldFee() const { return nOldFee; } - CAmount getNewFee() const { return nNewFee; } - uint256 getBumpedTxId() const { return bumpedTxid; } - - /* signs the new transaction, - * returns false if the tx couldn't be found or if it was - * impossible to create the signature(s) - */ - bool signTransaction(CWallet *pWallet); - - /* commits the fee bump, - * returns true, in case of CWallet::CommitTransaction was successful - * but, eventually sets vErrors if the tx could not be added to the mempool (will try later) - * or if the old transaction could not be marked as replaced - */ - bool commit(CWallet *pWalletNonConst); - -private: - bool preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx); - - const uint256 txid; - uint256 bumpedTxid; - CMutableTransaction mtx; - std::vector<std::string> vErrors; - BumpFeeResult currentResult; - CAmount nOldFee; - CAmount nNewFee; -}; +//! Return whether transaction can be bumped. +bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid); + +//! Create bumpfee transaction. +Result CreateTransaction(const CWallet* wallet, + const uint256& txid, + const CCoinControl& coin_control, + CAmount total_fee, + std::vector<std::string>& errors, + CAmount& old_fee, + CAmount& new_fee, + CMutableTransaction& mtx); + +//! Sign the new transaction, +//! @return false if the tx couldn't be found or if it was +//! impossible to create the signature(s) +bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx); + +//! Commit the bumpfee transaction. +//! @return success in case of CWallet::CommitTransaction was successful, +//! but sets errors if the tx could not be added to the mempool (will try later) +//! or if the old transaction could not be marked as replaced. +Result CommitTransaction(CWallet* wallet, + const uint256& txid, + CMutableTransaction&& mtx, + std::vector<std::string>& errors, + uint256& bumped_txid); + +} // namespace feebumper #endif // BITCOIN_WALLET_FEEBUMPER_H diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 76eeeeda05..73985dcf25 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -3,14 +3,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/fees.h" +#include <wallet/fees.h> -#include "policy/policy.h" -#include "txmempool.h" -#include "util.h" -#include "validation.h" -#include "wallet/coincontrol.h" -#include "wallet/wallet.h" +#include <policy/policy.h> +#include <txmempool.h> +#include <util.h> +#include <validation.h> +#include <wallet/coincontrol.h> +#include <wallet/wallet.h> CAmount GetRequiredFee(unsigned int nTxBytes) diff --git a/src/wallet/fees.h b/src/wallet/fees.h index 7b8a7dc868..225aff08ad 100644 --- a/src/wallet/fees.h +++ b/src/wallet/fees.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_WALLET_FEES_H #define BITCOIN_WALLET_FEES_H -#include "amount.h" +#include <amount.h> class CBlockPolicyEstimator; class CCoinControl; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index c984df1df8..67c46df87d 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -3,14 +3,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/init.h" +#include <wallet/init.h> -#include "net.h" -#include "util.h" -#include "utilmoneystr.h" -#include "validation.h" -#include "wallet/wallet.h" -#include "wallet/rpcwallet.h" +#include <net.h> +#include <util.h> +#include <utilmoneystr.h> +#include <validation.h> +#include <wallet/rpcwallet.h> +#include <wallet/wallet.h> +#include <wallet/walletutil.h> std::string GetWalletHelpString(bool showDebug) { @@ -34,6 +35,7 @@ std::string GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)")); 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)")); @@ -53,11 +55,16 @@ std::string GetWalletHelpString(bool showDebug) bool WalletParameterInteraction() { - gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + for (const std::string& wallet : gArgs.GetArgs("-wallet")) { + LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet); + } - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; + } + + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); @@ -173,15 +180,24 @@ bool WalletParameterInteraction() void RegisterWalletRPC(CRPCTable &t) { - if (gArgs.GetBoolArg("-disablewallet", false)) return; + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + return; + } RegisterWalletRPCCommands(t); } bool VerifyWallets() { - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; + } + + if (gArgs.IsArgSet("-walletdir") && !fs::is_directory(GetWalletDir())) { + return InitError(strprintf(_("Error: Specified wallet directory \"%s\" does not exist."), gArgs.GetArg("-walletdir", "").c_str())); + } + + LogPrintf("Using wallet directory %s\n", GetWalletDir().string()); uiInterface.InitMessage(_("Verifying wallet(s)...")); @@ -197,7 +213,7 @@ bool VerifyWallets() return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile)); } - fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + fs::path wallet_path = fs::absolute(walletFile, GetWalletDir()); if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile)); @@ -208,7 +224,7 @@ bool VerifyWallets() } std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + if (!CWalletDB::VerifyEnvironment(walletFile, GetWalletDir().string(), strError)) { return InitError(strError); } @@ -222,7 +238,7 @@ bool VerifyWallets() } std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); + bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetWalletDir().string(), strWarning, strError); if (!strWarning.empty()) { InitWarning(strWarning); } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 9539cc9f42..71d50be634 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -2,22 +2,22 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "base58.h" -#include "chain.h" -#include "rpc/safemode.h" -#include "rpc/server.h" -#include "init.h" -#include "validation.h" -#include "script/script.h" -#include "script/standard.h" -#include "sync.h" -#include "util.h" -#include "utiltime.h" -#include "wallet.h" -#include "merkleblock.h" -#include "core_io.h" - -#include "rpcwallet.h" +#include <base58.h> +#include <chain.h> +#include <rpc/safemode.h> +#include <rpc/server.h> +#include <wallet/init.h> +#include <validation.h> +#include <script/script.h> +#include <script/standard.h> +#include <sync.h> +#include <util.h> +#include <utiltime.h> +#include <wallet/wallet.h> +#include <merkleblock.h> +#include <core_io.h> + +#include <wallet/rpcwallet.h> #include <fstream> #include <stdint.h> @@ -80,10 +80,10 @@ UniValue importprivkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n" - "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" + "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n" + "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n" "\nArguments:\n" - "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n" + "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "\nNote: This call can take minutes to complete if rescan is true.\n" @@ -165,7 +165,7 @@ UniValue abortrescan(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 0) throw std::runtime_error( "abortrescan\n" - "\nStops current wallet rescan triggered e.g. by an importprivkey call.\n" + "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n" "\nExamples:\n" "\nImport a private key\n" + HelpExampleCli("importprivkey", "\"mykey\"") + @@ -226,7 +226,7 @@ UniValue importaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importaddress \"address\" ( \"label\" rescan p2sh )\n" - "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n" + "\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" "\nArguments:\n" "1. \"script\" (string, required) The hex-encoded script (or address)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" @@ -340,7 +340,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - if (pwallet->IsMine(wtx)) { + if (pwallet->IsMine(*wtx.tx)) { pwallet->AddToWallet(wtx, false); return NullUniValue; } @@ -396,7 +396,7 @@ UniValue importpubkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) throw std::runtime_error( "importpubkey \"pubkey\" ( \"label\" rescan )\n" - "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" + "\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n" "\nArguments:\n" "1. \"pubkey\" (string, required) The hex-encoded public key\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" @@ -456,7 +456,7 @@ UniValue importwallet(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "importwallet \"filename\"\n" - "\nImports keys from a wallet dump file (see dumpwallet).\n" + "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n" "\nArguments:\n" "1. \"filename\" (string, required) The wallet file\n" "\nExamples:\n" @@ -600,7 +600,10 @@ UniValue dumpwallet(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpwallet \"filename\"\n" - "\nDumps all wallet keys in a human-readable format.\n" + "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n" + "Imported scripts are not currently included in wallet dumps, these must be backed up separately.\n" + "Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n" + "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n" "\nResult:\n" @@ -616,9 +619,19 @@ UniValue dumpwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - std::ofstream file; boost::filesystem::path filepath = request.params[0].get_str(); filepath = boost::filesystem::absolute(filepath); + + /* Prevent arbitrary files from being overwritten. There have been reports + * that users have overwritten wallet files this way: + * https://github.com/bitcoin/bitcoin/issues/9934 + * It may also avoid other security issues. + */ + if (boost::filesystem::exists(filepath)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first"); + } + + std::ofstream file; file.open(filepath.string().c_str()); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -951,7 +964,7 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 pwallet->SetAddressBook(vchAddress, label, "receive"); if (pwallet->HaveKey(vchAddress)) { - return false; + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; @@ -1029,7 +1042,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2) throw std::runtime_error( "importmulti \"requests\" ( \"options\" )\n\n" - "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n\n" + "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n" "Arguments:\n" "1. requests (array, required) Data to be imported\n" " [ (array of json objects)\n" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae89b3c0a1..473acd8367 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3,29 +3,30 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "amount.h" -#include "base58.h" -#include "chain.h" -#include "consensus/validation.h" -#include "core_io.h" -#include "httpserver.h" -#include "validation.h" -#include "net.h" -#include "policy/feerate.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "policy/rbf.h" -#include "rpc/mining.h" -#include "rpc/safemode.h" -#include "rpc/server.h" -#include "script/sign.h" -#include "timedata.h" -#include "util.h" -#include "utilmoneystr.h" -#include "wallet/coincontrol.h" -#include "wallet/feebumper.h" -#include "wallet/wallet.h" -#include "wallet/walletdb.h" +#include <amount.h> +#include <base58.h> +#include <chain.h> +#include <consensus/validation.h> +#include <core_io.h> +#include <httpserver.h> +#include <validation.h> +#include <net.h> +#include <policy/feerate.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <policy/rbf.h> +#include <rpc/mining.h> +#include <rpc/safemode.h> +#include <rpc/server.h> +#include <script/sign.h> +#include <timedata.h> +#include <util.h> +#include <utilmoneystr.h> +#include <wallet/coincontrol.h> +#include <wallet/feebumper.h> +#include <wallet/wallet.h> +#include <wallet/walletdb.h> +#include <wallet/walletutil.h> #include <init.h> // For StartShutdown @@ -108,7 +109,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) std::string rbfStatus = "no"; if (confirms <= 0) { LOCK(mempool.cs); - RBFTransactionState rbfState = IsRBFOptIn(wtx, mempool); + RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool); if (rbfState == RBF_TRANSACTIONSTATE_UNKNOWN) rbfStatus = "unknown"; else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125) @@ -455,6 +456,11 @@ UniValue sendtoaddress(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str()); @@ -533,6 +539,11 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); @@ -645,6 +656,11 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address @@ -654,7 +670,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) } CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { - return ValueFromAmount(0); + throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } // Minimum confirmations @@ -707,6 +723,11 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); // Minimum confirmations @@ -780,6 +801,11 @@ UniValue getbalance(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); const UniValue& account_value = request.params[0]; @@ -825,6 +851,11 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request) "Returns the server's total unconfirmed balance\n"); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); return ValueFromAmount(pwallet->GetUnconfirmedBalance()); @@ -919,6 +950,11 @@ UniValue sendfrom(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = AccountFromValue(request.params[0]); @@ -1004,6 +1040,11 @@ UniValue sendmany(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); if (pwallet->GetBroadcastTransactions() && !g_connman) { @@ -1110,7 +1151,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) { std::string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" - "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" + "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n" "Each key is a Bitcoin address or hex-encoded public key.\n" "If 'account' is specified (DEPRECATED), assign address to that account.\n" @@ -1154,11 +1195,10 @@ class Witnessifier : public boost::static_visitor<bool> { public: CWallet * const pwallet; - CScriptID result; + CTxDestination result; + bool already_witness; - explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} - - bool operator()(const CNoDestination &dest) const { return false; } + explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {} bool operator()(const CKeyID &keyID) { if (pwallet) { @@ -1172,9 +1212,7 @@ public: !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } @@ -1185,7 +1223,8 @@ public: int witnessversion; std::vector<unsigned char> witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { - result = scriptID; + ExtractDestination(subscript, result); + already_witness = true; return true; } CScript witscript = GetScriptForWitness(subscript); @@ -1197,12 +1236,27 @@ public: !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } + + bool operator()(const WitnessV0KeyHash& id) + { + already_witness = true; + result = id; + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) + { + already_witness = true; + result = id; + return true; + } + + template<typename T> + bool operator()(const T& dest) { return false; } }; UniValue addwitnessaddress(const JSONRPCRequest& request) @@ -1212,17 +1266,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { - std::string msg = "addwitnessaddress \"address\"\n" - "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" + std::string msg = "addwitnessaddress \"address\" ( p2sh )\n" + "\nAdd a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n" "It returns the witness script.\n" "\nArguments:\n" "1. \"address\" (string, required) An address known to the wallet\n" + "2. p2sh (bool, optional, default=true) Embed inside P2SH\n" "\nResult:\n" - "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n" "}\n" ; throw std::runtime_error(msg); @@ -1240,13 +1295,31 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } + bool p2sh = true; + if (!request.params[1].isNull()) { + p2sh = request.params[1].get_bool(); + } + Witnessifier w(pwallet); bool ret = boost::apply_visitor(w, dest); if (!ret) { throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); } - pwallet->SetAddressBook(w.result, "", "receive"); + CScript witprogram = GetScriptForDestination(w.result); + + if (p2sh) { + w.result = CScriptID(witprogram); + } + + if (w.already_witness) { + if (!(dest == w.result)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types"); + } + } else { + pwallet->AddCScript(witprogram); + pwallet->SetAddressBook(w.result, "", "receive"); + } return EncodeDestination(w.result); } @@ -1366,14 +1439,14 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA if (fByAccounts) { - for (std::map<std::string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) + for (const auto& entry : mapAccountTally) { - CAmount nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; + CAmount nAmount = entry.second.nAmount; + int nConf = entry.second.nConf; UniValue obj(UniValue::VOBJ); - if((*it).second.fIsWatchonly) + if (entry.second.fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("account", entry.first)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); ret.push_back(obj); @@ -1423,6 +1496,11 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, false); @@ -1463,6 +1541,11 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, true); @@ -1651,6 +1734,11 @@ UniValue listtransactions(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = "*"; @@ -1745,6 +1833,11 @@ UniValue listaccounts(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = 1; @@ -1854,6 +1947,11 @@ UniValue listsinceblock(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. @@ -1861,19 +1959,20 @@ UniValue listsinceblock(const JSONRPCRequest& request) int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; - if (!request.params[0].isNull()) { + if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { uint256 blockId; blockId.SetHex(request.params[0].get_str()); BlockMap::iterator it = mapBlockIndex.find(blockId); - if (it != mapBlockIndex.end()) { - paltindex = pindex = it->second; - if (chainActive[pindex->nHeight] != pindex) { - // the block being asked for is a part of a deactivated chain; - // we don't want to depend on its perceived height in the block - // chain, we want to instead use the last common ancestor - pindex = chainActive.FindFork(pindex); - } + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + paltindex = pindex = it->second; + if (chainActive[pindex->nHeight] != pindex) { + // the block being asked for is a part of a deactivated chain; + // we don't want to depend on its perceived height in the block + // chain, we want to instead use the last common ancestor + pindex = chainActive.FindFork(pindex); } } @@ -1986,6 +2085,11 @@ UniValue gettransaction(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2018,7 +2122,7 @@ UniValue gettransaction(const JSONRPCRequest& request) ListTransactions(pwallet, wtx, "*", 0, false, details, filter); entry.push_back(Pair("details", details)); - std::string strHex = EncodeHexTx(static_cast<CTransaction>(wtx), RPCSerializationFlags()); + std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags()); entry.push_back(Pair("hex", strHex)); return entry; @@ -2048,6 +2152,11 @@ UniValue abandontransaction(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2082,6 +2191,10 @@ UniValue backupwallet(const JSONRPCRequest& request) + HelpExampleRpc("backupwallet", "\"backup.dat\"") ); + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); std::string strDest = request.params[0].get_str(); @@ -2147,7 +2260,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request) return NullUniValue; } - if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) { + if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "walletpassphrase \"passphrase\" timeout\n" "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" @@ -2211,7 +2324,7 @@ UniValue walletpassphrasechange(const JSONRPCRequest& request) return NullUniValue; } - if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) { + if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" @@ -2262,7 +2375,7 @@ UniValue walletlock(const JSONRPCRequest& request) return NullUniValue; } - if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) { + if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "walletlock\n" "\nRemoves the wallet encryption key from memory, locking the wallet.\n" @@ -2302,7 +2415,7 @@ UniValue encryptwallet(const JSONRPCRequest& request) return NullUniValue; } - if (!pwallet->IsCrypted() && (request.fHelp || request.params.size() != 1)) { + if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "encryptwallet \"passphrase\"\n" "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" @@ -2401,6 +2514,10 @@ UniValue lockunspent(const JSONRPCRequest& request) + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") ); + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); RPCTypeCheckArgument(request.params[0], UniValue::VBOOL); @@ -2415,12 +2532,15 @@ UniValue lockunspent(const JSONRPCRequest& request) RPCTypeCheckArgument(request.params[1], UniValue::VARR); - UniValue outputs = request.params[1].get_array(); - for (unsigned int idx = 0; idx < outputs.size(); idx++) { - const UniValue& output = outputs[idx]; - if (!output.isObject()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); - const UniValue& o = output.get_obj(); + const UniValue& output_params = request.params[1]; + + // Create and validate the COutPoints first. + + std::vector<COutPoint> outputs; + outputs.reserve(output_params.size()); + + for (unsigned int idx = 0; idx < output_params.size(); idx++) { + const UniValue& o = output_params[idx].get_obj(); RPCTypeCheckObj(o, { @@ -2428,20 +2548,50 @@ UniValue lockunspent(const JSONRPCRequest& request) {"vout", UniValueType(UniValue::VNUM)}, }); - std::string txid = find_value(o, "txid").get_str(); - if (!IsHex(txid)) + const std::string& txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + } - int nOutput = find_value(o, "vout").get_int(); - if (nOutput < 0) + const int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + } - COutPoint outpt(uint256S(txid), nOutput); + const COutPoint outpt(uint256S(txid), nOutput); - if (fUnlock) - pwallet->UnlockCoin(outpt); - else - pwallet->LockCoin(outpt); + const auto it = pwallet->mapWallet.find(outpt.hash); + if (it == pwallet->mapWallet.end()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, unknown transaction"); + } + + const CWalletTx& trans = it->second; + + if (outpt.n >= trans.tx->vout.size()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds"); + } + + if (pwallet->IsSpent(outpt.hash, outpt.n)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output"); + } + + const bool is_locked = pwallet->IsLockedCoin(outpt.hash, outpt.n); + + if (fUnlock && !is_locked) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected locked output"); + } + + if (!fUnlock && is_locked) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output already locked"); + } + + outputs.push_back(outpt); + } + + // Atomically set (un)locked status for the outputs. + for (const COutPoint& outpt : outputs) { + if (fUnlock) pwallet->UnlockCoin(outpt); + else pwallet->LockCoin(outpt); } return true; @@ -2560,6 +2710,11 @@ UniValue getwalletinfo(const JSONRPCRequest& request) ); ObserveSafeMode(); + + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); @@ -2769,9 +2924,12 @@ UniValue listunspent(const JSONRPCRequest& request) nMaximumCount = options["maximumCount"].get_int64(); } + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + UniValue results(UniValue::VARR); std::vector<COutput> vecOutputs; - assert(pwallet != nullptr); LOCK2(cs_main, pwallet->cs_wallet); pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); @@ -2879,6 +3037,10 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) ObserveSafeMode(); RPCTypeCheck(request.params, {UniValue::VSTR}); + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + CCoinControl coinControl; int changePosition = -1; bool lockUnspents = false; @@ -3089,48 +3251,57 @@ UniValue bumpfee(const JSONRPCRequest& request) } } + // Make sure the results are valid at least up to the most recent block + // the user could have gotten from another RPC command prior to now + pwallet->BlockUntilSyncedToCurrentChain(); + LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); - CFeeBumper feeBump(pwallet, hash, coin_control, totalFee); - BumpFeeResult res = feeBump.getResult(); - if (res != BumpFeeResult::OK) - { + + std::vector<std::string> errors; + CAmount old_fee; + CAmount new_fee; + CMutableTransaction mtx; + feebumper::Result res = feebumper::CreateTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx); + if (res != feebumper::Result::OK) { switch(res) { - case BumpFeeResult::INVALID_ADDRESS_OR_KEY: - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, feeBump.getErrors()[0]); + case feebumper::Result::INVALID_ADDRESS_OR_KEY: + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0]); break; - case BumpFeeResult::INVALID_REQUEST: - throw JSONRPCError(RPC_INVALID_REQUEST, feeBump.getErrors()[0]); + case feebumper::Result::INVALID_REQUEST: + throw JSONRPCError(RPC_INVALID_REQUEST, errors[0]); break; - case BumpFeeResult::INVALID_PARAMETER: - throw JSONRPCError(RPC_INVALID_PARAMETER, feeBump.getErrors()[0]); + case feebumper::Result::INVALID_PARAMETER: + throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0]); break; - case BumpFeeResult::WALLET_ERROR: - throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]); + case feebumper::Result::WALLET_ERROR: + throw JSONRPCError(RPC_WALLET_ERROR, errors[0]); break; default: - throw JSONRPCError(RPC_MISC_ERROR, feeBump.getErrors()[0]); + throw JSONRPCError(RPC_MISC_ERROR, errors[0]); break; } } // sign bumped transaction - if (!feeBump.signTransaction(pwallet)) { + if (!feebumper::SignTransaction(pwallet, mtx)) { throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); } // commit the bumped transaction - if(!feeBump.commit(pwallet)) { - throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]); + uint256 txid; + if (feebumper::CommitTransaction(pwallet, hash, std::move(mtx), errors, txid) != feebumper::Result::OK) { + throw JSONRPCError(RPC_WALLET_ERROR, errors[0]); } UniValue result(UniValue::VOBJ); - result.push_back(Pair("txid", feeBump.getBumpedTxId().GetHex())); - result.push_back(Pair("origfee", ValueFromAmount(feeBump.getOldFee()))); - result.push_back(Pair("fee", ValueFromAmount(feeBump.getNewFee()))); - UniValue errors(UniValue::VARR); - for (const std::string& err: feeBump.getErrors()) - errors.push_back(err); - result.push_back(Pair("errors", errors)); + result.push_back(Pair("txid", txid.GetHex())); + result.push_back(Pair("origfee", ValueFromAmount(old_fee))); + result.push_back(Pair("fee", ValueFromAmount(new_fee))); + UniValue result_errors(UniValue::VARR); + for (const std::string& error : errors) { + result_errors.push_back(error); + } + result.push_back(Pair("errors", result_errors)); return result; } @@ -3180,6 +3351,81 @@ UniValue generate(const JSONRPCRequest& request) return generateBlocks(coinbase_script, num_generate, max_tries, true); } +UniValue rescanblockchain(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 2) { + throw std::runtime_error( + "rescanblockchain (\"start_height\") (\"stop_height\")\n" + "\nRescan the local blockchain for wallet related transactions.\n" + "\nArguments:\n" + "1. \"start_height\" (numeric, optional) block height where the rescan should start\n" + "2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n" + "\nResult:\n" + "{\n" + " \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n" + " \"stop_height\" (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("rescanblockchain", "100000 120000") + + HelpExampleRpc("rescanblockchain", "100000, 120000") + ); + } + + LOCK2(cs_main, pwallet->cs_wallet); + + CBlockIndex *pindexStart = chainActive.Genesis(); + CBlockIndex *pindexStop = nullptr; + if (!request.params[0].isNull()) { + pindexStart = chainActive[request.params[0].get_int()]; + if (!pindexStart) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height"); + } + } + + if (!request.params[1].isNull()) { + pindexStop = chainActive[request.params[1].get_int()]; + if (!pindexStop) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height"); + } + else if (pindexStop->nHeight < pindexStart->nHeight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height"); + } + } + + // We can't rescan beyond non-pruned blocks, stop and throw an error + if (fPruneMode) { + CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip(); + while (block && block->nHeight >= pindexStart->nHeight) { + if (!(block->nStatus & BLOCK_HAVE_DATA)) { + throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height."); + } + block = block->pprev; + } + } + + CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, true); + if (!stopBlock) { + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); + } + // if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex + stopBlock = pindexStop ? pindexStop : chainActive.Tip(); + } + else { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files."); + } + + UniValue response(UniValue::VOBJ); + response.pushKV("start_height", pindexStart->nHeight); + response.pushKV("stop_height", stopBlock->nHeight); + return response; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -3190,6 +3436,7 @@ extern UniValue importwallet(const JSONRPCRequest& request); extern UniValue importprunedfunds(const JSONRPCRequest& request); extern UniValue removeprunedfunds(const JSONRPCRequest& request); extern UniValue importmulti(const JSONRPCRequest& request); +extern UniValue rescanblockchain(const JSONRPCRequest& request); static const CRPCCommand commands[] = { // category name actor (function) argNames @@ -3199,7 +3446,7 @@ static const CRPCCommand commands[] = { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abortrescan", &abortrescan, {} }, { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, - { "wallet", "addwitnessaddress", &addwitnessaddress, {"address"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, @@ -3244,6 +3491,7 @@ static const CRPCCommand commands[] = { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index 330878ceb5..b95bb14335 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -2,26 +2,24 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/wallet.h" +#include <wallet/wallet.h> -#include "wallet/test/wallet_test_fixture.h" +#include <wallet/test/wallet_test_fixture.h> #include <stdint.h> #include <boost/test/unit_test.hpp> -extern CWallet* pwalletMain; - BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup) static void -GetResults(std::map<CAmount, CAccountingEntry>& results) +GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results) { std::list<CAccountingEntry> aes; results.clear(); - BOOST_CHECK(pwalletMain->ReorderTransactions() == DB_LOAD_OK); - pwalletMain->ListAccountCreditDebit("", aes); + BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK); + wallet->ListAccountCreditDebit("", aes); for (CAccountingEntry& ae : aes) { results[ae.nOrderPos] = ae; @@ -54,7 +52,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) ae.strOtherAccount = "c"; pwalletMain->AddAccountingEntry(ae); - GetResults(results); + GetResults(pwalletMain.get(), results); BOOST_CHECK(pwalletMain->nOrderPosNext == 3); BOOST_CHECK(2 == results.size()); @@ -70,7 +68,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) ae.nOrderPos = pwalletMain->IncOrderPosNext(); pwalletMain->AddAccountingEntry(ae); - GetResults(results); + GetResults(pwalletMain.get(), results); BOOST_CHECK(results.size() == 3); BOOST_CHECK(pwalletMain->nOrderPosNext == 4); @@ -83,7 +81,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "y"; { - CMutableTransaction tx(wtx); + CMutableTransaction tx(*wtx.tx); --tx.nLockTime; // Just to change the hash :) wtx.SetTx(MakeTransactionRef(std::move(tx))); } @@ -93,7 +91,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "x"; { - CMutableTransaction tx(wtx); + CMutableTransaction tx(*wtx.tx); --tx.nLockTime; // Just to change the hash :) wtx.SetTx(MakeTransactionRef(std::move(tx))); } @@ -102,7 +100,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) vpwtx[2]->nTimeReceived = (unsigned int)1333333329; vpwtx[2]->nOrderPos = -1; - GetResults(results); + GetResults(pwalletMain.get(), results); BOOST_CHECK(results.size() == 3); BOOST_CHECK(pwalletMain->nOrderPosNext == 6); @@ -120,7 +118,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) ae.nOrderPos = -1; pwalletMain->AddAccountingEntry(ae); - GetResults(results); + GetResults(pwalletMain.get(), results); BOOST_CHECK(results.size() == 4); BOOST_CHECK(pwalletMain->nOrderPosNext == 7); diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index f4dabc50c0..3ff8c6d224 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -2,9 +2,9 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "test/test_bitcoin.h" -#include "utilstrencodings.h" -#include "wallet/crypter.h" +#include <test/test_bitcoin.h> +#include <utilstrencodings.h> +#include <wallet/crypter.h> #include <vector> diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index e2f48c45ab..3ee83d2d7c 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -2,13 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/test/wallet_test_fixture.h" +#include <wallet/test/wallet_test_fixture.h> -#include "rpc/server.h" -#include "wallet/db.h" -#include "wallet/wallet.h" - -CWallet *pwalletMain; +#include <rpc/server.h> +#include <wallet/db.h> WalletTestingSetup::WalletTestingSetup(const std::string& chainName): TestingSetup(chainName) @@ -17,18 +14,16 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName): bool fFirstRun; std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); - pwalletMain = new CWallet(std::move(dbw)); + pwalletMain = MakeUnique<CWallet>(std::move(dbw)); pwalletMain->LoadWallet(fFirstRun); - RegisterValidationInterface(pwalletMain); + RegisterValidationInterface(pwalletMain.get()); RegisterWalletRPCCommands(tableRPC); } WalletTestingSetup::~WalletTestingSetup() { - UnregisterValidationInterface(pwalletMain); - delete pwalletMain; - pwalletMain = nullptr; + UnregisterValidationInterface(pwalletMain.get()); bitdb.Flush(true); bitdb.Reset(); diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index 9373b7907c..292d654438 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -5,13 +5,17 @@ #ifndef BITCOIN_WALLET_TEST_FIXTURE_H #define BITCOIN_WALLET_TEST_FIXTURE_H -#include "test/test_bitcoin.h" +#include <test/test_bitcoin.h> + +#include <wallet/wallet.h> /** Testing setup and teardown for wallet. */ struct WalletTestingSetup: public TestingSetup { explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~WalletTestingSetup(); + + std::unique_ptr<CWallet> pwalletMain; }; #endif diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 5ebacd57d3..80e31a1ce0 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -2,25 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/wallet.h" +#include <wallet/wallet.h> #include <set> #include <stdint.h> #include <utility> #include <vector> -#include "consensus/validation.h" -#include "rpc/server.h" -#include "test/test_bitcoin.h" -#include "validation.h" -#include "wallet/coincontrol.h" -#include "wallet/test/wallet_test_fixture.h" +#include <consensus/validation.h> +#include <rpc/server.h> +#include <test/test_bitcoin.h> +#include <validation.h> +#include <wallet/coincontrol.h> +#include <wallet/test/wallet_test_fixture.h> #include <boost/test/unit_test.hpp> #include <univalue.h> -extern CWallet* pwalletMain; - extern UniValue importmulti(const JSONRPCRequest& request); extern UniValue dumpwallet(const JSONRPCRequest& request); extern UniValue importwallet(const JSONRPCRequest& request); @@ -386,7 +384,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { CWallet wallet; AddKey(wallet, coinbaseKey); - BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); + BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -399,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { CWallet wallet; AddKey(wallet, coinbaseKey); - BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); + BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -489,6 +487,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) vpwallets[0] = &wallet; ::importwallet(request); + LOCK(wallet.cs_wallet); BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3); BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103); for (size_t i = 0; i < coinbaseTxns.size(); ++i) { @@ -534,6 +533,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 SetMockTime(mockTime); CBlockIndex* block = nullptr; if (blockTime > 0) { + LOCK(cs_main); auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex); assert(inserted.second); const uint256& hash = inserted.first->first; @@ -547,6 +547,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 wtx.SetMerkleBranch(block, 0); } wallet.AddToWallet(wtx); + LOCK(wallet.cs_wallet); return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart; } @@ -583,6 +584,7 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart) BOOST_AUTO_TEST_CASE(LoadReceiveRequests) { CTxDestination dest = CKeyID(); + LOCK(pwalletMain->cs_wallet); pwalletMain->AddDestData(dest, "misc", "val_misc"); pwalletMain->AddDestData(dest, "rr0", "val_rr0"); pwalletMain->AddDestData(dest, "rr1", "val_rr1"); @@ -604,7 +606,7 @@ public: bool firstRun; wallet->LoadWallet(firstRun); AddKey(*wallet, coinbaseKey); - wallet->ScanForWalletTransactions(chainActive.Genesis()); + wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr); } ~ListCoinsTestingSetup() @@ -625,6 +627,7 @@ public: BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy)); CValidationState state; BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state)); + LOCK(wallet->cs_wallet); auto it = wallet->mapWallet.find(wtx.GetHash()); BOOST_CHECK(it != wallet->mapWallet.end()); CreateAndProcessBlock({CMutableTransaction(*it->second.tx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d376de2337..ee1894501c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3,36 +3,37 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/wallet.h" - -#include "base58.h" -#include "checkpoints.h" -#include "chain.h" -#include "wallet/coincontrol.h" -#include "consensus/consensus.h" -#include "consensus/validation.h" -#include "fs.h" -#include "init.h" -#include "key.h" -#include "keystore.h" -#include "validation.h" -#include "net.h" -#include "policy/fees.h" -#include "policy/policy.h" -#include "policy/rbf.h" -#include "primitives/block.h" -#include "primitives/transaction.h" -#include "script/script.h" -#include "script/sign.h" -#include "scheduler.h" -#include "timedata.h" -#include "txmempool.h" -#include "util.h" -#include "ui_interface.h" -#include "utilmoneystr.h" -#include "wallet/fees.h" +#include <wallet/wallet.h> + +#include <base58.h> +#include <checkpoints.h> +#include <chain.h> +#include <wallet/coincontrol.h> +#include <consensus/consensus.h> +#include <consensus/validation.h> +#include <fs.h> +#include <wallet/init.h> +#include <key.h> +#include <keystore.h> +#include <validation.h> +#include <net.h> +#include <policy/fees.h> +#include <policy/policy.h> +#include <policy/rbf.h> +#include <primitives/block.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <script/sign.h> +#include <scheduler.h> +#include <timedata.h> +#include <txmempool.h> +#include <util.h> +#include <ui_interface.h> +#include <utilmoneystr.h> +#include <wallet/fees.h> #include <assert.h> +#include <future> #include <boost/algorithm/string/replace.hpp> #include <boost/thread.hpp> @@ -111,7 +112,26 @@ public: Process(script); } - void operator()(const CNoDestination &none) {} + void operator()(const WitnessV0ScriptHash& scriptID) + { + CScriptID id; + CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin()); + CScript script; + if (keystore.GetCScript(id, script)) { + Process(script); + } + } + + void operator()(const WitnessV0KeyHash& keyid) + { + CKeyID id(keyid); + if (keystore.HaveKey(id)) { + vKeys.push_back(id); + } + } + + template<typename X> + void operator()(const X &none) {} }; const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const @@ -390,11 +410,11 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; @@ -513,12 +533,16 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran copyFrom = &mapWallet[hash]; } } + + assert(copyFrom); + // Now copy data from copyFrom to rest: for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; + assert(copyFrom && "Oldest wallet transaction in range assumed to have been found."); if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; @@ -595,11 +619,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) CCrypter crypter; int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + kMasterKey.nDeriveIterations = static_cast<unsigned int>(2500000 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; @@ -681,9 +705,9 @@ DBErrors CWallet::ReorderTransactions() typedef std::multimap<int64_t, TxPair > TxItems; TxItems txByTime; - for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (auto& entry : mapWallet) { - CWalletTx* wtx = &((*it).second); + CWalletTx* wtx = &entry.second; txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); } std::list<CAccountingEntry> acentries; @@ -914,6 +938,15 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } + // If we have a witness-stripped version of this transaction, and we + // see a new version with a witness, then we must be upgrading a pre-segwit + // wallet. Store the new version of the transaction with the witness, + // as the stripped-version must be invalid. + // TODO: Store all versions of the transaction, instead of just one. + if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) { + wtx.SetTx(wtxIn.tx); + fUpdated = true; + } } //// debug print @@ -933,7 +966,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) // notify an external script when a wallet transaction comes in or is updated std::string strCmd = gArgs.GetArg("-walletnotify", ""); - if ( !strCmd.empty()) + if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free @@ -1185,6 +1218,19 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) { LOCK2(cs_main, cs_wallet); SyncTransaction(ptx); + + auto it = mapWallet.find(ptx->GetHash()); + if (it != mapWallet.end()) { + it->second.fInMempool = true; + } +} + +void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) { + LOCK(cs_wallet); + auto it = mapWallet.find(ptx->GetHash()); + if (it != mapWallet.end()) { + it->second.fInMempool = false; + } } void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) { @@ -1199,10 +1245,14 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const for (const CTransactionRef& ptx : vtxConflicted) { SyncTransaction(ptx); + TransactionRemovedFromMempool(ptx); } for (size_t i = 0; i < pblock->vtx.size(); i++) { SyncTransaction(pblock->vtx[i], pindex, i); + TransactionRemovedFromMempool(pblock->vtx[i]); } + + m_last_block_processed = pindex; } void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) { @@ -1215,6 +1265,36 @@ void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) { +void CWallet::BlockUntilSyncedToCurrentChain() { + AssertLockNotHeld(cs_main); + AssertLockNotHeld(cs_wallet); + + { + // Skip the queue-draining stuff if we know we're caught up with + // chainActive.Tip()... + // We could also take cs_wallet here, and call m_last_block_processed + // protected by cs_wallet instead of cs_main, but as long as we need + // cs_main here anyway, its easier to just call it cs_main-protected. + LOCK(cs_main); + const CBlockIndex* initialChainTip = chainActive.Tip(); + + if (m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) { + return; + } + } + + // ...otherwise put a callback in the validation interface queue and wait + // for the queue to drain enough to execute it (indicating we are caught up + // at least with the time we entered this function). + + std::promise<void> promise; + CallFunctionInValidationInterfaceQueue([&promise] { + promise.set_value(); + }); + promise.get_future().wait(); +} + + isminetype CWallet::IsMine(const CTxIn &txin) const { { @@ -1539,7 +1619,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); if (startBlock) { - const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, update); + const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update); if (failedBlock) { return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; } @@ -1555,12 +1635,19 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update) * Returns null if scan was successful. Otherwise, if a complete rescan was not * possible (due to pruning or corruption), returns pointer to the most recent * block that could not be scanned. + * + * If pindexStop is not a nullptr, the scan will stop at the block-index + * defined by pindexStop */ -CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate) { int64_t nNow = GetTime(); const CChainParams& chainParams = Params(); + if (pindexStop) { + assert(pindexStop->nHeight >= pindexStart->nHeight); + } + CBlockIndex* pindex = pindexStart; CBlockIndex* ret = nullptr; { @@ -1588,6 +1675,9 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f } else { ret = pindex; } + if (pindex == pindexStop) { + break; + } pindex = chainActive.Next(pindex); } if (pindex && fAbortRescan) { @@ -1679,7 +1769,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const debit += nDebitCached; else { - nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); + nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE); fDebitCached = true; debit += nDebitCached; } @@ -1690,7 +1780,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const debit += nWatchDebitCached; else { - nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); + nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY); fWatchDebitCached = true; debit += nWatchDebitCached; } @@ -1712,7 +1802,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const credit += nCreditCached; else { - nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE); fCreditCached = true; credit += nCreditCached; } @@ -1723,7 +1813,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const credit += nWatchCreditCached; else { - nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY); fWatchCreditCached = true; credit += nWatchCreditCached; } @@ -1737,7 +1827,7 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; - nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE); fImmatureCreditCached = true; return nImmatureCreditCached; } @@ -1781,7 +1871,7 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const { if (fUseCache && fImmatureWatchCreditCached) return nImmatureWatchCreditCached; - nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); + nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY); fImmatureWatchCreditCached = true; return nImmatureWatchCreditCached; } @@ -1822,21 +1912,20 @@ CAmount CWalletTx::GetChange() const { if (fChangeCached) return nChangeCached; - nChangeCached = pwallet->GetChange(*this); + nChangeCached = pwallet->GetChange(*tx); fChangeCached = true; return nChangeCached; } bool CWalletTx::InMempool() const { - LOCK(mempool.cs); - return mempool.exists(GetHash()); + return fInMempool; } bool CWalletTx::IsTrusted() const { // Quick answer in most cases - if (!CheckFinalTx(*this)) + if (!CheckFinalTx(*tx)) return false; int nDepth = GetDepthInMainChain(); if (nDepth >= 1) @@ -1937,9 +2026,9 @@ CAmount CWallet::GetBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(); } @@ -1953,9 +2042,9 @@ CAmount CWallet::GetUnconfirmedBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) nTotal += pcoin->GetAvailableCredit(); } @@ -1968,9 +2057,9 @@ CAmount CWallet::GetImmatureBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; nTotal += pcoin->GetImmatureCredit(); } } @@ -1982,9 +2071,9 @@ CAmount CWallet::GetWatchOnlyBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } @@ -1998,9 +2087,9 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) nTotal += pcoin->GetAvailableWatchOnlyCredit(); } @@ -2013,9 +2102,9 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; nTotal += pcoin->GetImmatureWatchOnlyCredit(); } } @@ -2089,12 +2178,12 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CAmount nTotal = 0; - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const uint256& wtxid = it->first; - const CWalletTx* pcoin = &(*it).second; + const uint256& wtxid = entry.first; + const CWalletTx* pcoin = &entry.second; - if (!CheckFinalTx(*pcoin)) + if (!CheckFinalTx(*pcoin->tx)) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) @@ -2153,10 +2242,10 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount) continue; - if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i))) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) continue; - if (IsLockedCoin((*it).first, i)) + if (IsLockedCoin(entry.first, i)) continue; if (IsSpent(wtxid, i)) @@ -2675,6 +2764,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT if (recipient.fSubtractFeeFromAmount) { + assert(nSubtractFeeFromAmount != 0); txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient if (fFirst) // first receiver pays the remainder not divisible by output count @@ -2875,7 +2965,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); // Limit size - if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT) + if (GetTransactionWeight(*wtxNew.tx) >= MAX_STANDARD_TX_WEIGHT) { strFailReason = _("Transaction too large"); return false; @@ -2937,14 +3027,18 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon // Track how many getdata requests our transaction gets mapRequestCount[wtxNew.GetHash()] = 0; + // Get the inserted-CWalletTx from mapWallet so that the + // fInMempool flag is cached properly + CWalletTx& wtx = mapWallet[wtxNew.GetHash()]; + if (fBroadcastTransactions) { // Broadcast - if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { + if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. } else { - wtxNew.RelayWalletTransaction(connman); + wtx.RelayWalletTransaction(connman); } } } @@ -3611,9 +3705,9 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c // find first block that affects those keys, if there are any left std::vector<CKeyID> vAffected; - for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { + for (const auto& entry : mapWallet) { // iterate over all wallet transactions... - const CWalletTx &wtx = (*it).second; + const CWalletTx &wtx = entry.second; BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block @@ -3633,8 +3727,8 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c } // Extract block timestamps for those keys - for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) - mapKeyBirth[it->first] = it->second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off + for (const auto& entry : mapKeyFirstBlock) + mapKeyBirth[entry.first] = entry.second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off } /** @@ -3763,7 +3857,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) uiInterface.InitMessage(_("Zapping all transactions from wallet...")); std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile)); - std::unique_ptr<CWallet> tempWallet(new CWallet(std::move(dbw))); + std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(std::move(dbw)); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); @@ -3827,6 +3921,10 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (fFirstRun) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key + if (!gArgs.GetBoolArg("-usehd", true)) { + InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); + return nullptr; + } walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); // generate a new master key @@ -3837,15 +3935,15 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) // Top up the keypool if (!walletInstance->TopUpKeyPool()) { InitError(_("Unable to generate initial keys") += "\n"); - return NULL; + return nullptr; } walletInstance->SetBestChain(chainActive.GetLocator()); } else if (gArgs.IsArgSet("-usehd")) { - bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + bool useHD = gArgs.GetBoolArg("-usehd", true); if (walletInstance->IsHDEnabled() && !useHD) { - InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet or create new non-HD wallets."), walletFile)); + InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); return nullptr; } if (!walletInstance->IsHDEnabled() && useHD) { @@ -3856,8 +3954,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); - RegisterValidationInterface(walletInstance); - // Try to top up keypool. No-op if the wallet is locked. walletInstance->TopUpKeyPool(); @@ -3869,6 +3965,10 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (walletdb.ReadBestBlock(locator)) pindexRescan = FindForkInGlobalIndex(chainActive, locator); } + + walletInstance->m_last_block_processed = chainActive.Tip(); + RegisterValidationInterface(walletInstance); + if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { //We can't rescan beyond non-pruned blocks, stop and throw an error @@ -3896,7 +3996,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) } nStart = GetTimeMillis(); - walletInstance->ScanForWalletTransactions(pindexRescan, true); + walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); walletInstance->dbw->IncrementUpdateCounter(); @@ -4012,7 +4112,15 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) +bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - return ::AcceptToMemoryPool(mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee); + // We must set fInMempool here - while it will be re-set to true by the + // entered-mempool callback, if we did not there would be a race where a + // user could call sendmoney in a loop and hit spurious out of funds errors + // because we think that the transaction they just generated's change is + // unavailable as we're not yet aware its in mempool. + bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee); + fInMempool = ret; + return ret; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 542e9bd5c1..1bd0be7bd0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -6,18 +6,18 @@ #ifndef BITCOIN_WALLET_WALLET_H #define BITCOIN_WALLET_WALLET_H -#include "amount.h" -#include "policy/feerate.h" -#include "streams.h" -#include "tinyformat.h" -#include "ui_interface.h" -#include "utilstrencodings.h" -#include "validationinterface.h" -#include "script/ismine.h" -#include "script/sign.h" -#include "wallet/crypter.h" -#include "wallet/walletdb.h" -#include "wallet/rpcwallet.h" +#include <amount.h> +#include <policy/feerate.h> +#include <streams.h> +#include <tinyformat.h> +#include <ui_interface.h> +#include <utilstrencodings.h> +#include <validationinterface.h> +#include <script/ismine.h> +#include <script/sign.h> +#include <wallet/crypter.h> +#include <wallet/walletdb.h> +#include <wallet/rpcwallet.h> #include <algorithm> #include <atomic> @@ -65,8 +65,6 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6; static const bool DEFAULT_WALLET_RBF = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; -//! if set, all keys will be derived by using BIP32 -static const bool DEFAULT_USE_HD_WALLET = true; extern const char * DEFAULT_WALLET_DAT; @@ -216,10 +214,6 @@ public: Init(); } - /** Helper conversion operator to allow passing CMerkleTx where CTransaction is expected. - * TODO: adapt callers and remove this operator. */ - operator const CTransaction&() const { return *tx; } - void Init() { hashBlock = uint256(); @@ -254,8 +248,6 @@ public: int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } int GetBlocksToMaturity() const; - /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ - bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } void setAbandoned() { hashBlock = ABANDON_HASH; } @@ -332,6 +324,7 @@ public: mutable bool fImmatureWatchCreditCached; mutable bool fAvailableWatchCreditCached; mutable bool fChangeCached; + mutable bool fInMempool; mutable CAmount nDebitCached; mutable CAmount nCreditCached; mutable CAmount nImmatureCreditCached; @@ -371,6 +364,7 @@ public: fImmatureWatchCreditCached = false; fAvailableWatchCreditCached = false; fChangeCached = false; + fInMempool = false; nDebitCached = 0; nCreditCached = 0; nImmatureCreditCached = 0; @@ -475,6 +469,9 @@ public: // RelayWalletTransaction may only be called if fBroadcastTransactions! bool RelayWalletTransaction(CConnman* connman); + /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ + bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); + std::set<uint256> GetConflicts() const; }; @@ -724,6 +721,18 @@ private: std::unique_ptr<CWalletDBWrapper> dbw; + /** + * The following is used to keep track of how far behind the wallet is + * from the chain sync, and to allow clients to block on us being caught up. + * + * Note that this is *not* how far we've processed, we may need some rescan + * to have seen all transactions in the chain, but is only used to track + * live BlockConnected callbacks. + * + * Protected by cs_main (see BlockUntilSyncedToCurrentChain) + */ + const CBlockIndex* m_last_block_processed; + public: /* * Main wallet lock. @@ -921,7 +930,8 @@ public: void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override; bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); int64_t RescanFromTime(int64_t startTime, bool update); - CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate = false); + void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; // ResendWalletTransactionsBefore may only be called if fBroadcastTransactions! @@ -1108,6 +1118,14 @@ public: caller must ensure the current wallet version is correct before calling this function). */ bool SetHDMasterKey(const CPubKey& key); + + /** + * Blocks until the wallet state is up-to-date to /at least/ the current + * chain at the time this function is entered + * Obviously holding cs_main/cs_wallet when going into this call may cause + * deadlock + */ + void BlockUntilSyncedToCurrentChain(); }; /** A key allocated from the key pool. */ diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 52370a8eb5..5116d6419e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -3,18 +3,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet/walletdb.h" - -#include "base58.h" -#include "consensus/tx_verify.h" -#include "consensus/validation.h" -#include "fs.h" -#include "protocol.h" -#include "serialize.h" -#include "sync.h" -#include "util.h" -#include "utiltime.h" -#include "wallet/wallet.h" +#include <wallet/walletdb.h> + +#include <base58.h> +#include <consensus/tx_verify.h> +#include <consensus/validation.h> +#include <fs.h> +#include <protocol.h> +#include <serialize.h> +#include <sync.h> +#include <util.h> +#include <utiltime.h> +#include <wallet/wallet.h> #include <atomic> @@ -41,9 +41,9 @@ bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& s return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose); } -bool CWalletDB::ErasePurpose(const std::string& strPurpose) +bool CWalletDB::ErasePurpose(const std::string& strAddress) { - return EraseIC(std::make_pair(std::string("purpose"), strPurpose)); + return EraseIC(std::make_pair(std::string("purpose"), strAddress)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) @@ -268,7 +268,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletTx wtx; ssValue >> wtx; CValidationState state; - if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())) + if (!(CheckTransaction(*wtx.tx, state) && (wtx.GetHash() == hash) && state.IsValid())) return false; // Undo serialize changes in 31600 @@ -814,14 +814,14 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa return true; } -bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr) +bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr) { - return CDB::VerifyEnvironment(walletFile, dataDir, errorStr); + return CDB::VerifyEnvironment(walletFile, walletDir, errorStr); } -bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr) +bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr) { - return CDB::VerifyDatabaseFile(walletFile, dataDir, warningStr, errorStr, CWalletDB::Recover); + return CDB::VerifyDatabaseFile(walletFile, walletDir, warningStr, errorStr, CWalletDB::Recover); } bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 4f8ea185d5..e815bcfeda 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -6,10 +6,10 @@ #ifndef BITCOIN_WALLET_WALLETDB_H #define BITCOIN_WALLET_WALLETDB_H -#include "amount.h" -#include "primitives/transaction.h" -#include "wallet/db.h" -#include "key.h" +#include <amount.h> +#include <primitives/transaction.h> +#include <wallet/db.h> +#include <key.h> #include <list> #include <stdint.h> @@ -167,6 +167,8 @@ public: m_dbw(dbw) { } + CWalletDB(const CWalletDB&) = delete; + CWalletDB& operator=(const CWalletDB&) = delete; bool WriteName(const std::string& strAddress, const std::string& strName); bool EraseName(const std::string& strAddress); @@ -224,9 +226,9 @@ public: /* Function to determine if a certain KV/key-type is a key (cryptographical key) type */ static bool IsKeyType(const std::string& strType); /* verifies the database environment */ - static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr); + static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr); + static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr); //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain); @@ -244,9 +246,6 @@ public: private: CDB batch; CWalletDBWrapper& m_dbw; - - CWalletDB(const CWalletDB&); - void operator=(const CWalletDB&); }; //! Compacts BDB state so that wallet.dat is self-contained (if there are changes) diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp new file mode 100644 index 0000000000..fbb5215a51 --- /dev/null +++ b/src/wallet/walletutil.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2017 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 "wallet/walletutil.h" + +fs::path GetWalletDir() +{ + fs::path path; + + if (gArgs.IsArgSet("-walletdir")) { + path = fs::system_complete(gArgs.GetArg("-walletdir", "")); + if (!fs::is_directory(path)) { + // If the path specified doesn't exist, we return the deliberately + // invalid empty string. + path = ""; + } + } else { + path = GetDataDir(); + // If a wallets directory exists, use that, otherwise default to GetDataDir + if (fs::is_directory(path / "wallets")) { + path /= "wallets"; + } + } + + return path; +} diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h new file mode 100644 index 0000000000..a94f286a44 --- /dev/null +++ b/src/wallet/walletutil.h @@ -0,0 +1,13 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_UTIL_H +#define BITCOIN_WALLET_UTIL_H + +#include "util.h" + +//! Get the path of the wallet directory. +fs::path GetWalletDir(); + +#endif // BITCOIN_WALLET_UTIL_H diff --git a/src/warnings.cpp b/src/warnings.cpp index d4e33b701a..c52a1fdb47 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -3,10 +3,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "sync.h" -#include "clientversion.h" -#include "util.h" -#include "warnings.h" +#include <sync.h> +#include <clientversion.h> +#include <util.h> +#include <warnings.h> CCriticalSection cs_warnings; std::string strMiscWarning; diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp index 9f5cb3ba67..ce5c753eb4 100644 --- a/src/zmq/zmqabstractnotifier.cpp +++ b/src/zmq/zmqabstractnotifier.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "zmqabstractnotifier.h" -#include "util.h" +#include <zmq/zmqabstractnotifier.h> +#include <util.h> CZMQAbstractNotifier::~CZMQAbstractNotifier() diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h index 7828822149..6fbaba2036 100644 --- a/src/zmq/zmqabstractnotifier.h +++ b/src/zmq/zmqabstractnotifier.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H #define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H -#include "zmqconfig.h" +#include <zmq/zmqconfig.h> class CBlockIndex; class CZMQAbstractNotifier; diff --git a/src/zmq/zmqconfig.h b/src/zmq/zmqconfig.h index 610d7fbda4..ce8f3e13db 100644 --- a/src/zmq/zmqconfig.h +++ b/src/zmq/zmqconfig.h @@ -6,7 +6,7 @@ #define BITCOIN_ZMQ_ZMQCONFIG_H #if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" +#include <config/bitcoin-config.h> #endif #include <stdarg.h> @@ -16,8 +16,8 @@ #include <zmq.h> #endif -#include "primitives/block.h" -#include "primitives/transaction.h" +#include <primitives/block.h> +#include <primitives/transaction.h> void zmqError(const char *str); diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index 9909395d84..b663615752 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "zmqnotificationinterface.h" -#include "zmqpublishnotifier.h" +#include <zmq/zmqnotificationinterface.h> +#include <zmq/zmqpublishnotifier.h> -#include "version.h" -#include "validation.h" -#include "streams.h" -#include "util.h" +#include <version.h> +#include <validation.h> +#include <streams.h> +#include <util.h> void zmqError(const char *str) { @@ -40,15 +40,15 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create() factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>; factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>; - for (std::map<std::string, CZMQNotifierFactory>::const_iterator i=factories.begin(); i!=factories.end(); ++i) + for (const auto& entry : factories) { - std::string arg("-zmq" + i->first); + std::string arg("-zmq" + entry.first); if (gArgs.IsArgSet(arg)) { - CZMQNotifierFactory factory = i->second; + CZMQNotifierFactory factory = entry.second; std::string address = gArgs.GetArg(arg, ""); CZMQAbstractNotifier *notifier = factory(); - notifier->SetType(i->first); + notifier->SetType(entry.first); notifier->SetAddress(address); notifiers.push_back(notifier); } diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h index cb92216fa4..222d31e2ad 100644 --- a/src/zmq/zmqnotificationinterface.h +++ b/src/zmq/zmqnotificationinterface.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H #define BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H -#include "validationinterface.h" +#include <validationinterface.h> #include <string> #include <map> #include <list> diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index ab54e2bb8b..acccb896c0 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -2,13 +2,13 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "chain.h" -#include "chainparams.h" -#include "streams.h" -#include "zmqpublishnotifier.h" -#include "validation.h" -#include "util.h" -#include "rpc/server.h" +#include <chain.h> +#include <chainparams.h> +#include <streams.h> +#include <zmq/zmqpublishnotifier.h> +#include <validation.h> +#include <util.h> +#include <rpc/server.h> static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers; diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h index 1790fe5698..0e9bb23e5b 100644 --- a/src/zmq/zmqpublishnotifier.h +++ b/src/zmq/zmqpublishnotifier.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H #define BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H -#include "zmqabstractnotifier.h" +#include <zmq/zmqabstractnotifier.h> class CBlockIndex; |