aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp184
1 files changed, 123 insertions, 61 deletions
diff --git a/src/main.cpp b/src/main.cpp
index ac3ee06f6..e78e05514 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,8 +59,8 @@ CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
-map<uint256, CDataStream*> mapOrphanTransactions;
-map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
+map<uint256, CTransaction> mapOrphanTransactions;
+map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
@@ -399,16 +399,12 @@ CBlockTreeDB *pblocktree = NULL;
// mapOrphanTransactions
//
-bool AddOrphanTx(const CDataStream& vMsg)
+bool AddOrphanTx(const CTransaction& tx)
{
- CTransaction tx;
- CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
- CDataStream* pvMsg = new CDataStream(vMsg);
-
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
@@ -416,16 +412,16 @@ bool AddOrphanTx(const CDataStream& vMsg)
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
- if (pvMsg->size() > 5000)
+ unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
+ if (sz > 5000)
{
- printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().c_str());
- delete pvMsg;
+ printf("ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str());
return false;
}
- mapOrphanTransactions[hash] = pvMsg;
+ mapOrphanTransactions[hash] = tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
+ mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(),
mapOrphanTransactions.size());
@@ -436,16 +432,13 @@ void static EraseOrphanTx(uint256 hash)
{
if (!mapOrphanTransactions.count(hash))
return;
- const CDataStream* pvMsg = mapOrphanTransactions[hash];
- CTransaction tx;
- CDataStream(*pvMsg) >> tx;
+ const CTransaction& tx = mapOrphanTransactions[hash];
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
- delete pvMsg;
mapOrphanTransactions.erase(hash);
}
@@ -456,7 +449,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
- map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
@@ -793,7 +786,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
}
}
-bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFree,
+bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs)
{
if (pfMissingInputs)
@@ -810,9 +803,9 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet");
- // Rather not work on nonstandard transactions (unless -testnet)
+ // Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
- if (!TestNet() && !IsStandardTx(tx, reason))
+ if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason))
return error("CTxMemPool::accept() : nonstandard transaction: %s",
reason.c_str());
@@ -888,7 +881,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
}
// Check for non-standard pay-to-script-hash in inputs
- if (!TestNet() && !AreInputsStandard(tx, view))
+ if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view))
return error("CTxMemPool::accept() : nonstandard transaction input");
// Note: if you modify this code to accept non-standard transactions, then
@@ -960,7 +953,7 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
}
-bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
+bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx)
{
// Add to memory pool without checking anything. Don't call this directly,
// call CTxMemPool::accept to properly check the transaction first.
@@ -1383,6 +1376,82 @@ bool IsInitialBlockDownload()
pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60);
}
+bool fLargeWorkForkFound = false;
+bool fLargeWorkInvalidChainFound = false;
+CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL;
+
+void CheckForkWarningConditions()
+{
+ // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it)
+ // of our head, drop it
+ if (pindexBestForkTip && nBestHeight - pindexBestForkTip->nHeight >= 72)
+ pindexBestForkTip = NULL;
+
+ if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256())
+ {
+ if (!fLargeWorkForkFound)
+ {
+ std::string strCmd = GetArg("-alertnotify", "");
+ if (!strCmd.empty())
+ {
+ std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
+ pindexBestForkBase->phashBlock->ToString() + std::string("'");
+ boost::replace_all(strCmd, "%s", warning);
+ boost::thread t(runCommand, strCmd); // thread runs free
+ }
+ }
+ if (pindexBestForkTip)
+ {
+ printf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n",
+ pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString().c_str(),
+ pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString().c_str());
+ fLargeWorkForkFound = true;
+ }
+ else
+ {
+ printf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n");
+ fLargeWorkInvalidChainFound = true;
+ }
+ }
+ else
+ {
+ fLargeWorkForkFound = false;
+ fLargeWorkInvalidChainFound = false;
+ }
+}
+
+void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
+{
+ // If we are on a fork that is sufficiently large, set a warning flag
+ CBlockIndex* pfork = pindexNewForkTip;
+ CBlockIndex* plonger = pindexBest;
+ while (pfork && pfork != plonger)
+ {
+ while (plonger && plonger->nHeight > pfork->nHeight)
+ plonger = plonger->pprev;
+ if (pfork == plonger)
+ break;
+ pfork = pfork->pprev;
+ }
+
+ // We define a condition which we should warn the user about as a fork of at least 7 blocks
+ // who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours
+ // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
+ // hash rate operating on the fork.
+ // or a chain that is entirely longer than ours and invalid (note that this should be detected by both)
+ // We define it this way because it allows us to only store the highest fork tip (+ base) which meets
+ // the 7-block condition and from this always have the most-likely-to-cause-warning fork
+ if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) &&
+ pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7).getuint256() &&
+ nBestHeight - pindexNewForkTip->nHeight < 72)
+ {
+ pindexBestForkTip = pindexNewForkTip;
+ pindexBestForkBase = pfork;
+ }
+
+ CheckForkWarningConditions();
+}
+
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (pindexNew->nChainWork > nBestInvalidWork)
@@ -1398,8 +1467,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n",
hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str());
- if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256())
- printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n");
+ CheckForkWarningConditions();
}
void static InvalidBlockFound(CBlockIndex *pindex) {
@@ -1938,7 +2006,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
}
// Disconnect shorter branch
- vector<CTransaction> vResurrect;
+ list<CTransaction> vResurrect;
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
if (!ReadBlockFromDisk(block, pindex))
@@ -1952,9 +2020,9 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
// Queue memory transactions to resurrect.
// We only do this for blocks after the last checkpoint (reorganisation before that
// point should only happen with -reindex/-loadblock, or a misbehaving peer.
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ BOOST_REVERSE_FOREACH(const CTransaction& tx, block.vtx)
if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
- vResurrect.push_back(tx);
+ vResurrect.push_front(tx);
}
// Connect longer branch
@@ -2110,11 +2178,14 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos
if (pindexNew == pindexBest)
{
+ // Clear fork warning if its no longer applicable
+ CheckForkWarningConditions();
// Notify UI to display prev block's coinbase if it was ours
static uint256 hashPrevBestCoinBase;
UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.GetTxHash(0);
- }
+ } else
+ CheckForkWarningConditionsOnNewFork(pindexNew);
if (!pblocktree->Flush())
return state.Abort(_("Failed to sync block index"));
@@ -2271,7 +2342,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkle root
- if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree())
+ if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back())
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
return true;
@@ -3073,11 +3144,15 @@ string GetWarnings(string strFor)
strStatusBar = strMiscWarning;
}
- // Longer invalid proof-of-work chain
- if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256())
+ if (fLargeWorkForkFound)
+ {
+ nPriority = 2000;
+ strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ }
+ else if (fLargeWorkInvalidChainFound)
{
nPriority = 2000;
- strStatusBar = strRPC = _("Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.");
+ strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
// Alerts
@@ -3598,21 +3673,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
- // Truncate messages to the size of the tx in them
- unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
- unsigned int oldSize = vMsg.size();
- if (nSize < oldSize) {
- vMsg.resize(nSize);
- printf("truncating oversized TX %s (%u -> %u)\n",
- tx.GetHash().ToString().c_str(),
- oldSize, nSize);
- }
-
bool fMissingInputs = false;
CValidationState state;
if (mempool.accept(state, tx, true, &fMissingInputs))
{
- RelayTransaction(tx, inv.hash, vMsg);
+ RelayTransaction(tx, inv.hash);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
@@ -3621,31 +3686,31 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
- for (map<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
+ for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
- const CDataStream& vMsg = *((*mi).second);
- CTransaction tx;
- CDataStream(vMsg) >> tx;
- CInv inv(MSG_TX, tx.GetHash());
+ const uint256& orphanHash = *mi;
+ const CTransaction& orphanTx = mapOrphanTransactions[orphanHash];
bool fMissingInputs2 = false;
- // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned)
+ // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
+ // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
+ // anyone relaying LegitTxX banned)
CValidationState stateDummy;
- if (mempool.accept(stateDummy, tx, true, &fMissingInputs2))
+ if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2))
{
- printf(" accepted orphan tx %s\n", inv.hash.ToString().c_str());
- RelayTransaction(tx, inv.hash, vMsg);
- mapAlreadyAskedFor.erase(inv);
- vWorkQueue.push_back(inv.hash);
- vEraseQueue.push_back(inv.hash);
+ printf(" accepted orphan tx %s\n", orphanHash.ToString().c_str());
+ RelayTransaction(orphanTx, orphanHash);
+ mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash));
+ vWorkQueue.push_back(orphanHash);
+ vEraseQueue.push_back(orphanHash);
}
else if (!fMissingInputs2)
{
// invalid or too-little-fee orphan
- vEraseQueue.push_back(inv.hash);
- printf(" removed orphan tx %s\n", inv.hash.ToString().c_str());
+ vEraseQueue.push_back(orphanHash);
+ printf(" removed orphan tx %s\n", orphanHash.ToString().c_str());
}
}
}
@@ -3655,7 +3720,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
else if (fMissingInputs)
{
- AddOrphanTx(vMsg);
+ AddOrphanTx(tx);
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
@@ -4142,9 +4207,6 @@ public:
mapOrphanBlocks.clear();
// orphan transactions
- std::map<uint256, CDataStream*>::iterator it3 = mapOrphanTransactions.begin();
- for (; it3 != mapOrphanTransactions.end(); it3++)
- delete (*it3).second;
mapOrphanTransactions.clear();
}
} instance_of_cmaincleanup;