mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-17 13:07:54 +02:00
Merge UP TO 551c8e9526 into merged_master (UP TO bitcoin/bitcoin#26349)
Includes FIXMEs for a few functional tests
This commit is contained in:
commit
ca0a68b350
295 changed files with 5068 additions and 1834 deletions
|
|
@ -90,9 +90,6 @@ using node::SnapshotMetadata;
|
|||
using node::UndoReadFromDisk;
|
||||
using node::UnlinkPrunedFiles;
|
||||
|
||||
#define MICRO 0.000001
|
||||
#define MILLI 0.001
|
||||
|
||||
/** Maximum kilobytes for transactions to store for processing during reorg */
|
||||
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
|
||||
/** Time to wait between writing blocks/block index to disk. */
|
||||
|
|
@ -131,13 +128,6 @@ RecursiveMutex cs_main;
|
|||
GlobalMutex g_best_block_mutex;
|
||||
std::condition_variable g_best_block_cv;
|
||||
uint256 g_best_block;
|
||||
bool g_parallel_script_checks{false};
|
||||
bool fCheckBlockIndex = false;
|
||||
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
|
||||
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
|
||||
|
||||
uint256 hashAssumeValid;
|
||||
arith_uint256 nMinimumChainWork;
|
||||
|
||||
const CBlockIndex* Chainstate::FindForkInGlobalIndex(const CBlockLocator& locator) const
|
||||
{
|
||||
|
|
@ -442,11 +432,13 @@ namespace {
|
|||
class MemPoolAccept
|
||||
{
|
||||
public:
|
||||
explicit MemPoolAccept(CTxMemPool& mempool, Chainstate& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
|
||||
m_limit_ancestors(m_pool.m_limits.ancestor_count),
|
||||
m_limit_ancestor_size(m_pool.m_limits.ancestor_size_vbytes),
|
||||
m_limit_descendants(m_pool.m_limits.descendant_count),
|
||||
m_limit_descendant_size(m_pool.m_limits.descendant_size_vbytes) {
|
||||
explicit MemPoolAccept(CTxMemPool& mempool, Chainstate& active_chainstate) :
|
||||
m_pool(mempool),
|
||||
m_view(&m_dummy),
|
||||
m_viewmempool(&active_chainstate.CoinsTip(), m_pool),
|
||||
m_active_chainstate(active_chainstate),
|
||||
m_limits{m_pool.m_limits}
|
||||
{
|
||||
}
|
||||
|
||||
// We put the arguments we're handed into a struct, so we can pass them
|
||||
|
|
@ -680,13 +672,7 @@ private:
|
|||
|
||||
Chainstate& m_active_chainstate;
|
||||
|
||||
// The package limits in effect at the time of invocation.
|
||||
const size_t m_limit_ancestors;
|
||||
const size_t m_limit_ancestor_size;
|
||||
// These may be modified while evaluating a transaction (eg to account for
|
||||
// in-mempool conflicts; see below).
|
||||
size_t m_limit_descendants;
|
||||
size_t m_limit_descendant_size;
|
||||
CTxMemPool::Limits m_limits;
|
||||
|
||||
/** Whether the transaction(s) would replace any mempool transactions. If so, RBF rules apply. */
|
||||
bool m_rbf{false};
|
||||
|
|
@ -967,12 +953,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
assert(ws.m_iters_conflicting.size() == 1);
|
||||
CTxMemPool::txiter conflict = *ws.m_iters_conflicting.begin();
|
||||
|
||||
m_limit_descendants += 1;
|
||||
m_limit_descendant_size += conflict->GetSizeWithDescendants();
|
||||
m_limits.descendant_count += 1;
|
||||
m_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants();
|
||||
}
|
||||
|
||||
std::string errString;
|
||||
if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
|
||||
if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limits, errString)) {
|
||||
ws.m_ancestors.clear();
|
||||
// If CalculateMemPoolAncestors fails second time, we want the original error string.
|
||||
std::string dummy_err_string;
|
||||
|
|
@ -987,8 +973,16 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
// to be secure by simply only having two immediately-spendable
|
||||
// outputs - one for each counterparty. For more info on the uses for
|
||||
// this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
|
||||
CTxMemPool::Limits cpfp_carve_out_limits{
|
||||
.ancestor_count = 2,
|
||||
.ancestor_size_vbytes = m_limits.ancestor_size_vbytes,
|
||||
.descendant_count = m_limits.descendant_count + 1,
|
||||
.descendant_size_vbytes = m_limits.descendant_size_vbytes + EXTRA_DESCENDANT_TX_SIZE_LIMIT,
|
||||
};
|
||||
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
|
||||
!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
|
||||
!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors,
|
||||
cpfp_carve_out_limits,
|
||||
dummy_err_string)) {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", errString);
|
||||
}
|
||||
}
|
||||
|
|
@ -1064,8 +1058,7 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
|
|||
{ return !m_pool.exists(GenTxid::Txid(tx->GetHash()));}));
|
||||
|
||||
std::string err_string;
|
||||
if (!m_pool.CheckPackageLimits(txns, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants,
|
||||
m_limit_descendant_size, err_string)) {
|
||||
if (!m_pool.CheckPackageLimits(txns, m_limits, err_string)) {
|
||||
// This is a package-wide error, separate from an individual transaction error.
|
||||
return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
|
||||
}
|
||||
|
|
@ -1221,9 +1214,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
|
|||
// Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
|
||||
// last calculation done in PreChecks, since package ancestors have already been submitted.
|
||||
std::string unused_err_string;
|
||||
if(!m_pool.CalculateMemPoolAncestors(*ws.m_entry, ws.m_ancestors, m_limit_ancestors,
|
||||
m_limit_ancestor_size, m_limit_descendants,
|
||||
m_limit_descendant_size, unused_err_string)) {
|
||||
if(!m_pool.CalculateMemPoolAncestors(*ws.m_entry, ws.m_ancestors, m_limits, unused_err_string)) {
|
||||
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
|
||||
// Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
|
||||
Assume(false);
|
||||
|
|
@ -1516,7 +1507,7 @@ MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTra
|
|||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
const CChainParams& chainparams{active_chainstate.m_params};
|
||||
const CChainParams& chainparams{active_chainstate.m_chainman.GetParams()};
|
||||
assert(active_chainstate.GetMempool() != nullptr);
|
||||
CTxMemPool& pool{*active_chainstate.GetMempool()};
|
||||
|
||||
|
|
@ -1546,7 +1537,7 @@ PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxM
|
|||
assert(std::all_of(package.cbegin(), package.cend(), [](const auto& tx){return tx != nullptr;}));
|
||||
|
||||
std::vector<COutPoint> coins_to_uncache;
|
||||
const CChainParams& chainparams = active_chainstate.m_params;
|
||||
const CChainParams& chainparams = active_chainstate.m_chainman.GetParams();
|
||||
const auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
|
||||
AssertLockHeld(cs_main);
|
||||
if (test_accept) {
|
||||
|
|
@ -1604,7 +1595,6 @@ Chainstate::Chainstate(
|
|||
std::optional<uint256> from_snapshot_blockhash)
|
||||
: m_mempool(mempool),
|
||||
m_blockman(blockman),
|
||||
m_params(chainman.GetParams()),
|
||||
m_chainman(chainman),
|
||||
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
|
||||
|
||||
|
|
@ -1615,7 +1605,7 @@ void Chainstate::InitCoinsDB(
|
|||
fs::path leveldb_name)
|
||||
{
|
||||
if (m_from_snapshot_blockhash) {
|
||||
leveldb_name += "_" + m_from_snapshot_blockhash->ToString();
|
||||
leveldb_name += node::SNAPSHOT_CHAINSTATE_SUFFIX;
|
||||
}
|
||||
|
||||
m_coins_views = std::make_unique<CoinsViews>(
|
||||
|
|
@ -1648,10 +1638,12 @@ bool Chainstate::IsInitialBlockDownload() const
|
|||
return true;
|
||||
if (m_chain.Tip() == nullptr)
|
||||
return true;
|
||||
if (m_chain.Tip()->nChainWork < nMinimumChainWork)
|
||||
if (m_chain.Tip()->nChainWork < m_chainman.MinimumChainWork()) {
|
||||
return true;
|
||||
if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
|
||||
}
|
||||
if (m_chain.Tip()->Time() < NodeClock::now() - m_chainman.m_options.max_tip_age) {
|
||||
return true;
|
||||
}
|
||||
LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
|
||||
m_cached_finished_ibd.store(true, std::memory_order_relaxed);
|
||||
return false;
|
||||
|
|
@ -1985,11 +1977,21 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn
|
|||
return DISCONNECT_FAILED;
|
||||
}
|
||||
|
||||
// Ignore blocks that contain transactions which are 'overwritten' by later transactions,
|
||||
// unless those are already completely spent.
|
||||
// See https://github.com/bitcoin/bitcoin/issues/22596 for additional information.
|
||||
// Note: the blocks specified here are different than the ones used in ConnectBlock because DisconnectBlock
|
||||
// unwinds the blocks in reverse. As a result, the inconsistency is not discovered until the earlier
|
||||
// blocks with the duplicate coinbase transactions are disconnected.
|
||||
bool fEnforceBIP30 = !((pindex->nHeight==91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
|
||||
(pindex->nHeight==91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")));
|
||||
|
||||
// undo transactions in reverse order
|
||||
for (int i = block.vtx.size() - 1; i >= 0; i--) {
|
||||
const CTransaction &tx = *(block.vtx[i]);
|
||||
uint256 hash = tx.GetHash();
|
||||
bool is_coinbase = tx.IsCoinBase();
|
||||
bool is_bip30_exception = (is_coinbase && !fEnforceBIP30);
|
||||
|
||||
// Check that all outputs are available and match the outputs in the block itself
|
||||
// exactly.
|
||||
|
|
@ -1999,7 +2001,9 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn
|
|||
Coin coin;
|
||||
bool is_spent = view.SpendCoin(out, &coin);
|
||||
if (!is_spent || !TxOutDBEntryIsSame(tx.vout[o], coin.out) || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
|
||||
fClean = false; // transaction output mismatch
|
||||
if (!is_bip30_exception) {
|
||||
fClean = false; // transaction output mismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2120,14 +2124,14 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
|
|||
}
|
||||
|
||||
|
||||
static int64_t nTimeCheck = 0;
|
||||
static int64_t nTimeForks = 0;
|
||||
static int64_t nTimeConnect = 0;
|
||||
static int64_t nTimeVerify = 0;
|
||||
static int64_t nTimeUndo = 0;
|
||||
static int64_t nTimeIndex = 0;
|
||||
static int64_t nTimeTotal = 0;
|
||||
static int64_t nBlocksTotal = 0;
|
||||
static SteadyClock::duration time_check{};
|
||||
static SteadyClock::duration time_forks{};
|
||||
static SteadyClock::duration time_connect{};
|
||||
static SteadyClock::duration time_verify{};
|
||||
static SteadyClock::duration time_undo{};
|
||||
static SteadyClock::duration time_index{};
|
||||
static SteadyClock::duration time_total{};
|
||||
static int64_t num_blocks_total = 0;
|
||||
|
||||
bool CheckPeginRipeness(const CBlock& block, const std::vector<std::pair<CScript, CScript>>& fedpegscripts) {
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||
|
|
@ -2163,14 +2167,16 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
|
||||
uint256 block_hash{block.GetHash()};
|
||||
assert(*pindex->phashBlock == block_hash);
|
||||
const bool parallel_script_checks{scriptcheckqueue.HasThreads()};
|
||||
|
||||
int64_t nTimeStart = GetTimeMicros();
|
||||
const auto time_start{SteadyClock::now()};
|
||||
const CChainParams& params{m_chainman.GetParams()};
|
||||
|
||||
// verify that the view's current state corresponds to the previous block
|
||||
uint256 hashPrevBlock = pindex->pprev == nullptr ? uint256() : pindex->pprev->GetBlockHash();
|
||||
assert(hashPrevBlock == view.GetBestBlock());
|
||||
|
||||
const Consensus::Params& consensusParams = m_params.GetConsensus();
|
||||
const Consensus::Params& consensusParams = params.GetConsensus();
|
||||
// Add genesis outputs but don't validate.
|
||||
if (block_hash == consensusParams.hashGenesisBlock) {
|
||||
if (!fJustCheck) {
|
||||
|
|
@ -2182,7 +2188,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
}
|
||||
view.SetBestBlock(pindex->GetBlockHash());
|
||||
}
|
||||
nBlocksTotal++;
|
||||
num_blocks_total++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2199,7 +2205,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
|
||||
// re-enforce that rule here (at least until we make it impossible for
|
||||
// m_adjusted_time_callback() to go backward).
|
||||
if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) {
|
||||
if (!CheckBlock(block, state, params.GetConsensus(), !fJustCheck, !fJustCheck)) {
|
||||
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
|
||||
// We don't write down blocks to disk if they may have been
|
||||
// corrupted, so this should be impossible unless we're having hardware
|
||||
|
|
@ -2209,10 +2215,10 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
||||
}
|
||||
|
||||
nBlocksTotal++;
|
||||
num_blocks_total++;
|
||||
|
||||
// Check that all non-zero coinbase outputs pay to the required destination
|
||||
const CScript& mandatory_coinbase_destination = m_params.GetConsensus().mandatory_coinbase_destination;
|
||||
const CScript& mandatory_coinbase_destination = params.GetConsensus().mandatory_coinbase_destination;
|
||||
if (mandatory_coinbase_destination != CScript()) {
|
||||
for (auto& txout : block.vtx[0]->vout) {
|
||||
bool mustPay = !txout.nValue.IsExplicit() || txout.nValue.GetAmount() != 0;
|
||||
|
|
@ -2224,17 +2230,17 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
}
|
||||
|
||||
bool fScriptChecks = true;
|
||||
if (!hashAssumeValid.IsNull()) {
|
||||
if (!m_chainman.AssumedValidBlock().IsNull()) {
|
||||
// We've been configured with the hash of a block which has been externally verified to have a valid history.
|
||||
// A suitable default value is included with the software and updated from time to time. Because validity
|
||||
// relative to a piece of software is an objective fact these defaults can be easily reviewed.
|
||||
// This setting doesn't force the selection of any particular chain but makes validating some faster by
|
||||
// effectively caching the result of part of the verification.
|
||||
BlockMap::const_iterator it = m_blockman.m_block_index.find(hashAssumeValid);
|
||||
BlockMap::const_iterator it{m_blockman.m_block_index.find(m_chainman.AssumedValidBlock())};
|
||||
if (it != m_blockman.m_block_index.end()) {
|
||||
if (it->second.GetAncestor(pindex->nHeight) == pindex &&
|
||||
m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex &&
|
||||
m_chainman.m_best_header->nChainWork >= nMinimumChainWork) {
|
||||
m_chainman.m_best_header->nChainWork >= m_chainman.MinimumChainWork()) {
|
||||
// This block is a member of the assumed verified chain and an ancestor of the best header.
|
||||
// Script verification is skipped when connecting blocks under the
|
||||
// assumevalid block. Assuming the assumevalid block is valid this
|
||||
|
|
@ -2247,16 +2253,20 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// it hard to hide the implication of the demand. This also avoids having release candidates
|
||||
// that are hardly doing any signature verification at all in testing without having to
|
||||
// artificially set the default assumed verified block further back.
|
||||
// The test against nMinimumChainWork prevents the skipping when denied access to any chain at
|
||||
// The test against the minimum chain work prevents the skipping when denied access to any chain at
|
||||
// least as good as the expected chain.
|
||||
fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, m_params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
|
||||
fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart;
|
||||
assert(nBlocksTotal > 0);
|
||||
LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime1 - nTimeStart), nTimeCheck * MICRO, nTimeCheck * MILLI / nBlocksTotal);
|
||||
assert(num_blocks_total > 0);
|
||||
const auto time_1{SteadyClock::now()};
|
||||
time_check += time_1 - time_start;
|
||||
LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_1 - time_start),
|
||||
Ticks<SecondsDouble>(time_check),
|
||||
Ticks<MillisecondsDouble>(time_check) / num_blocks_total);
|
||||
|
||||
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
|
||||
// unless those are already completely spent.
|
||||
|
|
@ -2327,9 +2337,9 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// post BIP34 before approximately height 486,000,000. After block
|
||||
// 1,983,702 testnet3 starts doing unnecessary BIP30 checking again.
|
||||
assert(pindex->pprev);
|
||||
CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(m_params.GetConsensus().BIP34Height);
|
||||
CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(params.GetConsensus().BIP34Height);
|
||||
//Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
|
||||
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == m_params.GetConsensus().BIP34Hash));
|
||||
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == params.GetConsensus().BIP34Hash));
|
||||
|
||||
// TODO: Remove BIP30 checking from block height 1,983,702 on, once we have a
|
||||
// consensus change that ensures coinbases at those heights cannot
|
||||
|
|
@ -2354,8 +2364,12 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// Get the script flags for this block
|
||||
unsigned int flags{GetBlockScriptFlags(*pindex, m_chainman)};
|
||||
|
||||
int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1;
|
||||
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal);
|
||||
const auto time_2{SteadyClock::now()};
|
||||
time_forks += time_2 - time_1;
|
||||
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_2 - time_1),
|
||||
Ticks<SecondsDouble>(time_forks),
|
||||
Ticks<MillisecondsDouble>(time_forks) / num_blocks_total);
|
||||
|
||||
CBlockUndo blockundo;
|
||||
|
||||
|
|
@ -2364,10 +2378,10 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// in multiple threads). Preallocate the vector size so a new allocation
|
||||
// doesn't invalidate pointers into the vector, and keep txsdata in scope
|
||||
// for as long as `control`.
|
||||
CCheckQueueControl<CCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
|
||||
CCheckQueueControl<CCheck> control(fScriptChecks && parallel_script_checks ? &scriptcheckqueue : nullptr);
|
||||
std::vector<PrecomputedTransactionData> txsdata;
|
||||
for (unsigned int i = 0; i< block.vtx.size(); i++ ){
|
||||
txsdata.push_back(PrecomputedTransactionData(m_params.HashGenesisBlock()));
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++){
|
||||
txsdata.push_back(PrecomputedTransactionData(m_chainman.GetParams().HashGenesisBlock()));
|
||||
}
|
||||
|
||||
std::vector<int> prevheights;
|
||||
|
|
@ -2379,11 +2393,11 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// ELEMENTS:
|
||||
|
||||
// Enforce PAK post-dynafed
|
||||
if (m_params.GetEnforcePak() && !block.m_dynafed_params.IsNull()) {
|
||||
if (m_chainman.GetParams().GetEnforcePak() && !block.m_dynafed_params.IsNull()) {
|
||||
// GetActivePAKList computes for the following block, so use previous index
|
||||
CPAKList paklist = GetActivePAKList(pindex->pprev, m_params.GetConsensus());
|
||||
CPAKList paklist = GetActivePAKList(pindex->pprev, m_chainman.GetConsensus());
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (!IsPAKValidTx(*tx, paklist, m_params.ParentGenesisBlockHash(), m_params.GetConsensus().pegged_asset)) {
|
||||
if (!IsPAKValidTx(*tx, paklist, m_chainman.GetParams().ParentGenesisBlockHash(), m_chainman.GetConsensus().pegged_asset)) {
|
||||
LogPrintf("ERROR: ConnectBlock(): Bad PAK transaction\n");
|
||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-pak-tx");
|
||||
}
|
||||
|
|
@ -2394,7 +2408,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
std::set<std::pair<uint256, COutPoint>> setPeginsSpentDummy;
|
||||
|
||||
// Used when checking peg-ins
|
||||
const auto& fedpegscripts = GetValidFedpegScripts(pindex, m_params.GetConsensus(), false /* nextblock_validation */);
|
||||
const auto& fedpegscripts = GetValidFedpegScripts(pindex, m_chainman.GetConsensus(), false /* nextblock_validation */);
|
||||
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||
{
|
||||
|
|
@ -2409,7 +2423,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
TxValidationState tx_state;
|
||||
if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, fee_map,
|
||||
setPeginsSpent == nullptr ? setPeginsSpentDummy : *setPeginsSpent,
|
||||
g_parallel_script_checks ? &vChecks : nullptr, fCacheResults, fScriptChecks, fedpegscripts)) {
|
||||
parallel_script_checks ? &vChecks : nullptr, fCacheResults, fScriptChecks, fedpegscripts)) {
|
||||
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
||||
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
|
||||
|
|
@ -2455,7 +2469,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
std::vector<CCheck*> vChecks;
|
||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||
TxValidationState tx_state;
|
||||
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
|
||||
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], parallel_script_checks ? &vChecks : nullptr)) {
|
||||
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
||||
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
|
||||
|
|
@ -2472,8 +2486,13 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
|
||||
|
||||
}
|
||||
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
|
||||
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
|
||||
const auto time_3{SteadyClock::now()};
|
||||
time_connect += time_3 - time_2;
|
||||
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(),
|
||||
Ticks<MillisecondsDouble>(time_3 - time_2), Ticks<MillisecondsDouble>(time_3 - time_2) / block.vtx.size(),
|
||||
nInputs <= 1 ? 0 : Ticks<MillisecondsDouble>(time_3 - time_2) / (nInputs - 1),
|
||||
Ticks<SecondsDouble>(time_connect),
|
||||
Ticks<MillisecondsDouble>(time_connect) / num_blocks_total);
|
||||
|
||||
CAmountMap block_reward = fee_map;
|
||||
block_reward[consensusParams.subsidy_asset] += GetBlockSubsidy(pindex->nHeight, consensusParams);
|
||||
|
|
@ -2490,18 +2509,27 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
LogPrintf("ERROR: %s: CheckQueue failed\n", __func__);
|
||||
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "block-validation-failed");
|
||||
}
|
||||
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
|
||||
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal);
|
||||
const auto time_4{SteadyClock::now()};
|
||||
time_verify += time_4 - time_2;
|
||||
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1,
|
||||
Ticks<MillisecondsDouble>(time_4 - time_2),
|
||||
nInputs <= 1 ? 0 : Ticks<MillisecondsDouble>(time_4 - time_2) / (nInputs - 1),
|
||||
Ticks<SecondsDouble>(time_verify),
|
||||
Ticks<MillisecondsDouble>(time_verify) / num_blocks_total);
|
||||
|
||||
if (fJustCheck)
|
||||
return true;
|
||||
|
||||
if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
|
||||
if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t nTime5 = GetTimeMicros(); nTimeUndo += nTime5 - nTime4;
|
||||
LogPrint(BCLog::BENCH, " - Write undo data: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeUndo * MICRO, nTimeUndo * MILLI / nBlocksTotal);
|
||||
const auto time_5{SteadyClock::now()};
|
||||
time_undo += time_5 - time_4;
|
||||
LogPrint(BCLog::BENCH, " - Write undo data: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_5 - time_4),
|
||||
Ticks<SecondsDouble>(time_undo),
|
||||
Ticks<MillisecondsDouble>(time_undo) / num_blocks_total);
|
||||
|
||||
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
|
||||
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
||||
|
|
@ -2511,8 +2539,12 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
// add this block to the view's block chain
|
||||
view.SetBestBlock(pindex->GetBlockHash());
|
||||
|
||||
int64_t nTime6 = GetTimeMicros(); nTimeIndex += nTime6 - nTime5;
|
||||
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
|
||||
const auto time_6{SteadyClock::now()};
|
||||
time_index += time_6 - time_5;
|
||||
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_6 - time_5),
|
||||
Ticks<SecondsDouble>(time_index),
|
||||
Ticks<MillisecondsDouble>(time_index) / num_blocks_total);
|
||||
|
||||
TRACE6(validation, block_connected,
|
||||
block_hash.data(),
|
||||
|
|
@ -2520,7 +2552,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
block.vtx.size(),
|
||||
nInputs,
|
||||
nSigOpsCost,
|
||||
nTime5 - nTimeStart // in microseconds (µs)
|
||||
time_5 - time_start // in microseconds (µs)
|
||||
);
|
||||
|
||||
return true;
|
||||
|
|
@ -2607,7 +2639,7 @@ bool Chainstate::FlushStateToDisk(
|
|||
} else {
|
||||
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
|
||||
|
||||
m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
|
||||
m_blockman.FindFilesToPrune(setFilesToPrune, m_chainman.GetParams().PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
|
||||
m_blockman.m_check_for_pruning = false;
|
||||
}
|
||||
if (!setFilesToPrune.empty()) {
|
||||
|
|
@ -2815,13 +2847,15 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
|
|||
AssertLockHeld(::cs_main);
|
||||
const auto& coins_tip = this->CoinsTip();
|
||||
|
||||
const CChainParams& params{m_chainman.GetParams()};
|
||||
|
||||
// The remainder of the function isn't relevant if we are not acting on
|
||||
// the active chainstate, so return if need be.
|
||||
if (this != &m_chainman.ActiveChainstate()) {
|
||||
// Only log every so often so that we don't bury log messages at the tip.
|
||||
constexpr int BACKGROUND_LOG_INTERVAL = 2000;
|
||||
if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {
|
||||
UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "[background validation] ", "");
|
||||
UpdateTipLog(coins_tip, pindexNew, params, __func__, "[background validation] ", "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -2842,7 +2876,7 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
|
|||
const CBlockIndex* pindex = pindexNew;
|
||||
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
|
||||
WarningBitsConditionChecker checker(m_chainman, bit);
|
||||
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache.at(bit));
|
||||
ThresholdState state = checker.GetStateFor(pindex, params.GetConsensus(), warningcache.at(bit));
|
||||
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
|
||||
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
|
||||
if (state == ThresholdState::ACTIVE) {
|
||||
|
|
@ -2853,7 +2887,7 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
|
|||
}
|
||||
}
|
||||
}
|
||||
UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
|
||||
UpdateTipLog(coins_tip, pindexNew, params, __func__, "", warning_messages.original);
|
||||
|
||||
ForceUntrimHeader(pindexNew);
|
||||
// Do some logging if dynafed parameters changed.
|
||||
|
|
@ -2891,11 +2925,11 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
|
|||
// Read block from disk.
|
||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
|
||||
CBlock& block = *pblock;
|
||||
if (!ReadBlockFromDisk(block, pindexDelete, m_params.GetConsensus())) {
|
||||
if (!ReadBlockFromDisk(block, pindexDelete, m_chainman.GetConsensus())) {
|
||||
return error("DisconnectTip(): Failed to read block");
|
||||
}
|
||||
// Apply the block atomically to the chain state.
|
||||
int64_t nStart = GetTimeMicros();
|
||||
const auto time_start{SteadyClock::now()};
|
||||
{
|
||||
CCoinsViewCache view(&CoinsTip());
|
||||
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
|
||||
|
|
@ -2904,7 +2938,8 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
|
|||
bool flushed = view.Flush();
|
||||
assert(flushed);
|
||||
}
|
||||
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
|
||||
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n",
|
||||
Ticks<MillisecondsDouble>(SteadyClock::now() - time_start));
|
||||
|
||||
{
|
||||
// Prune locks that began at or after the tip should be moved backward so they get a chance to reorg
|
||||
|
|
@ -2944,11 +2979,11 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
|
|||
return true;
|
||||
}
|
||||
|
||||
static int64_t nTimeReadFromDiskTotal = 0;
|
||||
static int64_t nTimeConnectTotal = 0;
|
||||
static int64_t nTimeFlush = 0;
|
||||
static int64_t nTimeChainState = 0;
|
||||
static int64_t nTimePostConnect = 0;
|
||||
static SteadyClock::duration time_read_from_disk_total{};
|
||||
static SteadyClock::duration time_connect_total{};
|
||||
static SteadyClock::duration time_flush{};
|
||||
static SteadyClock::duration time_chainstate{};
|
||||
static SteadyClock::duration time_post_connect{};
|
||||
|
||||
struct PerBlockConnectTrace {
|
||||
CBlockIndex* pindex = nullptr;
|
||||
|
|
@ -3003,11 +3038,11 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
|
|||
|
||||
assert(pindexNew->pprev == m_chain.Tip());
|
||||
// Read block from disk.
|
||||
int64_t nTime1 = GetTimeMicros();
|
||||
const auto time_1{SteadyClock::now()};
|
||||
std::shared_ptr<const CBlock> pthisBlock;
|
||||
if (!pblock) {
|
||||
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>();
|
||||
if (!ReadBlockFromDisk(*pblockNew, pindexNew, m_params.GetConsensus())) {
|
||||
if (!ReadBlockFromDisk(*pblockNew, pindexNew, m_chainman.GetConsensus())) {
|
||||
return AbortNode(state, "Failed to read block");
|
||||
}
|
||||
pthisBlock = pblockNew;
|
||||
|
|
@ -3017,7 +3052,7 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
|
|||
}
|
||||
const CBlock& blockConnecting = *pthisBlock;
|
||||
|
||||
const auto& fedpegscripts = GetValidFedpegScripts(pindexNew, m_params.GetConsensus(), false /* nextblock_validation */);
|
||||
const auto& fedpegscripts = GetValidFedpegScripts(pindexNew, m_chainman.GetConsensus(), false /* nextblock_validation */);
|
||||
if (!CheckPeginRipeness(blockConnecting, fedpegscripts)) {
|
||||
LogPrintf("STALLING further progress in ConnectTip while waiting for parent chain daemon to catch up! Chain will not grow until this is remedied!\n");
|
||||
fStall = true;
|
||||
|
|
@ -3025,9 +3060,13 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
|
|||
}
|
||||
|
||||
// Apply the block atomically to the chain state.
|
||||
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDiskTotal += nTime2 - nTime1;
|
||||
int64_t nTime3;
|
||||
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDiskTotal * MICRO, nTimeReadFromDiskTotal * MILLI / nBlocksTotal);
|
||||
const auto time_2{SteadyClock::now()};
|
||||
time_read_from_disk_total += time_2 - time_1;
|
||||
SteadyClock::time_point time_3;
|
||||
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_2 - time_1),
|
||||
Ticks<SecondsDouble>(time_read_from_disk_total),
|
||||
Ticks<MillisecondsDouble>(time_read_from_disk_total) / num_blocks_total);
|
||||
|
||||
// ELEMENTS:
|
||||
// For mempool removal with pegin conflicts
|
||||
|
|
@ -3043,20 +3082,32 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
|
|||
}
|
||||
return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), state.ToString());
|
||||
}
|
||||
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
|
||||
assert(nBlocksTotal > 0);
|
||||
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal);
|
||||
time_3 = SteadyClock::now();
|
||||
time_connect_total += time_3 - time_2;
|
||||
assert(num_blocks_total > 0);
|
||||
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_3 - time_2),
|
||||
Ticks<SecondsDouble>(time_connect_total),
|
||||
Ticks<MillisecondsDouble>(time_connect_total) / num_blocks_total);
|
||||
bool flushed = view.Flush();
|
||||
assert(flushed);
|
||||
}
|
||||
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
|
||||
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal);
|
||||
const auto time_4{SteadyClock::now()};
|
||||
time_flush += time_4 - time_3;
|
||||
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_4 - time_3),
|
||||
Ticks<SecondsDouble>(time_flush),
|
||||
Ticks<MillisecondsDouble>(time_flush) / num_blocks_total);
|
||||
// Write the chain state to disk, if necessary.
|
||||
if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
|
||||
return false;
|
||||
}
|
||||
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
|
||||
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
|
||||
const auto time_5{SteadyClock::now()};
|
||||
time_chainstate += time_5 - time_4;
|
||||
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_5 - time_4),
|
||||
Ticks<SecondsDouble>(time_chainstate),
|
||||
Ticks<MillisecondsDouble>(time_chainstate) / num_blocks_total);
|
||||
// Remove conflicting transactions from the mempool.;
|
||||
if (m_mempool) {
|
||||
// ELEMENTS: We also eject peg-outs with now-invalid PAK proofs
|
||||
|
|
@ -3068,9 +3119,17 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew,
|
|||
m_chain.SetTip(*pindexNew);
|
||||
UpdateTip(pindexNew);
|
||||
|
||||
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
|
||||
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
|
||||
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * MILLI, nTimeTotal * MICRO, nTimeTotal * MILLI / nBlocksTotal);
|
||||
const auto time_6{SteadyClock::now()};
|
||||
time_post_connect += time_6 - time_5;
|
||||
time_total += time_6 - time_1;
|
||||
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_6 - time_5),
|
||||
Ticks<SecondsDouble>(time_post_connect),
|
||||
Ticks<MillisecondsDouble>(time_post_connect) / num_blocks_total);
|
||||
LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n",
|
||||
Ticks<MillisecondsDouble>(time_6 - time_1),
|
||||
Ticks<SecondsDouble>(time_total),
|
||||
Ticks<MillisecondsDouble>(time_total) / num_blocks_total);
|
||||
|
||||
connectTrace.BlockConnected(pindexNew, std::move(pthisBlock));
|
||||
return true;
|
||||
|
|
@ -3909,7 +3968,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|||
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work");
|
||||
|
||||
// Check against checkpoints
|
||||
if (fCheckpointsEnabled) {
|
||||
if (chainman.m_options.checkpoints_enabled) {
|
||||
// Don't accept any forks from the main chain prior to last checkpoint.
|
||||
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
|
||||
// BlockIndex().
|
||||
|
|
@ -4259,11 +4318,11 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
|
|||
// If our tip is behind, a peer could try to send us
|
||||
// low-work blocks on a fake chain that we would never
|
||||
// request; don't process these.
|
||||
if (pindex->nChainWork < nMinimumChainWork) return true;
|
||||
if (pindex->nChainWork < m_chainman.MinimumChainWork()) return true;
|
||||
}
|
||||
|
||||
if (m_chainman.GetConsensus().hashGenesisBlock != block.GetHash() &&
|
||||
(!CheckBlock(block, state, m_params.GetConsensus()) ||
|
||||
(!CheckBlock(block, state, m_chainman.GetConsensus()) ||
|
||||
!ContextualCheckBlock(block, state, m_chainman, pindex->pprev))) {
|
||||
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
|
||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||
|
|
@ -4280,7 +4339,7 @@ bool Chainstate::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockV
|
|||
// Write block to history file
|
||||
if (fNewBlock) *fNewBlock = true;
|
||||
try {
|
||||
FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp)};
|
||||
FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, m_chainman.GetParams(), dbp)};
|
||||
if (blockPos.IsNull()) {
|
||||
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
|
||||
return false;
|
||||
|
|
@ -4421,10 +4480,10 @@ bool Chainstate::LoadChainTip()
|
|||
|
||||
tip = m_chain.Tip();
|
||||
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
|
||||
tip->GetBlockHash().ToString(),
|
||||
m_chain.Height(),
|
||||
FormatISO8601DateTime(tip->GetBlockTime()),
|
||||
GuessVerificationProgress(tip, m_params.GetConsensus().nPowTargetSpacing));
|
||||
tip->GetBlockHash().ToString(),
|
||||
m_chain.Height(),
|
||||
FormatISO8601DateTime(tip->GetBlockTime()),
|
||||
GuessVerificationProgress(tip, m_chainman.GetConsensus().nPowTargetSpacing));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -4561,7 +4620,7 @@ bool Chainstate::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& in
|
|||
AssertLockHeld(cs_main);
|
||||
// TODO: merge with ConnectBlock
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pindex, m_params.GetConsensus())) {
|
||||
if (!ReadBlockFromDisk(block, pindex, m_chainman.GetConsensus())) {
|
||||
return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
||||
}
|
||||
|
||||
|
|
@ -4613,7 +4672,7 @@ bool Chainstate::ReplayBlocks()
|
|||
while (pindexOld != pindexFork) {
|
||||
if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
|
||||
CBlock block;
|
||||
if (!ReadBlockFromDisk(block, pindexOld, m_params.GetConsensus())) {
|
||||
if (!ReadBlockFromDisk(block, pindexOld, m_chainman.GetConsensus())) {
|
||||
return error("RollbackBlock(): ReadBlockFromDisk() failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString());
|
||||
}
|
||||
LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight);
|
||||
|
|
@ -4765,16 +4824,18 @@ bool Chainstate::LoadGenesisBlock()
|
|||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
const CChainParams& params{m_chainman.GetParams()};
|
||||
|
||||
// Check whether we're already initialized by checking for genesis in
|
||||
// m_blockman.m_block_index. Note that we can't use m_chain here, since it is
|
||||
// set based on the coins db, not the block index db, which is the only
|
||||
// thing loaded at this point.
|
||||
if (m_blockman.m_block_index.count(m_params.GenesisBlock().GetHash()))
|
||||
if (m_blockman.m_block_index.count(params.GenesisBlock().GetHash()))
|
||||
return true;
|
||||
|
||||
try {
|
||||
const CBlock& block = m_params.GenesisBlock();
|
||||
FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, m_params, nullptr)};
|
||||
const CBlock& block = params.GenesisBlock();
|
||||
FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, params, nullptr)};
|
||||
if (blockPos.IsNull()) {
|
||||
return error("%s: writing genesis block to disk failed", __func__);
|
||||
}
|
||||
|
|
@ -4798,6 +4859,7 @@ void Chainstate::LoadExternalBlockFile(
|
|||
assert(!dbp == !blocks_with_unknown_parent);
|
||||
|
||||
const auto start{SteadyClock::now()};
|
||||
const CChainParams& params{m_chainman.GetParams()};
|
||||
|
||||
int nLoaded = 0;
|
||||
try {
|
||||
|
|
@ -4814,10 +4876,10 @@ void Chainstate::LoadExternalBlockFile(
|
|||
try {
|
||||
// locate a header
|
||||
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
|
||||
blkdat.FindByte(m_params.MessageStart()[0]);
|
||||
blkdat.FindByte(params.MessageStart()[0]);
|
||||
nRewind = blkdat.GetPos() + 1;
|
||||
blkdat >> buf;
|
||||
if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
|
||||
if (memcmp(buf, params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
|
||||
continue;
|
||||
}
|
||||
// read size
|
||||
|
|
@ -4843,7 +4905,7 @@ void Chainstate::LoadExternalBlockFile(
|
|||
{
|
||||
LOCK(cs_main);
|
||||
// detect out of order blocks, and store them for later
|
||||
if (hash != m_params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
|
||||
if (hash != params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
|
||||
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
|
||||
block.hashPrevBlock.ToString());
|
||||
if (dbp && blocks_with_unknown_parent) {
|
||||
|
|
@ -4862,13 +4924,13 @@ void Chainstate::LoadExternalBlockFile(
|
|||
if (state.IsError()) {
|
||||
break;
|
||||
}
|
||||
} else if (hash != m_params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
|
||||
} else if (hash != params.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) {
|
||||
LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Activate the genesis block so normal node progress can continue
|
||||
if (hash == m_params.GetConsensus().hashGenesisBlock) {
|
||||
if (hash == params.GetConsensus().hashGenesisBlock) {
|
||||
BlockValidationState state;
|
||||
if (!ActivateBestChain(state, nullptr)) {
|
||||
break;
|
||||
|
|
@ -4889,7 +4951,7 @@ void Chainstate::LoadExternalBlockFile(
|
|||
while (range.first != range.second) {
|
||||
std::multimap<uint256, FlatFilePos>::iterator it = range.first;
|
||||
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
|
||||
if (ReadBlockFromDisk(*pblockrecursive, it->second, m_params.GetConsensus())) {
|
||||
if (ReadBlockFromDisk(*pblockrecursive, it->second, params.GetConsensus())) {
|
||||
LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(),
|
||||
head.ToString());
|
||||
LOCK(cs_main);
|
||||
|
|
@ -4905,7 +4967,18 @@ void Chainstate::LoadExternalBlockFile(
|
|||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what());
|
||||
// historical bugs added extra data to the block files that does not deserialize cleanly.
|
||||
// commonly this data is between readable blocks, but it does not really matter. such data is not fatal to the import process.
|
||||
// the code that reads the block files deals with invalid data by simply ignoring it.
|
||||
// it continues to search for the next {4 byte magic message start bytes + 4 byte length + block} that does deserialize cleanly
|
||||
// and passes all of the other block validation checks dealing with POW and the merkle root, etc...
|
||||
// we merely note with this informational log message when unexpected data is encountered.
|
||||
// we could also be experiencing a storage system read error, or a read of a previous bad write. these are possible, but
|
||||
// less likely scenarios. we don't have enough information to tell a difference here.
|
||||
// the reindex process is not the place to attempt to clean and/or compact the block files. if so desired, a studious node operator
|
||||
// may use knowledge of the fact that the block files are not entirely pristine in order to prepare a set of pristine, and
|
||||
// perhaps ordered, block files for later reindexing.
|
||||
LogPrint(BCLog::REINDEX, "%s: unexpected data at file offset 0x%x - %s. continuing\n", __func__, (nRewind - 1), e.what());
|
||||
}
|
||||
}
|
||||
} catch (const std::runtime_error& e) {
|
||||
|
|
@ -4916,7 +4989,7 @@ void Chainstate::LoadExternalBlockFile(
|
|||
|
||||
void Chainstate::CheckBlockIndex()
|
||||
{
|
||||
if (!fCheckBlockIndex) {
|
||||
if (!m_chainman.ShouldCheckBlockIndex()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -4989,7 +5062,7 @@ void Chainstate::CheckBlockIndex()
|
|||
// Begin: actual consistency checks.
|
||||
if (pindex->pprev == nullptr) {
|
||||
// Genesis block checks.
|
||||
assert(pindex->GetBlockHash() == m_params.GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
|
||||
assert(pindex->GetBlockHash() == m_chainman.GetConsensus().hashGenesisBlock); // Genesis block's hash must match.
|
||||
assert(pindex == m_chain.Genesis()); // The current active chain's genesis block must be this block.
|
||||
}
|
||||
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
||||
|
|
@ -5222,28 +5295,15 @@ std::vector<Chainstate*> ChainstateManager::GetAll()
|
|||
return out;
|
||||
}
|
||||
|
||||
Chainstate& ChainstateManager::InitializeChainstate(
|
||||
CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
|
||||
Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
bool is_snapshot = snapshot_blockhash.has_value();
|
||||
std::unique_ptr<Chainstate>& to_modify =
|
||||
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
|
||||
assert(!m_ibd_chainstate);
|
||||
assert(!m_active_chainstate);
|
||||
|
||||
if (to_modify) {
|
||||
throw std::logic_error("should not be overwriting a chainstate");
|
||||
}
|
||||
to_modify.reset(new Chainstate(mempool, m_blockman, *this, snapshot_blockhash));
|
||||
|
||||
// Snapshot chainstates and initial IBD chaintates always become active.
|
||||
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
|
||||
LogPrintf("Switching active chainstate to %s\n", to_modify->ToString());
|
||||
m_active_chainstate = to_modify.get();
|
||||
} else {
|
||||
throw std::logic_error("unexpected chainstate activation");
|
||||
}
|
||||
|
||||
return *to_modify;
|
||||
m_ibd_chainstate = std::make_unique<Chainstate>(mempool, m_blockman, *this);
|
||||
m_active_chainstate = m_ibd_chainstate.get();
|
||||
return *m_active_chainstate;
|
||||
}
|
||||
|
||||
const AssumeutxoData* ExpectedAssumeutxo(
|
||||
|
|
@ -5258,6 +5318,46 @@ const AssumeutxoData* ExpectedAssumeutxo(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool DeleteCoinsDBFromDisk(const fs::path db_path, bool is_snapshot)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
|
||||
if (is_snapshot) {
|
||||
fs::path base_blockhash_path = db_path / node::SNAPSHOT_BLOCKHASH_FILENAME;
|
||||
|
||||
if (fs::exists(base_blockhash_path)) {
|
||||
bool removed = fs::remove(base_blockhash_path);
|
||||
if (!removed) {
|
||||
LogPrintf("[snapshot] failed to remove file %s\n",
|
||||
fs::PathToString(base_blockhash_path));
|
||||
}
|
||||
} else {
|
||||
LogPrintf("[snapshot] snapshot chainstate dir being removed lacks %s file\n",
|
||||
fs::PathToString(node::SNAPSHOT_BLOCKHASH_FILENAME));
|
||||
}
|
||||
}
|
||||
|
||||
std::string path_str = fs::PathToString(db_path);
|
||||
LogPrintf("Removing leveldb dir at %s\n", path_str);
|
||||
|
||||
// We have to destruct before this call leveldb::DB in order to release the db
|
||||
// lock, otherwise `DestroyDB` will fail. See `leveldb::~DBImpl()`.
|
||||
const bool destroyed = dbwrapper::DestroyDB(path_str, {}).ok();
|
||||
|
||||
if (!destroyed) {
|
||||
LogPrintf("error: leveldb DestroyDB call failed on %s\n", path_str);
|
||||
}
|
||||
|
||||
// Datadir should be removed from filesystem; otherwise initialization may detect
|
||||
// it on subsequent statups and get confused.
|
||||
//
|
||||
// If the base_blockhash_path removal above fails in the case of snapshot
|
||||
// chainstates, this will return false since leveldb won't remove a non-empty
|
||||
// directory.
|
||||
return destroyed && !fs::exists(db_path);
|
||||
}
|
||||
|
||||
bool ChainstateManager::ActivateSnapshot(
|
||||
AutoFile& coins_file,
|
||||
const SnapshotMetadata& metadata,
|
||||
|
|
@ -5315,11 +5415,34 @@ bool ChainstateManager::ActivateSnapshot(
|
|||
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
|
||||
}
|
||||
|
||||
const bool snapshot_ok = this->PopulateAndValidateSnapshot(
|
||||
bool snapshot_ok = this->PopulateAndValidateSnapshot(
|
||||
*snapshot_chainstate, coins_file, metadata);
|
||||
|
||||
// If not in-memory, persist the base blockhash for use during subsequent
|
||||
// initialization.
|
||||
if (!in_memory) {
|
||||
LOCK(::cs_main);
|
||||
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
|
||||
snapshot_ok = false;
|
||||
}
|
||||
}
|
||||
if (!snapshot_ok) {
|
||||
WITH_LOCK(::cs_main, this->MaybeRebalanceCaches());
|
||||
LOCK(::cs_main);
|
||||
this->MaybeRebalanceCaches();
|
||||
|
||||
// PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
|
||||
// has been created, so only attempt removal if we got that far.
|
||||
if (auto snapshot_datadir = node::FindSnapshotChainstateDir()) {
|
||||
// We have to destruct leveldb::DB in order to release the db lock, otherwise
|
||||
// DestroyDB() (in DeleteCoinsDBFromDisk()) will fail. See `leveldb::~DBImpl()`.
|
||||
// Destructing the chainstate (and so resetting the coinsviews object) does this.
|
||||
snapshot_chainstate.reset();
|
||||
bool removed = DeleteCoinsDBFromDisk(*snapshot_datadir, /*is_snapshot=*/true);
|
||||
if (!removed) {
|
||||
AbortNode(strprintf("Failed to remove snapshot chainstate dir (%s). "
|
||||
"Manually remove it before restarting.\n", fs::PathToString(*snapshot_datadir)));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -5593,6 +5716,29 @@ void ChainstateManager::MaybeRebalanceCaches()
|
|||
}
|
||||
}
|
||||
|
||||
void ChainstateManager::ResetChainstates()
|
||||
{
|
||||
m_ibd_chainstate.reset();
|
||||
m_snapshot_chainstate.reset();
|
||||
m_active_chainstate = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply default chain params to nullopt members.
|
||||
* This helps to avoid coding errors around the accidental use of the compare
|
||||
* operators that accept nullopt, thus ignoring the intended default value.
|
||||
*/
|
||||
static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts)
|
||||
{
|
||||
if (!opts.check_block_index.has_value()) opts.check_block_index = opts.chainparams.DefaultConsistencyChecks();
|
||||
if (!opts.minimum_chain_work.has_value()) opts.minimum_chain_work = UintToArith256(opts.chainparams.GetConsensus().nMinimumChainWork);
|
||||
if (!opts.assumed_valid_block.has_value()) opts.assumed_valid_block = opts.chainparams.GetConsensus().defaultAssumeValid;
|
||||
Assert(opts.adjusted_time_callback);
|
||||
return std::move(opts);
|
||||
}
|
||||
|
||||
ChainstateManager::ChainstateManager(Options options) : m_options{Flatten(std::move(options))} {}
|
||||
|
||||
ChainstateManager::~ChainstateManager()
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
|
|
@ -5604,3 +5750,31 @@ ChainstateManager::~ChainstateManager()
|
|||
i.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool ChainstateManager::DetectSnapshotChainstate(CTxMemPool* mempool)
|
||||
{
|
||||
assert(!m_snapshot_chainstate);
|
||||
std::optional<fs::path> path = node::FindSnapshotChainstateDir();
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
std::optional<uint256> base_blockhash = node::ReadSnapshotBaseBlockhash(*path);
|
||||
if (!base_blockhash) {
|
||||
return false;
|
||||
}
|
||||
LogPrintf("[snapshot] detected active snapshot chainstate (%s) - loading\n",
|
||||
fs::PathToString(*path));
|
||||
|
||||
this->ActivateExistingSnapshot(mempool, *base_blockhash);
|
||||
return true;
|
||||
}
|
||||
|
||||
Chainstate& ChainstateManager::ActivateExistingSnapshot(CTxMemPool* mempool, uint256 base_blockhash)
|
||||
{
|
||||
assert(!m_snapshot_chainstate);
|
||||
m_snapshot_chainstate =
|
||||
std::make_unique<Chainstate>(mempool, m_blockman, *this, base_blockhash);
|
||||
LogPrintf("[snapshot] switching active chainstate to %s\n", m_snapshot_chainstate->ToString());
|
||||
m_active_chainstate = m_snapshot_chainstate.get();
|
||||
return *m_snapshot_chainstate;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue