PeerManager
and all of struct Peer
into net_processing.cpp.
PeerManager
and all of struct Peer
into net_processing.cpp.
This is the first step of the refactoring proposed in #20758 and just does encapsulation, it doesn’t remove any of the globals.
EDIT: Rationale for moving things from the header into cpp files is to:
More specifically, #20758 aims to reduce the number of net_processing globals by moving them into the PeerManager class, which will involve less churn if that class isn’t exposed as a header file; and #19398 aims to move things from net to net_processing which also involves less churn if it doesn’t need to go into the net_processing header file.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
Concept ACK. I like this kind of encapsulation a lot.
I’m not sure why the TSAN build is failing with “WARNING: ThreadSanitizer: double lock of a mutex”. It seems like a false alarm to me. I think it’s choking on std::condition_variable condMsgProc
/Mutex mutexMsgProc
so maybe it’s something similar to https://github.com/google/sanitizers/issues/1259?
1478@@ -1295,7 +1479,7 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_
1479 * Update our best height and announce any block hashes which weren't previously
1480 * in ::ChainActive() to our peers.
1481 */
1482-void PeerManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
1483+void PeerManagerImpl::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
mutex:PeerManager::UpdatedBlockTip
in ./test/sanitizer_suppressions/tsan
252- * their own locks.
253- */
254- std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex);
255+ /** Process a single message from a peer. Public for fuzz testing */
256+ virtual void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
257+ const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) = 0;
1210@@ -1034,7 +1211,7 @@ void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std::
1211 }
1212 }
1213
1214-bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
1215+bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
1216 bool via_compact_block, const std::string& message)
1306@@ -1130,9 +1307,16 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
1307 (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
1308 }
1309
1310-PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
1311- CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool,
1312- bool ignore_incoming_txs)
1313+std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
Adding a new parameter to the ctor now requires updating five locations (PeerManager::make
declaration, PeerManager::make
definition signature, PeerManager::make
function body
PeerManagerImplctor declaration,
PeerManagerImpl` ctor definition signature.
I wonder if it makes sense to create a PeerManagerArgs
struct that’s passed into the make()
method and forwarded to the PeerManagerImpl
ctor so that only one place needs to be changed?
PeerManagerImpl
methods, we could plausibly remove all the PeerManagerImpl
declarations by just having net_processing.h start with namespace { class PeerManagerImpl : public PeerManager {
and end with the closing brackets and the PeerManager::make
definition – there’s no need to declare class methods before use. That’d remove one of the redundancies above, as well as a bunch of others.
2450@@ -2267,7 +2451,7 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar
2451 connman.PushMessage(&peer, std::move(msg));
2452 }
2453
2454-void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
2455+void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
2456 const std::chrono::microseconds time_received,
19 class CTxMemPool;
20 class ChainstateManager;
21-class TxValidationState;
22
23 extern RecursiveMutex cs_main;
24 extern RecursiveMutex g_cs_orphans;
Peer
and RelayTransactions()
no longer needs to call ForEachNode()
and therefore take cs_vNodes
while holding g_cs_orphans
, this mutex can be private in net_processing.
PeerManager
to its own file (given how much this PR is moving stuff around anyway)?
utACK abeecaa66ed136b3f0b7ff500ae5b592256072a5
Thanks for being so responsive to review comments!
123+ /** Implement NetEventsInterface */
124 void InitializeNode(CNode* pnode) override;
125- /** Handle removal of a peer by updating various state and removing it from mapNodeState */
126 void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override;
127- /**
128- * Process protocol messages received from a given node
616@@ -617,11 +617,27 @@ uint16_t GetListenPort();
617 class NetEventsInterface
618 {
619 public:
620- virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
621- virtual bool SendMessages(CNode* pnode) = 0;
622+ /** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */
mapNodeState
) doesn’t make sense in the interface class. Same for FinalizeNode()
below.
I’m not sure if adding the comments to NetEventsInterface
adds much.
utACK 02f67760d9014b4c963c3c8a57ed5d56de98205d.
768@@ -769,11 +769,28 @@ class CNode
769 class NetEventsInterface
770 {
771 public:
772- virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
773- virtual bool SendMessages(CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_sendProcessing) = 0;
774+ /** Initialize a peer (setup state, queue any initial messages) */
I don’t think it’s a big deal, but if you’re going to add these comments to the interface class, I think there are some potential improvements:
SendMessages()
return value is not used to indicate if more work is done. The function always returns true
and that value is discarded by the caller. A future commit could change the function to return void
.ProcessMessages()
does return a bool that indicates whether there’s more work.Eventually, these functions could all be updated to just take a single CNode&
argument, which would be a very clean interface (#20228 removes the update_connection_time
arg from FinalizeNode()
, and I have another branch that eliminates interrupt
).
SendMessages
lost its new lock annotation in the rebase, so I’ve added the doc from ProcessMessages
return value.
I think “any initial messages” covers nothing, just a version message, or anything else we might think up fine. The return value of SendMessages
is “used” in the unit tests, so haven’t removed that in this PR. Looking at the CNode&
thing in #20758
utACK 3bcde8b84722be58c68309b7076aba238cf4c305
Did a git range-diff to verify that only the comments in NetEventsInterface
have changed.
ACK c97f70c861ac6959b8116a9bca3031edeb2b2aaa
After the further commits in #20758 almost all of net_processing is moved into
PeerManager
, so its effectively a dedicated file anyway. Splitting out sub-components (like orphan handling) into their own modules would be a good next step afterwards, I think.
That sounds good, because net_processing.cpp
is almost 5000 lines now, with a whole bunch of classes and structs.
87-};
88-
89-using PeerRef = std::shared_ptr<Peer>;
90-
91-class PeerManager final : public CValidationInterface, public NetEventsInterface {
92+class PeerManager : public CValidationInterface, public NetEventsInterface
PeerManager
-> PeerManagerInterface
PeerManagerImpl
-> PeerManager
for consistency with CValidationInterface
and NetEventsInterface
.
Interface
is for defining a class where some other module is going to implement the methods that do the actual work; but the work to fulfill the PeerManager
API really is done by net_processing; it’s just an implementation detail that the methods are virtual and inheritance is used – the unique_ptr<Impl> m_impl;
approach used in txrequest could equally well have been used too. So I think PeerManager*
is the right name for what is being exposed to other modules here.
1319+std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
1320+ CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool,
1321+ bool ignore_incoming_txs)
1322+{
1323+ return std::make_unique<PeerManagerImpl>(chainparams, connman, banman, scheduler, chainman, pool, ignore_incoming_txs);
1324+}
std::make_unique<PeerManagerImpl>(...)
in the callers and drop this method?
PeerManagerImpl
is only declared in the cpp file, so is not available to the callers.
346+ */
347+ std::map<NodeId, PeerRef> m_peer_map GUARDED_BY(m_peer_mutex);
348+};
349+} // namespace
350+
351 namespace {
nit:
0
ACK c97f70c861ac6959b8116a9bca3031edeb2b2aaa
Code review ok (some minor questions below), compiles locally with clang 12 without warnings, unit and functional tests pass.
Nice, good thing to have less implementation details in (commonly included) header files.
Code review ACK c97f70c861ac6959b8116a9bca3031edeb2b2aaa
Reviewing this really soon if you’re looking for more eyes on it before merge.
Don’t let it being merged keep you from reviewing it please 😄
169+ void InitializeNode(CNode* pnode) override;
170+ void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override;
171+ bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override;
172+ bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
173+
174+ /** Implement PeerManager */
217+ PeerRef RemovePeer(NodeId id);
218+
219+ /**
220+ * Potentially mark a node discouraged based on the contents of a BlockValidationState object
221+ *
222+ * @param[in] via_compact_block this bool is passed in because net_processing should
via_hb_compact_block
.
250+ bool via_compact_block);
251+
252+ void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req);
253+
254+ /** Register with TxRequestTracker that an INV has been received from a
255+ * peer. The announcement parameters are decided in PeerManager and then
134-
135- /** Set the best height */
136- void SetBestHeight(int height) { m_best_height = height; };
137+ /**
138+ * Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound.
139+ * Public for unit testing.
Code Review ACK c97f70c
Good code restructuring direction, only minors.