Fix compact block handling to not ban if block is invalid

This commit is contained in:
Suhas Daftuar 2016-10-31 10:03:49 -04:00
parent 82905069bf
commit 015865ee9e
7 changed files with 40 additions and 21 deletions

View File

@ -167,7 +167,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
// check its own merkle root and cache that check. // check its own merkle root and cache that check.
if (state.CorruptionPossible()) if (state.CorruptionPossible())
return READ_STATUS_FAILED; // Possible Short ID collision return READ_STATUS_FAILED; // Possible Short ID collision
return READ_STATUS_INVALID; return READ_STATUS_CHECKBLOCK_FAILED;
} }
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()); 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());

View File

@ -124,6 +124,8 @@ typedef enum ReadStatus_t
READ_STATUS_OK, READ_STATUS_OK,
READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
READ_STATUS_FAILED, // Failed to process object READ_STATUS_FAILED, // Failed to process object
READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a
// failure in CheckBlock.
} ReadStatus; } ReadStatus;
class CBlockHeaderAndShortTxIDs { class CBlockHeaderAndShortTxIDs {

View File

@ -178,8 +178,10 @@ namespace {
* Sources of received blocks, saved to be able to send them reject * Sources of received blocks, saved to be able to send them reject
* messages or ban them when processing happens afterwards. Protected by * messages or ban them when processing happens afterwards. Protected by
* cs_main. * cs_main.
* Set mapBlockSource[hash].second to false if the node should not be
* punished if the block is invalid.
*/ */
map<uint256, NodeId> mapBlockSource; map<uint256, std::pair<NodeId, bool>> mapBlockSource;
/** /**
* Filter for transactions that were recently rejected by * Filter for transactions that were recently rejected by
@ -1885,13 +1887,13 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
int nDoS = 0; int nDoS = 0;
if (state.IsInvalid(nDoS)) { if (state.IsInvalid(nDoS)) {
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash()); std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
if (it != mapBlockSource.end() && State(it->second)) { if (it != mapBlockSource.end() && State(it->second.first)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()};
State(it->second)->rejects.push_back(reject); State(it->second.first)->rejects.push_back(reject);
if (nDoS > 0) if (nDoS > 0 && it->second.second)
Misbehaving(it->second, nDoS); Misbehaving(it->second.first, nDoS);
} }
} }
if (!state.CorruptionPossible()) { if (!state.CorruptionPossible()) {
@ -3761,7 +3763,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned
} }
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool fMayBanPeerIfInvalid)
{ {
{ {
LOCK(cs_main); LOCK(cs_main);
@ -3773,7 +3775,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C
bool fNewBlock = false; bool fNewBlock = false;
bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp, &fNewBlock); bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp, &fNewBlock);
if (pindex && pfrom) { if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); mapBlockSource[pindex->GetBlockHash()] = std::make_pair(pfrom->GetId(), fMayBanPeerIfInvalid);
if (fNewBlock) pfrom->nLastBlockTime = GetTime(); if (fNewBlock) pfrom->nLastBlockTime = GetTime();
} }
CheckBlockIndex(chainparams.GetConsensus()); CheckBlockIndex(chainparams.GetConsensus());
@ -4717,7 +4719,6 @@ std::string GetWarnings(const std::string& strFor)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
//
// Messages // Messages
// //
@ -5791,17 +5792,33 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash)); invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
pfrom->PushMessage(NetMsgType::GETDATA, invs); pfrom->PushMessage(NetMsgType::GETDATA, invs);
} else { } else {
// Block is either okay, or possibly we received
// READ_STATUS_CHECKBLOCK_FAILED.
// Note that CheckBlock can only fail for one of a few reasons:
// 1. bad-proof-of-work (impossible here, because we've already
// accepted the header)
// 2. merkleroot doesn't match the transactions given (already
// caught in FillBlock with READ_STATUS_FAILED, so
// impossible here)
// 3. the block is otherwise invalid (eg invalid coinbase,
// block is too big, too many legacy sigops, etc).
// So if CheckBlock failed, #3 is the only possibility.
// Under BIP 152, we don't DoS-ban unless proof of work is
// invalid (we don't require all the stateless checks to have
// been run). This is handled below, so just treat this as
// though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is
// updated, reject messages go out, etc.
CValidationState state; CValidationState state;
ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL); // BIP 152 permits peers to relay compact blocks after validating
// the header only; we should not punish peers if the block turns
// out to be invalid.
ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL, false);
int nDoS; int nDoS;
if (state.IsInvalid(nDoS)) { if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash()); state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash());
if (nDoS > 0) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
}
} }
} }
} }
@ -5968,7 +5985,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Such an unrequested block may still be processed, subject to the // Such an unrequested block may still be processed, subject to the
// conditions in AcceptBlock(). // conditions in AcceptBlock().
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL); ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, true);
int nDoS; int nDoS;
if (state.IsInvalid(nDoS)) { if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes

View File

@ -222,7 +222,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
* @param[out] dbp The already known disk position of pblock, or NULL if not yet stored. * @param[out] dbp The already known disk position of pblock, or NULL if not yet stored.
* @return True if state.IsValid() * @return True if state.IsValid()
*/ */
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp); bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool fMayBanPeerIfInvalid);
/** Check whether enough disk space is available for an incoming block */ /** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */ /** Open a block file (blk?????.dat) */

View File

@ -131,7 +131,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
continue; continue;
} }
CValidationState state; CValidationState state;
if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, false))
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight; ++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex()); blockHashes.push_back(pblock->GetHash().GetHex());
@ -760,7 +760,7 @@ UniValue submitblock(const UniValue& params, bool fHelp)
CValidationState state; CValidationState state;
submitblock_StateCatcher sc(block.GetHash()); submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc); RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, false);
UnregisterValidationInterface(&sc); UnregisterValidationInterface(&sc);
if (fBlockPresent) if (fBlockPresent)
{ {

View File

@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce; pblock->nNonce = blockinfo[i].nonce;
CValidationState state; CValidationState state;
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)); BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, false));
BOOST_CHECK(state.IsValid()); BOOST_CHECK(state.IsValid());
pblock->hashPrevBlock = pblock->GetHash(); pblock->hashPrevBlock = pblock->GetHash();
} }

View File

@ -118,7 +118,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
CValidationState state; CValidationState state;
ProcessNewBlock(state, chainparams, NULL, &block, true, NULL); ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, false);
CBlock result = block; CBlock result = block;
delete pblocktemplate; delete pblocktemplate;