Skip to content

Commit 88c3549

Browse files
committed
Fix compact block handling to not ban if block is invalid
1 parent c93beac commit 88c3549

File tree

7 files changed

+40
-20
lines changed

7 files changed

+40
-20
lines changed

src/blockencodings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
167167
// check its own merkle root and cache that check.
168168
if (state.CorruptionPossible())
169169
return READ_STATUS_FAILED; // Possible Short ID collision
170-
return READ_STATUS_INVALID;
170+
return READ_STATUS_CHECKBLOCK_FAILED;
171171
}
172172

173173
LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", header.GetHash().ToString(), prefilled_count, mempool_count, vtx_missing.size());

src/blockencodings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ typedef enum ReadStatus_t
124124
READ_STATUS_OK,
125125
READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
126126
READ_STATUS_FAILED, // Failed to process object
127+
READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a
128+
// failure in CheckBlock.
127129
} ReadStatus;
128130

129131
class CBlockHeaderAndShortTxIDs {

src/main.cpp

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,10 @@ namespace {
179179
* Sources of received blocks, saved to be able to send them reject
180180
* messages or ban them when processing happens afterwards. Protected by
181181
* cs_main.
182+
* Set mapBlockSource[hash].second to false if the node should not be
183+
* punished if the block is invalid.
182184
*/
183-
map<uint256, NodeId> mapBlockSource;
185+
map<uint256, std::pair<NodeId, bool>> mapBlockSource;
184186

185187
/**
186188
* Filter for transactions that were recently rejected by
@@ -3759,7 +3761,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
37593761
return true;
37603762
}
37613763

3762-
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp)
3764+
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool fMayBanPeerIfInvalid)
37633765
{
37643766
{
37653767
LOCK(cs_main);
@@ -3769,7 +3771,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C
37693771
bool fNewBlock = false;
37703772
bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fForceProcessing, dbp, &fNewBlock);
37713773
if (pindex && pfrom) {
3772-
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
3774+
mapBlockSource[pindex->GetBlockHash()] = std::make_pair(pfrom->GetId(), fMayBanPeerIfInvalid);
37733775
if (fNewBlock) pfrom->nLastBlockTime = GetTime();
37743776
}
37753777
CheckBlockIndex(chainparams.GetConsensus());
@@ -4749,16 +4751,16 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
47494751
LOCK(cs_main);
47504752

47514753
const uint256 hash(block.GetHash());
4752-
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(hash);
4754+
std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
47534755

47544756
int nDoS = 0;
47554757
if (state.IsInvalid(nDoS)) {
4756-
if (it != mapBlockSource.end() && State(it->second)) {
4758+
if (it != mapBlockSource.end() && State(it->second.first)) {
47574759
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
47584760
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
4759-
State(it->second)->rejects.push_back(reject);
4760-
if (nDoS > 0)
4761-
Misbehaving(it->second, nDoS);
4761+
State(it->second.first)->rejects.push_back(reject);
4762+
if (nDoS > 0 && it->second.second)
4763+
Misbehaving(it->second.first, nDoS);
47624764
}
47634765
}
47644766
if (it != mapBlockSource.end())
@@ -5868,6 +5870,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
58685870
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
58695871
pfrom->PushMessage(NetMsgType::GETDATA, invs);
58705872
} else {
5873+
// Block is either okay, or possibly we received
5874+
// READ_STATUS_CHECKBLOCK_FAILED.
5875+
// Note that CheckBlock can only fail for one of a few reasons:
5876+
// 1. bad-proof-of-work (impossible here, because we've already
5877+
// accepted the header)
5878+
// 2. merkleroot doesn't match the transactions given (already
5879+
// caught in FillBlock with READ_STATUS_FAILED, so
5880+
// impossible here)
5881+
// 3. the block is otherwise invalid (eg invalid coinbase,
5882+
// block is too big, too many legacy sigops, etc).
5883+
// So if CheckBlock failed, #3 is the only possibility.
5884+
// Under BIP 152, we don't DoS-ban unless proof of work is
5885+
// invalid (we don't require all the stateless checks to have
5886+
// been run). This is handled below, so just treat this as
5887+
// though the block was successfully read, and rely on the
5888+
// handling in ProcessNewBlock to ensure the block index is
5889+
// updated, reject messages go out, etc.
58715890
MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
58725891
fBlockRead = true;
58735892
}
@@ -5876,16 +5895,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
58765895
CValidationState state;
58775896
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
58785897
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
5879-
ProcessNewBlock(state, chainparams, pfrom, &block, true, NULL);
5898+
// BIP 152 permits peers to relay compact blocks after validating
5899+
// the header only; we should not punish peers if the block turns
5900+
// out to be invalid.
5901+
ProcessNewBlock(state, chainparams, pfrom, &block, true, NULL, false);
58805902
int nDoS;
58815903
if (state.IsInvalid(nDoS)) {
58825904
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
58835905
pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
58845906
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash());
5885-
if (nDoS > 0) {
5886-
LOCK(cs_main);
5887-
Misbehaving(pfrom->GetId(), nDoS);
5888-
}
58895907
}
58905908
}
58915909
}
@@ -6056,7 +6074,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
60566074
// need it even though it is not a candidate for a new best tip.
60576075
forceProcessing |= MarkBlockAsReceived(block.GetHash());
60586076
}
6059-
ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
6077+
ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, true);
60606078
int nDoS;
60616079
if (state.IsInvalid(nDoS)) {
60626080
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes

src/main.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
223223
* @param[out] dbp The already known disk position of pblock, or NULL if not yet stored.
224224
* @return True if state.IsValid()
225225
*/
226-
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp);
226+
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool fMayBanPeerIfInvalid);
227227
/** Check whether enough disk space is available for an incoming block */
228228
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
229229
/** Open a block file (blk?????.dat) */

src/rpc/mining.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
132132
continue;
133133
}
134134
CValidationState state;
135-
if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL))
135+
if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, false))
136136
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
137137
++nHeight;
138138
blockHashes.push_back(pblock->GetHash().GetHex());
@@ -757,7 +757,7 @@ UniValue submitblock(const JSONRPCRequest& request)
757757
CValidationState state;
758758
submitblock_StateCatcher sc(block.GetHash());
759759
RegisterValidationInterface(&sc);
760-
bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL);
760+
bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, false);
761761
UnregisterValidationInterface(&sc);
762762
if (fBlockPresent)
763763
{

src/test/miner_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
223223
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
224224
pblock->nNonce = blockinfo[i].nonce;
225225
CValidationState state;
226-
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL));
226+
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, false));
227227
BOOST_CHECK(state.IsValid());
228228
pblock->hashPrevBlock = pblock->GetHash();
229229
}

src/test/test_bitcoin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
127127
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
128128

129129
CValidationState state;
130-
ProcessNewBlock(state, chainparams, NULL, &block, true, NULL);
130+
ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, false);
131131

132132
CBlock result = block;
133133
return result;

0 commit comments

Comments
 (0)