aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp172
1 files changed, 120 insertions, 52 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index fc1f6477d..3b8118b03 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -145,6 +145,12 @@ private:
*/
std::set<CBlockIndex*> m_failed_blocks;
+ /**
+ * the ChainState CriticalSection
+ * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
+ */
+ CCriticalSection m_cs_chainstate;
+
public:
CChain chainActive;
BlockMap mapBlockIndex;
@@ -199,7 +205,7 @@ private:
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state);
CBlockIndex* FindMostWorkChain();
- bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
+ void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params);
@@ -571,17 +577,17 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (tx.IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "coinbase");
- // Reject transactions with witness before segregated witness activates (override with -prematurewitness)
- bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus());
- if (!gArgs.GetBoolArg("-prematurewitness", false) && tx.HasWitness() && !witnessEnabled) {
- return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
- }
-
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason;
- if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled))
+ if (fRequireStandard && !IsStandardTx(tx, reason))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
+ // Do not work on transactions that are too small.
+ // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
+ // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
+ if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
+ return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
+
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
@@ -645,7 +651,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
view.SetBackend(viewMemPool);
// do all inputs exist?
- for (const CTxIn txin : tx.vin) {
+ for (const CTxIn& txin : tx.vin) {
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
@@ -951,7 +957,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
// Remove conflicting transactions from the mempool
- for (const CTxMemPool::txiter it : allConflicting)
+ for (CTxMemPool::txiter it : allConflicting)
{
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
it->GetTx().GetHash().ToString(),
@@ -1125,6 +1131,52 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
return true;
}
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start)
+{
+ CDiskBlockPos hpos = pos;
+ hpos.nPos -= 8; // Seek back 8 bytes for meta header
+ CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
+ }
+
+ try {
+ CMessageHeader::MessageStartChars blk_start;
+ unsigned int blk_size;
+
+ filein >> blk_start >> blk_size;
+
+ if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
+ return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
+ HexStr(blk_start, blk_start + CMessageHeader::MESSAGE_START_SIZE),
+ HexStr(message_start, message_start + CMessageHeader::MESSAGE_START_SIZE));
+ }
+
+ if (blk_size > MAX_SIZE) {
+ return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(),
+ blk_size, MAX_SIZE);
+ }
+
+ block.resize(blk_size); // Zeroing of memory is intentional here
+ filein.read((char*)block.data(), blk_size);
+ } catch(const std::exception& e) {
+ return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
+ }
+
+ return true;
+}
+
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
+{
+ CDiskBlockPos block_pos;
+ {
+ LOCK(cs_main);
+ block_pos = pindex->GetBlockPos();
+ }
+
+ return ReadRawBlockFromDisk(block, block_pos, message_start);
+}
+
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
@@ -2513,6 +2565,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
+
const CBlockIndex *pindexOldTip = chainActive.Tip();
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
@@ -2551,8 +2604,9 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
- if (!state.CorruptionPossible())
- InvalidChainFound(vpindexToConnect.back());
+ if (!state.CorruptionPossible()) {
+ InvalidChainFound(vpindexToConnect.front());
+ }
state = CValidationState();
fInvalidFound = true;
fContinue = false;
@@ -2628,6 +2682,12 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// sanely for performance or correctness!
AssertLockNotHeld(cs_main);
+ // ABC maintains a fair degree of expensive-to-calculate internal state
+ // because this function periodically releases cs_main so that it does not lock up other threads for too long
+ // during large connects - and to allow for e.g. the callback queue to drain
+ // we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time
+ LOCK(m_cs_chainstate);
+
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
@@ -2638,48 +2698,59 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
// reindex, causing memory blowup if we run too far ahead.
+ // Note that if a validationinterface callback ends up calling
+ // ActivateBestChain this may lead to a deadlock! We should
+ // probably have a DEBUG_LOCKORDER test for this in the future.
SyncWithValidationInterfaceQueue();
}
- const CBlockIndex *pindexFork;
- bool fInitialDownload;
{
LOCK(cs_main);
- ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
+ CBlockIndex* starting_tip = chainActive.Tip();
+ bool blocks_connected = false;
+ do {
+ // We absolutely may not unlock cs_main until we've made forward progress
+ // (with the exception of shutdown due to hardware issues, low disk space, etc).
+ ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
+
+ if (pindexMostWork == nullptr) {
+ pindexMostWork = FindMostWorkChain();
+ }
- CBlockIndex *pindexOldTip = chainActive.Tip();
- if (pindexMostWork == nullptr) {
- pindexMostWork = FindMostWorkChain();
- }
+ // Whether we have anything to do at all.
+ if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) {
+ break;
+ }
- // Whether we have anything to do at all.
- if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip())
- return true;
+ bool fInvalidFound = false;
+ std::shared_ptr<const CBlock> nullBlockPtr;
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
+ return false;
+ blocks_connected = true;
- bool fInvalidFound = false;
- std::shared_ptr<const CBlock> nullBlockPtr;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
- return false;
+ if (fInvalidFound) {
+ // Wipe cache, we may need another branch now.
+ pindexMostWork = nullptr;
+ }
+ pindexNewTip = chainActive.Tip();
- if (fInvalidFound) {
- // Wipe cache, we may need another branch now.
- pindexMostWork = nullptr;
- }
- pindexNewTip = chainActive.Tip();
- pindexFork = chainActive.FindFork(pindexOldTip);
- fInitialDownload = IsInitialBlockDownload();
+ for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
+ assert(trace.pblock && trace.pindex);
+ GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
+ }
+ } while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
+ if (!blocks_connected) return true;
- for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
- assert(trace.pblock && trace.pindex);
- GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
- }
+ const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
+ bool fInitialDownload = IsInitialBlockDownload();
// Notify external listeners about the new tip.
// Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
- GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
-
- // Always notify the UI if a new block tip was connected
if (pindexFork != pindexNewTip) {
+ // Notify ValidationInterface subscribers
+ GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
+
+ // Always notify the UI if a new block tip was connected
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
}
}
@@ -2703,6 +2774,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
return true;
}
+
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock));
}
@@ -2834,6 +2906,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(pindex);
+ m_failed_blocks.erase(pindex);
}
pindex = pindex->pprev;
}
@@ -2880,7 +2953,7 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2924,8 +2997,6 @@ bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationStat
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
}
}
-
- return true;
}
static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
@@ -3437,7 +3508,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// request; don't process these.
if (pindex->nChainWork < nMinimumChainWork) return true;
}
- if (fNewBlock) *fNewBlock = true;
if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
@@ -3454,14 +3524,14 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
GetMainSignals().NewPoWValidBlock(pindex, pblock);
// Write block to history file
+ if (fNewBlock) *fNewBlock = true;
try {
CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
}
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
- return error("AcceptBlock(): ReceivedBlockTransactions failed");
+ ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error: ") + e.what());
}
@@ -3764,7 +3834,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
- for (const std::pair<uint256, CBlockIndex*>& item : mapBlockIndex)
+ for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
@@ -3831,7 +3901,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<uint256, CBlockIndex*>& item : mapBlockIndex)
+ for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
@@ -3930,7 +4000,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
- if (pindex->nHeight < chainActive.Height()-nCheckDepth)
+ if (pindex->nHeight <= chainActive.Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, only go back as far as we have data.
@@ -4270,9 +4340,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = AddToBlockIndex(block);
- CValidationState state;
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
- return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state));
+ ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
} catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what());
}