|
5 | 5 | #include <governance/net_governance.h> |
6 | 6 |
|
7 | 7 | #include <chainparams.h> |
| 8 | +#include <evo/deterministicmns.h> |
8 | 9 | #include <governance/governance.h> |
9 | 10 | #include <logging.h> |
10 | 11 | #include <masternode/sync.h> |
11 | 12 | #include <net.h> |
12 | 13 | #include <netfulfilledman.h> |
13 | 14 | #include <netmessagemaker.h> |
14 | 15 | #include <node/interface_ui.h> |
| 16 | +#include <random.h> |
15 | 17 | #include <scheduler.h> |
16 | 18 | #include <shutdown.h> |
17 | 19 |
|
@@ -58,6 +60,94 @@ void NetGovernance::SendGovernanceSyncRequest(CNode* pnode, CConnman& connman) c |
58 | 60 | connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, uint256(), filter)); |
59 | 61 | } |
60 | 62 |
|
| 63 | +int NetGovernance::RequestGovernanceObjectVotes(const std::vector<CNode*>& vNodesCopy, CConnman& connman) const |
| 64 | +{ |
| 65 | + // Maximum number of nodes to request votes from for the same object hash on real networks |
| 66 | + // (mainnet, testnet, devnets). Keep this low to avoid unnecessary bandwidth usage. |
| 67 | + static constexpr size_t REALNET_PEERS_PER_HASH{3}; |
| 68 | + // Maximum number of nodes to request votes from for the same object hash on regtest. |
| 69 | + // During testing, nodes are isolated to create conflicting triggers. Using the real |
| 70 | + // networks limit of 3 nodes often results in querying only "non-isolated" nodes, missing the |
| 71 | + // isolated ones we need to test. This high limit ensures all available nodes are queried. |
| 72 | + static constexpr size_t REGTEST_PEERS_PER_HASH{std::numeric_limits<size_t>::max()}; |
| 73 | + |
| 74 | + if (vNodesCopy.empty()) return -1; |
| 75 | + |
| 76 | + int64_t nNow = GetTime(); |
| 77 | + int nTimeout = 60 * 60; |
| 78 | + size_t nPeersPerHashMax = Params().IsMockableChain() ? REGTEST_PEERS_PER_HASH : REALNET_PEERS_PER_HASH; |
| 79 | + |
| 80 | + |
| 81 | + // This should help us to get some idea about an impact this can bring once deployed on mainnet. |
| 82 | + // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, |
| 83 | + // so 1 obj on mainnet == ~10 objs or ~1000 votes on testnet. However we want to test a higher |
| 84 | + // number of votes to make sure it's robust enough, so aim at 2000 votes per masternode per request. |
| 85 | + // On mainnet nMaxObjRequestsPerNode is always set to 1. |
| 86 | + int nMaxObjRequestsPerNode = 1; |
| 87 | + size_t nProjectedVotes = 2000; |
| 88 | + if (Params().NetworkIDString() != CBaseChainParams::MAIN) { |
| 89 | + nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / std::max(1, (int)m_gov_manager.GetMNManager().GetListAtChainTip().GetValidMNsCount()))); |
| 90 | + } |
| 91 | + |
| 92 | + static Mutex cs_recently; |
| 93 | + static std::map<uint256, std::map<CService, int64_t> > mapAskedRecently GUARDED_BY(cs_recently); |
| 94 | + LOCK(cs_recently); |
| 95 | + |
| 96 | + auto [vTriggerObjHashes, vOtherObjHashes] = m_gov_manager.FetchGovernanceObjectVotes(nMaxObjRequestsPerNode, nNow, mapAskedRecently); |
| 97 | + |
| 98 | + if (vTriggerObjHashes.empty() && vOtherObjHashes.empty()) return -2; |
| 99 | + |
| 100 | + LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", |
| 101 | + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); |
| 102 | + |
| 103 | + Shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), FastRandomContext()); |
| 104 | + Shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), FastRandomContext()); |
| 105 | + |
| 106 | + for (int i = 0; i < nMaxObjRequestsPerNode; ++i) { |
| 107 | + uint256 nHashGovobj; |
| 108 | + |
| 109 | + // ask for triggers first |
| 110 | + if (!vTriggerObjHashes.empty()) { |
| 111 | + nHashGovobj = vTriggerObjHashes.back(); |
| 112 | + } else { |
| 113 | + if (vOtherObjHashes.empty()) break; |
| 114 | + nHashGovobj = vOtherObjHashes.back(); |
| 115 | + } |
| 116 | + bool fAsked = false; |
| 117 | + for (const auto& pnode : vNodesCopy) { |
| 118 | + // Don't try to sync any data from outbound non-relay "masternode" connections. |
| 119 | + // Inbound connection this early is most likely a "masternode" connection |
| 120 | + // initiated from another node, so skip it too. |
| 121 | + if (!pnode->CanRelay() || (connman.IsActiveMasternode() && pnode->IsInboundConn())) continue; |
| 122 | + // stop early to prevent setAskFor overflow |
| 123 | + { |
| 124 | + LOCK(::cs_main); |
| 125 | + size_t nProjectedSize = m_peer_manager->PeerGetRequestedObjectCount(pnode->GetId()) + nProjectedVotes; |
| 126 | + if (nProjectedSize > MAX_INV_SZ) continue; |
| 127 | + } |
| 128 | + // to early to ask the same node |
| 129 | + if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; |
| 130 | + |
| 131 | + m_gov_manager.RequestGovernanceObject(pnode, nHashGovobj, connman, true); |
| 132 | + mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; |
| 133 | + fAsked = true; |
| 134 | + // stop loop if max number of peers per obj was asked |
| 135 | + if (mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; |
| 136 | + } |
| 137 | + // NOTE: this should match `if` above (the one before `while`) |
| 138 | + if (!vTriggerObjHashes.empty()) { |
| 139 | + vTriggerObjHashes.pop_back(); |
| 140 | + } else { |
| 141 | + vOtherObjHashes.pop_back(); |
| 142 | + } |
| 143 | + if (!fAsked) i--; |
| 144 | + } |
| 145 | + LogPrint(BCLog::GOBJECT, "CGovernanceManager::RequestGovernanceObjectVotes -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", |
| 146 | + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); |
| 147 | + |
| 148 | + return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); |
| 149 | +} |
| 150 | + |
61 | 151 | void NetGovernance::ProcessTick(CConnman& connman) |
62 | 152 | { |
63 | 153 | assert(m_netfulfilledman.IsValid()); |
@@ -87,7 +177,7 @@ void NetGovernance::ProcessTick(CConnman& connman) |
87 | 177 |
|
88 | 178 | // gradually request the rest of the votes after sync finished |
89 | 179 | if (m_node_sync.IsSynced()) { |
90 | | - m_gov_manager.RequestGovernanceObjectVotes(snap.Nodes(), connman, m_peer_manager); |
| 180 | + RequestGovernanceObjectVotes(snap.Nodes(), connman); |
91 | 181 | return; |
92 | 182 | } |
93 | 183 |
|
@@ -217,7 +307,7 @@ void NetGovernance::ProcessTick(CConnman& connman) |
217 | 307 | continue; // to early for this node |
218 | 308 | } |
219 | 309 | const std::vector<CNode*> vNodeCopy{pnode}; |
220 | | - int nObjsLeftToAsk = m_gov_manager.RequestGovernanceObjectVotes(vNodeCopy, connman, m_peer_manager); |
| 310 | + int nObjsLeftToAsk = RequestGovernanceObjectVotes(vNodeCopy, connman); |
221 | 311 | // check for data |
222 | 312 | if (nObjsLeftToAsk == 0) { |
223 | 313 | static int64_t nTimeNoObjectsLeft = 0; |
|
0 commit comments