@@ -1205,7 +1205,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
1205
1205
connman->PushMessage (pfrom, msgMaker.Make (nSendFlags, NetMsgType::BLOCKTXN, resp));
1206
1206
}
1207
1207
1208
- bool static ProcessHeadersMessage (CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams)
1208
+ bool static ProcessHeadersMessage (CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool punish_duplicate_invalid )
1209
1209
{
1210
1210
const CNetMsgMaker msgMaker (pfrom->GetSendVersion ());
1211
1211
size_t nCount = headers.size ();
@@ -1258,13 +1258,48 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
1258
1258
}
1259
1259
1260
1260
CValidationState state;
1261
- if (!ProcessNewBlockHeaders (headers, state, chainparams, &pindexLast)) {
1261
+ CBlockHeader first_invalid_header;
1262
+ if (!ProcessNewBlockHeaders (headers, state, chainparams, &pindexLast, &first_invalid_header)) {
1262
1263
int nDoS;
1263
1264
if (state.IsInvalid (nDoS)) {
1264
1265
if (nDoS > 0 ) {
1265
1266
LOCK (cs_main);
1266
1267
Misbehaving (pfrom->GetId (), nDoS);
1267
1268
}
1269
+ if (punish_duplicate_invalid && mapBlockIndex.find (first_invalid_header.GetHash ()) != mapBlockIndex.end ()) {
1270
+ // Goal: don't allow outbound peers to use up our outbound
1271
+ // connection slots if they are on incompatible chains.
1272
+ //
1273
+ // We ask the caller to set punish_invalid appropriately based
1274
+ // on the peer and the method of header delivery (compact
1275
+ // blocks are allowed to be invalid in some circumstances,
1276
+ // under BIP 152).
1277
+ // Here, we try to detect the narrow situation that we have a
1278
+ // valid block header (ie it was valid at the time the header
1279
+ // was received, and hence stored in mapBlockIndex) but know the
1280
+ // block is invalid, and that a peer has announced that same
1281
+ // block as being on its active chain.
1282
+ // Disconnect the peer in such a situation.
1283
+ //
1284
+ // Note: if the header that is invalid was not accepted to our
1285
+ // mapBlockIndex at all, that may also be grounds for
1286
+ // disconnecting the peer, as the chain they are on is likely
1287
+ // to be incompatible. However, there is a circumstance where
1288
+ // that does not hold: if the header's timestamp is more than
1289
+ // 2 hours ahead of our current time. In that case, the header
1290
+ // may become valid in the future, and we don't want to
1291
+ // disconnect a peer merely for serving us one too-far-ahead
1292
+ // block header, to prevent an attacker from splitting the
1293
+ // network by mining a block right at the 2 hour boundary.
1294
+ //
1295
+ // TODO: update the DoS logic (or, rather, rewrite the
1296
+ // DoS-interface between validation and net_processing) so that
1297
+ // the interface is cleaner, and so that we disconnect on all the
1298
+ // reasons that a peer's headers chain is incompatible
1299
+ // with ours (eg block->nVersion softforks, MTP violations,
1300
+ // etc), and not just the duplicate-invalid case.
1301
+ pfrom->fDisconnect = true ;
1302
+ }
1268
1303
return error (" invalid header received" );
1269
1304
}
1270
1305
}
@@ -2219,7 +2254,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2219
2254
// If we end up treating this as a plain headers message, call that as well
2220
2255
// without cs_main.
2221
2256
bool fRevertToHeaderProcessing = false ;
2222
- CDataStream vHeadersMsg (SER_NETWORK, PROTOCOL_VERSION);
2223
2257
2224
2258
// Keep a CBlock for "optimistic" compactblock reconstructions (see
2225
2259
// below)
@@ -2336,10 +2370,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2336
2370
return true ;
2337
2371
} else {
2338
2372
// If this was an announce-cmpctblock, we want the same treatment as a header message
2339
- // Dirty hack to process as if it were just a headers message (TODO: move message handling into their own functions)
2340
- std::vector<CBlock> headers;
2341
- headers.push_back (cmpctblock.header );
2342
- vHeadersMsg << headers;
2343
2373
fRevertToHeaderProcessing = true ;
2344
2374
}
2345
2375
}
@@ -2348,8 +2378,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2348
2378
if (fProcessBLOCKTXN )
2349
2379
return ProcessMessage (pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
2350
2380
2351
- if (fRevertToHeaderProcessing )
2352
- return ProcessMessage (pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman, interruptMsgProc);
2381
+ if (fRevertToHeaderProcessing ) {
2382
+ // Headers received from HB compact block peers are permitted to be
2383
+ // relayed before full validation (see BIP 152), so we don't want to disconnect
2384
+ // the peer if the header turns out to be for an invalid block.
2385
+ // Note that if a peer tries to build on an invalid chain, that
2386
+ // will be detected and the peer will be banned.
2387
+ return ProcessHeadersMessage (pfrom, connman, {cmpctblock.header }, chainparams, /* punish_duplicate_invalid=*/ false );
2388
+ }
2353
2389
2354
2390
if (fBlockReconstructed ) {
2355
2391
// If we got here, we were able to optimistically reconstruct a
@@ -2480,7 +2516,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2480
2516
ReadCompactSize (vRecv); // ignore tx count; assume it is 0.
2481
2517
}
2482
2518
2483
- return ProcessHeadersMessage (pfrom, connman, headers, chainparams);
2519
+ // Headers received via a HEADERS message should be valid, and reflect
2520
+ // the chain the peer is on. If we receive a known-invalid header,
2521
+ // disconnect the peer if it is using one of our outbound connection
2522
+ // slots.
2523
+ bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection ;
2524
+ return ProcessHeadersMessage (pfrom, connman, headers, chainparams, should_punish);
2484
2525
}
2485
2526
2486
2527
else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex ) // Ignore blocks received while importing
0 commit comments