aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rwxr-xr-xci/lint/04_install.sh1
-rw-r--r--contrib/guix/README.md11
-rw-r--r--depends/Makefile33
-rw-r--r--doc/bips.md4
-rw-r--r--doc/developer-notes.md5
-rw-r--r--src/Makefile.am2
-rw-r--r--src/init.cpp2
-rw-r--r--src/net.cpp50
-rw-r--r--src/net.h39
-rw-r--r--src/net_processing.cpp364
-rw-r--r--src/net_processing.h4
-rw-r--r--src/netaddress.h2
-rw-r--r--src/netbase.cpp16
-rw-r--r--src/netbase.h2
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/rpc/net.cpp15
-rw-r--r--src/test/denialofservice_tests.cpp10
-rw-r--r--src/test/fuzz/crypto.cpp13
-rw-r--r--src/test/fuzz/muhash.cpp10
-rw-r--r--src/test/net_tests.cpp19
-rw-r--r--src/wallet/walletdb.cpp20
-rwxr-xr-xtest/functional/feature_config_args.py27
-rwxr-xr-xtest/functional/feature_proxy.py4
-rwxr-xr-xtest/functional/interface_zmq.py76
-rwxr-xr-xtest/functional/rpc_net.py6
-rw-r--r--test/functional/test_framework/key.py6
-rwxr-xr-xtest/lint/lint-python-dead-code.sh23
28 files changed, 460 insertions, 308 deletions
diff --git a/Makefile.am b/Makefile.am
index f6b824faaa..66be768277 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@
# Pattern rule to print variables, e.g. make print-top_srcdir
print-%:
- @echo $* = $($*)
+ @echo '$*' = '$($*)'
ACLOCAL_AMFLAGS = -I build-aux/m4
SUBDIRS = src
diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh
index a0b579de1e..343b82a1ad 100755
--- a/ci/lint/04_install.sh
+++ b/ci/lint/04_install.sh
@@ -15,6 +15,7 @@ ${CI_RETRY_EXE} pip3 install codespell==2.0.0
${CI_RETRY_EXE} pip3 install flake8==3.8.3
${CI_RETRY_EXE} pip3 install yq
${CI_RETRY_EXE} pip3 install mypy==0.781
+${CI_RETRY_EXE} pip3 install vulture==2.3
SHELLCHECK_VERSION=v0.7.1
curl -sL "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/
diff --git a/contrib/guix/README.md b/contrib/guix/README.md
index 5870deee39..1229932e50 100644
--- a/contrib/guix/README.md
+++ b/contrib/guix/README.md
@@ -217,11 +217,11 @@ As mentioned at the bottom of [this manual page][guix/bin-install]:
>
> make guix-binary.x86_64-linux.tar.xz
-### When will Guix be packaged in debian?
+### Is Guix packaged in my operating system?
-Thanks to Vagrant Cascadian's diligent work, Guix is now [in debian
-experimental][debian/guix-experimental]! Hopefully it will make its way into a
-release soon.
+Guix is shipped starting with [Debian Bullseye][debian/guix-bullseye] and
+[Ubuntu 21.04 "Hirsute Hippo"][ubuntu/guix-hirsute]. Other operating systems
+are working on packaging Guix as well.
[b17e]: http://bootstrappable.org/
[r12e/source-date-epoch]: https://reproducible-builds.org/docs/source-date-epoch/
@@ -233,5 +233,6 @@ release soon.
[guix/substitute-server-auth]: https://www.gnu.org/software/guix/manual/en/html_node/Substitute-Server-Authorization.html
[guix/time-machine]: https://guix.gnu.org/manual/en/html_node/Invoking-guix-time_002dmachine.html
-[debian/guix-experimental]: https://packages.debian.org/experimental/guix
+[debian/guix-bullseye]: https://packages.debian.org/bullseye/guix
+[ubuntu/guix-hirsute]: https://packages.ubuntu.com/hirsute/guix
[fanquake/guix-docker]: https://github.com/fanquake/core-review/tree/master/guix
diff --git a/depends/Makefile b/depends/Makefile
index 016b73a5cb..4cd4d72fc2 100644
--- a/depends/Makefile
+++ b/depends/Makefile
@@ -2,7 +2,7 @@
# Pattern rule to print variables, e.g. make print-top_srcdir
print-%:
- @echo $* = $($*)
+ @echo '$*' = '$($*)'
# When invoking a sub-make, keep only the command line variable definitions
# matching the pattern in the filter function.
@@ -112,19 +112,27 @@ include builders/$(build_os).mk
include builders/default.mk
include packages/packages.mk
+full_env=$(shell printenv)
+
build_id_string:=$(BUILD_ID_SALT)
-build_id_string+=$(shell $(build_CC) --version 2>/dev/null)
-build_id_string+=$(shell $(build_AR) --version 2>/dev/null)
-build_id_string+=$(shell $(build_CXX) --version 2>/dev/null)
-build_id_string+=$(shell $(build_RANLIB) --version 2>/dev/null)
-build_id_string+=$(shell $(build_STRIP) --version 2>/dev/null)
+
+# GCC only prints COLLECT_LTO_WRAPPER when invoked with just "-v", but we want
+# the information from "-v -E -" as well, so just include both.
+#
+# '3>&1 1>&2 2>&3 > /dev/null' is supposed to swap stdin and stdout and silence
+# stdin, since we only want the stderr output
+build_id_string+=$(shell $(build_CC) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(build_CC) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+build_id_string+=$(shell $(build_AR) --version 2>/dev/null) $(filter AR_%,$(full_env)) ZERO_AR_DATE=$(ZERO_AR_DATE)
+build_id_string+=$(shell $(build_CXX) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(build_CXX) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+build_id_string+=$(shell $(build_RANLIB) --version 2>/dev/null) $(filter RANLIB_%,$(full_env))
+build_id_string+=$(shell $(build_STRIP) --version 2>/dev/null) $(filter STRIP_%,$(full_env))
$(host_arch)_$(host_os)_id_string:=$(HOST_ID_SALT)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_CC) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_AR) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null)
-$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_CC) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(host_CC) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_AR) --version 2>/dev/null) $(filter AR_%,$(full_env)) ZERO_AR_DATE=$(ZERO_AR_DATE)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) -v < /dev/null 3>&1 1>&2 2>&3 > /dev/null) $(shell $(host_CXX) -v -E - < /dev/null 3>&1 1>&2 2>&3 > /dev/null)
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(filter RANLIB_%,$(full_env))
+$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) $(filter STRIP_%,$(full_env))
ifneq ($(strip $(FORCE_USE_SYSTEM_CLANG)),)
# Make sure that cache is invalidated when switching between system and
@@ -133,6 +141,9 @@ build_id_string+=system_clang
$(host_arch)_$(host_os)_id_string+=system_clang
endif
+build_id_string+=GUIX_ENVIRONMENT=$(GUIX_ENVIRONMENT)
+$(host_arch)_$(host_os)_id_string+=GUIX_ENVIRONMENT=$(GUIX_ENVIRONMENT)
+
qrencode_packages_$(NO_QR) = $(qrencode_packages)
qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) $(qrencode_packages_)
diff --git a/doc/bips.md b/doc/bips.md
index 8c20533c9b..a5e9a6c020 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -15,6 +15,9 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
* [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)). As of **v0.13.0**, this is only available for `NODE_BLOOM` (BIP 111) peers.
* [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial Merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)). Disabled by default since **v0.19.0**, can be enabled by the `-peerbloomfilters` option.
* [`BIP 42`](https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki): The bug that would have caused the subsidy schedule to resume after block 13440000 was fixed in **v0.9.2** ([PR #3842](https://github.com/bitcoin/bitcoin/pull/3842)).
+* [`BIP 43`](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 43. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
+* [`BIP 44`](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 44. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
+* [`BIP 49`](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 49. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting **v0.17.0**, whether to send reject messages can be configured with the `-enablebip61` option, and support is deprecated (disabled by default) as of **v0.18.0**. Support was removed in **v0.20.0** ([PR #15437](https://github.com/bitcoin/bitcoin/pull/15437)).
* [`BIP 65`](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki): The CHECKLOCKTIMEVERIFY softfork was merged in **v0.12.0** ([PR #6351](https://github.com/bitcoin/bitcoin/pull/6351)), and backported to **v0.11.2** and **v0.10.4**. Mempool-only CLTV was added in [PR #6124](https://github.com/bitcoin/bitcoin/pull/6124).
* [`BIP 66`](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki): The strict DER rules and associated version 3 blocks have been implemented since **v0.10.0** ([PR #5713](https://github.com/bitcoin/bitcoin/pull/5713)).
@@ -24,6 +27,7 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
Support can be optionally disabled at build time since **v0.18.0** ([PR 14451](https://github.com/bitcoin/bitcoin/pull/14451)),
and it is disabled by default at build time since **v0.19.0** ([PR #15584](https://github.com/bitcoin/bitcoin/pull/15584)).
It has been removed as of **v0.20.0** ([PR 17165](https://github.com/bitcoin/bitcoin/pull/17165)).
+* [`BIP 84`](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki): The experimental descriptor wallets introduced in **v0.21.0** by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 84. ([PR #16528](https://github.com/bitcoin/bitcoin/pull/16528))
* [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)).
* [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)).
* [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)), and has been *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)).
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 596f65cf10..011c38321c 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -75,6 +75,11 @@ tool to clean up patches automatically before submission.
on the same line as the `if`, without braces. In every other case,
braces are required, and the `then` and `else` clauses must appear
correctly indented on a new line.
+ - There's no hard limit on line width, but prefer to keep lines to <100
+ characters if doing so does not decrease readability. Break up long
+ function declarations over multiple lines using the Clang Format
+ [AlignAfterOpenBracket](https://clang.llvm.org/docs/ClangFormatStyleOptions.html)
+ style option.
- **Symbol naming conventions**. These are preferred in new code, but are not
required when doing so would need changes to significant pieces of existing
diff --git a/src/Makefile.am b/src/Makefile.am
index 869270d1fc..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
diff --git a/src/init.cpp b/src/init.cpp
index f71fae41e7..96fb32ce2a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -446,7 +446,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with ipv4 or ipv6 but not onion and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with ipv4 or ipv6 but not onion and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
diff --git a/src/net.cpp b/src/net.cpp
index 2c8f532bd8..81176785a2 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -476,7 +476,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(sock->Get());
- CNode* pnode = new CNode(id, nLocalServices, sock->Release(), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type);
+ 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)
@@ -601,21 +601,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
stats.minFeeFilter = 0;
}
- // It is common for nodes with good ping times to suddenly become lagged,
- // due to a new block arriving or other large transfer.
- // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
- // since pingtime does not update until the ping is complete, which might take a while.
- // So, if a ping is taking an unusually long time in flight,
- // the caller can immediately detect that this is happening.
- std::chrono::microseconds ping_wait{0};
- if ((0 != nPingNonceSent) && (0 != m_ping_start.load().count())) {
- ping_wait = GetTime<std::chrono::microseconds>() - m_ping_start.load();
- }
-
- // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :)
- stats.m_ping_usec = nPingUsecTime;
- stats.m_min_ping_usec = nMinPingUsecTime;
- stats.m_ping_wait_usec = count_microseconds(ping_wait);
+ stats.m_ping_usec = m_last_ping_time;
+ stats.m_min_ping_usec = m_min_ping_time;
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
@@ -837,7 +824,7 @@ size_t CConnman::SocketSendData(CNode& node) const
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
{
- return a.nMinPingUsecTime > b.nMinPingUsecTime;
+ return a.m_min_ping_time > b.m_min_ping_time;
}
static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -992,7 +979,7 @@ bool CConnman::AttemptToEvictConnection()
peer_relay_txes = node->m_tx_relay->fRelayTxes;
peer_filter_not_null = node->m_tx_relay->pfilter != nullptr;
}
- NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
+ NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->m_min_ping_time,
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
@@ -1221,18 +1208,17 @@ void CConnman::NotifyNumConnectionsChanged()
}
}
+bool CConnman::RunInactivityChecks(const CNode& node) const
+{
+ return GetSystemTimeInSeconds() > node.nTimeConnected + m_peer_connect_timeout;
+}
+
bool CConnman::InactivityCheck(const CNode& node) const
{
// Use non-mockable system time (otherwise these timers will pop when we
// use setmocktime in the tests).
int64_t now = GetSystemTimeInSeconds();
- if (now <= node.nTimeConnected + m_peer_connect_timeout) {
- // Only run inactivity checks if the peer has been connected longer
- // than m_peer_connect_timeout.
- return false;
- }
-
if (node.nLastRecv == 0 || node.nLastSend == 0) {
LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d peer=%d\n", m_peer_connect_timeout, node.nLastRecv != 0, node.nLastSend != 0, node.GetId());
return true;
@@ -1248,14 +1234,6 @@ bool CConnman::InactivityCheck(const CNode& node) const
return true;
}
- if (node.nPingNonceSent && node.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL} < GetTime<std::chrono::microseconds>()) {
- // We use mockable time for ping timeouts. This means that setmocktime
- // may cause pings to time out for peers that have been connected for
- // longer than m_peer_connect_timeout.
- LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(GetTime<std::chrono::microseconds>() - node.m_ping_start.load()), node.GetId());
- return true;
- }
-
if (!node.fSuccessfullyConnected) {
LogPrint(BCLog::NET, "version handshake timeout peer=%d\n", node.GetId());
return true;
@@ -1537,7 +1515,7 @@ void CConnman::SocketHandler()
if (bytes_sent) RecordBytesSent(bytes_sent);
}
- if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
+ if (RunInactivityChecks(*pnode) && InactivityCheck(*pnode)) pnode->fDisconnect = true;
}
{
LOCK(cs_vNodes);
@@ -1813,7 +1791,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
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 and all -seednode(s) attempted\n");
+ LogPrintf("Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n");
}
}
@@ -2855,12 +2833,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;
diff --git a/src/net.h b/src/net.h
index 1678eda8e7..682150d65a 100644
--- a/src/net.h
+++ b/src/net.h
@@ -260,7 +260,6 @@ public:
mapMsgCmdSize mapRecvBytesPerMsgCmd;
NetPermissionFlags m_permissionFlags;
int64_t m_ping_usec;
- int64_t m_ping_wait_usec;
int64_t m_min_ping_usec;
CAmount minFeeFilter;
// Our address, as reported by the peer
@@ -429,6 +428,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;
/**
@@ -589,19 +590,14 @@ public:
* in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastTXTime{0};
- // Ping time measurement:
- // The pong reply we're expecting, or 0 if no pong expected.
- std::atomic<uint64_t> nPingNonceSent{0};
- /** When the last ping was sent, or 0 if no ping was ever sent */
- std::atomic<std::chrono::microseconds> m_ping_start{0us};
- // Last measured round-trip time.
- std::atomic<int64_t> nPingUsecTime{0};
- // Best measured round-trip time.
- std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
- // 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);
+ /** Last measured round-trip time. Used only for RPC/GUI stats/debugging.*/
+ std::atomic<int64_t> m_last_ping_time{0};
+
+ /** Lowest measured round-trip time. Used as an inbound peer eviction
+ * criterium in CConnman::AttemptToEvictConnection. */
+ std::atomic<int64_t> m_min_ping_time{std::numeric_limits<int64_t>::max()};
+
+ 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;
@@ -719,8 +715,11 @@ 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; }
+ /** A ping-pong round trip has completed successfully. Update latest and minimum ping times. */
+ void PongReceived(std::chrono::microseconds ping_time) {
+ m_last_ping_time = count_microseconds(ping_time);
+ m_min_ping_time = std::min(m_min_ping_time.load(), count_microseconds(ping_time));
+ }
private:
const NodeId id;
@@ -754,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);
};
@@ -1026,6 +1022,9 @@ public:
void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = std::move(asmap); }
+ /** Return true if the peer has been connected for long enough to do inactivity checks. */
+ bool RunInactivityChecks(const CNode& node) const;
+
private:
struct ListenSocket {
public:
@@ -1254,7 +1253,7 @@ struct NodeEvictionCandidate
{
NodeId id;
int64_t nTimeConnected;
- int64_t nMinPingUsecTime;
+ int64_t m_min_ping_time;
int64_t nLastBlockTime;
int64_t nLastTXTime;
bool fRelevantServices;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 4340eb120c..881f5d7297 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.
@@ -211,6 +219,13 @@ struct Peer {
/** This peer's reported block height when we connected */
std::atomic<int> m_starting_height{-1};
+ /** The pong reply we're expecting, or 0 if no pong expected. */
+ std::atomic<uint64_t> m_ping_nonce_sent{0};
+ /** When the last ping was sent, or 0 if no ping was ever sent */
+ std::atomic<std::chrono::microseconds> m_ping_start{0us};
+ /** Whether a ping has been requested by the user */
+ std::atomic<bool> m_ping_queued{false};
+
/** Set of txids to reconsider once their parent transactions have been accepted **/
std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
@@ -248,6 +263,7 @@ public:
void CheckForStaleTipAndEvictPeers() override;
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override;
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
+ void SendPings() override;
void SetBestHeight(int height) override { m_best_height = height; };
void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override;
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
@@ -294,9 +310,10 @@ private:
/** Maybe disconnect a peer and discourage future connections from its address.
*
* @param[in] pnode The node to check.
+ * @param[in] peer The peer object to check.
* @return True if the peer was marked for disconnection in this function
*/
- bool MaybeDiscourageAndDisconnect(CNode& pnode);
+ bool MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer);
void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
/** Process a single headers message from a peer. */
@@ -315,6 +332,10 @@ private:
/** Send a version message to a peer */
void PushNodeVersion(CNode& pnode, int64_t nTime);
+ /** Send a ping message every PING_INTERVAL or if requested via RPC. May
+ * mark the peer to be disconnected if a ping has timed out. */
+ void MaybeSendPing(CNode& node_to, Peer& peer);
+
const CChainParams& m_chainparams;
CConnman& m_connman;
/** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
@@ -345,10 +366,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 +378,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 +428,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 +465,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 +659,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 +683,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 +701,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 +752,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 +768,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 +791,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 +815,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 +844,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 +952,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 +1045,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 +1057,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);
@@ -1060,6 +1101,18 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats)
PeerRef peer = GetPeerRef(nodeid);
if (peer == nullptr) return false;
stats.m_starting_height = peer->m_starting_height;
+ // It is common for nodes with good ping times to suddenly become lagged,
+ // due to a new block arriving or other large transfer.
+ // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
+ // since pingtime does not update until the ping is complete, which might take a while.
+ // So, if a ping is taking an unusually long time in flight,
+ // the caller can immediately detect that this is happening.
+ std::chrono::microseconds ping_wait{0};
+ if ((0 != peer->m_ping_nonce_sent) && (0 != peer->m_ping_start.load().count())) {
+ ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
+ }
+
+ stats.m_ping_wait_usec = count_microseconds(ping_wait);
return true;
}
@@ -1344,7 +1397,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 +1447,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 +1477,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 +1603,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 +1616,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 +1640,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)
@@ -1599,6 +1652,12 @@ bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED
return g_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr;
}
+void PeerManagerImpl::SendPings()
+{
+ LOCK(m_peer_mutex);
+ for(auto& it : m_peer_map) it.second->m_ping_queued = true;
+}
+
void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
{
connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
@@ -1825,10 +1884,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 +1911,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 +1940,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 +1978,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 +2001,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 +2160,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 +2204,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;
}
}
}
@@ -2761,7 +2819,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());
}
@@ -2904,7 +2962,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);
@@ -2946,7 +3004,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;
@@ -3185,7 +3243,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
@@ -3264,7 +3322,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());
@@ -3454,7 +3512,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 {
@@ -3810,15 +3868,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
vRecv >> nonce;
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
- if (pfrom.nPingNonceSent != 0) {
- if (nonce == pfrom.nPingNonceSent) {
+ if (peer->m_ping_nonce_sent != 0) {
+ if (nonce == peer->m_ping_nonce_sent) {
// Matching pong received, this ping is no longer outstanding
bPingFinished = true;
- const auto ping_time = ping_end - pfrom.m_ping_start.load();
+ const auto ping_time = ping_end - peer->m_ping_start.load();
if (ping_time.count() >= 0) {
- // Successful ping time measurement, replace previous
- pfrom.nPingUsecTime = count_microseconds(ping_time);
- pfrom.nMinPingUsecTime = std::min(pfrom.nMinPingUsecTime.load(), count_microseconds(ping_time));
+ // Let connman know about this successful ping-pong
+ pfrom.PongReceived(ping_time);
} else {
// This should never happen
sProblem = "Timing mishap";
@@ -3845,12 +3902,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
pfrom.GetId(),
sProblem,
- pfrom.nPingNonceSent,
+ peer->m_ping_nonce_sent,
nonce,
nAvail);
}
if (bPingFinished) {
- pfrom.nPingNonceSent = 0;
+ peer->m_ping_nonce_sent = 0;
}
return;
}
@@ -3969,43 +4026,39 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
-bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode)
+bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer)
{
- const NodeId peer_id{pnode.GetId()};
- PeerRef peer = GetPeerRef(peer_id);
- if (peer == nullptr) return false;
-
{
- LOCK(peer->m_misbehavior_mutex);
+ LOCK(peer.m_misbehavior_mutex);
// There's nothing to do if the m_should_discourage flag isn't set
- if (!peer->m_should_discourage) return false;
+ if (!peer.m_should_discourage) return false;
- peer->m_should_discourage = false;
+ peer.m_should_discourage = false;
} // peer.m_misbehavior_mutex
if (pnode.HasPermission(PF_NOBAN)) {
// We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag
- LogPrintf("Warning: not punishing noban peer %d!\n", peer_id);
+ LogPrintf("Warning: not punishing noban peer %d!\n", peer.m_id);
return false;
}
if (pnode.IsManualConn()) {
// We never disconnect or discourage manual peers for bad behavior
- LogPrintf("Warning: not punishing manually connected peer %d!\n", peer_id);
+ LogPrintf("Warning: not punishing manually connected peer %d!\n", peer.m_id);
return false;
}
if (pnode.addr.IsLocal()) {
// We disconnect local peers for bad behavior but don't discourage (since that would discourage
// all peers on the same local address)
- LogPrintf("Warning: disconnecting but not discouraging local peer %d!\n", peer_id);
+ LogPrintf("Warning: disconnecting but not discouraging local peer %d!\n", peer.m_id);
pnode.fDisconnect = true;
return true;
}
// Normal case: Disconnect the peer and discourage all nodes sharing the address
- LogPrint(BCLog::NET, "Disconnecting and discouraging peer %d!\n", peer_id);
+ LogPrint(BCLog::NET, "Disconnecting and discouraging peer %d!\n", peer.m_id);
if (m_banman) m_banman->Discourage(pnode.addr);
m_connman.DisconnectNode(pnode.addr);
return true;
@@ -4021,7 +4074,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);
}
}
@@ -4253,8 +4306,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);
@@ -4268,6 +4321,50 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
}
}
+void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer)
+{
+ // Use mockable time for ping timeouts.
+ // This means that setmocktime may cause pings to time out.
+ auto now = GetTime<std::chrono::microseconds>();
+
+ if (m_connman.RunInactivityChecks(node_to) && peer.m_ping_nonce_sent &&
+ now > peer.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL}) {
+ LogPrint(BCLog::NET, "ping timeout: %fs peer=%d\n", 0.000001 * count_microseconds(now - peer.m_ping_start.load()), peer.m_id);
+ node_to.fDisconnect = true;
+ return;
+ }
+
+ const CNetMsgMaker msgMaker(node_to.GetCommonVersion());
+ bool pingSend = false;
+
+ if (peer.m_ping_queued) {
+ // RPC ping request by user
+ pingSend = true;
+ }
+
+ if (peer.m_ping_nonce_sent == 0 && now > peer.m_ping_start.load() + PING_INTERVAL) {
+ // Ping automatically sent as a latency probe & keepalive.
+ pingSend = true;
+ }
+
+ if (pingSend) {
+ uint64_t nonce = 0;
+ while (nonce == 0) {
+ GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
+ }
+ peer.m_ping_queued = false;
+ peer.m_ping_start = now;
+ if (node_to.GetCommonVersion() > BIP0031_VERSION) {
+ peer.m_ping_nonce_sent = nonce;
+ m_connman.PushMessage(&node_to, msgMaker.Make(NetMsgType::PING, nonce));
+ } else {
+ // Peer is too old to support ping command with nonce, pong will never arrive.
+ peer.m_ping_nonce_sent = 0;
+ m_connman.PushMessage(&node_to, msgMaker.Make(NetMsgType::PING));
+ }
+ }
+}
+
namespace {
class CompareInvMempoolOrder
{
@@ -4292,11 +4389,12 @@ public:
bool PeerManagerImpl::SendMessages(CNode* pto)
{
PeerRef peer = GetPeerRef(pto->GetId());
+ if (!peer) return false;
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
// We must call MaybeDiscourageAndDisconnect first, to ensure that we'll
// disconnect misbehaving peers even before the version handshake is complete.
- if (MaybeDiscourageAndDisconnect(*pto)) return true;
+ if (MaybeDiscourageAndDisconnect(*pto, *peer)) return true;
// Don't send anything until the version handshake is complete
if (!pto->fSuccessfullyConnected || pto->fDisconnect)
@@ -4305,34 +4403,10 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
// If we get here, the outgoing message serialization version is set and can't change.
const CNetMsgMaker msgMaker(pto->GetCommonVersion());
- //
- // Message: ping
- //
- bool pingSend = false;
- if (pto->fPingQueued) {
- // RPC ping request by user
- pingSend = true;
- }
- if (pto->nPingNonceSent == 0 && pto->m_ping_start.load() + PING_INTERVAL < GetTime<std::chrono::microseconds>()) {
- // Ping automatically sent as a latency probe & keepalive.
- pingSend = true;
- }
- if (pingSend) {
- uint64_t nonce = 0;
- while (nonce == 0) {
- GetRandBytes((unsigned char*)&nonce, sizeof(nonce));
- }
- pto->fPingQueued = false;
- pto->m_ping_start = GetTime<std::chrono::microseconds>();
- if (pto->GetCommonVersion() > BIP0031_VERSION) {
- pto->nPingNonceSent = nonce;
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce));
- } else {
- // Peer is too old to support ping command with nonce, pong will never arrive.
- pto->nPingNonceSent = 0;
- m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING));
- }
- }
+ MaybeSendPing(*pto, *peer);
+
+ // MaybeSendPing may have marked peer for disconnection
+ if (pto->fDisconnect) return true;
{
LOCK(cs_main);
@@ -4781,11 +4855,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());
}
@@ -4807,7 +4881,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/net_processing.h b/src/net_processing.h
index eaa3b142a8..d7be453df5 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -30,6 +30,7 @@ struct CNodeStateStats {
int nSyncHeight = -1;
int nCommonHeight = -1;
int m_starting_height = -1;
+ int64_t m_ping_wait_usec;
std::vector<int> vHeightInFlight;
};
@@ -47,6 +48,9 @@ public:
/** Whether this node ignores txs received over p2p. */
virtual bool IgnoresIncomingTxs() = 0;
+ /** Send ping message to all peers */
+ virtual void SendPings() = 0;
+
/** Set the best height */
virtual void SetBestHeight(int height) = 0;
diff --git a/src/netaddress.h b/src/netaddress.h
index b9eade7fd5..d0986557f7 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -36,7 +36,7 @@ static constexpr int ADDRV2_FORMAT = 0x20000000;
* @note An address may belong to more than one network, for example `10.0.0.1`
* belongs to both `NET_UNROUTABLE` and `NET_IPV4`.
* Keep these sequential starting from 0 and `NET_MAX` as the last entry.
- * We have loops like `for (int i = 0; i < NET_MAX; i++)` that expect to iterate
+ * We have loops like `for (int i = 0; i < NET_MAX; ++i)` that expect to iterate
* over all enum values and also `GetExtNetwork()` "extends" this enum by
* introducing standalone constants starting from `NET_MAX`.
*/
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 24188f83c6..0c5b3a220e 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -59,7 +59,7 @@ enum Network ParseNetwork(const std::string& net_in) {
std::string GetNetworkName(enum Network net)
{
switch (net) {
- case NET_UNROUTABLE: return "unroutable";
+ case NET_UNROUTABLE: return "not_publicly_routable";
case NET_IPV4: return "ipv4";
case NET_IPV6: return "ipv6";
case NET_ONION: return "onion";
@@ -72,6 +72,20 @@ std::string GetNetworkName(enum Network net)
assert(false);
}
+std::vector<std::string> GetNetworkNames(bool append_unroutable)
+{
+ std::vector<std::string> names;
+ for (int n = 0; n < NET_MAX; ++n) {
+ const enum Network network{static_cast<Network>(n)};
+ if (network == NET_UNROUTABLE || network == NET_I2P || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ names.emplace_back(GetNetworkName(network));
+ }
+ if (append_unroutable) {
+ names.emplace_back(GetNetworkName(NET_UNROUTABLE));
+ }
+ return names;
+}
+
bool static LookupIntern(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
{
vIP.clear();
diff --git a/src/netbase.h b/src/netbase.h
index afc373ef49..847a72ca8e 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -42,6 +42,8 @@ public:
enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
+/** Return a vector of publicly routable Network names; optionally append NET_UNROUTABLE. */
+std::vector<std::string> GetNetworkNames(bool append_unroutable = false);
bool SetProxy(enum Network net, const proxyType &addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 2758ee351a..ea12ce1583 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1115,7 +1115,6 @@ void RPCConsole::updateDetailWidget()
ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_usec));
- ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.m_ping_wait_usec));
ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_usec));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
@@ -1149,6 +1148,7 @@ void RPCConsole::updateDetailWidget()
ui->peerCommonHeight->setText(tr("Unknown"));
ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height));
+ ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait_usec));
}
ui->detailWidget->show();
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 47d77b341a..0224ee697a 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -77,13 +77,12 @@ static RPCHelpMan ping()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
+ if (!node.peerman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
// Request that each node send a ping during next message processing pass
- node.connman->ForEachNode([](CNode* pnode) {
- pnode->fPingQueued = true;
- });
+ node.peerman->SendPings();
return NullUniValue;
},
};
@@ -104,7 +103,7 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
{RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
{RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
- {RPCResult::Type::STR, "network", "Network (ipv4, ipv6, or onion) the peer connected through"},
+ {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"},
{RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
"peer selection (only available if the asmap config flag is set)"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
@@ -209,8 +208,8 @@ static RPCHelpMan getpeerinfo()
if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) {
obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6);
}
- if (stats.m_ping_wait_usec > 0) {
- obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6);
+ if (fStateStats && statestats.m_ping_wait_usec > 0) {
+ obj.pushKV("pingwait", ((double)statestats.m_ping_wait_usec) / 1e6);
}
obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
@@ -587,7 +586,7 @@ static RPCHelpMan getnetworkinfo()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"},
+ {RPCResult::Type::STR, "name", "network (" + Join(GetNetworkNames(), ", ") + ")"},
{RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"},
{RPCResult::Type::BOOL, "reachable", "is the network reachable?"},
{RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"},
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/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index c2bb3a1a4e..17ac48fca7 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -4,7 +4,6 @@
#include <crypto/hmac_sha256.h>
#include <crypto/hmac_sha512.h>
-#include <crypto/muhash.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -36,7 +35,6 @@ FUZZ_TARGET(crypto)
CSHA512 sha512;
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
- MuHash3072 muhash;
while (fuzzed_data_provider.ConsumeBool()) {
CallOneOf(
@@ -63,12 +61,6 @@ FUZZ_TARGET(crypto)
(void)Hash(data);
(void)Hash160(data);
(void)sha512.Size();
-
- if (fuzzed_data_provider.ConsumeBool()) {
- muhash *= MuHash3072(data);
- } else {
- muhash /= MuHash3072(data);
- }
},
[&] {
(void)hash160.Reset();
@@ -78,7 +70,6 @@ FUZZ_TARGET(crypto)
(void)sha256.Reset();
(void)sha3.Reset();
(void)sha512.Reset();
- muhash = MuHash3072();
},
[&] {
CallOneOf(
@@ -122,10 +113,6 @@ FUZZ_TARGET(crypto)
[&] {
data.resize(SHA3_256::OUTPUT_SIZE);
sha3.Finalize(data);
- },
- [&] {
- uint256 out;
- muhash.Finalize(out);
});
});
}
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
index 8f843ca773..2d761cef15 100644
--- a/src/test/fuzz/muhash.cpp
+++ b/src/test/fuzz/muhash.cpp
@@ -41,6 +41,11 @@ FUZZ_TARGET(muhash)
muhash.Finalize(out2);
assert(out == out2);
+ MuHash3072 muhash3;
+ muhash3 *= muhash;
+ uint256 out3;
+ muhash3.Finalize(out3);
+ assert(out == out3);
// Test that removing all added elements brings the object back to it's initial state
muhash /= muhash;
@@ -50,4 +55,9 @@ FUZZ_TARGET(muhash)
muhash2.Finalize(out2);
assert(out == out2);
+
+ muhash3.Remove(data);
+ muhash3.Remove(data2);
+ muhash3.Finalize(out3);
+ assert(out == out3);
}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index a1b41e17ed..f52905e1ef 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
@@ -793,7 +794,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(const int n_c
candidates.push_back({
/* id */ id,
/* nTimeConnected */ static_cast<int64_t>(random_context.randrange(100)),
- /* nMinPingUsecTime */ static_cast<int64_t>(random_context.randrange(100)),
+ /* m_min_ping_time */ static_cast<int64_t>(random_context.randrange(100)),
/* nLastBlockTime */ static_cast<int64_t>(random_context.randrange(100)),
/* nLastTXTime */ static_cast<int64_t>(random_context.randrange(100)),
/* fRelevantServices */ random_context.randbool(),
@@ -853,7 +854,7 @@ BOOST_AUTO_TEST_CASE(node_eviction_test)
// from eviction.
BOOST_CHECK(!IsEvicted(
number_of_nodes, [](NodeEvictionCandidate& candidate) {
- candidate.nMinPingUsecTime = candidate.id;
+ candidate.m_min_ping_time = candidate.id;
},
{0, 1, 2, 3, 4, 5, 6, 7}, random_context));
@@ -900,7 +901,7 @@ BOOST_AUTO_TEST_CASE(node_eviction_test)
BOOST_CHECK(!IsEvicted(
number_of_nodes, [number_of_nodes](NodeEvictionCandidate& candidate) {
candidate.nKeyedNetGroup = number_of_nodes - candidate.id; // 4 protected
- candidate.nMinPingUsecTime = candidate.id; // 8 protected
+ candidate.m_min_ping_time = candidate.id; // 8 protected
candidate.nLastTXTime = number_of_nodes - candidate.id; // 4 protected
candidate.nLastBlockTime = number_of_nodes - candidate.id; // 4 protected
},
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/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 82dd0e3b80..573760a8cb 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -149,20 +149,21 @@ class ConfArgsTest(BitcoinTestFramework):
self.stop_node(0)
def test_seed_peers(self):
- self.log.info('Test seed peers, this will take about 2 minutes')
+ self.log.info('Test seed peers')
default_data_dir = self.nodes[0].datadir
# No peers.dat exists and -dnsseed=1
# We expect the node will use DNS Seeds, but Regtest mode has 0 DNS seeds
# So after 60 seconds, the node should fallback to fixed seeds (this is a slow test)
assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
- start = time.time()
+ start = int(time.time())
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
- "0 addresses found from DNS seeds",
- "Adding fixed seeds as 60 seconds have passed and addrman is empty"], timeout=80):
- self.start_node(0, extra_args=['-dnsseed=1'])
- assert time.time() - start >= 60
+ "0 addresses found from DNS seeds"]):
+ self.start_node(0, extra_args=['-dnsseed=1 -mocktime={}'.format(start)])
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Adding fixed seeds as 60 seconds have passed and addrman is empty"]):
+ self.nodes[0].setmocktime(start + 65)
self.stop_node(0)
# No peers.dat exists and -dnsseed=0
@@ -172,7 +173,7 @@ class ConfArgsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
"DNS seeding disabled",
- "Adding fixed seeds as -dnsseed=0, -addnode is not provided and and all -seednode(s) attempted\n"]):
+ "Adding fixed seeds as -dnsseed=0, -addnode is not provided and all -seednode(s) attempted\n"]):
self.start_node(0, extra_args=['-dnsseed=0'])
assert time.time() - start < 60
self.stop_node(0)
@@ -192,14 +193,14 @@ class ConfArgsTest(BitcoinTestFramework):
# No peers.dat exists and -dnsseed=0, but a -addnode is provided
# We expect the node will allow 60 seconds prior to using fixed seeds
assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
- start = time.time()
+ start = int(time.time())
with self.nodes[0].assert_debug_log(expected_msgs=[
"Loaded 0 addresses from peers.dat",
- "DNS seeding disabled",
- "Adding fixed seeds as 60 seconds have passed and addrman is empty"],
- timeout=80):
- self.start_node(0, extra_args=['-dnsseed=0', '-addnode=fakenodeaddr'])
- assert time.time() - start >= 60
+ "DNS seeding disabled"]):
+ self.start_node(0, extra_args=['-dnsseed=0', '-addnode=fakenodeaddr -mocktime={}'.format(start)])
+ with self.nodes[0].assert_debug_log(expected_msgs=[
+ "Adding fixed seeds as 60 seconds have passed and addrman is empty"]):
+ self.nodes[0].setmocktime(start + 65)
self.stop_node(0)
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index cd5eff9184..2983feaa0d 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -44,8 +44,8 @@ from test_framework.netutil import test_ipv6_local
RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports
-# Networks returned by RPC getpeerinfo, defined in src/netbase.cpp::GetNetworkName()
-NET_UNROUTABLE = "unroutable"
+# Networks returned by RPC getpeerinfo.
+NET_UNROUTABLE = "not_publicly_routable"
NET_IPV4 = "ipv4"
NET_IPV6 = "ipv6"
NET_ONION = "onion"
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index e9f61be4d4..d0967a9340 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -27,28 +27,31 @@ def hash256_reversed(byte_str):
class ZMQSubscriber:
def __init__(self, socket, topic):
- self.sequence = 0
+ self.sequence = None # no sequence number received yet
self.socket = socket
self.topic = topic
self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
- def receive(self):
+ # Receive message from publisher and verify that topic and sequence match
+ def _receive_from_publisher_and_check(self):
topic, body, seq = self.socket.recv_multipart()
# Topic should match the subscriber topic.
assert_equal(topic, self.topic)
# Sequence should be incremental.
- assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ received_seq = struct.unpack('<I', seq)[-1]
+ if self.sequence is None:
+ self.sequence = received_seq
+ else:
+ assert_equal(received_seq, self.sequence)
self.sequence += 1
return body
+ def receive(self):
+ return self._receive_from_publisher_and_check()
+
def receive_sequence(self):
- topic, body, seq = self.socket.recv_multipart()
- # Topic should match the subscriber topic.
- assert_equal(topic, self.topic)
- # Sequence should be incremental.
- assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
- self.sequence += 1
+ body = self._receive_from_publisher_and_check()
hash = body[:32].hex()
label = chr(body[32])
mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
@@ -64,6 +67,9 @@ class ZMQTest (BitcoinTestFramework):
self.num_nodes = 2
if self.is_wallet_compiled():
self.requires_wallet = True
+ # This test isn't testing txn relay/timing, so set whitelist on the
+ # peers for instant txn relay. This speeds up the test run time 2-3x.
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_py3_zmq()
@@ -84,23 +90,46 @@ class ZMQTest (BitcoinTestFramework):
# Restart node with the specified zmq notifications enabled, subscribe to
# all of them and return the corresponding ZMQSubscriber objects.
- def setup_zmq_test(self, services, recv_timeout=60, connect_nodes=False):
+ def setup_zmq_test(self, services, *, recv_timeout=60, sync_blocks=True):
subscribers = []
for topic, address in services:
socket = self.ctx.socket(zmq.SUB)
- socket.set(zmq.RCVTIMEO, recv_timeout*1000)
subscribers.append(ZMQSubscriber(socket, topic.encode()))
- self.restart_node(0, ["-zmqpub%s=%s" % (topic, address) for topic, address in services])
-
- if connect_nodes:
- self.connect_nodes(0, 1)
+ self.restart_node(0, ["-zmqpub%s=%s" % (topic, address) for topic, address in services] +
+ self.extra_args[0])
for i, sub in enumerate(subscribers):
sub.socket.connect(services[i][1])
- # Relax so that the subscribers are ready before publishing zmq messages
- sleep(0.2)
+ # Ensure that all zmq publisher notification interfaces are ready by
+ # running the following "sync up" procedure:
+ # 1. Generate a block on the node
+ # 2. Try to receive a notification on all subscribers
+ # 3. If all subscribers get a message within the timeout (1 second),
+ # we are done, otherwise repeat starting from step 1
+ for sub in subscribers:
+ sub.socket.set(zmq.RCVTIMEO, 1000)
+ while True:
+ self.nodes[0].generate(1)
+ recv_failed = False
+ for sub in subscribers:
+ try:
+ sub.receive()
+ except zmq.error.Again:
+ self.log.debug("Didn't receive sync-up notification, trying again.")
+ recv_failed = True
+ if not recv_failed:
+ self.log.debug("ZMQ sync-up completed, all subscribers are ready.")
+ break
+
+ # set subscriber's desired timeout for the test
+ for sub in subscribers:
+ sub.socket.set(zmq.RCVTIMEO, recv_timeout*1000)
+
+ self.connect_nodes(0, 1)
+ if sync_blocks:
+ self.sync_blocks()
return subscribers
@@ -110,9 +139,7 @@ class ZMQTest (BitcoinTestFramework):
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
address = 'tcp://127.0.0.1:28332'
- subs = self.setup_zmq_test(
- [(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]],
- connect_nodes=True)
+ subs = self.setup_zmq_test([(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]])
hashblock = subs[0]
hashtx = subs[1]
@@ -189,6 +216,7 @@ class ZMQTest (BitcoinTestFramework):
hashblock, hashtx = self.setup_zmq_test(
[(topic, address) for topic in ["hashblock", "hashtx"]],
recv_timeout=2) # 2 second timeout to check end of notifications
+ self.disconnect_nodes(0, 1)
# Generate 1 block in nodes[0] with 1 mempool tx and receive all notifications
payment_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
@@ -237,6 +265,7 @@ class ZMQTest (BitcoinTestFramework):
"""
self.log.info("Testing 'sequence' publisher")
[seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")])
+ self.disconnect_nodes(0, 1)
# Mempool sequence number starts at 1
seq_num = 1
@@ -387,7 +416,7 @@ class ZMQTest (BitcoinTestFramework):
return
self.log.info("Testing 'mempool sync' usage of sequence notifier")
- [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")], connect_nodes=True)
+ [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")])
# In-memory counter, should always start at 1
next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"]
@@ -487,10 +516,13 @@ class ZMQTest (BitcoinTestFramework):
def test_multiple_interfaces(self):
# Set up two subscribers with different addresses
+ # (note that after the reorg test, syncing would fail due to different
+ # chain lengths on node0 and node1; for this test we only need node0, so
+ # we can disable syncing blocks on the setup)
subscribers = self.setup_zmq_test([
("hashblock", "tcp://127.0.0.1:28334"),
("hashblock", "tcp://127.0.0.1:28335"),
- ])
+ ], sync_blocks=False)
# Generate 1 block in nodes[0] and receive all notifications
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index cf46616681..2d41963beb 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -104,6 +104,9 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[1][0]['connection_type'], 'manual')
assert_equal(peer_info[1][1]['connection_type'], 'inbound')
+ # Check dynamically generated networks list in getpeerinfo help output.
+ assert "(ipv4, ipv6, onion, not_publicly_routable)" in self.nodes[0].help("getpeerinfo")
+
def test_getnettotals(self):
self.log.info("Test getnettotals")
# Test getnettotals and getpeerinfo by doing a ping. The bytes
@@ -152,6 +155,9 @@ class NetTest(BitcoinTestFramework):
for info in network_info:
assert_net_servicesnames(int(info["localservices"], 0x10), info["localservicesnames"])
+ # Check dynamically generated networks list in getnetworkinfo help output.
+ assert "(ipv4, ipv6, onion)" in self.nodes[0].help("getnetworkinfo")
+
def test_getaddednodeinfo(self):
self.log.info("Test getaddednodeinfo")
assert_equal(self.nodes[0].getaddednodeinfo(), [])
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index e0cbab45ce..26526e35fa 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -20,10 +20,6 @@ def TaggedHash(tag, data):
ss += data
return hashlib.sha256(ss).digest()
-def xor_bytes(b0, b1):
- assert len(b0) == len(b1)
- return bytes(x ^ y for (x, y) in zip(b0, b1))
-
def jacobi_symbol(n, k):
"""Compute the Jacobi symbol of n modulo k
@@ -510,7 +506,7 @@ class TestFrameworkKey(unittest.TestCase):
if pubkey is not None:
keys[privkey] = pubkey
for msg in byte_arrays: # test every combination of message, signing key, verification key
- for sign_privkey, sign_pubkey in keys.items():
+ for sign_privkey, _ in keys.items():
sig = sign_schnorr(sign_privkey, msg)
for verify_privkey, verify_pubkey in keys.items():
if verify_privkey == sign_privkey:
diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh
new file mode 100755
index 0000000000..c3b6ff3c98
--- /dev/null
+++ b/test/lint/lint-python-dead-code.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+# Find dead Python code.
+
+export LC_ALL=C
+
+if ! command -v vulture > /dev/null; then
+ echo "Skipping Python dead code linting since vulture is not installed. Install by running \"pip3 install vulture\""
+ exit 0
+fi
+
+# --min-confidence 100 will only report code that is guaranteed to be unused within the analyzed files.
+# Any value below 100 introduces the risk of false positives, which would create an unacceptable maintenance burden.
+if ! vulture \
+ --min-confidence 100 \
+ $(git ls-files -- "*.py"); then
+ echo "Python dead code detection found some issues"
+ exit 1
+fi