aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp125
1 files changed, 73 insertions, 52 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 79ee4e55ec..d3956fafda 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -57,7 +57,7 @@ bool fPruneMode = false;
bool fIsBareMultisigStd = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = true;
-unsigned int nCoinCacheSize = 5000;
+size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
@@ -75,10 +75,9 @@ void EraseOrphansFor(NodeId peer);
/**
* Returns true if there are nRequired or more blocks of minVersion or above
- * in the last Params().ToCheckBlockUpgradeMajority() blocks, starting at pstart
- * and going backwards.
+ * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards.
*/
-static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired);
+static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams);
static void CheckBlockIndex();
/** Constant stuff for coinbase transactions we create: */
@@ -1189,19 +1188,17 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
return true;
}
-CAmount GetBlockValue(int nHeight, const CAmount& nFees)
+CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
- CAmount nSubsidy = 50 * COIN;
- int halvings = nHeight / Params().SubsidyHalvingInterval();
-
+ int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
// Force block reward to zero when right shift is undefined.
if (halvings >= 64)
- return nFees;
+ return 0;
+ CAmount nSubsidy = 50 * COIN;
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
nSubsidy >>= halvings;
-
- return nSubsidy + nFees;
+ return nSubsidy;
}
bool IsInitialBlockDownload()
@@ -1747,7 +1744,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
unsigned int flags = fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE;
// Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded:
- if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, Params().EnforceBlockUpgradeMajority())) {
+ if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) {
flags |= SCRIPT_VERIFY_DERSIG;
}
@@ -1810,10 +1807,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
- if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
+ CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
+ if (block.vtx[0].GetValueOut() > blockReward)
return state.DoS(100,
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
- block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)),
+ block.vtx[0].GetValueOut(), blockReward),
REJECT_INVALID, "bad-cb-amount");
if (!control.Wait())
@@ -1880,11 +1878,14 @@ enum FlushStateMode {
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
+ static int64_t nLastFlush = 0;
+ static int64_t nLastSetChain = 0;
std::set<int> setFilesToPrune;
bool fFlushForPrune = false;
try {
if (fPruneMode && fCheckForPruning) {
FindFilesToPrune(setFilesToPrune);
+ fCheckForPruning = false;
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
if (!fHavePruned) {
@@ -1893,16 +1894,32 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
}
}
}
- if ((mode == FLUSH_STATE_ALWAYS) ||
- ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) ||
- (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) ||
- fFlushForPrune) {
- // Typical CCoins structures on disk are around 100 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(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
+ int64_t nNow = GetTimeMicros();
+ // Avoid writing/flushing immediately after startup.
+ if (nLastWrite == 0) {
+ nLastWrite = nNow;
+ }
+ if (nLastFlush == 0) {
+ nLastFlush = nNow;
+ }
+ if (nLastSetChain == 0) {
+ nLastSetChain = nNow;
+ }
+ size_t cacheSize = pcoinsTip->DynamicMemoryUsage();
+ // The cache is large and close to the limit, but we have time now (not in the middle of a block processing).
+ bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage;
+ // The cache is over the limit, we have to write now.
+ bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage;
+ // 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.
+ bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
+ // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
+ bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
+ // Combine all conditions that result in a full cache flush.
+ bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
+ // Write blocks and block index to disk.
+ if (fDoFullFlush || fPeriodicWrite) {
+ // Depend on nMinDiskSpace to ensure we can write block index
+ if (!CheckDiskSpace(0))
return state.Error("out of disk space");
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
@@ -1924,21 +1941,29 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
return state.Abort("Files to write to block index database");
}
}
+ // Finally remove any pruned files
+ if (fFlushForPrune)
+ UnlinkPrunedFiles(setFilesToPrune);
+ nLastWrite = nNow;
+ }
+ // 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.
+ // 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()))
+ return state.Error("out of disk space");
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
return state.Abort("Failed to write to coin database");
-
- // Finally remove any pruned files
- if (fFlushForPrune) {
- UnlinkPrunedFiles(setFilesToPrune);
- fCheckForPruning = false;
- }
-
+ nLastFlush = nNow;
+ }
+ if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) {
// Update best block in wallet (so we can detect restored wallets).
- if (mode != FLUSH_STATE_IF_NEEDED) {
- GetMainSignals().SetBestChain(chainActive.GetLocator());
- }
- nLastWrite = GetTimeMicros();
+ GetMainSignals().SetBestChain(chainActive.GetLocator());
+ nLastSetChain = nNow;
}
} catch (const std::runtime_error& e) {
return state.Abort(std::string("System error while flushing: ") + e.what());
@@ -1966,10 +1991,10 @@ void static UpdateTip(CBlockIndex *pindexNew) {
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
- LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", __func__,
+ LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__,
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx,
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
- Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize());
+ Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
cvBlockChange.notify_all();
@@ -2644,18 +2669,14 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
}
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
- if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated))
- {
+ if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
return state.Invalid(error("%s: rejected nVersion=1 block", __func__),
REJECT_OBSOLETE, "bad-version");
- }
// Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded:
- if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated))
- {
+ if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams))
return state.Invalid(error("%s : rejected nVersion=2 block", __func__),
REJECT_OBSOLETE, "bad-version");
- }
return true;
}
@@ -2663,6 +2684,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
+ const Consensus::Params& consensusParams = Params().GetConsensus();
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
@@ -2672,7 +2694,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
- if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority()))
+ if (block.nVersion >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
@@ -2779,11 +2801,10 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
return true;
}
-static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired)
+static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams)
{
- unsigned int nToCheck = Params().ToCheckBlockUpgradeMajority();
unsigned int nFound = 0;
- for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++)
+ for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++)
{
if (pstart->nVersion >= minVersion)
++nFound;
@@ -2932,7 +2953,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune)
return;
}
- unsigned int nLastBlockWeMustKeep = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
+ unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files
// So we should leave a buffer under our target to account for another allocation
@@ -2952,7 +2973,7 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune)
break;
// don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip
- if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeMustKeep)
+ if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
break;
PruneOneBlockFile(fileNumber);
@@ -2963,10 +2984,10 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune)
}
}
- LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB min_must_keep=%d removed %d blk/rev pairs\n",
+ LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
- nLastBlockWeMustKeep, count);
+ nLastBlockWeCanPrune, count);
}
bool CheckDiskSpace(uint64_t nAdditionalBytes)
@@ -3197,7 +3218,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= nCoinCacheSize) {
+ if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
bool fClean = true;
if (!DisconnectBlock(block, state, pindex, coins, &fClean))
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());