mirror of https://github.com/bitcoin/bitcoin.git
Compare commits
30 Commits
51d4ef4ea1
...
eb74e70f86
Author | SHA1 | Date |
---|---|---|
|
eb74e70f86 | |
|
919e6d01e9 | |
|
452ea59281 | |
|
a33bd767a3 | |
|
2578da69f4 | |
|
25dbe4bc86 | |
|
cfb0d74698 | |
|
86eaa4d6cd | |
|
007900ee9b | |
|
8e47ed6906 | |
|
1ed00a0d39 | |
|
c76de2eea1 | |
|
75353a0163 | |
|
87e7f37918 | |
|
2a4450ccbb | |
|
4268abae1a | |
|
1aaaaa078b | |
|
fadad7a494 | |
|
14ae71f323 | |
|
99bc552980 | |
|
576dd97cb9 | |
|
0f7d4ee4e8 | |
|
06df14ba75 | |
|
26e71c237d | |
|
bbe8e9063c | |
|
3a4d1a25cf | |
|
dda5228e02 | |
|
7b5261f7ef | |
|
94db966a3b | |
|
eca50854e1 |
|
@ -105,7 +105,7 @@ jobs:
|
|||
name: ${{ matrix.job-name }}
|
||||
# Use any image to support the xcode-select below, but hardcode version to avoid silent upgrades (and breaks).
|
||||
# See: https://github.com/actions/runner-images#available-images.
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
|
||||
# When a contributor maintains a fork of the repo, any pull request they make
|
||||
# to their own fork, or to the main repository, will trigger two CI runs:
|
||||
|
@ -123,10 +123,10 @@ jobs:
|
|||
include:
|
||||
- job-type: standard
|
||||
file-env: './ci/test/00_setup_env_mac_native.sh'
|
||||
job-name: 'macOS 14 native, arm64, no depends, sqlite only, gui'
|
||||
job-name: 'macOS native, no depends, sqlite only, gui'
|
||||
- job-type: fuzz
|
||||
file-env: './ci/test/00_setup_env_mac_native_fuzz.sh'
|
||||
job-name: 'macOS 14 native, arm64, fuzz'
|
||||
job-name: 'macOS native, fuzz'
|
||||
|
||||
env:
|
||||
DANGER_RUN_CI_ON_HOST: 1
|
||||
|
@ -145,8 +145,8 @@ jobs:
|
|||
# Use the earliest Xcode supported by the version of macOS denoted in
|
||||
# doc/release-notes-empty-template.md and providing at least the
|
||||
# minimum clang version denoted in doc/dependencies.md.
|
||||
# See: https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes
|
||||
sudo xcode-select --switch /Applications/Xcode_15.0.app
|
||||
# See: https://developer.apple.com/documentation/xcode-release-notes/xcode-16-release-notes
|
||||
sudo xcode-select --switch /Applications/Xcode_16.0.app
|
||||
clang --version
|
||||
|
||||
- name: Install Homebrew packages
|
||||
|
|
|
@ -112,7 +112,6 @@ ELF_ALLOWED_LIBRARIES = {
|
|||
'libfontconfig.so.1', # font support
|
||||
'libfreetype.so.6', # font parsing
|
||||
'libdl.so.2', # programming interface to dynamic linker
|
||||
'libxcb-cursor.so.0',
|
||||
'libxcb-icccm.so.4',
|
||||
'libxcb-image.so.0',
|
||||
'libxcb-shm.so.0',
|
||||
|
@ -249,7 +248,7 @@ def check_MACHO_libraries(binary) -> bool:
|
|||
return ok
|
||||
|
||||
def check_MACHO_min_os(binary) -> bool:
|
||||
if binary.build_version.minos == [13,0,0]:
|
||||
if binary.build_version.minos == [14,0,0]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
@ -460,18 +460,18 @@ if config.translations_dir:
|
|||
sys.stderr.write(f"Error: Could not find translation dir \"{config.translations_dir[0]}\"\n")
|
||||
sys.exit(1)
|
||||
|
||||
print("+ Adding Qt translations +")
|
||||
print("+ Adding Qt translations +")
|
||||
|
||||
translations = Path(config.translations_dir[0])
|
||||
translations = Path(config.translations_dir[0])
|
||||
|
||||
regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)')
|
||||
regex = re.compile('qt_[a-z]*(.qm|_[A-Z]*.qm)')
|
||||
|
||||
lang_files = [x for x in translations.iterdir() if regex.match(x.name)]
|
||||
lang_files = [x for x in translations.iterdir() if regex.match(x.name)]
|
||||
|
||||
for file in lang_files:
|
||||
if verbose:
|
||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
for file in lang_files:
|
||||
if verbose:
|
||||
print(file.as_posix(), "->", os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
shutil.copy2(file.as_posix(), os.path.join(applicationBundle.resourcesPath, file.name))
|
||||
|
||||
# ------------------------------------------------
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
OSX_MIN_VERSION=13.0
|
||||
OSX_MIN_VERSION=14.0
|
||||
OSX_SDK_VERSION=14.0
|
||||
XCODE_VERSION=15.0
|
||||
XCODE_BUILD_ID=15A240d
|
||||
|
|
|
@ -6,7 +6,7 @@ $(package)_sha256_hash=0e9c5446dc6f3beb8af6ebfcc9e27bcc6da6fe2860f7fc07b99144dfa
|
|||
$(package)_dependencies=libxcb libxcb_util_render libxcb_util_image
|
||||
|
||||
define $(package)_set_vars
|
||||
$(package)_config_opts = --disable-static
|
||||
$(package)_config_opts = --disable-shared
|
||||
$(package)_config_opts += --disable-dependency-tracking --enable-option-checking
|
||||
endef
|
||||
|
||||
|
|
|
@ -81,8 +81,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
|||
|
||||
sudo apt-get install qt6-base-dev qt6-tools-dev qt6-l10n-tools qt6-tools-dev-tools libgl-dev
|
||||
|
||||
For Qt 6.5 and later, the `libxcb-cursor0` package must be installed at runtime.
|
||||
|
||||
Additionally, to support Wayland protocol for modern desktop environments:
|
||||
|
||||
sudo apt install qt6-wayland
|
||||
|
@ -133,8 +131,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
|||
|
||||
sudo dnf install qt6-qtbase-devel qt6-qttools-devel
|
||||
|
||||
For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime.
|
||||
|
||||
Additionally, to support Wayland protocol for modern desktop environments:
|
||||
|
||||
sudo dnf install qt6-qtwayland
|
||||
|
@ -182,8 +178,6 @@ the necessary parts of Qt, the libqrencode and pass `-DBUILD_GUI=ON`. Skip if yo
|
|||
|
||||
apk add qt6-qtbase-dev qt6-qttools-dev
|
||||
|
||||
For Qt 6.5 and later, the `xcb-util-cursor` package must be installed at runtime.
|
||||
|
||||
The GUI will be able to encode addresses in QR codes unless this feature is explicitly disabled. To install libqrencode, run:
|
||||
|
||||
apk add libqrencode-dev
|
||||
|
|
|
@ -36,7 +36,7 @@ Compatibility
|
|||
==============
|
||||
|
||||
Bitcoin Core is supported and tested on operating systems using the
|
||||
Linux Kernel 3.17+, macOS 13+, and Windows 10+. Bitcoin
|
||||
Linux Kernel 3.17+, macOS 14+, and Windows 10+. Bitcoin
|
||||
Core should also work on most other Unix-like systems but is not as
|
||||
frequently tested on them. It is not recommended to use Bitcoin Core on
|
||||
unsupported systems.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<plist version="0.9">
|
||||
<dict>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13</string>
|
||||
<string>14</string>
|
||||
|
||||
<key>LSArchitecturePriority</key>
|
||||
<array>
|
||||
|
|
120
src/net.cpp
120
src/net.cpp
|
@ -108,7 +108,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
|
|||
|
||||
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
|
||||
//
|
||||
// Global state variables
|
||||
//
|
||||
|
@ -331,42 +331,22 @@ bool IsLocal(const CService& addr)
|
|||
return mapLocalHost.count(addr) > 0;
|
||||
}
|
||||
|
||||
CNode* CConnman::FindNode(const CNetAddr& ip)
|
||||
bool CConnman::AlreadyConnectedToHost(const std::string& host) const
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
for (CNode* pnode : m_nodes) {
|
||||
if (static_cast<CNetAddr>(pnode->addr) == ip) {
|
||||
return pnode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return std::ranges::any_of(m_nodes, [&host](CNode* node) { return node->m_addr_name == host; });
|
||||
}
|
||||
|
||||
CNode* CConnman::FindNode(const std::string& addrName)
|
||||
bool CConnman::AlreadyConnectedToAddressPort(const CService& addr_port) const
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
for (CNode* pnode : m_nodes) {
|
||||
if (pnode->m_addr_name == addrName) {
|
||||
return pnode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return std::ranges::any_of(m_nodes, [&addr_port](CNode* node) { return node->addr == addr_port; });
|
||||
}
|
||||
|
||||
CNode* CConnman::FindNode(const CService& addr)
|
||||
bool CConnman::AlreadyConnectedToAddress(const CNetAddr& addr) const
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
for (CNode* pnode : m_nodes) {
|
||||
if (static_cast<CService>(pnode->addr) == addr) {
|
||||
return pnode;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
|
||||
{
|
||||
return FindNode(static_cast<CNetAddr>(addr));
|
||||
return std::ranges::any_of(m_nodes, [&addr](CNode* node) { return node->addr == addr; });
|
||||
}
|
||||
|
||||
bool CConnman::CheckIncomingNonce(uint64_t nonce)
|
||||
|
@ -393,7 +373,12 @@ static CService GetBindAddress(const Sock& sock)
|
|||
return addr_bind;
|
||||
}
|
||||
|
||||
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport)
|
||||
CNode* CConnman::ConnectNode(CAddress addrConnect,
|
||||
const char* pszDest,
|
||||
bool fCountFailure,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override)
|
||||
{
|
||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||
assert(conn_type != ConnectionType::INBOUND);
|
||||
|
@ -403,10 +388,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
return nullptr;
|
||||
|
||||
// Look for an existing connection
|
||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
||||
if (pnode)
|
||||
{
|
||||
LogPrintf("Failed to open new connection, already connected\n");
|
||||
if (AlreadyConnectedToAddressPort(addrConnect)) {
|
||||
LogInfo("Failed to open new connection to %s, already connected", addrConnect.ToStringAddrPort());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -436,9 +419,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
}
|
||||
// It is possible that we already have a connection to the IP/port pszDest resolved to.
|
||||
// In that case, drop the connection that was just created.
|
||||
LOCK(m_nodes_mutex);
|
||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
||||
if (pnode) {
|
||||
if (AlreadyConnectedToAddressPort(addrConnect)) {
|
||||
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -463,7 +444,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
|
||||
for (auto& target_addr: connect_to) {
|
||||
if (target_addr.IsValid()) {
|
||||
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
||||
bool use_proxy;
|
||||
if (proxy_override.has_value()) {
|
||||
use_proxy = true;
|
||||
proxy = proxy_override.value();
|
||||
} else {
|
||||
use_proxy = GetProxy(target_addr.GetNetwork(), proxy);
|
||||
}
|
||||
bool proxyConnectionFailed = false;
|
||||
|
||||
if (target_addr.IsI2P() && use_proxy) {
|
||||
|
@ -530,6 +517,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
if (!addr_bind.IsValid()) {
|
||||
addr_bind = GetBindAddress(*sock);
|
||||
}
|
||||
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
||||
.Write(target_addr.GetNetClass())
|
||||
.Write(addr_bind.GetAddrBytes())
|
||||
// For outbound connections, the port of the bound address is randomly
|
||||
// assigned by the OS and would therefore not be useful for seeding.
|
||||
.Write(0)
|
||||
.Finalize();
|
||||
CNode* pnode = new CNode(id,
|
||||
std::move(sock),
|
||||
target_addr,
|
||||
|
@ -539,6 +533,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||
pszDest ? pszDest : "",
|
||||
conn_type,
|
||||
/*inbound_onion=*/false,
|
||||
network_id,
|
||||
CNodeOptions{
|
||||
.permission_flags = permission_flags,
|
||||
.i2p_sam_session = std::move(i2p_transient_session),
|
||||
|
@ -1832,6 +1827,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||
ServiceFlags local_services = GetLocalServices();
|
||||
const bool use_v2transport(local_services & NODE_P2P_V2);
|
||||
|
||||
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
||||
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
|
||||
.Write(addr_bind.GetAddrBytes())
|
||||
.Write(addr_bind.GetPort()) // inbound connections use bind port
|
||||
.Finalize();
|
||||
CNode* pnode = new CNode(id,
|
||||
std::move(sock),
|
||||
CAddress{addr, NODE_NONE},
|
||||
|
@ -1841,6 +1841,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
inbound_onion,
|
||||
network_id,
|
||||
CNodeOptions{
|
||||
.permission_flags = permission_flags,
|
||||
.prefer_evict = discouraged,
|
||||
|
@ -2882,7 +2883,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
|
|||
const bool count_failures{((int)outbound_ipv46_peer_netgroups.size() + outbound_privacy_network_peers) >= std::min(m_max_automatic_connections - 1, 2)};
|
||||
// Use BIP324 transport when both us and them have NODE_V2_P2P set.
|
||||
const bool use_v2transport(addrConnect.nServices & GetLocalServices() & NODE_P2P_V2);
|
||||
OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*strDest=*/nullptr, conn_type, use_v2transport);
|
||||
OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*pszDest=*/nullptr, conn_type, use_v2transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2991,7 +2992,13 @@ void CConnman::ThreadOpenAddedConnections()
|
|||
}
|
||||
|
||||
// if successful, this moves the passed grant to the constructed node
|
||||
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char *pszDest, ConnectionType conn_type, bool use_v2transport)
|
||||
bool CConnman::OpenNetworkConnection(const CAddress& addrConnect,
|
||||
bool fCountFailure,
|
||||
CountingSemaphoreGrant<>&& grant_outbound,
|
||||
const char* pszDest,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override)
|
||||
{
|
||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||
assert(conn_type != ConnectionType::INBOUND);
|
||||
|
@ -3000,23 +3007,24 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
|||
// Initiate outbound network connection
|
||||
//
|
||||
if (m_interrupt_net->interrupted()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!fNetworkActive) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!pszDest) {
|
||||
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
|
||||
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
} else if (FindNode(std::string(pszDest)))
|
||||
return;
|
||||
} else if (AlreadyConnectedToHost(pszDest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport);
|
||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport, proxy_override);
|
||||
|
||||
if (!pnode)
|
||||
return;
|
||||
return false;
|
||||
pnode->grantOutbound = std::move(grant_outbound);
|
||||
|
||||
m_msgproc->InitializeNode(*pnode, m_local_services);
|
||||
|
@ -3034,6 +3042,8 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
|||
pnode->ConnectionTypeAsString().c_str(),
|
||||
pnode->ConnectedThroughNetwork(),
|
||||
GetNodeCount(ConnectionDirection::Out));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Mutex NetEventsInterface::g_msgproc_mutex;
|
||||
|
@ -3529,15 +3539,9 @@ std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t
|
|||
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
||||
{
|
||||
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
||||
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
|
||||
.Write(requestor.ConnectedThroughNetwork())
|
||||
.Write(local_socket_bytes)
|
||||
// For outbound connections, the port of the bound address is randomly
|
||||
// assigned by the OS and would therefore not be useful for seeding.
|
||||
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
|
||||
.Finalize();
|
||||
uint64_t network_id = requestor.m_network_key;
|
||||
const auto current_time = GetTime<std::chrono::microseconds>();
|
||||
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
|
||||
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
|
||||
CachedAddrResponse& cache_entry = r.first->second;
|
||||
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
||||
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
|
||||
|
@ -3651,9 +3655,11 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
|
|||
bool CConnman::DisconnectNode(const std::string& strNode)
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
if (CNode* pnode = FindNode(strNode)) {
|
||||
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->DisconnectMsg(fLogIPs));
|
||||
pnode->fDisconnect = true;
|
||||
auto it = std::ranges::find_if(m_nodes, [&strNode](CNode* node) { return node->m_addr_name == strNode; });
|
||||
if (it != m_nodes.end()) {
|
||||
CNode* node{*it};
|
||||
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), node->DisconnectMsg(fLogIPs));
|
||||
node->fDisconnect = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -3814,6 +3820,7 @@ CNode::CNode(NodeId idIn,
|
|||
const std::string& addrNameIn,
|
||||
ConnectionType conn_type_in,
|
||||
bool inbound_onion,
|
||||
uint64_t network_key,
|
||||
CNodeOptions&& node_opts)
|
||||
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
||||
m_permission_flags{node_opts.permission_flags},
|
||||
|
@ -3826,6 +3833,7 @@ CNode::CNode(NodeId idIn,
|
|||
m_inbound_onion{inbound_onion},
|
||||
m_prefer_evict{node_opts.prefer_evict},
|
||||
nKeyedNetGroup{nKeyedNetGroupIn},
|
||||
m_network_key{network_key},
|
||||
m_conn_type{conn_type_in},
|
||||
id{idIn},
|
||||
nLocalHostNonce{nLocalHostNonceIn},
|
||||
|
|
73
src/net.h
73
src/net.h
|
@ -738,6 +738,10 @@ public:
|
|||
std::atomic_bool fPauseRecv{false};
|
||||
std::atomic_bool fPauseSend{false};
|
||||
|
||||
/** Network key used to prevent fingerprinting our node across networks.
|
||||
* Influenced by the network and the bind address (+ bind port for inbounds) */
|
||||
const uint64_t m_network_key;
|
||||
|
||||
const ConnectionType m_conn_type;
|
||||
|
||||
/** Move all messages from the received queue to the processing queue. */
|
||||
|
@ -889,6 +893,7 @@ public:
|
|||
const std::string& addrNameIn,
|
||||
ConnectionType conn_type_in,
|
||||
bool inbound_onion,
|
||||
uint64_t network_key,
|
||||
CNodeOptions&& node_opts = {});
|
||||
CNode(const CNode&) = delete;
|
||||
CNode& operator=(const CNode&) = delete;
|
||||
|
@ -1143,7 +1148,28 @@ public:
|
|||
bool GetNetworkActive() const { return fNetworkActive; };
|
||||
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
||||
void SetNetworkActive(bool active);
|
||||
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char* strDest, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
/**
|
||||
* Open a new P2P connection and initialize it with the PeerManager at `m_msgproc`.
|
||||
* @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
|
||||
* @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
|
||||
* @param[in] grant_outbound Take ownership of this grant, to be released later when the connection is closed.
|
||||
* @param[in] pszDest Address to resolve and connect to.
|
||||
* @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
|
||||
* @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
|
||||
* @param[in] proxy_override Optional proxy to use and override normal proxy selection.
|
||||
* @retval true The connection was opened successfully.
|
||||
* @retval false The connection attempt failed.
|
||||
*/
|
||||
bool OpenNetworkConnection(const CAddress& addrConnect,
|
||||
bool fCountFailure,
|
||||
CountingSemaphoreGrant<>&& grant_outbound,
|
||||
const char* pszDest,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override = std::nullopt)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
bool CheckIncomingNonce(uint64_t nonce);
|
||||
void ASMapHealthCheck();
|
||||
|
||||
|
@ -1370,18 +1396,49 @@ private:
|
|||
|
||||
uint64_t CalculateKeyedNetGroup(const CNetAddr& ad) const;
|
||||
|
||||
CNode* FindNode(const CNetAddr& ip);
|
||||
CNode* FindNode(const std::string& addrName);
|
||||
CNode* FindNode(const CService& addr);
|
||||
/**
|
||||
* Determine whether we're already connected to a given "host:port".
|
||||
* Note that for inbound connections, the peer is likely using a random outbound
|
||||
* port on their side, so this will likely not match any inbound connections.
|
||||
* @param[in] host String of the form "host[:port]", e.g. "localhost" or "localhost:8333" or "1.2.3.4:8333".
|
||||
* @return true if connected to `host`.
|
||||
*/
|
||||
bool AlreadyConnectedToHost(const std::string& host) const;
|
||||
|
||||
/**
|
||||
* Determine whether we're already connected to a given address, in order to
|
||||
* avoid initiating duplicate connections.
|
||||
* Determine whether we're already connected to a given address:port.
|
||||
* Note that for inbound connections, the peer is likely using a random outbound
|
||||
* port on their side, so this will likely not match any inbound connections.
|
||||
* @param[in] addr_port Address and port to check.
|
||||
* @return true if connected to addr_port.
|
||||
*/
|
||||
bool AlreadyConnectedToAddress(const CAddress& addr);
|
||||
bool AlreadyConnectedToAddressPort(const CService& addr_port) const;
|
||||
|
||||
/**
|
||||
* Determine whether we're already connected to a given address.
|
||||
*/
|
||||
bool AlreadyConnectedToAddress(const CNetAddr& addr) const;
|
||||
|
||||
bool AttemptToEvictConnection();
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
/**
|
||||
* Open a new P2P connection.
|
||||
* @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
|
||||
* @param[in] pszDest Address to resolve and connect to.
|
||||
* @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
|
||||
* @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
|
||||
* @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
|
||||
* @param[in] proxy_override Optional proxy to use and override normal proxy selection.
|
||||
* @return Newly created CNode object or nullptr if the connection failed.
|
||||
*/
|
||||
CNode* ConnectNode(CAddress addrConnect,
|
||||
const char* pszDest,
|
||||
bool fCountFailure,
|
||||
ConnectionType conn_type,
|
||||
bool use_v2transport,
|
||||
const std::optional<Proxy>& proxy_override)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
|
||||
|
||||
void DeleteNode(CNode* pnode);
|
||||
|
|
|
@ -807,7 +807,7 @@ private:
|
|||
|
||||
uint32_t GetFetchFlags(const Peer& peer) const;
|
||||
|
||||
std::atomic<std::chrono::microseconds> m_next_inv_to_inbounds{0us};
|
||||
std::map<uint64_t, std::chrono::microseconds> m_next_inv_to_inbounds_per_network_key GUARDED_BY(g_msgproc_mutex);
|
||||
|
||||
/** Number of nodes with fSyncStarted. */
|
||||
int nSyncStarted GUARDED_BY(cs_main) = 0;
|
||||
|
@ -837,12 +837,14 @@ private:
|
|||
|
||||
/**
|
||||
* For sending `inv`s to inbound peers, we use a single (exponentially
|
||||
* distributed) timer for all peers. If we used a separate timer for each
|
||||
* distributed) timer for all peers with the same network key. If we used a separate timer for each
|
||||
* peer, a spy node could make multiple inbound connections to us to
|
||||
* accurately determine when we received the transaction (and potentially
|
||||
* determine the transaction's origin). */
|
||||
* accurately determine when we received a transaction (and potentially
|
||||
* determine the transaction's origin). Each network key has its own timer
|
||||
* to make fingerprinting harder. */
|
||||
std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
|
||||
std::chrono::seconds average_interval) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||
std::chrono::seconds average_interval,
|
||||
uint64_t network_key) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||
|
||||
|
||||
// All of the following cache a recent block, and are protected by m_most_recent_block_mutex
|
||||
|
@ -1143,15 +1145,15 @@ static bool CanServeWitnesses(const Peer& peer)
|
|||
}
|
||||
|
||||
std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
|
||||
std::chrono::seconds average_interval)
|
||||
std::chrono::seconds average_interval,
|
||||
uint64_t network_key)
|
||||
{
|
||||
if (m_next_inv_to_inbounds.load() < now) {
|
||||
// If this function were called from multiple threads simultaneously
|
||||
// it would possible that both update the next send variable, and return a different result to their caller.
|
||||
// This is not possible in practice as only the net processing thread invokes this function.
|
||||
m_next_inv_to_inbounds = now + m_rng.rand_exp_duration(average_interval);
|
||||
auto [it, inserted] = m_next_inv_to_inbounds_per_network_key.try_emplace(network_key, 0us);
|
||||
auto& timer{it->second};
|
||||
if (timer < now) {
|
||||
timer = now + m_rng.rand_exp_duration(average_interval);
|
||||
}
|
||||
return m_next_inv_to_inbounds;
|
||||
return timer;
|
||||
}
|
||||
|
||||
bool PeerManagerImpl::IsBlockRequested(const uint256& hash)
|
||||
|
@ -5715,7 +5717,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|||
if (tx_relay->m_next_inv_send_time < current_time) {
|
||||
fSendTrickle = true;
|
||||
if (pto->IsInboundConn()) {
|
||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL, pto->m_network_key);
|
||||
} else {
|
||||
tx_relay->m_next_inv_send_time = current_time + m_rng.rand_exp_duration(OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ static RPCHelpMan getpeerinfo()
|
|||
{
|
||||
{
|
||||
{RPCResult::Type::NUM, "id", "Peer index"},
|
||||
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
|
||||
{RPCResult::Type::STR, "addr", "(host:port) The IP address/hostname optionally followed by :port of the peer"},
|
||||
{RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"},
|
||||
{RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"},
|
||||
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/*append_unroutable=*/true), ", ") + ")"},
|
||||
|
@ -322,7 +322,7 @@ static RPCHelpMan addnode()
|
|||
strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
|
||||
" and are counted separately from the -maxconnections limit.\n",
|
||||
{
|
||||
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the peer to connect to"},
|
||||
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address/hostname optionally followed by :port of the peer to connect to"},
|
||||
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
|
||||
{"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol (ignored for 'remove' command)"},
|
||||
},
|
||||
|
|
|
@ -459,10 +459,16 @@ BOOST_AUTO_TEST_CASE(getaddr_unfiltered)
|
|||
addrman->Attempt(addr3, /*fCountFailure=*/true, /*time=*/Now<NodeSeconds>() - 61s);
|
||||
}
|
||||
|
||||
// Set time more than 10 minutes in the future (flying DeLorean), so this
|
||||
// addr should be isTerrible = true
|
||||
CAddress addr4 = CAddress(ResolveService("250.252.2.4", 9997), NODE_NONE);
|
||||
addr4.nTime = Now<NodeSeconds>() + 11min;
|
||||
BOOST_CHECK(addrman->Add({addr4}, source));
|
||||
|
||||
// GetAddr filtered by quality (i.e. not IsTerrible) should only return addr1
|
||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt).size(), 1U);
|
||||
// Unfiltered GetAddr should return all addrs
|
||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 3U);
|
||||
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false).size(), 4U);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
|
||||
|
|
|
@ -62,7 +62,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0};
|
||||
|
||||
connman.Handshake(
|
||||
/*node=*/dummyNode1,
|
||||
|
@ -128,7 +129,8 @@ void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager&
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
connType,
|
||||
/*inbound_onion=*/false});
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0});
|
||||
CNode &node = *vNodes.back();
|
||||
node.SetCommonVersion(PROTOCOL_VERSION);
|
||||
|
||||
|
@ -327,7 +329,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
nodes[0]->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(*nodes[0], NODE_NETWORK);
|
||||
nodes[0]->fSuccessfullyConnected = true;
|
||||
|
@ -347,7 +350,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
nodes[1]->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(*nodes[1], NODE_NETWORK);
|
||||
nodes[1]->fSuccessfullyConnected = true;
|
||||
|
@ -377,7 +381,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/2};
|
||||
nodes[2]->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(*nodes[2], NODE_NETWORK);
|
||||
nodes[2]->fSuccessfullyConnected = true;
|
||||
|
@ -419,7 +424,8 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
|
|||
CAddress(),
|
||||
/*addrNameIn=*/"",
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerLogic->InitializeNode(dummyNode, NODE_NETWORK);
|
||||
dummyNode.fSuccessfullyConnected = true;
|
||||
|
|
|
@ -177,7 +177,7 @@ FUZZ_TARGET(connman, .init = initialize_connman)
|
|||
/*addrConnect=*/random_address,
|
||||
/*fCountFailure=*/fuzzed_data_provider.ConsumeBool(),
|
||||
/*grant_outbound=*/{},
|
||||
/*strDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
|
||||
/*pszDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
|
||||
/*conn_type=*/conn_type,
|
||||
/*use_v2transport=*/fuzzed_data_provider.ConsumeBool());
|
||||
},
|
||||
|
|
|
@ -75,7 +75,7 @@ auto& FuzzTargets()
|
|||
|
||||
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
|
||||
{
|
||||
const auto [it, ins]{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped after Apple-Clang-16 ? */ {std::move(target), std::move(opts)})};
|
||||
const auto [it, ins]{FuzzTargets().try_emplace(name, std::move(target), std::move(opts))};
|
||||
Assert(ins);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ void HeadersSyncSetup::ResetAndInitialize()
|
|||
|
||||
for (auto conn_type : conn_types) {
|
||||
CAddress addr{};
|
||||
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false));
|
||||
m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false, 0));
|
||||
CNode& p2p_node = *m_connections.back();
|
||||
|
||||
connman.Handshake(
|
||||
|
|
|
@ -325,7 +325,7 @@ FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
|
|||
return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
|
||||
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
|
||||
/*bypass_limits=*/fuzzed_data_provider.ConsumeBool(), /*test_accept=*/!single_submit));
|
||||
/*bypass_limits=*/false, /*test_accept=*/!single_submit));
|
||||
|
||||
if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
|
||||
// We don't know anything about the validity since transactions were randomly generated, so
|
||||
|
|
|
@ -296,7 +296,6 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
|
|||
std::set<CTransactionRef> added;
|
||||
auto txr = std::make_shared<TransactionsDelta>(removed, added);
|
||||
node.validation_signals->RegisterSharedValidationInterface(txr);
|
||||
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
|
||||
|
||||
// Make sure ProcessNewPackage on one transaction works.
|
||||
// The result is not guaranteed to be the same as what is returned by ATMP.
|
||||
|
@ -311,7 +310,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
|
|||
it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID);
|
||||
}
|
||||
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), /*bypass_limits=*/false, /*test_accept=*/false));
|
||||
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||
node.validation_signals->SyncWithValidationInterfaceQueue();
|
||||
node.validation_signals->UnregisterSharedValidationInterface(txr);
|
||||
|
@ -394,6 +393,9 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
|
|||
|
||||
chainstate.SetMempool(&tx_pool);
|
||||
|
||||
// If we ever bypass limits, do not do TRUC invariants checks
|
||||
bool ever_bypassed_limits{false};
|
||||
|
||||
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
|
||||
{
|
||||
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
|
||||
|
@ -412,13 +414,17 @@ FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
|
|||
tx_pool.PrioritiseTransaction(txid, delta);
|
||||
}
|
||||
|
||||
const bool bypass_limits{fuzzed_data_provider.ConsumeBool()};
|
||||
ever_bypassed_limits |= bypass_limits;
|
||||
|
||||
const auto tx = MakeTransactionRef(mut_tx);
|
||||
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
|
||||
const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false));
|
||||
const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
|
||||
if (accepted) {
|
||||
txids.push_back(tx->GetHash());
|
||||
CheckMempoolTRUCInvariants(tx_pool);
|
||||
if (!ever_bypassed_limits) {
|
||||
CheckMempoolTRUCInvariants(tx_pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
Finish(fuzzed_data_provider, tx_pool, chainstate);
|
||||
|
|
|
@ -275,6 +275,8 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
|
|||
const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
|
||||
const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray(ALL_CONNECTION_TYPES);
|
||||
const bool inbound_onion{conn_type == ConnectionType::INBOUND ? fuzzed_data_provider.ConsumeBool() : false};
|
||||
const uint64_t network_id = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
|
||||
|
||||
NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
|
||||
if constexpr (ReturnUniquePtr) {
|
||||
return std::make_unique<CNode>(node_id,
|
||||
|
@ -286,6 +288,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
|
|||
addr_name,
|
||||
conn_type,
|
||||
inbound_onion,
|
||||
network_id,
|
||||
CNodeOptions{ .permission_flags = permission_flags });
|
||||
} else {
|
||||
return CNode{node_id,
|
||||
|
@ -297,6 +300,7 @@ auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional<N
|
|||
addr_name,
|
||||
conn_type,
|
||||
inbound_onion,
|
||||
network_id,
|
||||
CNodeOptions{ .permission_flags = permission_flags }};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,8 @@ void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, Connm
|
|||
CAddress{},
|
||||
/*addrNameIn=*/"",
|
||||
conn_type,
|
||||
/*inbound_onion=*/inbound_onion});
|
||||
/*inbound_onion=*/inbound_onion,
|
||||
/*network_key=*/0});
|
||||
CNode& node = *nodes.back();
|
||||
node.SetCommonVersion(PROTOCOL_VERSION);
|
||||
|
||||
|
@ -151,15 +152,8 @@ BOOST_FIXTURE_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection,
|
|||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected");
|
||||
for (auto node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr));
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("\nCheck that peers with the same addresses as connected peers but different ports are detected as connected.");
|
||||
for (auto node : connman->TestNodes()) {
|
||||
uint16_t changed_port = node->addr.GetPort() + 1;
|
||||
CService address_with_changed_port{node->addr, changed_port};
|
||||
BOOST_CHECK(connman->AlreadyConnectedPublic(CAddress{address_with_changed_port, NODE_NONE}));
|
||||
for (const auto& node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AlreadyConnectedToAddressPublic(node->addr));
|
||||
}
|
||||
|
||||
// Clean up
|
||||
|
|
|
@ -67,7 +67,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0);
|
||||
BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
|
||||
BOOST_CHECK(pnode1->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
|
||||
|
@ -85,7 +86,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1);
|
||||
BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
|
||||
BOOST_CHECK(pnode2->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
|
||||
|
@ -103,7 +105,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/2);
|
||||
BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
|
||||
BOOST_CHECK(pnode3->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
|
||||
|
@ -121,7 +124,8 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
|
|||
CAddress(),
|
||||
pszDest,
|
||||
ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/true);
|
||||
/*inbound_onion=*/true,
|
||||
/*network_key=*/3);
|
||||
BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
|
||||
BOOST_CHECK(pnode4->IsManualConn() == false);
|
||||
BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
|
||||
|
@ -613,7 +617,8 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
|
|||
CAddress{},
|
||||
/*pszDest=*/std::string{},
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0);
|
||||
pnode->fSuccessfullyConnected.store(true);
|
||||
|
||||
// the peer claims to be reaching us via IPv6
|
||||
|
@ -667,7 +672,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
|
|||
/*addrBindIn=*/CService{},
|
||||
/*addrNameIn=*/std::string{},
|
||||
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0};
|
||||
peer_out.fSuccessfullyConnected = true;
|
||||
peer_out.SetAddrLocal(peer_us);
|
||||
|
||||
|
@ -688,7 +694,8 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
|
|||
/*addrBindIn=*/CService{},
|
||||
/*addrNameIn=*/std::string{},
|
||||
/*conn_type_in=*/ConnectionType::INBOUND,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/1};
|
||||
peer_in.fSuccessfullyConnected = true;
|
||||
peer_in.SetAddrLocal(peer_us);
|
||||
|
||||
|
@ -825,7 +832,8 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message)
|
|||
/*addrBindIn=*/CService{},
|
||||
/*addrNameIn=*/std::string{},
|
||||
/*conn_type_in=*/ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false};
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/2};
|
||||
|
||||
const uint64_t services{NODE_NETWORK | NODE_WITNESS};
|
||||
const int64_t time{0};
|
||||
|
@ -900,7 +908,8 @@ BOOST_AUTO_TEST_CASE(advertise_local_address)
|
|||
CAddress{},
|
||||
/*pszDest=*/std::string{},
|
||||
ConnectionType::OUTBOUND_FULL_RELAY,
|
||||
/*inbound_onion=*/false);
|
||||
/*inbound_onion=*/false,
|
||||
/*network_key=*/0);
|
||||
};
|
||||
g_reachable_nets.Add(NET_CJDNS);
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) co
|
|||
|
||||
CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
|
||||
{
|
||||
CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true);
|
||||
CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true, /*proxy_override=*/std::nullopt);
|
||||
if (!node) return nullptr;
|
||||
node->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK | NODE_WITNESS));
|
||||
|
|
|
@ -107,7 +107,7 @@ struct ConnmanTestMsg : public CConnman {
|
|||
bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const;
|
||||
void FlushSendBuffer(CNode& node) const;
|
||||
|
||||
bool AlreadyConnectedPublic(const CAddress& addr) { return AlreadyConnectedToAddress(addr); };
|
||||
bool AlreadyConnectedToAddressPublic(const CNetAddr& addr) { return AlreadyConnectedToAddress(addr); };
|
||||
|
||||
CNode* ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
|
|
|
@ -1042,26 +1042,28 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
// Even though just checking direct mempool parents for inheritance would be sufficient, we
|
||||
// check using the full ancestor set here because it's more convenient to use what we have
|
||||
// already calculated.
|
||||
if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
|
||||
// Single transaction contexts only.
|
||||
if (args.m_allow_sibling_eviction && err->second != nullptr) {
|
||||
// We should only be considering where replacement is considered valid as well.
|
||||
Assume(args.m_allow_replacement);
|
||||
if (!args.m_bypass_limits) {
|
||||
if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
|
||||
// Single transaction contexts only.
|
||||
if (args.m_allow_sibling_eviction && err->second != nullptr) {
|
||||
// We should only be considering where replacement is considered valid as well.
|
||||
Assume(args.m_allow_replacement);
|
||||
|
||||
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
|
||||
// included in RBF checks.
|
||||
ws.m_conflicts.insert(err->second->GetHash());
|
||||
// Adding the sibling to m_iters_conflicting here means that it doesn't count towards
|
||||
// RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
|
||||
// the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
|
||||
ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value());
|
||||
ws.m_sibling_eviction = true;
|
||||
// The sibling will be treated as part of the to-be-replaced set in ReplacementChecks.
|
||||
// Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC
|
||||
// (which is normally done in PreChecks). However, the only way a TRUC transaction can
|
||||
// have a non-TRUC and non-BIP125 descendant is due to a reorg.
|
||||
} else {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
|
||||
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
|
||||
// included in RBF checks.
|
||||
ws.m_conflicts.insert(err->second->GetHash());
|
||||
// Adding the sibling to m_iters_conflicting here means that it doesn't count towards
|
||||
// RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
|
||||
// the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
|
||||
ws.m_iters_conflicting.insert(m_pool.GetIter(err->second->GetHash()).value());
|
||||
ws.m_sibling_eviction = true;
|
||||
// The sibling will be treated as part of the to-be-replaced set in ReplacementChecks.
|
||||
// Note that we are not checking whether it opts in to replaceability via BIP125 or TRUC
|
||||
// (which is normally done in PreChecks). However, the only way a TRUC transaction can
|
||||
// have a non-TRUC and non-BIP125 descendant is due to a reorg.
|
||||
} else {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,23 +165,36 @@ class MempoolTRUC(BitcoinTestFramework):
|
|||
def test_truc_reorg(self):
|
||||
node = self.nodes[0]
|
||||
self.log.info("Test that, during a reorg, TRUC rules are not enforced")
|
||||
tx_v2_block = self.wallet.send_self_transfer(from_node=node, version=2)
|
||||
tx_v3_block = self.wallet.send_self_transfer(from_node=node, version=3)
|
||||
tx_v3_block2 = self.wallet.send_self_transfer(from_node=node, version=3)
|
||||
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"]])
|
||||
self.check_mempool([])
|
||||
|
||||
# Testing 2<-3 versions allowed
|
||||
tx_v2_block = self.wallet.create_self_transfer(version=2)
|
||||
|
||||
# Testing 3<-2 versions allowed
|
||||
tx_v3_block = self.wallet.create_self_transfer(version=3)
|
||||
|
||||
# Testing overly-large child size
|
||||
tx_v3_block2 = self.wallet.create_self_transfer(version=3)
|
||||
|
||||
# Also create a linear chain of 3 TRUC transactions that will be directly mined, followed by one v2 in-mempool after block is made
|
||||
tx_chain_1 = self.wallet.create_self_transfer(version=3)
|
||||
tx_chain_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_1["new_utxo"], version=3)
|
||||
tx_chain_3 = self.wallet.create_self_transfer(utxo_to_spend=tx_chain_2["new_utxo"], version=3)
|
||||
|
||||
tx_to_mine = [tx_v3_block["hex"], tx_v2_block["hex"], tx_v3_block2["hex"], tx_chain_1["hex"], tx_chain_2["hex"], tx_chain_3["hex"]]
|
||||
block = self.generateblock(node, output="raw(42)", transactions=tx_to_mine)
|
||||
|
||||
block = self.generate(node, 1)
|
||||
self.check_mempool([])
|
||||
tx_v2_from_v3 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block["new_utxo"], version=2)
|
||||
tx_v3_from_v2 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v2_block["new_utxo"], version=3)
|
||||
tx_v3_child_large = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block2["new_utxo"], target_vsize=1250, version=3)
|
||||
assert_greater_than(node.getmempoolentry(tx_v3_child_large["txid"])["vsize"], TRUC_CHILD_MAX_VSIZE)
|
||||
self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
|
||||
node.invalidateblock(block[0])
|
||||
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
|
||||
# This is needed because generate() will create the exact same block again.
|
||||
node.reconsiderblock(block[0])
|
||||
tx_chain_4 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_chain_3["new_utxo"], version=2)
|
||||
self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_4["txid"]])
|
||||
|
||||
# Reorg should have all block transactions re-accepted, ignoring TRUC enforcement
|
||||
node.invalidateblock(block["hash"])
|
||||
self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"], tx_chain_1["txid"], tx_chain_2["txid"], tx_chain_3["txid"], tx_chain_4["txid"]])
|
||||
|
||||
@cleanup(extra_args=["-limitdescendantsize=10"])
|
||||
def test_nondefault_package_limits(self):
|
||||
|
|
|
@ -15,7 +15,7 @@ from test_framework.wallet import MiniWallet
|
|||
import time
|
||||
|
||||
class P2PNode(P2PDataStore):
|
||||
def on_inv(self, msg):
|
||||
def on_inv(self, message):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
def run_test(self):
|
||||
self.gen_node = self.nodes[0] # The block and tx generating node
|
||||
self.miniwallet = MiniWallet(self.gen_node)
|
||||
self.mocktime = int(time.time())
|
||||
|
||||
self.test_tx_in_block()
|
||||
self.test_notfound_on_replaced_tx()
|
||||
|
@ -33,20 +34,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
|
||||
def test_tx_in_block(self):
|
||||
self.log.info("Check that a transaction in the last block is uploaded (beneficial for compact block relay)")
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer = self.gen_node.add_p2p_connection(P2PNode())
|
||||
|
||||
self.log.debug("Generate transaction and block")
|
||||
inbound_peer.last_message.pop("inv", None)
|
||||
|
||||
self.gen_node.setmocktime(int(time.time())) # pause time based activities
|
||||
wtxid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
|
||||
rawmp = self.gen_node.getrawmempool(False, True)
|
||||
pi = self.gen_node.getpeerinfo()[0]
|
||||
assert_equal(rawmp["mempool_sequence"], 2) # our tx cause mempool activity
|
||||
assert_equal(pi["last_inv_sequence"], 1) # that is after the last inv
|
||||
assert_equal(pi["inv_to_send"], 1) # and our tx has been queued
|
||||
self.gen_node.setmocktime(0)
|
||||
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_until(lambda: "inv" in inbound_peer.last_message and inbound_peer.last_message.get("inv").inv[0].hash == int(wtxid, 16))
|
||||
|
||||
rawmp = self.gen_node.getrawmempool(False, True)
|
||||
|
@ -65,15 +66,20 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
|
||||
def test_notfound_on_replaced_tx(self):
|
||||
self.gen_node.disconnect_p2ps()
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer = self.gen_node.add_p2p_connection(P2PTxInvStore())
|
||||
|
||||
self.log.info("Transaction tx_a is broadcast")
|
||||
tx_a = self.miniwallet.send_self_transfer(from_node=self.gen_node)
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_for_broadcast(txns=[tx_a["wtxid"]])
|
||||
|
||||
tx_b = tx_a["tx"]
|
||||
tx_b.vout[0].nValue -= 9000
|
||||
self.gen_node.sendrawtransaction(tx_b.serialize().hex())
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_until(lambda: "tx" in inbound_peer.last_message and inbound_peer.last_message.get("tx").tx.wtxid_hex == tx_b.wtxid_hex)
|
||||
|
||||
self.log.info("Re-request of tx_a after replacement is answered with notfound")
|
||||
|
@ -96,28 +102,31 @@ class P2PLeakTxTest(BitcoinTestFramework):
|
|||
self.gen_node.disconnect_p2ps()
|
||||
inbound_peer = self.gen_node.add_p2p_connection(P2PNode()) # An "attacking" inbound peer
|
||||
|
||||
MAX_REPEATS = 100
|
||||
self.log.info("Running test up to {} times.".format(MAX_REPEATS))
|
||||
for i in range(MAX_REPEATS):
|
||||
self.log.info('Run repeat {}'.format(i + 1))
|
||||
txid = self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"]
|
||||
# Set a mock time so that time does not pass, and gen_node never announces the transaction
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
wtxid = int(self.miniwallet.send_self_transfer(from_node=self.gen_node)["wtxid"], 16)
|
||||
|
||||
want_tx = msg_getdata()
|
||||
want_tx.inv.append(CInv(t=MSG_TX, h=int(txid, 16)))
|
||||
with p2p_lock:
|
||||
inbound_peer.last_message.pop('notfound', None)
|
||||
inbound_peer.send_and_ping(want_tx)
|
||||
want_tx = msg_getdata()
|
||||
want_tx.inv.append(CInv(t=MSG_WTX, h=wtxid))
|
||||
with p2p_lock:
|
||||
inbound_peer.last_message.pop('notfound', None)
|
||||
inbound_peer.send_and_ping(want_tx)
|
||||
inbound_peer.wait_until(lambda: "notfound" in inbound_peer.last_message)
|
||||
with p2p_lock:
|
||||
assert_equal(inbound_peer.last_message.get("notfound").vec[0].hash, wtxid)
|
||||
inbound_peer.last_message.pop('notfound')
|
||||
|
||||
if inbound_peer.last_message.get('notfound'):
|
||||
self.log.debug('tx {} was not yet announced to us.'.format(txid))
|
||||
self.log.debug("node has responded with a notfound message. End test.")
|
||||
assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
|
||||
with p2p_lock:
|
||||
inbound_peer.last_message.pop('notfound')
|
||||
break
|
||||
else:
|
||||
self.log.debug('tx {} was already announced to us. Try test again.'.format(txid))
|
||||
assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
|
||||
# Move mocktime forward and wait for the announcement.
|
||||
inbound_peer.last_message.pop('inv', None)
|
||||
self.mocktime += 120
|
||||
self.gen_node.setmocktime(self.mocktime)
|
||||
inbound_peer.wait_for_inv([CInv(t=MSG_WTX, h=wtxid)], timeout=120)
|
||||
|
||||
# Send the getdata again, this time the node should send us a TX message.
|
||||
inbound_peer.last_message.pop('tx', None)
|
||||
inbound_peer.send_and_ping(want_tx)
|
||||
self.wait_until(lambda: "tx" in inbound_peer.last_message)
|
||||
assert_equal(wtxid, int(inbound_peer.last_message["tx"].tx.wtxid_hex, 16))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -474,6 +474,7 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect=
|
|||
# min_required_fds = MIN_CORE_FDS + MAX_ADDNODE_CONNECTIONS + nBind = 151 + 8 + 3 = 162;
|
||||
# nMaxConnections = available_fds - min_required_fds = 256 - 161 = 94;
|
||||
f.write("maxconnections=94\n")
|
||||
f.write("par=" + str(min(2, os.cpu_count())) + "\n")
|
||||
f.write(extra_config)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue