aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-tidy2
-rw-r--r--src/Makefile.am12
-rw-r--r--src/Makefile.qt.include4
-rw-r--r--src/Makefile.qttest.include1
-rw-r--r--src/addrdb.cpp97
-rw-r--r--src/addrdb.h22
-rw-r--r--src/addrman.cpp55
-rw-r--r--src/addrman.h14
-rw-r--r--src/bench/addrman.cpp4
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/bitcoin-cli.cpp34
-rw-r--r--src/compat/glibc_compat.cpp62
-rw-r--r--src/dummywallet.cpp7
-rw-r--r--src/httpserver.cpp4
-rw-r--r--src/index/base.h3
-rw-r--r--src/index/txindex.cpp163
-rw-r--r--src/index/txindex.h5
-rw-r--r--src/init.cpp24
-rw-r--r--src/init/bitcoin-node.cpp9
-rw-r--r--src/init/bitcoind.cpp11
-rw-r--r--src/interfaces/chain.h2
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/key.h1
-rw-r--r--src/logging/timer.h24
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net.cpp33
-rw-r--r--src/net.h4
-rw-r--r--src/net_processing.cpp8
-rw-r--r--src/netaddress.cpp5
-rw-r--r--src/netaddress.h2
-rw-r--r--src/netbase.cpp2
-rw-r--r--src/node/blockstorage.cpp10
-rw-r--r--src/node/interfaces.cpp8
-rw-r--r--src/policy/rbf.cpp138
-rw-r--r--src/policy/rbf.h62
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/bantablemodel.h1
-rw-r--r--src/qt/bitcoin.cpp16
-rw-r--r--src/qt/bitcoin.h8
-rw-r--r--src/qt/forms/optionsdialog.ui6
-rw-r--r--src/qt/optionsdialog.cpp20
-rw-r--r--src/qt/qrimagewidget.cpp2
-rw-r--r--src/qt/rpcconsole.cpp7
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/test/addressbooktests.cpp4
-rw-r--r--src/qt/test/test_main.cpp7
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/transactionview.cpp2
-rw-r--r--src/qt/walletframe.cpp1
-rw-r--r--src/rpc/misc.cpp5
-rw-r--r--src/signet.cpp2
-rw-r--r--src/sync.cpp6
-rw-r--r--src/sync.h7
-rw-r--r--src/test/README.md26
-rw-r--r--src/test/addrman_tests.cpp21
-rw-r--r--src/test/data/README.md2
-rw-r--r--src/test/fuzz/addrman.cpp27
-rw-r--r--src/test/fuzz/asmap.cpp3
-rw-r--r--src/test/fuzz/data_stream.cpp6
-rw-r--r--src/test/fuzz/integer.cpp1
-rw-r--r--src/test/fuzz/net.cpp7
-rw-r--r--src/test/fuzz/versionbits.cpp1
-rw-r--r--src/test/logging_tests.cpp8
-rw-r--r--src/test/util/setup_common.cpp1
-rw-r--r--src/txdb.cpp23
-rw-r--r--src/txdb.h11
-rw-r--r--src/txmempool.cpp6
-rw-r--r--src/txmempool.h4
-rw-r--r--src/util/asmap.cpp38
-rw-r--r--src/util/asmap.h7
-rw-r--r--src/util/rbf.h14
-rw-r--r--src/util/sock.cpp8
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/util/types.h11
-rw-r--r--src/validation.cpp110
-rw-r--r--src/validation.h8
-rw-r--r--src/wallet/init.cpp3
-rw-r--r--src/wallet/load.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp18
-rw-r--r--src/wallet/test/coinselector_tests.cpp6
-rw-r--r--src/wallet/test/wallet_tests.cpp6
-rw-r--r--src/wallet/transaction.h29
-rw-r--r--src/zmq/zmqpublishnotifier.cpp24
83 files changed, 663 insertions, 710 deletions
diff --git a/src/.clang-tidy b/src/.clang-tidy
new file mode 100644
index 0000000000..27616ad072
--- /dev/null
+++ b/src/.clang-tidy
@@ -0,0 +1,2 @@
+Checks: '-*,bugprone-argument-comment'
+WarningsAsErrors: bugprone-argument-comment
diff --git a/src/Makefile.am b/src/Makefile.am
index 6f8245de8a..52c8b85357 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -268,6 +268,7 @@ BITCOIN_CORE_H = \
util/tokenpipe.h \
util/trace.h \
util/translation.h \
+ util/types.h \
util/ui_change_type.h \
util/url.h \
util/vector.h \
@@ -570,7 +571,7 @@ libbitcoin_common_a_SOURCES = \
# util: shared between all executables.
# This library *must* be included to make sure that the glibc
-# backward-compatibility objects and their sanity checks are linked.
+# sanity checks are linked.
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_util_a_SOURCES = \
@@ -618,11 +619,6 @@ if USE_LIBEVENT
libbitcoin_util_a_SOURCES += util/url.cpp
endif
-if GLIBC_BACK_COMPAT
-libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
-AM_LDFLAGS += $(COMPAT_LDFLAGS)
-endif
-
# cli: shared between bitcoin-cli and bitcoin-qt
libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@@ -750,10 +746,6 @@ if BUILD_BITCOIN_LIBS
include_HEADERS = script/bitcoinconsensus.h
libbitcoinconsensus_la_SOURCES = support/cleanse.cpp $(crypto_libbitcoin_crypto_base_a_SOURCES) $(libbitcoin_consensus_a_SOURCES)
-if GLIBC_BACK_COMPAT
- libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp
-endif
-
libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1)
libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 6f450bbc74..f4b0b3adbe 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -338,14 +338,14 @@ bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
qt_bitcoin_qt_CXXFLAGS = $(bitcoin_qt_cxxflags)
-qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources)
+qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
qt_bitcoin_qt_LDADD = $(bitcoin_qt_ldadd)
qt_bitcoin_qt_LDFLAGS = $(bitcoin_qt_ldflags)
qt_bitcoin_qt_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
bitcoin_gui_CPPFLAGS = $(bitcoin_qt_cppflags)
bitcoin_gui_CXXFLAGS = $(bitcoin_qt_cxxflags)
-bitcoin_gui_SOURCES = $(bitcoin_qt_sources)
+bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
bitcoin_gui_LDADD = $(bitcoin_qt_ldadd)
bitcoin_gui_LDFLAGS = $(bitcoin_qt_ldflags)
bitcoin_gui_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 91a5e9fd9b..8a5521eeb5 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -28,6 +28,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
$(QT_INCLUDES) $(QT_TEST_INCLUDES)
qt_test_test_bitcoin_qt_SOURCES = \
+ init/bitcoind.cpp \
qt/test/apptests.cpp \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 345dbdfb16..1e73750ce3 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -18,8 +18,15 @@
#include <univalue.h>
#include <util/settings.h>
#include <util/system.h>
+#include <util/translation.h>
namespace {
+
+class DbNotFoundError : public std::exception
+{
+ using std::exception::exception;
+};
+
template <typename Stream, typename Data>
bool SerializeDB(Stream& stream, const Data& data)
{
@@ -77,47 +84,40 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
}
template <typename Stream, typename Data>
-bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
+void DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
{
- try {
- CHashVerifier<Stream> verifier(&stream);
- // de-serialize file header (network specific magic number) and ..
- unsigned char pchMsgTmp[4];
- verifier >> pchMsgTmp;
- // ... verify the network matches ours
- if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
- return error("%s: Invalid network magic number", __func__);
-
- // de-serialize data
- verifier >> data;
-
- // verify checksum
- if (fCheckSum) {
- uint256 hashTmp;
- stream >> hashTmp;
- if (hashTmp != verifier.GetHash()) {
- return error("%s: Checksum mismatch, data corrupted", __func__);
- }
- }
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ CHashVerifier<Stream> verifier(&stream);
+ // de-serialize file header (network specific magic number) and ..
+ unsigned char pchMsgTmp[4];
+ verifier >> pchMsgTmp;
+ // ... verify the network matches ours
+ if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) {
+ throw std::runtime_error{"Invalid network magic number"};
}
- return true;
+ // de-serialize data
+ verifier >> data;
+
+ // verify checksum
+ if (fCheckSum) {
+ uint256 hashTmp;
+ stream >> hashTmp;
+ if (hashTmp != verifier.GetHash()) {
+ throw std::runtime_error{"Checksum mismatch, data corrupted"};
+ }
+ }
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data, int version)
+void DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
- LogPrintf("Missing or invalid file %s\n", path.string());
- return false;
+ throw DbNotFoundError{};
}
- return DeserializeDB(filein, data);
+ DeserializeDB(filein, data);
}
} // namespace
@@ -170,24 +170,38 @@ bool CBanDB::Read(banmap_t& banSet)
return true;
}
-CAddrDB::CAddrDB()
-{
- pathAddr = gArgs.GetDataDirNet() / "peers.dat";
-}
-
-bool CAddrDB::Write(const CAddrMan& addr)
+bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr)
{
+ const auto pathAddr = args.GetDataDirNet() / "peers.dat";
return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
-bool CAddrDB::Read(CAddrMan& addr)
+void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers)
{
- return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
+ DeserializeDB(ssPeers, addr, false);
}
-bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman)
{
- return DeserializeDB(ssPeers, addr, false);
+ auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
+ addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+
+ int64_t nStart = GetTimeMillis();
+ const auto path_addr{args.GetDataDirNet() / "peers.dat"};
+ try {
+ DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION);
+ LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->size(), GetTimeMillis() - nStart);
+ } catch (const DbNotFoundError&) {
+ // Addrman can be in an inconsistent state after failure, reset it
+ addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
+ LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
+ DumpPeerAddresses(args, *addrman);
+ } catch (const std::exception& e) {
+ addrman = nullptr;
+ return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."),
+ e.what(), PACKAGE_BUGREPORT, path_addr);
+ }
+ return std::nullopt;
}
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
@@ -199,9 +213,10 @@ void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& a
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
+ try {
+ DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
- } else {
+ } catch (const std::exception&) {
anchors.clear();
}
diff --git a/src/addrdb.h b/src/addrdb.h
index 26b1c5880f..33cc1f9204 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -10,23 +10,18 @@
#include <net_types.h> // For banmap_t
#include <univalue.h>
+#include <optional>
#include <vector>
-class CAddress;
+class ArgsManager;
class CAddrMan;
+class CAddress;
class CDataStream;
+struct bilingual_str;
-/** Access to the (IP) address database (peers.dat) */
-class CAddrDB
-{
-private:
- fs::path pathAddr;
-public:
- CAddrDB();
- bool Write(const CAddrMan& addr);
- bool Read(CAddrMan& addr);
- static bool Read(CAddrMan& addr, CDataStream& ssPeers);
-};
+bool DumpPeerAddresses(const ArgsManager& args, const CAddrMan& addr);
+/** Only used by tests. */
+void ReadFromStream(CAddrMan& addr, CDataStream& ssPeers);
/** Access to the banlist database (banlist.json) */
class CBanDB
@@ -52,6 +47,9 @@ public:
bool Read(banmap_t& banSet);
};
+/** Returns an error string on failure */
+std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const ArgsManager& args, std::unique_ptr<CAddrMan>& addrman);
+
/**
* Dump the anchor IP address database (anchors.dat)
*
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 717cadaedf..a1e8cb1bf1 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -5,10 +5,13 @@
#include <addrman.h>
+#include <clientversion.h>
#include <hash.h>
#include <logging.h>
#include <netaddress.h>
#include <serialize.h>
+#include <streams.h>
+#include <util/check.h>
#include <cmath>
#include <optional>
@@ -486,11 +489,14 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
AssertLockHeld(cs);
// remove the entry from all new buckets
- for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
- int pos = info.GetBucketPosition(nKey, true, bucket);
+ const int start_bucket{info.GetNewBucket(nKey, m_asmap)};
+ for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
+ const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
+ const int pos{info.GetBucketPosition(nKey, true, bucket)};
if (vvNew[bucket][pos] == nId) {
vvNew[bucket][pos] = -1;
info.nRefCount--;
+ if (info.nRefCount == 0) break;
}
}
nNew--;
@@ -562,22 +568,10 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
if (info.fInTried)
return;
- // find a bucket it is in now
- int nRnd = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
- int nUBucket = -1;
- for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
- int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
- int nBpos = info.GetBucketPosition(nKey, true, nB);
- if (vvNew[nB][nBpos] == nId) {
- nUBucket = nB;
- break;
- }
- }
-
- // if no bucket is found, something bad happened;
- // TODO: maybe re-add the node, but for now, just bail out
- if (nUBucket == -1)
+ // if it is not in new, something bad happened
+ if (!Assume(info.nRefCount > 0)) {
return;
+ }
// which tried bucket to move the entry to
int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
@@ -1007,30 +1001,3 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
return mapInfo[id_old];
}
-
-std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
-{
- std::vector<bool> bits;
- FILE *filestr = fsbridge::fopen(path, "rb");
- CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
- if (file.IsNull()) {
- LogPrintf("Failed to open asmap file from disk\n");
- return bits;
- }
- fseek(filestr, 0, SEEK_END);
- int length = ftell(filestr);
- LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
- fseek(filestr, 0, SEEK_SET);
- uint8_t cur_byte;
- for (int i = 0; i < length; ++i) {
- file >> cur_byte;
- for (int bit = 0; bit < 8; ++bit) {
- bits.push_back((cur_byte >> bit) & 1);
- }
- }
- if (!SanityCheckASMap(bits)) {
- LogPrintf("Sanity check of asmap file %s failed\n", path);
- return {};
- }
- return bits;
-}
diff --git a/src/addrman.h b/src/addrman.h
index 74bfe9748b..0885231ebc 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -6,23 +6,16 @@
#ifndef BITCOIN_ADDRMAN_H
#define BITCOIN_ADDRMAN_H
-#include <clientversion.h>
-#include <config/bitcoin-config.h>
#include <fs.h>
-#include <hash.h>
+#include <logging.h>
#include <netaddress.h>
#include <protocol.h>
-#include <random.h>
-#include <streams.h>
#include <sync.h>
#include <timedata.h>
-#include <tinyformat.h>
-#include <util/system.h>
-#include <iostream>
+#include <cstdint>
#include <optional>
#include <set>
-#include <stdint.h>
#include <unordered_map>
#include <vector>
@@ -149,9 +142,6 @@ static constexpr int ADDRMAN_BUCKET_SIZE{1 << ADDRMAN_BUCKET_SIZE_LOG2};
class CAddrMan
{
public:
- // Read asmap from provided binary file
- static std::vector<bool> DecodeAsmap(fs::path path);
-
template <typename Stream>
void Serialize(Stream& s_) const EXCLUSIVE_LOCKS_REQUIRED(!cs);
diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
index 8fbb68c04c..e5dd571a4c 100644
--- a/src/bench/addrman.cpp
+++ b/src/bench/addrman.cpp
@@ -5,6 +5,7 @@
#include <addrman.h>
#include <bench/bench.h>
#include <random.h>
+#include <util/check.h>
#include <util/time.h>
#include <optional>
@@ -110,7 +111,8 @@ static void AddrManGood(benchmark::Bench& bench)
* we want to do the same amount of work in every loop iteration. */
bench.epochs(5).epochIterations(1);
- const size_t addrman_count{bench.epochs() * bench.epochIterations()};
+ const uint64_t addrman_count{bench.epochs() * bench.epochIterations()};
+ Assert(addrman_count == 5U);
std::vector<std::unique_ptr<CAddrMan>> addrmans(addrman_count);
for (size_t i{0}; i < addrman_count; ++i) {
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index aa79aab755..934b574f8b 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -53,7 +53,7 @@ static void CoinSelection(benchmark::Bench& bench)
const CoinSelectionParams coin_selection_params(/* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 297f3066ff..d6e7298fd0 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -93,9 +93,6 @@ static void SetupCliArgs(ArgsManager& argsman)
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
-#ifndef EVENT_LOG_ERR // EVENT_LOG_ERR was added in 2.0.19; but before then _EVENT_LOG_ERR existed.
-# define EVENT_LOG_ERR _EVENT_LOG_ERR
-#endif
// Ignore everything other than errors
if (severity >= EVENT_LOG_ERR) {
throw std::runtime_error(strprintf("libevent error: %s", msg));
@@ -245,7 +242,7 @@ public:
class AddrinfoRequestHandler : public BaseRequestHandler
{
private:
- static constexpr std::array m_networks{"ipv4", "ipv6", "torv2", "torv3", "i2p"};
+ static constexpr std::array m_networks{"ipv4", "ipv6", "onion", "i2p"};
int8_t NetworkStringToId(const std::string& str) const
{
for (size_t i = 0; i < m_networks.size(); ++i) {
@@ -271,13 +268,10 @@ public:
if (!nodes.empty() && nodes.at(0)["network"].isNull()) {
throw std::runtime_error("-addrinfo requires bitcoind server to be running v22.0 and up");
}
- // Count the number of peers we know by network, including torv2 versus torv3.
+ // Count the number of peers known to our node, by network.
std::array<uint64_t, m_networks.size()> counts{{}};
for (const UniValue& node : nodes) {
std::string network_name{node["network"].get_str()};
- if (network_name == "onion") {
- network_name = node["address"].get_str().size() > 22 ? "torv3" : "torv2";
- }
const int8_t network_id{NetworkStringToId(network_name)};
if (network_id == UNKNOWN_NETWORK) continue;
++counts.at(network_id);
@@ -343,7 +337,7 @@ public:
connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
result.pushKV("connections", connections);
- result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
+ result.pushKV("networks", batch[ID_NETWORKINFO]["result"]["networks"]);
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO]["result"].isNull()) {
@@ -992,8 +986,26 @@ static void ParseGetInfoResult(UniValue& result)
RESET);
result_string += strprintf("Version: %s\n", result["version"].getValStr());
result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
- const std::string proxy = result["proxy"].getValStr();
- result_string += strprintf("Proxy: %s\n", proxy.empty() ? "N/A" : proxy);
+
+ // proxies
+ std::map<std::string, std::vector<std::string>> proxy_networks;
+ std::vector<std::string> ordered_proxies;
+
+ for (const UniValue& network : result["networks"].getValues()) {
+ const std::string proxy = network["proxy"].getValStr();
+ if (proxy.empty()) continue;
+ // Add proxy to ordered_proxy if has not been processed
+ if (proxy_networks.find(proxy) == proxy_networks.end()) ordered_proxies.push_back(proxy);
+
+ proxy_networks[proxy].push_back(network["name"].getValStr());
+ }
+
+ std::vector<std::string> formatted_proxies;
+ for (const std::string& proxy : ordered_proxies) {
+ formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", ")));
+ }
+ result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", "));
+
result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
if (!result["has_wallet"].isNull()) {
diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp
deleted file mode 100644
index ff581d4a9e..0000000000
--- a/src/compat/glibc_compat.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2009-2020 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#if defined(HAVE_CONFIG_H)
-#include <config/bitcoin-config.h>
-#endif
-
-#include <cstddef>
-#include <cstdint>
-
-#if defined(__i386__) || defined(__arm__)
-
-extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp);
-
-extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp)
-{
- int32_t c1 = 0, c2 = 0;
- int64_t uu = u, vv = v;
- int64_t w;
- int64_t r;
-
- if (uu < 0) {
- c1 = ~c1, c2 = ~c2, uu = -uu;
- }
- if (vv < 0) {
- c1 = ~c1, vv = -vv;
- }
-
- w = __udivmoddi4(uu, vv, (uint64_t*)&r);
- if (c1)
- w = -w;
- if (c2)
- r = -r;
-
- *rp = r;
- return w;
-}
-#endif
-
-extern "C" float log2f_old(float x);
-#ifdef __i386__
-__asm(".symver log2f_old,log2f@GLIBC_2.1");
-#elif defined(__amd64__)
-__asm(".symver log2f_old,log2f@GLIBC_2.2.5");
-#elif defined(__arm__)
-__asm(".symver log2f_old,log2f@GLIBC_2.4");
-#elif defined(__aarch64__)
-__asm(".symver log2f_old,log2f@GLIBC_2.17");
-#elif defined(__powerpc64__)
-# ifdef WORDS_BIGENDIAN
-__asm(".symver log2f_old,log2f@GLIBC_2.3");
-# else
-__asm(".symver log2f_old,log2f@GLIBC_2.17");
-# endif
-#elif defined(__riscv)
-__asm(".symver log2f_old,log2f@GLIBC_2.27");
-#endif
-extern "C" float __wrap_log2f(float x)
-{
- return log2f_old(x);
-}
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 2d897f4c40..8caeb32627 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -5,12 +5,14 @@
#include <util/system.h>
#include <walletinitinterface.h>
+class ArgsManager;
class CWallet;
namespace interfaces {
class Chain;
class Handler;
class Wallet;
+class WalletClient;
}
class DummyWalletInit : public WalletInitInterface {
@@ -64,4 +66,9 @@ std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet)
throw std::logic_error("Wallet function called in non-wallet build.");
}
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args)
+{
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
} // namespace interfaces
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 8741ad9b86..fa0379f612 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -338,10 +338,6 @@ static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg)
{
-#ifndef EVENT_LOG_WARN
-// EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed.
-# define EVENT_LOG_WARN _EVENT_LOG_WARN
-#endif
if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
LogPrintf("libevent: %s\n", msg);
else
diff --git a/src/index/base.h b/src/index/base.h
index df4bdff1ea..1390e3e570 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -6,11 +6,10 @@
#define BITCOIN_INDEX_BASE_H
#include <dbwrapper.h>
-#include <primitives/block.h>
-#include <primitives/transaction.h>
#include <threadinterrupt.h>
#include <validationinterface.h>
+class CBlock;
class CBlockIndex;
class CChainState;
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index cde9821f3d..209785d487 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -2,18 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <index/disktxpos.h>
#include <index/txindex.h>
+
+#include <index/disktxpos.h>
#include <node/blockstorage.h>
-#include <node/ui_interface.h>
-#include <shutdown.h>
#include <util/system.h>
-#include <util/translation.h>
#include <validation.h>
-constexpr uint8_t DB_BEST_BLOCK{'B'};
constexpr uint8_t DB_TXINDEX{'t'};
-constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
std::unique_ptr<TxIndex> g_txindex;
@@ -30,10 +26,6 @@ public:
/// Write a batch of transaction positions to the DB.
bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
-
- /// Migrate txindex data from the block tree DB, where it may be for older nodes that have not
- /// been upgraded yet to the new database.
- bool MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator);
};
TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
@@ -54,163 +46,12 @@ bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_
return WriteBatch(batch);
}
-/*
- * Safely persist a transfer of data from the old txindex database to the new one, and compact the
- * range of keys updated. This is used internally by MigrateData.
- */
-static void WriteTxIndexMigrationBatches(CDBWrapper& newdb, CDBWrapper& olddb,
- CDBBatch& batch_newdb, CDBBatch& batch_olddb,
- const std::pair<uint8_t, uint256>& begin_key,
- const std::pair<uint8_t, uint256>& end_key)
-{
- // Sync new DB changes to disk before deleting from old DB.
- newdb.WriteBatch(batch_newdb, /*fSync=*/ true);
- olddb.WriteBatch(batch_olddb);
- olddb.CompactRange(begin_key, end_key);
-
- batch_newdb.Clear();
- batch_olddb.Clear();
-}
-
-bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator)
-{
- // The prior implementation of txindex was always in sync with block index
- // and presence was indicated with a boolean DB flag. If the flag is set,
- // this means the txindex from a previous version is valid and in sync with
- // the chain tip. The first step of the migration is to unset the flag and
- // write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the
- // index entries are copied over in batches to the new database. Finally,
- // DB_TXINDEX_BLOCK is erased from the old database and the block hash is
- // written to the new database.
- //
- // Unsetting the boolean flag ensures that if the node is downgraded to a
- // previous version, it will not see a corrupted, partially migrated index
- // -- it will see that the txindex is disabled. When the node is upgraded
- // again, the migration will pick up where it left off and sync to the block
- // with hash DB_TXINDEX_BLOCK.
- bool f_legacy_flag = false;
- block_tree_db.ReadFlag("txindex", f_legacy_flag);
- if (f_legacy_flag) {
- if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) {
- return error("%s: cannot write block indicator", __func__);
- }
- if (!block_tree_db.WriteFlag("txindex", false)) {
- return error("%s: cannot write block index db flag", __func__);
- }
- }
-
- CBlockLocator locator;
- if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) {
- return true;
- }
-
- int64_t count = 0;
- LogPrintf("Upgrading txindex database... [0%%]\n");
- uiInterface.ShowProgress(_("Upgrading txindex database").translated, 0, true);
- int report_done = 0;
- const size_t batch_size = 1 << 24; // 16 MiB
-
- CDBBatch batch_newdb(*this);
- CDBBatch batch_olddb(block_tree_db);
-
- std::pair<uint8_t, uint256> key;
- std::pair<uint8_t, uint256> begin_key{DB_TXINDEX, uint256()};
- std::pair<uint8_t, uint256> prev_key = begin_key;
-
- bool interrupted = false;
- std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
- for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
- if (ShutdownRequested()) {
- interrupted = true;
- break;
- }
-
- if (!cursor->GetKey(key)) {
- return error("%s: cannot get key from valid cursor", __func__);
- }
- if (key.first != DB_TXINDEX) {
- break;
- }
-
- // Log progress every 10%.
- if (++count % 256 == 0) {
- // Since txids are uniformly random and traversed in increasing order, the high 16 bits
- // of the hash can be used to estimate the current progress.
- const uint256& txid = key.second;
- uint32_t high_nibble =
- (static_cast<uint32_t>(*(txid.begin() + 0)) << 8) +
- (static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
- int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
-
- uiInterface.ShowProgress(_("Upgrading txindex database").translated, percentage_done, true);
- if (report_done < percentage_done/10) {
- LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done);
- report_done = percentage_done/10;
- }
- }
-
- CDiskTxPos value;
- if (!cursor->GetValue(value)) {
- return error("%s: cannot parse txindex record", __func__);
- }
- batch_newdb.Write(key, value);
- batch_olddb.Erase(key);
-
- if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) {
- // NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating
- // because LevelDB iterators are guaranteed to provide a consistent view of the
- // underlying data, like a lightweight snapshot.
- WriteTxIndexMigrationBatches(*this, block_tree_db,
- batch_newdb, batch_olddb,
- prev_key, key);
- prev_key = key;
- }
- }
-
- // If these final DB batches complete the migration, write the best block
- // hash marker to the new database and delete from the old one. This signals
- // that the former is fully caught up to that point in the blockchain and
- // that all txindex entries have been removed from the latter.
- if (!interrupted) {
- batch_olddb.Erase(DB_TXINDEX_BLOCK);
- batch_newdb.Write(DB_BEST_BLOCK, locator);
- }
-
- WriteTxIndexMigrationBatches(*this, block_tree_db,
- batch_newdb, batch_olddb,
- begin_key, key);
-
- if (interrupted) {
- LogPrintf("[CANCELLED].\n");
- return false;
- }
-
- uiInterface.ShowProgress("", 100, false);
-
- LogPrintf("[DONE].\n");
- return true;
-}
-
TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
: m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
{}
TxIndex::~TxIndex() {}
-bool TxIndex::Init()
-{
- LOCK(cs_main);
-
- // Attempt to migrate txindex from the old database to the new one. Even if
- // chain_tip is null, the node could be reindexing and we still want to
- // delete txindex records in the old database.
- if (!m_db->MigrateData(*m_chainstate->m_blockman.m_block_tree_db, m_chainstate->m_chain.GetLocator())) {
- return false;
- }
-
- return BaseIndex::Init();
-}
-
bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
{
// Exclude genesis block transaction because outputs are not spendable.
diff --git a/src/index/txindex.h b/src/index/txindex.h
index 8202c3c951..59375bc204 100644
--- a/src/index/txindex.h
+++ b/src/index/txindex.h
@@ -5,9 +5,7 @@
#ifndef BITCOIN_INDEX_TXINDEX_H
#define BITCOIN_INDEX_TXINDEX_H
-#include <chain.h>
#include <index/base.h>
-#include <txdb.h>
/**
* TxIndex is used to look up transactions included in the blockchain by hash.
@@ -23,9 +21,6 @@ private:
const std::unique_ptr<DB> m_db;
protected:
- /// Override base class init to migrate from old database.
- bool Init() override;
-
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
BaseIndex::DB& GetDB() const override;
diff --git a/src/init.cpp b/src/init.cpp
index b744298667..ff36ec805c 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -26,6 +26,7 @@
#include <index/txindex.h>
#include <init/common.h>
#include <interfaces/chain.h>
+#include <interfaces/init.h>
#include <interfaces/node.h>
#include <mapport.h>
#include <miner.h>
@@ -1063,7 +1064,7 @@ bool AppInitLockDataDirectory()
bool AppInitInterfaces(NodeContext& node)
{
- node.chain = interfaces::MakeChain(node);
+ node.chain = node.init->makeChain();
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
@@ -1189,7 +1190,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
InitError(strprintf(_("Could not find asmap file %s"), asmap_path));
return false;
}
- asmap = CAddrMan::DecodeAsmap(asmap_path);
+ asmap = DecodeAsmap(asmap_path);
if (asmap.size() == 0) {
InitError(strprintf(_("Could not parse asmap file %s"), asmap_path));
return false;
@@ -1200,20 +1201,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
LogPrintf("Using /16 prefix for IP bucketing\n");
}
- auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
- node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
-
- // Load addresses from peers.dat
uiInterface.InitMessage(_("Loading P2P addresses…").translated);
- int64_t nStart = GetTimeMillis();
- CAddrDB adb;
- if (adb.Read(*node.addrman)) {
- LogPrintf("Loaded %i addresses from peers.dat %dms\n", node.addrman->size(), GetTimeMillis() - nStart);
- } else {
- // Addrman can be in an inconsistent state after failure, reset it
- node.addrman = std::make_unique<CAddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
- LogPrintf("Recreating peers.dat\n");
- adb.Write(*node.addrman);
+ if (const auto error{LoadAddrman(asmap, args, node.addrman)}) {
+ return InitError(*error);
}
}
@@ -1577,6 +1567,10 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
+ if (const auto error{CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db))}) {
+ return InitError(*error);
+ }
+
g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false;
diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp
index 6b6157c139..fa56153745 100644
--- a/src/init/bitcoin-node.cpp
+++ b/src/init/bitcoin-node.cpp
@@ -2,9 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <interfaces/chain.h>
#include <interfaces/echo.h>
#include <interfaces/init.h>
#include <interfaces/ipc.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/system.h>
@@ -24,6 +27,12 @@ public:
m_node.args = &gArgs;
m_node.init = this;
}
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
interfaces::Ipc* ipc() override { return m_ipc.get(); }
NodeContext& m_node;
diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp
index 1d4504c24f..9c8d5bd9bb 100644
--- a/src/init/bitcoind.cpp
+++ b/src/init/bitcoind.cpp
@@ -2,7 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <interfaces/chain.h>
+#include <interfaces/echo.h>
#include <interfaces/init.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
#include <node/context.h>
#include <util/system.h>
@@ -18,6 +22,13 @@ public:
m_node.args = &gArgs;
m_node.init = this;
}
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
NodeContext& m_node;
};
} // namespace
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index eceede3c8f..9a97cad1f8 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -177,7 +177,7 @@ public:
std::string& err_string) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
- virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
+ virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) = 0;
//! Get the node's package limits.
//! Currently only returns the ancestor and descendant count limits, but could be enhanced to
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 77129423db..770b1b8753 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -230,7 +230,7 @@ public:
};
//! Return implementation of Node interface.
-std::unique_ptr<Node> MakeNode(NodeContext* context = nullptr);
+std::unique_ptr<Node> MakeNode(NodeContext& context);
//! Block tip (could be a header or not, depends on the subscribed signal).
struct BlockTip {
diff --git a/src/key.h b/src/key.h
index 92cbc1e899..9b94baa026 100644
--- a/src/key.h
+++ b/src/key.h
@@ -17,7 +17,6 @@
/**
- * secure_allocator is defined in allocators.h
* CPrivKey is a serialized private key, with all parameters included
* (SIZE bytes)
*/
diff --git a/src/logging/timer.h b/src/logging/timer.h
index 647e3fa30e..79627b1fe3 100644
--- a/src/logging/timer.h
+++ b/src/logging/timer.h
@@ -9,6 +9,7 @@
#include <logging.h>
#include <util/macros.h>
#include <util/time.h>
+#include <util/types.h>
#include <chrono>
#include <string>
@@ -58,23 +59,15 @@ public:
return strprintf("%s: %s", m_prefix, msg);
}
- if (std::is_same<TimeType, std::chrono::microseconds>::value) {
+ if constexpr (std::is_same<TimeType, std::chrono::microseconds>::value) {
return strprintf("%s: %s (%iμs)", m_prefix, msg, end_time.count());
+ } else if constexpr (std::is_same<TimeType, std::chrono::milliseconds>::value) {
+ return strprintf("%s: %s (%.2fms)", m_prefix, msg, end_time.count() * 0.001);
+ } else if constexpr (std::is_same<TimeType, std::chrono::seconds>::value) {
+ return strprintf("%s: %s (%.2fs)", m_prefix, msg, end_time.count() * 0.000001);
+ } else {
+ static_assert(ALWAYS_FALSE<TimeType>, "Error: unexpected time type");
}
-
- std::string units;
- float divisor = 1;
-
- if (std::is_same<TimeType, std::chrono::milliseconds>::value) {
- units = "ms";
- divisor = 1000.;
- } else if (std::is_same<TimeType, std::chrono::seconds>::value) {
- units = "s";
- divisor = 1000. * 1000.;
- }
-
- const float time_ms = end_time.count() / divisor;
- return strprintf("%s: %s (%.2f%s)", m_prefix, msg, time_ms, units);
}
private:
@@ -89,7 +82,6 @@ private:
//! Forwarded on to LogPrint if specified - has the effect of only
//! outputting the timing log when a particular debug= category is specified.
const BCLog::LogFlags m_log_category{};
-
};
} // namespace BCLog
diff --git a/src/miner.cpp b/src/miner.cpp
index 168ade5507..38c7b4b8cc 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -237,7 +237,7 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
if (fPrintPriority) {
- LogPrintf("fee %s txid %s\n",
+ LogPrintf("fee rate %s txid %s\n",
CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(),
iter->GetTx().GetHash().ToString());
}
diff --git a/src/net.cpp b/src/net.cpp
index 35376b89ac..cc8f4c4316 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -9,6 +9,7 @@
#include <net.h>
+#include <addrdb.h>
#include <banman.h>
#include <clientversion.h>
#include <compat.h>
@@ -24,6 +25,7 @@
#include <scheduler.h>
#include <util/sock.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <util/thread.h>
#include <util/trace.h>
#include <util/translation.h>
@@ -190,8 +192,8 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
static int GetnScore(const CService& addr)
{
LOCK(cs_mapLocalHost);
- if (mapLocalHost.count(addr) == 0) return 0;
- return mapLocalHost[addr].nScore;
+ const auto it = mapLocalHost.find(addr);
+ return (it != mapLocalHost.end()) ? it->second.nScore : 0;
}
// Is our peer's addrLocal potentially useful as an external IP source?
@@ -243,10 +245,10 @@ bool AddLocal(const CService& addr, int nScore)
{
LOCK(cs_mapLocalHost);
- bool fAlready = mapLocalHost.count(addr) > 0;
- LocalServiceInfo &info = mapLocalHost[addr];
- if (!fAlready || nScore >= info.nScore) {
- info.nScore = nScore + (fAlready ? 1 : 0);
+ const auto [it, is_newly_added] = mapLocalHost.emplace(addr, LocalServiceInfo());
+ LocalServiceInfo &info = it->second;
+ if (is_newly_added || nScore >= info.nScore) {
+ info.nScore = nScore + (is_newly_added ? 0 : 1);
info.nPort = addr.GetPort();
}
}
@@ -288,12 +290,10 @@ bool IsReachable(const CNetAddr &addr)
/** vote for a local address */
bool SeenLocal(const CService& addr)
{
- {
- LOCK(cs_mapLocalHost);
- if (mapLocalHost.count(addr) == 0)
- return false;
- mapLocalHost[addr].nScore++;
- }
+ LOCK(cs_mapLocalHost);
+ const auto it = mapLocalHost.find(addr);
+ if (it == mapLocalHost.end()) return false;
+ ++it->second.nScore;
return true;
}
@@ -552,14 +552,13 @@ Network CNode::ConnectedThroughNetwork() const
#undef X
#define X(name) stats.name = name
-void CNode::CopyStats(CNodeStats& stats, const std::vector<bool>& asmap)
+void CNode::CopyStats(CNodeStats& stats)
{
stats.nodeid = this->GetId();
X(nServices);
X(addr);
X(addrBind);
stats.m_network = ConnectedThroughNetwork();
- stats.m_mapped_as = addr.GetMappedAS(asmap);
if (m_tx_relay != nullptr) {
LOCK(m_tx_relay->cs_filter);
stats.fRelayTxes = m_tx_relay->fRelayTxes;
@@ -1747,8 +1746,7 @@ void CConnman::DumpAddresses()
{
int64_t nStart = GetTimeMillis();
- CAddrDB adb;
- adb.Write(addrman);
+ DumpPeerAddresses(::gArgs, addrman);
LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n",
addrman.size(), GetTimeMillis() - nStart);
@@ -2804,7 +2802,8 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
vstats.reserve(vNodes.size());
for (CNode* pnode : vNodes) {
vstats.emplace_back();
- pnode->CopyStats(vstats.back(), addrman.GetAsmap());
+ pnode->CopyStats(vstats.back());
+ vstats.back().m_mapped_as = pnode->addr.GetMappedAS(addrman.GetAsmap());
}
}
diff --git a/src/net.h b/src/net.h
index a884a4521d..0a72ca888d 100644
--- a/src/net.h
+++ b/src/net.h
@@ -6,7 +6,6 @@
#ifndef BITCOIN_NET_H
#define BITCOIN_NET_H
-#include <addrdb.h>
#include <addrman.h>
#include <amount.h>
#include <bloom.h>
@@ -652,7 +651,7 @@ public:
void CloseSocketDisconnect();
- void CopyStats(CNodeStats& stats, const std::vector<bool>& asmap);
+ void CopyStats(CNodeStats& stats);
ServiceFlags GetLocalServices() const
{
@@ -768,7 +767,6 @@ public:
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
- std::vector<bool> m_asmap;
bool m_i2p_accept_incoming;
};
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 3ad34e83ba..80655c61e7 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2909,13 +2909,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- // We won't accept tx inv's if we're in blocks-only mode, or this is a
+ // Reject tx INVs when the -blocksonly setting is enabled, or this is a
// block-relay-only peer
- bool fBlocksOnly = m_ignore_incoming_txs || (pfrom.m_tx_relay == nullptr);
+ bool reject_tx_invs{m_ignore_incoming_txs || (pfrom.m_tx_relay == nullptr)};
// Allow peers with relay permission to send data other than blocks in blocks only mode
if (pfrom.HasPermission(NetPermissionFlags::Relay)) {
- fBlocksOnly = false;
+ reject_tx_invs = false;
}
LOCK(cs_main);
@@ -2954,7 +2954,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
pfrom.AddKnownTx(inv.hash);
- if (fBlocksOnly) {
+ if (reject_tx_invs) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
pfrom.fDisconnect = true;
return;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index e7b3377475..b2f4945e3b 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -1242,8 +1242,3 @@ bool operator<(const CSubNet& a, const CSubNet& b)
{
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
}
-
-bool SanityCheckASMap(const std::vector<bool>& asmap)
-{
- return SanityCheckASMap(asmap, 128); // For IP address lookups, the input is 128 bits
-}
diff --git a/src/netaddress.h b/src/netaddress.h
index eb35ed3fac..cfb2edcd34 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -567,6 +567,4 @@ public:
}
};
-bool SanityCheckASMap(const std::vector<bool>& asmap);
-
#endif // BITCOIN_NETADDRESS_H
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 2980bdf459..64d17189a6 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -23,8 +23,6 @@
#ifndef WIN32
#include <fcntl.h>
-#else
-#include <codecvt>
#endif
#ifdef USE_POLL
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index 90f7ba191d..5ddcf95c84 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -394,18 +394,14 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
- FlatFilePos blockPos;
- {
- LOCK(cs_main);
- blockPos = pindex->GetBlockPos();
- }
+ const FlatFilePos block_pos{WITH_LOCK(cs_main, return pindex->GetBlockPos())};
- if (!ReadBlockFromDisk(block, blockPos, consensusParams)) {
+ if (!ReadBlockFromDisk(block, block_pos, consensusParams)) {
return false;
}
if (block.GetHash() != pindex->GetBlockHash()) {
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
- pindex->ToString(), pindex->GetBlockPos().ToString());
+ pindex->ToString(), block_pos.ToString());
}
return true;
}
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index b46ad0333e..d7860f0115 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -72,7 +72,7 @@ class NodeImpl : public Node
private:
ChainstateManager& chainman() { return *Assert(m_context->chainman); }
public:
- explicit NodeImpl(NodeContext* context) { setContext(context); }
+ explicit NodeImpl(NodeContext& context) { setContext(&context); }
void initLogging() override { InitLogging(*Assert(m_context->args)); }
void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); }
bilingual_str getWarnings() override { return GetWarnings(true); }
@@ -575,11 +575,11 @@ public:
// that Chain clients do not need to know about.
return TransactionError::OK == err;
}
- void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
+ void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize, CAmount* ancestorfees) override
{
ancestors = descendants = 0;
if (!m_node.mempool) return;
- m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants);
+ m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants, ancestorsize, ancestorfees);
}
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
{
@@ -710,6 +710,6 @@ public:
} // namespace node
namespace interfaces {
-std::unique_ptr<Node> MakeNode(NodeContext* context) { return std::make_unique<node::NodeImpl>(context); }
+std::unique_ptr<Node> MakeNode(NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
std::unique_ptr<Chain> MakeChain(NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
} // namespace interfaces
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 43624c7993..15527afb8a 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -13,7 +13,7 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
AssertLockHeld(pool.cs);
- CTxMemPool::setEntries setAncestors;
+ CTxMemPool::setEntries ancestors;
// First check the transaction itself.
if (SignalsOptInRBF(tx)) {
@@ -31,9 +31,9 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
uint64_t noLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
- pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
+ pool.CalculateMemPoolAncestors(entry, ancestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
- for (CTxMemPool::txiter it : setAncestors) {
+ for (CTxMemPool::txiter it : ancestors) {
if (SignalsOptInRBF(it->GetTx())) {
return RBFTransactionState::REPLACEABLE_BIP125;
}
@@ -47,33 +47,127 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
}
-bool GetEntriesForConflicts(const CTransaction& tx,
- CTxMemPool& m_pool,
- const CTxMemPool::setEntries& setIterConflicting,
- CTxMemPool::setEntries& allConflicting,
- std::string& err_string)
+std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
+ CTxMemPool& pool,
+ const CTxMemPool::setEntries& iters_conflicting,
+ CTxMemPool::setEntries& all_conflicts)
{
- AssertLockHeld(m_pool.cs);
- const uint256 hash = tx.GetHash();
+ AssertLockHeld(pool.cs);
+ const uint256 txid = tx.GetHash();
uint64_t nConflictingCount = 0;
- for (const auto& mi : setIterConflicting) {
+ for (const auto& mi : iters_conflicting) {
nConflictingCount += mi->GetCountWithDescendants();
- // This potentially overestimates the number of actual descendants
- // but we just want to be conservative to avoid doing too much
- // work.
+ // This potentially overestimates the number of actual descendants but we just want to be
+ // conservative to avoid doing too much work.
if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) {
- err_string = strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
- hash.ToString(),
- nConflictingCount,
- MAX_BIP125_REPLACEMENT_CANDIDATES);
- return false;
+ return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
+ txid.ToString(),
+ nConflictingCount,
+ MAX_BIP125_REPLACEMENT_CANDIDATES);
}
}
// If not too many to replace, then calculate the set of
// transactions that would have to be evicted
- for (CTxMemPool::txiter it : setIterConflicting) {
- m_pool.CalculateDescendants(it, allConflicting);
+ for (CTxMemPool::txiter it : iters_conflicting) {
+ pool.CalculateDescendants(it, all_conflicts);
}
- return true;
+ return std::nullopt;
}
+std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
+ const CTxMemPool& pool,
+ const CTxMemPool::setEntries& iters_conflicting)
+{
+ AssertLockHeld(pool.cs);
+ std::set<uint256> parents_of_conflicts;
+ for (const auto& mi : iters_conflicting) {
+ for (const CTxIn &txin : mi->GetTx().vin) {
+ parents_of_conflicts.insert(txin.prevout.hash);
+ }
+ }
+
+ for (unsigned int j = 0; j < tx.vin.size(); j++) {
+ // We don't want to accept replacements that require low feerate junk to be mined first.
+ // Ideally we'd keep track of the ancestor feerates and make the decision based on that, but
+ // for now requiring all new inputs to be confirmed works.
+ //
+ // Note that if you relax this to make RBF a little more useful, this may break the
+ // CalculateMempoolAncestors RBF relaxation, above. See the comment above the first
+ // CalculateMempoolAncestors call for more info.
+ if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
+ // Rather than check the UTXO set - potentially expensive - it's cheaper to just check
+ // if the new input refers to a tx that's in the mempool.
+ if (pool.exists(tx.vin[j].prevout.hash)) {
+ return strprintf("replacement %s adds unconfirmed input, idx %d",
+ tx.GetHash().ToString(), j);
+ }
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
+ const std::set<uint256>& direct_conflicts,
+ const uint256& txid)
+{
+ for (CTxMemPool::txiter ancestorIt : ancestors) {
+ const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
+ if (direct_conflicts.count(hashAncestor)) {
+ return strprintf("%s spends conflicting transaction %s",
+ txid.ToString(),
+ hashAncestor.ToString());
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
+ CFeeRate replacement_feerate,
+ const uint256& txid)
+{
+ for (const auto& mi : iters_conflicting) {
+ // Don't allow the replacement to reduce the feerate of the mempool.
+ //
+ // We usually don't want to accept replacements with lower feerates than what they replaced
+ // as that would lower the feerate of the next block. Requiring that the feerate always be
+ // increased is also an easy-to-reason about way to prevent DoS attacks via replacements.
+ //
+ // We only consider the feerates of transactions being directly replaced, not their indirect
+ // descendants. While that does mean high feerate children are ignored when deciding whether
+ // or not to replace, we do require the replacement to pay more overall fees too, mitigating
+ // most cases.
+ CFeeRate original_feerate(mi->GetModifiedFee(), mi->GetTxSize());
+ if (replacement_feerate <= original_feerate) {
+ return strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
+ txid.ToString(),
+ replacement_feerate.ToString(),
+ original_feerate.ToString());
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::string> PaysForRBF(CAmount original_fees,
+ CAmount replacement_fees,
+ size_t replacement_vsize,
+ const uint256& txid)
+{
+ // The replacement must pay greater fees than the transactions it
+ // replaces - if we did the bandwidth used by those conflicting
+ // transactions would not be paid for.
+ if (replacement_fees < original_fees) {
+ return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
+ txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
+ }
+
+ // Finally in addition to paying more fees than the conflicts the
+ // new transaction must pay for its own bandwidth.
+ CAmount additional_fees = replacement_fees - original_fees;
+ if (additional_fees < ::incrementalRelayFee.GetFee(replacement_vsize)) {
+ return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
+ txid.ToString(),
+ FormatMoney(additional_fees),
+ FormatMoney(::incrementalRelayFee.GetFee(replacement_vsize)));
+ }
+ return std::nullopt;
+}
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index a67e9058df..56468a09b2 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -35,19 +35,61 @@ enum class RBFTransactionState {
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
-/** Get all descendants of setIterConflicting. Also enforce BIP125 Rule #5, "The number of original
+/** Get all descendants of iters_conflicting. Also enforce BIP125 Rule #5, "The number of original
* transactions to be replaced and their descendant transactions which will be evicted from the
* mempool must not exceed a total of 100 transactions." Quit as early as possible. There cannot be
* more than MAX_BIP125_REPLACEMENT_CANDIDATES potential entries.
- * @param[in] setIterConflicting The set of iterators to mempool entries.
- * @param[out] err_string Used to return errors, if any.
- * @param[out] allConflicting Populated with all the mempool entries that would be replaced,
- * which includes descendants of setIterConflicting. Not cleared at
+ * @param[in] iters_conflicting The set of iterators to mempool entries.
+ * @param[out] all_conflicts Populated with all the mempool entries that would be replaced,
+ * which includes descendants of iters_conflicting. Not cleared at
* the start; any existing mempool entries will remain in the set.
- * @returns false if Rule 5 is broken.
+ * @returns an error message if Rule #5 is broken, otherwise a std::nullopt.
*/
-bool GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& m_pool,
- const CTxMemPool::setEntries& setIterConflicting,
- CTxMemPool::setEntries& allConflicting,
- std::string& err_string) EXCLUSIVE_LOCKS_REQUIRED(m_pool.cs);
+std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
+ const CTxMemPool::setEntries& iters_conflicting,
+ CTxMemPool::setEntries& all_conflicts)
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+
+/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input
+ * was included in one of the original transactions."
+ * @returns error message if Rule #2 is broken, otherwise std::nullopt. */
+std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool,
+ const CTxMemPool::setEntries& iters_conflicting)
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+
+/** Check the intersection between two sets of transactions (a set of mempool entries and a set of
+ * txids) to make sure they are disjoint.
+ * @param[in] ancestors Set of mempool entries corresponding to ancestors of the
+ * replacement transactions.
+ * @param[in] direct_conflicts Set of txids corresponding to the mempool conflicts
+ * (candidates to be replaced).
+ * @param[in] txid Transaction ID, included in the error message if violation occurs.
+ * @returns error message if the sets intersect, std::nullopt if they are disjoint.
+ */
+std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
+ const std::set<uint256>& direct_conflicts,
+ const uint256& txid);
+
+/** Check that the feerate of the replacement transaction(s) is higher than the feerate of each
+ * of the transactions in iters_conflicting.
+ * @param[in] iters_conflicting The set of mempool entries.
+ * @returns error message if fees insufficient, otherwise std::nullopt.
+ */
+std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
+ CFeeRate replacement_feerate, const uint256& txid);
+
+/** Enforce BIP125 Rule #3 "The replacement transaction pays an absolute fee of at least the sum
+ * paid by the original transactions." Enforce BIP125 Rule #4 "The replacement transaction must also
+ * pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting."
+ * @param[in] original_fees Total modified fees of original transaction(s).
+ * @param[in] replacement_fees Total modified fees of replacement transaction(s).
+ * @param[in] replacement_vsize Total virtual size of replacement transaction(s).
+ * @param[in] txid Transaction ID, included in the error message if violation occurs.
+ * @returns error string if fees are insufficient, otherwise std::nullopt.
+ */
+std::optional<std::string> PaysForRBF(CAmount original_fees,
+ CAmount replacement_fees,
+ size_t replacement_vsize,
+ const uint256& txid);
+
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index c31f0aceea..e78594390b 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -282,7 +282,7 @@ void AddressBookPage::on_exportButton_clicked()
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Address List"), QString(),
/*: Expanded name of the CSV file format.
- See https://en.wikipedia.org/wiki/Comma-separated_values */
+ See: https://en.wikipedia.org/wiki/Comma-separated_values. */
tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h
index 57f559fc14..4b5b38e43f 100644
--- a/src/qt/bantablemodel.h
+++ b/src/qt/bantablemodel.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_QT_BANTABLEMODEL_H
#define BITCOIN_QT_BANTABLEMODEL_H
+#include <addrdb.h>
#include <net.h>
#include <memory>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index f6ea147ddb..d4895ea6ff 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -11,6 +11,7 @@
#include <chainparams.h>
#include <init.h>
#include <interfaces/handler.h>
+#include <interfaces/init.h>
#include <interfaces/node.h>
#include <node/context.h>
#include <node/ui_interface.h>
@@ -275,10 +276,10 @@ void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
connect(this, &BitcoinApplication::requestedShutdown, m_splash, &QWidget::close);
}
-void BitcoinApplication::setNode(interfaces::Node& node)
+void BitcoinApplication::createNode(interfaces::Init& init)
{
assert(!m_node);
- m_node = &node;
+ m_node = init.makeNode();
if (optionsModel) optionsModel->setNode(*m_node);
if (m_splash) m_splash->setNode(*m_node);
}
@@ -460,11 +461,13 @@ int GuiMain(int argc, char* argv[])
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
- SetupEnvironment();
- util::ThreadSetInternalName("main");
NodeContext node_context;
- std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
+ int unused_exit_status;
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+
+ SetupEnvironment();
+ util::ThreadSetInternalName("main");
// Subscribe to global signals from core
boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
@@ -492,7 +495,6 @@ int GuiMain(int argc, char* argv[])
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- node_context.args = &gArgs;
SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
std::string error;
@@ -623,7 +625,7 @@ int GuiMain(int argc, char* argv[])
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
- app.setNode(*node);
+ app.createNode(*init);
int rv = EXIT_SUCCESS;
try
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index ed2f26b7f3..602b76052c 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -27,6 +27,9 @@ class PlatformStyle;
class SplashScreen;
class WalletController;
class WalletModel;
+namespace interfaces {
+class Init;
+} // namespace interfaces
/** Main Bitcoin application object */
@@ -51,6 +54,8 @@ public:
void createWindow(const NetworkStyle *networkStyle);
/// Create splash screen
void createSplashScreen(const NetworkStyle *networkStyle);
+ /// Create or spawn node
+ void createNode(interfaces::Init& init);
/// Basic initialization, before starting initialization/shutdown thread. Return true on success.
bool baseInitialize();
@@ -69,7 +74,6 @@ public:
void setupPlatformStyle();
interfaces::Node& node() const { assert(m_node); return *m_node; }
- void setNode(interfaces::Node& node);
public Q_SLOTS:
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
@@ -103,7 +107,7 @@ private:
const PlatformStyle *platformStyle;
std::unique_ptr<QWidget> shutdownWindow;
SplashScreen* m_splash = nullptr;
- interfaces::Node* m_node = nullptr;
+ std::unique_ptr<interfaces::Node> m_node;
void startThread();
};
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 2ff1445709..59d220636d 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -104,6 +104,9 @@
<layout class="QHBoxLayout" name="horizontalLayout_2_Main">
<item>
<widget class="QLabel" name="databaseCacheLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for Options window setting that sets the size of the database cache. Explains the corresponding effects of increasing/decreasing this value.">Maximum database cache size. A larger cache can contribute to faster sync, after which the benefit is less pronounced for most use cases. Lowering the cache size will reduce memory usage. Unused mempool memory is shared for this cache.</string>
+ </property>
<property name="text">
<string>Size of &amp;database cache</string>
</property>
@@ -147,6 +150,9 @@
<layout class="QHBoxLayout" name="horizontalLayout_Main_VerifyLabel">
<item>
<widget class="QLabel" name="threadsScriptVerifLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for Options window setting that sets the number of script verification threads. Explains that negative values mean to leave these many cores free to the system.">Set the number of script verification threads. Negative values correspond to the number of cores you want to leave free to the system.</string>
+ </property>
<property name="text">
<string>Number of script &amp;verification threads</string>
</property>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 5ad4fc9b33..92644ef24b 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -296,10 +296,22 @@ void OptionsDialog::on_resetButton_clicked()
void OptionsDialog::on_openBitcoinConfButton_clicked()
{
- /* explain the purpose of the config file */
- QMessageBox::information(this, tr("Configuration options"),
- tr("The configuration file is used to specify advanced user options which override GUI settings. "
- "Additionally, any command-line options will override this configuration file."));
+ QMessageBox config_msgbox(this);
+ config_msgbox.setIcon(QMessageBox::Information);
+ //: Window title text of pop-up box that allows opening up of configuration file.
+ config_msgbox.setWindowTitle(tr("Configuration options"));
+ /*: Explanatory text about the priority order of instructions considered by client.
+ The order from high to low being: command-line, configuration file, GUI settings. */
+ config_msgbox.setText(tr("The configuration file is used to specify advanced user options which override GUI settings. "
+ "Additionally, any command-line options will override this configuration file."));
+
+ QPushButton* open_button = config_msgbox.addButton(tr("Continue"), QMessageBox::ActionRole);
+ config_msgbox.addButton(tr("Cancel"), QMessageBox::RejectRole);
+ open_button->setDefault(true);
+
+ config_msgbox.exec();
+
+ if (config_msgbox.clickedButton() != open_button) return;
/* show an error if there was some problem opening the file */
if (!GUIUtil::openBitcoinConf())
diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp
index 7cdd568644..0799e01aac 100644
--- a/src/qt/qrimagewidget.cpp
+++ b/src/qt/qrimagewidget.cpp
@@ -119,7 +119,7 @@ void QRImageWidget::saveImage()
QString fn = GUIUtil::getSaveFileName(
this, tr("Save QR Code"), QString(),
/*: Expanded name of the PNG file format.
- See https://en.wikipedia.org/wiki/Portable_Network_Graphics */
+ See: https://en.wikipedia.org/wiki/Portable_Network_Graphics. */
tr("PNG Image") + QLatin1String(" (*.png)"), nullptr);
if (!fn.isEmpty())
{
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 829f7add80..4554f11a41 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -680,6 +680,11 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
// create peer table context menu
peersTableContextMenu = new QMenu(this);
+ //: Context menu action to copy the address of a peer.
+ peersTableContextMenu->addAction(tr("&Copy address"), [this] {
+ GUIUtil::copyEntryData(ui->peerWidget, PeerTableModel::Address, Qt::DisplayRole);
+ });
+ peersTableContextMenu->addSeparator();
peersTableContextMenu->addAction(tr("&Disconnect"), this, &RPCConsole::disconnectSelectedNode);
peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 &hour"), [this] { banSelectedNode(60 * 60); });
peersTableContextMenu->addAction(ts.ban_for + " " + tr("1 d&ay"), [this] { banSelectedNode(60 * 60 * 24); });
@@ -708,7 +713,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
banTableContextMenu = new QMenu(this);
/*: Context menu action to copy the IP/Netmask of a banned peer.
IP/Netmask is the combination of a peer's IP address and its Netmask.
- For IP address see: https://en.wikipedia.org/wiki/IP_address */
+ For IP address, see: https://en.wikipedia.org/wiki/IP_address. */
banTableContextMenu->addAction(tr("&Copy IP/Netmask"), [this] {
GUIUtil::copyEntryData(ui->banlistWidget, BanTableModel::Address, Qt::DisplayRole);
});
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index c9bf757dfc..ff53d8160f 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -200,7 +200,7 @@ void SendCoinsDialog::setModel(WalletModel *_model)
ui->optInRBF->setCheckState(Qt::Checked);
if (model->wallet().hasExternalSigner()) {
- //: "device" usually means a hardware wallet
+ //: "device" usually means a hardware wallet.
ui->sendButton->setText(tr("Sign on device"));
if (gArgs.GetArg("-signer", "") != "") {
ui->sendButton->setEnabled(true);
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 022f367422..0de781661a 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -60,6 +60,8 @@ void EditAddressAndSubmit(
void TestAddAddressesToSendBook(interfaces::Node& node)
{
TestChain100Setup test;
+ auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args));
+ test.m_node.wallet_client = wallet_client.get();
node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
@@ -112,7 +114,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
WalletContext& context = *node.walletClient().context();
AddWallet(context, wallet);
WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
editAddressDialog.setModel(walletModel.getAddressTableModel());
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 7d66f67f8a..884ed25637 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -6,6 +6,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <interfaces/init.h>
#include <interfaces/node.h>
#include <qt/bitcoin.h>
#include <qt/initexecutor.h>
@@ -53,7 +54,8 @@ int main(int argc, char* argv[])
}
NodeContext node_context;
- std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
+ int unused_exit_status;
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
gArgs.ForceSetArg("-listen", "0");
gArgs.ForceSetArg("-listenonion", "0");
gArgs.ForceSetArg("-discover", "0");
@@ -76,10 +78,9 @@ int main(int argc, char* argv[])
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
BitcoinApplication app;
- app.setNode(*node);
app.setApplicationName("Bitcoin-Qt-test");
+ app.createNode(*init);
- app.node().context()->args = &gArgs; // Make gArgs available in the NodeContext
AppTests app_tests(app);
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 1976bee74b..62b135d3f1 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -138,6 +138,8 @@ void TestGUI(interfaces::Node& node)
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
+ auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args));
+ test.m_node.wallet_client = wallet_client.get();
node.setContext(&test.m_node);
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->LoadWallet();
@@ -167,7 +169,7 @@ void TestGUI(interfaces::Node& node)
WalletContext& context = *node.walletClient().context();
AddWallet(context, wallet);
WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 908cb917f1..2f16e6edb4 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -353,7 +353,7 @@ void TransactionView::exportClicked()
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Transaction History"), QString(),
/*: Expanded name of the CSV file format.
- See https://en.wikipedia.org/wiki/Comma-separated_values */
+ See: https://en.wikipedia.org/wiki/Comma-separated_values. */
tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index f694fbecb5..5eeb2d5308 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -11,6 +11,7 @@
#include <qt/psbtoperationsdialog.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
+#include <util/system.h>
#include <cassert>
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index e5804211f3..14b0e5a984 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -670,8 +670,9 @@ static RPCHelpMan echoipc()
RPCExamples{HelpExampleCli("echo", "\"Hello world\"") +
HelpExampleRpc("echo", "\"Hello world\"")},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ interfaces::Init& local_init = *EnsureAnyNodeContext(request.context).init;
std::unique_ptr<interfaces::Echo> echo;
- if (interfaces::Ipc* ipc = Assert(EnsureAnyNodeContext(request.context).init)->ipc()) {
+ if (interfaces::Ipc* ipc = local_init.ipc()) {
// Spawn a new bitcoin-node process and call makeEcho to get a
// client pointer to a interfaces::Echo instance running in
// that process. This is just for testing. A slightly more
@@ -689,7 +690,7 @@ static RPCHelpMan echoipc()
// interfaces::Echo object and return it so the `echoipc` RPC
// method will work, and the python test calling `echoipc`
// can expect the same result.
- echo = interfaces::MakeEcho();
+ echo = local_init.makeEcho();
}
return echo->echo(request.params[0].get_str());
},
diff --git a/src/signet.cpp b/src/signet.cpp
index 1ba8502287..aafd1999ee 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -141,7 +141,7 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
PrecomputedTransactionData txdata;
txdata.Init(signet_txs->m_to_sign, {signet_txs->m_to_spend.vout[0]});
- TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs->m_to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
+ TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /* nInIn= */ 0, /* amountIn= */ signet_txs->m_to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
if (!VerifyScript(scriptSig, signet_txs->m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
diff --git a/src/sync.cpp b/src/sync.cpp
index eace86d9dd..98e6d3d65d 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -9,7 +9,6 @@
#include <sync.h>
#include <logging.h>
-#include <logging/timer.h>
#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/threadnames.h>
@@ -24,11 +23,6 @@
#include <utility>
#include <vector>
-void LockContention(const char* pszName, const char* pszFile, int nLine)
-{
- LOG_TIME_MICROS_WITH_CATEGORY(strprintf("%s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
-}
-
#ifdef DEBUG_LOCKORDER
//
// Early deadlock detection.
diff --git a/src/sync.h b/src/sync.h
index bf15c0b4eb..6ba63d5e4d 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_SYNC_H
#define BITCOIN_SYNC_H
+#include <logging.h>
+#include <logging/timer.h>
#include <threadsafety.h>
#include <util/macros.h>
@@ -126,9 +128,6 @@ using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
/** Wrapped mutex: supports waiting but not recursive locking */
typedef AnnotatedMixin<std::mutex> Mutex;
-/** Prints a lock contention to the log */
-void LockContention(const char* pszName, const char* pszFile, int nLine);
-
/** Wrapper around std::unique_lock style lock for Mutex. */
template <typename Mutex, typename Base = typename Mutex::UniqueLock>
class SCOPED_LOCKABLE UniqueLock : public Base
@@ -138,7 +137,7 @@ private:
{
EnterCritical(pszName, pszFile, nLine, Base::mutex());
if (Base::try_lock()) return;
- LockContention(pszName, pszFile, nLine); // log the contention
+ LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
Base::lock();
}
diff --git a/src/test/README.md b/src/test/README.md
index 57cda26d7c..d03411c3ed 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -74,3 +74,29 @@ start debugging, just like you would with any other program:
```bash
gdb src/test/test_bitcoin
```
+
+#### Segmentation faults
+
+If you hit a segmentation fault during a test run, you can diagnose where the fault
+is happening by running `gdb ./src/test/test_bitcoin` and then using the `bt` command
+within gdb.
+
+Another tool that can be used to resolve segmentation faults is
+[valgrind](https://valgrind.org/).
+
+If for whatever reason you want to produce a core dump file for this fault, you can do
+that as well. By default, the boost test runner will intercept system errors and not
+produce a core file. To bypass this, add `--catch_system_errors=no` to the
+`test_bitcoin` arguments and ensure that your ulimits are set properly (e.g. `ulimit -c
+unlimited`).
+
+Running the tests and hitting a segmentation fault should now produce a file called `core`
+(on Linux platforms, the file name will likely depend on the contents of
+`/proc/sys/kernel/core_pattern`).
+
+You can then explore the core dump using
+``` bash
+gdb src/test/test_bitcoin core
+
+(gbd) bt # produce a backtrace for where a segfault occurred
+```
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index e1b5df9502..01a492a20b 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -5,13 +5,14 @@
#include <addrdb.h>
#include <addrman.h>
#include <chainparams.h>
+#include <clientversion.h>
+#include <hash.h>
+#include <netbase.h>
+#include <random.h>
#include <test/data/asmap.raw.h>
#include <test/util/setup_common.h>
#include <util/asmap.h>
#include <util/string.h>
-#include <hash.h>
-#include <netbase.h>
-#include <random.h>
#include <boost/test/unit_test.hpp>
@@ -1002,7 +1003,7 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks)
BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0");
}
-BOOST_AUTO_TEST_CASE(caddrdb_read)
+BOOST_AUTO_TEST_CASE(load_addrman)
{
CAddrManUncorrupted addrmanUncorrupted;
@@ -1037,17 +1038,17 @@ BOOST_AUTO_TEST_CASE(caddrdb_read)
BOOST_CHECK(addrman1.size() == 3);
BOOST_CHECK(exceptionThrown == false);
- // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
+ // Test that ReadFromStream creates an addrman with the correct number of addrs.
CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(CAddrDB::Read(addrman2, ssPeers2));
+ ReadFromStream(addrman2, ssPeers2);
BOOST_CHECK(addrman2.size() == 3);
}
-BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
+BOOST_AUTO_TEST_CASE(load_addrman_corrupted)
{
CAddrManCorrupted addrmanCorrupted;
@@ -1063,16 +1064,16 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
} catch (const std::exception&) {
exceptionThrown = true;
}
- // Even through de-serialization failed addrman is not left in a clean state.
+ // Even though de-serialization failed addrman is not left in a clean state.
BOOST_CHECK(addrman1.size() == 1);
BOOST_CHECK(exceptionThrown);
- // Test that CAddrDB::Read fails if peers.dat is corrupt
+ // Test that ReadFromStream fails if peers.dat is corrupt
CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);
CAddrMan addrman2(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 100);
BOOST_CHECK(addrman2.size() == 0);
- BOOST_CHECK(!CAddrDB::Read(addrman2, ssPeers2));
+ BOOST_CHECK_THROW(ReadFromStream(addrman2, ssPeers2), std::ios_base::failure);
}
diff --git a/src/test/data/README.md b/src/test/data/README.md
index 2463daa42a..a05d9c668b 100644
--- a/src/test/data/README.md
+++ b/src/test/data/README.md
@@ -8,5 +8,5 @@ License
The data files in this directory are distributed under the MIT software
license, see the accompanying file COPYING or
-http://www.opensource.org/licenses/mit-license.php.
+https://www.opensource.org/licenses/mit-license.php.
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index e95126a80f..95c5a99c1b 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -85,7 +85,7 @@ public:
// 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
const size_t n = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
- const size_t num_sources = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(10, 50);
+ const size_t num_sources = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
CNetAddr prev_source;
// Use insecure_rand inside the loops instead of m_fuzzed_data_provider because when
// the latter is exhausted it just returns 0.
@@ -96,31 +96,12 @@ public:
for (size_t j = 0; j < num_addresses; ++j) {
const auto addr = CAddress{CService{RandAddr(), 8333}, NODE_NETWORK};
const auto time_penalty = insecure_rand.randrange(100000001);
-#if 1
- // 2.83 sec to fill.
- if (n > 0 && mapInfo.size() % n == 0 && mapAddr.find(addr) == mapAddr.end()) {
- // Add to the "tried" table (if the bucket slot is free).
- const CAddrInfo dummy{addr, source};
- const int bucket = dummy.GetTriedBucket(nKey, m_asmap);
- const int bucket_pos = dummy.GetBucketPosition(nKey, false, bucket);
- if (vvTried[bucket][bucket_pos] == -1) {
- int id;
- CAddrInfo* addr_info = Create(addr, source, &id);
- vvTried[bucket][bucket_pos] = id;
- addr_info->fInTried = true;
- ++nTried;
- }
- } else {
- // Add to the "new" table.
- Add_(addr, source, time_penalty);
- }
-#else
- // 261.91 sec to fill.
Add_(addr, source, time_penalty);
+
if (n > 0 && mapInfo.size() % n == 0) {
Good_(addr, false, GetTime());
}
-#endif
+
// Add 10% of the addresses from more than one source.
if (insecure_rand.randrange(10) == 0 && prev_source.IsValid()) {
Add_(addr, prev_source, time_penalty);
@@ -221,7 +202,7 @@ public:
[[nodiscard]] inline std::vector<bool> ConsumeAsmap(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
- if (!SanityCheckASMap(asmap)) asmap.clear();
+ if (!SanityCheckASMap(asmap, 128)) asmap.clear();
return asmap;
}
diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp
index 4c5bc0cbf2..d402f8632c 100644
--- a/src/test/fuzz/asmap.cpp
+++ b/src/test/fuzz/asmap.cpp
@@ -4,6 +4,7 @@
#include <netaddress.h>
#include <test/fuzz/fuzz.h>
+#include <util/asmap.h>
#include <cstdint>
#include <vector>
@@ -42,7 +43,7 @@ FUZZ_TARGET(asmap)
asmap.push_back((buffer[1 + i] >> j) & 1);
}
}
- if (!SanityCheckASMap(asmap)) return;
+ if (!SanityCheckASMap(asmap, 128)) return;
const uint8_t* addr_data = buffer.data() + 1 + asmap_size;
CNetAddr net_addr;
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
index 8178878c30..323090e041 100644
--- a/src/test/fuzz/data_stream.cpp
+++ b/src/test/fuzz/data_stream.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <addrdb.h>
#include <addrman.h>
#include <net.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -22,5 +23,8 @@ FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- CAddrDB::Read(addr_man, data_stream);
+ try {
+ ReadFromStream(addr_man, data_stream);
+ } catch (const std::exception&) {
+ }
}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 5d26529837..5a732aeeff 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -23,6 +23,7 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <uint256.h>
+#include <univalue.h>
#include <util/check.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index ff0259c182..bd1bb79d0e 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -14,6 +14,7 @@
#include <test/fuzz/util.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
+#include <util/asmap.h>
#include <cstdint>
#include <optional>
@@ -38,12 +39,8 @@ FUZZ_TARGET_INIT(net, initialize_net)
node.CloseSocketDisconnect();
},
[&] {
- const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
- if (!SanityCheckASMap(asmap)) {
- return;
- }
CNodeStats stats;
- node.CopyStats(stats, asmap);
+ node.CopyStats(stats);
},
[&] {
const CNode* add_ref_node = node.AddRef();
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index 9186821836..73a7d24971 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -6,6 +6,7 @@
#include <chainparams.h>
#include <consensus/params.h>
#include <primitives/block.h>
+#include <util/system.h>
#include <versionbits.h>
#include <test/fuzz/FuzzedDataProvider.h>
diff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp
index e2e31c62d7..84ddbc50c6 100644
--- a/src/test/logging_tests.cpp
+++ b/src/test/logging_tests.cpp
@@ -15,9 +15,9 @@ BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(logging_timer)
{
SetMockTime(1);
- auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
+ auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
SetMockTime(2);
- BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
+ BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000μs)");
SetMockTime(1);
auto ms_timer = BCLog::Timer<std::chrono::milliseconds>("tests", "end_msg");
@@ -25,9 +25,9 @@ BOOST_AUTO_TEST_CASE(logging_timer)
BOOST_CHECK_EQUAL(ms_timer.LogMsg("test ms"), "tests: test ms (1000.00ms)");
SetMockTime(1);
- auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
+ auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
SetMockTime(2);
- BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000μs)");
+ BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index ba6b3e32ea..cabc4b3b49 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -114,7 +114,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
InitSignatureCache();
InitScriptExecutionCache();
m_node.chain = interfaces::MakeChain(m_node);
- g_wallet_init_interface.Construct(m_node);
fCheckBlockIndex = true;
static bool noui_connected = false;
if (!noui_connected) {
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 4b76bee5ab..cfa864668a 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -5,6 +5,7 @@
#include <txdb.h>
+#include <chain.h>
#include <node/ui_interface.h>
#include <pow.h>
#include <random.h>
@@ -27,6 +28,28 @@ static constexpr uint8_t DB_FLAG{'F'};
static constexpr uint8_t DB_REINDEX_FLAG{'R'};
static constexpr uint8_t DB_LAST_BLOCK{'l'};
+// Keys used in previous version that might still be found in the DB:
+static constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
+// uint8_t DB_TXINDEX{'t'}
+
+std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db)
+{
+ CBlockLocator ignored{};
+ if (block_tree_db.Read(DB_TXINDEX_BLOCK, ignored)) {
+ return _("The -txindex upgrade started by a previous version can not be completed. Restart with the previous version or run a full -reindex.");
+ }
+ bool txindex_legacy_flag{false};
+ block_tree_db.ReadFlag("txindex", txindex_legacy_flag);
+ if (txindex_legacy_flag) {
+ // Disable legacy txindex and warn once about occupied disk space
+ if (!block_tree_db.WriteFlag("txindex", false)) {
+ return Untranslated("Failed to write block index db flag 'txindex'='0'");
+ }
+ return _("The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.");
+ }
+ return std::nullopt;
+}
+
namespace {
struct CoinEntry {
diff --git a/src/txdb.h b/src/txdb.h
index 845d80788f..1bdce71126 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -8,17 +8,20 @@
#include <coins.h>
#include <dbwrapper.h>
-#include <chain.h>
-#include <primitives/block.h>
#include <memory>
+#include <optional>
#include <string>
#include <utility>
#include <vector>
+class CBlockFileInfo;
class CBlockIndex;
-class CCoinsViewDBCursor;
class uint256;
+namespace Consensus {
+struct Params;
+};
+struct bilingual_str;
//! -dbcache default (MiB)
static const int64_t nDefaultDbCache = 450;
@@ -86,4 +89,6 @@ public:
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
};
+std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
+
#endif // BITCOIN_TXDB_H
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index d5a888ac67..3cf62f3c0e 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -924,7 +924,7 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
++nTransactionsUpdated;
}
}
- LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
+ LogPrintf("PrioritiseTransaction: %s fee += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
}
void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const
@@ -1174,12 +1174,14 @@ uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
return maximum;
}
-void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const {
+void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* const ancestorsize, CAmount* const ancestorfees) const {
LOCK(cs);
auto it = mapTx.find(txid);
ancestors = descendants = 0;
if (it != mapTx.end()) {
ancestors = it->GetCountWithAncestors();
+ if (ancestorsize) *ancestorsize = it->GetSizeWithAncestors();
+ if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors();
descendants = CalculateDescendantMaximum(it);
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 0a84a6e6b1..d1308aeeed 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -748,8 +748,10 @@ public:
/**
* Calculate the ancestor and descendant count for the given transaction.
* The counts include the transaction itself.
+ * When ancestors is non-zero (ie, the transaction itself is in the mempool),
+ * ancestorsize and ancestorfees will also be set to the appropriate values.
*/
- void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
+ void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) const;
/** @returns true if the mempool is fully loaded */
bool IsLoaded() const;
diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp
index bacc3690a2..5695c62012 100644
--- a/src/util/asmap.cpp
+++ b/src/util/asmap.cpp
@@ -2,10 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/asmap.h>
+
+#include <clientversion.h>
+#include <crypto/common.h>
+#include <logging.h>
+#include <streams.h>
+
+#include <cassert>
#include <map>
#include <vector>
-#include <assert.h>
-#include <crypto/common.h>
namespace {
@@ -183,3 +189,31 @@ bool SanityCheckASMap(const std::vector<bool>& asmap, int bits)
}
return false; // Reached EOF without RETURN instruction
}
+
+std::vector<bool> DecodeAsmap(fs::path path)
+{
+ std::vector<bool> bits;
+ FILE *filestr = fsbridge::fopen(path, "rb");
+ CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ LogPrintf("Failed to open asmap file from disk\n");
+ return bits;
+ }
+ fseek(filestr, 0, SEEK_END);
+ int length = ftell(filestr);
+ LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
+ fseek(filestr, 0, SEEK_SET);
+ uint8_t cur_byte;
+ for (int i = 0; i < length; ++i) {
+ file >> cur_byte;
+ for (int bit = 0; bit < 8; ++bit) {
+ bits.push_back((cur_byte >> bit) & 1);
+ }
+ }
+ if (!SanityCheckASMap(bits, 128)) {
+ LogPrintf("Sanity check of asmap file %s failed\n", path);
+ return {};
+ }
+ return bits;
+}
+
diff --git a/src/util/asmap.h b/src/util/asmap.h
index d0588bc8c3..810d70b9a1 100644
--- a/src/util/asmap.h
+++ b/src/util/asmap.h
@@ -5,11 +5,16 @@
#ifndef BITCOIN_UTIL_ASMAP_H
#define BITCOIN_UTIL_ASMAP_H
-#include <stdint.h>
+#include <fs.h>
+
+#include <cstdint>
#include <vector>
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip);
bool SanityCheckASMap(const std::vector<bool>& asmap, int bits);
+/** Read asmap from provided binary file */
+std::vector<bool> DecodeAsmap(fs::path path);
+
#endif // BITCOIN_UTIL_ASMAP_H
diff --git a/src/util/rbf.h b/src/util/rbf.h
index 4eb44b904f..6d44a2cb83 100644
--- a/src/util/rbf.h
+++ b/src/util/rbf.h
@@ -11,15 +11,13 @@ class CTransaction;
static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
-/** Check whether the sequence numbers on this transaction are signaling
-* opt-in to replace-by-fee, according to BIP 125.
-* Allow opt-out of transaction replacement by setting
-* nSequence > MAX_BIP125_RBF_SEQUENCE (SEQUENCE_FINAL-2) on all inputs.
+/** Check whether the sequence numbers on this transaction are signaling opt-in to replace-by-fee,
+ * according to BIP 125. Allow opt-out of transaction replacement by setting nSequence >
+ * MAX_BIP125_RBF_SEQUENCE (SEQUENCE_FINAL-2) on all inputs.
*
-* SEQUENCE_FINAL-1 is picked to still allow use of nLockTime by
-* non-replaceable transactions. All inputs rather than just one
-* is for the sake of multi-party protocols, where we don't
-* want a single party to be able to disable replacement. */
+* SEQUENCE_FINAL-1 is picked to still allow use of nLockTime by non-replaceable transactions. All
+* inputs rather than just one is for the sake of multi-party protocols, where we don't want a single
+* party to be able to disable replacement. */
bool SignalsOptInRBF(const CTransaction &tx);
#endif // BITCOIN_UTIL_RBF_H
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
index b6c2a47434..1a4d67a65e 100644
--- a/src/util/sock.cpp
+++ b/src/util/sock.cpp
@@ -10,12 +10,14 @@
#include <util/system.h>
#include <util/time.h>
-#include <codecvt>
-#include <cwchar>
-#include <locale>
#include <stdexcept>
#include <string>
+#ifdef WIN32
+#include <codecvt>
+#include <locale>
+#endif
+
#ifdef USE_POLL
#include <poll.h>
#endif
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 4e16a83c87..08f62f1da7 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1306,7 +1306,7 @@ void SetupEnvironment()
#endif
// On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
// may be invalid, in which case the "C.UTF-8" locale is used as fallback.
-#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
try {
std::locale(""); // Raises a runtime error if current locale is invalid
} catch (const std::runtime_error&) {
diff --git a/src/util/types.h b/src/util/types.h
new file mode 100644
index 0000000000..0047b00026
--- /dev/null
+++ b/src/util/types.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_TYPES_H
+#define BITCOIN_UTIL_TYPES_H
+
+template <class>
+inline constexpr bool ALWAYS_FALSE{false};
+
+#endif // BITCOIN_UTIL_TYPES_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 753b824167..cc87f98913 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -192,7 +192,7 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
// CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within
- // CBlock::AcceptBlock(), the height of the block *being*
+ // AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call
// IsFinalTx() with one more than active_chain_tip.Height().
@@ -415,7 +415,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
}
// Call CheckInputScripts() to cache signature and script validity against current tip consensus rules.
- return CheckInputScripts(tx, state, view, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata);
+ return CheckInputScripts(tx, state, view, flags, /* cacheSigStore= */ true, /* cacheFullScriptStore= */ true, txdata);
}
namespace {
@@ -770,16 +770,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// that we have the set of all ancestors we can detect this
// pathological case by making sure setConflicts and setAncestors don't
// intersect.
- for (CTxMemPool::txiter ancestorIt : setAncestors)
- {
- const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
- if (setConflicts.count(hashAncestor))
- {
- return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx",
- strprintf("%s spends conflicting transaction %s",
- hash.ToString(),
- hashAncestor.ToString()));
- }
+ if (const auto err_string{EntriesAndTxidsDisjoint(setAncestors, setConflicts, hash)}) {
+ return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
}
@@ -789,98 +781,30 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
fReplacementTransaction = setConflicts.size();
if (fReplacementTransaction)
{
- std::string err_string;
CFeeRate newFeeRate(nModifiedFees, nSize);
- for (const auto& mi : setIterConflicting) {
- // Don't allow the replacement to reduce the feerate of the
- // mempool.
- //
- // We usually don't want to accept replacements with lower
- // feerates than what they replaced as that would lower the
- // feerate of the next block. Requiring that the feerate always
- // be increased is also an easy-to-reason about way to prevent
- // DoS attacks via replacements.
- //
- // We only consider the feerates of transactions being directly
- // replaced, not their indirect descendants. While that does
- // mean high feerate children are ignored when deciding whether
- // or not to replace, we do require the replacement to pay more
- // overall fees too, mitigating most cases.
- CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
- if (newFeeRate <= oldFeeRate)
- {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee",
- strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
- hash.ToString(),
- newFeeRate.ToString(),
- oldFeeRate.ToString()));
- }
+ if (const auto err_string{PaysMoreThanConflicts(setIterConflicting, newFeeRate, hash)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
// Calculate all conflicting entries and enforce Rule #5.
- if (!GetEntriesForConflicts(tx, m_pool, setIterConflicting, allConflicting, err_string)) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too many potential replacements", err_string);
+ if (const auto err_string{GetEntriesForConflicts(tx, m_pool, setIterConflicting, allConflicting)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
+ "too many potential replacements", *err_string);
+ }
+ // Enforce Rule #2.
+ if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, setIterConflicting)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
+ "replacement-adds-unconfirmed", *err_string);
}
// Check if it's economically rational to mine this transaction rather
- // than the ones it replaces.
+ // than the ones it replaces. Enforce Rules #3 and #4.
for (CTxMemPool::txiter it : allConflicting) {
nConflictingFees += it->GetModifiedFee();
nConflictingSize += it->GetTxSize();
}
-
- std::set<uint256> setConflictsParents;
- for (const auto& mi : setIterConflicting) {
- for (const CTxIn &txin : mi->GetTx().vin)
- {
- setConflictsParents.insert(txin.prevout.hash);
- }
- }
-
- for (unsigned int j = 0; j < tx.vin.size(); j++)
- {
- // We don't want to accept replacements that require low
- // feerate junk to be mined first. Ideally we'd keep track of
- // the ancestor feerates and make the decision based on that,
- // but for now requiring all new inputs to be confirmed works.
- //
- // Note that if you relax this to make RBF a little more useful,
- // this may break the CalculateMempoolAncestors RBF relaxation,
- // above. See the comment above the first CalculateMempoolAncestors
- // call for more info.
- if (!setConflictsParents.count(tx.vin[j].prevout.hash))
- {
- // Rather than check the UTXO set - potentially expensive -
- // it's cheaper to just check if the new input refers to a
- // tx that's in the mempool.
- if (m_pool.exists(tx.vin[j].prevout.hash)) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "replacement-adds-unconfirmed",
- strprintf("replacement %s adds unconfirmed input, idx %d",
- hash.ToString(), j));
- }
- }
- }
-
- // The replacement must pay greater fees than the transactions it
- // replaces - if we did the bandwidth used by those conflicting
- // transactions would not be paid for.
- if (nModifiedFees < nConflictingFees)
- {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee",
- strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
- hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)));
- }
-
- // Finally in addition to paying more fees than the conflicts the
- // new transaction must pay for its own bandwidth.
- CAmount nDeltaFees = nModifiedFees - nConflictingFees;
- if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
- {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee",
- strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
- hash.ToString(),
- FormatMoney(nDeltaFees),
- FormatMoney(::incrementalRelayFee.GetFee(nSize))));
+ if (const auto err_string{PaysForRBF(nConflictingFees, nModifiedFees, nSize, hash)}) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
}
return true;
diff --git a/src/validation.h b/src/validation.h
index d4fcac1d48..078b988052 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -11,7 +11,9 @@
#endif
#include <amount.h>
+#include <arith_uint256.h>
#include <attributes.h>
+#include <chain.h>
#include <coins.h>
#include <consensus/validation.h>
#include <crypto/common.h> // for ReadLE64
@@ -21,10 +23,11 @@
#include <policy/packages.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
+#include <serialize.h>
#include <sync.h>
-#include <txmempool.h> // For CTxMemPool::cs
#include <txdb.h>
-#include <serialize.h>
+#include <txmempool.h> // For CTxMemPool::cs
+#include <uint256.h>
#include <util/check.h>
#include <util/hasher.h>
#include <util/translation.h>
@@ -42,7 +45,6 @@
class CChainState;
class BlockValidationState;
-class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
class CChainParams;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index bb5f0cceff..7abdbb0e55 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,6 +5,7 @@
#include <init.h>
#include <interfaces/chain.h>
+#include <interfaces/init.h>
#include <interfaces/wallet.h>
#include <net.h>
#include <node/context.h>
@@ -130,7 +131,7 @@ void WalletInit::Construct(NodeContext& node) const
LogPrintf("Wallet disabled!\n");
return;
}
- auto wallet_client = interfaces::MakeWalletClient(*node.chain, args);
+ auto wallet_client = node.init->makeWalletClient(*node.chain);
node.wallet_client = wallet_client.get();
node.chain_clients.emplace_back(std::move(wallet_client));
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index a994976394..1b841026b8 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -165,7 +165,7 @@ void UnloadWallets(WalletContext& context)
auto wallet = wallets.back();
wallets.pop_back();
std::vector<bilingual_str> warnings;
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt, warnings);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings);
UnloadWallet(std::move(wallet));
}
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index ff9e10c5ad..7d194ae262 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2960,6 +2960,9 @@ static RPCHelpMan listunspent()
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
+ {RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "ancestorfees", /* optional */ true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
{RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
{RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
@@ -3126,6 +3129,16 @@ static RPCHelpMan listunspent()
entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
entry.pushKV("confirmations", out.nDepth);
+ if (!out.nDepth) {
+ size_t ancestor_count, descendant_count, ancestor_size;
+ CAmount ancestor_fees;
+ pwallet->chain().getTransactionAncestry(out.tx->GetHash(), ancestor_count, descendant_count, &ancestor_size, &ancestor_fees);
+ if (ancestor_count) {
+ entry.pushKV("ancestorcount", uint64_t(ancestor_count));
+ entry.pushKV("ancestorsize", uint64_t(ancestor_size));
+ entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
+ }
+ }
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
@@ -4650,10 +4663,11 @@ static RPCHelpMan upgradewallet()
#ifdef ENABLE_EXTERNAL_SIGNER
static RPCHelpMan walletdisplayaddress()
{
- return RPCHelpMan{"walletdisplayaddress",
+ return RPCHelpMan{
+ "walletdisplayaddress",
"Display address on an external signer for verification.",
{
- {"address", RPCArg::Type::STR, RPCArg::Optional::NO, /* default_val */ "", "bitcoin address to display"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
},
RPCResult{
RPCResult::Type::OBJ,"","",
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index f901679efc..5d51809241 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -40,7 +40,7 @@ CoinEligibilityFilter filter_standard_extra(6, 6, 0);
CoinSelectionParams coin_selection_params(/* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
{
@@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CoinSelectionParams coin_selection_params_bnb(/* change_output_size= */ 0,
/* change_spend_size= */ 0, /* effective_feerate= */ CFeeRate(3000),
/* long_term_feerate= */ CFeeRate(1000), /* discard_feerate= */ CFeeRate(1000),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
CoinSet setCoinsRet;
CAmount nValueRet;
empty_wallet();
@@ -654,7 +654,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CoinSelectionParams cs_params(/* change_output_size= */ 34,
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
- /* tx_no_inputs_size= */ 0, /* avoid_partial= */ false);
+ /* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
CoinSet out_set;
CAmount out_value = 0;
CCoinControl cc;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 12a22f458a..5431a38bee 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -235,7 +235,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
}
}
@@ -280,7 +280,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
::dumpwallet().HandleRequest(request);
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
@@ -299,7 +299,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(context, wallet);
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
::importwallet().HandleRequest(request);
- RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt);
+ RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 094221adf2..0cd91b9ebe 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -19,25 +19,6 @@
typedef std::map<std::string, std::string> mapValue_t;
-
-static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (!mapValue.count("n"))
- {
- nOrderPos = -1; // TODO: calculate elsewhere
- return;
- }
- nOrderPos = atoi64(mapValue["n"]);
-}
-
-
-static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (nOrderPos == -1)
- return;
- mapValue["n"] = ToString(nOrderPos);
-}
-
/** Legacy class used for deserializing vtxPrev for backwards compatibility.
* vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
* but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
@@ -192,7 +173,9 @@ public:
mapValue_t mapValueCopy = mapValue;
mapValueCopy["fromaccount"] = "";
- WriteOrderPos(nOrderPos, mapValueCopy);
+ if (nOrderPos != -1) {
+ mapValueCopy["n"] = ToString(nOrderPos);
+ }
if (nTimeSmart) {
mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
}
@@ -232,8 +215,10 @@ public:
setConfirmed();
}
- ReadOrderPos(nOrderPos, mapValue);
- nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
+ const auto it_op = mapValue.find("n");
+ nOrderPos = (it_op != mapValue.end()) ? atoi64(it_op->second) : -1;
+ const auto it_ts = mapValue.find("timesmart");
+ nTimeSmart = (it_ts != mapValue.end()) ? static_cast<unsigned int>(atoi64(it_ts->second)) : 0;
mapValue.erase("fromaccount");
mapValue.erase("spent");
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 6ae866cc07..56f4c98317 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -6,6 +6,7 @@
#include <chain.h>
#include <chainparams.h>
+#include <netbase.h>
#include <node/blockstorage.h>
#include <rpc/server.h>
#include <streams.h>
@@ -73,6 +74,20 @@ static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)
return 0;
}
+static bool IsZMQAddressIPV6(const std::string &zmq_address)
+{
+ const std::string tcp_prefix = "tcp://";
+ const size_t tcp_index = zmq_address.rfind(tcp_prefix);
+ const size_t colon_index = zmq_address.rfind(":");
+ if (tcp_index == 0 && colon_index != std::string::npos) {
+ const std::string ip = zmq_address.substr(tcp_prefix.length(), colon_index - tcp_prefix.length());
+ CNetAddr addr;
+ LookupHost(ip, addr, false);
+ if (addr.IsIPv6()) return true;
+ }
+ return false;
+}
+
bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
{
assert(!psocket);
@@ -107,6 +122,15 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
+ // On some systems (e.g. OpenBSD) the ZMQ_IPV6 must not be enabled, if the address to bind isn't IPv6
+ const int enable_ipv6 { IsZMQAddressIPV6(address) ? 1 : 0};
+ rc = zmq_setsockopt(psocket, ZMQ_IPV6, &enable_ipv6, sizeof(enable_ipv6));
+ if (rc != 0) {
+ zmqError("Failed to set ZMQ_IPV6");
+ zmq_close(psocket);
+ return false;
+ }
+
rc = zmq_bind(psocket, address.c_str());
if (rc != 0)
{