Skip to content

Commit fc966bb

Browse files
sdaftuarMarcoFalke
authored andcommitted
moveonly: factor out headers processing into separate function
ProcessMessages will now return earlier when processing headers messages, rather than continuing on (and do nothing). Github-Pull: #11568 Rebased-From: 4637f18
1 parent e327224 commit fc966bb

File tree

1 file changed

+173
-163
lines changed

1 file changed

+173
-163
lines changed

src/net_processing.cpp

Lines changed: 173 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,178 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
11931193
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
11941194
}
11951195

1196+
bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams)
1197+
{
1198+
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
1199+
size_t nCount = headers.size();
1200+
1201+
if (nCount == 0) {
1202+
// Nothing interesting. Stop asking this peers for more headers.
1203+
return true;
1204+
}
1205+
1206+
const CBlockIndex *pindexLast = nullptr;
1207+
{
1208+
LOCK(cs_main);
1209+
CNodeState *nodestate = State(pfrom->GetId());
1210+
1211+
// If this looks like it could be a block announcement (nCount <
1212+
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
1213+
// don't connect:
1214+
// - Send a getheaders message in response to try to connect the chain.
1215+
// - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
1216+
// don't connect before giving DoS points
1217+
// - Once a headers message is received that is valid and does connect,
1218+
// nUnconnectingHeaders gets reset back to 0.
1219+
if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
1220+
nodestate->nUnconnectingHeaders++;
1221+
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
1222+
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
1223+
headers[0].GetHash().ToString(),
1224+
headers[0].hashPrevBlock.ToString(),
1225+
pindexBestHeader->nHeight,
1226+
pfrom->GetId(), nodestate->nUnconnectingHeaders);
1227+
// Set hashLastUnknownBlock for this peer, so that if we
1228+
// eventually get the headers - even from a different peer -
1229+
// we can use this peer to download.
1230+
UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
1231+
1232+
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
1233+
Misbehaving(pfrom->GetId(), 20);
1234+
}
1235+
return true;
1236+
}
1237+
1238+
uint256 hashLastBlock;
1239+
for (const CBlockHeader& header : headers) {
1240+
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
1241+
Misbehaving(pfrom->GetId(), 20);
1242+
return error("non-continuous headers sequence");
1243+
}
1244+
hashLastBlock = header.GetHash();
1245+
}
1246+
}
1247+
1248+
CValidationState state;
1249+
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
1250+
int nDoS;
1251+
if (state.IsInvalid(nDoS)) {
1252+
if (nDoS > 0) {
1253+
LOCK(cs_main);
1254+
Misbehaving(pfrom->GetId(), nDoS);
1255+
}
1256+
return error("invalid header received");
1257+
}
1258+
}
1259+
1260+
{
1261+
LOCK(cs_main);
1262+
CNodeState *nodestate = State(pfrom->GetId());
1263+
if (nodestate->nUnconnectingHeaders > 0) {
1264+
LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
1265+
}
1266+
nodestate->nUnconnectingHeaders = 0;
1267+
1268+
assert(pindexLast);
1269+
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
1270+
1271+
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
1272+
// because it is set in UpdateBlockAvailability. Some nullptr checks
1273+
// are still present, however, as belt-and-suspenders.
1274+
1275+
if (nCount == MAX_HEADERS_RESULTS) {
1276+
// Headers message had its maximum size; the peer may have more headers.
1277+
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
1278+
// from there instead.
1279+
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
1280+
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
1281+
}
1282+
1283+
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
1284+
// If this set of headers is valid and ends in a block with at least as
1285+
// much work as our tip, download as much as possible.
1286+
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
1287+
std::vector<const CBlockIndex*> vToFetch;
1288+
const CBlockIndex *pindexWalk = pindexLast;
1289+
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
1290+
while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
1291+
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
1292+
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
1293+
(!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
1294+
// We don't have this block, and it's not yet in flight.
1295+
vToFetch.push_back(pindexWalk);
1296+
}
1297+
pindexWalk = pindexWalk->pprev;
1298+
}
1299+
// If pindexWalk still isn't on our main chain, we're looking at a
1300+
// very large reorg at a time we think we're close to caught up to
1301+
// the main chain -- this shouldn't really happen. Bail out on the
1302+
// direct fetch and rely on parallel download instead.
1303+
if (!chainActive.Contains(pindexWalk)) {
1304+
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
1305+
pindexLast->GetBlockHash().ToString(),
1306+
pindexLast->nHeight);
1307+
} else {
1308+
std::vector<CInv> vGetData;
1309+
// Download as much as possible, from earliest to latest.
1310+
for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
1311+
if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
1312+
// Can't download any more from this peer
1313+
break;
1314+
}
1315+
uint32_t nFetchFlags = GetFetchFlags(pfrom);
1316+
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
1317+
MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex);
1318+
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
1319+
pindex->GetBlockHash().ToString(), pfrom->GetId());
1320+
}
1321+
if (vGetData.size() > 1) {
1322+
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
1323+
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
1324+
}
1325+
if (vGetData.size() > 0) {
1326+
if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
1327+
// In any case, we want to download using a compact block, not a regular one
1328+
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
1329+
}
1330+
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
1331+
}
1332+
}
1333+
}
1334+
// If we're in IBD, we want outbound peers that will serve us a useful
1335+
// chain. Disconnect peers that are on chains with insufficient work.
1336+
if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
1337+
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
1338+
// headers to fetch from this peer.
1339+
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
1340+
// This peer has too little work on their headers chain to help
1341+
// us sync -- disconnect if using an outbound slot (unless
1342+
// whitelisted or addnode).
1343+
// Note: We compare their tip to nMinimumChainWork (rather than
1344+
// chainActive.Tip()) because we won't start block download
1345+
// until we have a headers chain that has at least
1346+
// nMinimumChainWork, even if a peer has a chain past our tip,
1347+
// as an anti-DoS measure.
1348+
if (IsOutboundDisconnectionCandidate(pfrom)) {
1349+
LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId());
1350+
pfrom->fDisconnect = true;
1351+
}
1352+
}
1353+
}
1354+
1355+
if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) {
1356+
// If this is an outbound peer, check to see if we should protect
1357+
// it from the bad/lagging chain logic.
1358+
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
1359+
nodestate->m_chain_sync.m_protect = true;
1360+
++g_outbound_peers_with_protect_from_disconnect;
1361+
}
1362+
}
1363+
}
1364+
1365+
return true;
1366+
}
1367+
11961368
bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc)
11971369
{
11981370
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
@@ -2286,169 +2458,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
22862458
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
22872459
}
22882460

