diff options
Diffstat (limited to 'src')
66 files changed, 839 insertions, 213 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index ac822d6c5e..8de7f7ff7a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,8 +4,8 @@ DIST_SUBDIRS = secp256k1 univalue -AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) -AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) +AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) +AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 13c27299f8..748c5b7887 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -27,7 +27,7 @@ bench_bench_bitcoin_SOURCES = \ bench/lockedpool.cpp \ bench/perf.cpp \ bench/perf.h \ - bench/prevector_destructor.cpp + bench/prevector.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) diff --git a/src/addrman.cpp b/src/addrman.cpp index 9eefffb45b..e811dd4bea 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -187,7 +187,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId) info.fInTried = true; } -void CAddrMan::Good_(const CService& addr, int64_t nTime) +void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime) { int nId; @@ -233,10 +233,22 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime) if (nUBucket == -1) return; - LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString()); + // which tried bucket to move the entry to + int tried_bucket = info.GetTriedBucket(nKey); + int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket); + + // Will moving this address into tried evict another entry? + if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) { + LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table, moving %s to m_tried_collisions=%d\n", addr.ToString(), m_tried_collisions.size()); + if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) { + m_tried_collisions.insert(nId); + } + } else { + LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString()); - // move nId to the tried tables - MakeTried(info, nId); + // move nId to the tried tables + MakeTried(info, nId); + } } bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) @@ -521,3 +533,82 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices) int CAddrMan::RandomInt(int nMax){ return GetRandInt(nMax); } + +void CAddrMan::ResolveCollisions_() +{ + for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) { + int id_new = *it; + + bool erase_collision = false; + + // If id_new not found in mapInfo remove it from m_tried_collisions + if (mapInfo.count(id_new) != 1) { + erase_collision = true; + } else { + CAddrInfo& info_new = mapInfo[id_new]; + + // Which tried bucket to move the entry to. + int tried_bucket = info_new.GetTriedBucket(nKey); + int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket); + if (!info_new.IsValid()) { // id_new may no longer map to a valid address + erase_collision = true; + } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty + + // Get the to-be-evicted address that is being tested + int id_old = vvTried[tried_bucket][tried_bucket_pos]; + CAddrInfo& info_old = mapInfo[id_old]; + + // Has successfully connected in last X hours + if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { + erase_collision = true; + } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours + + // Give address at least 60 seconds to successfully connect + if (GetAdjustedTime() - info_old.nLastTry > 60) { + LogPrint(BCLog::ADDRMAN, "Swapping %s for %s in tried table\n", info_new.ToString(), info_old.ToString()); + + // Replaces an existing address already in the tried table with the new address + Good_(info_new, false, GetAdjustedTime()); + erase_collision = true; + } + } + } else { // Collision is not actually a collision anymore + Good_(info_new, false, GetAdjustedTime()); + erase_collision = true; + } + } + + if (erase_collision) { + m_tried_collisions.erase(it++); + } else { + it++; + } + } +} + +CAddrInfo CAddrMan::SelectTriedCollision_() +{ + if (m_tried_collisions.size() == 0) return CAddrInfo(); + + std::set<int>::iterator it = m_tried_collisions.begin(); + + // Selects a random element from m_tried_collisions + std::advance(it, GetRandInt(m_tried_collisions.size())); + int id_new = *it; + + // If id_new not found in mapInfo remove it from m_tried_collisions + if (mapInfo.count(id_new) != 1) { + m_tried_collisions.erase(it); + return CAddrInfo(); + } + + CAddrInfo& newInfo = mapInfo[id_new]; + + // which tried bucket to move the entry to + int tried_bucket = newInfo.GetTriedBucket(nKey); + int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket); + + int id_old = vvTried[tried_bucket][tried_bucket_pos]; + + return mapInfo[id_old]; +} diff --git a/src/addrman.h b/src/addrman.h index 38da754afb..67423c6c55 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -165,6 +165,9 @@ public: //! ... in at least this many days #define ADDRMAN_MIN_FAIL_DAYS 7 +//! how recent a successful connection should be before we allow an address to be evicted from tried +#define ADDRMAN_REPLACEMENT_HOURS 4 + //! the maximum percentage of nodes to return in a getaddr call #define ADDRMAN_GETADDR_MAX_PCT 23 @@ -176,6 +179,9 @@ public: #define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2) #define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2) +//! the maximum number of tried addr collisions to store +#define ADDRMAN_SET_TRIED_COLLISION_SIZE 10 + /** * Stochastical (IP) address manager */ @@ -212,6 +218,9 @@ private: //! last time Good was called (memory only) int64_t nLastGood; + //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discpline used to resolve these collisions. + std::set<int> m_tried_collisions; + protected: //! secret key to randomize bucket select with uint256 nKey; @@ -239,7 +248,7 @@ protected: void ClearNew(int nUBucket, int nUBucketPos); //! Mark an entry "good", possibly moving it from "new" to "tried". - void Good_(const CService &addr, int64_t nTime); + void Good_(const CService &addr, bool test_before_evict, int64_t time); //! Add an entry to the "new" table. bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); @@ -250,6 +259,12 @@ protected: //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. CAddrInfo Select_(bool newOnly); + //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. + void ResolveCollisions_(); + + //! Return a random to-be-evicted tried table address. + CAddrInfo SelectTriedCollision_(); + //! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic. virtual int RandomInt(int nMax); @@ -537,11 +552,11 @@ public: } //! Mark an entry as accessible. - void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime()) { LOCK(cs); Check(); - Good_(addr, nTime); + Good_(addr, test_before_evict, nTime); Check(); } @@ -554,6 +569,28 @@ public: Check(); } + //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. + void ResolveCollisions() + { + LOCK(cs); + Check(); + ResolveCollisions_(); + Check(); + } + + //! Randomly select an address in tried that another address is attempting to evict. + CAddrInfo SelectTriedCollision() + { + CAddrInfo ret; + { + LOCK(cs); + Check(); + ret = SelectTriedCollision_(); + Check(); + } + return ret; + } + /** * Choose an address to connect to. */ diff --git a/src/arith_uint256.h b/src/arith_uint256.h index dc2627592e..3f4cc8c2bf 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -85,7 +85,7 @@ public: base_uint ret; for (int i = 0; i < WIDTH; i++) ret.pn[i] = ~pn[i]; - ret++; + ++ret; return ret; } diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 06d2abeac6..6f438b60e9 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -37,11 +37,6 @@ static void CoinSelection(benchmark::State& state) LOCK(wallet.cs_wallet); while (state.KeepRunning()) { - // Empty wallet. - for (COutput output : vCoins) - delete output.tx; - vCoins.clear(); - // Add coins. for (int i = 0; i < 1000; i++) addCoin(1000 * COIN, wallet, vCoins); @@ -53,6 +48,12 @@ static void CoinSelection(benchmark::State& state) assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); + + // Empty wallet. + for (COutput& output : vCoins) { + delete output.tx; + } + vCoins.clear(); } } diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp new file mode 100644 index 0000000000..d0f28d1a3e --- /dev/null +++ b/src/bench/prevector.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2015-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 <compat.h> +#include <prevector.h> + +#include <bench/bench.h> + +struct nontrivial_t { + int x; + nontrivial_t() :x(-1) {} +}; +static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value, + "expected nontrivial_t to not be trivially constructible"); + +typedef unsigned char trivial_t; +static_assert(IS_TRIVIALLY_CONSTRUCTIBLE<trivial_t>::value, + "expected trivial_t to be trivially constructible"); + +template <typename T> +static void PrevectorDestructor(benchmark::State& state) +{ + while (state.KeepRunning()) { + for (auto x = 0; x < 1000; ++x) { + prevector<28, T> t0; + prevector<28, T> t1; + t0.resize(28); + t1.resize(29); + } + } +} + +template <typename T> +static void PrevectorClear(benchmark::State& state) +{ + + while (state.KeepRunning()) { + for (auto x = 0; x < 1000; ++x) { + prevector<28, T> t0; + prevector<28, T> t1; + t0.resize(28); + t0.clear(); + t1.resize(29); + t0.clear(); + } + } +} + +template <typename T> +void PrevectorResize(benchmark::State& state) +{ + while (state.KeepRunning()) { + prevector<28, T> t0; + prevector<28, T> t1; + for (auto x = 0; x < 1000; ++x) { + t0.resize(28); + t0.resize(0); + t1.resize(29); + t1.resize(0); + } + } +} + +#define PREVECTOR_TEST(name, nontrivops, trivops) \ + static void Prevector ## name ## Nontrivial(benchmark::State& state) { \ + PrevectorResize<nontrivial_t>(state); \ + } \ + BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \ + static void Prevector ## name ## Trivial(benchmark::State& state) { \ + PrevectorResize<trivial_t>(state); \ + } \ + BENCHMARK(Prevector ## name ## Trivial, trivops); + +PREVECTOR_TEST(Clear, 28300, 88600) +PREVECTOR_TEST(Destructor, 28800, 88900) +PREVECTOR_TEST(Resize, 28900, 90300) diff --git a/src/bench/prevector_destructor.cpp b/src/bench/prevector_destructor.cpp deleted file mode 100644 index 39d0ee5eb1..0000000000 --- a/src/bench/prevector_destructor.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2015-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 <bench/bench.h> -#include <prevector.h> - -static void PrevectorDestructor(benchmark::State& state) -{ - while (state.KeepRunning()) { - for (auto x = 0; x < 1000; ++x) { - prevector<28, unsigned char> t0; - prevector<28, unsigned char> t1; - t0.resize(28); - t1.resize(29); - } - } -} - -static void PrevectorClear(benchmark::State& state) -{ - - while (state.KeepRunning()) { - for (auto x = 0; x < 1000; ++x) { - prevector<28, unsigned char> t0; - prevector<28, unsigned char> t1; - t0.resize(28); - t0.clear(); - t1.resize(29); - t0.clear(); - } - } -} - -BENCHMARK(PrevectorDestructor, 5700); -BENCHMARK(PrevectorClear, 5600); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6eb223171f..c2b3480f9d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -175,6 +175,9 @@ public: // (the tx=... number in the SetBestChain debug.log lines) 3.5 // * estimated number of transactions per second after that timestamp }; + + /* disable fallback fee on mainnet */ + m_fallback_fee_enabled = false; } }; @@ -266,6 +269,8 @@ public: 0.09 }; + /* enable fallback fee on testnet */ + m_fallback_fee_enabled = true; } }; @@ -343,6 +348,9 @@ public: base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; bech32_hrp = "bcrt"; + + /* enable fallback fee on regtest */ + m_fallback_fee_enabled = true; } }; diff --git a/src/chainparams.h b/src/chainparams.h index d478da9891..6b1f813afb 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -65,6 +65,8 @@ public: bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } /** Return the BIP70 network string (main, test or regtest) */ std::string NetworkIDString() const { return strNetworkID; } + /** Return true if the fallback fee is by default enabled for this network */ + bool IsFallbackFeeEnabled() const { return m_fallback_fee_enabled; } /** Return the list of hostnames to look up for DNS seeds */ const std::vector<std::string>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } @@ -91,6 +93,7 @@ protected: bool fMineBlocksOnDemand; CCheckpointData checkpointData; ChainTxData chainTxData; + bool m_fallback_fee_enabled; }; /** diff --git a/src/compat.h b/src/compat.h index aae84b1181..8a0f901304 100644 --- a/src/compat.h +++ b/src/compat.h @@ -10,6 +10,16 @@ #include <config/bitcoin-config.h> #endif +#include <type_traits> + +// GCC 4.8 is missing some C++11 type_traits, +// https://www.gnu.org/software/gcc/gcc-5/changes.html +#if defined(__GNUC__) && __GNUC__ < 5 +#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivial +#else +#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_constructible +#endif + #ifdef WIN32 #ifdef _WIN32_WINNT #undef _WIN32_WINNT diff --git a/src/crypto/common.h b/src/crypto/common.h index 825b430978..6e9d6dc82a 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -82,12 +82,12 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x) /** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ uint64_t static inline CountBits(uint64_t x) { -#ifdef HAVE_DECL___BUILTIN_CLZL +#if HAVE_DECL___BUILTIN_CLZL if (sizeof(unsigned long) >= sizeof(uint64_t)) { return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; } #endif -#ifdef HAVE_DECL___BUILTIN_CLZLL +#if HAVE_DECL___BUILTIN_CLZLL if (sizeof(unsigned long long) >= sizeof(uint64_t)) { return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; } diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 4e1e403f69..6cac625abc 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -89,6 +89,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) } CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate) + : m_name(fs::basename(path)) { penv = nullptr; readoptions.verify_checksums = true; @@ -155,11 +156,30 @@ CDBWrapper::~CDBWrapper() bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) { + const bool log_memory = LogAcceptCategory(BCLog::LEVELDB); + double mem_before = 0; + if (log_memory) { + mem_before = DynamicMemoryUsage() / 1024 / 1024; + } leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); dbwrapper_private::HandleError(status); + if (log_memory) { + double mem_after = DynamicMemoryUsage() / 1024 / 1024; + LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n", + m_name, mem_before, mem_after); + } return true; } +size_t CDBWrapper::DynamicMemoryUsage() const { + std::string memory; + if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory)) { + LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n"); + return 0; + } + return stoul(memory); +} + // Prefixed with null character to avoid collisions with other keys // // We must use a string constructor which specifies length so that we copy diff --git a/src/dbwrapper.h b/src/dbwrapper.h index a29938ce33..6f80eedc7a 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -198,6 +198,9 @@ private: //! the database itself leveldb::DB* pdb; + //! the name of this database + std::string m_name; + //! a key used for optional XOR-obfuscation of the database std::vector<unsigned char> obfuscate_key; @@ -284,6 +287,9 @@ public: bool WriteBatch(CDBBatch& batch, bool fSync = false); + // Get an estimate of LevelDB memory usage (in bytes). + size_t DynamicMemoryUsage() const; + // not available for LevelDB; provide for compatibility with BDB bool Flush() { diff --git a/src/init.cpp b/src/init.cpp index 5c5d1ee792..f763c3a435 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -448,6 +448,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)"); + strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost"); } strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + "."); @@ -490,7 +491,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); strUsage += HelpMessageGroup(_("Block creation options:")); - strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight")); + if (showDebug) + strUsage += HelpMessageOpt("-blockmaxsize=<n>", "Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight"); strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); if (showDebug) @@ -1081,7 +1083,7 @@ bool AppInitParameterInteraction() if (gArgs.IsArgSet("-dustrelayfee")) { CAmount n = 0; - if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n) + if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n)) return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); dustRelayFee = CFeeRate(n); } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index f43ad76794..3bb58e560a 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -414,7 +414,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, status.ok()) { if (record.size() < 12) { reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + record.size(), Status::Corruption("log record too small", fname)); continue; } WriteBatchInternal::SetContents(&batch, record); diff --git a/src/leveldb/db/leveldbutil.cc b/src/leveldb/db/leveldbutil.cc index 9f4b7dd70c..d06d64d640 100644 --- a/src/leveldb/db/leveldbutil.cc +++ b/src/leveldb/db/leveldbutil.cc @@ -19,6 +19,7 @@ class StdoutPrinter : public WritableFile { virtual Status Close() { return Status::OK(); } virtual Status Flush() { return Status::OK(); } virtual Status Sync() { return Status::OK(); } + virtual std::string GetName() const { return "[stdout]"; } }; bool HandleDumpCommand(Env* env, char** files, int num) { diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc index a6d304545d..8b6ad136d7 100644 --- a/src/leveldb/db/log_reader.cc +++ b/src/leveldb/db/log_reader.cc @@ -186,7 +186,7 @@ uint64_t Reader::LastRecordOffset() { } void Reader::ReportCorruption(uint64_t bytes, const char* reason) { - ReportDrop(bytes, Status::Corruption(reason)); + ReportDrop(bytes, Status::Corruption(reason, file_->GetName())); } void Reader::ReportDrop(uint64_t bytes, const Status& reason) { diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc index 4cd4bb047f..7281e3d345 100644 --- a/src/leveldb/db/repair.cc +++ b/src/leveldb/db/repair.cc @@ -203,7 +203,7 @@ class Repairer { while (reader.ReadRecord(&record, &scratch)) { if (record.size() < 12) { reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + record.size(), Status::Corruption("log record too small", logname)); continue; } WriteBatchInternal::SetContents(&batch, record); diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc index 9a98884daf..68c0614a59 100644 --- a/src/leveldb/helpers/memenv/memenv.cc +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -176,6 +176,7 @@ class SequentialFileImpl : public SequentialFile { return Status::OK(); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; uint64_t pos_; @@ -196,6 +197,7 @@ class RandomAccessFileImpl : public RandomAccessFile { return file_->Read(offset, n, result, scratch); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; }; @@ -218,6 +220,7 @@ class WritableFileImpl : public WritableFile { virtual Status Flush() { return Status::OK(); } virtual Status Sync() { return Status::OK(); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; }; diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h index 99b6c21414..275d441eae 100644 --- a/src/leveldb/include/leveldb/env.h +++ b/src/leveldb/include/leveldb/env.h @@ -191,6 +191,9 @@ class SequentialFile { // REQUIRES: External synchronization virtual Status Skip(uint64_t n) = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed SequentialFile(const SequentialFile&); @@ -215,6 +218,9 @@ class RandomAccessFile { virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed RandomAccessFile(const RandomAccessFile&); @@ -234,6 +240,9 @@ class WritableFile { virtual Status Flush() = 0; virtual Status Sync() = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed WritableFile(const WritableFile&); diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc index 24e4e02445..285e1c0de3 100644 --- a/src/leveldb/table/format.cc +++ b/src/leveldb/table/format.cc @@ -82,7 +82,7 @@ Status ReadBlock(RandomAccessFile* file, } if (contents.size() != n + kBlockTrailerSize) { delete[] buf; - return Status::Corruption("truncated block read"); + return Status::Corruption("truncated block read", file->GetName()); } // Check the crc of the type and the block contents @@ -92,7 +92,7 @@ Status ReadBlock(RandomAccessFile* file, const uint32_t actual = crc32c::Value(data, n + 1); if (actual != crc) { delete[] buf; - s = Status::Corruption("block checksum mismatch"); + s = Status::Corruption("block checksum mismatch", file->GetName()); return s; } } @@ -119,13 +119,13 @@ Status ReadBlock(RandomAccessFile* file, size_t ulength = 0; if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { delete[] buf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } char* ubuf = new char[ulength]; if (!port::Snappy_Uncompress(data, n, ubuf)) { delete[] buf; delete[] ubuf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } delete[] buf; result->data = Slice(ubuf, ulength); @@ -135,7 +135,7 @@ Status ReadBlock(RandomAccessFile* file, } default: delete[] buf; - return Status::Corruption("bad block type"); + return Status::Corruption("bad block type", file->GetName()); } return Status::OK(); diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index dd852af354..4676bc2240 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -121,6 +121,8 @@ class PosixSequentialFile: public SequentialFile { } return Status::OK(); } + + virtual std::string GetName() const { return filename_; } }; // pread() based random-access @@ -172,6 +174,8 @@ class PosixRandomAccessFile: public RandomAccessFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; // mmap() based random-access @@ -206,6 +210,8 @@ class PosixMmapReadableFile: public RandomAccessFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; class PosixWritableFile : public WritableFile { @@ -287,6 +293,8 @@ class PosixWritableFile : public WritableFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; static int LockOrUnlock(int fd, bool lock) { diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc index d32c4e676c..81380216bb 100644 --- a/src/leveldb/util/env_win.cc +++ b/src/leveldb/util/env_win.cc @@ -78,6 +78,7 @@ public: virtual Status Read(size_t n, Slice* result, char* scratch); virtual Status Skip(uint64_t n); BOOL isEnable(); + virtual std::string GetName() const { return _filename; } private: BOOL _Init(); void _CleanUp(); @@ -94,6 +95,7 @@ public: virtual ~Win32RandomAccessFile(); virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; BOOL isEnable(); + virtual std::string GetName() const { return _filename; } private: BOOL _Init(LPCWSTR path); void _CleanUp(); @@ -114,6 +116,7 @@ public: virtual Status Flush(); virtual Status Sync(); BOOL isEnable(); + virtual std::string GetName() const { return filename_; } private: std::string filename_; ::HANDLE _hFile; diff --git a/src/miner.cpp b/src/miner.cpp index dda52790c6..fcb376c6cb 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -30,12 +30,6 @@ #include <queue> #include <utility> -////////////////////////////////////////////////////////////////////////////// -// -// BitcoinMiner -// - -// // Unconfirmed transactions in the memory pool often depend on other // transactions in the memory pool. When we select transactions from the // pool, we select by highest fee rate of a transaction combined with all diff --git a/src/net.cpp b/src/net.cpp index 201914685c..f7e6c300b1 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -181,6 +181,10 @@ void AdvertiseLocal(CNode *pnode) if (fListen && pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); + if (gArgs.GetBoolArg("-addrmantest", false)) { + // use IPv4 loopback during addrmantest + addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices()); + } // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. @@ -189,7 +193,7 @@ void AdvertiseLocal(CNode *pnode) { addrLocal.SetIP(pnode->GetAddrLocal()); } - if (addrLocal.IsRoutable()) + if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false)) { LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); FastRandomContext insecure_rand; @@ -1824,11 +1828,18 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) } } + addrman.ResolveCollisions(); + int64_t nANow = GetAdjustedTime(); int nTries = 0; while (!interruptNet) { - CAddrInfo addr = addrman.Select(fFeeler); + CAddrInfo addr = addrman.SelectTriedCollision(); + + // SelectTriedCollision returns an invalid address if it is empty. + if (!fFeeler || !addr.IsValid()) { + addr = addrman.Select(fFeeler); + } // if we selected an invalid address, restart if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) @@ -2718,6 +2729,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn fOneShot = false; m_manual_connection = false; fClient = false; // set by version message + m_limited_node = false; // set by version message fFeeler = false; fSuccessfullyConnected = false; fDisconnect = false; @@ -641,6 +641,7 @@ public: bool fOneShot; bool m_manual_connection; bool fClient; + bool m_limited_node; //after BIP159 const bool fInbound; std::atomic_bool fSuccessfullyConnected; std::atomic_bool fDisconnect; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index bf9307727a..482a206c8b 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -401,6 +401,12 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } +/** + * When a peer sends us a valid block, instruct it to announce blocks to us + * using CMPCTBLOCK if possible by adding its nodeid to the end of + * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by + * removing the first element if necessary. + */ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) { AssertLockHeld(cs_main); CNodeState* nodestate = State(nodeid); @@ -749,7 +755,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) return nEvicted; } -// Requires cs_main. +/** + * Mark a misbehaving peer to be banned depending upon the value of `-banscore`. + * + * Requires cs_main. + */ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) { if (howmuch == 0) @@ -808,6 +818,10 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &schedu scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000); } +/** + * Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected + * block. Also save the time of the last tip update. + */ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) { LOCK(g_cs_orphans); @@ -828,7 +842,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb } } - // Erase orphan transactions include or precluded by this block + // Erase orphan transactions included or precluded by this block if (vOrphanErase.size()) { int nErased = 0; for (uint256 &orphanHash : vOrphanErase) { @@ -847,6 +861,10 @@ static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_bloc static uint256 most_recent_block_hash; static bool fWitnessesPresentInMostRecentCompactBlock; +/** + * Maintain state about the best-seen block and fast-announce a compact block + * to compatible peers. + */ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true); const CNetMsgMaker msgMaker(PROTOCOL_VERSION); @@ -888,10 +906,15 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: }); } +/** + * Update our best height and announce any block hashes which weren't previously + * in chainActive to our peers. + */ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; connman->SetBestHeight(nNewHeight); + SetServiceFlagsIBDCache(!fInitialDownload); if (!fInitialDownload) { // Find the hashes of all blocks that weren't previously in the best chain. std::vector<uint256> vHashes; @@ -919,6 +942,10 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB nTimeBestReceived = GetTime(); } +/** + * Handle invalid block rejection and consequent peer banning, maintain which + * peers announce compact blocks. + */ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) { LOCK(cs_main); @@ -1642,7 +1669,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->cleanSubVer = cleanSubVer; } pfrom->nStartingHeight = nStartingHeight; - pfrom->fClient = !(nServices & NODE_NETWORK); + + // set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients" + pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED)); + + // set nodes not capable of serving the complete blockchain history as "limited nodes" + pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED)); + { LOCK(pfrom->cs_filter); pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message @@ -1801,7 +1834,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // 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)) + if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices)) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) @@ -3611,7 +3644,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM // Message: getdata (blocks) // std::vector<CInv> vGetData; - if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { std::vector<const CBlockIndex*> vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams); diff --git a/src/net_processing.h b/src/net_processing.h index b534ef01c3..ff1ebc59da 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -42,13 +42,26 @@ private: public: explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler); + /** + * Overridden from CValidationInterface. + */ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override; + /** + * Overridden from CValidationInterface. + */ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; + /** + * Overridden from CValidationInterface. + */ void BlockChecked(const CBlock& block, const CValidationState& state) override; + /** + * Overridden from CValidationInterface. + */ void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; - + /** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */ void InitializeNode(CNode* pnode) override; + /** Handle removal of a peer by updating various state and removing it from mapNodeState */ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override; /** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; @@ -61,8 +74,11 @@ public: */ bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override; + /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ void ConsiderEviction(CNode *pto, int64_t time_in_seconds); + /** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */ void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams); + /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ void EvictExtraOutboundPeers(int64_t time_in_seconds); private: diff --git a/src/netbase.cpp b/src/netbase.cpp index 5be3fe34f8..3ea3141d5e 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -139,7 +139,7 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, if (pszName[0] == 0) return false; int port = portDefault; - std::string hostname = ""; + std::string hostname; SplitHostPort(std::string(pszName), port, hostname); std::vector<CNetAddr> vIP; diff --git a/src/prevector.h b/src/prevector.h index f8d6a09145..103ead82cc 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -10,9 +10,12 @@ #include <stdint.h> #include <string.h> +#include <cstddef> #include <iterator> #include <type_traits> +#include <compat.h> + #pragma pack(push, 1) /** Implements a drop-in replacement for std::vector<T> which stores up to N * elements directly (without heap allocation). The types Size and Diff are @@ -194,16 +197,42 @@ private: T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } + void fill(T* dst, ptrdiff_t count) { + if (IS_TRIVIALLY_CONSTRUCTIBLE<T>::value) { + // The most common use of prevector is where T=unsigned char. For + // trivially constructible types, we can use memset() to avoid + // looping. + ::memset(dst, 0, count * sizeof(T)); + } else { + for (auto i = 0; i < count; ++i) { + new(static_cast<void*>(dst + i)) T(); + } + } + } + + void fill(T* dst, ptrdiff_t count, const T& value) { + for (auto i = 0; i < count; ++i) { + new(static_cast<void*>(dst + i)) T(value); + } + } + + template<typename InputIterator> + void fill(T* dst, InputIterator first, InputIterator last) { + while (first != last) { + new(static_cast<void*>(dst)) T(*first); + ++dst; + ++first; + } + } + public: void assign(size_type n, const T& val) { clear(); if (capacity() < n) { change_capacity(n); } - while (size() < n) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(val); - } + _size += n; + fill(item_ptr(0), n, val); } template<typename InputIterator> @@ -213,11 +242,8 @@ public: if (capacity() < n) { change_capacity(n); } - while (first != last) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*first); - ++first; - } + _size += n; + fill(item_ptr(0), first, last); } prevector() : _size(0), _union{{}} {} @@ -228,31 +254,23 @@ public: explicit prevector(size_type n, const T& val = T()) : _size(0) { change_capacity(n); - while (size() < n) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(val); - } + _size += n; + fill(item_ptr(0), n, val); } template<typename InputIterator> prevector(InputIterator first, InputIterator last) : _size(0) { size_type n = last - first; change_capacity(n); - while (first != last) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*first); - ++first; - } + _size += n; + fill(item_ptr(0), first, last); } prevector(const prevector<N, T, Size, Diff>& other) : _size(0) { - change_capacity(other.size()); - const_iterator it = other.begin(); - while (it != other.end()) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*it); - ++it; - } + size_type n = other.size(); + change_capacity(n); + _size += n; + fill(item_ptr(0), other.begin(), other.end()); } prevector(prevector<N, T, Size, Diff>&& other) : _size(0) { @@ -263,14 +281,7 @@ public: if (&other == this) { return *this; } - resize(0); - change_capacity(other.size()); - const_iterator it = other.begin(); - while (it != other.end()) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*it); - ++it; - } + assign(other.begin(), other.end()); return *this; } @@ -314,16 +325,20 @@ public: } void resize(size_type new_size) { - if (size() > new_size) { + size_type cur_size = size(); + if (cur_size == new_size) { + return; + } + if (cur_size > new_size) { erase(item_ptr(new_size), end()); + return; } if (new_size > capacity()) { change_capacity(new_size); } - while (size() < new_size) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(); - } + ptrdiff_t increase = new_size - cur_size; + fill(item_ptr(cur_size), increase); + _size += increase; } void reserve(size_type new_capacity) { @@ -346,10 +361,11 @@ public: if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T)); + T* ptr = item_ptr(p); + memmove(ptr + 1, ptr, (size() - p) * sizeof(T)); _size++; - new(static_cast<void*>(item_ptr(p))) T(value); - return iterator(item_ptr(p)); + new(static_cast<void*>(ptr)) T(value); + return iterator(ptr); } void insert(iterator pos, size_type count, const T& value) { @@ -358,11 +374,10 @@ public: if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + T* ptr = item_ptr(p); + memmove(ptr + count, ptr, (size() - p) * sizeof(T)); _size += count; - for (size_type i = 0; i < count; i++) { - new(static_cast<void*>(item_ptr(p + i))) T(value); - } + fill(item_ptr(p), count, value); } template<typename InputIterator> @@ -373,13 +388,10 @@ public: if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + T* ptr = item_ptr(p); + memmove(ptr + count, ptr, (size() - p) * sizeof(T)); _size += count; - while (first != last) { - new(static_cast<void*>(item_ptr(p))) T(*first); - ++p; - ++first; - } + fill(ptr, first, last); } iterator erase(iterator pos) { diff --git a/src/protocol.cpp b/src/protocol.cpp index c412ad9ffe..2ec26fbd3e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -12,6 +12,8 @@ # include <arpa/inet.h> #endif +static std::atomic<bool> g_initial_block_download_completed(false); + namespace NetMsgType { const char *VERSION="version"; const char *VERACK="verack"; @@ -127,6 +129,17 @@ bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const } +ServiceFlags GetDesirableServiceFlags(ServiceFlags services) { + if ((services & NODE_NETWORK_LIMITED) && g_initial_block_download_completed) { + return ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS); + } + return ServiceFlags(NODE_NETWORK | NODE_WITNESS); +} + +void SetServiceFlagsIBDCache(bool state) { + g_initial_block_download_completed = state; +} + CAddress::CAddress() : CService() { diff --git a/src/protocol.h b/src/protocol.h index 42eb57e4f0..e518d11944 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -15,6 +15,7 @@ #include <uint256.h> #include <version.h> +#include <atomic> #include <stdint.h> #include <string> @@ -301,9 +302,10 @@ enum ServiceFlags : uint64_t { * If the NODE_NONE return value is changed, contrib/seeds/makeseeds.py * should be updated appropriately to filter for the same nodes. */ -static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) { - return ServiceFlags(NODE_NETWORK | NODE_WITNESS); -} +ServiceFlags GetDesirableServiceFlags(ServiceFlags services); + +/** Set the current IBD status in order to figure out the desirable service flags */ +void SetServiceFlagsIBDCache(bool status); /** * A shortcut for (services & GetDesirableServiceFlags(services)) @@ -316,10 +318,10 @@ static inline bool HasAllDesirableServiceFlags(ServiceFlags 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. + * robust address-storage DB. */ static inline bool MayHaveUsefulAddressDB(ServiceFlags services) { - return services & NODE_NETWORK; + return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED); } /** A CService with information about it as peer */ diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 06e1f1a37c..ab381bfb5d 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -29,6 +29,7 @@ #include <init.h> #include <rpc/server.h> #include <ui_interface.h> +#include <uint256.h> #include <util.h> #include <warnings.h> @@ -80,6 +81,7 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool*) Q_DECLARE_METATYPE(CAmount) +Q_DECLARE_METATYPE(uint256) static void InitMessage(const std::string &message) { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4e868b7c17..427eb95a84 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -923,6 +923,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned showNormalIfMinimized(); QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this); + mBox.setTextFormat(Qt::PlainText); int r = mBox.exec(); if (ret != nullptr) *ret = r == QMessageBox::Ok; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 3642e5d4d4..eaf2896bc3 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -138,9 +138,9 @@ size_t ClientModel::getMempoolDynamicUsage() const double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const { CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn); + LOCK(cs_main); if (!tip) { - LOCK(cs_main); tip = chainActive.Tip(); } return GuessVerificationProgress(Params().TxData(), tip); diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui index fdc52dc455..b5a69c578d 100644 --- a/src/qt/forms/modaloverlay.ui +++ b/src/qt/forms/modaloverlay.ui @@ -351,6 +351,12 @@ QLabel { color: rgb(40,40,40); }</string> <property name="text"> <string>Hide</string> </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="default"> + <bool>true</bool> + </property> </widget> </item> </layout> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 195a5560f7..6b31ddea90 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -848,7 +848,9 @@ <item> <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> + <string>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size. + +Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</string> </property> <property name="text"> <string>per kilobyte</string> diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 871822ccb4..50d51c527d 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -369,12 +369,19 @@ void SendCoinsDialog::on_sendButton_clicked() accept(); CoinControlDialog::coinControl()->UnSelectAll(); coinControlUpdateLabels(); + Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash()); } fNewRecipientAllowed = true; } void SendCoinsDialog::clear() { + // Clear coin control settings + CoinControlDialog::coinControl()->UnSelectAll(); + ui->checkBoxCoinControlChange->setChecked(false); + ui->lineEditCoinControlChange->clear(); + coinControlUpdateLabels(); + // Remove entries until only one left while(ui->entries->count()) { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 7c27785d12..48885bbcad 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -54,6 +54,9 @@ public Q_SLOTS: void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance); +Q_SIGNALS: + void coinsSent(const uint256& txid); + private: Ui::SendCoinsDialog *ui; ClientModel *clientModel; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index c1d28be0ab..6f30e56327 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -17,6 +17,7 @@ #include <util.h> #include <wallet/db.h> #include <wallet/wallet.h> +#include <policy/policy.h> #include <stdint.h> #include <string> @@ -241,6 +242,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>"; strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>"; + strHTML += "<b>" + tr("Transaction virtual size") + ":</b> " + QString::number(GetVirtualTransactionSize(*wtx.tx)) + " bytes<br>"; strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>"; // Message from normal bitcoin:URI (bitcoin:123...?message=example) diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 88f8f463bc..006f9fe443 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -263,8 +263,7 @@ void TransactionView::setModel(WalletModel *_model) void TransactionView::chooseDate(int idx) { - if(!transactionProxyModel) - return; + if (!transactionProxyModel) return; QDate current = QDate::currentDate(); dateRangeWidget->setVisible(false); switch(dateWidget->itemData(idx).toInt()) @@ -592,6 +591,32 @@ void TransactionView::focusTransaction(const QModelIndex &idx) transactionView->setFocus(); } +void TransactionView::focusTransaction(const uint256& txid) +{ + if (!transactionProxyModel) + return; + + const QModelIndexList results = this->model->getTransactionTableModel()->match( + this->model->getTransactionTableModel()->index(0,0), + TransactionTableModel::TxHashRole, + QString::fromStdString(txid.ToString()), -1); + + transactionView->setFocus(); + transactionView->selectionModel()->clearSelection(); + for (const QModelIndex& index : results) { + const QModelIndex targetIndex = transactionProxyModel->mapFromSource(index); + transactionView->selectionModel()->select( + targetIndex, + QItemSelectionModel::Rows | QItemSelectionModel::Select); + // Called once per destination to ensure all results are in view, unless + // transactions are not ordered by (ascending or descending) date. + transactionView->scrollTo(targetIndex); + // scrollTo() does not scroll far enough the first time when transactions + // are ordered by ascending date. + if (index == results[0]) transactionView->scrollTo(targetIndex); + } +} + // We override the virtual resizeEvent of the QWidget to adjust tables column // sizes as the tables width is proportional to the dialogs width. void TransactionView::resizeEvent(QResizeEvent* event) diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 82e929b53f..66dc5bc86b 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -7,6 +7,8 @@ #include <qt/guiutil.h> +#include <uint256.h> + #include <QWidget> #include <QKeyEvent> @@ -116,7 +118,7 @@ public Q_SLOTS: void changedSearch(); void exportClicked(); void focusTransaction(const QModelIndex&); - + void focusTransaction(const uint256& txid); }; #endif // BITCOIN_QT_TRANSACTIONVIEW_H diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 7eced9289d..64497a3431 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -68,6 +68,9 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent): connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo())); + // Highlight transaction after send + connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), transactionView, SLOT(focusTransaction(uint256))); + // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); @@ -91,6 +94,9 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui) // Clicking on a transaction on the overview page simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoHistoryPage())); + // Navigate to transaction history page after send + connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), gui, SLOT(gotoHistoryPage())); + // Receive and report messages connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); diff --git a/src/rest.cpp b/src/rest.cpp index eeeb3f5141..8cba59dbbc 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -90,7 +90,7 @@ static enum RetFormat ParseDataFormat(std::string& param, const std::string& str static std::string AvailableDataFormatsString() { - std::string formats = ""; + std::string formats; for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++) if (strlen(rf_names[i].name) > 0) { formats.append("."); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f1352a13cf..f2a1fd048f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -372,6 +372,9 @@ std::string EntryDescriptionString() " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n" + " \"transactionid\", (string) child transaction id\n" " ... ]\n"; } @@ -406,6 +409,15 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) } info.pushKV("depends", depends); + + UniValue spent(UniValue::VARR); + const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash()); + const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it); + for (const CTxMemPool::txiter &childiter : setChildren) { + spent.push_back(childiter->GetTx().GetHash().ToString()); + } + + info.pushKV("spentby", spent); } UniValue mempoolToJSON(bool fVerbose) @@ -1542,25 +1554,19 @@ UniValue getchaintxstats(const JSONRPCRequest& request) const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month - bool havehash = !request.params[1].isNull(); - uint256 hash; - if (havehash) { - hash = uint256S(request.params[1].get_str()); - } - - { + if (request.params[1].isNull()) { LOCK(cs_main); - if (havehash) { - auto it = mapBlockIndex.find(hash); - if (it == mapBlockIndex.end()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - pindex = it->second; - if (!chainActive.Contains(pindex)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); - } - } else { - pindex = chainActive.Tip(); + pindex = chainActive.Tip(); + } else { + uint256 hash = uint256S(request.params[1].get_str()); + LOCK(cs_main); + auto it = mapBlockIndex.find(hash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + pindex = it->second; + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); } } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7a0225ff0d..fee2b765ba 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -89,7 +89,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) " \"pingtime\": n, (numeric) ping time (if available)\n" " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n" " \"pingwait\": n, (numeric) ping wait (if non-zero)\n" - " \"version\": v, (numeric) The peer version, such as 7001\n" + " \"version\": v, (numeric) The peer version, such as 70001\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/-connect or if it was an automatic/inbound connection\n" diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 838e502a0a..aaba5e5926 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -140,10 +140,9 @@ static CScript PushAll(const std::vector<valtype>& values) bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { - CScript script = fromPubKey; std::vector<valtype> result; txnouttype whichType; - bool solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE); + bool solved = SignStep(creator, fromPubKey, result, whichType, SIGVERSION_BASE); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -153,8 +152,8 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - script = subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; + subscript = CScript(result[0].begin(), result[0].end()); + solved = solved && SignStep(creator, subscript, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; P2SH = true; } diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index b338d6d366..0d8bd90119 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -52,6 +52,17 @@ public: { CAddrMan::Delete(nId); } + + // Simulates connection failure so that we can test eviction of offline nodes + void SimConnFail(CService& addr) + { + int64_t nLastSuccess = 1; + Good_(addr, true, nLastSuccess); // Set last good connection in the deep past. + + bool count_failure = false; + int64_t nLastTry = GetAdjustedTime()-61; + Attempt(addr, count_failure, nLastTry); + } }; static CNetAddr ResolveIP(const char* ip) @@ -226,7 +237,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) BOOST_CHECK_EQUAL(addrman.size(), 0); for (unsigned int i = 1; i < 18; i++) { - CService addr = ResolveService("250.1.1." + boost::to_string(i)); + CService addr = ResolveService("250.1.1." + std::to_string(i)); addrman.Add(CAddress(addr, NODE_NONE), source); //Test: No collision in new table yet. @@ -252,7 +263,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) BOOST_CHECK_EQUAL(addrman.size(), 0); for (unsigned int i = 1; i < 80; i++) { - CService addr = ResolveService("250.1.1." + boost::to_string(i)); + CService addr = ResolveService("250.1.1." + std::to_string(i)); addrman.Add(CAddress(addr, NODE_NONE), source); addrman.Good(CAddress(addr, NODE_NONE)); @@ -385,7 +396,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) for (unsigned int i = 1; i < (8 * 256); i++) { int octet1 = i % 256; int octet2 = i >> 8 % 256; - std::string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + ".1.23"; + std::string strAddr = std::to_string(octet1) + "." + std::to_string(octet2) + ".1.23"; CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE); // Ensure that for all addrs in addrman, isTerrible == false. @@ -436,8 +447,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) std::set<int> buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo( - CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), - ResolveIP("250.1.1." + boost::to_string(i))); + CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + std::to_string(i))); int bucket = infoi.GetTriedBucket(nKey1); buckets.insert(bucket); } @@ -448,8 +459,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) buckets.clear(); for (int j = 0; j < 255; j++) { CAddrInfo infoj = CAddrInfo( - CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE), - ResolveIP("250." + boost::to_string(j) + ".1.1")); + CAddress(ResolveService("250." + std::to_string(j) + ".1.1"), NODE_NONE), + ResolveIP("250." + std::to_string(j) + ".1.1")); int bucket = infoj.GetTriedBucket(nKey1); buckets.insert(bucket); } @@ -488,8 +499,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) std::set<int> buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo( - CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), - ResolveIP("250.1.1." + boost::to_string(i))); + CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + std::to_string(i))); int bucket = infoi.GetNewBucket(nKey1); buckets.insert(bucket); } @@ -501,7 +512,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) for (int j = 0; j < 4 * 255; j++) { CAddrInfo infoj = CAddrInfo(CAddress( ResolveService( - boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE), + std::to_string(250 + (j / 255)) + "." + std::to_string(j % 256) + ".1.1"), NODE_NONE), ResolveIP("251.4.1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); @@ -514,7 +525,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) for (int p = 0; p < 255; p++) { CAddrInfo infoj = CAddrInfo( CAddress(ResolveService("250.1.1.1"), NODE_NONE), - ResolveIP("250." + boost::to_string(p) + ".1.1")); + ResolveIP("250." + std::to_string(p) + ".1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); } @@ -522,4 +533,158 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) // than 64 buckets. BOOST_CHECK(buckets.size() > 64); } + + +BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + BOOST_CHECK(addrman.size() == 0); + + // Empty addrman should return blank addrman info. + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // Add twenty two addresses. + CNetAddr source = ResolveIP("252.2.2.2"); + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + // No collisions yet. + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Ensure Good handles duplicates well. + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Good(addr); + + BOOST_CHECK(addrman.size() == 22); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + +} + +BOOST_AUTO_TEST_CASE(addrman_noevict) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + // Add twenty two addresses. + CNetAddr source = ResolveIP("252.2.2.2"); + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + // No collision yet. + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Collision between 23 and 19. + CService addr23 = ResolveService("250.1.1.23"); + addrman.Add(CAddress(addr23, NODE_NONE), source); + addrman.Good(addr23); + + BOOST_CHECK(addrman.size() == 23); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.19:0"); + + // 23 should be discarded and 19 not evicted. + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // Lets create two collisions. + for (unsigned int i = 24; i < 33; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Cause a collision. + CService addr33 = ResolveService("250.1.1.33"); + addrman.Add(CAddress(addr33, NODE_NONE), source); + addrman.Good(addr33); + BOOST_CHECK(addrman.size() == 33); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.27:0"); + + // Cause a second collision. + addrman.Add(CAddress(addr23, NODE_NONE), source); + addrman.Good(addr23); + BOOST_CHECK(addrman.size() == 33); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() != "[::]:0"); + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); +} + +BOOST_AUTO_TEST_CASE(addrman_evictionworks) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + BOOST_CHECK(addrman.size() == 0); + + // Empty addrman should return blank addrman info. + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // Add twenty two addresses. + CNetAddr source = ResolveIP("252.2.2.2"); + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + // No collision yet. + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Collision between 23 and 19. + CService addr = ResolveService("250.1.1.23"); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + BOOST_CHECK(addrman.size() == 23); + CAddrInfo info = addrman.SelectTriedCollision(); + BOOST_CHECK(info.ToString() == "250.1.1.19:0"); + + // Ensure test of address fails, so that it is evicted. + addrman.SimConnFail(info); + + // Should swap 23 for 19. + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // If 23 was swapped for 19, then this should cause no collisions. + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // If we insert 19 is should collide with 23. + CService addr19 = ResolveService("250.1.1.19"); + addrman.Add(CAddress(addr19, NODE_NONE), source); + addrman.Good(addr19); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.23:0"); + + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); +} + + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 42f9dd0600..8e0ec5243b 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -406,11 +406,11 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) boost::thread_group tg; std::mutex m; std::condition_variable cv; + bool has_lock{false}; + bool has_tried{false}; + bool done{false}; + bool done_ack{false}; { - bool has_lock {false}; - bool has_tried {false}; - bool done {false}; - bool done_ack {false}; std::unique_lock<std::mutex> l(m); tg.create_thread([&]{ CCheckQueueControl<FakeCheck> control(queue.get()); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index ca57f58905..e03234060d 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) ipv4Addr.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); - std::string pszDest = ""; + std::string pszDest; bool fInboundIn = false; // Test that fFeeler is false by default. diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index db9162c0db..01c3a6cedd 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.erase(InsecureRandRange(test.size())); } if (InsecureRandBits(3) == 2) { - int new_size = std::max<int>(0, std::min<int>(30, test.size() + (InsecureRandRange(5)) - 2)); + int new_size = std::max(0, std::min(30, (int)test.size() + (int)InsecureRandRange(5) - 2)); test.resize(new_size); } if (InsecureRandBits(3) == 3) { diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 760f933abc..179df7dd38 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -56,8 +56,8 @@ BOOST_AUTO_TEST_CASE(manythreads) int counter[10] = { 0 }; FastRandomContext rng(42); auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9] - auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + rc.randrange(1012); }; // [-11, 1000] - auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + rc.randrange(2001); }; // [-1000, 1000] + auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] + auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); boost::chrono::system_clock::time_point now = start; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bdd44489f4..95c4825b84 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -28,6 +28,9 @@ void CConnmanTest::AddNode(CNode& node) void CConnmanTest::ClearNodes() { LOCK(g_connman->cs_vNodes); + for (CNode* node : g_connman->vNodes) { + delete node; + } g_connman->vNodes.clear(); } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 463bed5957..58f033cd89 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -82,6 +82,20 @@ BOOST_AUTO_TEST_CASE(util_HexStr) "04 67 8a fd b0"); BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected + sizeof(ParseHex_expected), + ParseHex_expected + sizeof(ParseHex_expected)), + ""); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected + sizeof(ParseHex_expected), + ParseHex_expected + sizeof(ParseHex_expected), true), + ""); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected, ParseHex_expected), + ""); + + BOOST_CHECK_EQUAL( HexStr(ParseHex_expected, ParseHex_expected, true), ""); @@ -90,6 +104,58 @@ BOOST_AUTO_TEST_CASE(util_HexStr) BOOST_CHECK_EQUAL( HexStr(ParseHex_vec, true), "04 67 8a fd b0"); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()), + "b0fd8a6704" + ); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend(), true), + "b0 fd 8a 67 04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected), + std::reverse_iterator<const uint8_t *>(ParseHex_expected), true), + "" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1), + std::reverse_iterator<const uint8_t *>(ParseHex_expected), true), + "04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "b0fd8a6704" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5), + std::reverse_iterator<const uint8_t *>(ParseHex_expected), true), + "b0 fd 8a 67 04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de611feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704" + ); } @@ -688,7 +754,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory) thr.join(); BOOST_CHECK_EQUAL(threadresult, true); #ifndef WIN32 - // Try to aquire lock in child process while we're holding it, this should fail. + // Try to acquire lock in child process while we're holding it, this should fail. char ch; BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); @@ -699,7 +765,7 @@ BOOST_AUTO_TEST_CASE(test_LockDirectory) // Probing lock from our side now should succeed, but not hold on to the lock. BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); - // Try to acquire the lock in the child process, this should be succesful. + // Try to acquire the lock in the child process, this should be successful. BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); BOOST_CHECK_EQUAL((bool)ch, true); diff --git a/src/util.cpp b/src/util.cpp index dcf7ed38b1..82c99a3c2f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -79,6 +79,7 @@ #include <openssl/crypto.h> #include <openssl/rand.h> #include <openssl/conf.h> +#include <thread> // Application startup time (used for uptime calculation) const int64_t nStartupTime = GetTime(); @@ -927,11 +928,7 @@ bool SetupNetworking() int GetNumCores() { -#if BOOST_VERSION >= 105600 - return boost::thread::physical_concurrency(); -#else // Must fall back to hardware_concurrency, which unfortunately counts virtual cores - return boost::thread::hardware_concurrency(); -#endif + return std::thread::hardware_concurrency(); } std::string CopyrightHolders(const std::string& strPrefix) diff --git a/src/util.h b/src/util.h index 9490a5678f..e4170d8aa2 100644 --- a/src/util.h +++ b/src/util.h @@ -312,9 +312,8 @@ std::string HelpMessageGroup(const std::string& message); std::string HelpMessageOpt(const std::string& option, const std::string& message); /** - * Return the number of physical cores available on the current system. - * @note This does not count virtual cores, such as those provided by HyperThreading - * when boost is newer than 1.56. + * Return the number of cores available on the current system. + * @note This does count virtual cores, such as those provided by HyperThreading. */ int GetNumCores(); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 52158e9804..ebafe078f4 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -127,7 +127,7 @@ std::string EncodeBase64(const unsigned char* pch, size_t len) { static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string strRet = ""; + std::string strRet; strRet.reserve((len+2)/3*4); int mode=0, left=0; @@ -267,7 +267,7 @@ std::string EncodeBase32(const unsigned char* pch, size_t len) { static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; - std::string strRet=""; + std::string strRet; strRet.reserve((len+4)/5*8); int mode=0, left=0; diff --git a/src/validation.cpp b/src/validation.cpp index e809f66e25..a77362f5d6 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1773,9 +1773,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl { AssertLockHeld(cs_main); assert(pindex); - // pindex->phashBlock can be null if called by CreateNewBlock/TestBlockValidity - assert((pindex->phashBlock == nullptr) || - (*pindex->phashBlock == block.GetHash())); + assert(*pindex->phashBlock == block.GetHash()); int64_t nTimeStart = GetTimeMicros(); // Check it again in case a previous version let a bad block in @@ -1849,8 +1847,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes during their // initial block download. - bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. - !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || + bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); // Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting @@ -3434,9 +3431,11 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainActive.Tip()); CCoinsViewCache viewNew(pcoinsTip.get()); + uint256 block_hash(block.GetHash()); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; + indexDummy.phashBlock = &block_hash; // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) @@ -4658,6 +4657,7 @@ bool DumpMempool(void) } //! Guess how far we are in the verification process at the given block index +//! require cs_main if pindex has not been validated yet (because nChainTx might be unset) double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) { if (pindex == nullptr) return 0.0; diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 73985dcf25..385fdc963a 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -53,6 +53,9 @@ CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, c // if we don't have enough data for estimateSmartFee, then use fallbackFee fee_needed = CWallet::fallbackFee.GetFee(nTxBytes); if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; + + // directly return if fallback fee is disabled (feerate 0 == disabled) + if (CWallet::fallbackFee.GetFee(1000) == 0) return fee_needed; } // Obey mempool min fee when using smart fee estimation CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 74036f4f0f..9ac48bff77 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -5,6 +5,7 @@ #include <wallet/init.h> +#include <chainparams.h> #include <net.h> #include <util.h> #include <utilmoneystr.h> @@ -123,6 +124,8 @@ bool WalletParameterInteraction() _("This is the minimum transaction fee you pay on every transaction.")); CWallet::minTxFee = CFeeRate(n); } + + g_wallet_allow_fallback_fee = Params().IsFallbackFeeEnabled(); if (gArgs.IsArgSet("-fallbackfee")) { CAmount nFeePerK = 0; @@ -132,6 +135,7 @@ bool WalletParameterInteraction() InitWarning(AmountHighWarn("-fallbackfee") + " " + _("This is the transaction fee you may pay when fee estimates are not available.")); CWallet::fallbackFee = CFeeRate(nFeePerK); + g_wallet_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value } if (gArgs.IsArgSet("-discardfee")) { diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 741ea25340..930e8bbbb4 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -279,7 +279,7 @@ UniValue importaddress(const JSONRPCRequest& request) ); - std::string strLabel = ""; + std::string strLabel; if (!request.params[1].isNull()) strLabel = request.params[1].get_str(); @@ -452,7 +452,7 @@ UniValue importpubkey(const JSONRPCRequest& request) ); - std::string strLabel = ""; + std::string strLabel; if (!request.params[1].isNull()) strLabel = request.params[1].get_str(); diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index cafd69d075..e6510cc214 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "y"; { CMutableTransaction tx(*wtx.tx); - --tx.nLockTime; // Just to change the hash :) + ++tx.nLockTime; // Just to change the hash :) wtx.SetTx(MakeTransactionRef(std::move(tx))); } pwalletMain->AddToWallet(wtx); @@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "x"; { CMutableTransaction tx(*wtx.tx); - --tx.nLockTime; // Just to change the hash :) + ++tx.nLockTime; // Just to change the hash :) wtx.SetTx(MakeTransactionRef(std::move(tx))); } pwalletMain->AddToWallet(wtx); diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 7797f85f07..18abf9a9db 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -11,7 +11,6 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName): TestingSetup(chainName) { bitdb.MakeMock(); - bool fFirstRun; g_address_type = OUTPUT_TYPE_DEFAULT; g_change_type = OUTPUT_TYPE_DEFAULT; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 408a01c50b..bb7be2df33 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -43,6 +43,7 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; bool fWalletRbf = DEFAULT_WALLET_RBF; OutputType g_address_type = OUTPUT_TYPE_NONE; OutputType g_change_type = OUTPUT_TYPE_NONE; +bool g_wallet_allow_fallback_fee = true; //<! will be defined via chainparams const char * DEFAULT_WALLET_DAT = "wallet.dat"; const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; @@ -1668,20 +1669,15 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip); } + double gvp = dProgressStart; while (pindex && !fAbortRescan) { if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) { - double gvp = 0; - { - LOCK(cs_main); - gvp = GuessVerificationProgress(chainParams.TxData(), pindex); - } ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); } if (GetTime() >= nNow + 60) { nNow = GetTime(); - LOCK(cs_main); - LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, gvp); } CBlock block; @@ -1705,6 +1701,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock { LOCK(cs_main); pindex = chainActive.Next(pindex); + gvp = GuessVerificationProgress(chainParams.TxData(), pindex); if (tip != chainActive.Tip()) { tip = chainActive.Tip(); // in case the tip has changed, update progress max @@ -1713,7 +1710,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock } } if (pindex && fAbortRescan) { - LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, gvp); } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI } @@ -2922,6 +2919,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); + if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) { + // eventually allow a fallback fee + strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); + return false; + } // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index fefe415bb1..4db45f16ef 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -39,6 +39,7 @@ extern CFeeRate payTxFee; extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; extern bool fWalletRbf; +extern bool g_wallet_allow_fallback_fee; static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; //! -paytxfee default |