aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release-notes.md10
-rw-r--r--src/coins.cpp7
-rw-r--r--src/init.cpp14
-rw-r--r--src/net.cpp42
-rw-r--r--src/net.h11
-rw-r--r--src/net_processing.cpp2
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/sync.h1
-rw-r--r--src/test/coins_tests.cpp28
-rw-r--r--src/validation.cpp2
11 files changed, 96 insertions, 25 deletions
diff --git a/doc/release-notes.md b/doc/release-notes.md
index fe7f69d1f8..7d0d689684 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -62,6 +62,16 @@ Removal of Priority Estimation
major version. To prepare for this, the default for the rate limit of priority
transactions (`-limitfreerelay`) has been set to `0` kB/minute.
+P2P connection management
+--------------------------
+
+- Peers manually added through the addnode option or addnode RPC now have their own
+ limit of eight connections which does not compete with other inbound or outbound
+ connection usage and is not subject to the maxconnections limitation.
+
+- New connections to manually added peers are much faster.
+
+
0.14.0 Change log
=================
diff --git a/src/coins.cpp b/src/coins.cpp
index 68f32a9da4..4d0e4bc0ad 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -207,6 +207,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
entry.flags |= CCoinsCacheEntry::FRESH;
}
} else {
+ // Assert that the child cache entry was not marked FRESH if the
+ // parent cache entry has unspent outputs. If this ever happens,
+ // it means the FRESH flag was misapplied and there is a logic
+ // error in the calling code.
+ if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coins.IsPruned())
+ throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
+
// Found the entry in the parent cache
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
// The grandparent does not have an entry, and the child is
diff --git a/src/init.cpp b/src/init.cpp
index eb191ab7b8..992ce8ebdc 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -130,6 +130,7 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
//
std::atomic<bool> fRequestShutdown(false);
+std::atomic<bool> fDumpMempoolLater(false);
void StartShutdown()
{
@@ -211,7 +212,8 @@ void Shutdown()
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
- DumpMempool();
+ if (fDumpMempoolLater)
+ DumpMempool();
if (fFeeEstimatesInitialized)
{
@@ -669,6 +671,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
}
} // End scope of CImportingNow
LoadMempool();
+ fDumpMempoolLater = !fRequestShutdown;
}
/** Sanity checks
@@ -871,11 +874,11 @@ bool AppInitParameterInteraction()
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
- nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
- nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
+ nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0);
+ nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
- nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections);
+ nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
if (nMaxConnections < nUserMaxConnections)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
@@ -1109,7 +1112,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
- LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
+ LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
InitSignatureCache();
@@ -1566,6 +1569,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
connOptions.nRelevantServices = nRelevantServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
+ connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = 1;
connOptions.nBestHeight = chainActive.Height();
connOptions.uiInterface = &uiInterface;
diff --git a/src/net.cpp b/src/net.cpp
index bf2beb7740..37e7dfed4c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -621,6 +621,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nVersion);
X(cleanSubVer);
X(fInbound);
+ X(fAddnode);
X(nStartingHeight);
X(nSendBytes);
X(mapSendBytesPerMsgCmd);
@@ -1631,7 +1632,12 @@ void CConnman::ThreadOpenConnections()
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes) {
- if (!pnode->fInbound) {
+ if (!pnode->fInbound && !pnode->fAddnode) {
+ // Netgroups for inbound and addnode peers are not excluded because our goal here
+ // is to not use multiple of our limited outbound slots on a single netgroup
+ // but inbound and addnode peers do not use our outbound slots. Inbound peers
+ // also have the added issue that they're attacker controlled and could be used
+ // to prevent us from connecting to particular hosts if we used them here.
setConnected.insert(pnode->addr.GetGroup());
nOutbound++;
}
@@ -1776,27 +1782,35 @@ void CConnman::ThreadOpenAddedConnections()
vAddedNodes = mapMultiArgs.at("-addnode");
}
- for (unsigned int i = 0; true; i++)
+ while (true)
{
+ CSemaphoreGrant grant(*semAddnode);
std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo();
+ bool tried = false;
for (const AddedNodeInfo& info : vInfo) {
if (!info.fConnected) {
- CSemaphoreGrant grant(*semOutbound);
+ if (!grant.TryAcquire()) {
+ // If we've used up our semaphore and need a new one, lets not wait here since while we are waiting
+ // the addednodeinfo state might change.
+ break;
+ }
// If strAddedNode is an IP/port, decode it immediately, so
// OpenNetworkConnection can detect existing connections to that IP/port.
+ tried = true;
CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort()));
- OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false);
+ OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true);
if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
return;
}
}
- if (!interruptNet.sleep_for(std::chrono::minutes(2)))
+ // Retry every 60 seconds if a connection was attempted, otherwise two seconds
+ if (!interruptNet.sleep_for(std::chrono::seconds(tried ? 60 : 2)))
return;
}
}
// if successful, this moves the passed grant to the constructed node
-bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
+bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool fAddnode)
{
//
// Initiate outbound network connection
@@ -1825,6 +1839,8 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
pnode->fOneShot = true;
if (fFeeler)
pnode->fFeeler = true;
+ if (fAddnode)
+ pnode->fAddnode = true;
return true;
}
@@ -2076,8 +2092,10 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
nSendBufferMaxSize = 0;
nReceiveFloodSize = 0;
semOutbound = NULL;
+ semAddnode = NULL;
nMaxConnections = 0;
nMaxOutbound = 0;
+ nMaxAddnode = 0;
nBestHeight = 0;
clientInterface = NULL;
flagInterruptMsgProc = false;
@@ -2099,6 +2117,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
nLocalServices = connOptions.nLocalServices;
nMaxConnections = connOptions.nMaxConnections;
nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections);
+ nMaxAddnode = connOptions.nMaxAddnode;
nMaxFeeler = connOptions.nMaxFeeler;
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
@@ -2151,6 +2170,10 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
// initialize semaphore
semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
}
+ if (semAddnode == NULL) {
+ // initialize semaphore
+ semAddnode = new CSemaphore(nMaxAddnode);
+ }
//
// Start threads
@@ -2227,6 +2250,10 @@ void CConnman::Stop()
if (threadSocketHandler.joinable())
threadSocketHandler.join();
+ if (semAddnode)
+ for (int i=0; i<nMaxAddnode; i++)
+ semOutbound->post();
+
if (fAddressesInitialized)
{
DumpData();
@@ -2254,6 +2281,8 @@ void CConnman::Stop()
vhListenSocket.clear();
delete semOutbound;
semOutbound = NULL;
+ delete semAddnode;
+ semAddnode = NULL;
}
void CConnman::DeleteNode(CNode* pnode)
@@ -2554,6 +2583,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
strSubVer = "";
fWhitelisted = false;
fOneShot = false;
+ fAddnode = false;
fClient = false; // set by version message
fFeeler = false;
fSuccessfullyConnected = false;
diff --git a/src/net.h b/src/net.h
index 6ca402f71d..97b27dcdf4 100644
--- a/src/net.h
+++ b/src/net.h
@@ -58,8 +58,10 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000;
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
/** Maximum length of strSubVer in `version` message */
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
-/** Maximum number of outgoing nodes */
+/** Maximum number of automatic outgoing nodes */
static const int MAX_OUTBOUND_CONNECTIONS = 8;
+/** Maximum number of addnode outgoing nodes */
+static const int MAX_ADDNODE_CONNECTIONS = 8;
/** -listen default */
static const bool DEFAULT_LISTEN = true;
/** -upnp default */
@@ -135,6 +137,7 @@ public:
ServiceFlags nRelevantServices = NODE_NONE;
int nMaxConnections = 0;
int nMaxOutbound = 0;
+ int nMaxAddnode = 0;
int nMaxFeeler = 0;
int nBestHeight = 0;
CClientUIInterface* uiInterface = nullptr;
@@ -151,7 +154,7 @@ public:
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
bool GetNetworkActive() const { return fNetworkActive; };
void SetNetworkActive(bool active);
- bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
+ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -414,8 +417,10 @@ private:
ServiceFlags nRelevantServices;
CSemaphore *semOutbound;
+ CSemaphore *semAddnode;
int nMaxConnections;
int nMaxOutbound;
+ int nMaxAddnode;
int nMaxFeeler;
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
@@ -529,6 +534,7 @@ public:
int nVersion;
std::string cleanSubVer;
bool fInbound;
+ bool fAddnode;
int nStartingHeight;
uint64_t nSendBytes;
mapMsgCmdSize mapSendBytesPerMsgCmd;
@@ -626,6 +632,7 @@ public:
bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot;
+ bool fAddnode;
bool fClient;
const bool fInbound;
bool fSuccessfullyConnected;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index ccfbb77fcd..3a956e89e7 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2644,6 +2644,8 @@ bool SendMessages(CNode* pto, CConnman& connman, std::atomic<bool>& interruptMsg
state.fShouldBan = false;
if (pto->fWhitelisted)
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
+ else if (pto->fAddnode)
+ LogPrintf("Warning: not punishing addnoded peer %s!\n", pto->addr.ToString());
else {
pto->fDisconnect = true;
if (pto->addr.IsLocal())
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 16b5893753..72f5f4aac9 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -444,6 +444,8 @@ void BitcoinApplication::requestShutdown()
delete clientModel;
clientModel = 0;
+ StartShutdown();
+
// Request shutdown from core thread
Q_EMIT requestedShutdown();
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 632514fc85..2af00c1de9 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -92,6 +92,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
" \"version\": v, (numeric) The peer version, such as 7001\n"
" \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n"
" \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n"
+ " \"addnode\": true|false, (boolean) Whether connection was due to addnode and is using an addnode slot\n"
" \"startingheight\": n, (numeric) The starting height (block) of the peer\n"
" \"banscore\": n, (numeric) The ban score\n"
" \"synced_headers\": n, (numeric) The last header we have in common with this peer\n"
@@ -152,6 +153,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request)
// their ver message.
obj.push_back(Pair("subver", stats.cleanSubVer));
obj.push_back(Pair("inbound", stats.fInbound));
+ obj.push_back(Pair("addnode", stats.fAddnode));
obj.push_back(Pair("startingheight", stats.nStartingHeight));
if (fStateStats) {
obj.push_back(Pair("banscore", statestats.nMisbehavior));
diff --git a/src/sync.h b/src/sync.h
index 680d603044..3b29050e0e 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -264,7 +264,6 @@ public:
grant.Release();
grant.sem = sem;
grant.fHaveGrant = fHaveGrant;
- sem = NULL;
fHaveGrant = false;
}
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 70b958c296..b25c7ccc51 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -791,12 +791,18 @@ BOOST_AUTO_TEST_CASE(ccoins_modify_new)
void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
{
SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
- WriteCoinsViewEntry(test.cache, child_value, child_flags);
- test.cache.SelfTest();
CAmount result_value;
char result_flags;
- GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
+ try {
+ WriteCoinsViewEntry(test.cache, child_value, child_flags);
+ test.cache.SelfTest();
+ GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
+ } catch (std::logic_error& e) {
+ result_value = FAIL;
+ result_flags = NO_ENTRY;
+ }
+
BOOST_CHECK_EQUAL(result_value, expected_value);
BOOST_CHECK_EQUAL(result_flags, expected_flags);
}
@@ -840,21 +846,21 @@ BOOST_AUTO_TEST_CASE(ccoins_write)
CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY );
CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
- CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY|FRESH, DIRTY );
+ CheckWriteCoins(VALUE1, PRUNED, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
- CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
+ CheckWriteCoins(VALUE1, PRUNED, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
- CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY|FRESH, DIRTY );
+ CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
- CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
+ CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
- CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
+ CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
- CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
+ CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
- CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
+ CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
- CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
+ CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
// The checks above omit cases where the child flags are not DIRTY, since
// they would be too repetitive (the parent cache is never updated in these
diff --git a/src/validation.cpp b/src/validation.cpp
index 648fed8bc6..37a4186e0a 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -4099,6 +4099,8 @@ bool LoadMempool(void)
} else {
++skipped;
}
+ if (ShutdownRequested())
+ return false;
}
std::map<uint256, CAmount> mapDeltas;
file >> mapDeltas;