diff options
Diffstat (limited to 'src')
31 files changed, 330 insertions, 109 deletions
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/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/init.cpp b/src/init.cpp index 1cc5c5f9c7..47bcdf2294 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() + "."); diff --git a/src/net.cpp b/src/net.cpp index 201914685c..c7c19f83a1 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; @@ -2718,6 +2722,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..ddf0dbbbea 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -892,6 +892,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB 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; @@ -1642,7 +1643,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 +1808,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 +3618,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/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/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 871822ccb4..ef36aab1a4 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -369,6 +369,7 @@ void SendCoinsDialog::on_sendButton_clicked() accept(); CoinControlDialog::coinControl()->UnSelectAll(); coinControlUpdateLabels(); + Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash()); } fNewRecipientAllowed = true; } 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/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/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/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/util_tests.cpp b/src/test/util_tests.cpp index 9093cbb86d..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" + ); } 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/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/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 b35f8c7f2b..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; @@ -2918,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 |