aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/Makefile.test.include28
-rw-r--r--src/addrman.h94
-rw-r--r--src/bench/bench.h8
-rw-r--r--src/bench/block_assemble.cpp5
-rw-r--r--src/bitcoin-cli.cpp69
-rw-r--r--src/bitcoin-wallet.cpp69
-rw-r--r--src/clientversion.h9
-rw-r--r--src/crypto/muhash.cpp42
-rw-r--r--src/crypto/muhash.h7
-rw-r--r--src/flatfile.cpp2
-rw-r--r--src/init.cpp19
-rw-r--r--src/net.cpp143
-rw-r--r--src/net.h19
-rw-r--r--src/net_processing.cpp258
-rw-r--r--src/netbase.cpp152
-rw-r--r--src/netbase.h27
-rw-r--r--src/node/coinstats.cpp63
-rw-r--r--src/node/coinstats.h1
-rw-r--r--src/node/transaction.cpp18
-rw-r--r--src/qt/guiutil.cpp4
-rw-r--r--src/qt/guiutil.h2
-rw-r--r--src/qt/rpcconsole.cpp4
-rw-r--r--src/rpc/blockchain.cpp50
-rw-r--r--src/rpc/misc.cpp19
-rw-r--r--src/rpc/rawtransaction.cpp51
-rw-r--r--src/rpc/util.cpp17
-rw-r--r--src/rpc/util.h2
-rw-r--r--src/script/bitcoinconsensus.h12
-rw-r--r--src/script/sigcache.cpp12
-rw-r--r--src/script/sign.cpp9
-rw-r--r--src/script/standard.cpp31
-rw-r--r--src/test/cuckoocache_tests.cpp19
-rw-r--r--src/test/denialofservice_tests.cpp10
-rw-r--r--src/test/fs_tests.cpp18
-rw-r--r--src/test/fuzz/deserialize.cpp4
-rw-r--r--src/test/fuzz/fuzz.cpp12
-rw-r--r--src/test/fuzz/fuzz.h37
-rw-r--r--src/test/fuzz/netaddress.cpp1
-rw-r--r--src/test/fuzz/process_message.cpp82
-rw-r--r--src/test/fuzz/process_messages.cpp2
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp8
-rw-r--r--src/test/fuzz/string.cpp2
-rw-r--r--src/test/fuzz/system.cpp2
-rw-r--r--src/test/net_tests.cpp13
-rw-r--r--src/test/script_standard_tests.cpp16
-rw-r--r--src/test/sock_tests.cpp149
-rw-r--r--src/test/txvalidation_tests.cpp16
-rw-r--r--src/test/txvalidationcache_tests.cpp6
-rw-r--r--src/test/util_tests.cpp3
-rw-r--r--src/test/validation_block_tests.cpp10
-rw-r--r--src/torcontrol.cpp1
-rw-r--r--src/txmempool.cpp16
-rw-r--r--src/txmempool.h13
-rw-r--r--src/util/getuniquepath.cpp10
-rw-r--r--src/util/getuniquepath.h19
-rw-r--r--src/util/macros.h7
-rw-r--r--src/util/sock.cpp149
-rw-r--r--src/util/sock.h118
-rw-r--r--src/util/system.cpp56
-rw-r--r--src/util/system.h24
-rw-r--r--src/util/time.cpp19
-rw-r--r--src/util/time.h13
-rw-r--r--src/validation.cpp113
-rw-r--r--src/validation.h55
-rw-r--r--src/wallet/rpcdump.cpp4
-rw-r--r--src/wallet/scriptpubkeyman.cpp5
-rw-r--r--src/wallet/test/db_tests.cpp8
-rw-r--r--src/wallet/test/init_tests.cpp8
-rw-r--r--src/wallet/walletdb.cpp20
-rw-r--r--src/wallet/wallettool.cpp10
-rw-r--r--src/wallet/wallettool.h2
72 files changed, 1541 insertions, 793 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 2616eb8638..67efbbeae4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@
# Pattern rule to print variables, e.g. make print-top_srcdir
print-%:
- @echo $* = $($*)
+ @echo '$*' = '$($*)'
DIST_SUBDIRS = secp256k1 univalue
@@ -233,6 +233,7 @@ BITCOIN_CORE_H = \
util/check.h \
util/error.h \
util/fees.h \
+ util/getuniquepath.h \
util/golombrice.h \
util/hasher.h \
util/macros.h \
@@ -242,6 +243,7 @@ BITCOIN_CORE_H = \
util/rbf.h \
util/ref.h \
util/settings.h \
+ util/sock.h \
util/spanparsing.h \
util/string.h \
util/system.h \
@@ -556,7 +558,9 @@ libbitcoin_util_a_SOURCES = \
util/bytevectorhash.cpp \
util/error.cpp \
util/fees.cpp \
+ util/getuniquepath.cpp \
util/hasher.cpp \
+ util/sock.cpp \
util/system.cpp \
util/message.cpp \
util/moneystr.cpp \
@@ -683,7 +687,7 @@ endif
bitcoin_util_SOURCES = bitcoin-util.cpp
bitcoin_util_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_util_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_util_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_util_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_util_SOURCES += bitcoin-util-res.rc
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index e9f9b73abe..e817bb2ee2 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -2,9 +2,11 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-if ENABLE_FUZZ
+if ENABLE_FUZZ_BINARY
noinst_PROGRAMS += test/fuzz/fuzz
-else
+endif
+
+if !ENABLE_FUZZ
bin_PROGRAMS += test/test_bitcoin
endif
@@ -50,6 +52,14 @@ FUZZ_SUITE_LD_COMMON = \
$(EVENT_LIBS) \
$(EVENT_PTHREADS_LIBS)
+if USE_UPNP
+FUZZ_SUITE_LD_COMMON += $(MINIUPNPC_LIBS)
+endif
+
+if USE_NATPMP
+FUZZ_SUITE_LD_COMMON += $(NATPMP_LIBS)
+endif
+
# test_bitcoin binary #
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
@@ -114,6 +124,7 @@ BITCOIN_TESTS =\
test/sighash_tests.cpp \
test/sigopcount_tests.cpp \
test/skiplist_tests.cpp \
+ test/sock_tests.cpp \
test/streams_tests.cpp \
test/sync_tests.cpp \
test/system_tests.cpp \
@@ -145,10 +156,16 @@ BITCOIN_TESTS += \
wallet/test/ismine_tests.cpp \
wallet/test/scriptpubkeyman_tests.cpp
+FUZZ_SUITE_LD_COMMON +=\
+ $(LIBBITCOIN_WALLET) \
+ $(SQLITE_LIBS) \
+ $(BDB_LIBS)
+
if USE_BDB
BITCOIN_TESTS += wallet/test/db_tests.cpp
endif
+
BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.cpp \
wallet/test/wallet_test_fixture.h \
@@ -172,12 +189,12 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
+FUZZ_SUITE_LD_COMMON += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
-if ENABLE_FUZZ
-
FUZZ_SUITE_LDFLAGS_COMMON = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
+if ENABLE_FUZZ_BINARY
test_fuzz_fuzz_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fuzz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fuzz_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -278,8 +295,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/tx_in.cpp \
test/fuzz/tx_out.cpp \
test/fuzz/txrequest.cpp
-
-endif # ENABLE_FUZZ
+endif # ENABLE_FUZZ_BINARY
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
diff --git a/src/addrman.h b/src/addrman.h
index 9ac67b7af6..92a5570953 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -335,22 +335,20 @@ public:
* * nNew
* * nTried
* * number of "new" buckets XOR 2**30
- * * all nNew addrinfos in vvNew
- * * all nTried addrinfos in vvTried
- * * for each bucket:
+ * * all new addresses (total count: nNew)
+ * * all tried addresses (total count: nTried)
+ * * for each new bucket:
* * number of elements
- * * for each element: index
+ * * for each element: index in the serialized "all new addresses"
+ * * asmap checksum
*
* 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
* as incompatible. This is necessary because it did not check the version number on
* deserialization.
*
- * Notice that vvTried, mapAddr and vVector are never encoded explicitly;
+ * vvNew, vvTried, mapInfo, mapAddr and vRandom are never encoded explicitly;
* they are instead reconstructed from the other information.
*
- * vvNew is serialized, but only used if ADDRMAN_UNKNOWN_BUCKET_COUNT didn't change,
- * otherwise it is reconstructed as well.
- *
* This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
* changes to the ADDRMAN_ parameters without breaking the on-disk structure.
*
@@ -413,13 +411,13 @@ public:
}
}
}
- // Store asmap version after bucket entries so that it
+ // Store asmap checksum after bucket entries so that it
// can be ignored by older clients for backward compatibility.
- uint256 asmap_version;
+ uint256 asmap_checksum;
if (m_asmap.size() != 0) {
- asmap_version = SerializeHash(m_asmap);
+ asmap_checksum = SerializeHash(m_asmap);
}
- s << asmap_version;
+ s << asmap_checksum;
}
template <typename Stream>
@@ -500,47 +498,63 @@ public:
nTried -= nLost;
// Store positions in the new table buckets to apply later (if possible).
- std::map<int, int> entryToBucket; // Represents which entry belonged to which bucket when serializing
-
- for (int bucket = 0; bucket < nUBuckets; bucket++) {
- int nSize = 0;
- s >> nSize;
- for (int n = 0; n < nSize; n++) {
- int nIndex = 0;
- s >> nIndex;
- if (nIndex >= 0 && nIndex < nNew) {
- entryToBucket[nIndex] = bucket;
+ // An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
+ // so we store all bucket-entry_index pairs to iterate through later.
+ std::vector<std::pair<int, int>> bucket_entries;
+
+ for (int bucket = 0; bucket < nUBuckets; ++bucket) {
+ int num_entries{0};
+ s >> num_entries;
+ for (int n = 0; n < num_entries; ++n) {
+ int entry_index{0};
+ s >> entry_index;
+ if (entry_index >= 0 && entry_index < nNew) {
+ bucket_entries.emplace_back(bucket, entry_index);
}
}
}
- uint256 supplied_asmap_version;
+ // If the bucket count and asmap checksum haven't changed, then attempt
+ // to restore the entries to the buckets/positions they were in before
+ // serialization.
+ uint256 supplied_asmap_checksum;
if (m_asmap.size() != 0) {
- supplied_asmap_version = SerializeHash(m_asmap);
+ supplied_asmap_checksum = SerializeHash(m_asmap);
}
- uint256 serialized_asmap_version;
+ uint256 serialized_asmap_checksum;
if (format >= Format::V2_ASMAP) {
- s >> serialized_asmap_version;
+ s >> serialized_asmap_checksum;
}
+ const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
+ serialized_asmap_checksum == supplied_asmap_checksum};
- for (int n = 0; n < nNew; n++) {
- CAddrInfo &info = mapInfo[n];
- int bucket = entryToBucket[n];
- int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
- if (format >= Format::V2_ASMAP && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
- info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
+ if (!restore_bucketing) {
+ LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
+ }
+
+ for (auto bucket_entry : bucket_entries) {
+ int bucket{bucket_entry.first};
+ const int entry_index{bucket_entry.second};
+ CAddrInfo& info = mapInfo[entry_index];
+
+ // The entry shouldn't appear in more than
+ // ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
+ // this bucket_entry.
+ if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
+
+ int bucket_position = info.GetBucketPosition(nKey, true, bucket);
+ if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
// Bucketing has not changed, using existing bucket positions for the new table
- vvNew[bucket][nUBucketPos] = n;
- info.nRefCount++;
+ vvNew[bucket][bucket_position] = entry_index;
+ ++info.nRefCount;
} else {
- // In case the new table data cannot be used (format unknown, bucket count wrong or new asmap),
+ // In case the new table data cannot be used (bucket count wrong or new asmap),
// try to give them a reference based on their primary source address.
- LogPrint(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
bucket = info.GetNewBucket(nKey, m_asmap);
- nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
- if (vvNew[bucket][nUBucketPos] == -1) {
- vvNew[bucket][nUBucketPos] = n;
- info.nRefCount++;
+ bucket_position = info.GetBucketPosition(nKey, true, bucket);
+ if (vvNew[bucket][bucket_position] == -1) {
+ vvNew[bucket][bucket_position] = entry_index;
+ ++info.nRefCount;
}
}
}
diff --git a/src/bench/bench.h b/src/bench/bench.h
index bafc7f8716..22f06d8cb8 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_BENCH_BENCH_H
#define BITCOIN_BENCH_BENCH_H
+#include <util/macros.h>
+
#include <chrono>
#include <functional>
#include <map>
@@ -12,8 +14,6 @@
#include <vector>
#include <bench/nanobench.h>
-#include <boost/preprocessor/cat.hpp>
-#include <boost/preprocessor/stringize.hpp>
/*
* Usage:
@@ -56,8 +56,8 @@ public:
static void RunAll(const Args& args);
};
}
-// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo");
+// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
#define BENCHMARK(n) \
- benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))(BOOST_PP_STRINGIZE(n), n);
+ benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n);
#endif // BITCOIN_BENCH_BENCH_H
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index af5a82f69f..9f7d582e8a 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -48,9 +48,8 @@ static void AssembleBlock(benchmark::Bench& bench)
LOCK(::cs_main); // Required for ::AcceptToMemoryPool.
for (const auto& txr : txs) {
- TxValidationState state;
- bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* plTxnReplaced */, false /* bypass_limits */)};
- assert(ret);
+ const MempoolAcceptResult res = ::AcceptToMemoryPool(*test_setup.m_node.mempool, txr, false /* bypass_limits */);
+ assert(res.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index fa41208a31..a9a0ec6f5d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -300,9 +300,12 @@ class NetinfoRequestHandler : public BaseRequestHandler
{
private:
static constexpr int8_t UNKNOWN_NETWORK{-1};
- static constexpr uint8_t m_networks_size{3};
- const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion"}};
- std::array<std::array<uint16_t, m_networks_size + 2>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total/block-relay)
+ static constexpr int8_t NET_I2P{3}; // pos of "i2p" in m_networks
+ static constexpr uint8_t m_networks_size{4};
+ const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion", "i2p"}};
+ std::array<std::array<uint16_t, m_networks_size + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total)
+ uint8_t m_block_relay_peers_count{0};
+ uint8_t m_manual_peers_count{0};
int8_t NetworkStringToId(const std::string& str) const
{
for (uint8_t i = 0; i < m_networks_size; ++i) {
@@ -316,12 +319,14 @@ private:
bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
bool m_is_asmap_on{false};
+ bool m_is_i2p_on{false};
size_t m_max_addr_length{0};
- size_t m_max_age_length{4};
+ size_t m_max_age_length{3};
size_t m_max_id_length{2};
struct Peer {
std::string addr;
std::string sub_version;
+ std::string conn_type;
std::string network;
std::string age;
double min_ping;
@@ -333,6 +338,8 @@ private:
int id;
int mapped_as;
int version;
+ bool is_bip152_hb_from;
+ bool is_bip152_hb_to;
bool is_block_relay;
bool is_outbound;
bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
@@ -351,6 +358,14 @@ private:
const double milliseconds{round(1000 * seconds)};
return milliseconds > 999999 ? "-" : ToString(milliseconds);
}
+ std::string ConnectionTypeForNetinfo(const std::string& conn_type) const
+ {
+ if (conn_type == "outbound-full-relay") return "full";
+ if (conn_type == "block-relay-only") return "block";
+ if (conn_type == "manual" || conn_type == "feeler") return conn_type;
+ if (conn_type == "addr-fetch") return "addr";
+ return "";
+ }
const UniValue NetinfoHelp()
{
return std::string{
@@ -379,6 +394,9 @@ private:
" type Type of peer connection\n"
" \"full\" - full relay, the default\n"
" \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
+ " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
+ " \"feeler\" - short-lived connection for testing addresses\n"
+ " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
" net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", or \"cjdns\")\n"
" mping Minimum observed ping time, in milliseconds (ms)\n"
" ping Last observed ping time, in milliseconds (ms)\n"
@@ -386,6 +404,9 @@ private:
" recv Time since last message received from the peer, in seconds\n"
" txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
" blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
+ " hb High-bandwidth BIP152 compact block relay\n"
+ " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
+ " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
" age Duration of connection to the peer, in minutes\n"
" asmap Mapped AS (Autonomous System) number in the BGP route to the peer, used for diversifying\n"
" peer selection (only displayed if the -asmap config option is set)\n"
@@ -393,7 +414,7 @@ private:
" address IP address and port of the peer\n"
" version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
"* The connection counts table displays the number of peers by direction, network, and the totals\n"
- " for each, as well as a column for block relay peers.\n\n"
+ " for each, as well as two special outbound columns for block relay peers and manual peers.\n\n"
"* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
"Examples:\n\n"
"Connection counts and local addresses only\n"
@@ -450,16 +471,16 @@ public:
const std::string network{peer["network"].get_str()};
const int8_t network_id{NetworkStringToId(network)};
if (network_id == UNKNOWN_NETWORK) continue;
+ m_is_i2p_on |= (network_id == NET_I2P);
const bool is_outbound{!peer["inbound"].get_bool()};
const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const std::string conn_type{peer["connection_type"].get_str()};
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(m_networks_size); // in/out overall
++m_counts.at(2).at(network_id); // total by network
++m_counts.at(2).at(m_networks_size); // total overall
- if (is_block_relay) {
- ++m_counts.at(is_outbound).at(m_networks_size + 1); // in/out block-relay
- ++m_counts.at(2).at(m_networks_size + 1); // total block-relay
- }
+ if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
+ if (conn_type == "manual") ++m_manual_peers_count;
if (DetailsRequested()) {
// Push data for this peer to the peers vector.
const int peer_id{peer["id"].get_int()};
@@ -475,7 +496,9 @@ public:
const std::string addr{peer["addr"].get_str()};
const std::string age{conn_time == 0 ? "" : ToString((m_time_now - conn_time) / 60)};
const std::string sub_version{peer["subver"].get_str()};
- m_peers.push_back({addr, sub_version, network, age, min_ping, ping, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_block_relay, is_outbound});
+ const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
+ const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
+ m_peers.push_back({addr, sub_version, conn_type, network, age, min_ping, ping, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_bip152_hb_from, is_bip152_hb_to, is_block_relay, is_outbound});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
m_max_age_length = std::max(age.length(), m_max_age_length);
m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
@@ -489,15 +512,15 @@ public:
// Report detailed peer connections list sorted by direction and minimum ping time.
if (DetailsRequested() && !m_peers.empty()) {
std::sort(m_peers.begin(), m_peers.end());
- result += strprintf("<-> relay net mping ping send recv txn blk %*s ", m_max_age_length, "age");
+ result += strprintf("<-> type net mping ping send recv txn blk hb %*s ", m_max_age_length, "age");
if (m_is_asmap_on) result += " asmap ";
result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
for (const Peer& peer : m_peers) {
std::string version{ToString(peer.version) + peer.sub_version};
result += strprintf(
- "%3s %5s %5s%7s%7s%5s%5s%5s%5s %*s%*i %*s %-*s%s\n",
+ "%3s %6s %5s%7s%7s%5s%5s%5s%5s %2s %*s%*i %*s %-*s%s\n",
peer.is_outbound ? "out" : "in",
- peer.is_block_relay ? "block" : "full",
+ ConnectionTypeForNetinfo(peer.conn_type),
peer.network,
PingTimeToString(peer.min_ping),
PingTimeToString(peer.ping),
@@ -505,6 +528,7 @@ public:
peer.last_recv == 0 ? "" : ToString(m_time_now - peer.last_recv),
peer.last_trxn == 0 ? "" : ToString((m_time_now - peer.last_trxn) / 60),
peer.last_blck == 0 ? "" : ToString((m_time_now - peer.last_blck) / 60),
+ strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
m_max_age_length, // variable spacing
peer.age,
m_is_asmap_on ? 7 : 0, // variable spacing
@@ -515,18 +539,27 @@ public:
IsAddressSelected() ? peer.addr : "",
IsVersionSelected() && version != "0" ? version : "");
}
- result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
+ result += strprintf(" ms ms sec sec min min %*s\n\n", m_max_age_length, "min");
}
// Report peer connection totals by type.
- result += " ipv4 ipv6 onion total block-relay\n";
+ result += " ipv4 ipv6 onion";
+ if (m_is_i2p_on) result += " i2p";
+ result += " total block";
+ if (m_manual_peers_count) result += " manual";
const std::array<std::string, 3> rows{{"in", "out", "total"}};
- for (uint8_t i = 0; i < m_networks_size; ++i) {
- result += strprintf("%-5s %5i %5i %5i %5i %5i\n", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2), m_counts.at(i).at(m_networks_size), m_counts.at(i).at(m_networks_size + 1));
+ for (uint8_t i = 0; i < 3; ++i) {
+ result += strprintf("\n%-5s %5i %5i %5i", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2)); // ipv4/ipv6/onion peers counts
+ if (m_is_i2p_on) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p peers count
+ result += strprintf(" %5i", m_counts.at(i).at(m_networks_size)); // total peers count
+ if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
+ result += strprintf(" %5i", m_block_relay_peers_count);
+ if (m_manual_peers_count) result += strprintf(" %5i", m_manual_peers_count);
+ }
}
// Report local addresses, ports, and scores.
- result += "\nLocal addresses";
+ result += "\n\nLocal addresses";
const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()};
if (local_addrs.empty()) {
result += ": n/a\n";
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 3e8e5fc7bc..b84d909b07 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -33,51 +33,52 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("dump", "Print out all of the wallet key-value records", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
- argsman.AddArg("createfromdump", "Create new wallet file from dumped records", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
+ argsman.AddCommand("info", "Get wallet info", OptionsCategory::COMMANDS);
+ argsman.AddCommand("create", "Create new wallet file", OptionsCategory::COMMANDS);
+ argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.", OptionsCategory::COMMANDS);
+ argsman.AddCommand("dump", "Print out all of the wallet key-value records", OptionsCategory::COMMANDS);
+ argsman.AddCommand("createfromdump", "Create new wallet file from dumped records", OptionsCategory::COMMANDS);
}
-static bool WalletAppInit(int argc, char* argv[])
+static bool WalletAppInit(ArgsManager& args, int argc, char* argv[])
{
- SetupWalletToolArgs(gArgs);
+ SetupWalletToolArgs(args);
std::string error_message;
- if (!gArgs.ParseParameters(argc, argv, error_message)) {
+ if (!args.ParseParameters(argc, argv, error_message)) {
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message);
return false;
}
- if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
+ if (argc < 2 || HelpRequested(args) || args.IsArgSet("-version")) {
std::string strUsage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n";
- if (!gArgs.IsArgSet("-version")) {
- strUsage += "\n"
- "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n"
- "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n"
- "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n"
- "Usage:\n"
- " bitcoin-wallet [options] <command>\n";
- strUsage += "\n" + gArgs.GetHelpMessage();
- }
+ if (!args.IsArgSet("-version")) {
+ strUsage += "\n"
+ "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n"
+ "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n"
+ "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n"
+ "Usage:\n"
+ " bitcoin-wallet [options] <command>\n";
+ strUsage += "\n" + args.GetHelpMessage();
+ }
tfm::format(std::cout, "%s", strUsage);
return false;
}
// check for printtoconsole, allow -debug
- LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
+ LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", args.GetBoolArg("-debug", false));
if (!CheckDataDirOption()) {
- tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
+ tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""));
return false;
}
// Check for chain settings (Params() calls are only valid after this clause)
- SelectParams(gArgs.GetChainName());
+ SelectParams(args.GetChainName());
return true;
}
int main(int argc, char* argv[])
{
+ ArgsManager& args = gArgs;
#ifdef WIN32
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
@@ -85,7 +86,7 @@ int main(int argc, char* argv[])
SetupEnvironment();
RandomInit();
try {
- if (!WalletAppInit(argc, argv)) return EXIT_FAILURE;
+ if (!WalletAppInit(args, argc, argv)) return EXIT_FAILURE;
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "WalletAppInit()");
return EXIT_FAILURE;
@@ -94,33 +95,19 @@ int main(int argc, char* argv[])
return EXIT_FAILURE;
}
- std::string method {};
- for(int i = 1; i < argc; ++i) {
- if (!IsSwitchChar(argv[i][0])) {
- if (!method.empty()) {
- tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method, argv[i]);
- return EXIT_FAILURE;
- }
- method = argv[i];
- }
- }
-
- if (method.empty()) {
+ const auto command = args.GetCommand();
+ if (!command) {
tfm::format(std::cerr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
return EXIT_FAILURE;
}
-
- // A name must be provided when creating a file
- if (method == "create" && !gArgs.IsArgSet("-wallet")) {
- tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
+ if (command->args.size() != 0) {
+ tfm::format(std::cerr, "Error: Additional arguments provided (%s). Methods do not take arguments. Please refer to `-help`.\n", Join(command->args, ", "));
return EXIT_FAILURE;
}
- std::string name = gArgs.GetArg("-wallet", "");
-
ECCVerifyHandle globalVerifyHandle;
ECC_Start();
- if (!WalletTool::ExecuteWalletToolFunc(gArgs, method, name)) {
+ if (!WalletTool::ExecuteWalletToolFunc(args, command->command)) {
return EXIT_FAILURE;
}
ECC_Stop();
diff --git a/src/clientversion.h b/src/clientversion.h
index 2da909f829..0ed3f68094 100644
--- a/src/clientversion.h
+++ b/src/clientversion.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_CLIENTVERSION_H
#define BITCOIN_CLIENTVERSION_H
+#include <util/macros.h>
+
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif //HAVE_CONFIG_H
@@ -14,13 +16,6 @@
#error Client version information missing: version is not defined by bitcoin-config.h or in any other way
#endif
-/**
- * Converts the parameter X to a string after macro replacement on X has been performed.
- * Don't merge these into one macro!
- */
-#define STRINGIZE(X) DO_STRINGIZE(X)
-#define DO_STRINGIZE(X) #X
-
//! Copyright string used in Windows .rc files
#define COPYRIGHT_STR "2009-" STRINGIZE(COPYRIGHT_YEAR) " " COPYRIGHT_HOLDERS_FINAL
diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp
index fbd14f9325..e5a0d4cb9c 100644
--- a/src/crypto/muhash.cpp
+++ b/src/crypto/muhash.cpp
@@ -17,7 +17,6 @@ namespace {
using limb_t = Num3072::limb_t;
using double_limb_t = Num3072::double_limb_t;
constexpr int LIMB_SIZE = Num3072::LIMB_SIZE;
-constexpr int LIMBS = Num3072::LIMBS;
/** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */
constexpr limb_t MAX_PRIME_DIFF = 1103717;
@@ -123,7 +122,7 @@ inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul)
} // namespace
-/** Indicates wether d is larger than the modulus. */
+/** Indicates whether d is larger than the modulus. */
bool Num3072::IsOverflow() const
{
if (this->limbs[0] <= std::numeric_limits<limb_t>::max() - MAX_PRIME_DIFF) return false;
@@ -276,18 +275,33 @@ void Num3072::Divide(const Num3072& a)
if (this->IsOverflow()) this->FullReduce();
}
-Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
- Num3072 out{};
- uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256();
- unsigned char tmp[BYTE_SIZE];
- ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, BYTE_SIZE);
+Num3072::Num3072(const unsigned char (&data)[BYTE_SIZE]) {
+ for (int i = 0; i < LIMBS; ++i) {
+ if (sizeof(limb_t) == 4) {
+ this->limbs[i] = ReadLE32(data + 4 * i);
+ } else if (sizeof(limb_t) == 8) {
+ this->limbs[i] = ReadLE64(data + 8 * i);
+ }
+ }
+}
+
+void Num3072::ToBytes(unsigned char (&out)[BYTE_SIZE]) {
for (int i = 0; i < LIMBS; ++i) {
if (sizeof(limb_t) == 4) {
- out.limbs[i] = ReadLE32(tmp + 4 * i);
+ WriteLE32(out + i * 4, this->limbs[i]);
} else if (sizeof(limb_t) == 8) {
- out.limbs[i] = ReadLE64(tmp + 8 * i);
+ WriteLE64(out + i * 8, this->limbs[i]);
}
}
+}
+
+Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
+ unsigned char tmp[Num3072::BYTE_SIZE];
+
+ uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256();
+ ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, Num3072::BYTE_SIZE);
+ Num3072 out{tmp};
+
return out;
}
@@ -301,14 +315,8 @@ void MuHash3072::Finalize(uint256& out) noexcept
m_numerator.Divide(m_denominator);
m_denominator.SetToOne(); // Needed to keep the MuHash object valid
- unsigned char data[384];
- for (int i = 0; i < LIMBS; ++i) {
- if (sizeof(limb_t) == 4) {
- WriteLE32(data + i * 4, m_numerator.limbs[i]);
- } else if (sizeof(limb_t) == 8) {
- WriteLE64(data + i * 8, m_numerator.limbs[i]);
- }
- }
+ unsigned char data[Num3072::BYTE_SIZE];
+ m_numerator.ToBytes(data);
out = (CHashWriter(SER_DISK, 0) << data).GetSHA256();
}
diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h
index 0c710007c4..c023a8b9d3 100644
--- a/src/crypto/muhash.h
+++ b/src/crypto/muhash.h
@@ -22,6 +22,7 @@ private:
Num3072 GetInverse() const;
public:
+ static constexpr size_t BYTE_SIZE = 384;
#ifdef HAVE___INT128
typedef unsigned __int128 double_limb_t;
@@ -48,8 +49,10 @@ public:
void Divide(const Num3072& a);
void SetToOne();
void Square();
+ void ToBytes(unsigned char (&out)[BYTE_SIZE]);
Num3072() { this->SetToOne(); };
+ Num3072(const unsigned char (&data)[BYTE_SIZE]);
SERIALIZE_METHODS(Num3072, obj)
{
@@ -78,7 +81,7 @@ public:
* arbitrary subset of the update operations, allowing them to be
* efficiently combined later.
*
- * Muhash does not support checking if an element is already part of the
+ * MuHash does not support checking if an element is already part of the
* set. That is why this class does not enforce the use of a set as the
* data it represents because there is no efficient way to do so.
* It is possible to add elements more than once and also to remove
@@ -91,8 +94,6 @@ public:
class MuHash3072
{
private:
- static constexpr size_t BYTE_SIZE = 384;
-
Num3072 m_numerator;
Num3072 m_denominator;
diff --git a/src/flatfile.cpp b/src/flatfile.cpp
index 11cf357f3d..151f1a38f1 100644
--- a/src/flatfile.cpp
+++ b/src/flatfile.cpp
@@ -66,7 +66,7 @@ size_t FlatFileSeq::Allocate(const FlatFilePos& pos, size_t add_size, bool& out_
if (CheckDiskSpace(m_dir, inc_size)) {
FILE *file = Open(pos);
if (file) {
- LogPrintf("Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile);
+ LogPrint(BCLog::VALIDATION, "Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile);
AllocateFileRange(file, pos.nPos, inc_size);
fclose(file);
return inc_size;
diff --git a/src/init.cpp b/src/init.cpp
index 2647fd0ce2..96fb32ce2a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -434,8 +434,9 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listenonion", strprintf("Automatically create Tor onion service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -454,8 +455,8 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
- argsman.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
@@ -519,10 +520,11 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-capturemessages", "Capture all P2P messages to disk", ArgsManager::ALLOW_BOOL | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
- "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + LogInstance().LogCategoriesString() + ".",
+ "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except the specified category. This option can be specified multiple times to exclude multiple categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
#ifdef HAVE_THREAD_LOCAL
@@ -1040,16 +1042,17 @@ bool AppInitParameterInteraction(const ArgsManager& args)
// Trim requested connection counts, to fit into system limitations
// <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
- nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind);
+ nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind + NUM_FDS_MESSAGE_CAPTURE);
+
#ifdef USE_POLL
int fd_max = nFD;
#else
int fd_max = FD_SETSIZE;
#endif
- nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
+ nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
- nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
+ nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE, nMaxConnections);
if (nMaxConnections < nUserMaxConnections)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
diff --git a/src/net.cpp b/src/net.cpp
index 76bf7effa4..68388da094 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -20,6 +20,7 @@
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
+#include <util/sock.h>
#include <util/strencodings.h>
#include <util/translation.h>
@@ -429,24 +430,26 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Connect
bool connected = false;
- SOCKET hSocket = INVALID_SOCKET;
+ std::unique_ptr<Sock> sock;
proxyType proxy;
if (addrConnect.IsValid()) {
bool proxyConnectionFailed = false;
if (GetProxy(addrConnect.GetNetwork(), proxy)) {
- hSocket = CreateSocket(proxy.proxy);
- if (hSocket == INVALID_SOCKET) {
+ sock = CreateSock(proxy.proxy);
+ if (!sock) {
return nullptr;
}
- connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, proxyConnectionFailed);
+ connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(),
+ *sock, nConnectTimeout, proxyConnectionFailed);
} else {
// no proxy needed (none set for target network)
- hSocket = CreateSocket(addrConnect);
- if (hSocket == INVALID_SOCKET) {
+ sock = CreateSock(addrConnect);
+ if (!sock) {
return nullptr;
}
- connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, conn_type == ConnectionType::MANUAL);
+ connected = ConnectSocketDirectly(addrConnect, sock->Get(), nConnectTimeout,
+ conn_type == ConnectionType::MANUAL);
}
if (!proxyConnectionFailed) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
@@ -454,26 +457,26 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addrman.Attempt(addrConnect, fCountFailure);
}
} else if (pszDest && GetNameProxy(proxy)) {
- hSocket = CreateSocket(proxy.proxy);
- if (hSocket == INVALID_SOCKET) {
+ sock = CreateSock(proxy.proxy);
+ if (!sock) {
return nullptr;
}
std::string host;
int port = default_port;
SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed;
- connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, proxyConnectionFailed);
+ connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,
+ proxyConnectionFailed);
}
if (!connected) {
- CloseSocket(hSocket);
return nullptr;
}
// Add node
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type);
+ CAddress addr_bind = GetBindAddress(sock->Get());
+ CNode* pnode = new CNode(id, nLocalServices, sock->Release(), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /* inbound_onion */ false);
pnode->AddRef();
// We're making a new connection, harvest entropy from the time (and our peer count)
@@ -1769,11 +1772,19 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
// Initiate network connections
- int64_t nStart = GetTime();
+ auto start = GetTime<std::chrono::seconds>();
// Minimum time before next feeler connection (in microseconds).
- int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
- int64_t nNextExtraBlockRelay = PoissonNextSend(nStart*1000*1000, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+
+ int64_t nNextFeeler = PoissonNextSend(count_microseconds(start), FEELER_INTERVAL);
+ int64_t nNextExtraBlockRelay = PoissonNextSend(count_microseconds(start), EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
+ bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
+
+ if (!add_fixed_seeds) {
+ LogPrintf("Fixed seeds are disabled\n");
+ }
+
while (!interruptNet)
{
ProcessAddrFetch();
@@ -1785,18 +1796,32 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (interruptNet)
return;
- // Add seed nodes if DNS seeds are all down (an infrastructure attack?).
- // Note that we only do this if we started with an empty peers.dat,
- // (in which case we will query DNS seeds immediately) *and* the DNS
- // seeds have not returned any results.
- if (addrman.size() == 0 && (GetTime() - nStart > 60)) {
- static bool done = false;
- if (!done) {
- LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n");
+ if (add_fixed_seeds && addrman.size() == 0) {
+ // When the node starts with an empty peers.dat, there are a few other sources of peers before
+ // we fallback on to fixed seeds: -dnsseed, -seednode, -addnode
+ // If none of those are available, we fallback on to fixed seeds immediately, else we allow
+ // 60 seconds for any of those sources to populate addrman.
+ bool add_fixed_seeds_now = false;
+ // It is cheapest to check if enough time has passed first.
+ if (GetTime<std::chrono::seconds>() > start + std::chrono::minutes{1}) {
+ add_fixed_seeds_now = true;
+ LogPrintf("Adding fixed seeds as 60 seconds have passed and addrman is empty\n");
+ }
+
+ // Checking !dnsseed is cheaper before locking 2 mutexes.
+ if (!add_fixed_seeds_now && !dnsseed) {
+ LOCK2(m_addr_fetches_mutex, cs_vAddedNodes);
+ if (m_addr_fetches.empty() && vAddedNodes.empty()) {
+ add_fixed_seeds_now = true;
+ LogPrintf("Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n");
+ }
+ }
+
+ if (add_fixed_seeds_now) {
CNetAddr local;
local.SetInternal("fixedseeds");
addrman.Add(convertSeed6(Params().FixedSeeds()), local);
- done = true;
+ add_fixed_seeds = false;
}
}
@@ -2188,9 +2213,8 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
return false;
}
- SOCKET hListenSocket = CreateSocket(addrBind);
- if (hListenSocket == INVALID_SOCKET)
- {
+ std::unique_ptr<Sock> sock = CreateSock(addrBind);
+ if (!sock) {
strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError.original);
return false;
@@ -2198,21 +2222,21 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
- setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
+ setsockopt(sock->Get(), SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
- setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
+ setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
- setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
+ setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
#endif
}
- if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
+ if (::bind(sock->Get(), (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
@@ -2220,21 +2244,19 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
LogPrintf("%s\n", strError.original);
- CloseSocket(hListenSocket);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
// Listen for incoming connections
- if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR)
+ if (listen(sock->Get(), SOMAXCONN) == SOCKET_ERROR)
{
strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError.original);
- CloseSocket(hListenSocket);
return false;
}
- vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
+ vhListenSocket.push_back(ListenSocket(sock->Release(), permissions));
return true;
}
@@ -2434,7 +2456,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
// Send and receive from sockets, accept connections
threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this)));
- if (!gArgs.GetBoolArg("-dnsseed", true))
+ if (!gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED))
LogPrintf("DNS seeding disabled\n");
else
threadDNSAddressSeed = std::thread(&TraceThread<std::function<void()> >, "dnsseed", std::function<void()>(std::bind(&CConnman::ThreadDNSAddressSeed, this)));
@@ -2833,12 +2855,12 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
: nTimeConnected(GetSystemTimeInSeconds()),
addr(addrIn),
addrBind(addrBindIn),
+ m_inbound_onion(inbound_onion),
nKeyedNetGroup(nKeyedNetGroupIn),
id(idIn),
nLocalHostNonce(nLocalHostNonceIn),
m_conn_type(conn_type_in),
- nLocalServices(nLocalServicesIn),
- m_inbound_onion(inbound_onion)
+ nLocalServices(nLocalServicesIn)
{
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
hSocket = hSocketIn;
@@ -2879,6 +2901,9 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
size_t nMessageSize = msg.data.size();
LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", SanitizeString(msg.m_type), nMessageSize, pnode->GetId());
+ if (gArgs.GetBoolArg("-capturemessages", false)) {
+ CaptureMessage(pnode->addr, msg.m_type, msg.data, /* incoming */ false);
+ }
// make sure we use the appropriate network transport format
std::vector<unsigned char> serializedHeader;
@@ -2894,18 +2919,14 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
pnode->mapSendBytesPerMsgCmd[msg.m_type] += nTotalSize;
pnode->nSendSize += nTotalSize;
- if (pnode->nSendSize > nSendBufferMaxSize)
- pnode->fPauseSend = true;
+ if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true;
pnode->vSendMsg.push_back(std::move(serializedHeader));
- if (nMessageSize)
- pnode->vSendMsg.push_back(std::move(msg.data));
+ if (nMessageSize) pnode->vSendMsg.push_back(std::move(msg.data));
// If write queue empty, attempt "optimistic write"
- if (optimisticSend == true)
- nBytesSent = SocketSendData(*pnode);
+ if (optimisticSend) nBytesSent = SocketSendData(*pnode);
}
- if (nBytesSent)
- RecordBytesSent(nBytesSent);
+ if (nBytesSent) RecordBytesSent(nBytesSent);
}
bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
@@ -2948,3 +2969,31 @@ uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const
return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize();
}
+
+void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Span<const unsigned char>& data, bool is_incoming)
+{
+ // Note: This function captures the message at the time of processing,
+ // not at socket receive/send time.
+ // This ensures that the messages are always in order from an application
+ // layer (processing) perspective.
+ auto now = GetTime<std::chrono::microseconds>();
+
+ // Windows folder names can not include a colon
+ std::string clean_addr = addr.ToString();
+ std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
+
+ fs::path base_path = GetDataDir() / "message_capture" / clean_addr;
+ fs::create_directories(base_path);
+
+ fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat");
+ CAutoFile f(fsbridge::fopen(path, "ab"), SER_DISK, CLIENT_VERSION);
+
+ ser_writedata64(f, now.count());
+ f.write(msg_type.data(), msg_type.length());
+ for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) {
+ f << '\0';
+ }
+ uint32_t size = data.size();
+ ser_writedata32(f, size);
+ f.write((const char*)data.data(), data.size());
+}
diff --git a/src/net.h b/src/net.h
index 86fcee512a..79efb4a566 100644
--- a/src/net.h
+++ b/src/net.h
@@ -20,6 +20,7 @@
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
+#include <span.h>
#include <streams.h>
#include <sync.h>
#include <threadinterrupt.h>
@@ -75,8 +76,12 @@ static constexpr uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
static const bool DEFAULT_BLOCKSONLY = false;
/** -peertimeout default */
static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
+/** Number of file descriptors required for message capture **/
+static const int NUM_FDS_MESSAGE_CAPTURE = 1;
static const bool DEFAULT_FORCEDNSSEED = false;
+static const bool DEFAULT_DNSSEED = true;
+static const bool DEFAULT_FIXEDSEEDS = true;
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
@@ -424,6 +429,8 @@ public:
const CAddress addr;
// Bind address of our side of the connection
const CAddress addrBind;
+ //! Whether this peer is an inbound onion, i.e. connected via our Tor onion service.
+ const bool m_inbound_onion;
std::atomic<int> nVersion{0};
RecursiveMutex cs_SubVer;
/**
@@ -442,6 +449,7 @@ public:
* messages, implying a preference to receive ADDRv2 instead of ADDR ones.
*/
std::atomic_bool m_wants_addrv2{false};
+ /** fSuccessfullyConnected is set to true on receiving VERACK from the peer. */
std::atomic_bool fSuccessfullyConnected{false};
// Setting fDisconnect to true will cause the node to be disconnected the
// next time DisconnectNodes() runs
@@ -595,7 +603,7 @@ public:
// Whether a ping is requested.
std::atomic<bool> fPingQueued{false};
- CNode(NodeId id, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion = false);
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion);
~CNode();
CNode(const CNode&) = delete;
CNode& operator=(const CNode&) = delete;
@@ -713,9 +721,6 @@ public:
std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); }
- /** Whether this peer is an inbound onion, e.g. connected via our Tor onion service. */
- bool IsInboundOnion() const { return m_inbound_onion; }
-
private:
const NodeId id;
const uint64_t nLocalHostNonce;
@@ -748,9 +753,6 @@ private:
CService addrLocal GUARDED_BY(cs_addrLocal);
mutable RecursiveMutex cs_addrLocal;
- //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service.
- const bool m_inbound_onion{false};
-
mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend);
mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
};
@@ -1241,6 +1243,9 @@ inline std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now,
return std::chrono::microseconds{PoissonNextSend(now.count(), average_interval.count())};
}
+/** Dump binary message to file, with timestamp */
+void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Span<const unsigned char>& data, bool is_incoming);
+
struct NodeEvictionCandidate
{
NodeId id;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b68453759a..c782498e14 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -169,6 +169,14 @@ void EraseOrphansFor(NodeId peer);
// Internal stuff
namespace {
+/** Blocks that are in flight, and that are in the queue to be downloaded. */
+struct QueuedBlock {
+ uint256 hash;
+ const CBlockIndex* pindex; //!< Optional.
+ bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
+ std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
+};
+
/**
* Data structure for an individual peer. This struct is not protected by
* cs_main since it does not contain validation-critical data.
@@ -345,10 +353,7 @@ private:
* their own locks.
*/
std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex);
-};
-} // namespace
-namespace {
/** Number of nodes with fSyncStarted. */
int nSyncStarted GUARDED_BY(cs_main) = 0;
@@ -360,6 +365,14 @@ namespace {
*/
std::map<uint256, std::pair<NodeId, bool>> mapBlockSource GUARDED_BY(cs_main);
+ /** Number of peers with wtxid relay. */
+ int m_wtxid_relay_peers GUARDED_BY(cs_main) = 0;
+
+ /** Number of outbound peers with m_chain_sync.m_protect. */
+ int m_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
+
+ bool AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
/**
* Filter for transactions that were recently rejected by
* AcceptToMemoryPool. These are not rerequested until the chain tip
@@ -402,35 +415,36 @@ namespace {
* We use this to avoid requesting transactions that have already been
* confirnmed.
*/
- Mutex g_cs_recent_confirmed_transactions;
- std::unique_ptr<CRollingBloomFilter> g_recent_confirmed_transactions GUARDED_BY(g_cs_recent_confirmed_transactions);
-
- /** Blocks that are in flight, and that are in the queue to be downloaded. */
- struct QueuedBlock {
- uint256 hash;
- const CBlockIndex* pindex; //!< Optional.
- bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
- std::unique_ptr<PartiallyDownloadedBlock> partialBlock; //!< Optional, used for CMPCTBLOCK downloads
- };
- std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight GUARDED_BY(cs_main);
+ Mutex m_recent_confirmed_transactions_mutex;
+ std::unique_ptr<CRollingBloomFilter> m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex);
- /** Stack of nodes which we have set to announce using compact blocks */
- std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
+ /* Returns a bool indicating whether we requested this block.
+ * Also used if a block was /not/ received and timed out or started with another peer
+ */
+ bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of preferable block download peers. */
- int nPreferredDownload GUARDED_BY(cs_main) = 0;
+ /* Mark a block as in flight
+ * Returns false, still setting pit, if the block was already in flight from the same peer
+ * pit will only be valid as long as the same cs_main lock is being held
+ */
+ bool MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of peers from which we're downloading blocks. */
- int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+ bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of peers with wtxid relay. */
- int g_wtxid_relay_peers GUARDED_BY(cs_main) = 0;
+ /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
+ * at most count entries.
+ */
+ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Number of outbound peers with m_chain_sync.m_protect. */
- int g_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0;
+ std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight GUARDED_BY(cs_main);
/** When our tip was last updated. */
- std::atomic<int64_t> g_last_tip_update(0);
+ std::atomic<int64_t> m_last_tip_update{0};
+
+ /** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
+ CTransactionRef FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main);
+
+ void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex);
/** Relay map (txid or wtxid -> CTransactionRef) */
typedef std::map<uint256, CTransactionRef> MapRelay;
@@ -438,6 +452,28 @@ namespace {
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
+ /**
+ * When a peer sends us a valid block, instruct it to announce blocks to us
+ * using CMPCTBLOCK if possible by adding its nodeid to the end of
+ * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
+ * removing the first element if necessary.
+ */
+ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ /** Stack of nodes which we have set to announce using compact blocks */
+ std::list<NodeId> lNodesAnnouncingHeaderAndIDs GUARDED_BY(cs_main);
+
+ /** Number of peers from which we're downloading blocks. */
+ int nPeersWithValidatedDownloads GUARDED_BY(cs_main) = 0;
+
+};
+} // namespace
+
+namespace {
+
+ /** Number of preferable block download peers. */
+ int nPreferredDownload GUARDED_BY(cs_main) = 0;
+
struct IteratorComparator
{
template<typename I>
@@ -610,9 +646,8 @@ static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUS
nPreferredDownload += state->fPreferredDownload;
}
-// Returns a bool indicating whether we requested this block.
-// Also used if a block was /not/ received and timed out or started with another peer
-static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
if (itInFlight != mapBlocksInFlight.end()) {
CNodeState *state = State(itInFlight->second.first);
@@ -635,9 +670,8 @@ static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs
return false;
}
-// returns false, still setting pit, if the block was already in flight from the same peer
-// pit will only be valid as long as the same cs_main lock is being held
-static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint256& hash, const CBlockIndex* pindex = nullptr, std::list<QueuedBlock>::iterator** pit = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
+bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
CNodeState *state = State(nodeid);
assert(state != nullptr);
@@ -654,7 +688,7 @@ static bool MarkBlockAsInFlight(CTxMemPool& mempool, NodeId nodeid, const uint25
MarkBlockAsReceived(hash);
std::list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(),
- {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&mempool) : nullptr)});
+ {hash, pindex, pindex != nullptr, std::unique_ptr<PartiallyDownloadedBlock>(pit ? new PartiallyDownloadedBlock(&m_mempool) : nullptr)});
state->nBlocksInFlight++;
state->nBlocksInFlightValidHeaders += it->fValidatedHeaders;
if (state->nBlocksInFlight == 1) {
@@ -705,13 +739,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV
}
}
-/**
- * When a peer sends us a valid block, instruct it to announce blocks to us
- * using CMPCTBLOCK if possible by adding its nodeid to the end of
- * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by
- * removing the first element if necessary.
- */
-static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
CNodeState* nodestate = State(nodeid);
@@ -727,21 +755,21 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
return;
}
}
- connman.ForNode(nodeid, [&connman](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ m_connman.ForNode(nodeid, [this](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
- connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
- connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
+ m_connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [this, nCMPCTBLOCKVersion](CNode* pnodeStop){
+ m_connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
// save BIP152 bandwidth state: we select peer to be low-bandwidth
pnodeStop->m_bip152_highbandwidth_to = false;
return true;
});
lNodesAnnouncingHeaderAndIDs.pop_front();
}
- connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
+ m_connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion));
// save BIP152 bandwidth state: we select peer to be high-bandwidth
pfrom->m_bip152_highbandwidth_to = true;
lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId());
@@ -750,13 +778,14 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
}
}
-static bool TipMayBeStale(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
- if (g_last_tip_update == 0) {
- g_last_tip_update = GetTime();
+ const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
+ if (m_last_tip_update == 0) {
+ m_last_tip_update = GetTime();
}
- return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+ return m_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
}
static bool CanDirectFetch(const Consensus::Params &consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -773,9 +802,7 @@ static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIV
return false;
}
-/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
- * at most count entries. */
-static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (count == 0)
return;
@@ -804,6 +831,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
return;
+ const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
// Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
@@ -911,7 +939,7 @@ void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid,
auto delay = std::chrono::microseconds{0};
const bool preferred = state->fPreferredDownload;
if (!preferred) delay += NONPREF_PEER_TX_DELAY;
- if (!gtxid.IsWtxid() && g_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
+ if (!gtxid.IsWtxid() && m_wtxid_relay_peers > 0) delay += TXID_RELAY_DELAY;
const bool overloaded = !node.HasPermission(PF_RELAY) &&
m_txrequest.CountInFlight(nodeid) >= MAX_PEER_TX_REQUEST_IN_FLIGHT;
if (overloaded) delay += OVERLOADED_PEER_TX_DELAY;
@@ -1004,10 +1032,10 @@ void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTim
nPreferredDownload -= state->fPreferredDownload;
nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0);
assert(nPeersWithValidatedDownloads >= 0);
- g_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
- assert(g_outbound_peers_with_protect_from_disconnect >= 0);
- g_wtxid_relay_peers -= state->m_wtxid_relay;
- assert(g_wtxid_relay_peers >= 0);
+ m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync.m_protect;
+ assert(m_outbound_peers_with_protect_from_disconnect >= 0);
+ m_wtxid_relay_peers -= state->m_wtxid_relay;
+ assert(m_wtxid_relay_peers >= 0);
mapNodeState.erase(nodeid);
@@ -1016,8 +1044,8 @@ void PeerManagerImpl::FinalizeNode(const CNode& node, bool& fUpdateConnectionTim
assert(mapBlocksInFlight.empty());
assert(nPreferredDownload == 0);
assert(nPeersWithValidatedDownloads == 0);
- assert(g_outbound_peers_with_protect_from_disconnect == 0);
- assert(g_wtxid_relay_peers == 0);
+ assert(m_outbound_peers_with_protect_from_disconnect == 0);
+ assert(m_wtxid_relay_peers == 0);
assert(m_txrequest.Size() == 0);
}
LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid);
@@ -1344,7 +1372,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
// The false positive rate of 1/1M should come out to less than 1
// transaction per day that would be inadvertently ignored (which is the
// same probability that we have in the reject filter).
- g_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
+ m_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
// Stale tip checking and peer eviction are on two different timers, but we
// don't want them to get out of sync due to drift in the scheduler, so we
@@ -1394,14 +1422,14 @@ void PeerManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
}
- g_last_tip_update = GetTime();
+ m_last_tip_update = GetTime();
}
{
- LOCK(g_cs_recent_confirmed_transactions);
+ LOCK(m_recent_confirmed_transactions_mutex);
for (const auto& ptx : pblock->vtx) {
- g_recent_confirmed_transactions->insert(ptx->GetHash());
+ m_recent_confirmed_transactions->insert(ptx->GetHash());
if (ptx->GetHash() != ptx->GetWitnessHash()) {
- g_recent_confirmed_transactions->insert(ptx->GetWitnessHash());
+ m_recent_confirmed_transactions->insert(ptx->GetWitnessHash());
}
}
}
@@ -1424,8 +1452,8 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
// block's worth of transactions in it, but that should be fine, since
// presumably the most common case of relaying a confirmed transaction
// should be just after a new block containing it is found.
- LOCK(g_cs_recent_confirmed_transactions);
- g_recent_confirmed_transactions->reset();
+ LOCK(m_recent_confirmed_transactions_mutex);
+ m_recent_confirmed_transactions->reset();
}
// All of the following cache a recent block, and are protected by cs_most_recent_block
@@ -1550,7 +1578,7 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
!::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
- MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, m_connman);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first);
}
}
if (it != mapBlockSource.end())
@@ -1563,7 +1591,7 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
//
-bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
assert(recentRejects);
if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
@@ -1587,11 +1615,11 @@ bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLU
}
{
- LOCK(g_cs_recent_confirmed_transactions);
- if (g_recent_confirmed_transactions->contains(hash)) return true;
+ LOCK(m_recent_confirmed_transactions_mutex);
+ if (m_recent_confirmed_transactions->contains(hash)) return true;
}
- return recentRejects->contains(hash) || mempool.exists(gtxid);
+ return recentRejects->contains(hash) || m_mempool.exists(gtxid);
}
bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1825,10 +1853,9 @@ void static ProcessGetBlockData(CNode& pfrom, Peer& peer, const CChainParams& ch
}
}
-//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
-static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
+CTransactionRef PeerManagerImpl::FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
{
- auto txinfo = mempool.info(gtxid);
+ auto txinfo = m_mempool.info(gtxid);
if (txinfo.tx) {
// If a TX could have been INVed in reply to a MEMPOOL request,
// or is older than UNCONDITIONAL_RELAY_DELAY, permit the request
@@ -1853,7 +1880,7 @@ static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode&
return {};
}
-void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainparams, CConnman& connman, CTxMemPool& mempool, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex)
+void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex)
{
AssertLockNotHeld(cs_main);
@@ -1882,17 +1909,17 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa
continue;
}
- CTransactionRef tx = FindTxForGetData(mempool, pfrom, ToGenTxid(inv), mempool_req, now);
+ CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), mempool_req, now);
if (tx) {
// WTX and WITNESS_TX imply we serialize with witness
int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
- mempool.RemoveUnbroadcastTx(tx->GetHash());
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *tx));
+ m_mempool.RemoveUnbroadcastTx(tx->GetHash());
// As we're going to send tx, make sure its unconfirmed parents are made requestable.
std::vector<uint256> parent_ids_to_add;
{
- LOCK(mempool.cs);
- auto txiter = mempool.GetIter(tx->GetHash());
+ LOCK(m_mempool.cs);
+ auto txiter = m_mempool.GetIter(tx->GetHash());
if (txiter) {
const CTxMemPoolEntry::Parents& parents = (*txiter)->GetMemPoolParentsConst();
parent_ids_to_add.reserve(parents.size());
@@ -1920,7 +1947,7 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa
if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
if (inv.IsGenBlkMsg()) {
- ProcessGetBlockData(pfrom, peer, chainparams, inv, connman);
+ ProcessGetBlockData(pfrom, peer, m_chainparams, inv, m_connman);
}
// else: If the first item on the queue is an unknown type, we erase it
// and continue processing the queue on the next call.
@@ -1943,7 +1970,7 @@ void static ProcessGetData(CNode& pfrom, Peer& peer, const CChainParams& chainpa
// In normal operation, we often send NOTFOUND messages for parents of
// transactions that we relay; if a peer is missing a parent, they may
// assume we have them and request the parents from us.
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound));
}
}
@@ -2102,7 +2129,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -2146,10 +2173,10 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// thus always subject to eviction under the bad/lagging chain logic.
// See ChainSyncTimeoutState.
if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
- if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
nodestate->m_chain_sync.m_protect = true;
- ++g_outbound_peers_with_protect_from_disconnect;
+ ++m_outbound_peers_with_protect_from_disconnect;
}
}
}
@@ -2178,10 +2205,10 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
if (orphan_it == mapOrphanTransactions.end()) continue;
const CTransactionRef porphanTx = orphan_it->second.tx;
- TxValidationState state;
- std::list<CTransactionRef> removed_txn;
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, porphanTx, false /* bypass_limits */);
+ const TxValidationState& state = result.m_state;
- if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */)) {
+ if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman);
for (unsigned int i = 0; i < porphanTx->vout.size(); i++) {
@@ -2193,7 +2220,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
}
}
EraseOrphanTx(orphanHash);
- for (const CTransactionRef& removedTx : removed_txn) {
+ for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
AddToCompactExtraTransactions(removedTx);
}
break;
@@ -2489,6 +2516,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
bool fRelay = true;
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe;
+ if (nTime < 0) {
+ nTime = 0;
+ }
nServices = ServiceFlags(nServiceInt);
if (!pfrom.IsInboundConn())
{
@@ -2745,12 +2775,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- // Feature negotiation of wtxidrelay must happen between VERSION and VERACK
- // to avoid relay problems from switching after a connection is up.
+ // BIP339 defines feature negotiation of wtxidrelay, which must happen between
+ // VERSION and VERACK to avoid relay problems from switching after a connection is up.
if (msg_type == NetMsgType::WTXIDRELAY) {
if (pfrom.fSuccessfullyConnected) {
- // Disconnect peers that send wtxidrelay message after VERACK; this
- // must be negotiated between VERSION and VERACK.
+ // Disconnect peers that send a wtxidrelay message after VERACK.
LogPrint(BCLog::NET, "wtxidrelay received after verack from peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
@@ -2759,7 +2788,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LOCK(cs_main);
if (!State(pfrom.GetId())->m_wtxid_relay) {
State(pfrom.GetId())->m_wtxid_relay = true;
- g_wtxid_relay_peers++;
+ m_wtxid_relay_peers++;
} else {
LogPrint(BCLog::NET, "ignoring duplicate wtxidrelay from peer=%d\n", pfrom.GetId());
}
@@ -2769,10 +2798,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ // BIP155 defines feature negotiation of addrv2 and sendaddrv2, which must happen
+ // between VERSION and VERACK.
if (msg_type == NetMsgType::SENDADDRV2) {
if (pfrom.fSuccessfullyConnected) {
- // Disconnect peers that send SENDADDRV2 message after VERACK; this
- // must be negotiated between VERSION and VERACK.
+ // Disconnect peers that send a SENDADDRV2 message after VERACK.
LogPrint(BCLog::NET, "sendaddrv2 received after verack from peer=%d; disconnecting\n", pfrom.GetId());
pfrom.fDisconnect = true;
return;
@@ -2901,7 +2931,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
} else if (inv.IsGenTxMsg()) {
const GenTxid gtxid = ToGenTxid(inv);
- const bool fAlreadyHave = AlreadyHaveTx(gtxid, m_mempool);
+ const bool fAlreadyHave = AlreadyHaveTx(gtxid);
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
pfrom.AddKnownTx(inv.hash);
@@ -2943,7 +2973,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
{
LOCK(peer->m_getdata_requests_mutex);
peer->m_getdata_requests.insert(peer->m_getdata_requests.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ ProcessGetData(pfrom, *peer, interruptMsgProc);
}
return;
@@ -3182,7 +3212,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid), m_mempool)) {
+ if (AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid))) {
if (pfrom.HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from peers with forcerelay
// permission, even if they were already in the mempool, allowing
@@ -3197,10 +3227,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
- TxValidationState state;
- std::list<CTransactionRef> lRemovedTxn;
+ const MempoolAcceptResult result = AcceptToMemoryPool(m_mempool, ptx, false /* bypass_limits */);
+ const TxValidationState& state = result.m_state;
- if (AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */)) {
+ if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
m_mempool.check(&::ChainstateActive().CoinsTip());
// As this version of the transaction was acceptable, we can forget about any
// requests for it.
@@ -3223,7 +3253,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
tx.GetHash().ToString(),
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
- for (const CTransactionRef& removedTx : lRemovedTxn) {
+ for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
AddToCompactExtraTransactions(removedTx);
}
@@ -3261,7 +3291,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// protocol for getting all unconfirmed parents.
const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
pfrom.AddKnownTx(parent_txid);
- if (!AlreadyHaveTx(gtxid, m_mempool)) AddTxAnnouncement(pfrom, gtxid, current_time);
+ if (!AlreadyHaveTx(gtxid)) AddTxAnnouncement(pfrom, gtxid, current_time);
}
AddOrphanTx(ptx, pfrom.GetId());
@@ -3451,7 +3481,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) ||
(fAlreadyInFlight && blockInFlightIt->second.first == pfrom.GetId())) {
std::list<QueuedBlock>::iterator* queuedBlockIt = nullptr;
- if (!MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
+ if (!MarkBlockAsInFlight(pfrom.GetId(), pindex->GetBlockHash(), pindex, &queuedBlockIt)) {
if (!(*queuedBlockIt)->partialBlock)
(*queuedBlockIt)->partialBlock.reset(new PartiallyDownloadedBlock(&m_mempool));
else {
@@ -4018,7 +4048,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
{
LOCK(peer->m_getdata_requests_mutex);
if (!peer->m_getdata_requests.empty()) {
- ProcessGetData(*pfrom, *peer, m_chainparams, m_connman, m_mempool, interruptMsgProc);
+ ProcessGetData(*pfrom, *peer, interruptMsgProc);
}
}
@@ -4045,14 +4075,12 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
}
// Don't bother if send buffer is too full to respond anyway
- if (pfrom->fPauseSend)
- return false;
+ if (pfrom->fPauseSend) return false;
std::list<CNetMessage> msgs;
{
LOCK(pfrom->cs_vProcessMsg);
- if (pfrom->vProcessMsg.empty())
- return false;
+ if (pfrom->vProcessMsg.empty()) return false;
// Just take one message
msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
pfrom->nProcessQueueSize -= msgs.front().m_raw_message_size;
@@ -4061,6 +4089,10 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
}
CNetMessage& msg(msgs.front());
+ if (gArgs.GetBoolArg("-capturemessages", false)) {
+ CaptureMessage(pfrom->addr, msg.m_command, MakeUCharSpan(msg.m_recv), /* incoming */ true);
+ }
+
msg.SetVersion(pfrom->GetCommonVersion());
const std::string& msg_type = msg.m_command;
@@ -4248,8 +4280,8 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
if (time_in_seconds > m_stale_tip_check_time) {
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
- if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(m_chainparams.GetConsensus())) {
- LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
+ if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) {
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - m_last_tip_update);
m_connman.SetTryNewOutboundPeer(true);
} else if (m_connman.GetTryNewOutboundPeer()) {
m_connman.SetTryNewOutboundPeer(false);
@@ -4776,11 +4808,11 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
- FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);
+ FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
for (const CBlockIndex *pindex : vToDownload) {
uint32_t nFetchFlags = GetFetchFlags(*pto);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(m_mempool, pto->GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
pindex->nHeight, pto->GetId());
}
@@ -4802,7 +4834,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
entry.second.GetHash().ToString(), entry.first);
}
for (const GenTxid& gtxid : requestable) {
- if (!AlreadyHaveTx(gtxid, m_mempool)) {
+ if (!AlreadyHaveTx(gtxid)) {
LogPrint(BCLog::NET, "Requesting %s %s peer=%d\n", gtxid.IsWtxid() ? "wtx" : "tx",
gtxid.GetHash().ToString(), pto->GetId());
vGetData.emplace_back(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 93b4c9d659..0c5b3a220e 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -7,13 +7,17 @@
#include <sync.h>
#include <tinyformat.h>
+#include <util/sock.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/time.h>
#include <atomic>
#include <cstdint>
+#include <functional>
#include <limits>
+#include <memory>
#ifndef WIN32
#include <fcntl.h>
@@ -285,14 +289,6 @@ CService LookupNumeric(const std::string& name, int portDefault)
return addr;
}
-struct timeval MillisToTimeval(int64_t nTimeout)
-{
- struct timeval timeout;
- timeout.tv_sec = nTimeout / 1000;
- timeout.tv_usec = (nTimeout % 1000) * 1000;
- return timeout;
-}
-
/** SOCKS version */
enum SOCKSVersion: uint8_t {
SOCKS4 = 0x04,
@@ -350,8 +346,7 @@ enum class IntrRecvError {
* @param data The buffer where the read bytes should be stored.
* @param len The number of bytes to read into the specified buffer.
* @param timeout The total timeout in milliseconds for this read.
- * @param hSocket The socket (has to be in non-blocking mode) from which to read
- * bytes.
+ * @param sock The socket (has to be in non-blocking mode) from which to read bytes.
*
* @returns An IntrRecvError indicating the resulting status of this read.
* IntrRecvError::OK only if all of the specified number of bytes were
@@ -361,7 +356,7 @@ enum class IntrRecvError {
* Sockets can be made non-blocking with SetSocketNonBlocking(const
* SOCKET&, bool).
*/
-static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
+static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
@@ -369,7 +364,7 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
// (in millis) to break off in case of an interruption.
const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
- ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
+ ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first
if (ret > 0) {
len -= ret;
data += ret;
@@ -378,25 +373,10 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
} else { // Other error or blocking
int nErr = WSAGetLastError();
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
- if (!IsSelectableSocket(hSocket)) {
- return IntrRecvError::NetworkError;
- }
// Only wait at most maxWait milliseconds at a time, unless
// we're approaching the end of the specified total timeout
int timeout_ms = std::min(endTime - curTime, maxWait);
-#ifdef USE_POLL
- struct pollfd pollfd = {};
- pollfd.fd = hSocket;
- pollfd.events = POLLIN;
- int nRet = poll(&pollfd, 1, timeout_ms);
-#else
- struct timeval tval = MillisToTimeval(timeout_ms);
- fd_set fdset;
- FD_ZERO(&fdset);
- FD_SET(hSocket, &fdset);
- int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
-#endif
- if (nRet == SOCKET_ERROR) {
+ if (!sock.Wait(std::chrono::milliseconds{timeout_ms}, Sock::RECV)) {
return IntrRecvError::NetworkError;
}
} else {
@@ -450,7 +430,7 @@ static std::string Socks5ErrorString(uint8_t err)
* @param port The destination port.
* @param auth The credentials with which to authenticate with the specified
* SOCKS5 proxy.
- * @param hSocket The SOCKS5 proxy socket.
+ * @param sock The SOCKS5 proxy socket.
*
* @returns Whether or not the operation succeeded.
*
@@ -460,7 +440,7 @@ static std::string Socks5ErrorString(uint8_t err)
* @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
* Version 5</a>
*/
-static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
+static bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock)
{
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
@@ -478,12 +458,12 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5Init.push_back(0x01); // 1 method identifier follows...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
- ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
+ ssize_t ret = sock.Send(vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5Init.size()) {
return error("Error sending to proxy");
}
uint8_t pchRet1[2];
- if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
return false;
}
@@ -500,13 +480,13 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end());
vAuth.push_back(auth->password.size());
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
- ret = send(hSocket, (const char*)vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
+ ret = sock.Send(vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vAuth.size()) {
return error("Error sending authentication to proxy");
}
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
uint8_t pchRetA[2];
- if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
return error("Error reading proxy authentication response");
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
@@ -526,12 +506,12 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
vSocks5.push_back((port >> 8) & 0xFF);
vSocks5.push_back((port >> 0) & 0xFF);
- ret = send(hSocket, (const char*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
+ ret = sock.Send(vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5.size()) {
return error("Error sending to proxy");
}
uint8_t pchRet2[4];
- if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
if (recvr == IntrRecvError::Timeout) {
/* If a timeout happens here, this effectively means we timed out while connecting
* to the remote node. This is very common for Tor, so do not print an
@@ -555,16 +535,16 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
uint8_t pchRet3[256];
switch (pchRet2[3])
{
- case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
- case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
+ case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, sock); break;
+ case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, sock); break;
case SOCKS5Atyp::DOMAINNAME:
{
- recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
+ recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, sock);
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
int nRecv = pchRet3[0];
- recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
+ recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, sock);
break;
}
default: return error("Error: malformed proxy response");
@@ -572,41 +552,35 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
- if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
return error("Error reading from proxy");
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
return true;
}
-/**
- * Try to create a socket file descriptor with specific properties in the
- * communications domain (address family) of the specified service.
- *
- * For details on the desired properties, see the inline comments in the source
- * code.
- */
-SOCKET CreateSocket(const CService &addrConnect)
+std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
{
// Create a sockaddr from the specified service.
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
- return INVALID_SOCKET;
+ if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
+ LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToString());
+ return nullptr;
}
// Create a TCP socket in the address family of the specified service.
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
- if (hSocket == INVALID_SOCKET)
- return INVALID_SOCKET;
+ if (hSocket == INVALID_SOCKET) {
+ return nullptr;
+ }
// Ensure that waiting for I/O on this socket won't result in undefined
// behavior.
if (!IsSelectableSocket(hSocket)) {
CloseSocket(hSocket);
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
- return INVALID_SOCKET;
+ return nullptr;
}
#ifdef SO_NOSIGPIPE
@@ -622,11 +596,14 @@ SOCKET CreateSocket(const CService &addrConnect)
// Set the non-blocking option on the socket.
if (!SetSocketNonBlocking(hSocket, true)) {
CloseSocket(hSocket);
- LogPrintf("CreateSocket: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
+ LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
+ return nullptr;
}
- return hSocket;
+ return std::make_unique<Sock>(hSocket);
}
+std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
+
template<typename... Args>
static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) {
std::string error_message = tfm::format(fmt, args...);
@@ -800,7 +777,7 @@ bool IsProxy(const CNetAddr &addr) {
* @param proxy The SOCKS5 proxy.
* @param strDest The destination service to which to connect.
* @param port The destination port.
- * @param hSocket The socket on which to connect to the SOCKS5 proxy.
+ * @param sock The socket on which to connect to the SOCKS5 proxy.
* @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
* proxy to be established.
* @param[out] outProxyConnectionFailed Whether or not the connection to the
@@ -808,10 +785,10 @@ bool IsProxy(const CNetAddr &addr) {
*
* @returns Whether or not the operation succeeded.
*/
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool& outProxyConnectionFailed)
+bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
{
// first connect to proxy server
- if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout, true)) {
+ if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) {
outProxyConnectionFailed = true;
return false;
}
@@ -820,11 +797,11 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int
ProxyCredentials random_auth;
static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++);
- if (!Socks5(strDest, (uint16_t)port, &random_auth, hSocket)) {
+ if (!Socks5(strDest, (uint16_t)port, &random_auth, sock)) {
return false;
}
} else {
- if (!Socks5(strDest, (uint16_t)port, 0, hSocket)) {
+ if (!Socks5(strDest, (uint16_t)port, 0, sock)) {
return false;
}
}
@@ -883,57 +860,6 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
return false;
}
-#ifdef WIN32
-std::string NetworkErrorString(int err)
-{
- wchar_t buf[256];
- buf[0] = 0;
- if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
- nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- buf, ARRAYSIZE(buf), nullptr))
- {
- return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
- }
- else
- {
- return strprintf("Unknown error (%d)", err);
- }
-}
-#else
-std::string NetworkErrorString(int err)
-{
- char buf[256];
- buf[0] = 0;
- /* Too bad there are two incompatible implementations of the
- * thread-safe strerror. */
- const char *s;
-#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
- s = strerror_r(err, buf, sizeof(buf));
-#else /* POSIX variant always returns message in buffer */
- s = buf;
- if (strerror_r(err, buf, sizeof(buf)))
- buf[0] = 0;
-#endif
- return strprintf("%s (%d)", s, err);
-}
-#endif
-
-bool CloseSocket(SOCKET& hSocket)
-{
- if (hSocket == INVALID_SOCKET)
- return false;
-#ifdef WIN32
- int ret = closesocket(hSocket);
-#else
- int ret = close(hSocket);
-#endif
- if (ret) {
- LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
- }
- hSocket = INVALID_SOCKET;
- return ret != SOCKET_ERROR;
-}
-
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking)
{
if (fNonBlocking) {
diff --git a/src/netbase.h b/src/netbase.h
index 17b029a40a..847a72ca8e 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -12,7 +12,10 @@
#include <compat.h>
#include <netaddress.h>
#include <serialize.h>
+#include <util/sock.h>
+#include <functional>
+#include <memory>
#include <stdint.h>
#include <string>
#include <vector>
@@ -53,21 +56,25 @@ bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllo
bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
CService LookupNumeric(const std::string& name, int portDefault = 0);
bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet);
-SOCKET CreateSocket(const CService &addrConnect);
+
+/**
+ * Create a TCP socket in the given address family.
+ * @param[in] address_family The socket is created in the same address family as this address.
+ * @return pointer to the created Sock object or unique_ptr that owns nothing in case of failure
+ */
+std::unique_ptr<Sock> CreateSockTCP(const CService& address_family);
+
+/**
+ * Socket factory. Defaults to `CreateSockTCP()`, but can be overridden by unit tests.
+ */
+extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock;
+
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection);
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool& outProxyConnectionFailed);
-/** Return readable error string for a network error code */
-std::string NetworkErrorString(int err);
-/** Close socket and set hSocket to INVALID_SOCKET */
-bool CloseSocket(SOCKET& hSocket);
+bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
/** Disable or enable blocking-mode for a socket */
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
/** Set the TCP_NODELAY flag on a socket */
bool SetSocketNoDelay(const SOCKET& hSocket);
-/**
- * Convert milliseconds to a struct timeval for e.g. select.
- */
-struct timeval MillisToTimeval(int64_t nTimeout);
void InterruptSocks5(bool interrupt);
#endif // BITCOIN_NETBASE_H
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 56113cb731..b994e79391 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -6,6 +6,7 @@
#include <node/coinstats.h>
#include <coins.h>
+#include <crypto/muhash.h>
#include <hash.h>
#include <serialize.h>
#include <uint256.h>
@@ -24,31 +25,47 @@ static uint64_t GetBogoSize(const CScript& scriptPubKey)
scriptPubKey.size() /* scriptPubKey */;
}
-static void ApplyStats(CCoinsStats& stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+static void ApplyHash(CCoinsStats& stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it)
{
- assert(!outputs.empty());
- ss << hash;
- ss << VARINT(outputs.begin()->second.nHeight * 2 + outputs.begin()->second.fCoinBase ? 1u : 0u);
- stats.nTransactions++;
- for (const auto& output : outputs) {
- ss << VARINT(output.first + 1);
- ss << output.second.out.scriptPubKey;
- ss << VARINT_MODE(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
- stats.nTransactionOutputs++;
- stats.nTotalAmount += output.second.out.nValue;
- stats.nBogoSize += GetBogoSize(output.second.out.scriptPubKey);
+ if (it == outputs.begin()) {
+ ss << hash;
+ ss << VARINT(it->second.nHeight * 2 + it->second.fCoinBase ? 1u : 0u);
+ }
+
+ ss << VARINT(it->first + 1);
+ ss << it->second.out.scriptPubKey;
+ ss << VARINT_MODE(it->second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
+
+ if (it == std::prev(outputs.end())) {
+ ss << VARINT(0u);
}
- ss << VARINT(0u);
}
-static void ApplyStats(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+static void ApplyHash(CCoinsStats& stats, std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it) {}
+
+static void ApplyHash(CCoinsStats& stats, MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs, std::map<uint32_t, Coin>::const_iterator it)
+{
+ COutPoint outpoint = COutPoint(hash, it->first);
+ Coin coin = it->second;
+
+ CDataStream ss(SER_DISK, PROTOCOL_VERSION);
+ ss << outpoint;
+ ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
+ ss << coin.out;
+ muhash.Insert(MakeUCharSpan(ss));
+}
+
+template <typename T>
+static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
assert(!outputs.empty());
stats.nTransactions++;
- for (const auto& output : outputs) {
+ for (auto it = outputs.begin(); it != outputs.end(); ++it) {
+ ApplyHash(stats, hash_obj, hash, outputs, it);
+
stats.nTransactionOutputs++;
- stats.nTotalAmount += output.second.out.nValue;
- stats.nBogoSize += GetBogoSize(output.second.out.scriptPubKey);
+ stats.nTotalAmount += it->second.out.nValue;
+ stats.nBogoSize += GetBogoSize(it->second.out.scriptPubKey);
}
}
@@ -104,6 +121,10 @@ bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, CoinStatsHashType hash_t
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
return GetUTXOStats(view, stats, ss, interruption_point);
}
+ case(CoinStatsHashType::MUHASH): {
+ MuHash3072 muhash;
+ return GetUTXOStats(view, stats, muhash, interruption_point);
+ }
case(CoinStatsHashType::NONE): {
return GetUTXOStats(view, stats, nullptr, interruption_point);
}
@@ -116,10 +137,18 @@ static void PrepareHash(CHashWriter& ss, const CCoinsStats& stats)
{
ss << stats.hashBlock;
}
+// MuHash does not need the prepare step
+static void PrepareHash(MuHash3072& muhash, CCoinsStats& stats) {}
static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
static void FinalizeHash(CHashWriter& ss, CCoinsStats& stats)
{
stats.hashSerialized = ss.GetHash();
}
+static void FinalizeHash(MuHash3072& muhash, CCoinsStats& stats)
+{
+ uint256 out;
+ muhash.Finalize(out);
+ stats.hashSerialized = out;
+}
static void FinalizeHash(std::nullptr_t, CCoinsStats& stats) {}
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 7c56bfc2ad..f02b95235f 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -16,6 +16,7 @@ class CCoinsView;
enum class CoinStatsHashType {
HASH_SERIALIZED,
+ MUHASH,
NONE,
};
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index d3bb9687a8..6f5acf41e3 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -50,22 +50,22 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
}
if (!node.mempool->exists(hashTx)) {
// Transaction is not already in the mempool.
- TxValidationState state;
if (max_tx_fee > 0) {
// First, call ATMP with test_accept and check the fee. If ATMP
// fails here, return error immediately.
- CAmount fee{0};
- if (!AcceptToMemoryPool(*node.mempool, state, tx,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee)) {
- return HandleATMPError(state, err_string);
- } else if (fee > max_tx_fee) {
+ const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
+ true /* test_accept */);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
+ return HandleATMPError(result.m_state, err_string);
+ } else if (result.m_base_fees.value() > max_tx_fee) {
return TransactionError::MAX_FEE_EXCEEDED;
}
}
// Try to submit the transaction to the mempool.
- if (!AcceptToMemoryPool(*node.mempool, state, tx,
- nullptr /* plTxnReplaced */, false /* bypass_limits */)) {
- return HandleATMPError(state, err_string);
+ const MempoolAcceptResult result = AcceptToMemoryPool(*node.mempool, tx, false /* bypass_limits */,
+ false /* test_accept */);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
+ return HandleATMPError(result.m_state, err_string);
}
// Transaction was accepted to the mempool.
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 376acf0963..0c2bbe9516 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -766,10 +766,10 @@ QString NetworkToQString(Network net)
assert(false);
}
-QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes)
+QString ConnectionTypeToQString(ConnectionType conn_type)
{
switch (conn_type) {
- case ConnectionType::INBOUND: return relay_txes ? QObject::tr("Inbound Full Relay") : QObject::tr("Inbound Block Relay");
+ case ConnectionType::INBOUND: return QObject::tr("Inbound");
case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay");
case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay");
case ConnectionType::MANUAL: return QObject::tr("Outbound Manual");
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index edfb5b13a2..9fb108dff9 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -233,7 +233,7 @@ namespace GUIUtil
QString NetworkToQString(Network net);
/** Convert enum ConnectionType to QString */
- QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes);
+ QString ConnectionTypeToQString(ConnectionType conn_type);
/** Convert seconds into a QString with days, hours, mins, secs */
QString formatDurationStr(int secs);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index a252685d2f..2758ee351a 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -462,7 +462,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
constexpr QChar nonbreaking_hyphen(8209);
const std::vector<QString> CONNECTION_TYPE_DOC{
- tr("Inbound Full/Block Relay: initiated by peer"),
+ tr("Inbound: initiated by peer"),
tr("Outbound Full Relay: default"),
tr("Outbound Block Relay: does not relay transactions or addresses"),
tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options")
@@ -1120,7 +1120,7 @@ void RPCConsole::updateDetailWidget()
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
- ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, stats->nodeStats.fRelayTxes));
+ ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type));
ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network));
if (stats->nodeStats.m_permissionFlags == PF_NONE) {
ui->peerPermissions->setText(tr("N/A"));
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index a83b45ae1d..5be02b1e4e 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -17,8 +17,8 @@
#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
-#include <policy/fees.h>
#include <policy/feerate.h>
+#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
@@ -156,21 +156,11 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
{
- // Serialize passed information without accessing chain state of the active chain!
- AssertLockNotHeld(cs_main); // For performance reasons
+ UniValue result = blockheaderToJSON(tip, blockindex);
- UniValue result(UniValue::VOBJ);
- result.pushKV("hash", blockindex->GetBlockHash().GetHex());
- const CBlockIndex* pnext;
- int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
- result.pushKV("confirmations", confirmations);
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
- result.pushKV("height", blockindex->nHeight);
- result.pushKV("version", block.nVersion);
- result.pushKV("versionHex", strprintf("%08x", block.nVersion));
- result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
UniValue txs(UniValue::VARR);
if (txDetails) {
CBlockUndo blockUndo;
@@ -189,18 +179,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
}
}
result.pushKV("tx", txs);
- result.pushKV("time", block.GetBlockTime());
- result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
- result.pushKV("nonce", (uint64_t)block.nNonce);
- result.pushKV("bits", strprintf("%08x", block.nBits));
- result.pushKV("difficulty", GetDifficulty(blockindex));
- result.pushKV("chainwork", blockindex->nChainWork.GetHex());
- result.pushKV("nTx", (uint64_t)blockindex->nTx);
- if (blockindex->pprev)
- result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
- if (pnext)
- result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
@@ -1047,13 +1026,26 @@ static RPCHelpMan pruneblockchain()
};
}
+CoinStatsHashType ParseHashType(const std::string& hash_type_input)
+{
+ if (hash_type_input == "hash_serialized_2") {
+ return CoinStatsHashType::HASH_SERIALIZED;
+ } else if (hash_type_input == "muhash") {
+ return CoinStatsHashType::MUHASH;
+ } else if (hash_type_input == "none") {
+ return CoinStatsHashType::NONE;
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s is not a valid hash_type", hash_type_input));
+ }
+}
+
static RPCHelpMan gettxoutsetinfo()
{
return RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
{
- {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'none'."},
+ {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1063,7 +1055,8 @@ static RPCHelpMan gettxoutsetinfo()
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
- {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
}},
@@ -1078,7 +1071,7 @@ static RPCHelpMan gettxoutsetinfo()
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
- const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
+ const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
NodeContext& node = EnsureNodeContext(request.context);
@@ -1091,6 +1084,9 @@ static RPCHelpMan gettxoutsetinfo()
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
}
+ if (hash_type == CoinStatsHashType::MUHASH) {
+ ret.pushKV("muhash", stats.hashSerialized.GetHex());
+ }
ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
} else {
@@ -1500,6 +1496,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
ret.pushKV("size", (int64_t)pool.size());
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
+ ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
@@ -1520,6 +1517,7 @@ static RPCHelpMan getmempoolinfo()
{RPCResult::Type::NUM, "size", "Current tx count"},
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
+ {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritizetransaction"},
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index b75a7b8d26..38a0bddddb 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -365,13 +365,13 @@ static RPCHelpMan signmessagewithprivkey()
static RPCHelpMan setmocktime()
{
return RPCHelpMan{"setmocktime",
- "\nSet the local time to given timestamp (-regtest only)\n",
- {
- {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
- " Pass 0 to go back to using the system time."},
- },
- RPCResult{RPCResult::Type::NONE, "", ""},
- RPCExamples{""},
+ "\nSet the local time to given timestamp (-regtest only)\n",
+ {
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
+ "Pass 0 to go back to using the system time."},
+ },
+ RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
if (!Params().IsMockableChain()) {
@@ -386,7 +386,10 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
- int64_t time = request.params[0].get_int64();
+ const int64_t time{request.params[0].get_int64()};
+ if (time < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime can not be negative: %s.", time));
+ }
SetMockTime(time);
if (request.context.Has<NodeContext>()) {
for (const auto& chain_client : request.context.Get<NodeContext>().chain_clients) {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ac42404470..784a53e060 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -946,44 +946,35 @@ static RPCHelpMan testmempoolaccept()
result_0.pushKV("txid", tx->GetHash().GetHex());
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
- TxValidationState state;
- bool test_accept_res;
- CAmount fee{0};
- {
- LOCK(cs_main);
- test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
- }
-
- // Check that fee does not exceed maximum fee
- if (test_accept_res && max_raw_tx_fee && fee > max_raw_tx_fee) {
- result_0.pushKV("allowed", false);
- result_0.pushKV("reject-reason", "max-fee-exceeded");
- result.push_back(std::move(result_0));
- return result;
- }
- result_0.pushKV("allowed", test_accept_res);
+ const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(mempool, std::move(tx),
+ false /* bypass_limits */, /* test_accept */ true));
// Only return the fee and vsize if the transaction would pass ATMP.
// These can be used to calculate the feerate.
- if (test_accept_res) {
- result_0.pushKV("vsize", virtual_size);
- UniValue fees(UniValue::VOBJ);
- fees.pushKV("base", ValueFromAmount(fee));
- result_0.pushKV("fees", fees);
+ if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
+ const CAmount fee = accept_result.m_base_fees.value();
+ // Check that fee does not exceed maximum fee
+ if (max_raw_tx_fee && fee > max_raw_tx_fee) {
+ result_0.pushKV("allowed", false);
+ result_0.pushKV("reject-reason", "max-fee-exceeded");
+ } else {
+ result_0.pushKV("allowed", true);
+ result_0.pushKV("vsize", virtual_size);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(fee));
+ result_0.pushKV("fees", fees);
+ }
+ result.push_back(std::move(result_0));
} else {
- if (state.IsInvalid()) {
- if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
- result_0.pushKV("reject-reason", "missing-inputs");
- } else {
- result_0.pushKV("reject-reason", strprintf("%s", state.GetRejectReason()));
- }
+ result_0.pushKV("allowed", false);
+ const TxValidationState state = accept_result.m_state;
+ if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
+ result_0.pushKV("reject-reason", "missing-inputs");
} else {
result_0.pushKV("reject-reason", state.GetRejectReason());
}
+ result.push_back(std::move(result_0));
}
-
- result.push_back(std::move(result_0));
return result;
},
};
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index bfdba5253c..e890c0108a 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -113,23 +113,6 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
-CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type)
-{
- if (param.isNull()) {
- return default_type;
- } else {
- std::string hash_type_input = param.get_str();
-
- if (hash_type_input == "hash_serialized_2") {
- return CoinStatsHashType::HASH_SERIALIZED;
- } else if (hash_type_input == "none") {
- return CoinStatsHashType::NONE;
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%d is not a valid hash_type", hash_type_input));
- }
- }
-}
-
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 444a013ca1..c54ce85f60 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -77,8 +77,6 @@ extern uint256 ParseHashO(const UniValue& o, std::string strKey);
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type);
-
extern CAmount AmountFromValue(const UniValue& value);
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h
index c5dceac848..b6939127e1 100644
--- a/src/script/bitcoinconsensus.h
+++ b/src/script/bitcoinconsensus.h
@@ -11,14 +11,12 @@
#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#if defined(_WIN32)
- #if defined(DLL_EXPORT)
- #if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT)
- #define EXPORT_SYMBOL __declspec(dllexport)
- #else
- #define EXPORT_SYMBOL
- #endif
+ #if defined(HAVE_DLLEXPORT_ATTRIBUTE)
+ #define EXPORT_SYMBOL __declspec(dllexport)
+ #else
+ #define EXPORT_SYMBOL
#endif
- #elif defined(HAVE_FUNC_ATTRIBUTE_VISIBILITY)
+ #elif defined(HAVE_DEFAULT_VISIBILITY_ATTRIBUTE)
#define EXPORT_SYMBOL __attribute__ ((visibility ("default")))
#endif
#elif defined(MSC_VER) && !defined(STATIC_LIBBITCOINCONSENSUS)
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index cf47d37e70..c6d898a25a 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -12,8 +12,10 @@
#include <cuckoocache.h>
-#include <boost/thread/lock_types.hpp>
-#include <boost/thread/shared_mutex.hpp>
+#include <algorithm>
+#include <mutex>
+#include <shared_mutex>
+#include <vector>
namespace {
/**
@@ -29,7 +31,7 @@ private:
CSHA256 m_salted_hasher_schnorr;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
- boost::shared_mutex cs_sigcache;
+ std::shared_mutex cs_sigcache;
public:
CSignatureCache()
@@ -64,13 +66,13 @@ public:
bool
Get(const uint256& entry, const bool erase)
{
- boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
+ std::shared_lock<std::shared_mutex> lock(cs_sigcache);
return setValid.contains(entry, erase);
}
void Set(const uint256& entry)
{
- boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
+ std::unique_lock<std::shared_mutex> lock(cs_sigcache);
setValid.insert(entry);
}
uint32_t setup_bytes(size_t n)
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 8afbe9ebed..dba5ce621a 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -106,8 +106,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
std::vector<valtype> vSolutions;
whichTypeRet = Solver(scriptPubKey, vSolutions);
- switch (whichTypeRet)
- {
+ switch (whichTypeRet) {
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
@@ -173,10 +172,8 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
// Could not find witnessScript, add to missing
sigdata.missing_witness_script = uint256(vSolutions[0]);
return false;
-
- default:
- return false;
- }
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
static CScript PushAll(const std::vector<valtype>& values)
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 7967c01858..4d882cd1f1 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -45,8 +45,7 @@ WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
std::string GetTxnOutputType(TxoutType t)
{
- switch (t)
- {
+ switch (t) {
case TxoutType::NONSTANDARD: return "nonstandard";
case TxoutType::PUBKEY: return "pubkey";
case TxoutType::PUBKEYHASH: return "pubkeyhash";
@@ -182,7 +181,8 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
std::vector<valtype> vSolutions;
TxoutType whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TxoutType::PUBKEY) {
+ switch (whichType) {
+ case TxoutType::PUBKEY: {
CPubKey pubKey(vSolutions[0]);
if (!pubKey.IsValid())
return false;
@@ -190,26 +190,28 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = PKHash(pubKey);
return true;
}
- else if (whichType == TxoutType::PUBKEYHASH)
- {
+ case TxoutType::PUBKEYHASH: {
addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
- else if (whichType == TxoutType::SCRIPTHASH)
- {
+ case TxoutType::SCRIPTHASH: {
addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
- } else if (whichType == TxoutType::WITNESS_V0_KEYHASH) {
+ }
+ case TxoutType::WITNESS_V0_KEYHASH: {
WitnessV0KeyHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
+ }
+ case TxoutType::WITNESS_V0_SCRIPTHASH: {
WitnessV0ScriptHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TxoutType::WITNESS_UNKNOWN || whichType == TxoutType::WITNESS_V1_TAPROOT) {
+ }
+ case TxoutType::WITNESS_UNKNOWN:
+ case TxoutType::WITNESS_V1_TAPROOT: {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -217,8 +219,13 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = unk;
return true;
}
- // Multisig txns have more than one address...
- return false;
+ case TxoutType::MULTISIG:
+ // Multisig txns have more than one address...
+ case TxoutType::NULL_DATA:
+ case TxoutType::NONSTANDARD:
+ return false;
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
}
bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index 75c7e47e64..35b66cfc53 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -1,15 +1,18 @@
// Copyright (c) 2012-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.
-#include <boost/test/unit_test.hpp>
-#include <boost/thread/lock_types.hpp>
-#include <boost/thread/shared_mutex.hpp>
#include <cuckoocache.h>
-#include <deque>
#include <random.h>
#include <script/sigcache.h>
#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <deque>
+#include <mutex>
+#include <shared_mutex>
#include <thread>
+#include <vector>
/** Test Suite for CuckooCache
*
@@ -201,11 +204,11 @@ static void test_cache_erase_parallel(size_t megabytes)
* "future proofed".
*/
std::vector<uint256> hashes_insert_copy = hashes;
- boost::shared_mutex mtx;
+ std::shared_mutex mtx;
{
/** Grab lock to make sure we release inserts */
- boost::unique_lock<boost::shared_mutex> l(mtx);
+ std::unique_lock<std::shared_mutex> l(mtx);
/** Insert the first half */
for (uint32_t i = 0; i < (n_insert / 2); ++i)
set.insert(hashes_insert_copy[i]);
@@ -219,7 +222,7 @@ static void test_cache_erase_parallel(size_t megabytes)
/** Each thread is emplaced with x copy-by-value
*/
threads.emplace_back([&, x] {
- boost::shared_lock<boost::shared_mutex> l(mtx);
+ std::shared_lock<std::shared_mutex> l(mtx);
size_t ntodo = (n_insert/4)/3;
size_t start = ntodo*x;
size_t end = ntodo*(x+1);
@@ -234,7 +237,7 @@ static void test_cache_erase_parallel(size_t megabytes)
for (std::thread& t : threads)
t.join();
/** Grab lock to make sure we observe erases */
- boost::unique_lock<boost::shared_mutex> l(mtx);
+ std::unique_lock<std::shared_mutex> l(mtx);
/** Insert the second half */
for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
set.insert(hashes_insert_copy[i]);
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index cf6009d591..0d480e35ea 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY);
+ CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr1, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false);
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
- vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY));
+ vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false));
CNode &node = *vNodes.back();
node.SetCommonVersion(PROTOCOL_VERSION);
@@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, NODE_NETWORK, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND);
+ CNode dummyNode1(id++, NODE_NETWORK, INVALID_SOCKET, addr1, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.fSuccessfullyConnected = true;
@@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(!banman->IsDiscouraged(ip(0xa0b0c001|0x0000ff00))); // Different IP, not discouraged
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
- CNode dummyNode2(id++, NODE_NETWORK, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND);
+ CNode dummyNode2(id++, NODE_NETWORK, INVALID_SOCKET, addr2, /* nKeyedNetGroupIn */ 1, /* nLocalHostNonceIn */ 1, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
dummyNode2.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode2);
dummyNode2.fSuccessfullyConnected = true;
@@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
SetMockTime(nStartTime); // Overrides future calls to GetTime()
CAddress addr(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode(id++, NODE_NETWORK, INVALID_SOCKET, addr, 4, 4, CAddress(), "", ConnectionType::INBOUND);
+ CNode dummyNode(id++, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 4, /* nLocalHostNonceIn */ 4, CAddress(), /* pszDest */ "", ConnectionType::INBOUND, /* inbound_onion */ false);
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode);
dummyNode.fSuccessfullyConnected = true;
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index ec487aa3ff..e52cd5230c 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -5,6 +5,7 @@
#include <fs.h>
#include <test/util/setup_common.h>
#include <util/system.h>
+#include <util/getuniquepath.h>
#include <boost/test/unit_test.hpp>
@@ -69,6 +70,21 @@ BOOST_AUTO_TEST_CASE(fsbridge_fstream)
BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, ""));
BOOST_CHECK_EQUAL(tmpfile1, fsbridge::AbsPathJoin(tmpfile1, {}));
}
+ {
+ fs::path p1 = GetUniquePath(tmpfolder);
+ fs::path p2 = GetUniquePath(tmpfolder);
+ fs::path p3 = GetUniquePath(tmpfolder);
+
+ // Ensure that the parent path is always the same.
+ BOOST_CHECK_EQUAL(tmpfolder, p1.parent_path());
+ BOOST_CHECK_EQUAL(tmpfolder, p2.parent_path());
+ BOOST_CHECK_EQUAL(tmpfolder, p3.parent_path());
+
+ // Ensure that generated paths are actually different.
+ BOOST_CHECK(p1 != p2);
+ BOOST_CHECK(p2 != p3);
+ BOOST_CHECK(p1 != p3);
+ }
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 74dec6475e..ba5f0c1a75 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -30,8 +30,6 @@
#include <stdint.h>
#include <unistd.h>
-#include <vector>
-
#include <test/fuzz/fuzz.h>
void initialize_deserialize()
@@ -71,7 +69,7 @@ T Deserialize(CDataStream ds)
}
template <typename T>
-void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj, const Optional<int> protocol_version = nullopt)
+void DeserializeFromFuzzingInput(FuzzBufferType buffer, T& obj, const Optional<int> protocol_version = nullopt)
{
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
if (protocol_version) {
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index fd87667755..45151258b3 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -13,15 +13,15 @@
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
-std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize>>& FuzzTargets()
+std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>>& FuzzTargets()
{
- static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize>> g_fuzz_targets;
+ static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>> g_fuzz_targets;
return g_fuzz_targets;
}
-void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init)
+void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden)
{
- const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init));
+ const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init), hidden);
Assert(it_ins.second);
}
@@ -31,6 +31,7 @@ void initialize()
{
if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
for (const auto& t : FuzzTargets()) {
+ if (std::get<2>(t.second)) continue;
std::cout << t.first << std::endl;
}
Assert(false);
@@ -59,8 +60,7 @@ static bool read_stdin(std::vector<uint8_t>& data)
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
static const auto& test_one_input = *Assert(g_test_one_input);
- const std::vector<uint8_t> input(data, data + size);
- test_one_input(input);
+ test_one_input({data, size});
return 0;
}
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 52841e069a..4abc52c15a 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -5,29 +5,36 @@
#ifndef BITCOIN_TEST_FUZZ_FUZZ_H
#define BITCOIN_TEST_FUZZ_FUZZ_H
+#include <span.h>
+
#include <cstdint>
#include <functional>
#include <string_view>
-#include <vector>
-using TypeTestOneInput = std::function<void(const std::vector<uint8_t>&)>;
+using FuzzBufferType = Span<const uint8_t>;
+
+using TypeTestOneInput = std::function<void(FuzzBufferType)>;
using TypeInitialize = std::function<void()>;
+using TypeHidden = bool;
-void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init);
+void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden);
-inline void FuzzFrameworkEmptyFun() {}
+inline void FuzzFrameworkEmptyInitFun() {}
#define FUZZ_TARGET(name) \
- FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyFun)
-
-#define FUZZ_TARGET_INIT(name, init_fun) \
- void name##_fuzz_target(const std::vector<uint8_t>&); \
- struct name##_Before_Main { \
- name##_Before_Main() \
- { \
- FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun); \
- } \
- } const static g_##name##_before_main; \
- void name##_fuzz_target(const std::vector<uint8_t>& buffer)
+ FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyInitFun)
+
+#define FUZZ_TARGET_INIT(name, init_fun) \
+ FUZZ_TARGET_INIT_HIDDEN(name, init_fun, false)
+
+#define FUZZ_TARGET_INIT_HIDDEN(name, init_fun, hidden) \
+ void name##_fuzz_target(FuzzBufferType); \
+ struct name##_Before_Main { \
+ name##_Before_Main() \
+ { \
+ FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun, hidden); \
+ } \
+ } const static g_##name##_before_main; \
+ void name##_fuzz_target(FuzzBufferType buffer)
#endif // BITCOIN_TEST_FUZZ_FUZZ_H
diff --git a/src/test/fuzz/netaddress.cpp b/src/test/fuzz/netaddress.cpp
index 6e9bb47ff6..a42080eb66 100644
--- a/src/test/fuzz/netaddress.cpp
+++ b/src/test/fuzz/netaddress.cpp
@@ -9,7 +9,6 @@
#include <cassert>
#include <cstdint>
-#include <netinet/in.h>
#include <vector>
FUZZ_TARGET(netaddress)
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index e7cc0f5297..442e32d4ca 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -30,14 +30,32 @@
#include <iostream>
#include <memory>
#include <string>
-#include <vector>
namespace {
const TestingSetup* g_setup;
} // namespace
+size_t& GetNumMsgTypes()
+{
+ static size_t g_num_msg_types{0};
+ return g_num_msg_types;
+}
+#define FUZZ_TARGET_MSG(msg_type) \
+ struct msg_type##_Count_Before_Main { \
+ msg_type##_Count_Before_Main() \
+ { \
+ ++GetNumMsgTypes(); \
+ } \
+ } const static g_##msg_type##_count_before_main; \
+ FUZZ_TARGET_INIT(process_message_##msg_type, initialize_process_message) \
+ { \
+ fuzz_target(buffer, #msg_type); \
+ }
+
void initialize_process_message()
{
+ Assert(GetNumMsgTypes() == getAllNetMessageTypes().size()); // If this fails, add or remove the message type below
+
static const auto testing_setup = MakeFuzzingContext<const TestingSetup>();
g_setup = testing_setup.get();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
@@ -46,7 +64,7 @@ void initialize_process_message()
SyncWithValidationInterfaceQueue();
}
-void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO_MESSAGE_TYPE)
+void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
@@ -61,7 +79,7 @@ void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO
}
CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider).release();
- const bool successfully_connected{true};
+ const bool successfully_connected{fuzzed_data_provider.ConsumeBool()};
p2p_node.fSuccessfullyConnected = successfully_connected;
connman.AddTestNode(p2p_node);
g_setup->m_node.peerman->InitializeNode(&p2p_node);
@@ -87,27 +105,37 @@ void fuzz_target(const std::vector<uint8_t>& buffer, const std::string& LIMIT_TO
}
FUZZ_TARGET_INIT(process_message, initialize_process_message) { fuzz_target(buffer, ""); }
-FUZZ_TARGET_INIT(process_message_addr, initialize_process_message) { fuzz_target(buffer, "addr"); }
-FUZZ_TARGET_INIT(process_message_block, initialize_process_message) { fuzz_target(buffer, "block"); }
-FUZZ_TARGET_INIT(process_message_blocktxn, initialize_process_message) { fuzz_target(buffer, "blocktxn"); }
-FUZZ_TARGET_INIT(process_message_cmpctblock, initialize_process_message) { fuzz_target(buffer, "cmpctblock"); }
-FUZZ_TARGET_INIT(process_message_feefilter, initialize_process_message) { fuzz_target(buffer, "feefilter"); }
-FUZZ_TARGET_INIT(process_message_filteradd, initialize_process_message) { fuzz_target(buffer, "filteradd"); }
-FUZZ_TARGET_INIT(process_message_filterclear, initialize_process_message) { fuzz_target(buffer, "filterclear"); }
-FUZZ_TARGET_INIT(process_message_filterload, initialize_process_message) { fuzz_target(buffer, "filterload"); }
-FUZZ_TARGET_INIT(process_message_getaddr, initialize_process_message) { fuzz_target(buffer, "getaddr"); }
-FUZZ_TARGET_INIT(process_message_getblocks, initialize_process_message) { fuzz_target(buffer, "getblocks"); }
-FUZZ_TARGET_INIT(process_message_getblocktxn, initialize_process_message) { fuzz_target(buffer, "getblocktxn"); }
-FUZZ_TARGET_INIT(process_message_getdata, initialize_process_message) { fuzz_target(buffer, "getdata"); }
-FUZZ_TARGET_INIT(process_message_getheaders, initialize_process_message) { fuzz_target(buffer, "getheaders"); }
-FUZZ_TARGET_INIT(process_message_headers, initialize_process_message) { fuzz_target(buffer, "headers"); }
-FUZZ_TARGET_INIT(process_message_inv, initialize_process_message) { fuzz_target(buffer, "inv"); }
-FUZZ_TARGET_INIT(process_message_mempool, initialize_process_message) { fuzz_target(buffer, "mempool"); }
-FUZZ_TARGET_INIT(process_message_notfound, initialize_process_message) { fuzz_target(buffer, "notfound"); }
-FUZZ_TARGET_INIT(process_message_ping, initialize_process_message) { fuzz_target(buffer, "ping"); }
-FUZZ_TARGET_INIT(process_message_pong, initialize_process_message) { fuzz_target(buffer, "pong"); }
-FUZZ_TARGET_INIT(process_message_sendcmpct, initialize_process_message) { fuzz_target(buffer, "sendcmpct"); }
-FUZZ_TARGET_INIT(process_message_sendheaders, initialize_process_message) { fuzz_target(buffer, "sendheaders"); }
-FUZZ_TARGET_INIT(process_message_tx, initialize_process_message) { fuzz_target(buffer, "tx"); }
-FUZZ_TARGET_INIT(process_message_verack, initialize_process_message) { fuzz_target(buffer, "verack"); }
-FUZZ_TARGET_INIT(process_message_version, initialize_process_message) { fuzz_target(buffer, "version"); }
+FUZZ_TARGET_MSG(addr);
+FUZZ_TARGET_MSG(addrv2);
+FUZZ_TARGET_MSG(block);
+FUZZ_TARGET_MSG(blocktxn);
+FUZZ_TARGET_MSG(cfcheckpt);
+FUZZ_TARGET_MSG(cfheaders);
+FUZZ_TARGET_MSG(cfilter);
+FUZZ_TARGET_MSG(cmpctblock);
+FUZZ_TARGET_MSG(feefilter);
+FUZZ_TARGET_MSG(filteradd);
+FUZZ_TARGET_MSG(filterclear);
+FUZZ_TARGET_MSG(filterload);
+FUZZ_TARGET_MSG(getaddr);
+FUZZ_TARGET_MSG(getblocks);
+FUZZ_TARGET_MSG(getblocktxn);
+FUZZ_TARGET_MSG(getcfcheckpt);
+FUZZ_TARGET_MSG(getcfheaders);
+FUZZ_TARGET_MSG(getcfilters);
+FUZZ_TARGET_MSG(getdata);
+FUZZ_TARGET_MSG(getheaders);
+FUZZ_TARGET_MSG(headers);
+FUZZ_TARGET_MSG(inv);
+FUZZ_TARGET_MSG(mempool);
+FUZZ_TARGET_MSG(merkleblock);
+FUZZ_TARGET_MSG(notfound);
+FUZZ_TARGET_MSG(ping);
+FUZZ_TARGET_MSG(pong);
+FUZZ_TARGET_MSG(sendaddrv2);
+FUZZ_TARGET_MSG(sendcmpct);
+FUZZ_TARGET_MSG(sendheaders);
+FUZZ_TARGET_MSG(tx);
+FUZZ_TARGET_MSG(verack);
+FUZZ_TARGET_MSG(version);
+FUZZ_TARGET_MSG(wtxidrelay);
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 810f0aac92..ef45196671 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -46,7 +46,7 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, i).release());
CNode& p2p_node = *peers.back();
- const bool successfully_connected{true};
+ const bool successfully_connected{fuzzed_data_provider.ConsumeBool()};
p2p_node.fSuccessfullyConnected = successfully_connected;
p2p_node.fPauseSend = false;
g_setup->m_node.peerman->InitializeNode(&p2p_node);
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
index 2091ad5d91..8d9a939dab 100644
--- a/src/test/fuzz/script_assets_test_minimizer.cpp
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -28,12 +28,12 @@
//
// (normal build)
// $ mkdir dump
-// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done
+// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot.py --dumptests; done
// $ ...
//
-// (fuzz test build)
+// (libFuzzer build)
// $ mkdir dump-min
-// $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/
+// $ FUZZ=script_assets_test_minimizer ./src/test/fuzz/fuzz -merge=1 -use_value_profile=1 dump-min/ dump/
// $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json
namespace {
@@ -190,7 +190,7 @@ ECCVerifyHandle handle;
} // namespace
-FUZZ_TARGET(script_assets_test_minimizer)
+FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, FuzzFrameworkEmptyInitFun, /* hidden */ true)
{
if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
const std::string str((const char*)buffer.data(), buffer.size() - 2);
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 282a2cd8ca..d6bbb17f63 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -78,7 +78,9 @@ FUZZ_TARGET(string)
}
(void)SanitizeString(random_string_1);
(void)SanitizeString(random_string_1, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 3));
+#ifndef WIN32
(void)ShellEscape(random_string_1);
+#endif // WIN32
int port_out;
std::string host_out;
SplitHostPort(random_string_1, port_out, host_out);
diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp
index 47b38b6d23..3621702e45 100644
--- a/src/test/fuzz/system.cpp
+++ b/src/test/fuzz/system.cpp
@@ -54,7 +54,7 @@ FUZZ_TARGET(system)
if (args_manager.GetArgFlags(argument_name) != nullopt) {
return;
}
- args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>(), options_category);
+ args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>() & ~ArgsManager::COMMAND, options_category);
},
[&] {
// Avoid hitting:
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index a1b41e17ed..010a3375cf 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -192,14 +192,15 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 0,
/* nLocalHostNonceIn = */ 0,
- CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
+ CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY,
+ /* inbound_onion = */ false);
BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
BOOST_CHECK(pnode1->IsManualConn() == false);
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode1->IsFeelerConn() == false);
BOOST_CHECK(pnode1->IsAddrFetchConn() == false);
BOOST_CHECK(pnode1->IsInboundConn() == false);
- BOOST_CHECK(pnode1->IsInboundOnion() == false);
+ BOOST_CHECK(pnode1->m_inbound_onion == false);
BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(
@@ -214,7 +215,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode2->IsFeelerConn() == false);
BOOST_CHECK(pnode2->IsAddrFetchConn() == false);
BOOST_CHECK(pnode2->IsInboundConn() == true);
- BOOST_CHECK(pnode2->IsInboundOnion() == false);
+ BOOST_CHECK(pnode2->m_inbound_onion == false);
BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode3 = MakeUnique<CNode>(
@@ -229,7 +230,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode3->IsFeelerConn() == false);
BOOST_CHECK(pnode3->IsAddrFetchConn() == false);
BOOST_CHECK(pnode3->IsInboundConn() == false);
- BOOST_CHECK(pnode3->IsInboundOnion() == false);
+ BOOST_CHECK(pnode3->m_inbound_onion == false);
BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode4 = MakeUnique<CNode>(
@@ -244,7 +245,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
BOOST_CHECK(pnode4->IsFeelerConn() == false);
BOOST_CHECK(pnode4->IsAddrFetchConn() == false);
BOOST_CHECK(pnode4->IsInboundConn() == true);
- BOOST_CHECK(pnode4->IsInboundOnion() == true);
+ BOOST_CHECK(pnode4->m_inbound_onion == true);
BOOST_CHECK_EQUAL(pnode4->ConnectedThroughNetwork(), Network::NET_ONION);
}
@@ -679,7 +680,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
in_addr ipv4AddrPeer;
ipv4AddrPeer.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
- std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY);
+ std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, INVALID_SOCKET, addr, /* nKeyedNetGroupIn */ 0, /* nLocalHostNonceIn */ 0, CAddress{}, /* pszDest */ std::string{}, ConnectionType::OUTBOUND_FULL_RELAY, /* inbound_onion */ false);
pnode->fSuccessfullyConnected.store(true);
// the peer claims to be reaching us via IPv6
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 366385b619..4dc0dd5f51 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -107,6 +107,22 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(scriptHash));
+ // TxoutType::WITNESS_V1_TAPROOT
+ s.clear();
+ s << OP_1 << ToByteVector(uint256::ZERO);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V1_TAPROOT);
+ BOOST_CHECK_EQUAL(solutions.size(), 2U);
+ BOOST_CHECK(solutions[0] == std::vector<unsigned char>{1});
+ BOOST_CHECK(solutions[1] == ToByteVector(uint256::ZERO));
+
+ // TxoutType::WITNESS_UNKNOWN
+ s.clear();
+ s << OP_16 << ToByteVector(uint256::ONE);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_UNKNOWN);
+ BOOST_CHECK_EQUAL(solutions.size(), 2U);
+ BOOST_CHECK(solutions[0] == std::vector<unsigned char>{16});
+ BOOST_CHECK(solutions[1] == ToByteVector(uint256::ONE));
+
// TxoutType::NONSTANDARD
s.clear();
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
diff --git a/src/test/sock_tests.cpp b/src/test/sock_tests.cpp
new file mode 100644
index 0000000000..cc0e6e7057
--- /dev/null
+++ b/src/test/sock_tests.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2021-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 <compat.h>
+#include <test/util/setup_common.h>
+#include <util/sock.h>
+#include <util/system.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <thread>
+
+using namespace std::chrono_literals;
+
+BOOST_FIXTURE_TEST_SUITE(sock_tests, BasicTestingSetup)
+
+static bool SocketIsClosed(const SOCKET& s)
+{
+ // Notice that if another thread is running and creates its own socket after `s` has been
+ // closed, it may be assigned the same file descriptor number. In this case, our test will
+ // wrongly pretend that the socket is not closed.
+ int type;
+ socklen_t len = sizeof(type);
+ return getsockopt(s, SOL_SOCKET, SO_TYPE, (sockopt_arg_type)&type, &len) == SOCKET_ERROR;
+}
+
+static SOCKET CreateSocket()
+{
+ const SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ BOOST_REQUIRE(s != static_cast<SOCKET>(SOCKET_ERROR));
+ return s;
+}
+
+BOOST_AUTO_TEST_CASE(constructor_and_destructor)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock = new Sock(s);
+ BOOST_CHECK_EQUAL(sock->Get(), s);
+ BOOST_CHECK(!SocketIsClosed(s));
+ delete sock;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(move_constructor)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock1 = new Sock(s);
+ Sock* sock2 = new Sock(std::move(*sock1));
+ delete sock1;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_CHECK_EQUAL(sock2->Get(), s);
+ delete sock2;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(move_assignment)
+{
+ const SOCKET s = CreateSocket();
+ Sock* sock1 = new Sock(s);
+ Sock* sock2 = new Sock();
+ *sock2 = std::move(*sock1);
+ delete sock1;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_CHECK_EQUAL(sock2->Get(), s);
+ delete sock2;
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+BOOST_AUTO_TEST_CASE(release)
+{
+ SOCKET s = CreateSocket();
+ Sock* sock = new Sock(s);
+ BOOST_CHECK_EQUAL(sock->Release(), s);
+ delete sock;
+ BOOST_CHECK(!SocketIsClosed(s));
+ BOOST_REQUIRE(CloseSocket(s));
+}
+
+BOOST_AUTO_TEST_CASE(reset)
+{
+ const SOCKET s = CreateSocket();
+ Sock sock(s);
+ sock.Reset();
+ BOOST_CHECK(SocketIsClosed(s));
+}
+
+#ifndef WIN32 // Windows does not have socketpair(2).
+
+static void CreateSocketPair(int s[2])
+{
+ BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0);
+}
+
+static void SendAndRecvMessage(const Sock& sender, const Sock& receiver)
+{
+ const char* msg = "abcd";
+ constexpr size_t msg_len = 4;
+ char recv_buf[10];
+
+ BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len);
+ BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len);
+ BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0);
+}
+
+BOOST_AUTO_TEST_CASE(send_and_receive)
+{
+ int s[2];
+ CreateSocketPair(s);
+
+ Sock* sock0 = new Sock(s[0]);
+ Sock* sock1 = new Sock(s[1]);
+
+ SendAndRecvMessage(*sock0, *sock1);
+
+ Sock* sock0moved = new Sock(std::move(*sock0));
+ Sock* sock1moved = new Sock();
+ *sock1moved = std::move(*sock1);
+
+ delete sock0;
+ delete sock1;
+
+ SendAndRecvMessage(*sock1moved, *sock0moved);
+
+ delete sock0moved;
+ delete sock1moved;
+
+ BOOST_CHECK(SocketIsClosed(s[0]));
+ BOOST_CHECK(SocketIsClosed(s[1]));
+}
+
+BOOST_AUTO_TEST_CASE(wait)
+{
+ int s[2];
+ CreateSocketPair(s);
+
+ Sock sock0(s[0]);
+ Sock sock1(s[1]);
+
+ std::thread waiter([&sock0]() { sock0.Wait(24h, Sock::RECV); });
+
+ BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1);
+
+ waiter.join();
+}
+
+#endif /* WIN32 */
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 7e6246d68f..9b0d2e3135 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -30,25 +30,21 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
- TxValidationState state;
-
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
+ const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(coinbaseTx),
+ true /* bypass_limits */);
- BOOST_CHECK_EQUAL(
- false,
- AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
- nullptr /* plTxnReplaced */,
- true /* bypass_limits */));
+ BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
// Check that the transaction hasn't been added to mempool.
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
// Check that the validation state reflects the unsuccessful attempt.
- BOOST_CHECK(state.IsInvalid());
- BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
- BOOST_CHECK(state.GetResult() == TxValidationResult::TX_CONSENSUS);
+ BOOST_CHECK(result.m_state.IsInvalid());
+ BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
+ BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index bed2ba3608..af0090cc10 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -28,9 +28,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
const auto ToMemPool = [this](const CMutableTransaction& tx) {
LOCK(cs_main);
- TxValidationState state;
- return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
- nullptr /* plTxnReplaced */, true /* bypass_limits */);
+ const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, MakeTransactionRef(tx),
+ true /* bypass_limits */);
+ return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
};
// Create a double-spend of mature coinbase txn:
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 4133f2623b..dccbf00d6f 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -13,6 +13,7 @@
#include <test/util/setup_common.h>
#include <test/util/str.h>
#include <uint256.h>
+#include <util/getuniquepath.h>
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
#include <util/spanparsing.h>
@@ -1816,7 +1817,7 @@ BOOST_AUTO_TEST_CASE(test_DirIsWritable)
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), true);
// Should not be able to write to a non-existent dir.
- tmpdirname = tmpdirname / fs::unique_path();
+ tmpdirname = GetUniquePath(tmpdirname);
BOOST_CHECK_EQUAL(DirIsWritable(tmpdirname), false);
fs::create_directory(tmpdirname);
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index ec45d9a434..6c18a9e1bb 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -283,15 +283,9 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Add the txs to the tx pool
{
LOCK(cs_main);
- TxValidationState state;
- std::list<CTransactionRef> plTxnReplaced;
for (const auto& tx : txs) {
- BOOST_REQUIRE(AcceptToMemoryPool(
- *m_node.mempool,
- state,
- tx,
- &plTxnReplaced,
- /* bypass_limits */ false));
+ const MempoolAcceptResult result = AcceptToMemoryPool(*m_node.mempool, tx, false /* bypass_limits */);
+ BOOST_REQUIRE(result.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 908ad35e1b..605c77fc3a 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -14,6 +14,7 @@
#include <netbase.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/time.h>
#include <deque>
#include <functional>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c370f9e981..9fa7b4e251 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -9,14 +9,14 @@
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <optional.h>
-#include <validation.h>
-#include <policy/policy.h>
#include <policy/fees.h>
+#include <policy/policy.h>
#include <policy/settings.h>
#include <reverse_iterator.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/system.h>
#include <util/time.h>
+#include <validation.h>
#include <validationinterface.h>
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
@@ -396,7 +396,10 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
- if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);}
+ m_total_fee += entry.GetFee();
+ if (minerPolicyEstimator) {
+ minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
+ }
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
@@ -432,6 +435,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
vTxHashes.clear();
totalTxSize -= it->GetTxSize();
+ m_total_fee -= it->GetFee();
cachedInnerUsage -= it->DynamicMemoryUsage();
cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
mapTx.erase(it);
@@ -590,6 +594,7 @@ void CTxMemPool::_clear()
mapTx.clear();
mapNextTx.clear();
totalTxSize = 0;
+ m_total_fee = 0;
cachedInnerUsage = 0;
lastRollingFeeUpdate = GetTime();
blockSinceLastRollingFeeBump = false;
@@ -623,6 +628,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
uint64_t checkTotal = 0;
+ CAmount check_total_fee{0};
uint64_t innerUsage = 0;
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
@@ -632,6 +638,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
unsigned int i = 0;
checkTotal += it->GetTxSize();
+ check_total_fee += it->GetFee();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
@@ -726,6 +733,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
}
assert(totalTxSize == checkTotal);
+ assert(m_total_fee == check_total_fee);
assert(innerUsage == cachedInnerUsage);
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 0a9cd81ff5..c0df33fe13 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -19,8 +19,8 @@
#include <optional.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
-#include <sync.h>
#include <random.h>
+#include <sync.h>
#include <util/hasher.h>
#include <boost/multi_index_container.hpp>
@@ -478,8 +478,9 @@ private:
std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
CBlockPolicyEstimator* minerPolicyEstimator;
- uint64_t totalTxSize; //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
- uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves)
+ uint64_t totalTxSize GUARDED_BY(cs); //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
+ CAmount m_total_fee GUARDED_BY(cs); //!< sum of all mempool tx's fees (NOT modified fee)
+ uint64_t cachedInnerUsage GUARDED_BY(cs); //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves)
mutable int64_t lastRollingFeeUpdate;
mutable bool blockSinceLastRollingFeeBump;
@@ -724,6 +725,12 @@ public:
return totalTxSize;
}
+ CAmount GetTotalFee() const EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+ AssertLockHeld(cs);
+ return m_total_fee;
+ }
+
bool exists(const GenTxid& gtxid) const
{
LOCK(cs);
diff --git a/src/util/getuniquepath.cpp b/src/util/getuniquepath.cpp
new file mode 100644
index 0000000000..9839d2f624
--- /dev/null
+++ b/src/util/getuniquepath.cpp
@@ -0,0 +1,10 @@
+#include <random.h>
+#include <fs.h>
+#include <util/strencodings.h>
+
+fs::path GetUniquePath(const fs::path& base)
+{
+ FastRandomContext rnd;
+ fs::path tmpFile = base / HexStr(rnd.randbytes(8));
+ return tmpFile;
+} \ No newline at end of file
diff --git a/src/util/getuniquepath.h b/src/util/getuniquepath.h
new file mode 100644
index 0000000000..e0c6147876
--- /dev/null
+++ b/src/util/getuniquepath.h
@@ -0,0 +1,19 @@
+// 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_GETUNIQUEPATH_H
+#define BITCOIN_UTIL_GETUNIQUEPATH_H
+
+#include <fs.h>
+
+/**
+ * Helper function for getting a unique path
+ *
+ * @param[in] base Base path
+ * @returns base joined with a random 8-character long string.
+ * @post Returned path is unique with high probability.
+ */
+fs::path GetUniquePath(const fs::path& base);
+
+#endif // BITCOIN_UTIL_GETUNIQUEPATH_H \ No newline at end of file
diff --git a/src/util/macros.h b/src/util/macros.h
index 36ea87c0fe..0887c80fd7 100644
--- a/src/util/macros.h
+++ b/src/util/macros.h
@@ -8,4 +8,11 @@
#define PASTE(x, y) x ## y
#define PASTE2(x, y) PASTE(x, y)
+/**
+ * Converts the parameter X to a string after macro replacement on X has been performed.
+ * Don't merge these into one macro!
+ */
+#define STRINGIZE(X) DO_STRINGIZE(X)
+#define DO_STRINGIZE(X) #X
+
#endif // BITCOIN_UTIL_MACROS_H
diff --git a/src/util/sock.cpp b/src/util/sock.cpp
new file mode 100644
index 0000000000..4c65b5b680
--- /dev/null
+++ b/src/util/sock.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2020-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 <compat.h>
+#include <logging.h>
+#include <tinyformat.h>
+#include <util/sock.h>
+#include <util/system.h>
+#include <util/time.h>
+
+#include <codecvt>
+#include <cwchar>
+#include <locale>
+#include <string>
+
+#ifdef USE_POLL
+#include <poll.h>
+#endif
+
+Sock::Sock() : m_socket(INVALID_SOCKET) {}
+
+Sock::Sock(SOCKET s) : m_socket(s) {}
+
+Sock::Sock(Sock&& other)
+{
+ m_socket = other.m_socket;
+ other.m_socket = INVALID_SOCKET;
+}
+
+Sock::~Sock() { Reset(); }
+
+Sock& Sock::operator=(Sock&& other)
+{
+ Reset();
+ m_socket = other.m_socket;
+ other.m_socket = INVALID_SOCKET;
+ return *this;
+}
+
+SOCKET Sock::Get() const { return m_socket; }
+
+SOCKET Sock::Release()
+{
+ const SOCKET s = m_socket;
+ m_socket = INVALID_SOCKET;
+ return s;
+}
+
+void Sock::Reset() { CloseSocket(m_socket); }
+
+ssize_t Sock::Send(const void* data, size_t len, int flags) const
+{
+ return send(m_socket, static_cast<const char*>(data), len, flags);
+}
+
+ssize_t Sock::Recv(void* buf, size_t len, int flags) const
+{
+ return recv(m_socket, static_cast<char*>(buf), len, flags);
+}
+
+bool Sock::Wait(std::chrono::milliseconds timeout, Event requested) const
+{
+#ifdef USE_POLL
+ pollfd fd;
+ fd.fd = m_socket;
+ fd.events = 0;
+ if (requested & RECV) {
+ fd.events |= POLLIN;
+ }
+ if (requested & SEND) {
+ fd.events |= POLLOUT;
+ }
+
+ return poll(&fd, 1, count_milliseconds(timeout)) != SOCKET_ERROR;
+#else
+ if (!IsSelectableSocket(m_socket)) {
+ return false;
+ }
+
+ fd_set fdset_recv;
+ fd_set fdset_send;
+ FD_ZERO(&fdset_recv);
+ FD_ZERO(&fdset_send);
+
+ if (requested & RECV) {
+ FD_SET(m_socket, &fdset_recv);
+ }
+
+ if (requested & SEND) {
+ FD_SET(m_socket, &fdset_send);
+ }
+
+ timeval timeout_struct = MillisToTimeval(timeout);
+
+ return select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) != SOCKET_ERROR;
+#endif /* USE_POLL */
+}
+
+#ifdef WIN32
+std::string NetworkErrorString(int err)
+{
+ wchar_t buf[256];
+ buf[0] = 0;
+ if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buf, ARRAYSIZE(buf), nullptr))
+ {
+ return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
+ }
+ else
+ {
+ return strprintf("Unknown error (%d)", err);
+ }
+}
+#else
+std::string NetworkErrorString(int err)
+{
+ char buf[256];
+ buf[0] = 0;
+ /* Too bad there are two incompatible implementations of the
+ * thread-safe strerror. */
+ const char *s;
+#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
+ s = strerror_r(err, buf, sizeof(buf));
+#else /* POSIX variant always returns message in buffer */
+ s = buf;
+ if (strerror_r(err, buf, sizeof(buf)))
+ buf[0] = 0;
+#endif
+ return strprintf("%s (%d)", s, err);
+}
+#endif
+
+bool CloseSocket(SOCKET& hSocket)
+{
+ if (hSocket == INVALID_SOCKET)
+ return false;
+#ifdef WIN32
+ int ret = closesocket(hSocket);
+#else
+ int ret = close(hSocket);
+#endif
+ if (ret) {
+ LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
+ }
+ hSocket = INVALID_SOCKET;
+ return ret != SOCKET_ERROR;
+}
diff --git a/src/util/sock.h b/src/util/sock.h
new file mode 100644
index 0000000000..26fe60f18f
--- /dev/null
+++ b/src/util/sock.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2020-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_SOCK_H
+#define BITCOIN_UTIL_SOCK_H
+
+#include <compat.h>
+
+#include <chrono>
+#include <string>
+
+/**
+ * RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it
+ * contains a socket and closes it automatically when it goes out of scope.
+ */
+class Sock
+{
+public:
+ /**
+ * Default constructor, creates an empty object that does nothing when destroyed.
+ */
+ Sock();
+
+ /**
+ * Take ownership of an existent socket.
+ */
+ explicit Sock(SOCKET s);
+
+ /**
+ * Copy constructor, disabled because closing the same socket twice is undesirable.
+ */
+ Sock(const Sock&) = delete;
+
+ /**
+ * Move constructor, grab the socket from another object and close ours (if set).
+ */
+ Sock(Sock&& other);
+
+ /**
+ * Destructor, close the socket or do nothing if empty.
+ */
+ virtual ~Sock();
+
+ /**
+ * Copy assignment operator, disabled because closing the same socket twice is undesirable.
+ */
+ Sock& operator=(const Sock&) = delete;
+
+ /**
+ * Move assignment operator, grab the socket from another object and close ours (if set).
+ */
+ virtual Sock& operator=(Sock&& other);
+
+ /**
+ * Get the value of the contained socket.
+ * @return socket or INVALID_SOCKET if empty
+ */
+ virtual SOCKET Get() const;
+
+ /**
+ * Get the value of the contained socket and drop ownership. It will not be closed by the
+ * destructor after this call.
+ * @return socket or INVALID_SOCKET if empty
+ */
+ virtual SOCKET Release();
+
+ /**
+ * Close if non-empty.
+ */
+ virtual void Reset();
+
+ /**
+ * send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
+ * wrapper can be unit-tested if this method is overridden by a mock Sock implementation.
+ */
+ virtual ssize_t Send(const void* data, size_t len, int flags) const;
+
+ /**
+ * recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
+ * wrapper can be unit-tested if this method is overridden by a mock Sock implementation.
+ */
+ virtual ssize_t Recv(void* buf, size_t len, int flags) const;
+
+ using Event = uint8_t;
+
+ /**
+ * If passed to `Wait()`, then it will wait for readiness to read from the socket.
+ */
+ static constexpr Event RECV = 0b01;
+
+ /**
+ * If passed to `Wait()`, then it will wait for readiness to send to the socket.
+ */
+ static constexpr Event SEND = 0b10;
+
+ /**
+ * Wait for readiness for input (recv) or output (send).
+ * @param[in] timeout Wait this much for at least one of the requested events to occur.
+ * @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`.
+ * @return true on success and false otherwise
+ */
+ virtual bool Wait(std::chrono::milliseconds timeout, Event requested) const;
+
+private:
+ /**
+ * Contained socket. `INVALID_SOCKET` designates the object is empty.
+ */
+ SOCKET m_socket;
+};
+
+/** Return readable error string for a network error code */
+std::string NetworkErrorString(int err);
+
+/** Close socket and set hSocket to INVALID_SOCKET */
+bool CloseSocket(SOCKET& hSocket);
+
+#endif // BITCOIN_UTIL_SOCK_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index d1fb921642..9a2e719bbc 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -3,7 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <sync.h>
#include <util/system.h>
#ifdef HAVE_BOOST_PROCESS
@@ -11,6 +10,9 @@
#endif // HAVE_BOOST_PROCESS
#include <chainparamsbase.h>
+#include <sync.h>
+#include <util/check.h>
+#include <util/getuniquepath.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
@@ -123,7 +125,7 @@ void ReleaseDirectoryLocks()
bool DirIsWritable(const fs::path& directory)
{
- fs::path tmpFile = directory / fs::unique_path();
+ fs::path tmpFile = GetUniquePath(directory);
FILE* file = fsbridge::fopen(tmpFile, "a");
if (!file) return false;
@@ -310,8 +312,22 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
key[0] = '-';
#endif
- if (key[0] != '-')
+ if (key[0] != '-') {
+ if (!m_accept_any_command && m_command.empty()) {
+ // The first non-dash arg is a registered command
+ Optional<unsigned int> flags = GetArgFlags(key);
+ if (!flags || !(*flags & ArgsManager::COMMAND)) {
+ error = strprintf("Invalid command '%s'", argv[i]);
+ return false;
+ }
+ }
+ m_command.push_back(key);
+ while (++i < argc) {
+ // The remaining args are command args
+ m_command.push_back(argv[i]);
+ }
break;
+ }
// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-')
@@ -359,6 +375,26 @@ Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
return nullopt;
}
+std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
+{
+ Command ret;
+ LOCK(cs_args);
+ auto it = m_command.begin();
+ if (it == m_command.end()) {
+ // No command was passed
+ return std::nullopt;
+ }
+ if (!m_accept_any_command) {
+ // The registered command
+ ret.command = *(it++);
+ }
+ while (it != m_command.end()) {
+ // The unregistered command and args (if any)
+ ret.args.push_back(*(it++));
+ }
+ return ret;
+}
+
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
{
std::vector<std::string> result;
@@ -504,8 +540,22 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
m_settings.forced_settings[SettingName(strArg)] = strValue;
}
+void ArgsManager::AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat)
+{
+ Assert(cmd.find('=') == std::string::npos);
+ Assert(cmd.at(0) != '-');
+
+ LOCK(cs_args);
+ m_accept_any_command = false; // latch to false
+ std::map<std::string, Arg>& arg_map = m_available_args[cat];
+ auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
+ Assert(ret.second); // Fail on duplicate commands
+}
+
void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
{
+ Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
+
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {
diff --git a/src/util/system.h b/src/util/system.h
index d06c30bfa7..5959bc4196 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -166,7 +166,7 @@ struct SectionInfo
class ArgsManager
{
public:
- enum Flags {
+ enum Flags : uint32_t {
// Boolean options can accept negation syntax -noOPTION or -noOPTION=1
ALLOW_BOOL = 0x01,
ALLOW_INT = 0x02,
@@ -181,6 +181,7 @@ public:
NETWORK_ONLY = 0x200,
// This argument's value is sensitive (such as a password).
SENSITIVE = 0x400,
+ COMMAND = 0x800,
};
protected:
@@ -193,9 +194,11 @@ protected:
mutable RecursiveMutex cs_args;
util::Settings m_settings GUARDED_BY(cs_args);
+ std::vector<std::string> m_command GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
+ bool m_accept_any_command GUARDED_BY(cs_args){true};
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
@@ -246,6 +249,20 @@ public:
*/
const std::list<SectionInfo> GetUnrecognizedSections() const;
+ struct Command {
+ /** The command (if one has been registered with AddCommand), or empty */
+ std::string command;
+ /**
+ * If command is non-empty: Any args that followed it
+ * If command is empty: The unregistered command and any args that followed it
+ */
+ std::vector<std::string> args;
+ };
+ /**
+ * Get the command and command args (returns std::nullopt if no command provided)
+ */
+ std::optional<const Command> GetCommand() const;
+
/**
* Return a vector of strings of the given argument
*
@@ -332,6 +349,11 @@ public:
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
+ * Add subcommand
+ */
+ void AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat);
+
+ /**
* Add many hidden arguments
*/
void AddHiddenArgs(const std::vector<std::string>& args);
diff --git a/src/util/time.cpp b/src/util/time.cpp
index e96972fe12..295806c54a 100644
--- a/src/util/time.cpp
+++ b/src/util/time.cpp
@@ -7,8 +7,11 @@
#include <config/bitcoin-config.h>
#endif
+#include <compat.h>
#include <util/time.h>
+#include <util/check.h>
+
#include <atomic>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
@@ -18,7 +21,7 @@
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
-static std::atomic<int64_t> nMockTime(0); //!< For unit testing
+static std::atomic<int64_t> nMockTime(0); //!< For testing
int64_t GetTime()
{
@@ -46,6 +49,7 @@ template std::chrono::microseconds GetTime();
void SetMockTime(int64_t nMockTimeIn)
{
+ Assert(nMockTimeIn >= 0);
nMockTime.store(nMockTimeIn, std::memory_order_relaxed);
}
@@ -114,3 +118,16 @@ int64_t ParseISO8601DateTime(const std::string& str)
return 0;
return (ptime - epoch).total_seconds();
}
+
+struct timeval MillisToTimeval(int64_t nTimeout)
+{
+ struct timeval timeout;
+ timeout.tv_sec = nTimeout / 1000;
+ timeout.tv_usec = (nTimeout % 1000) * 1000;
+ return timeout;
+}
+
+struct timeval MillisToTimeval(std::chrono::milliseconds ms)
+{
+ return MillisToTimeval(count_milliseconds(ms));
+}
diff --git a/src/util/time.h b/src/util/time.h
index c69f604dc6..03b75b5be5 100644
--- a/src/util/time.h
+++ b/src/util/time.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_UTIL_TIME_H
#define BITCOIN_UTIL_TIME_H
+#include <compat.h>
+
#include <chrono>
#include <stdint.h>
#include <string>
@@ -25,6 +27,7 @@ void UninterruptibleSleep(const std::chrono::microseconds& n);
* interface that doesn't support std::chrono (e.g. RPC, debug log, or the GUI)
*/
inline int64_t count_seconds(std::chrono::seconds t) { return t.count(); }
+inline int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); }
inline int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); }
/**
@@ -57,4 +60,14 @@ std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
int64_t ParseISO8601DateTime(const std::string& str);
+/**
+ * Convert milliseconds to a struct timeval for e.g. select.
+ */
+struct timeval MillisToTimeval(int64_t nTimeout);
+
+/**
+ * Convert milliseconds to a struct timeval for e.g. select.
+ */
+struct timeval MillisToTimeval(std::chrono::milliseconds ms);
+
#endif // BITCOIN_UTIL_TIME_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 38df71b994..778d75ce18 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -380,10 +380,8 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
// ignore validation errors in resurrected transactions
- TxValidationState stateDummy;
if (!fAddToMempool || (*it)->IsCoinBase() ||
- !AcceptToMemoryPool(mempool, stateDummy, *it,
- nullptr /* plTxnReplaced */, true /* bypass_limits */)) {
+ AcceptToMemoryPool(mempool, *it, true /* bypass_limits */).m_result_type != MempoolAcceptResult::ResultType::VALID) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
@@ -463,9 +461,7 @@ public:
// around easier.
struct ATMPArgs {
const CChainParams& m_chainparams;
- TxValidationState &m_state;
const int64_t m_accept_time;
- std::list<CTransactionRef>* m_replaced_transactions;
const bool m_bypass_limits;
/*
* Return any outpoints which were not previously present in the coins
@@ -476,11 +472,10 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
- CAmount* m_fee_out;
};
// Single transaction acceptance
- bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ MempoolAcceptResult AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
private:
// All the intermediate state that gets passed between the various levels
@@ -491,14 +486,17 @@ private:
CTxMemPool::setEntries m_all_conflicting;
CTxMemPool::setEntries m_ancestors;
std::unique_ptr<CTxMemPoolEntry> m_entry;
+ std::list<CTransactionRef> m_replaced_transactions;
bool m_replacement_transaction;
+ CAmount m_base_fees;
CAmount m_modified_fees;
CAmount m_conflicting_fees;
size_t m_conflicting_size;
const CTransactionRef& m_ptx;
const uint256& m_hash;
+ TxValidationState m_state;
};
// Run the policy checks on a given transaction, excluding any script checks.
@@ -509,18 +507,18 @@ private:
// Run the script checks using our policy flags. As this can be slow, we should
// only invoke this on transactions that have otherwise passed policy checks.
- bool PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Re-run the script checks, using consensus flags, and try to cache the
// result in the scriptcache. This should be done after
// PolicyScriptChecks(). This requires that all inputs either be in our
// utxo set or in the mempool.
- bool ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Try to add the transaction to the mempool, removing any conflicts first.
// Returns true if the transaction is in the mempool after any size
// limiting is performed, false otherwise.
- bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
+ bool Finalize(const ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Compare a package's feerate against minimum allowed.
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
@@ -558,12 +556,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
const uint256& hash = ws.m_hash;
// Copy/alias what we need out of args
- TxValidationState &state = args.m_state;
const int64_t nAcceptTime = args.m_accept_time;
const bool bypass_limits = args.m_bypass_limits;
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
// Alias what we need out of ws
+ TxValidationState& state = ws.m_state;
std::set<uint256>& setConflicts = ws.m_conflicts;
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
CTxMemPool::setEntries& setAncestors = ws.m_ancestors;
@@ -683,16 +681,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
if (!CheckSequenceLocks(m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
- CAmount nFees = 0;
- if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), nFees)) {
+ if (!Consensus::CheckTxInputs(tx, state, m_view, g_chainman.m_blockman.GetSpendHeight(m_view), ws.m_base_fees)) {
return false; // state filled in by CheckTxInputs
}
- // If fee_out is passed, return the fee to the caller
- if (args.m_fee_out) {
- *args.m_fee_out = nFees;
- }
-
// Check for non-standard pay-to-script-hash in inputs
const auto& params = args.m_chainparams.GetConsensus();
auto taproot_state = VersionBitsState(::ChainActive().Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
@@ -707,7 +699,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
// nModifiedFees includes any fee deltas from PrioritiseTransaction
- nModifiedFees = nFees;
+ nModifiedFees = ws.m_base_fees;
m_pool.ApplyDelta(hash, nModifiedFees);
// Keep track of transactions that spend a coinbase, which we re-scan
@@ -721,7 +713,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}
- entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, ::ChainActive().Height(),
+ entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, ::ChainActive().Height(),
fSpendsCoinbase, nSigOpsCost, lp));
unsigned int nSize = entry->GetTxSize();
@@ -925,11 +917,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return true;
}
-bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata)
+bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
{
const CTransaction& tx = *ws.m_ptx;
-
- TxValidationState &state = args.m_state;
+ TxValidationState& state = ws.m_state;
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
@@ -952,12 +943,11 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, const Workspace& ws, Prec
return true;
}
-bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, PrecomputedTransactionData& txdata)
+bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata)
{
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
-
- TxValidationState &state = args.m_state;
+ TxValidationState& state = ws.m_state;
const CChainParams& chainparams = args.m_chainparams;
// Check again against the current block tip's script verification
@@ -984,11 +974,11 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, const Workspace& ws, P
return true;
}
-bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
+bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
{
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
- TxValidationState &state = args.m_state;
+ TxValidationState& state = ws.m_state;
const bool bypass_limits = args.m_bypass_limits;
CTxMemPool::setEntries& allConflicting = ws.m_all_conflicting;
@@ -1007,8 +997,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
hash.ToString(),
FormatMoney(nModifiedFees - nConflictingFees),
(int)entry->GetTxSize() - (int)nConflictingSize);
- if (args.m_replaced_transactions)
- args.m_replaced_transactions->push_back(it->GetSharedTx());
+ ws.m_replaced_transactions.push_back(it->GetSharedTx());
}
m_pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED);
@@ -1031,14 +1020,14 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws)
return true;
}
-bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
+MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args)
{
AssertLockHeld(cs_main);
LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool())
- Workspace workspace(ptx);
+ Workspace ws(ptx);
- if (!PreChecks(args, workspace)) return false;
+ if (!PreChecks(args, ws)) return MempoolAcceptResult(ws.m_state);
// Only compute the precomputed transaction data if we need to verify
// scripts (ie, other policy checks pass). We perform the inexpensive
@@ -1046,31 +1035,35 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata;
- if (!PolicyScriptChecks(args, workspace, txdata)) return false;
+ if (!PolicyScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
- if (!ConsensusScriptChecks(args, workspace, txdata)) return false;
+ if (!ConsensusScriptChecks(args, ws, txdata)) return MempoolAcceptResult(ws.m_state);
// Tx was accepted, but not added
- if (args.m_test_accept) return true;
+ if (args.m_test_accept) {
+ return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
+ }
- if (!Finalize(args, workspace)) return false;
+ if (!Finalize(args, ws)) return MempoolAcceptResult(ws.m_state);
GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
- return true;
+ return MempoolAcceptResult(std::move(ws.m_replaced_transactions), ws.m_base_fees);
}
} // anon namespace
/** (try to) add transaction to memory pool with a specified acceptance time **/
-static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
- int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static MempoolAcceptResult AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool,
+ const CTransactionRef &tx, int64_t nAcceptTime,
+ bool bypass_limits, bool test_accept)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, coins_to_uncache, test_accept, fee_out };
- bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
- if (!res) {
+ MemPoolAccept::ATMPArgs args { chainparams, nAcceptTime, bypass_limits, coins_to_uncache, test_accept };
+
+ const MempoolAcceptResult result = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
+ if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
// Remove coins that were not present in the coins cache before calling ATMPW;
// this is to prevent memory DoS in case we receive a large number of
// invalid transactions that attempt to overrun the in-memory coins cache
@@ -1082,15 +1075,12 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
BlockValidationState state_dummy;
::ChainstateActive().FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC);
- return res;
+ return result;
}
-bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
- std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, bool test_accept, CAmount* fee_out)
+MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, const CTransactionRef &tx, bool bypass_limits, bool test_accept)
{
- const CChainParams& chainparams = Params();
- return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, test_accept, fee_out);
+ return AcceptToMemoryPoolWithTime(Params(), pool, tx, GetTime(), bypass_limits, test_accept);
}
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
@@ -3219,7 +3209,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
if ((int)nFile != nLastBlockFile) {
if (!fKnown) {
- LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
+ LogPrint(BCLog::VALIDATION, "Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
}
FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
@@ -5029,13 +5019,10 @@ bool LoadMempool(CTxMemPool& pool)
if (amountdelta) {
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
}
- TxValidationState state;
if (nTime > nNow - nExpiryTimeout) {
LOCK(cs_main);
- AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nTime,
- nullptr /* plTxnReplaced */, false /* bypass_limits */,
- false /* test_accept */);
- if (state.IsValid()) {
+ if (AcceptToMemoryPoolWithTime(chainparams, pool, tx, nTime, false /* bypass_limits */,
+ false /* test_accept */).m_result_type == MempoolAcceptResult::ResultType::VALID) {
++count;
} else {
// mempool may contain the transaction already, e.g. from
@@ -5159,7 +5146,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
}
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
- LOCK(::cs_main); // for m_active_chainstate access
+ LOCK(::cs_main);
if (m_active_chainstate != nullptr) {
// If a snapshot chainstate exists, it will always be our active.
return m_active_chainstate->m_from_snapshot_blockhash;
@@ -5169,6 +5156,7 @@ Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
std::vector<CChainState*> ChainstateManager::GetAll()
{
+ LOCK(::cs_main);
std::vector<CChainState*> out;
if (!IsSnapshotValidated() && m_ibd_chainstate) {
@@ -5213,11 +5201,13 @@ CChainState& ChainstateManager::ActiveChainstate() const
bool ChainstateManager::IsSnapshotActive() const
{
- return m_snapshot_chainstate && WITH_LOCK(::cs_main, return m_active_chainstate) == m_snapshot_chainstate.get();
+ LOCK(::cs_main);
+ return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
}
CChainState& ChainstateManager::ValidatedChainstate() const
{
+ LOCK(::cs_main);
if (m_snapshot_chainstate && IsSnapshotValidated()) {
return *m_snapshot_chainstate.get();
}
@@ -5227,6 +5217,7 @@ CChainState& ChainstateManager::ValidatedChainstate() const
bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
{
+ LOCK(::cs_main);
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
}
@@ -5242,12 +5233,10 @@ void ChainstateManager::Unload()
void ChainstateManager::Reset()
{
+ LOCK(::cs_main);
m_ibd_chainstate.reset();
m_snapshot_chainstate.reset();
- {
- LOCK(::cs_main);
- m_active_chainstate = nullptr;
- }
+ m_active_chainstate = nullptr;
m_snapshot_validated = false;
}
diff --git a/src/validation.h b/src/validation.h
index e86b3d3af8..238d6009b4 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -12,6 +12,7 @@
#include <amount.h>
#include <coins.h>
+#include <consensus/validation.h>
#include <crypto/common.h> // for ReadLE64
#include <fs.h>
#include <optional.h>
@@ -23,6 +24,7 @@
#include <txdb.h>
#include <versionbits.h>
#include <serialize.h>
+#include <util/check.h>
#include <util/hasher.h>
#include <atomic>
@@ -40,13 +42,12 @@ class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
class CChainParams;
-class CCheckpointData;
+struct CCheckpointData;
class CInv;
class CConnman;
class CScriptCheck;
class CTxMemPool;
class ChainstateManager;
-class TxValidationState;
struct ChainTxData;
struct DisconnectedBlockTransactions;
@@ -181,12 +182,46 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
/** Prune block files up to a given height */
void PruneBlockFilesManual(int nManualPruneHeight);
-/** (try to) add transaction to memory pool
- * plTxnReplaced will be appended to with all transactions replaced from mempool
- * @param[out] fee_out optional argument to return tx fee to the caller **/
-bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
- std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+/**
+* Validation result for a single transaction mempool acceptance.
+*/
+struct MempoolAcceptResult {
+ /** Used to indicate the results of mempool validation,
+ * including the possibility of unfinished validation.
+ */
+ enum class ResultType {
+ VALID, //!> Fully validated, valid.
+ INVALID, //!> Invalid.
+ };
+ ResultType m_result_type;
+ TxValidationState m_state;
+
+ // The following fields are only present when m_result_type = ResultType::VALID
+ /** Mempool transactions replaced by the tx per BIP 125 rules. */
+ std::optional<std::list<CTransactionRef>> m_replaced_transactions;
+ /** Raw base fees. */
+ std::optional<CAmount> m_base_fees;
+
+ /** Constructor for failure case */
+ explicit MempoolAcceptResult(TxValidationState state)
+ : m_result_type(ResultType::INVALID),
+ m_state(state), m_replaced_transactions(nullopt), m_base_fees(nullopt) {
+ Assume(!state.IsValid()); // Can be invalid or error
+ }
+
+ /** Constructor for success case */
+ explicit MempoolAcceptResult(std::list<CTransactionRef>&& replaced_txns, CAmount fees)
+ : m_result_type(ResultType::VALID), m_state(TxValidationState{}),
+ m_replaced_transactions(std::move(replaced_txns)), m_base_fees(fees) {}
+};
+
+/**
+ * (Try to) add a transaction to the memory pool.
+ * @param[in] bypass_limits When true, don't enforce mempool fee limits.
+ * @param[in] test_accept When true, run validation checks but don't submit to mempool.
+ */
+MempoolAcceptResult AcceptToMemoryPool(CTxMemPool& pool, const CTransactionRef& tx,
+ bool bypass_limits, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
@@ -802,7 +837,7 @@ private:
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- std::unique_ptr<CChainState> m_ibd_chainstate;
+ std::unique_ptr<CChainState> m_ibd_chainstate GUARDED_BY(::cs_main);
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
//! non-null, it is always our active chainstate.
@@ -815,7 +850,7 @@ private:
//! This is especially important when, e.g., calling ActivateBestChain()
//! on all chainstates because we are not able to hold ::cs_main going into
//! that call.
- std::unique_ptr<CChainState> m_snapshot_chainstate;
+ std::unique_ptr<CChainState> m_snapshot_chainstate GUARDED_BY(::cs_main);
//! Points to either the ibd or snapshot chainstate; indicates our
//! most-work chain.
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 8505ddc309..99803a91d2 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -934,9 +934,9 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
case TxoutType::NONSTANDARD:
case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT:
- default:
return "unrecognized script";
- }
+ } // no default case, so the compiler can warn about missing cases
+ CHECK_NONFATAL(false);
}
static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data, std::vector<CKeyID>& ordered_pubkeys)
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 15972fe7bb..55110f30d9 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -94,8 +94,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
TxoutType whichType = Solver(scriptPubKey, vSolutions);
CKeyID keyID;
- switch (whichType)
- {
+ switch (whichType) {
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
@@ -194,7 +193,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
}
break;
}
- }
+ } // no default case, so the compiler can warn about missing cases
if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
ret = std::max(ret, IsMineResult::WATCH_ONLY);
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index 27179839b7..b2eb8e4bca 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -30,8 +30,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
- BOOST_CHECK(filename == test_name);
- BOOST_CHECK(env->Directory() == datadir);
+ BOOST_CHECK_EQUAL(filename, test_name);
+ BOOST_CHECK_EQUAL(env->Directory(), datadir);
}
BOOST_AUTO_TEST_CASE(getwalletenv_directory)
@@ -41,8 +41,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_directory)
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
- BOOST_CHECK(filename == expected_name);
- BOOST_CHECK(env->Directory() == datadir);
+ BOOST_CHECK_EQUAL(filename, expected_name);
+ BOOST_CHECK_EQUAL(env->Directory(), datadir);
}
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index e70b56c529..45e1b8c4b8 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
@@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
- BOOST_CHECK(walletdir == expected_path);
+ BOOST_CHECK_EQUAL(walletdir, expected_path);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 4e6270220e..69854cae05 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -551,13 +551,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CHDChain chain;
ssValue >> chain;
pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
- } else if (strType == DBKeys::FLAGS) {
- uint64_t flags;
- ssValue >> flags;
- if (!pwallet->LoadWalletFlags(flags)) {
- strErr = "Error reading wallet database: Unknown non-tolerable wallet flags found";
- return false;
- }
} else if (strType == DBKeys::OLD_KEY) {
strErr = "Found unsupported 'wkey' record, try loading with version 0.18";
return false;
@@ -662,7 +655,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.fIsEncrypted = true;
} else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
- strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) {
+ strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
+ strType != DBKeys::FLAGS) {
wss.m_unknown_records++;
}
} catch (const std::exception& e) {
@@ -707,6 +701,16 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
pwallet->LoadMinVersion(nMinVersion);
}
+ // Load wallet flags, so they are known when processing other records.
+ // The FLAGS key is absent during wallet creation.
+ uint64_t flags;
+ if (m_batch->Read(DBKeys::FLAGS, flags)) {
+ if (!pwallet->LoadWalletFlags(flags)) {
+ pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
+ return DBErrors::CORRUPT;
+ }
+ }
+
// Get cursor
if (!m_batch->StartCursor())
{
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index bc90491a2c..b2cb0bf479 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -103,10 +103,8 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
-bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command, const std::string& name)
+bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
{
- const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name);
-
if (args.IsArgSet("-format") && command != "createfromdump") {
tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n");
return false;
@@ -119,6 +117,12 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command,
tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
return false;
}
+ if (command == "create" && !args.IsArgSet("-wallet")) {
+ tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
+ return false;
+ }
+ const std::string name = args.GetArg("-wallet", "");
+ const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), name);
if (command == "create") {
DatabaseOptions options;
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index f544a6f727..f4516bb5bc 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -10,7 +10,7 @@
namespace WalletTool {
void WalletShowInfo(CWallet* wallet_instance);
-bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command, const std::string& file);
+bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command);
} // namespace WalletTool