diff options
| author | langerhans <[email protected]> | 2014-08-24 17:09:34 +0200 |
|---|---|---|
| committer | langerhans <[email protected]> | 2014-08-24 17:09:34 +0200 |
| commit | 65228644e10328172e9fa3ebe64251983e1153b3 (patch) | |
| tree | 8b1bcf377be305a81a73e8dd727e008c26c3b7d5 /src/main.cpp | |
| parent | Merge branch '1.7-maint' (diff) | |
| parent | Merge pull request #622 from rnicoll/1.8-dev-safemode (diff) | |
| download | discoin-65228644e10328172e9fa3ebe64251983e1153b3.tar.xz discoin-65228644e10328172e9fa3ebe64251983e1153b3.zip | |
Merge branch '1.8-dev'v1.8.0
Conflicts:
configure.ac
contrib/gitian-descriptors/deps-linux.yml
contrib/gitian-descriptors/deps-win.yml
contrib/gitian-descriptors/gitian-linux.yml
contrib/gitian-descriptors/gitian-win.yml
contrib/gitian-descriptors/qt-win.yml
doc/release-process.md
src/clientversion.h
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 779 |
1 files changed, 562 insertions, 217 deletions
diff --git a/src/main.cpp b/src/main.cpp index 5b83a6d44..f6e709365 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,7 @@ #include "checkpoints.h" #include "checkqueue.h" #include "init.h" +#include "auxpow.h" #include "net.h" #include "txdb.h" #include "txmempool.h" @@ -59,8 +60,6 @@ int64_t CTransaction::nMinTxFee = 100000000; // Override with -mintxfee /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ int64_t CTransaction::nMinRelayTxFee = 100000000; -static CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have - struct COrphanBlock { uint256 hashBlock; uint256 hashPrev; @@ -79,43 +78,54 @@ const string strMessageMagic = "Dogecoin Signed Message:\n"; // Internal stuff namespace { -struct CBlockIndexWorkComparator -{ - bool operator()(CBlockIndex *pa, CBlockIndex *pb) { - // First sort by most total work, ... - if (pa->nChainWork > pb->nChainWork) return false; - if (pa->nChainWork < pb->nChainWork) return true; - - // ... then by earliest time received, ... - if (pa->nSequenceId < pb->nSequenceId) return false; - if (pa->nSequenceId > pb->nSequenceId) return true; - - // Use pointer address as tie breaker (should only happen with blocks - // loaded from disk, as those all have id 0). - if (pa < pb) return false; - if (pa > pb) return true; - - // Identical blocks. - return false; - } -}; - -CBlockIndex *pindexBestInvalid; -set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed + struct CBlockIndexWorkComparator + { + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + // First sort by most total work, ... + if (pa->nChainWork > pb->nChainWork) return false; + if (pa->nChainWork < pb->nChainWork) return true; -CCriticalSection cs_LastBlockFile; -CBlockFileInfo infoLastBlockFile; -int nLastBlockFile = 0; + // ... then by earliest time received, ... + if (pa->nSequenceId < pb->nSequenceId) return false; + if (pa->nSequenceId > pb->nSequenceId) return true; -// Every received block is assigned a unique and increasing identifier, so we -// know which one to give priority in case of a fork. -CCriticalSection cs_nBlockSequenceId; -// Blocks loaded from disk are assigned id 0, so start the counter at 1. -uint32_t nBlockSequenceId = 1; + // Use pointer address as tie breaker (should only happen with blocks + // loaded from disk, as those all have id 0). + if (pa < pb) return false; + if (pa > pb) return true; -// Sources of received blocks, to be able to send them reject messages or ban -// them, if processing happens afterwards. Protected by cs_main. -map<uint256, NodeId> mapBlockSource; + // Identical blocks. + return false; + } + }; + + CBlockIndex *pindexBestInvalid; + // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed + set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; + + CCriticalSection cs_LastBlockFile; + CBlockFileInfo infoLastBlockFile; + int nLastBlockFile = 0; + + // Every received block is assigned a unique and increasing identifier, so we + // know which one to give priority in case of a fork. + CCriticalSection cs_nBlockSequenceId; + // Blocks loaded from disk are assigned id 0, so start the counter at 1. + uint32_t nBlockSequenceId = 1; + + // Sources of received blocks, to be able to send them reject messages or ban + // them, if processing happens afterwards. Protected by cs_main. + map<uint256, NodeId> mapBlockSource; + + // Blocks that are in flight, and that are in the queue to be downloaded. + // Protected by cs_main. + struct QueuedBlock { + uint256 hash; + int64_t nTime; // Time of "getdata" request in microseconds. + int nQueuedBefore; // Number of blocks in flight at the time of request. + }; + map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; + map<uint256, pair<NodeId, list<uint256>::iterator> > mapBlocksToDownload; } ////////////////////////////////////////////////////////////////////////////// @@ -199,10 +209,20 @@ struct CNodeState { std::string name; // List of asynchronously-determined block rejections to notify this peer about. std::vector<CBlockReject> rejects; + list<QueuedBlock> vBlocksInFlight; + int nBlocksInFlight; + list<uint256> vBlocksToDownload; + int nBlocksToDownload; + int64_t nLastBlockReceive; + int64_t nLastBlockProcess; CNodeState() { nMisbehavior = 0; fShouldBan = false; + nBlocksToDownload = 0; + nBlocksInFlight = 0; + nLastBlockReceive = 0; + nLastBlockProcess = 0; } }; @@ -231,8 +251,71 @@ void InitializeNode(NodeId nodeid, const CNode *pnode) { void FinalizeNode(NodeId nodeid) { LOCK(cs_main); + CNodeState *state = State(nodeid); + + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) + mapBlocksInFlight.erase(entry.hash); + BOOST_FOREACH(const uint256& hash, state->vBlocksToDownload) + mapBlocksToDownload.erase(hash); + mapNodeState.erase(nodeid); } + +// Requires cs_main. +void MarkBlockAsReceived(const uint256 &hash, NodeId nodeFrom = -1) { + map<uint256, pair<NodeId, list<uint256>::iterator> >::iterator itToDownload = mapBlocksToDownload.find(hash); + if (itToDownload != mapBlocksToDownload.end()) { + CNodeState *state = State(itToDownload->second.first); + state->vBlocksToDownload.erase(itToDownload->second.second); + state->nBlocksToDownload--; + mapBlocksToDownload.erase(itToDownload); + } + + map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); + if (itInFlight != mapBlocksInFlight.end()) { + CNodeState *state = State(itInFlight->second.first); + state->vBlocksInFlight.erase(itInFlight->second.second); + state->nBlocksInFlight--; + if (itInFlight->second.first == nodeFrom) + state->nLastBlockReceive = GetTimeMicros(); + mapBlocksInFlight.erase(itInFlight); + } + +} + +// Requires cs_main. +bool AddBlockToQueue(NodeId nodeid, const uint256 &hash) { + if (mapBlocksToDownload.count(hash) || mapBlocksInFlight.count(hash)) + return false; + + CNodeState *state = State(nodeid); + if (state == NULL) + return false; + + list<uint256>::iterator it = state->vBlocksToDownload.insert(state->vBlocksToDownload.end(), hash); + state->nBlocksToDownload++; + if (state->nBlocksToDownload > 5000) + Misbehaving(nodeid, 10); + mapBlocksToDownload[hash] = std::make_pair(nodeid, it); + return true; +} + +// Requires cs_main. +void MarkBlockAsInFlight(NodeId nodeid, const uint256 &hash) { + CNodeState *state = State(nodeid); + assert(state != NULL); + + // Make sure it's not listed somewhere already. + MarkBlockAsReceived(hash); + + QueuedBlock newentry = {hash, GetTimeMicros(), state->nBlocksInFlight}; + if (state->nBlocksInFlight == 0) + state->nLastBlockReceive = newentry.nTime; // Reset when a first request is sent. + list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); + state->nBlocksInFlight++; + mapBlocksInFlight[hash] = std::make_pair(nodeid, it); +} + } bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { @@ -396,6 +479,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) //TODO: this isn't identical to dogecoin reference client. bool IsStandardTx(const CTransaction& tx, string& reason) { + AssertLockHeld(cs_main); if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { reason = "version"; return false; @@ -435,10 +519,14 @@ bool IsStandardTx(const CTransaction& tx, string& reason) BOOST_FOREACH(const CTxIn& txin, tx.vin) { - // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG - // pay-to-script-hash, which is 3 ~80-byte signatures, 3 - // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) { + // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed + // keys. (remember the 520 byte limit on redeemScript size) That works + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624 + // bytes of scriptSig, which we round off to 1650 bytes for some minor + // future-proofing. That's also enough to spend a 20-of-20 + // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not + // considered standard) + if (txin.scriptSig.size() > 1650) { reason = "scriptsig-size"; return false; } @@ -478,6 +566,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { + AssertLockHeld(cs_main); // Time based nLockTime implemented in 0.1.6 if (tx.nLockTime == 0) return true; @@ -589,6 +678,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, CCoinsViewCache& inputs) int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { + AssertLockHeld(cs_main); CBlock blockTmp; if (pblock == NULL) { @@ -704,7 +794,7 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; - if (fAllowFree) + if (fAllowFree && mode != GMF_SEND) { // Free transaction area if (nBytes < 26000) @@ -726,6 +816,7 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) { + AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; @@ -853,7 +944,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS)) { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } @@ -871,6 +962,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; + AssertLockHeld(cs_main); // Find the block it claims to be in map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); @@ -894,6 +986,7 @@ int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { + AssertLockHeld(cs_main); int nResult = GetDepthInMainChainINTERNAL(pindexRet); if (nResult == 0 && !mempool.exists(GetHash())) return -1; // Not in chain, not in mempool @@ -949,11 +1042,11 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock fseek(file, postx.nTxOffset, SEEK_CUR); file >> txOut; } catch (std::exception &e) { - return error("%s : Deserialize or I/O error - %s", __PRETTY_FUNCTION__, e.what()); + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } hashBlock = header.GetHash(); if (txOut.GetHash() != hash) - return error("%s : txid mismatch", __PRETTY_FUNCTION__); + return error("%s : txid mismatch", __func__); return true; } } @@ -1037,11 +1130,11 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) filein >> block; } catch (std::exception &e) { - return error("%s : Deserialize or I/O error - %s", __PRETTY_FUNCTION__, e.what()); + return error("%s : Deserialize or I/O error - %s", __func__, e.what()); } // Check the header - if (!CheckProofOfWork(block.GetPoWHash(), block.nBits)) + if (!block.CheckProofOfWork(mapBlockIndex[block.GetHash()]->nHeight)) return error("ReadBlockFromDisk : Errors in block header"); return true; @@ -1056,6 +1149,20 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } +void CBlockHeader::SetAuxPow(CAuxPow* pow) +{ + if (pow != NULL) + nVersion |= BLOCK_VERSION_AUXPOW; + else + nVersion &= ~BLOCK_VERSION_AUXPOW; + auxpow.reset(pow); +} + +bool IsAuxPowVersion(int nVersion) +{ + return (nVersion == BLOCK_VERSION_AUXPOW_WITH_AUX || nVersion == BLOCK_VERSION_AUXPOW_WITHOUT_AUX); +} + uint256 static GetOrphanRoot(const uint256& hash) { map<uint256, COrphanBlock*>::iterator it = mapOrphanBlocks.find(hash); @@ -1113,11 +1220,11 @@ int64_t GetBlockValue(int nHeight, int64_t nFees, uint256 prevHash) int rand = generateMTRandom(seed, 999999); int rand1 = 0; - if(nHeight < 100000) + if(nHeight < 100000 && !Params().SimplifiedRewards()) { nSubsidy = (1 + rand) * COIN; } - else if(nHeight < 145000) + else if(nHeight < 145000 && !Params().SimplifiedRewards()) { cseed_str = prevHash.ToString().substr(7,7); cseed = cseed_str.c_str(); @@ -1144,6 +1251,7 @@ static const int64_t nTargetSpacing = 60; // Dogecoin: 1 minute static const int64_t nInterval = nTargetTimespan / nTargetSpacing; static const int64_t nDiffChangeTarget = 145000; // Patch effective @ block 145000 +static const int64_t nTestnetResetTargetFix = 157500; // Testnet enables target reset at block 157500 // // minimum amount of work that could possibly be required nTime after @@ -1186,7 +1294,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead bool fNewDifficultyProtocol = (nHeight >= nDiffChangeTarget); int64_t retargetTimespan = nTargetTimespan; - int64_t retargetSpacing = nTargetSpacing; int64_t retargetInterval = nInterval; if (fNewDifficultyProtocol) { @@ -1198,18 +1305,26 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead if (pindexLast == NULL) return nProofOfWorkLimit; + if (TestNet() && pindexLast->nHeight >= nTestnetResetTargetFix && pblock->nTime > pindexLast->nTime + nTargetSpacing*2) + { + // Special difficulty rule for testnet: + // If the new block's timestamp is more than 2* nTargetSpacing minutes + // then allow mining of a min-difficulty block. + return nProofOfWorkLimit; + } + // Only change once per interval if ((pindexLast->nHeight+1) % retargetInterval != 0) { if (TestNet()) { - // Special difficulty rule for testnet: - // If the new block's timestamp is more than 2* nTargetSpacing minutes - // then allow mining of a min-difficulty block. if (pblock->nTime > pindexLast->nTime + nTargetSpacing*2) - return nProofOfWorkLimit; - else { + // Special difficulty rule for testnet: + // If the new block's timestamp is more than 2* nTargetSpacing minutes + // then allow mining of a min-difficulty block. + return nProofOfWorkLimit; + } else { // Return the last non-special-min-difficulty-rules-block const CBlockIndex* pindex = pindexLast; while (pindex->pprev && pindex->nHeight % retargetInterval != 0 && pindex->nBits == nProofOfWorkLimit) @@ -1234,53 +1349,53 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead // Limit adjustment step int64_t nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); - LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan); + int64_t nModulatedTimespan = nActualTimespan; if (fNewDifficultyProtocol) //DigiShield implementation - thanks to RealSolid & WDC for this code { // amplitude filter - thanks to daft27 for this code - nActualTimespan = retargetTimespan + (nActualTimespan - retargetTimespan)/8; + nModulatedTimespan = retargetTimespan + (nModulatedTimespan - retargetTimespan)/8; - if (nActualTimespan < (retargetTimespan - (retargetTimespan/4)) ) nActualTimespan = (retargetTimespan - (retargetTimespan/4)); - if (nActualTimespan > (retargetTimespan + (retargetTimespan/2)) ) nActualTimespan = (retargetTimespan + (retargetTimespan/2)); + if (nModulatedTimespan < (retargetTimespan - (retargetTimespan/4)) ) nModulatedTimespan = (retargetTimespan - (retargetTimespan/4)); + if (nModulatedTimespan > (retargetTimespan + (retargetTimespan/2)) ) nModulatedTimespan = (retargetTimespan + (retargetTimespan/2)); } else if (pindexLast->nHeight+1 > 10000) { - if (nActualTimespan < nTargetTimespan/4) - nActualTimespan = nTargetTimespan/4; - if (nActualTimespan > nTargetTimespan*4) - nActualTimespan = nTargetTimespan*4; + if (nModulatedTimespan < nTargetTimespan/4) + nModulatedTimespan = nTargetTimespan/4; + if (nModulatedTimespan > nTargetTimespan*4) + nModulatedTimespan = nTargetTimespan*4; } else if (pindexLast->nHeight+1 > 5000) { - if (nActualTimespan < nTargetTimespan/8) - nActualTimespan = nTargetTimespan/8; - if (nActualTimespan > nTargetTimespan*4) - nActualTimespan = nTargetTimespan*4; + if (nModulatedTimespan < nTargetTimespan/8) + nModulatedTimespan = nTargetTimespan/8; + if (nModulatedTimespan > nTargetTimespan*4) + nModulatedTimespan = nTargetTimespan*4; } else { - if (nActualTimespan < nTargetTimespan/16) - nActualTimespan = nTargetTimespan/16; - if (nActualTimespan > nTargetTimespan*4) - nActualTimespan = nTargetTimespan*4; + if (nModulatedTimespan < nTargetTimespan/16) + nModulatedTimespan = nTargetTimespan/16; + if (nModulatedTimespan > nTargetTimespan*4) + nModulatedTimespan = nTargetTimespan*4; } // Retarget CBigNum bnNew; bnNew.SetCompact(pindexLast->nBits); - bnNew *= nActualTimespan; + bnNew *= nModulatedTimespan; bnNew /= retargetTimespan; if (bnNew > Params().ProofOfWorkLimit()) bnNew = Params().ProofOfWorkLimit(); + unsigned int nNewBits = bnNew.GetCompact(); + /// debug print - LogPrintf("GetNextWorkRequired RETARGET\n"); - LogPrintf("nTargetTimespan = %d nActualTimespan = %d\n", retargetTimespan, nActualTimespan); - LogPrintf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString()); - LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString()); + LogPrintf("GetNextWorkRequired() : RETARGET; target: %d, actual: %d, modulated: %d, before: %08x, after: %08x\n", + retargetTimespan, nActualTimespan, nModulatedTimespan, pindexLast->nBits, nNewBits); - return bnNew.GetCompact(); + return nNewBits; } bool CheckProofOfWork(uint256 hash, unsigned int nBits) @@ -1299,14 +1414,9 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } -// Return maximum amount of blocks that other nodes claim to have -int GetNumBlocksOfPeers() -{ - return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); -} - bool IsInitialBlockDownload() { + LOCK(cs_main); if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) return true; static int64_t nLastUpdate; @@ -1324,19 +1434,39 @@ bool fLargeWorkForkFound = false; bool fLargeWorkInvalidChainFound = false; CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; +// Temporarily declare this here so CheckForkWarningConditions() knows it exists +int GetAuxPowStartBlock(); + void CheckForkWarningConditions() { + AssertLockHeld(cs_main); // Before we get past initial download, we cannot reliably alert about forks // (we assume we don't get stuck on a fork before the last checkpoint) if (IsInitialBlockDownload()) return; + + // For an hour before, and a day after the AuxPoW hard fork, disable + // warnings. + int proximityToAuxPoWFork = chainActive.Height() - GetAuxPowStartBlock(); + + if (proximityToAuxPoWFork < 0) { + // One hour of one-minute blocks + if (proximityToAuxPoWFork >= -60) { + return; + } + } else { + // 1440 is 24 * 60 (24 hours of one-minute blocks) + if (proximityToAuxPoWFork < 1440) { + return; + } + } - // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) + // If our best fork is no longer within 360 blocks (+/- 6 hours if no one mines it) // of our head, drop it - if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) + if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 360) pindexBestForkTip = NULL; - if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6).getuint256())) + if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 30).getuint256())) { if (!fLargeWorkForkFound) { @@ -1371,6 +1501,7 @@ void CheckForkWarningConditions() void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) { + AssertLockHeld(cs_main); // If we are on a fork that is sufficiently large, set a warning flag CBlockIndex* pfork = pindexNewForkTip; CBlockIndex* plonger = chainActive.Tip(); @@ -1401,6 +1532,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) CheckForkWarningConditions(); } +// Requires cs_main. void Misbehaving(NodeId pnode, int howmuch) { if (howmuch == 0) @@ -1453,7 +1585,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state } if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + pblocktree->WriteBlockIndex(*pindex); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); } @@ -1596,14 +1728,26 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); } else if (!check()) { - if (flags & SCRIPT_VERIFY_STRICTENC) { - // For now, check whether the failure was caused by non-canonical - // encodings or not; if so, don't trigger DoS protection. - CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); + if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { + // Check whether the failure was caused by a + // non-mandatory script verification check, such as + // non-standard DER encodings or non-null dummy + // arguments; if so, don't trigger DoS protection to + // avoid splitting the network between upgraded and + // non-upgraded nodes. + CScriptCheck check(coins, tx, i, + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, 0); if (check()) - return state.Invalid(false, REJECT_NONSTANDARD, "non-canonical"); + return state.Invalid(false, REJECT_NONSTANDARD, "non-mandatory-script-verify-flag"); } - return state.DoS(100,false, REJECT_NONSTANDARD, "non-canonical"); + // Failures of other flags indicate a transaction that is + // invalid in new blocks, e.g. a invalid P2SH. We DoS ban + // such nodes as they are not following the protocol. That + // said during an upgrade careful thought should be taken + // as to the correct behavior - we may want to continue + // peering with non-upgraded nodes even after a soft-fork + // super-majority vote has passed. + return state.DoS(100,false, REJECT_INVALID, "mandatory-script-verify-flag-failed"); } } } @@ -1736,8 +1880,9 @@ void ThreadScriptCheck() { bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { + AssertLockHeld(cs_main); // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, pindex->nHeight, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -1769,9 +1914,11 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (fEnforceBIP30) { for (unsigned int i = 0; i < block.vtx.size(); i++) { uint256 hash = block.GetTxHash(i); - if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) - return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), + if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) { + std::string errorMsg = "ConnectBlock() : tried to overwrite transaction " + (hash.GetHex()); + return state.DoS(100, error(errorMsg.data()), REJECT_INVALID, "bad-txns-BIP30"); + } } } @@ -1849,7 +1996,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%d vs limit=%d)", block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees, prevHash)), - REJECT_INVALID, "bad-cb-amount"); + REJECT_INVALID, "bad-cb-amount"); if (!control.Wait()) return state.DoS(100, false); @@ -1860,8 +2007,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (fJustCheck) return true; + // Correct transaction counts. + pindex->nTx = block.vtx.size(); + if (pindex->pprev) + pindex->nChainTx = pindex->pprev->nChainTx + block.vtx.size(); + // Write undo information to disk - if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) + if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; @@ -1875,10 +2027,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C pindex->nStatus |= BLOCK_HAVE_UNDO; } - pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); - CDiskBlockIndex blockindex(pindex); - if (!pblocktree->WriteBlockIndex(blockindex)) + if (!pblocktree->WriteBlockIndex(*pindex)) return state.Abort(_("Failed to write block index")); } @@ -1942,7 +2093,7 @@ void static UpdateTip(CBlockIndex *pindexNew) { const CBlockIndex* pindex = chainActive.Tip(); for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) + if (pindex->nVersion > CBlock::CURRENT_VERSION && !IsAuxPowVersion(pindex->nVersion)) ++nUpgraded; pindex = pindex->pprev; } @@ -2069,10 +2220,11 @@ void static FindMostWorkChain() { CBlockIndex *pindexTest = pindexNew; bool fInvalidAncestor = false; while (pindexTest && !chainActive.Contains(pindexTest)) { - if (pindexTest->nStatus & BLOCK_FAILED_MASK) { + if (!pindexTest->IsValid(BLOCK_VALID_TRANSACTIONS) || !(pindexTest->nStatus & BLOCK_HAVE_DATA)) { // Candidate has an invalid ancestor, remove entire chain from the set. if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork) - pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; + pindexBestInvalid = pindexNew; + CBlockIndex *pindexFailed = pindexNew; while (pindexTest != pindexFailed) { pindexFailed->nStatus |= BLOCK_FAILED_CHILD; setBlockIndexValid.erase(pindexFailed); @@ -2099,6 +2251,7 @@ void static FindMostWorkChain() { // Try to activate to the most-work chain (thereby connecting it). bool ActivateBestChain(CValidationState &state) { + LOCK(cs_main); CBlockIndex *pindexOldTip = chainActive.Tip(); bool fComplete = false; while (!fComplete) { @@ -2145,21 +2298,22 @@ bool ActivateBestChain(CValidationState &state) { return true; } -bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) + +CBlockIndex* AddToBlockIndex(CBlockHeader& block) { // Check for duplicate uint256 hash = block.GetHash(); - if (mapBlockIndex.count(hash)) - return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString()), 0, "duplicate"); + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hash); + if (it != mapBlockIndex.end()) + return it->second; // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); + assert(pindexNew); { LOCK(cs_nBlockSequenceId); pindexNew->nSequenceId = nBlockSequenceId++; } - assert(pindexNew); - mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); @@ -2168,22 +2322,48 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } - pindexNew->nTx = block.vtx.size(); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256(); - pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; + pindexNew->RaiseValidity(BLOCK_VALID_TREE); + + return pindexNew; +} + + +// Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). +bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos) +{ + pindexNew->nTx = block.vtx.size(); + if (pindexNew->pprev) { + // Not the genesis block. + if (pindexNew->pprev->nChainTx) { + // This parent's block's total number transactions is known, so compute outs. + pindexNew->nChainTx = pindexNew->pprev->nChainTx + pindexNew->nTx; + } else { + // The total number of transactions isn't known yet. + // We will compute it when the block is connected. + pindexNew->nChainTx = 0; + } + } else { + // Genesis block. + pindexNew->nChainTx = pindexNew->nTx; + } pindexNew->nFile = pos.nFile; pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; - pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; - setBlockIndexValid.insert(pindexNew); + pindexNew->nStatus |= BLOCK_HAVE_DATA; - if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + if (pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS)) + setBlockIndexValid.insert(pindexNew); + + /* write both the immutible data (CDiskBlockIndex) and the mutable data (BlockIndex) */ + if (!pblocktree->WriteDiskBlockIndex(CDiskBlockIndex(pindexNew, block.auxpow)) || !pblocktree->WriteBlockIndex(*pindexNew)) return state.Abort(_("Failed to write block index")); // New best? if (!ActivateBestChain(state)) return false; + LOCK(cs_main); if (pindexNew == chainActive.Tip()) { // Clear fork warning if its no longer applicable @@ -2202,6 +2382,58 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos return true; } +// to enable merged mining: +// - set a block from which it will be enabled +// - set a unique chain ID +// each merged minable scrypt_1024_1_1_256 coin should have a different one +// (if two have the same ID, they can't be merge mined together) +int GetAuxPowStartBlock() +{ + if (TestNet()) + return AUXPOW_START_TESTNET; + else + return AUXPOW_START_MAINNET; +} + +bool CBlockHeader::CheckProofOfWork(int nHeight) const +{ + if (nHeight >= GetAuxPowStartBlock()) + { + // Prevent same work from being submitted twice: + // - this block must have our chain ID + // - parent block must not have the same chain ID (see CAuxPow::Check) + // - index of this chain in chain merkle tree must be pre-determined (see CAuxPow::Check) + if (!TestNet() && nHeight != INT_MAX && GetChainID() != AUXPOW_CHAIN_ID) + return error("CheckProofOfWork() : block does not have our chain ID"); + + if (auxpow.get() != NULL) + { + if (!auxpow->Check(GetHash(), GetChainID())) + return error("CheckProofOfWork() : AUX POW is not valid"); + // Check proof of work matches claimed amount + if (!::CheckProofOfWork(auxpow->GetParentBlockHash(), nBits)) + return error("CheckProofOfWork() : AUX proof of work failed"); + } + else + { + // Check proof of work matches claimed amount + if (!::CheckProofOfWork(GetPoWHash(), nBits)) + return error("CheckProofOfWork() : proof of work failed"); + } + } + else + { + if (auxpow.get() != NULL) + { + return error("CheckProofOfWork() : AUX POW is not allowed at this block"); + } + + // Check if proof of work marches claimed amount + if (!::CheckProofOfWork(GetPoWHash(), nBits)) + return error("CheckProofOfWork() : proof of work failed"); + } + return true; +} bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { @@ -2297,27 +2529,55 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, int nHeight, bool fCheckPOW) +{ + // Check proof of work matches claimed amount + if (fCheckPOW && !block.CheckProofOfWork(nHeight)) + return state.DoS(50, error("CheckBlockHeader() : proof of work failed"), + REJECT_INVALID, "high-hash"); + + // Check timestamp + if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + return state.Invalid(error("CheckBlockHeader() : block timestamp too far in the future"), + REJECT_INVALID, "time-too-new"); + + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64_t deltaTime = block.GetBlockTime() - pcheckpoint->nTime; + if (deltaTime < 0) + { + return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"), + REJECT_CHECKPOINT, "time-too-old"); + } + CBigNum bnNewBlock; + bnNewBlock.SetCompact(block.nBits); + CBigNum bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (bnNewBlock > bnRequired) + { + return state.DoS(100, error("CheckBlockHeader() : block with too little proof-of-work"), + REJECT_INVALID, "bad-diffbits"); + } + } + + return true; +} -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) +bool CheckBlock(const CBlock& block, CValidationState& state, int nHeight, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context // that can be verified before saving an orphan block. + if (!CheckBlockHeader(block, state, nHeight, fCheckPOW)) + return false; + // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock() : size limits failed"), REJECT_INVALID, "bad-blk-length"); - // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(), block.nBits)) - return state.DoS(50, error("CheckBlock() : proof of work failed"), - REJECT_INVALID, "high-hash"); - - // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), - REJECT_INVALID, "time-too-new"); - // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), @@ -2364,12 +2624,18 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) +bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) { + AssertLockHeld(cs_main); // Check for duplicate uint256 hash = block.GetHash(); - if (mapBlockIndex.count(hash)) - return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"), 0, "duplicate"); + std::map<uint256, CBlockIndex*>::iterator miSelf = mapBlockIndex.find(hash); + CBlockIndex *pindex = NULL; + if (miSelf != mapBlockIndex.end()) { + pindex = miSelf->second; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return state.Invalid(error("AcceptBlock() : block is marked invalid"), 0, "duplicate"); + } // Get prev block index CBlockIndex* pindexPrev = NULL; @@ -2391,12 +2657,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) return state.Invalid(error("AcceptBlock() : block's timestamp is too early"), REJECT_INVALID, "time-too-old"); - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) - return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"), - REJECT_INVALID, "bad-txns-nonfinal"); - // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight), @@ -2417,18 +2677,58 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) REJECT_OBSOLETE, "bad-version"); } } - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (block.nVersion >= 2) + } + + if (pindex == NULL) + pindex = AddToBlockIndex(block); + + if (ppindex) + *ppindex = pindex; + + return true; +} + +bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp) +{ + AssertLockHeld(cs_main); + + CBlockIndex *&pindex = *ppindex; + + if (!AcceptBlockHeader(block, state, &pindex)) + return false; + + int nHeight = pindex->nHeight; + + if (!CheckBlock(block, state, nHeight)) { + if (state.Invalid() && !state.CorruptionPossible()) { + pindex->nStatus |= BLOCK_FAILED_VALID; + } + return false; + } + + uint256 hash = pindex->GetBlockHash(); + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { + pindex->nStatus |= BLOCK_FAILED_VALID; + return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"), + REJECT_INVALID, "bad-txns-nonfinal"); + } + + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + if (block.nVersion >= 2) + { + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) || + (TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100))) { - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) || - (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) - { - CScript expect = CScript() << nHeight; - if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || - !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), - REJECT_INVALID, "bad-cb-height"); + CScript expect = CScript() << nHeight; + if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { + pindex->nStatus |= BLOCK_FAILED_VALID; + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), + REJECT_INVALID, "bad-cb-height"); } } } @@ -2444,8 +2744,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos)) return state.Abort(_("Failed to write block")); - if (!AddToBlockIndex(block, state, blockPos)) - return error("AcceptBlock() : AddToBlockIndex failed"); + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + return error("AcceptBlock() : ReceivedBlockTransactions failed"); } catch(std::runtime_error &e) { return state.Abort(_("System error: ") + e.what()); } @@ -2480,6 +2780,7 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns int64_t CBlockIndex::GetMedianTime() const { + AssertLockHeld(cs_main); const CBlockIndex* pindex = this; for (int i = 0; i < nMedianTimeSpan/2; i++) { @@ -2490,8 +2791,51 @@ int64_t CBlockIndex::GetMedianTime() const return pindex->GetMedianTimePast(); } +std::string CBlockIndex::ToString() const +{ + return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", + pprev, nHeight, + hashMerkleRoot.ToString().substr(0,10).c_str(), + GetBlockHash().ToString().c_str()); +} + +std::string CDiskBlockIndex::ToString() const +{ + std::string str = "CDiskBlockIndex("; + str += CBlockIndex::ToString(); + str += strprintf("\n hashBlock=%s, hashPrev=%s, hashParentBlock=%s)", + GetBlockHash().ToString().c_str(), + hashPrev.ToString().c_str(), + (auxpow.get() != NULL) ? auxpow->GetParentBlockHash().ToString().substr(0,20).c_str() : "-"); + return str; +} + +CBlockHeader CBlockIndex::GetBlockHeader() const +{ + CBlockHeader block; + + if (nVersion & BLOCK_VERSION_AUXPOW) { + CDiskBlockIndex diskblockindex; + // auxpow is not in memory, load CDiskBlockHeader + // from database to get it + + pblocktree->ReadDiskBlockIndex(*phashBlock, diskblockindex); + block.auxpow = diskblockindex.auxpow; + } + + block.nVersion = nVersion; + if (pprev) + block.hashPrevBlock = pprev->GetBlockHash(); + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; +} + void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) { + AssertLockHeld(cs_main); // Filter out duplicate requests if (pindexBegin == pnode->pindexLastGetBlocksBegin && hashEnd == pnode->hashLastGetBlocksEnd) return; @@ -2513,36 +2857,15 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate"); // Preliminary checks - if (!CheckBlock(*pblock, state)) { + if (!CheckBlock(*pblock, state, INT_MAX)) { if (state.CorruptionPossible()) mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); return error("ProcessBlock() : CheckBlock FAILED"); } - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); - if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) - { - // Extra checks to prevent "fill up memory by spamming with bogus blocks" - int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; - if (deltaTime < 0) - { - return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"), - REJECT_CHECKPOINT, "time-too-old"); - } - CBigNum bnNewBlock; - bnNewBlock.SetCompact(pblock->nBits); - CBigNum bnRequired; - bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); - if (bnNewBlock > bnRequired) - { - return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"), - REJECT_INVALID, "bad-diffbits"); - } - } - - - // If we don't already have its previous block, shunt it off to holding area until we get it - if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) + // If we don't already have its previous block (with full data), shunt it off to holding area until we get it + std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pblock->hashPrevBlock); + if (pblock->hashPrevBlock != 0 && (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA))) { LogPrintf("ProcessBlock: ORPHAN BLOCK %lu, prev=%s\n", (unsigned long)mapOrphanBlocks.size(), pblock->hashPrevBlock.ToString()); @@ -2567,7 +2890,9 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl } // Store to disk - if (!AcceptBlock(*pblock, state, dbp)) + CBlockIndex *pindex = NULL; + bool ret = AcceptBlock(*pblock, state, &pindex, dbp); + if (!ret) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2588,7 +2913,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl block.BuildMerkleTree(); // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) CValidationState stateDummy; - if (AcceptBlock(block, stateDummy)) + CBlockIndex *pindexChild = NULL; + if (AcceptBlock(block, stateDummy, &pindexChild)) vWorkQueue.push_back(mi->second->hashBlock); mapOrphanBlocks.erase(mi->second->hashBlock); delete mi->second; @@ -2851,7 +3177,7 @@ bool static LoadBlockIndexDB() CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork().getuint256(); pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) + if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) setBlockIndexValid.insert(pindex); if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindex; @@ -2887,6 +3213,7 @@ bool static LoadBlockIndexDB() bool VerifyDB(int nCheckLevel, int nCheckDepth) { + LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; @@ -2912,7 +3239,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(block, state)) + if (nCheckLevel >= 1 && !CheckBlock(block, state, pindex->nHeight)) return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -2976,6 +3303,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { + LOCK(cs_main); // Check whether we're already initialized if (chainActive.Genesis() != NULL) return true; @@ -2997,7 +3325,8 @@ bool InitBlockIndex() { return error("LoadBlockIndex() : FindBlockPos failed"); if (!WriteBlockToDisk(block, blockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!AddToBlockIndex(block, state, blockPos)) + CBlockIndex *pindex = AddToBlockIndex(block); + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("LoadBlockIndex() : genesis block not accepted"); } catch(std::runtime_error &e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); @@ -3011,6 +3340,7 @@ bool InitBlockIndex() { void PrintBlockTree() { + AssertLockHeld(cs_main); // pre-compute tree structure map<CBlockIndex*, vector<CBlockIndex*> > mapNext; for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) @@ -3054,7 +3384,7 @@ void PrintBlockTree() // print item CBlock block; ReadBlockFromDisk(block, pindex); - LogPrintf("%d (blk%05u.dat:0x%x) %s tx %" PRIszu"", + LogPrintf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"\n", pindex->nHeight, pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()), @@ -3103,11 +3433,11 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) unsigned int nSize = 0; try { // locate a header - unsigned char buf[4]; + unsigned char buf[MESSAGE_START_SIZE]; blkdat.FindByte(Params().MessageStart()[0]); nRewind = blkdat.GetPos()+1; blkdat >> FLATDATA(buf); - if (memcmp(buf, Params().MessageStart(), 4)) + if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE)) continue; // read size blkdat >> nSize; @@ -3137,7 +3467,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) break; } } catch (std::exception &e) { - LogPrintf("%s : Deserialize or I/O error - %s", __PRETTY_FUNCTION__, e.what()); + LogPrintf("%s : Deserialize or I/O error - %s", __func__, e.what()); } } fclose(fileIn); @@ -3277,14 +3607,14 @@ void static ProcessGetData(CNode* pfrom) int nHeight = mi->second->nHeight; CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); if (pcheckpoint && nHeight < pcheckpoint->nHeight) { - if (!chainActive.Contains(mi->second)) - { - LogPrintf("ProcessGetData(): ignoring request for old block that isn't in the main chain\n"); - } else { - send = true; - } + if (!chainActive.Contains(mi->second)) + { + LogPrintf("ProcessGetData(): ignoring request for old block that isn't in the main chain\n"); + } else { + send = true; + } } else { - send = true; + send = true; } } if (send) @@ -3388,7 +3718,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; } - + { + LOCK(cs_main); + State(pfrom->GetId())->nLastBlockProcess = GetTimeMicros(); + } @@ -3494,9 +3827,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), addrFrom.ToString(), pfrom->addr.ToString()); AddTimeData(pfrom->addr, nTime); - - LOCK(cs_main); - cPeerBlockCounts.input(pfrom->nStartingHeight); } @@ -3591,15 +3921,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return error("message inv size() = %" PRIszu"", vInv.size()); } - // find last block in inv vector - unsigned int nLastBlock = (unsigned int)(-1); - for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { - if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { - nLastBlock = vInv.size() - 1 - nInv; - break; - } - } - LOCK(cs_main); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) @@ -3613,17 +3934,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) { - if (!fImporting && !fReindex) - pfrom->AskFor(inv); + if (!fImporting && !fReindex) { + if (inv.type == MSG_BLOCK) + AddBlockToQueue(pfrom->GetId(), inv.hash); + else + pfrom->AskFor(inv); + } } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(inv.hash)); - } else if (nInv == nLastBlock) { - // In case we are on a very long side-chain, it is possible that we already have - // the last block in an inv bundle sent in response to getblocks. Try to detect - // this situation and push another getblocks to continue. - PushGetBlocks(pfrom, mapBlockIndex[inv.hash], uint256(0)); - if (fDebug) - LogPrintf("force request: %s\n", inv.ToString()); } // Track requests for our stuff @@ -3804,7 +4122,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } int nDoS = 0; if (state.IsInvalid(nDoS)) - { + { LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->addr.ToString(), pfrom->cleanSubVer, state.GetRejectReason()); @@ -3830,6 +4148,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LOCK(cs_main); // Remember who we got this block from. mapBlockSource[inv.hash] = pfrom->GetId(); + MarkBlockAsReceived(inv.hash, pfrom->GetId()); CValidationState state; ProcessBlock(state, pfrom, &block); @@ -4234,6 +4553,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } + TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() + if (!lockMain) + return true; + // Address refresh broadcast static int64_t nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) @@ -4284,10 +4607,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("addr", vAddr); } - TRY_LOCK(cs_main, lockMain); - if (!lockMain) - return true; - CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { if (pto->addr.IsLocal()) @@ -4366,12 +4685,38 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("inv", vInv); + // Detect stalled peers. Require that blocks are in flight, we haven't + // received a (requested) block in one minute, and that all blocks are + // in flight for over two minutes, since we first had a chance to + // process an incoming block. + int64_t nNow = GetTimeMicros(); + if (!pto->fDisconnect && state.nBlocksInFlight && + state.nLastBlockReceive < state.nLastBlockProcess - BLOCK_DOWNLOAD_TIMEOUT*1000000 && + state.vBlocksInFlight.front().nTime < state.nLastBlockProcess - 2*BLOCK_DOWNLOAD_TIMEOUT*1000000) { + LogPrintf("Peer %s is stalling block download, disconnecting\n", state.name.c_str()); + pto->fDisconnect = true; + } + // - // Message: getdata + // Message: getdata (blocks) // vector<CInv> vGetData; - int64_t nNow = GetTime() * 1000000; - while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) + while (!pto->fDisconnect && state.nBlocksToDownload && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + uint256 hash = state.vBlocksToDownload.front(); + vGetData.push_back(CInv(MSG_BLOCK, hash)); + MarkBlockAsInFlight(pto->GetId(), hash); + LogPrint("net", "Requesting block %s from %s\n", hash.ToString().c_str(), state.name.c_str()); + if (vGetData.size() >= 1000) + { + pto->PushMessage("getdata", vGetData); + vGetData.clear(); + } + } + + // + // Message: getdata (non-blocks) + // + while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) |