Be stricter in processing unrequested blocks
AcceptBlock will no longer process an unrequested block, unless it has not been previously processed and has more work than chainActive.Tip()
This commit is contained in:
parent
f00b62391b
commit
9be0e6837b
5 changed files with 33 additions and 22 deletions
40
src/main.cpp
40
src/main.cpp
|
@ -296,7 +296,8 @@ void FinalizeNode(NodeId nodeid) {
|
|||
}
|
||||
|
||||
// Requires cs_main.
|
||||
void MarkBlockAsReceived(const uint256& hash) {
|
||||
// Returns a bool indicating whether we requested this block.
|
||||
bool MarkBlockAsReceived(const uint256& hash) {
|
||||
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
|
||||
if (itInFlight != mapBlocksInFlight.end()) {
|
||||
CNodeState *state = State(itInFlight->second.first);
|
||||
|
@ -306,7 +307,9 @@ void MarkBlockAsReceived(const uint256& hash) {
|
|||
state->nBlocksInFlight--;
|
||||
state->nStallingSince = 0;
|
||||
mapBlocksInFlight.erase(itInFlight);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requires cs_main.
|
||||
|
@ -2826,7 +2829,7 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp)
|
||||
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp)
|
||||
{
|
||||
const CChainParams& chainparams = Params();
|
||||
AssertLockHeld(cs_main);
|
||||
|
@ -2836,13 +2839,18 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
|
|||
if (!AcceptBlockHeader(block, state, &pindex))
|
||||
return false;
|
||||
|
||||
// If we're pruning, ensure that we don't allow a peer to dump a copy
|
||||
// of old blocks. But we might need blocks that are not on the main chain
|
||||
// to handle a reorg, even if we've processed once.
|
||||
if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
|
||||
// TODO: deal better with duplicate blocks.
|
||||
// return state.DoS(20, error("AcceptBlock(): already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate");
|
||||
return true;
|
||||
// Try to process all requested blocks that we don't have, but only
|
||||
// process an unrequested block if it's new and has enough work to
|
||||
// advance our tip.
|
||||
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
|
||||
bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true);
|
||||
|
||||
// TODO: deal better with return value and error conditions for duplicate
|
||||
// and unrequested blocks.
|
||||
if (fAlreadyHave) return true;
|
||||
if (!fRequested) { // If we didn't ask for it:
|
||||
if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned
|
||||
if (!fHasMoreWork) return true; // Don't process less-work chains
|
||||
}
|
||||
|
||||
if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
|
||||
|
@ -2891,21 +2899,22 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned
|
|||
}
|
||||
|
||||
|
||||
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
|
||||
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp)
|
||||
{
|
||||
// Preliminary checks
|
||||
bool checked = CheckBlock(*pblock, state);
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
MarkBlockAsReceived(pblock->GetHash());
|
||||
bool fRequested = MarkBlockAsReceived(pblock->GetHash());
|
||||
fRequested |= fForceProcessing;
|
||||
if (!checked) {
|
||||
return error("%s: CheckBlock FAILED", __func__);
|
||||
}
|
||||
|
||||
// Store to disk
|
||||
CBlockIndex *pindex = NULL;
|
||||
bool ret = AcceptBlock(*pblock, state, &pindex, dbp);
|
||||
bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp);
|
||||
if (pindex && pfrom) {
|
||||
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
|
||||
}
|
||||
|
@ -3453,7 +3462,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
|
|||
// process in case the block isn't known yet
|
||||
if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) {
|
||||
CValidationState state;
|
||||
if (ProcessNewBlock(state, NULL, &block, dbp))
|
||||
if (ProcessNewBlock(state, NULL, &block, true, dbp))
|
||||
nLoaded++;
|
||||
if (state.IsError())
|
||||
break;
|
||||
|
@ -3475,7 +3484,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
|
|||
LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(),
|
||||
head.ToString());
|
||||
CValidationState dummy;
|
||||
if (ProcessNewBlock(dummy, NULL, &block, &it->second))
|
||||
if (ProcessNewBlock(dummy, NULL, &block, true, &it->second))
|
||||
{
|
||||
nLoaded++;
|
||||
queue.push_back(block.GetHash());
|
||||
|
@ -4462,7 +4471,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||
pfrom->AddInventoryKnown(inv);
|
||||
|
||||
CValidationState state;
|
||||
ProcessNewBlock(state, pfrom, &block);
|
||||
// Process all blocks from whitelisted peers, even if not requested.
|
||||
ProcessNewBlock(state, pfrom, &block, pfrom->fWhitelisted, NULL);
|
||||
int nDoS;
|
||||
if (state.IsInvalid(nDoS)) {
|
||||
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
|
||||
|
|
|
@ -153,10 +153,11 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
|
|||
* @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation.
|
||||
* @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid.
|
||||
* @param[in] pblock The block we want to process.
|
||||
* @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers.
|
||||
* @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location.
|
||||
* @return True if state.IsValid()
|
||||
*/
|
||||
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL);
|
||||
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp);
|
||||
/** Check whether enough disk space is available for an incoming block */
|
||||
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
|
||||
/** Open a block file (blk?????.dat) */
|
||||
|
@ -400,8 +401,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
|
|||
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
|
||||
bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
|
||||
|
||||
/** Store block on disk. If dbp is provided, the file is known to already reside on disk */
|
||||
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL);
|
||||
/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */
|
||||
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp);
|
||||
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);
|
||||
|
||||
|
||||
|
|
|
@ -434,7 +434,7 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
|
|||
|
||||
// Process this block the same as if we had received it from another node
|
||||
CValidationState state;
|
||||
if (!ProcessNewBlock(state, NULL, pblock))
|
||||
if (!ProcessNewBlock(state, NULL, pblock, true, NULL))
|
||||
return error("BitcoinMiner: ProcessNewBlock, block not accepted");
|
||||
|
||||
return true;
|
||||
|
|
|
@ -164,7 +164,7 @@ Value generate(const Array& params, bool fHelp)
|
|||
++pblock->nNonce;
|
||||
}
|
||||
CValidationState state;
|
||||
if (!ProcessNewBlock(state, NULL, pblock))
|
||||
if (!ProcessNewBlock(state, NULL, pblock, true, NULL))
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
|
||||
++nHeight;
|
||||
blockHashes.push_back(pblock->GetHash().GetHex());
|
||||
|
@ -650,7 +650,7 @@ Value submitblock(const Array& params, bool fHelp)
|
|||
CValidationState state;
|
||||
submitblock_StateCatcher sc(block.GetHash());
|
||||
RegisterValidationInterface(&sc);
|
||||
bool fAccepted = ProcessNewBlock(state, NULL, &block);
|
||||
bool fAccepted = ProcessNewBlock(state, NULL, &block, true, NULL);
|
||||
UnregisterValidationInterface(&sc);
|
||||
if (fBlockPresent)
|
||||
{
|
||||
|
|
|
@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||
pblock->nNonce = blockinfo[i].nonce;
|
||||
CValidationState state;
|
||||
BOOST_CHECK(ProcessNewBlock(state, NULL, pblock));
|
||||
BOOST_CHECK(ProcessNewBlock(state, NULL, pblock, true, NULL));
|
||||
BOOST_CHECK(state.IsValid());
|
||||
pblock->hashPrevBlock = pblock->GetHash();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue