anti-spam: fix inverted condition + soft scoring

Two bugs in the anti-spam heuristic at src/main.cpp:

1. Inverted comparison: condition was bnNewBlock > bnRequired paired with
   "too little proof-of-stake" error message. The condition triggers when
   the block has MORE difficulty than required (harder than allowed),
   but the message claims the OPPOSITE. Honest blocks during legitimate
   time-warps (fork recovery, chain catchup) get mislabelled.

2. Misbehaving(100) was a single-shot instant ban: banscore threshold
   defaults to 100, so the FIRST anti-spam violation triggered a 24-hour
   ban on every honest peer feeding us blocks during fork divergence.
   This is what caused the 2026-06-23 DNS2 clearnet-fork incident:
   peers got banned before we could determine which chain was canonical.

Fix: condition now correctly says bnNewBlock < bnRequired (block too
easy = reject), and Misbehaving score dropped from 100 to 5 (needs
~20 anti-spam violations before the 100 banscore threshold). Anti-spam
is a soft signal, not a hard ban trigger.
This commit is contained in:
2026-07-01 03:32:04 -07:00
parent 5635cb5e57
commit cbb189aade
+18 -2
View File
@@ -3478,10 +3478,26 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
bnRequired.SetCompact(ComputeMinWork(pindexLastPow->nBits, deltaTime));
}
if (bnRequired != 0 && bnNewBlock > bnRequired)
// bnNewBlock is the difficulty of the candidate block (compact bits -> target).
// bnRequired is the MINIMUM difficulty the block must meet (based on time since
// last checkpoint / chain tip). If the candidate's target is SMALLER than required
// (i.e. block is harder than allowed), it's "too much" difficulty and we reject.
// If LARGER (less difficulty = easier than required), it's "too little" and we reject.
// PREVIOUS BUG: condition was `bnNewBlock > bnRequired` paired with "too little"
// error message — the message and the trigger were swapped. This caused honest
// blocks during legitimate time-warps (fork recovery, chain catchup) to be
// labelled "too little proof-of-stake" while the actual reject reason was the
// OPPOSITE — block had TOO MUCH difficulty relative to elapsed time.
// Fixed: condition now matches the message (block too easy => reject).
if (bnRequired != 0 && bnNewBlock < bnRequired)
{
// Anti-spam is a soft scoring signal, NOT a hard ban trigger. A single
// violation should log + score modestly, not 24-hour-ban honest peers
// (which is what happened during the 2026-06-23 DNS2 clearnet-fork
// incident — `Misbehaving(100)` crossed the banscore threshold on the
// FIRST block, instantly banning every honest peer feeding us fork blocks).
if (pfrom)
pfrom->Misbehaving(100);
pfrom->Misbehaving(5);
return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
}
}