// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_NET_PROCESSING_H #define BITCOIN_NET_PROCESSING_H #include #include #include #include #include class BlockTransactionsRequest; class BlockValidationState; class CBlockHeader; class CChainParams; class CTxMemPool; class ChainstateManager; class TxValidationState; extern RecursiveMutex cs_main; extern RecursiveMutex g_cs_orphans; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default number of orphan+recently-replaced txn to keep around for block reconstruction */ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; static const bool DEFAULT_PEERBLOOMFILTERS = false; static const bool DEFAULT_PEERBLOCKFILTERS = false; /** Threshold for marking a node to be discouraged, e.g. disconnected and added to the discouragement filter. */ static const int DISCOURAGEMENT_THRESHOLD{100}; struct CNodeStateStats { int nSyncHeight = -1; int nCommonHeight = -1; int m_starting_height = -1; std::vector vHeightInFlight; }; /** * Data structure for an individual peer. This struct is not protected by * cs_main since it does not contain validation-critical data. * * Memory is owned by shared pointers and this object is destructed when * the refcount drops to zero. * * Mutexes inside this struct must not be held when locking m_peer_mutex. * * TODO: move most members from CNodeState to this structure. * TODO: move remaining application-layer data members from CNode to this structure. */ struct Peer { /** Same id as the CNode object for this peer */ const NodeId m_id{0}; /** Protects misbehavior data members */ Mutex m_misbehavior_mutex; /** Accumulated misbehavior score for this peer */ int m_misbehavior_score GUARDED_BY(m_misbehavior_mutex){0}; /** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */ bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false}; /** Protects block inventory data members */ Mutex m_block_inv_mutex; /** List of blocks that we'll announce via an `inv` message. * There is no final sorting before sending, as they are always sent * immediately and in the order requested. */ std::vector m_blocks_for_inv_relay GUARDED_BY(m_block_inv_mutex); /** Unfiltered list of blocks that we'd like to announce via a `headers` * message. If we can't announce via a `headers` message, we'll fall back to * announcing via `inv`. */ std::vector m_blocks_for_headers_relay GUARDED_BY(m_block_inv_mutex); /** The final block hash that we sent in an `inv` message to this peer. * When the peer requests this block, we send an `inv` message to trigger * the peer to request the next sequence of block hashes. * Most peers use headers-first syncing, which doesn't use this mechanism */ uint256 m_continuation_block GUARDED_BY(m_block_inv_mutex) {}; /** This peer's reported block height when we connected */ std::atomic m_starting_height{-1}; /** Set of txids to reconsider once their parent transactions have been accepted **/ std::set m_orphan_work_set GUARDED_BY(g_cs_orphans); /** Protects m_getdata_requests **/ Mutex m_getdata_requests_mutex; /** Work queue of items requested by this peer **/ std::deque m_getdata_requests GUARDED_BY(m_getdata_requests_mutex); explicit Peer(NodeId id) : m_id(id) {} }; using PeerRef = std::shared_ptr; class PeerManager final : public CValidationInterface, public NetEventsInterface { public: PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool, bool ignore_incoming_txs); /** Overridden from CValidationInterface. */ void BlockConnected(const std::shared_ptr& pblock, const CBlockIndex* pindexConnected) override; void BlockDisconnected(const std::shared_ptr &block, const CBlockIndex* pindex) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; void BlockChecked(const CBlock& block, const BlockValidationState& state) override; void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr& pblock) override; /** Implement NetEventsInterface */ void InitializeNode(CNode* pnode) override; void FinalizeNode(const CNode& node, bool& fUpdateConnectionTime) override; bool ProcessMessages(CNode* pfrom, std::atomic& interrupt) override; bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); /** Get statistics from node state */ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats); /** Whether this node ignores txs received over p2p. */ bool IgnoresIncomingTxs() { return m_ignore_incoming_txs; }; /** Set the best height */ void SetBestHeight(int height) { m_best_height = height; }; /** * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter. * Public for unit testing. */ void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message); /** * Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound. * Public for unit testing. */ void CheckForStaleTipAndEvictPeers(); /** Process a single message from a peer. Public for fuzz testing */ void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, const std::chrono::microseconds time_received, const std::atomic& interruptMsgProc); private: /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */ void ReattemptInitialBroadcast(CScheduler& scheduler) const; /** Get a shared pointer to the Peer object. * May return an empty shared_ptr if the Peer object can't be found. */ PeerRef GetPeerRef(NodeId id) const; /** Get a shared pointer to the Peer object and remove it from m_peer_map. * May return an empty shared_ptr if the Peer object can't be found. */ PeerRef RemovePeer(NodeId id); /** * Potentially mark a node discouraged based on the contents of a BlockValidationState object * * @param[in] via_compact_block this bool is passed in because net_processing should * punish peers differently depending on whether the data was provided in a compact * block message or not. If the compact block had a valid header, but contained invalid * txs, the peer should not be punished. See BIP 152. * * @return Returns true if the peer was punished (probably disconnected) */ bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = ""); /** * Potentially disconnect and discourage a node based on the contents of a TxValidationState object * * @return Returns true if the peer was punished (probably disconnected) */ bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = ""); /** Maybe disconnect a peer and discourage future connections from its address. * * @param[in] pnode The node to check. * @return True if the peer was marked for disconnection in this function */ bool MaybeDiscourageAndDisconnect(CNode& pnode); void ProcessOrphanTx(std::set& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans); /** Process a single headers message from a peer. */ void ProcessHeadersMessage(CNode& pfrom, const Peer& peer, const std::vector& headers, bool via_compact_block); void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req); /** Register with TxRequestTracker that an INV has been received from a * peer. The announcement parameters are decided in PeerManager and then * passed to TxRequestTracker. */ void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** Send a version message to a peer */ void PushNodeVersion(CNode& pnode, int64_t nTime); const CChainParams& m_chainparams; CConnman& m_connman; /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */ BanMan* const m_banman; ChainstateManager& m_chainman; CTxMemPool& m_mempool; TxRequestTracker m_txrequest GUARDED_BY(::cs_main); /** The height of the best chain */ std::atomic m_best_height{-1}; int64_t m_stale_tip_check_time; //!< Next time to check for stale tip /** Whether this node is running in blocks only mode */ const bool m_ignore_incoming_txs; /** Whether we've completed initial sync yet, for determining when to turn * on extra block-relay-only peers. */ bool m_initial_sync_finished{false}; /** Protects m_peer_map. This mutex must not be locked while holding a lock * on any of the mutexes inside a Peer object. */ mutable Mutex m_peer_mutex; /** * Map of all Peer objects, keyed by peer id. This map is protected * by the m_peer_mutex. Once a shared pointer reference is * taken, the lock may be released. Individual fields are protected by * their own locks. */ std::map m_peer_map GUARDED_BY(m_peer_mutex); }; /** Relay transaction to every node */ void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); #endif // BITCOIN_NET_PROCESSING_H