Merge pull request #3366 from codablock/pr_fix_mnconns

Multiple fixes for masternode connection handling
This commit is contained in:
Alexander Block 2020-03-24 10:57:42 +01:00 committed by GitHub
commit c0bb06e766
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 75 additions and 41 deletions

View File

@ -395,6 +395,7 @@ public:
fMineBlocksOnDemand = false;
fAllowMultipleAddressesFromGroup = false;
fAllowMultiplePorts = false;
nLLMQConnectionRetryTimeout = 60;
nPoolMinParticipants = 3;
nPoolMaxParticipants = 5; // TODO: bump on next HF / mandatory upgrade
@ -571,6 +572,7 @@ public:
fMineBlocksOnDemand = false;
fAllowMultipleAddressesFromGroup = false;
fAllowMultiplePorts = true;
nLLMQConnectionRetryTimeout = 60;
nPoolMinParticipants = 3; // TODO drop to 2 with next mandatory upgrade
nPoolMaxParticipants = 5;
@ -727,6 +729,7 @@ public:
fMineBlocksOnDemand = false;
fAllowMultipleAddressesFromGroup = true;
fAllowMultiplePorts = true;
nLLMQConnectionRetryTimeout = 60;
nPoolMinParticipants = 3; // same, drop to 2 w/ breaking change
nPoolMaxParticipants = 5;
@ -837,6 +840,7 @@ public:
fMineBlocksOnDemand = true;
fAllowMultipleAddressesFromGroup = true;
fAllowMultiplePorts = true;
nLLMQConnectionRetryTimeout = 30; // must be lower then the LLMQ signing session timeout so that tests have control over failing behavior
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
nPoolMinParticipants = 2;

View File

@ -70,6 +70,8 @@ public:
bool AllowMultipleAddressesFromGroup() const { return fAllowMultipleAddressesFromGroup; }
/** Allow nodes with the same address and multiple ports */
bool AllowMultiplePorts() const { return fAllowMultiplePorts; }
/** How long to wait until we allow retrying of a LLMQ connection */
int LLMQConnectionRetryTimeout() const { return nLLMQConnectionRetryTimeout; }
/** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; }
/** Return the list of hostnames to look up for DNS seeds */
@ -112,6 +114,7 @@ protected:
bool fMineBlocksOnDemand;
bool fAllowMultipleAddressesFromGroup;
bool fAllowMultiplePorts;
int nLLMQConnectionRetryTimeout;
CCheckpointData checkpointData;
ChainTxData chainTxData;
int nPoolMinParticipants;

View File

@ -6,7 +6,7 @@
CMasternodeMetaMan mmetaman;
const std::string CMasternodeMetaMan::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-1";
const std::string CMasternodeMetaMan::SERIALIZATION_VERSION_STRING = "CMasternodeMetaMan-Version-2";
void CMasternodeMetaInfo::AddGovernanceVote(const uint256& nGovernanceObjectHash)
{

View File

@ -33,6 +33,8 @@ private:
// KEEP TRACK OF GOVERNANCE ITEMS EACH MASTERNODE HAS VOTE UPON FOR RECALCULATION
std::map<uint256, int> mapGovernanceObjectsVotedOn;
int64_t lastOutboundAttempt = 0;
public:
CMasternodeMetaInfo() {}
explicit CMasternodeMetaInfo(const uint256& _proTxHash) : proTxHash(_proTxHash) {}
@ -40,7 +42,8 @@ public:
proTxHash(ref.proTxHash),
nLastDsq(ref.nLastDsq),
nMixingTxCount(ref.nMixingTxCount),
mapGovernanceObjectsVotedOn(ref.mapGovernanceObjectsVotedOn)
mapGovernanceObjectsVotedOn(ref.mapGovernanceObjectsVotedOn),
lastOutboundAttempt(ref.lastOutboundAttempt)
{
}
@ -53,6 +56,7 @@ public:
READWRITE(nLastDsq);
READWRITE(nMixingTxCount);
READWRITE(mapGovernanceObjectsVotedOn);
READWRITE(lastOutboundAttempt);
}
public:
@ -66,6 +70,9 @@ public:
void AddGovernanceVote(const uint256& nGovernanceObjectHash);
void RemoveGovernanceObject(const uint256& nGovernanceObjectHash);
void SetLastOutboundAttempt(int64_t t) { LOCK(cs); lastOutboundAttempt = t; }
int64_t GetLastOutboundAttempt() const { LOCK(cs); return lastOutboundAttempt; }
};
typedef std::shared_ptr<CMasternodeMetaInfo> CMasternodeMetaInfoPtr;

