aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuhas Daftuar <sdaftuar@gmail.com>2015-04-09 13:21:11 -0400
committerSuhas Daftuar <sdaftuar@gmail.com>2015-06-02 13:54:29 -0400
commit9be0e6837b878f72bd087ce32b7a2f2ffb2fd544 (patch)
tree7c1a9a8c887697e3f87a107ea27448da66abc251
parentf00b62391b9317bf4c83676520fede1fb4027c43 (diff)
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()
-rw-r--r--src/main.cpp40
-rw-r--r--src/main.h7
-rw-r--r--src/miner.cpp2
-rw-r--r--src/rpcmining.cpp4
-rw-r--r--src/test/miner_tests.cpp2
5 files changed, 33 insertions, 22 deletions
diff --git a/src/main.cpp b/src/main.cpp
index efb7fb3f87..760cda5dee 100644
--- a/src/main.cpp
+++ b/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(),
diff --git a/src/main.h b/src/main.h
index 3b25217869..669ea7128a 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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);
diff --git a/src/miner.cpp b/src/miner.cpp
index dfabfd48d0..f5919ca3af 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -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;
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index d58d438573..528d5406f4 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -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)
{
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index e3e9061cce..9e4fd8513e 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -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();
}