2289-
if (nCount == 0) {
2290-
// Nothing interesting. Stop asking this peers for more headers.
2291-
return true;
2292-
}
2293-
2294-
const CBlockIndex *pindexLast = nullptr;
2295-
{
2296-
LOCK(cs_main);
2297-
CNodeState *nodestate = State(pfrom->GetId());
2298-
2299-
// If this looks like it could be a block announcement (nCount <
2300-
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that
2301-
// don't connect:
2302-
// - Send a getheaders message in response to try to connect the chain.
2303-
// - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that
2304-
// don't connect before giving DoS points
2305-
// - Once a headers message is received that is valid and does connect,
2306-
// nUnconnectingHeaders gets reset back to 0.
2307-
if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
2308-
nodestate->nUnconnectingHeaders++;
2309-
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256()));
2310-
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
2311-
headers[0].GetHash().ToString(),
2312-
headers[0].hashPrevBlock.ToString(),
2313-
pindexBestHeader->nHeight,
2314-
pfrom->GetId(), nodestate->nUnconnectingHeaders);
2315-
// Set hashLastUnknownBlock for this peer, so that if we
2316-
// eventually get the headers - even from a different peer -
2317-
// we can use this peer to download.
2318-
UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash());
2319-
2320-
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == 0) {
2321-
Misbehaving(pfrom->GetId(), 20);
2322-
}
2323-
return true;
2324-
}
2325-
2326-
uint256 hashLastBlock;
2327-
for (const CBlockHeader& header : headers) {
2328-
if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) {
2329-
Misbehaving(pfrom->GetId(), 20);
2330-
return error("non-continuous headers sequence");
2331-
}
2332-
hashLastBlock = header.GetHash();
2333-
}
2334-
}
2335-
2336-
CValidationState state;
2337-
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
2338-
int nDoS;
2339-
if (state.IsInvalid(nDoS)) {
2340-
if (nDoS > 0) {
2341-
LOCK(cs_main);
2342-
Misbehaving(pfrom->GetId(), nDoS);
2343-
}
2344-
return error("invalid header received");
2345-
}
2346-
}
2347-
2348-
{
2349-
LOCK(cs_main);
2350-
CNodeState *nodestate = State(pfrom->GetId());
2351-
if (nodestate->nUnconnectingHeaders > 0) {
2352-
LogPrint(BCLog::NET, "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", pfrom->GetId(), nodestate->nUnconnectingHeaders);
2353-
}
2354-
nodestate->nUnconnectingHeaders = 0;
2355-
2356-
assert(pindexLast);
2357-
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash());
2358-
2359-
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
2360-
// because it is set in UpdateBlockAvailability. Some nullptr checks
2361-
// are still present, however, as belt-and-suspenders.
2362-
2363-
if (nCount == MAX_HEADERS_RESULTS) {
2364-
// Headers message had its maximum size; the peer may have more headers.
2365-
// TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue
2366-
// from there instead.
2367-
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight);
2368-
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()));
2369-
}
2370-
2371-
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
2372-
// If this set of headers is valid and ends in a block with at least as
2373-
// much work as our tip, download as much as possible.
2374-
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
2375-
std::vector<const CBlockIndex*> vToFetch;
2376-
const CBlockIndex *pindexWalk = pindexLast;
2377-
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
2378-
while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
2379-
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
2380-
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
2381-
(!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
2382-
// We don't have this block, and it's not yet in flight.
2383-
vToFetch.push_back(pindexWalk);
2384-
}
2385-
pindexWalk = pindexWalk->pprev;
2386-
}
2387-
// If pindexWalk still isn't on our main chain, we're looking at a
2388-
// very large reorg at a time we think we're close to caught up to
2389-
// the main chain -- this shouldn't really happen. Bail out on the
2390-
// direct fetch and rely on parallel download instead.
2391-
if (!chainActive.Contains(pindexWalk)) {
2392-
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
2393-
pindexLast->GetBlockHash().ToString(),
2394-
pindexLast->nHeight);
2395-
} else {
2396-
std::vector<CInv> vGetData;
2397-
// Download as much as possible, from earliest to latest.
2398-
for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) {
2399-
if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
2400-
// Can't download any more from this peer
2401-
break;
2402-
}
2403-
uint32_t nFetchFlags = GetFetchFlags(pfrom);
2404-
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
2405-
MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), pindex);
2406-
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
2407-
pindex->GetBlockHash().ToString(), pfrom->GetId());
2408-
}
2409-
if (vGetData.size() > 1) {
2410-
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
2411-
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
2412-
}
2413-
if (vGetData.size() > 0) {
2414-
if (nodestate->fSupportsDesiredCmpctVersion && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
2415-
// In any case, we want to download using a compact block, not a regular one
2416-
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
2417-
}
2418-
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
2419-
}
2420-
}
2421-
}
2422-
// If we're in IBD, we want outbound peers that will serve us a useful
2423-
// chain. Disconnect peers that are on chains with insufficient work.
2424-
if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
2425-
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
2426-
// headers to fetch from this peer.
2427-
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
2428-
// This peer has too little work on their headers chain to help
2429-
// us sync -- disconnect if using an outbound slot (unless
2430-
// whitelisted or addnode).
2431-
// Note: We compare their tip to nMinimumChainWork (rather than
2432-
// chainActive.Tip()) because we won't start block download
2433-
// until we have a headers chain that has at least
2434-
// nMinimumChainWork, even if a peer has a chain past our tip,
2435-
// as an anti-DoS measure.
2436-
if (IsOutboundDisconnectionCandidate(pfrom)) {
2437-
LogPrintf("Disconnecting outbound peer %d -- headers chain has insufficient work\n", pfrom->GetId());
2438-
pfrom->fDisconnect = true;
2439-
}
2440-
}
2441-
}
2442-
2443-
if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) {
2444-
// If this is an outbound peer, check to see if we should protect
2445-
// it from the bad/lagging chain logic.
2446-
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
2447-
nodestate->m_chain_sync.m_protect = true;
2448-
++g_outbound_peers_with_protect_from_disconnect;
2449-
}
2450-
}
2451-
}
2461+
return ProcessHeadersMessage(pfrom, connman, headers, chainparams);
24522462
}
24532463

24542464
else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing

0 commit comments

Comments
 (0)