validation: sync utxo state after block sync

Github-Pull: #15218
Rebased-From: 2626a8a1a2b5697572925c356c77d1dde449f7ab
This commit is contained in:
Andrew Toth 2024-03-13 13:48:28 -04:00 committed by Luke Dashjr
parent 96ec3b67a7
commit 2e729d248c
3 changed files with 55 additions and 0 deletions

View File

@ -140,6 +140,9 @@ static constexpr bool DEFAULT_REST_ENABLE{false};
static constexpr bool DEFAULT_I2P_ACCEPT_INCOMING{true};
static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false};
//! Check if initial sync is done with no change in block height or queued downloads every 30s
static constexpr auto SYNC_CHECK_INTERVAL{30s};
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
// accessing block files don't count towards the fd_set size limit
@ -1064,6 +1067,44 @@ bool AppInitLockDataDirectory()
return true;
}
/**
* Once initial block sync is finished and no change in block height or queued downloads,
* sync utxo state to protect against data loss
*/
static void SyncCoinsTipAfterChainSync(const NodeContext& node)
{
LOCK(node.chainman->GetMutex());
if (node.chainman->IsInitialBlockDownload()) {
LogPrintfCategory(BCLog::COINDB, "Node is still in IBD, rescheduling chainstate disk sync...\n");
node.scheduler->scheduleFromNow([&node] {
SyncCoinsTipAfterChainSync(node);
}, SYNC_CHECK_INTERVAL);
return;
}
static auto last_chain_height{-1};
const auto current_height{node.chainman->ActiveHeight()};
if (last_chain_height != current_height) {
LogPrintfCategory(BCLog::COINDB, "Chain height updated since last check, rescheduling chainstate disk sync...\n");
last_chain_height = current_height;
node.scheduler->scheduleFromNow([&node] {
SyncCoinsTipAfterChainSync(node);
}, SYNC_CHECK_INTERVAL);
return;
}
if (node.peerman->GetNumberOfPeersWithValidatedDownloads() > 0) {
LogPrintfCategory(BCLog::COINDB, "Still downloading blocks from peers, rescheduling chainstate disk sync...\n");
node.scheduler->scheduleFromNow([&node] {
SyncCoinsTipAfterChainSync(node);
}, SYNC_CHECK_INTERVAL);
return;
}
LogPrintfCategory(BCLog::COINDB, "Finished syncing to tip, syncing chainstate to disk\n");
node.chainman->ActiveChainstate().CoinsTip().Sync();
}
bool AppInitInterfaces(NodeContext& node)
{
node.chain = node.init->makeChain();
@ -1923,6 +1964,10 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
StartupNotify(args);
#endif
node.scheduler->scheduleFromNow([&node] {
SyncCoinsTipAfterChainSync(node);
}, SYNC_CHECK_INTERVAL);
return true;
}

View File

@ -518,6 +518,7 @@ public:
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
int GetNumberOfPeersWithValidatedDownloads() const override EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
@ -1713,6 +1714,12 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
return true;
}
int PeerManagerImpl::GetNumberOfPeersWithValidatedDownloads() const
{
AssertLockHeld(m_chainman.GetMutex());
return m_peers_downloading_from;
}
void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
{
if (m_opts.max_extra_txs <= 0)

View File

@ -110,6 +110,9 @@ public:
/** This function is used for testing the stale tip eviction logic, see denialofservice_tests.cpp */
virtual void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) = 0;
/** Get number of peers from which we're downloading blocks */
virtual int GetNumberOfPeersWithValidatedDownloads() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) = 0;
};
#endif // BITCOIN_NET_PROCESSING_H