diff options
Diffstat (limited to 'src/net_processing.cpp')
-rw-r--r-- | src/net_processing.cpp | 411 |
1 files changed, 183 insertions, 228 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 37eed95b60..9603d098a4 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -153,8 +153,14 @@ struct COrphanTx { int64_t nTimeExpire; size_t list_pos; }; + +/** Guards orphan transactions and extra txs for compact blocks */ RecursiveMutex g_cs_orphans; +/** Map from txid to orphan transaction record. Limited by + * -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */ std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans); +/** Index from wtxid into the mapOrphanTransactions to lookup orphan + * transactions using their witness ids. */ std::map<uint256, std::map<uint256, COrphanTx>::iterator> g_orphans_by_wtxid GUARDED_BY(g_cs_orphans); void EraseOrphansFor(NodeId peer); @@ -258,12 +264,19 @@ namespace { return &(*a) < &(*b); } }; - std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans); - std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans); //! For random eviction + /** Index from the parents' COutPoint into the mapOrphanTransactions. Used + * to remove orphan transactions from the mapOrphanTransactions */ + std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans); + /** Orphan transactions in vector for quick random eviction */ + std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans); - static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; + /** Orphan/conflicted/etc transactions that are kept for compact block reconstruction. + * The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of + * these are kept in a ring buffer */ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans); + /** Offset into vExtraTxnForCompact to insert the next tx */ + static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; } // namespace namespace { @@ -672,19 +685,19 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma return; } } - connman.ForNode(nodeid, [&connman](CNode* pfrom){ - LockAssertion lock(::cs_main); + connman.ForNode(nodeid, [&connman](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + AssertLockHeld(::cs_main); uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { // As per BIP152, we only get 3 of our peers to announce // blocks using compact encodings. connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){ - connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion)); + connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion)); return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion)); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetCommonVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/true, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); @@ -879,7 +892,7 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) if (state) state->m_last_block_announcement = time_in_seconds; } -void PeerLogicValidation::InitializeNode(CNode *pnode) { +void PeerManager::InitializeNode(CNode *pnode) { CAddress addr = pnode->addr; std::string addrName = pnode->GetAddrName(); NodeId nodeid = pnode->GetId(); @@ -897,17 +910,18 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) { } } -void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const +void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const { - std::map<uint256, uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs(); + std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs(); + + for (const auto& txid : unbroadcast_txids) { + CTransactionRef tx = m_mempool.get(txid); - for (const auto& elem : unbroadcast_txids) { - // Sanity check: all unbroadcast txns should exist in the mempool - if (m_mempool.exists(elem.first)) { + if (tx != nullptr) { LOCK(cs_main); - RelayTransaction(elem.first, elem.second, m_connman); + RelayTransaction(txid, tx->GetWitnessHash(), m_connman); } else { - m_mempool.RemoveUnbroadcastTx(elem.first, true); + m_mempool.RemoveUnbroadcastTx(txid, true); } } @@ -917,7 +931,7 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } -void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { +void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { fUpdateConnectionTime = false; LOCK(cs_main); int misbehavior{0}; @@ -1120,11 +1134,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) return nEvicted; } -/** - * 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. - */ -void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) +void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) { assert(howmuch > 0); @@ -1142,17 +1152,9 @@ void Misbehaving(const NodeId pnode, const int howmuch, const std::string& messa } } -/** - * 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) - */ -static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = "") { +bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, + bool via_compact_block, const std::string& message) +{ switch (state.GetResult()) { case BlockValidationResult::BLOCK_RESULT_UNSET: break; @@ -1200,12 +1202,7 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s return false; } -/** - * Potentially disconnect and discourage a node based on the contents of a TxValidationState object - * - * @return Returns true if the peer was punished (probably disconnected) - */ -static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "") +bool PeerManager::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) { switch (state.GetResult()) { case TxValidationResult::TX_RESULT_UNSET: @@ -1251,8 +1248,10 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); } -PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool) - : m_connman(connman), +PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman, + CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool) + : m_chainparams(chainparams), + m_connman(connman), m_banman(banman), m_chainman(chainman), m_mempool(pool), @@ -1272,13 +1271,12 @@ PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CSch // same probability that we have in the reject filter). g_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001)); - const Consensus::Params& consensusParams = Params().GetConsensus(); // Stale tip checking and peer eviction are on two different timers, but we // don't want them to get out of sync due to drift in the scheduler, so we // combine them in one function and schedule at the quicker (peer-eviction) // timer. static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer"); - scheduler.scheduleEvery([this, consensusParams] { this->CheckForStaleTipAndEvictPeers(consensusParams); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL}); + scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL}); // schedule next run for 10-15 minutes in the future const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5}); @@ -1289,7 +1287,7 @@ PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CSch * Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected * block. Also save the time of the last tip update. */ -void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex) +void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex) { { LOCK(g_cs_orphans); @@ -1333,7 +1331,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb } } -void PeerLogicValidation::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) +void PeerManager::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) { // To avoid relay problems with transactions that were previously // confirmed, clear our filter of recently confirmed transactions whenever @@ -1358,7 +1356,7 @@ static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_ * Maintain state about the best-seen block and fast-announce a compact block * to compatible peers. */ -void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { +void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true); const CNetMsgMaker msgMaker(PROTOCOL_VERSION); @@ -1369,7 +1367,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: return; nHighestFastAnnounce = pindex->nHeight; - bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, Params().GetConsensus()); + bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()); uint256 hashBlock(pblock->GetHash()); { @@ -1380,11 +1378,11 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled; } - m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) { - LockAssertion lock(::cs_main); + m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + AssertLockHeld(::cs_main); // TODO: Avoid the repeated-serialization here - if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect) + if (pnode->GetCommonVersion() < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect) return; ProcessBlockAvailability(pnode->GetId()); CNodeState &state = *State(pnode->GetId()); @@ -1393,7 +1391,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) && !PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) { - LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock", + LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock", hashBlock.ToString(), pnode->GetId()); m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock)); state.pindexBestHeaderSent = pindex; @@ -1405,7 +1403,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: * Update our best height and announce any block hashes which weren't previously * in ::ChainActive() to our peers. */ -void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { +void PeerManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; m_connman.SetBestHeight(nNewHeight); @@ -1440,7 +1438,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB * Handle invalid block rejection and consequent peer discouragement, maintain which * peers announce compact blocks. */ -void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) { +void PeerManager::BlockChecked(const CBlock& block, const BlockValidationState& state) { LOCK(cs_main); const uint256 hash(block.GetHash()); @@ -1514,12 +1512,12 @@ bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman) { - connman.ForEachNode([&txid, &wtxid](CNode* pnode) - { - LockAssertion lock(::cs_main); + connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + AssertLockHeld(::cs_main); - CNodeState &state = *State(pnode->GetId()); - if (state.m_wtxid_relay) { + CNodeState* state = State(pnode->GetId()); + if (state == nullptr) return; + if (state->m_wtxid_relay) { pnode->PushTxInventory(wtxid); } else { pnode->PushTxInventory(txid); @@ -1529,7 +1527,6 @@ void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& connman) { - unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) // Relay to a limited number of other nodes // Use deterministic randomness to send to the same nodes for 24 hours @@ -1538,6 +1535,9 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24 * 60 * 60)); FastRandomContext insecure_rand; + // Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers. + unsigned int nRelayNodes = (fReachable || (hasher.Finalize() & 1)) ? 2 : 1; + std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}}; assert(nRelayNodes <= best.size()); @@ -1595,7 +1595,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c } // release cs_main before calling ActivateBestChain if (need_activate_chain) { BlockValidationState state; - if (!ActivateBestChain(state, Params(), a_recent_block)) { + if (!ActivateBestChain(state, chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } @@ -1608,7 +1608,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId()); } } - const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); + const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); // disconnect node in case we have reached the outbound limit for serving historical blocks if (send && connman.OutboundTargetReached(true) && @@ -1718,7 +1718,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c } //! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). -CTransactionRef static FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main) +static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main) { auto txinfo = mempool.info(gtxid); if (txinfo.tx) { @@ -1751,7 +1751,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin(); std::vector<CInv> vNotFound; - const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); + const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); const std::chrono::seconds now = GetTime<std::chrono::seconds>(); // Get last mempool request time @@ -1774,7 +1774,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm continue; } - CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), mempool_req, now); + CTransactionRef tx = FindTxForGetData(mempool, pfrom, ToGenTxid(inv), mempool_req, now); if (tx) { // WTX and WITNESS_TX imply we serialize with witness int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0); @@ -1786,11 +1786,11 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm LOCK(mempool.cs); auto txiter = mempool.GetIter(tx->GetHash()); if (txiter) { - const CTxMemPool::setEntries& parents = mempool.GetMemPoolParents(*txiter); + const CTxMemPoolEntry::Parents& parents = (*txiter)->GetMemPoolParentsConst(); parent_ids_to_add.reserve(parents.size()); - for (CTxMemPool::txiter parent_iter : parents) { - if (parent_iter->GetTime() > now - UNCONDITIONAL_RELAY_DELAY) { - parent_ids_to_add.push_back(parent_iter->GetTx().GetHash()); + for (const CTxMemPoolEntry& parent : parents) { + if (parent.GetTime() > now - UNCONDITIONAL_RELAY_DELAY) { + parent_ids_to_add.push_back(parent.GetTx().GetHash()); } } } @@ -1847,7 +1847,7 @@ static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_ma return nFetchFlags; } -inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode& pfrom, CConnman& connman) { +void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req) { BlockTransactions resp(req); for (size_t i = 0; i < req.indexes.size(); i++) { if (req.indexes[i] >= block.vtx.size()) { @@ -1857,14 +1857,14 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac resp.txn[i] = block.vtx[req.indexes[i]]; } LOCK(cs_main); - const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); + const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); + m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } -static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block) +void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block) { - const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); + const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); size_t nCount = headers.size(); if (nCount == 0) { @@ -1888,7 +1888,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan // nUnconnectingHeaders gets reset back to 0. if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), @@ -1922,7 +1922,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan } BlockValidationState state; - if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) { + if (!m_chainman.ProcessNewBlockHeaders(headers, state, m_chainparams, &pindexLast)) { if (state.IsInvalid()) { MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received"); return; @@ -1953,10 +1953,10 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan // TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue // from there instead. LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight); - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256())); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256())); } - bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); + bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus()); // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) { @@ -1966,7 +1966,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && - (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) { + (!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) { // We don't have this block, and it's not yet in flight. vToFetch.push_back(pindexWalk); } @@ -1990,7 +1990,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan } uint32_t nFetchFlags = GetFetchFlags(pfrom); vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash())); - MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex); + MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex); LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", pindex->GetBlockHash().ToString(), pfrom.GetId()); } @@ -2003,7 +2003,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } - connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); } } } @@ -2045,13 +2045,20 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan return; } -void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans) +/** + * Reconsider orphan transactions after a parent has been accepted to the mempool. + * + * @param[in/out] orphan_work_set The set of orphan transactions to reconsider. Generally only one + * orphan will be reconsidered on each call of this function. This set + * may be added to if accepting an orphan causes its children to be + * reconsidered. + */ +void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set) { AssertLockHeld(cs_main); AssertLockHeld(g_cs_orphans); - std::set<NodeId> setMisbehaving; - bool done = false; - while (!done && !orphan_work_set.empty()) { + + while (!orphan_work_set.empty()) { const uint256 orphanHash = *orphan_work_set.begin(); orphan_work_set.erase(orphan_work_set.begin()); @@ -2059,18 +2066,13 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin if (orphan_it == mapOrphanTransactions.end()) continue; const CTransactionRef porphanTx = orphan_it->second.tx; - const CTransaction& orphanTx = *porphanTx; - NodeId fromPeer = orphan_it->second.fromPeer; - // Use a new TxValidationState because orphans come from different peers (and we call - // MaybePunishNodeForTx based on the source peer from the orphan map, not based on the peer - // that relayed the previous transaction). - TxValidationState orphan_state; - - if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { + TxValidationState state; + std::list<CTransactionRef> removed_txn; + + if (AcceptToMemoryPool(m_mempool, state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), connman); - for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { + RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_connman); + for (unsigned int i = 0; i < porphanTx->vout.size(); i++) { auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i)); if (it_by_prev != mapOrphanTransactionsByPrev.end()) { for (const auto& elem : it_by_prev->second) { @@ -2079,22 +2081,23 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin } } EraseOrphanTx(orphanHash); - done = true; - } else if (orphan_state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) { - if (orphan_state.IsInvalid()) { - // Punish peer that gave us an invalid orphan tx - if (MaybePunishNodeForTx(fromPeer, orphan_state)) { - setMisbehaving.insert(fromPeer); - } + for (const CTransactionRef& removedTx : removed_txn) { + AddToCompactExtraTransactions(removedTx); + } + break; + } else if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) { + if (state.IsInvalid()) { LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n", orphanHash.ToString(), - fromPeer, - orphan_state.ToString()); + orphan_it->second.fromPeer, + state.ToString()); + // Maybe punish peer that gave us an invalid orphan tx + MaybePunishNodeForTx(orphan_it->second.fromPeer, state); } // Has inputs but not accepted to mempool // Probably non-standard or insufficient fee LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString()); - if (orphan_state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) { + if (state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) { // We can add the wtxid of this transaction to our reject filter. // Do not add txids of witness transactions or witness-stripped // transactions to the filter, as they can have been malleated; @@ -2109,7 +2112,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin // for concerns around weakening security of unupgraded nodes // if we start doing this too early. assert(recentRejects); - recentRejects->insert(orphanTx.GetWitnessHash()); + recentRejects->insert(porphanTx->GetWitnessHash()); // If the transaction failed for TX_INPUTS_NOT_STANDARD, // then we know that the witness was irrelevant to the policy // failure, since this check depends only on the txid @@ -2118,17 +2121,17 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin // processing of this transaction in the event that child // transactions are later received (resulting in // parent-fetching by txid via the orphan-handling logic). - if (orphan_state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && orphanTx.GetWitnessHash() != orphanTx.GetHash()) { + if (state.GetResult() == TxValidationResult::TX_INPUTS_NOT_STANDARD && porphanTx->GetWitnessHash() != porphanTx->GetHash()) { // We only add the txid if it differs from the wtxid, to // avoid wasting entries in the rolling bloom filter. - recentRejects->insert(orphanTx.GetHash()); + recentRejects->insert(porphanTx->GetHash()); } } EraseOrphanTx(orphanHash); - done = true; + break; } - mempool.check(&::ChainstateActive().CoinsTip()); } + m_mempool.check(&::ChainstateActive().CoinsTip()); } /** @@ -2235,7 +2238,7 @@ static void ProcessGetCFilters(CNode& peer, CDataStream& vRecv, const CChainPara } for (const auto& filter : filters) { - CSerializedNetMsg msg = CNetMsgMaker(peer.GetSendVersion()) + CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion()) .Make(NetMsgType::CFILTER, filter); connman.PushMessage(&peer, std::move(msg)); } @@ -2287,7 +2290,7 @@ static void ProcessGetCFHeaders(CNode& peer, CDataStream& vRecv, const CChainPar return; } - CSerializedNetMsg msg = CNetMsgMaker(peer.GetSendVersion()) + CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion()) .Make(NetMsgType::CFHEADERS, filter_type_ser, stop_index->GetBlockHash(), @@ -2339,7 +2342,7 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar } } - CSerializedNetMsg msg = CNetMsgMaker(peer.GetSendVersion()) + CSerializedNetMsg msg = CNetMsgMaker(peer.GetCommonVersion()) .Make(NetMsgType::CFCHECKPT, filter_type_ser, stop_index->GetBlockHash(), @@ -2347,9 +2350,9 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar connman.PushMessage(&peer, std::move(msg)); } -void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, +void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv, const std::chrono::microseconds time_received, - const CChainParams& chainparams, const std::atomic<bool>& interruptMsgProc) + const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId()); if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) @@ -2374,13 +2377,11 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty uint64_t nServiceInt; ServiceFlags nServices; int nVersion; - int nSendVersion; std::string cleanSubVer; int nStartingHeight = -1; bool fRelay = true; vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; - nSendVersion = std::min(nVersion, PROTOCOL_VERSION); nServices = ServiceFlags(nServiceInt); if (!pfrom.IsInboundConn()) { @@ -2429,11 +2430,16 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty if (pfrom.IsInboundConn()) PushNodeVersion(pfrom, m_connman, GetAdjustedTime()); - if (nVersion >= WTXID_RELAY_VERSION) { - m_connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::WTXIDRELAY)); + // Change version + const int greatest_common_version = std::min(nVersion, PROTOCOL_VERSION); + pfrom.SetCommonVersion(greatest_common_version); + pfrom.nVersion = nVersion; + + if (greatest_common_version >= WTXID_RELAY_VERSION) { + m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::WTXIDRELAY)); } - m_connman.PushMessage(&pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::VERACK)); pfrom.nServices = nServices; pfrom.SetAddrLocal(addrMe); @@ -2454,10 +2460,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty pfrom.m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message } - // Change version - pfrom.SetSendVersion(nSendVersion); - pfrom.nVersion = nVersion; - if((nServices & NODE_WITNESS)) { LOCK(cs_main); @@ -2503,7 +2505,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } // Get recent addresses - m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR)); pfrom.fGetAddr = true; // Moves address from New to Tried table in Addrman, resolves @@ -2525,9 +2527,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty AddTimeData(pfrom.addr, nTimeOffset); // If the peer is old enough to have the old alert system, send it the final alert. - if (pfrom.nVersion <= 70012) { + if (greatest_common_version <= 70012) { CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION); - m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); + m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make("alert", finalAlert)); } // Feeler connections exist only to verify if address is online. @@ -2544,12 +2546,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } // At this point, the outgoing message serialization version can't change. - const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); + const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); if (msg_type == NetMsgType::VERACK) { - pfrom.SetRecvVersion(std::min(pfrom.nVersion.load(), PROTOCOL_VERSION)); - if (!pfrom.IsInboundConn()) { // Mark this node as currently connected, so we update its timestamp later. LOCK(cs_main); @@ -2560,14 +2560,14 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay"); } - if (pfrom.nVersion >= SENDHEADERS_VERSION) { + if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) { // Tell our peer we prefer to receive headers rather than inv's // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); } - if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) { + if (pfrom.GetCommonVersion() >= SHORT_IDS_BLOCKS_VERSION) { // Tell our peer we are willing to provide version 1 or 2 cmpctblocks // However, we do not request new block announcements using // cmpctblock messages. @@ -2593,7 +2593,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty pfrom.fDisconnect = true; return; } - if (pfrom.nVersion >= WTXID_RELAY_VERSION) { + if (pfrom.GetCommonVersion() >= WTXID_RELAY_VERSION) { LOCK(cs_main); if (!State(pfrom.GetId())->m_wtxid_relay) { State(pfrom.GetId())->m_wtxid_relay = true; @@ -2741,7 +2741,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } } else if (inv.IsGenTxMsg()) { const GenTxid gtxid = ToGenTxid(inv); - const bool fAlreadyHave = AlreadyHaveTx(gtxid, mempool); + const bool fAlreadyHave = AlreadyHaveTx(gtxid, m_mempool); LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId()); pfrom.AddKnownTx(inv.hash); @@ -2781,7 +2781,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom, chainparams, m_connman, m_mempool, interruptMsgProc); + ProcessGetData(pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc); return; } @@ -2810,7 +2810,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty a_recent_block = most_recent_block; } BlockValidationState state; - if (!ActivateBestChain(state, Params(), a_recent_block)) { + if (!ActivateBestChain(state, m_chainparams, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString()); } } @@ -2834,7 +2834,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } // If pruning, don't inv blocks unless we have on disk and are likely to still have // for some reasonable time window (1 hour) that block relay might require. - const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing; + const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing; if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave)) { LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); @@ -2865,7 +2865,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty // Unlock cs_most_recent_block to avoid cs_main lock inversion } if (recent_block) { - SendBlockTransactions(*recent_block, req, pfrom, m_connman); + SendBlockTransactions(pfrom, *recent_block, req); return; } @@ -2895,10 +2895,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } CBlock block; - bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()); + bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus()); assert(ret); - SendBlockTransactions(block, req, pfrom, m_connman); + SendBlockTransactions(pfrom, block, req); return; } @@ -2929,7 +2929,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty return; } - if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) { + if (!BlockRequestAllowed(pindex, m_chainparams.GetConsensus())) { LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId()); return; } @@ -3012,7 +3012,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty std::list<CTransactionRef> lRemovedTxn; - // We do the AlreadyHave() check using wtxid, rather than txid - in the + // We do the AlreadyHaveTx() check using wtxid, rather than txid - in the // absence of witness malleation, this is strictly better, because the // recent rejects filter may contain the wtxid but rarely contains // the txid of a segwit transaction that has been rejected. @@ -3044,8 +3044,12 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty tx.GetHash().ToString(), m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000); + for (const CTransactionRef& removedTx : lRemovedTxn) { + AddToCompactExtraTransactions(removedTx); + } + // Recursively process any orphan transactions that depended on this one - ProcessOrphanTx(m_connman, m_mempool, pfrom.orphan_work_set, lRemovedTxn); + ProcessOrphanTx(pfrom.orphan_work_set); } else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { @@ -3130,8 +3134,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty if (RecursiveDynamicUsage(*ptx) < 100000) { AddToCompactExtraTransactions(ptx); } - } else if (tx.HasWitness() && RecursiveDynamicUsage(*ptx) < 100000) { - AddToCompactExtraTransactions(ptx); } if (pfrom.HasPermission(PF_FORCERELAY)) { @@ -3148,9 +3150,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } } - for (const CTransactionRef& removedTx : lRemovedTxn) - AddToCompactExtraTransactions(removedTx); - // If a tx has been detected by recentRejects, we will have reached // this point and the tx will have been ignored. Because we haven't run // the tx through AcceptToMemoryPool, we won't have computed a DoS @@ -3207,7 +3206,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty const CBlockIndex *pindex = nullptr; BlockValidationState state; - if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) { + if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, m_chainparams, &pindex)) { if (state.IsInvalid()) { MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock"); return; @@ -3263,10 +3262,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } // If we're not close to tip yet, give up and let parallel block fetch work its magic - if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus())) + if (!fAlreadyInFlight && !CanDirectFetch(m_chainparams.GetConsensus())) return; - if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) { + if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) { // Don't bother trying to process compact blocks from v1 peers // after segwit activates. return; @@ -3350,8 +3349,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } } // cs_main - if (fProcessBLOCKTXN) - return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, interruptMsgProc); + if (fProcessBLOCKTXN) { + return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, interruptMsgProc); + } if (fRevertToHeaderProcessing) { // Headers received from HB compact block peers are permitted to be @@ -3359,7 +3359,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty // the peer if the header turns out to be for an invalid block. // Note that if a peer tries to build on an invalid chain, that // will be detected and the peer will be disconnected/discouraged. - return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true); + return ProcessHeadersMessage(pfrom, {cmpctblock.header}, /*via_compact_block=*/true); } if (fBlockReconstructed) { @@ -3379,7 +3379,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty // we have a chain with at least nMinimumChainWork), and we ignore // compact blocks with less work than our tip, it is safe to treat // reconstructed compact blocks as having been requested. - m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); + m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom.nLastBlockTime = GetTime(); } else { @@ -3469,7 +3469,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty // disk-space attacks), but this should be safe due to the // protections in the compact block handler -- see related comment // in compact block optimistic reconstruction handling. - m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); + m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock); if (fNewBlock) { pfrom.nLastBlockTime = GetTime(); } else { @@ -3502,7 +3502,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, headers, chainparams, /*via_compact_block=*/false); + return ProcessHeadersMessage(pfrom, headers, /*via_compact_block=*/false); } if (msg_type == NetMsgType::BLOCK) @@ -3531,7 +3531,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true)); } bool fNewBlock = false; - m_chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock); + m_chainman.ProcessNewBlock(m_chainparams, pblock, forceProcessing, &fNewBlock); if (fNewBlock) { pfrom.nLastBlockTime = GetTime(); } else { @@ -3548,11 +3548,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty // Making nodes which are behind NAT and can only make outgoing connections ignore // the getaddr message mitigates the attack. if (!pfrom.IsInboundConn()) { - LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId()); - return; - } - if (!pfrom.RelayAddrsWithConn()) { - LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId()); + LogPrint(BCLog::NET, "Ignoring \"getaddr\" from %s connection. peer=%d\n", pfrom.ConnectionTypeAsString(), pfrom.GetId()); return; } @@ -3569,7 +3565,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty if (pfrom.HasPermission(PF_ADDR)) { vAddr = m_connman.GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); } else { - vAddr = m_connman.GetAddresses(pfrom.addr.GetNetwork(), MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); + vAddr = m_connman.GetAddresses(pfrom, MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND); } FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) { @@ -3607,8 +3603,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } if (msg_type == NetMsgType::PING) { - if (pfrom.nVersion > BIP0031_VERSION) - { + if (pfrom.GetCommonVersion() > BIP0031_VERSION) { uint64_t nonce = 0; vRecv >> nonce; // Echo the message back with the nonce. This allows for two useful features: @@ -3760,17 +3755,17 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty } if (msg_type == NetMsgType::GETCFILTERS) { - ProcessGetCFilters(pfrom, vRecv, chainparams, m_connman); + ProcessGetCFilters(pfrom, vRecv, m_chainparams, m_connman); return; } if (msg_type == NetMsgType::GETCFHEADERS) { - ProcessGetCFHeaders(pfrom, vRecv, chainparams, m_connman); + ProcessGetCFHeaders(pfrom, vRecv, m_chainparams, m_connman); return; } if (msg_type == NetMsgType::GETCFCHECKPT) { - ProcessGetCFCheckPt(pfrom, vRecv, chainparams, m_connman); + ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman); return; } @@ -3804,12 +3799,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty return; } -/** 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 PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode) +bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode) { const NodeId peer_id{pnode.GetId()}; PeerRef peer = GetPeerRef(peer_id); @@ -3851,29 +3841,16 @@ bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode) return true; } -bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) +bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) { - const CChainParams& chainparams = Params(); - // - // Message format - // (4) message start - // (12) command - // (4) size - // (4) checksum - // (x) data - // bool fMoreWork = false; if (!pfrom->vRecvGetData.empty()) - ProcessGetData(*pfrom, chainparams, m_connman, m_mempool, interruptMsgProc); + ProcessGetData(*pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc); if (!pfrom->orphan_work_set.empty()) { - std::list<CTransactionRef> removed_txn; LOCK2(cs_main, g_cs_orphans); - ProcessOrphanTx(m_connman, m_mempool, pfrom->orphan_work_set, removed_txn); - for (const CTransactionRef& removedTx : removed_txn) { - AddToCompactExtraTransactions(removedTx); - } + ProcessOrphanTx(pfrom->orphan_work_set); } if (pfrom->fDisconnect) @@ -3901,36 +3878,14 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter } CNetMessage& msg(msgs.front()); - msg.SetVersion(pfrom->GetRecvVersion()); - // Check network magic - if (!msg.m_valid_netmagic) { - LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId()); - pfrom->fDisconnect = true; - return false; - } - - // Check header - if (!msg.m_valid_header) - { - LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId()); - return fMoreWork; - } + msg.SetVersion(pfrom->GetCommonVersion()); const std::string& msg_type = msg.m_command; // Message size unsigned int nMessageSize = msg.m_message_size; - // Checksum - CDataStream& vRecv = msg.m_recv; - if (!msg.m_valid_checksum) - { - LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR peer=%d\n", __func__, - SanitizeString(msg_type), nMessageSize, pfrom->GetId()); - return fMoreWork; - } - try { - ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, interruptMsgProc); + ProcessMessage(*pfrom, msg_type, msg.m_recv, msg.m_time, interruptMsgProc); if (interruptMsgProc) return false; if (!pfrom->vRecvGetData.empty()) @@ -3944,12 +3899,12 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter return fMoreWork; } -void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds) +void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) { AssertLockHeld(cs_main); CNodeState &state = *State(pto.GetId()); - const CNetMsgMaker msgMaker(pto.GetSendVersion()); + const CNetMsgMaker msgMaker(pto.GetCommonVersion()); if (!state.m_chain_sync.m_protect && pto.IsOutboundOrBlockRelayConn() && state.fSyncStarted) { // This is an outbound peer subject to disconnection if they don't @@ -3997,7 +3952,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds) } } -void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) +void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) { // Check whether we have too many outbound peers int extra_peers = m_connman.GetExtraOutboundCount(); @@ -4009,8 +3964,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) NodeId worst_peer = -1; int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); - m_connman.ForEachNode([&](CNode* pnode) { - LockAssertion lock(::cs_main); + m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + AssertLockHeld(::cs_main); // Ignore non-outbound peers, or nodes marked for disconnect already if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return; @@ -4026,8 +3981,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) } }); if (worst_peer != -1) { - bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) { - LockAssertion lock(::cs_main); + bool disconnected = m_connman.ForNode(worst_peer, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { + AssertLockHeld(::cs_main); // Only disconnect a peer that has been connected to us for // some reasonable fraction of our check-frequency, to give @@ -4056,7 +4011,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) } } -void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams) +void PeerManager::CheckForStaleTipAndEvictPeers() { LOCK(cs_main); @@ -4067,7 +4022,7 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params if (time_in_seconds > m_stale_tip_check_time) { // Check whether our tip is stale, and if so, allow using an extra // outbound peer - if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) { + if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(m_chainparams.GetConsensus())) { LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update); m_connman.SetTryNewOutboundPeer(true); } else if (m_connman.GetTryNewOutboundPeer()) { @@ -4098,9 +4053,9 @@ public: }; } -bool PeerLogicValidation::SendMessages(CNode* pto) +bool PeerManager::SendMessages(CNode* pto) { - const Consensus::Params& consensusParams = Params().GetConsensus(); + const Consensus::Params& consensusParams = m_chainparams.GetConsensus(); // We must call MaybeDiscourageAndDisconnect first, to ensure that we'll // disconnect misbehaving peers even before the version handshake is complete. @@ -4111,7 +4066,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) return true; // If we get here, the outgoing message serialization version is set and can't change. - const CNetMsgMaker msgMaker(pto->GetSendVersion()); + const CNetMsgMaker msgMaker(pto->GetCommonVersion()); // // Message: ping @@ -4132,7 +4087,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) } pto->fPingQueued = false; pto->m_ping_start = GetTime<std::chrono::microseconds>(); - if (pto->nVersion > BIP0031_VERSION) { + if (pto->GetCommonVersion() > BIP0031_VERSION) { pto->nPingNonceSent = nonce; m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); } else { @@ -4671,7 +4626,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // // Message: feefilter // - if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && + if (pto->m_tx_relay != nullptr && pto->GetCommonVersion() >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && !pto->HasPermission(PF_FORCERELAY) // peers with the forcerelay permission should not filter txs to us ) { CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); |