aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorlangerhans <[email protected]>2014-08-24 17:09:34 +0200
committerlangerhans <[email protected]>2014-08-24 17:09:34 +0200
commit65228644e10328172e9fa3ebe64251983e1153b3 (patch)
tree8b1bcf377be305a81a73e8dd727e008c26c3b7d5 /src/main.cpp
parentMerge branch '1.7-maint' (diff)
parentMerge pull request #622 from rnicoll/1.8-dev-safemode (diff)
downloaddiscoin-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.cpp779
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))