aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp365
1 files changed, 189 insertions, 176 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index f0cccf70b7..9350735bab 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -44,7 +44,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/join.hpp>
-#include <boost/math/distributions/poisson.hpp>
#include <boost/thread.hpp>
#if defined(NDEBUG)
@@ -175,6 +174,7 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
return chain.Genesis();
}
+CCoinsViewDB *pcoinsdbview = NULL;
CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = NULL;
@@ -186,8 +186,11 @@ enum FlushStateMode {
};
// See definition for documentation
-bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
-void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
+static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
+static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
+static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
+static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL);
+static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
bool CheckFinalTx(const CTransaction &tx, int flags)
{
@@ -268,15 +271,15 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
const CTxIn& txin = tx.vin[txinIndex];
- CCoins coins;
- if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) {
+ Coin coin;
+ if (!viewMemPool.GetCoin(txin.prevout, coin)) {
return error("%s: Missing input", __func__);
}
- if (coins.nHeight == MEMPOOL_HEIGHT) {
+ if (coin.nHeight == MEMPOOL_HEIGHT) {
// Assume all mempool transaction confirm in the next block
prevheights[txinIndex] = tip->nHeight + 1;
} else {
- prevheights[txinIndex] = coins.nHeight;
+ prevheights[txinIndex] = coin.nHeight;
}
}
lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
@@ -309,15 +312,15 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
return EvaluateSequenceLocks(index, lockPair);
}
-void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
+static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
}
- std::vector<uint256> vNoSpendsRemaining;
+ std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
- BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining)
+ BOOST_FOREACH(const COutPoint& removed, vNoSpendsRemaining)
pcoinsTip->Uncache(removed);
}
@@ -392,9 +395,9 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
-bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
+static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache)
+ bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
{
const CTransaction& tx = *ptx;
const uint256 hash = tx.GetHash();
@@ -410,7 +413,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return state.DoS(100, false, REJECT_INVALID, "coinbase");
// Reject transactions with witness before segregated witness activates (override with -prematurewitness)
- bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus());
+ bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus());
if (!GetBoolArg("-prematurewitness",false) && tx.HasWitness() && !witnessEnabled) {
return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
}
@@ -487,30 +490,30 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
view.SetBackend(viewMemPool);
// do we already have it?
- bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash);
- if (view.HaveCoins(hash)) {
- if (!fHadTxInCache)
- vHashTxnToUncache.push_back(hash);
- return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
+ for (size_t out = 0; out < tx.vout.size(); out++) {
+ COutPoint outpoint(hash, out);
+ bool had_coin_in_cache = pcoinsTip->HaveCoinInCache(outpoint);
+ if (view.HaveCoin(outpoint)) {
+ if (!had_coin_in_cache) {
+ coins_to_uncache.push_back(outpoint);
+ }
+ return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
+ }
}
// do all inputs exist?
- // Note that this does not check for the presence of actual outputs (see the next check for that),
- // and only helps with filling in pfMissingInputs (to determine missing vs spent).
BOOST_FOREACH(const CTxIn txin, tx.vin) {
- if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash))
- vHashTxnToUncache.push_back(txin.prevout.hash);
- if (!view.HaveCoins(txin.prevout.hash)) {
- if (pfMissingInputs)
+ if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
+ coins_to_uncache.push_back(txin.prevout);
+ }
+ if (!view.HaveCoin(txin.prevout)) {
+ if (pfMissingInputs) {
*pfMissingInputs = true;
+ }
return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
}
}
- // are the actual inputs available?
- if (!view.HaveInputs(tx))
- return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent");
-
// Bring the best block into scope
view.GetBestBlock();
@@ -548,8 +551,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// during reorgs to ensure COINBASE_MATURITY is still met.
bool fSpendsCoinbase = false;
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
- const CCoins *coins = view.AccessCoins(txin.prevout.hash);
- if (coins->IsCoinBase()) {
+ const Coin &coin = view.AccessCoin(txin.prevout);
+ if (coin.IsCoinBase()) {
fSpendsCoinbase = true;
break;
}
@@ -739,7 +742,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
}
unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
- if (!Params().RequireStandard()) {
+ if (!chainparams.RequireStandard()) {
scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags);
}
@@ -809,19 +812,20 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return true;
}
-bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
+/** (try to) add transaction to memory pool with a specified acceptance time **/
+static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
- std::vector<uint256> vHashTxToUncache;
- bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
+ std::vector<COutPoint> coins_to_uncache;
+ bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache);
if (!res) {
- BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
+ BOOST_FOREACH(const COutPoint& hashTx, coins_to_uncache)
pcoinsTip->Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
- FlushStateToDisk(stateDummy, FLUSH_STATE_PERIODIC);
+ FlushStateToDisk(chainparams, stateDummy, FLUSH_STATE_PERIODIC);
return res;
}
@@ -829,7 +833,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
- return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee);
+ const CChainParams& chainparams = Params();
+ return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee);
}
/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
@@ -868,15 +873,8 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus
}
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
- int nHeight = -1;
- {
- const CCoinsViewCache& view = *pcoinsTip;
- const CCoins* coins = view.AccessCoins(hash);
- if (coins)
- nHeight = coins->nHeight;
- }
- if (nHeight > 0)
- pindexSlow = chainActive[nHeight];
+ const Coin& coin = AccessByTxid(*pcoinsTip, hash);
+ if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
}
if (pindexSlow) {
@@ -905,7 +903,7 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus
// CBlock and CBlockIndex
//
-bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart)
+static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart)
{
// Open history file to append
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
@@ -1018,7 +1016,7 @@ static void AlertNotify(const std::string& strMessage)
boost::thread t(runCommand, strCmd); // thread runs free
}
-void CheckForkWarningConditions()
+static void CheckForkWarningConditions()
{
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
@@ -1059,7 +1057,7 @@ void CheckForkWarningConditions()
}
}
-void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
+static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
{
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag
@@ -1124,24 +1122,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
if (!tx.IsCoinBase()) {
txundo.vprevout.reserve(tx.vin.size());
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
- CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash);
- unsigned nPos = txin.prevout.n;
-
- if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull())
- assert(false);
- // mark an outpoint spent, and construct undo information
- txundo.vprevout.push_back(CTxInUndo(coins->vout[nPos]));
- coins->Spend(nPos);
- if (coins->vout.size() == 0) {
- CTxInUndo& undo = txundo.vprevout.back();
- undo.nHeight = coins->nHeight;
- undo.fCoinBase = coins->fCoinBase;
- undo.nVersion = coins->nVersion;
- }
+ txundo.vprevout.emplace_back();
+ inputs.SpendCoin(txin.prevout, &txundo.vprevout.back());
}
}
// add outputs
- inputs.ModifyNewCoins(tx.GetHash(), tx.IsCoinBase())->FromTx(tx, nHeight);
+ AddCoins(inputs, tx, nHeight);
}
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
@@ -1163,7 +1149,12 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
return pindexPrev->nHeight + 1;
}
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
+/**
+ * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
+ * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
+ * instead of being performed inline.
+ */
+static bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
{
if (!tx.IsCoinBase())
{
@@ -1185,11 +1176,19 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
if (fScriptChecks) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
- const CCoins* coins = inputs.AccessCoins(prevout.hash);
- assert(coins);
+ const Coin& coin = inputs.AccessCoin(prevout);
+ assert(!coin.IsSpent());
+
+ // We very carefully only pass in things to CScriptCheck which
+ // are clearly committed to by tx' witness hash. This provides
+ // a sanity check that our caching is not introducing consensus
+ // failures through additional data in, eg, the coins being
+ // spent being checked as a part of CScriptCheck.
+ const CScript& scriptPubKey = coin.out.scriptPubKey;
+ const CAmount amount = coin.out.nValue;
// Verify signature
- CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata);
+ CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheStore, &txdata);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
@@ -1201,7 +1200,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
- CScriptCheck check2(*coins, tx, i,
+ CScriptCheck check2(scriptPubKey, amount, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
if (check2())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
@@ -1260,8 +1259,10 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
// Read block
uint256 hashChecksum;
+ CHashVerifier<CAutoFile> verifier(&filein); // We need a CHashVerifier as reserializing may lose data
try {
- filein >> blockundo;
+ verifier << hashBlock;
+ verifier >> blockundo;
filein >> hashChecksum;
}
catch (const std::exception& e) {
@@ -1269,10 +1270,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
}
// Verify checksum
- CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
- hasher << hashBlock;
- hasher << blockundo;
- if (hashChecksum != hasher.GetHash())
+ if (hashChecksum != verifier.GetHash())
return error("%s: Checksum mismatch", __func__);
return true;
@@ -1298,46 +1296,43 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std
} // anon namespace
+enum DisconnectResult
+{
+ DISCONNECT_OK, // All good.
+ DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
+ DISCONNECT_FAILED // Something else went wrong.
+};
+
/**
- * Apply the undo operation of a CTxInUndo to the given chain state.
- * @param undo The undo object.
+ * Restore the UTXO in a Coin at a given COutPoint
+ * @param undo The Coin to be restored.
* @param view The coins view to which to apply the changes.
* @param out The out point that corresponds to the tx input.
- * @return True on success.
+ * @return A DisconnectResult as an int
*/
-bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out)
+int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
{
bool fClean = true;
- CCoinsModifier coins = view.ModifyCoins(out.hash);
- if (undo.nHeight != 0) {
- // undo data contains height: this is the last output of the prevout tx being spent
- if (!coins->IsPruned())
- fClean = fClean && error("%s: undo data overwriting existing transaction", __func__);
- coins->Clear();
- coins->fCoinBase = undo.fCoinBase;
- coins->nHeight = undo.nHeight;
- coins->nVersion = undo.nVersion;
- } else {
- if (coins->IsPruned())
- fClean = fClean && error("%s: undo data adding output to missing transaction", __func__);
+ if (view.HaveCoin(out)) fClean = false; // overwriting transaction output
+
+ if (undo.nHeight == 0) {
+ // Missing undo metadata (height and coinbase). Older versions included this
+ // information only in undo records for the last spend of a transactions'
+ // outputs. This implies that it must be present for some other output of the same tx.
+ const Coin& alternate = AccessByTxid(view, out.hash);
+ if (!alternate.IsSpent()) {
+ undo.nHeight = alternate.nHeight;
+ undo.fCoinBase = alternate.fCoinBase;
+ } else {
+ return DISCONNECT_FAILED; // adding output for transaction without known metadata
+ }
}
- if (coins->IsAvailable(out.n))
- fClean = fClean && error("%s: undo data overwriting existing output", __func__);
- if (coins->vout.size() < out.n+1)
- coins->vout.resize(out.n+1);
- coins->vout[out.n] = undo.txout;
+ view.AddCoin(out, std::move(undo), undo.fCoinBase);
- return fClean;
+ return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
-enum DisconnectResult
-{
- DISCONNECT_OK, // All good.
- DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
- DISCONNECT_FAILED // Something else went wrong.
-};
-
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
* When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */
static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view)
@@ -1369,36 +1364,31 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex*
// Check that all outputs are available and match the outputs in the block itself
// exactly.
- {
- CCoinsModifier outs = view.ModifyCoins(hash);
- outs->ClearUnspendable();
-
- CCoins outsBlock(tx, pindex->nHeight);
- // The CCoins serialization does not serialize negative numbers.
- // No network rules currently depend on the version here, so an inconsistency is harmless
- // but it must be corrected before txout nversion ever influences a network rule.
- if (outsBlock.nVersion < 0)
- outs->nVersion = outsBlock.nVersion;
- if (*outs != outsBlock)
- fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted");
-
- // remove outputs
- outs->Clear();
+ for (size_t o = 0; o < tx.vout.size(); o++) {
+ if (!tx.vout[o].scriptPubKey.IsUnspendable()) {
+ COutPoint out(hash, o);
+ Coin coin;
+ view.SpendCoin(out, &coin);
+ if (tx.vout[o] != coin.out) {
+ fClean = false; // transaction output mismatch
+ }
+ }
}
// restore inputs
if (i > 0) { // not coinbases
- const CTxUndo &txundo = blockUndo.vtxundo[i-1];
+ CTxUndo &txundo = blockUndo.vtxundo[i-1];
if (txundo.vprevout.size() != tx.vin.size()) {
error("DisconnectBlock(): transaction and undo data inconsistent");
return DISCONNECT_FAILED;
}
for (unsigned int j = tx.vin.size(); j-- > 0;) {
const COutPoint &out = tx.vin[j].prevout;
- const CTxInUndo &undo = txundo.vprevout[j];
- if (!ApplyTxInUndo(undo, view, out))
- fClean = false;
+ int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out);
+ if (res == DISCONNECT_FAILED) return DISCONNECT_FAILED;
+ fClean = fClean && res != DISCONNECT_UNCLEAN;
}
+ // At this point, all of txundo.vprevout should have been moved out.
}
}
@@ -1431,7 +1421,7 @@ void static FlushBlockFile(bool fFinalize = false)
}
}
-bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
@@ -1579,10 +1569,12 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
if (fEnforceBIP30) {
for (const auto& tx : block.vtx) {
- const CCoins* coins = view.AccessCoins(tx->GetHash());
- if (coins && !coins->IsPruned())
- return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
- REJECT_INVALID, "bad-txns-BIP30");
+ for (size_t o = 0; o < tx->vout.size(); o++) {
+ if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
+ return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
+ REJECT_INVALID, "bad-txns-BIP30");
+ }
+ }
}
}
@@ -1649,7 +1641,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
// be in ConnectBlock because they require the UTXO set
prevheights.resize(tx.vin.size());
for (size_t j = 0; j < tx.vin.size(); j++) {
- prevheights[j] = view.AccessCoins(tx.vin[j].prevout.hash)->nHeight;
+ prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
}
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
@@ -1748,9 +1740,8 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
* if they're too large, if it's been a while since the last write,
* or always and in all cases if we're in prune mode and are deleting files.
*/
-bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
+bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
- const CChainParams& chainparams = Params();
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
@@ -1787,9 +1778,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage() * DB_PEAK_USAGE_FACTOR;
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
- // The cache is large and we're within 10% and 200 MiB or 50% and 50MiB of the limit, but we have time now (not in the middle of a block processing).
- bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::min(std::max(nTotalSpace / 2, nTotalSpace - MIN_BLOCK_COINSDB_USAGE * 1024 * 1024),
- std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024));
+ // The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
+ bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
@@ -1830,12 +1820,12 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
if (fDoFullFlush) {
- // Typical CCoins structures on disk are around 128 bytes in size.
+ // Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize()))
+ if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space");
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
@@ -1855,13 +1845,25 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n
void FlushStateToDisk() {
CValidationState state;
- FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
+ const CChainParams& chainparams = Params();
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS);
}
void PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
- FlushStateToDisk(state, FLUSH_STATE_NONE);
+ const CChainParams& chainparams = Params();
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE);
+}
+
+static void DoWarning(const std::string& strWarning)
+{
+ static bool fWarned = false;
+ SetMiscWarning(strWarning);
+ if (!fWarned) {
+ AlertNotify(strWarning);
+ fWarned = true;
+ }
}
/** Update chainActive and related internal data structures. */
@@ -1873,7 +1875,6 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
cvBlockChange.notify_all();
- static bool fWarned = false;
std::vector<std::string> warningMessages;
if (!IsInitialBlockDownload())
{
@@ -1883,15 +1884,11 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) {
+ const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
if (state == THRESHOLD_ACTIVE) {
- std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
- SetMiscWarning(strWarning);
- if (!fWarned) {
- AlertNotify(strWarning);
- fWarned = true;
- }
+ DoWarning(strWarning);
} else {
- warningMessages.push_back(strprintf("unknown new rules are about to activate (versionbit %i)", bit));
+ warningMessages.push_back(strWarning);
}
}
}
@@ -1904,19 +1901,15 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
pindex = pindex->pprev;
}
if (nUpgraded > 0)
- warningMessages.push_back(strprintf("%d of last 100 blocks have unexpected version", nUpgraded));
+ warningMessages.push_back(strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
if (nUpgraded > 100/2)
{
std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
// notify GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
- SetMiscWarning(strWarning);
- if (!fWarned) {
- AlertNotify(strWarning);
- fWarned = true;
- }
+ DoWarning(strWarning);
}
}
- LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)", __func__,
+ LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__,
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion,
log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
@@ -1957,7 +1950,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
}
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
+ if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
return false;
if (disconnectpool) {
@@ -2094,7 +2087,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
// Write the chain state to disk, if necessary.
- if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
+ if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
@@ -2301,6 +2294,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL;
+ int nStopAtHeight = GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
boost::this_thread::interruption_point();
if (ShutdownRequested())
@@ -2350,17 +2344,16 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
if (pindexFork != pindexNewTip) {
uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
}
+
+ if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
// Write changes periodically to disk, after relay.
- if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
+ if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_PERIODIC)) {
return false;
}
- int nStopAtHeight = GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
- if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
-
return true;
}
@@ -2471,7 +2464,7 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) {
return true;
}
-CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
+static CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
{
// Check for duplicate
uint256 hash = block.GetHash();
@@ -2555,7 +2548,7 @@ static bool ReceivedBlockTransactions(const CBlock &block, CValidationState& sta
return true;
}
-bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -2612,7 +2605,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
return true;
}
-bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
@@ -2643,7 +2636,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
return true;
}
-bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW)
+static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
@@ -2793,7 +2786,10 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
-bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
+/** Context-dependent validity checks.
+ * By "context", we mean only the previous block headers, but not the UTXO
+ * set; UTXO-related validity checks are done in ConnectBlock(). */
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
assert(pindexPrev != NULL);
const int nHeight = pindexPrev->nHeight + 1;
@@ -2820,7 +2816,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
return true;
}
-bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
+static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
@@ -3044,7 +3040,7 @@ static bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidation
}
if (fCheckForPruning)
- FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE); // we just allocated more disk space for block files
return true;
}
@@ -3112,7 +3108,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
*/
/* Calculate the amount of disk space the block & undo files currently use */
-uint64_t CalculateCurrentUsage()
+static uint64_t CalculateCurrentUsage()
{
uint64_t retval = 0;
BOOST_FOREACH(const CBlockFileInfo &file, vinfoBlockFile) {
@@ -3165,7 +3161,7 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
}
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
-void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight)
+static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight)
{
assert(fPruneMode && nManualPruneHeight > 0);
@@ -3190,11 +3186,26 @@ void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeig
void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
- FlushStateToDisk(state, FLUSH_STATE_NONE, nManualPruneHeight);
+ const CChainParams& chainparams = Params();
+ FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE, nManualPruneHeight);
}
-/* Calculate the block/rev files that should be deleted to remain under target*/
-void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
+/**
+ * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
+ * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
+ * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
+ * (which in this case means the blockchain must be re-downloaded.)
+ *
+ * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+ * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
+ * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+ * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
+ * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
+ * A db flag records the fact that at least some block files have been pruned.
+ *
+ * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
+ */
+static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
{
LOCK2(cs_main, cs_LastBlockFile);
if (chainActive.Tip() == NULL || nPruneTarget == 0) {
@@ -3252,7 +3263,7 @@ bool CheckDiskSpace(uint64_t nAdditionalBytes)
return true;
}
-FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
+static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return NULL;
@@ -3279,7 +3290,8 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "blk", fReadOnly);
}
-FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
+/** Open an undo file (rev?????.dat) */
+static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
@@ -3551,7 +3563,7 @@ bool RewindBlockIndex(const CChainParams& params)
return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
}
// Occasionally flush state to disk.
- if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC))
+ if (!FlushStateToDisk(params, state, FLUSH_STATE_PERIODIC))
return false;
}
@@ -3600,7 +3612,7 @@ bool RewindBlockIndex(const CChainParams& params)
CheckBlockIndex(params.GetConsensus());
- if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) {
+ if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) {
return false;
}
@@ -3673,7 +3685,7 @@ bool InitBlockIndex(const CChainParams& chainparams)
if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
return error("LoadBlockIndex(): genesis block not accepted");
// Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data
- return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
+ return FlushStateToDisk(chainparams, state, FLUSH_STATE_ALWAYS);
} catch (const std::runtime_error& e) {
return error("LoadBlockIndex(): failed to initialize block database: %s", e.what());
}
@@ -4015,6 +4027,7 @@ static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(void)
{
+ const CChainParams& chainparams = Params();
int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
@@ -4051,7 +4064,7 @@ bool LoadMempool(void)
CValidationState state;
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
- AcceptToMemoryPoolWithTime(mempool, state, tx, true, NULL, nTime);
+ AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, NULL, nTime, NULL, false, 0);
if (state.IsValid()) {
++count;
} else {