View File

@ -25,6 +25,7 @@
#include <utilstrencodings.h>
#include <validation.h>
#include <masternode/masternode-meta.h>
#include <masternode/masternode-sync.h>
#include <privatesend/privatesend.h>
#include <evo/deterministicmns.h>
@ -2099,6 +2100,8 @@ void CConnman::ThreadOpenMasternodeConnections()
if (gArgs.IsArgSet("-connect") && gArgs.GetArgs("-connect").size() > 0)
return;
auto& chainParams = Params();
while (!interruptNet)
{
if (!interruptNet.sleep_for(std::chrono::milliseconds(1000)))
@ -2123,49 +2126,55 @@ void CConnman::ThreadOpenMasternodeConnections()
// NOTE: Process only one pending masternode at a time
CService addr;
CDeterministicMNCPtr connectToDmn;
{ // don't hold lock while calling OpenMasternodeConnection as cs_main is locked deep inside
LOCK2(cs_vNodes, cs_vPendingMasternodes);
std::vector<CService> pending;
for (const auto& group : masternodeQuorumNodes) {
for (const auto& proRegTxHash : group.second) {
auto dmn = mnList.GetMN(proRegTxHash);
if (!dmn) {
continue;
}
const auto& addr2 = dmn->pdmnState->addr;
if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) {
auto addrInfo = addrman.GetAddressInfo(addr2);
// back off trying connecting to an address if we already tried recently
if (addrInfo.IsValid() && nANow - addrInfo.nLastTry < 60) {
if (!vPendingMasternodes.empty()) {
auto dmn = mnList.GetValidMN(vPendingMasternodes.front());
vPendingMasternodes.erase(vPendingMasternodes.begin());
if (dmn && !connectedNodes.count(dmn->pdmnState->addr) && !IsMasternodeOrDisconnectRequested(dmn->pdmnState->addr)) {
connectToDmn = dmn;
LogPrint(BCLog::NET, "CConnman::%s -- opening pending masternode connection to %s, service=%s\n", __func__, dmn->proTxHash.ToString(), dmn->pdmnState->addr.ToString(false));
}
}
if (!connectToDmn) {
std::vector<CDeterministicMNCPtr> pending;
for (const auto& group : masternodeQuorumNodes) {
for (const auto& proRegTxHash : group.second) {
auto dmn = mnList.GetMN(proRegTxHash);
if (!dmn) {
continue;
}
pending.emplace_back(addr2);
const auto& addr2 = dmn->pdmnState->addr;
if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) {
int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt();
// back off trying connecting to an address if we already tried recently
if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) {
continue;
}
pending.emplace_back(dmn);
}
}
}
}
if (!vPendingMasternodes.empty()) {
auto addr2 = vPendingMasternodes.front();
vPendingMasternodes.erase(vPendingMasternodes.begin());
if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2)) {
pending.emplace_back(addr2);
if (!pending.empty()) {
connectToDmn = pending[GetRandInt(pending.size())];
LogPrint(BCLog::NET, "CConnman::%s -- opening quorum connection to %s, service=%s\n", __func__, connectToDmn->proTxHash.ToString(), connectToDmn->pdmnState->addr.ToString(false));
}
}
if (pending.empty()) {
// nothing to do, keep waiting
continue;
}
std::random_shuffle(pending.begin(), pending.end());
addr = pending.front();
}
OpenMasternodeConnection(CAddress(addr, NODE_NETWORK));
if (!connectToDmn) {
continue;
}
mmetaman.GetMetaInfo(connectToDmn->proTxHash)->SetLastOutboundAttempt(nANow);
OpenMasternodeConnection(CAddress(connectToDmn->pdmnState->addr, NODE_NETWORK));
// should be in the list now if connection was opened
ForNode(addr, CConnman::AllNodes, [&](CNode* pnode) {
ForNode(connectToDmn->pdmnState->addr, CConnman::AllNodes, [&](CNode* pnode) {
if (pnode->fDisconnect) {
return false;
}
@ -2777,15 +2786,14 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
return false;
}
bool CConnman::AddPendingMasternode(const CService& service)
bool CConnman::AddPendingMasternode(const uint256& proTxHash)
{
LOCK(cs_vPendingMasternodes);
for(const auto& s : vPendingMasternodes) {
if (service == s)
return false;
if (std::find(vPendingMasternodes.begin(), vPendingMasternodes.end(), proTxHash) != vPendingMasternodes.end()) {
return false;
}
vPendingMasternodes.push_back(service);
vPendingMasternodes.push_back(proTxHash);
return true;
}

View File

@ -407,7 +407,7 @@ public:
bool RemoveAddedNode(const std::string& node);
std::vector<AddedNodeInfo> GetAddedNodeInfo();
bool AddPendingMasternode(const CService& addr);
bool AddPendingMasternode(const uint256& proTxHash);
bool AddMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash, const std::set<uint256>& proTxHashes);
bool HasMasternodeQuorumNodes(Consensus::LLMQType llmqType, const uint256& quorumHash);
std::set<uint256> GetMasternodeQuorums(Consensus::LLMQType llmqType);
@ -543,7 +543,7 @@ private:
CCriticalSection cs_vOneShots;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
CCriticalSection cs_vAddedNodes;
std::vector<CService> vPendingMasternodes;
std::vector<uint256> vPendingMasternodes;
std::map<std::pair<Consensus::LLMQType, uint256>, std::set<uint256>> masternodeQuorumNodes; // protected by cs_vPendingMasternodes
mutable CCriticalSection cs_vPendingMasternodes;
std::vector<CNode*> vNodes;

View File

@ -1081,7 +1081,7 @@ bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymize
nSessionDenom = dsq.nDenom;
mixingMasternode = dmn;
pendingDsaRequest = CPendingDsaRequest(dmn->pdmnState->addr, CPrivateSendAccept(nSessionDenom, txMyCollateral));
connman.AddPendingMasternode(dmn->pdmnState->addr);
connman.AddPendingMasternode(dmn->proTxHash);
// TODO: add new state POOL_STATE_CONNECTING and bump MIN_PRIVATESEND_PEER_PROTO_VERSION
SetState(POOL_STATE_QUEUE);
nTimeLastSuccessfulStep = GetTime();
@ -1158,7 +1158,7 @@ bool CPrivateSendClientSession::StartNewQueue(CAmount nBalanceNeedsAnonymized, C
}
mixingMasternode = dmn;
connman.AddPendingMasternode(dmn->pdmnState->addr);
connman.AddPendingMasternode(dmn->proTxHash);
pendingDsaRequest = CPendingDsaRequest(dmn->pdmnState->addr, CPrivateSendAccept(nSessionDenom, txMyCollateral));
// TODO: add new state POOL_STATE_CONNECTING and bump MIN_PRIVATESEND_PEER_PROTO_VERSION
SetState(POOL_STATE_QUEUE);

View File

@ -60,6 +60,9 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
self.wait_for_tx(txid, self.nodes[1])
self.wait_for_tx(txid, self.nodes[2])
reconnect_isolated_node(self.nodes[3], 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
set_node_times(self.nodes, self.mocktime)
self.wait_for_mnauth(self.nodes[3], 2)
# node 3 fully reconnected but the TX wasn't relayed to it, so there should be no IS lock
self.wait_for_instantlock(txid, self.nodes[0], False, 5)
@ -91,6 +94,9 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
self.wait_for_tx(txid, self.nodes[1])
self.wait_for_tx(txid, self.nodes[2])
reconnect_isolated_node(self.nodes[3], 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
set_node_times(self.nodes, self.mocktime)
self.wait_for_mnauth(self.nodes[3], 2)
# node 3 fully reconnected but the TX wasn't relayed to it, so there should be no IS lock
self.wait_for_instantlock(txid, self.nodes[0], False, 5)
@ -133,6 +139,9 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
set_node_times(self.nodes, self.mocktime)
time.sleep(2) # make sure Cleanup() is called
reconnect_isolated_node(self.nodes[3], 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
set_node_times(self.nodes, self.mocktime)
self.wait_for_mnauth(self.nodes[3], 2)
# node 3 fully reconnected but the signing session is already timed out on all nodes, so no IS lock
self.wait_for_instantlock(txid, self.nodes[0], False, 5)
@ -158,6 +167,9 @@ class LLMQ_IS_RetroactiveSigning(DashTestFramework):
set_node_times(self.nodes, self.mocktime)
time.sleep(2) # make sure Cleanup() is called
reconnect_isolated_node(self.nodes[3], 0)
# Make sure nodes actually try re-connecting quorum connections
self.bump_mocktime(30)
set_node_times(self.nodes, self.mocktime)
self.wait_for_mnauth(self.nodes[3], 2)
self.nodes[0].sendrawtransaction(rawtx)
# Make sure nodes 1 and 2 received the TX