aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSuhas Daftuar <sdaftuar@gmail.com>2016-10-31 10:03:49 -0400
committerSuhas Daftuar <sdaftuar@gmail.com>2016-11-03 13:07:12 -0400
commit88c35491ab19f9afdf9b3fa9356a072f70ef2f55 (patch)
tree979296a970eea7001a5133bb792bda690bcb428c /src
parentc93beac43fa66c8431e3952283edb5be8c65606c (diff)
Fix compact block handling to not ban if block is invalid
Diffstat (limited to 'src')
-rw-r--r--src/blockencodings.cpp2
-rw-r--r--src/blockencodings.h2
-rw-r--r--src/main.cpp46
-rw-r--r--src/main.h2
-rw-r--r--src/rpc/mining.cpp4
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/test_bitcoin.cpp2
7 files changed, 40 insertions, 20 deletions
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index 93d3fa372b..737102f168 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -167,7 +167,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
// check its own merkle root and cache that check.
if (state.CorruptionPossible())
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());
diff --git a/src/blockencodings.h b/src/blockencodings.h
index 99b1cb140d..705eaf28aa 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -124,6 +124,8 @@ typedef enum ReadStatus_t
READ_STATUS_OK,
READ_STATUS_INVALID, // Invalid object, peer is sending bogus crap
READ_STATUS_FAILED, // Failed to process object
+ READ_STATUS_CHECKBLOCK_FAILED, // Used only by FillBlock to indicate a
+ // failure in CheckBlock.
} ReadStatus;
class CBlockHeaderAndShortTxIDs {
diff --git a/src/main.cpp b/src/main.cpp
index 3f706b1503..b65b9874e9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -179,8 +179,10 @@ namespace {
* Sources of received blocks, saved to be able to send them reject
* messages or ban them when processing happens afterwards. Protected by
* 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
@@ -3759,7 +3761,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
return true;
}
-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);
@@ -3769,7 +3771,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C
bool fNewBlock = false;
bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fForceProcessing, dbp, &fNewBlock);
if (pindex && pfrom) {
- mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
+ mapBlockSource[pindex->GetBlockHash()] = std::make_pair(pfrom->GetId(), fMayBanPeerIfInvalid);
if (fNewBlock) pfrom->nLastBlockTime = GetTime();
}
CheckBlockIndex(chainparams.GetConsensus());
@@ -4749,16 +4751,16 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
LOCK(cs_main);
const uint256 hash(block.GetHash());
- std::map<uint256, NodeId>::iterator it = mapBlockSource.find(hash);
+ std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
int nDoS = 0;
if (state.IsInvalid(nDoS)) {
- 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
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
- State(it->second)->rejects.push_back(reject);
- if (nDoS > 0)
- Misbehaving(it->second, nDoS);
+ State(it->second.first)->rejects.push_back(reject);
+ if (nDoS > 0 && it->second.second)
+ Misbehaving(it->second.first, nDoS);
}
}
if (it != mapBlockSource.end())
@@ -5868,6 +5870,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom, chainActive.Tip(), chainparams.GetConsensus()), resp.blockhash));
pfrom->PushMessage(NetMsgType::GETDATA, invs);
} 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.
MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
fBlockRead = true;
}
@@ -5876,16 +5895,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CValidationState state;
// Since we requested this block (it was in mapBlocksInFlight), force it to be processed,
// even if it would not be a candidate for new tip (missing previous block, chain not long enough, etc)
- ProcessNewBlock(state, chainparams, pfrom, &block, true, 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, true, NULL, false);
int nDoS;
if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash());
- if (nDoS > 0) {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), nDoS);
- }
}
}
}
@@ -6056,7 +6074,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// need it even though it is not a candidate for a new best tip.
forceProcessing |= MarkBlockAsReceived(block.GetHash());
}
- ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
+ ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, true);
int nDoS;
if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
diff --git a/src/main.h b/src/main.h
index e80314a64b..9343330587 100644
--- a/src/main.h
+++ b/src/main.h
@@ -223,7 +223,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
* @param[out] dbp The already known disk position of pblock, or NULL if not yet stored.
* @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 */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index be0776ea22..564102ff95 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -132,7 +132,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
continue;
}
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");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
@@ -757,7 +757,7 @@ UniValue submitblock(const JSONRPCRequest& request)
CValidationState state;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
- bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL);
+ bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, false);
UnregisterValidationInterface(&sc);
if (fBlockPresent)
{
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index a94979fd77..2762cafa38 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce;
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());
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 98f4ed939f..3da0be8ca4 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -127,7 +127,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
CValidationState state;
- ProcessNewBlock(state, chainparams, NULL, &block, true, NULL);
+ ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, false);
CBlock result = block;
return result;