aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-tidy2
-rw-r--r--src/Makefile.am1
-rw-r--r--src/addrdb.cpp97
-rw-r--r--src/addrdb.h22
-rw-r--r--src/addrman.cpp29
-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.cpp3
-rw-r--r--src/httpserver.cpp4
-rw-r--r--src/init.cpp17
-rw-r--r--src/logging/timer.h24
-rw-r--r--src/net.cpp11
-rw-r--r--src/net.h4
-rw-r--r--src/netaddress.cpp5
-rw-r--r--src/netaddress.h2
-rw-r--r--src/policy/rbf.cpp138
-rw-r--r--src/policy/rbf.h62
-rw-r--r--src/qt/bantablemodel.h1
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/wallettests.cpp2
-rw-r--r--src/qt/walletframe.cpp1
-rw-r--r--src/signet.cpp2
-rw-r--r--src/sync.cpp6
-rw-r--r--src/sync.h7
-rw-r--r--src/test/addrman_tests.cpp21
-rw-r--r--src/test/fuzz/addrman.cpp2
-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/util/asmap.cpp38
-rw-r--r--src/util/asmap.h7
-rw-r--r--src/util/getuniquepath.cpp4
-rw-r--r--src/util/rbf.h14
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/util/types.h11
-rw-r--r--src/validation.cpp108
-rw-r--r--src/wallet/load.cpp2
-rw-r--r--src/wallet/test/coinselector_tests.cpp6
-rw-r--r--src/wallet/test/wallet_tests.cpp6
-rw-r--r--src/zmq/zmqpublishnotifier.cpp24
44 files changed, 409 insertions, 324 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..eea98c7f22 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 \
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..772c34ae77 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -5,10 +5,12 @@
#include <addrman.h>
+#include <clientversion.h>
#include <hash.h>
#include <logging.h>
#include <netaddress.h>
#include <serialize.h>
+#include <streams.h>
#include <cmath>
#include <optional>
@@ -1007,30 +1009,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..e75ba81b54 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));
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/init.cpp b/src/init.cpp
index b744298667..d4ba441b0c 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1189,7 +1189,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 +1200,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);
}
}
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/net.cpp b/src/net.cpp
index 35376b89ac..c72cd75ba7 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>
@@ -552,14 +554,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 +1748,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 +2804,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/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/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/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/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 022f367422..f4d561286e 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -112,7 +112,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/wallettests.cpp b/src/qt/test/wallettests.cpp
index 1976bee74b..89f2258c0d 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -167,7 +167,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/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/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/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/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index e95126a80f..fdbfb3b93b 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -221,7 +221,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/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/getuniquepath.cpp b/src/util/getuniquepath.cpp
index 9839d2f624..6776e7785b 100644
--- a/src/util/getuniquepath.cpp
+++ b/src/util/getuniquepath.cpp
@@ -1,3 +1,7 @@
+// 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.
+
#include <random.h>
#include <fs.h>
#include <util/strencodings.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/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..8696f1af85 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -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/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/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/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)
{