Skip to content

Commit

Permalink
Merge pull request #227 from vertiond/23.x-headers
Browse files Browse the repository at this point in the history
23.x rebase: headers
  • Loading branch information
vertiond authored Jul 21, 2023
2 parents 7a53ed0 + c323991 commit 3697037
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3030,6 +3030,8 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s
m_conn_type(conn_type_in),
nLocalServices(nLocalServicesIn)
{
nPendingHeaderRequests = 0;

if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
if (conn_type_in != ConnectionType::BLOCK_RELAY) {
m_tx_relay = std::make_unique<TxRelay>();
Expand Down
5 changes: 4 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,10 @@ class CNode
std::chrono::microseconds m_next_send_feefilter{0};
};

// m_tx_relay == nullptr if we're not relaying transactions with this peer
// Counts getheaders requests sent to this peer
std::atomic<int64_t> nPendingHeaderRequests;

// m_tx_relay == nullptr if we're not relaying transactions with this peer
std::unique_ptr<TxRelay> m_tx_relay;

/** UNIX epoch time of the last block received from this peer that we had
Expand Down
81 changes: 74 additions & 7 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ class PeerManagerImpl final : public PeerManager
void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) override;
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override;
void RequestHeadersFrom(CNode& pto, CConnman& connman, const CBlockIndex* pindex, uint256 untilHash, bool fforceQuery);

private:
void _RelayTransaction(const uint256& txid, const uint256& wtxid)
Expand Down Expand Up @@ -771,6 +772,8 @@ struct CNodeState {
bool m_protect{false};
};

int8_t nDuplicateHeaderRequests;

ChainSyncTimeoutState m_chain_sync;

//! Time of last new block announcement
Expand Down Expand Up @@ -1130,6 +1133,26 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count

} // namespace

// Do not request headers from a peer we are
// already requesting headers from, unless forced.
void PeerManagerImpl::RequestHeadersFrom(CNode& pto, CConnman& connman, const CBlockIndex* pindex, uint256 untilHash, bool fforceQuery)
{
if (pto.nPendingHeaderRequests > 0) {
if (fforceQuery) {
LogPrint(BCLog::NET, "forcing getheaders request (%d) to peer=%d (%d open)\n",
pindex->nHeight, pto.GetId(), pto.nPendingHeaderRequests);
} else {
LogPrint(BCLog::NET, "dropped getheaders request (%d) to peer=%d\n", pindex->nHeight, pto.GetId());
return;
}
}
const CNetMsgMaker msgMaker(pto.GetCommonVersion());
LogPrint(BCLog::NET, "getheaders request (%d) to peer=%d (%d open)\n", pindex->nHeight, pto.GetId(), pto.nPendingHeaderRequests);
connman.PushMessage(&pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindex), untilHash));
pto.nPendingHeaderRequests += 1;

}

void PeerManagerImpl::PushNodeVersion(CNode& pnode)
{
// Note that pnode->GetLocalServices() is a reflection of the local
Expand Down Expand Up @@ -2140,7 +2163,8 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// nUnconnectingHeaders gets reset back to 0.
if (!m_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
// Allow a single getheaders query before triggering DoS
RequestHeadersFrom(pfrom, m_connman, pindexBestHeader, uint256(), true);
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(),
Expand Down Expand Up @@ -2204,9 +2228,13 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
// Headers message had its maximum size; the peer may have more headers.
// TODO: optimize: if pindexLast is an ancestor of m_chainman.ActiveChain().Tip or pindexBestHeader, continue
// from there instead.
//
// Do not allow multiple getheader queries in parallel at
// this point - makes sure that any parallel queries will end here,
// preventing "getheaders" spam.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n",
pindexLast->nHeight, pfrom.GetId(), peer.m_starting_height);
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexLast), uint256()));
RequestHeadersFrom(pfrom, m_connman, pindexLast, uint256(), false);
}

// If this set of headers is valid and ends in a block with at least as
Expand Down Expand Up @@ -3072,7 +3100,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}

if (best_block != nullptr) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), *best_block));
// We force this check, in case we're only connected to nodes that send invs
RequestHeadersFrom(pfrom, m_connman, pindexBestHeader, *best_block, true);
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, best_block->ToString(), pfrom.GetId());
}

Expand Down Expand Up @@ -3271,6 +3300,35 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)
break;
}

// Pre-Lyra2REv3 nodes are still active and keep requesting blocks
// that do have a common ancestor to our chain - and thus we keep
// sending them leading to huge amounts of data being transfered
// in vain. We should prevent sending duplicate header chunks over
// and over. So if we're sending MAX_HEADERS_RESULTS headers of which
// the last one we already sent, we're treating it as a duplicate request.
// A node should get 3 strikes and then the ban score gets increased
bool bDuplicate = false;
if(nLimit <= 0) {
if (pindex == nodestate->pindexBestHeaderSent) {
LogPrint(BCLog::NET, "getheaders already sent peer=%d\n", pfrom.GetId());
bDuplicate = true;
} else if (nodestate->pindexBestHeaderSent) {
if (pindex && (nodestate->pindexBestHeaderSent->GetAncestor(pindex->nHeight) == pindex)) {
LogPrint(BCLog::NET, "getheaders before best index sent from peer=%d\n", pfrom.GetId());
bDuplicate = true;
}
}
}
if (bDuplicate) {
nodestate->nDuplicateHeaderRequests++;
LogPrintf("getheaders peer %d sent a duplicate request (happened %d times)\n", pfrom.GetId(), nodestate->nDuplicateHeaderRequests);
if(nodestate->nDuplicateHeaderRequests >= 3) {
Misbehaving(pfrom.GetId(), (nodestate->nDuplicateHeaderRequests-2) * 10, "Duplicate header requests");
}
}


// pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR
// if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty
// headers message). In both cases it's safe to update
Expand Down Expand Up @@ -3521,8 +3579,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,

if (!m_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload())
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexBestHeader), uint256()));
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
RequestHeadersFrom(pfrom, m_connman, pindexBestHeader, uint256(), true);
}
return;
}

Expand Down Expand Up @@ -3804,7 +3863,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,

std::vector<CBlockHeader> headers;

// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
if (pfrom.nPendingHeaderRequests > 0) {
pfrom.nPendingHeaderRequests -= 1;
}

// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
Expand Down Expand Up @@ -4659,10 +4722,14 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
the peer's known best block. This wouldn't be possible
if we requested starting at pindexBestHeader and
got back an empty response. */

// Make sure that if we are already processing an inv
// or header message from this peer caused by a new block being
// mined at chaintip, we do not send another getheaders request
if (pindexStart->pprev)
pindexStart = pindexStart->pprev;
LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), peer->m_starting_height);
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, m_chainman.ActiveChain().GetLocator(pindexStart), uint256()));
RequestHeadersFrom(*pto, m_connman, pindexStart, uint256(), false);
}
}

Expand Down

0 comments on commit 3697037

Please sign in to comment.