9389a883f1
CSyncManager extracts the headers-first IBD planner from main.cpp into its own translation unit. main.cpp loses ~570 lines of file-scope state and helper functions; the headers handler, block-delivery latency tracking, stall-recovery, and per-peer Tick cadence now route through g_syncManager. MaybeMigrateLevelDbToRocksDb() is now reachable via -migratechaindb / -migratechaindbforce in init.cpp. Reads from <datadir>/txleveldb and writes byte-for-byte identical records into <datadir>/rocksdb via a new CRocksTxDB::WriteRawRecordForMigration() shim over WriteRaw. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
665 lines
20 KiB
C++
665 lines
20 KiB
C++
// Copyright (c) 2026 The Triangles developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "syncmanager.h"
|
|
|
|
#include "bignum.h"
|
|
#include "checkpoints.h"
|
|
#include "main.h"
|
|
#include "net.h"
|
|
#include "util.h"
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
|
|
struct CSyncManager::HeaderNode
|
|
{
|
|
CBlock header;
|
|
int nHeight;
|
|
uint256 nChainTrust;
|
|
bool fRequested;
|
|
int64_t nLastRequestTime;
|
|
int64_t nFirstRequestTime;
|
|
int64_t nInsertTime;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
static const unsigned int MAX_HEADER_SYNC_CACHE = 15000;
|
|
static const size_t HEADER_REDUNDANT_PEER_THRESHOLD = 4;
|
|
static const int64_t HEADER_REQUEST_TIMEOUT_MICROS = 60 * 1000000;
|
|
static const int64_t HEADER_REDUNDANT_REQUEST_MICROS = 5 * 1000000;
|
|
static const int64_t HEADER_SYNC_TTL_MICROS = 15 * 60 * 1000000;
|
|
|
|
std::map<uint256, CSyncManager::HeaderNode> mapHeaders;
|
|
uint256 hashBestHeader = 0;
|
|
int64_t nLastNewHeaderTime = 0;
|
|
}
|
|
|
|
CSyncManager g_syncManager;
|
|
|
|
bool CSyncManager::HaveHeader(const uint256& hash) const
|
|
{
|
|
return mapHeaders.count(hash) != 0;
|
|
}
|
|
|
|
uint256 CSyncManager::GetBestHeader() const
|
|
{
|
|
return hashBestHeader;
|
|
}
|
|
|
|
std::size_t CSyncManager::GetHeaderCount() const
|
|
{
|
|
return mapHeaders.size();
|
|
}
|
|
|
|
uint256 CSyncManager::GetHeaderTrust(unsigned int nBits) const
|
|
{
|
|
CBigNum bnTarget;
|
|
bnTarget.SetCompact(nBits);
|
|
|
|
if (bnTarget <= 0)
|
|
return 0;
|
|
|
|
return ((CBigNum(1) << 256) / (bnTarget + 1)).getuint256();
|
|
}
|
|
|
|
bool CSyncManager::GetKnownHeaderState(const uint256& hash, int& nHeight, uint256& nChainTrust) const
|
|
{
|
|
std::map<uint256, CBlockIndex*>::const_iterator miBlock = mapBlockIndex.find(hash);
|
|
if (miBlock != mapBlockIndex.end())
|
|
{
|
|
nHeight = miBlock->second->nHeight;
|
|
nChainTrust = miBlock->second->nChainTrust;
|
|
return true;
|
|
}
|
|
|
|
std::map<uint256, HeaderNode>::const_iterator miHeader = mapHeaders.find(hash);
|
|
if (miHeader != mapHeaders.end())
|
|
{
|
|
nHeight = miHeader->second.nHeight;
|
|
nChainTrust = miHeader->second.nChainTrust;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CSyncManager::GetPrevHash(const uint256& hash, uint256& hashPrev) const
|
|
{
|
|
std::map<uint256, HeaderNode>::const_iterator miHeader = mapHeaders.find(hash);
|
|
if (miHeader != mapHeaders.end())
|
|
{
|
|
hashPrev = miHeader->second.header.hashPrevBlock;
|
|
return true;
|
|
}
|
|
|
|
std::map<uint256, CBlockIndex*>::const_iterator miBlock = mapBlockIndex.find(hash);
|
|
if (miBlock != mapBlockIndex.end() && miBlock->second->pprev)
|
|
{
|
|
hashPrev = miBlock->second->pprev->GetBlockHash();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CSyncManager::RecomputeBestHeader()
|
|
{
|
|
hashBestHeader = 0;
|
|
uint256 nBestTrust = 0;
|
|
|
|
for (std::map<uint256, HeaderNode>::const_iterator it = mapHeaders.begin(); it != mapHeaders.end(); ++it)
|
|
{
|
|
if (hashBestHeader == 0 || it->second.nChainTrust > nBestTrust)
|
|
{
|
|
hashBestHeader = it->first;
|
|
nBestTrust = it->second.nChainTrust;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSyncManager::PruneHeaders()
|
|
{
|
|
const int64_t nNow = GetTime() * 1000000;
|
|
|
|
if (mapHeaders.size() > MAX_HEADER_SYNC_CACHE / 2)
|
|
{
|
|
unsigned int nEvicted = 0;
|
|
for (std::map<uint256, HeaderNode>::iterator it = mapHeaders.begin(); it != mapHeaders.end(); )
|
|
{
|
|
if (nNow - it->second.nInsertTime >= HEADER_SYNC_TTL_MICROS)
|
|
{
|
|
it = mapHeaders.erase(it);
|
|
++nEvicted;
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
if (nEvicted > 0)
|
|
{
|
|
printf("IBD-DIAG: TTL-evicted %u stale sync headers, %u remain\n",
|
|
nEvicted, (unsigned int)mapHeaders.size());
|
|
RecomputeBestHeader();
|
|
}
|
|
}
|
|
|
|
if (mapHeaders.size() > MAX_HEADER_SYNC_CACHE)
|
|
{
|
|
printf("IBD-DIAG: sync header cache exceeded %u entries, evicting oldest\n", MAX_HEADER_SYNC_CACHE);
|
|
while (mapHeaders.size() > MAX_HEADER_SYNC_CACHE * 3 / 4)
|
|
{
|
|
std::map<uint256, HeaderNode>::iterator oldest = mapHeaders.begin();
|
|
for (std::map<uint256, HeaderNode>::iterator it = mapHeaders.begin(); it != mapHeaders.end(); ++it)
|
|
{
|
|
if (it->second.nInsertTime < oldest->second.nInsertTime)
|
|
oldest = it;
|
|
}
|
|
mapHeaders.erase(oldest);
|
|
}
|
|
RecomputeBestHeader();
|
|
}
|
|
}
|
|
|
|
bool CSyncManager::AddHeaderNode(const CBlock& header, const uint256& hashHeader)
|
|
{
|
|
if (mapBlockIndex.count(hashHeader) || mapHeaders.count(hashHeader))
|
|
return true;
|
|
|
|
if (!header.vtx.empty())
|
|
{
|
|
printf("IBD-DIAG: header rejected (has vtx) hash=%s\n", hashHeader.ToString().substr(0,20).c_str());
|
|
return false;
|
|
}
|
|
|
|
if (header.GetBlockTime() > GetTime() + 15 * 60)
|
|
{
|
|
printf("IBD-DIAG: header rejected (future time) hash=%s time=%u\n",
|
|
hashHeader.ToString().substr(0,20).c_str(), header.nTime);
|
|
return false;
|
|
}
|
|
|
|
int nPrevHeight = -1;
|
|
uint256 nPrevChainTrust = 0;
|
|
if (!GetKnownHeaderState(header.hashPrevBlock, nPrevHeight, nPrevChainTrust))
|
|
{
|
|
printf("IBD-DIAG: header rejected (prev unknown) hash=%s prevHash=%s\n",
|
|
hashHeader.ToString().substr(0,20).c_str(),
|
|
header.hashPrevBlock.ToString().substr(0,20).c_str());
|
|
return false;
|
|
}
|
|
|
|
const int nHeight = nPrevHeight + 1;
|
|
if (nHeight <= CUTOFF_POW_BLOCK && !CheckProofOfWork(hashHeader, header.nBits))
|
|
{
|
|
printf("IBD-DIAG: header PoW FAILED at height %d hash=%s nBits=%08x prevHash=%s\n",
|
|
nHeight, hashHeader.ToString().substr(0,20).c_str(), header.nBits,
|
|
header.hashPrevBlock.ToString().substr(0,20).c_str());
|
|
return false;
|
|
}
|
|
|
|
HeaderNode node;
|
|
node.header = header;
|
|
node.nHeight = nHeight;
|
|
node.nChainTrust = nPrevChainTrust + GetHeaderTrust(header.nBits);
|
|
node.fRequested = false;
|
|
node.nLastRequestTime = 0;
|
|
node.nFirstRequestTime = 0;
|
|
node.nInsertTime = GetTime() * 1000000;
|
|
|
|
mapHeaders.insert({hashHeader, node});
|
|
|
|
if (hashBestHeader == 0 || node.nChainTrust > mapHeaders[hashBestHeader].nChainTrust)
|
|
hashBestHeader = hashHeader;
|
|
|
|
PruneHeaders();
|
|
return true;
|
|
}
|
|
|
|
std::vector<uint256> CSyncManager::GetDownloadPath(uint256 hashTip) const
|
|
{
|
|
std::vector<uint256> vPath;
|
|
|
|
while (hashTip != 0 && !mapBlockIndex.count(hashTip))
|
|
{
|
|
std::map<uint256, HeaderNode>::const_iterator mi = mapHeaders.find(hashTip);
|
|
if (mi == mapHeaders.end())
|
|
break;
|
|
|
|
vPath.push_back(hashTip);
|
|
hashTip = mi->second.header.hashPrevBlock;
|
|
}
|
|
|
|
std::reverse(vPath.begin(), vPath.end());
|
|
return vPath;
|
|
}
|
|
|
|
unsigned int CSyncManager::CountInFlight() const
|
|
{
|
|
const int64_t nNow = GetTime() * 1000000;
|
|
unsigned int nInFlight = 0;
|
|
for (std::map<uint256, HeaderNode>::const_iterator it = mapHeaders.begin(); it != mapHeaders.end(); ++it)
|
|
{
|
|
if (it->second.fRequested && nNow - it->second.nLastRequestTime < HEADER_REQUEST_TIMEOUT_MICROS)
|
|
++nInFlight;
|
|
}
|
|
return nInFlight;
|
|
}
|
|
|
|
unsigned int CSyncManager::GetPlannerDepth() const
|
|
{
|
|
if (hashBestHeader == 0)
|
|
return 0;
|
|
|
|
return (unsigned int)GetDownloadPath(hashBestHeader).size();
|
|
}
|
|
|
|
int CSyncManager::GetPlannerHeight() const
|
|
{
|
|
if (hashBestHeader == 0)
|
|
return pindexBest ? pindexBest->nHeight : -1;
|
|
|
|
std::map<uint256, HeaderNode>::const_iterator mi = mapHeaders.find(hashBestHeader);
|
|
if (mi == mapHeaders.end())
|
|
return pindexBest ? pindexBest->nHeight : -1;
|
|
|
|
return mi->second.nHeight;
|
|
}
|
|
|
|
int64_t CSyncManager::GetRequestTime(const uint256& hashBlock) const
|
|
{
|
|
std::map<uint256, HeaderNode>::const_iterator mi = mapHeaders.find(hashBlock);
|
|
if (mi == mapHeaders.end())
|
|
return 0;
|
|
return mi->second.nFirstRequestTime;
|
|
}
|
|
|
|
void CSyncManager::BlockAccepted(const uint256& hashBlock)
|
|
{
|
|
std::map<uint256, HeaderNode>::iterator mi = mapHeaders.find(hashBlock);
|
|
if (mi == mapHeaders.end())
|
|
return;
|
|
|
|
mapHeaders.erase(mi);
|
|
if (hashBestHeader == hashBlock)
|
|
RecomputeBestHeader();
|
|
}
|
|
|
|
void CSyncManager::ContinueHeaders(CNode* pfrom, const uint256& hashTip)
|
|
{
|
|
if (!pfrom || hashTip == 0)
|
|
return;
|
|
|
|
std::vector<uint256> vHave;
|
|
uint256 hashWalk = hashTip;
|
|
int nStep = 1;
|
|
|
|
while (hashWalk != 0)
|
|
{
|
|
vHave.push_back(hashWalk);
|
|
|
|
for (int i = 0; i < nStep && hashWalk != 0; ++i)
|
|
{
|
|
uint256 hashPrev = 0;
|
|
if (!GetPrevHash(hashWalk, hashPrev))
|
|
hashWalk = 0;
|
|
else
|
|
hashWalk = hashPrev;
|
|
}
|
|
|
|
if (vHave.size() > 10)
|
|
nStep *= 2;
|
|
}
|
|
|
|
vHave.push_back(!fTestNet ? hashGenesisBlockOfficial : hashGenesisBlockTestNet);
|
|
pfrom->PushMessage("getheaders", CBlockLocator(vHave), uint256(0));
|
|
}
|
|
|
|
bool CSyncManager::RequestRefill(CNode* pfrom, uint256 hashTip, int64_t nMinIntervalSeconds, const char* pszReason)
|
|
{
|
|
if (!pfrom || pfrom->fClient || pfrom->nVersion == 0 || !IsInitialBlockDownload())
|
|
return false;
|
|
|
|
const int64_t nNowSec = GetTime();
|
|
if (nMinIntervalSeconds > 0 &&
|
|
nNowSec - pfrom->nLastIbdHeaderRequest < nMinIntervalSeconds)
|
|
return false;
|
|
|
|
uint256 hashLocatorTip = hashTip;
|
|
if (hashLocatorTip == 0 ||
|
|
(!mapBlockIndex.count(hashLocatorTip) && !mapHeaders.count(hashLocatorTip)))
|
|
{
|
|
hashLocatorTip = hashBestHeader;
|
|
}
|
|
|
|
if (hashLocatorTip != 0 && (!pindexBest || hashLocatorTip != pindexBest->GetBlockHash()))
|
|
{
|
|
ContinueHeaders(pfrom, hashLocatorTip);
|
|
}
|
|
else
|
|
{
|
|
if (!pindexBest)
|
|
return false;
|
|
|
|
pfrom->pindexLastGetHeadersBegin = NULL;
|
|
pfrom->PushGetHeaders(pindexBest, uint256(0));
|
|
hashLocatorTip = pindexBest->GetBlockHash();
|
|
}
|
|
|
|
pfrom->nLastIbdHeaderRequest = nNowSec;
|
|
printf("IBD-DIAG: %s getheaders to peer=%s locator=%s plannerDepth=%u inflight=%u\n",
|
|
pszReason, pfrom->addr.ToString().c_str(),
|
|
hashLocatorTip.ToString().substr(0,20).c_str(),
|
|
GetPlannerDepth(), CountInFlight());
|
|
return true;
|
|
}
|
|
|
|
unsigned int CSyncManager::RequestRefillAllPeers(uint256 hashTip, int64_t nMinIntervalSeconds, const char* pszReason)
|
|
{
|
|
std::vector<CNode*> vEligiblePeers;
|
|
{
|
|
LOCK(cs_vNodes);
|
|
for (CNode* pnode : vNodes)
|
|
{
|
|
if (!pnode->fClient && pnode->nVersion != 0 && !pnode->fDisconnect)
|
|
vEligiblePeers.push_back(pnode);
|
|
}
|
|
}
|
|
|
|
unsigned int nRequested = 0;
|
|
for (CNode* pnode : vEligiblePeers)
|
|
{
|
|
if (RequestRefill(pnode, hashTip, nMinIntervalSeconds, pszReason))
|
|
++nRequested;
|
|
}
|
|
|
|
return nRequested;
|
|
}
|
|
|
|
unsigned int CSyncManager::QueueBlocksParallel(unsigned int nWindow)
|
|
{
|
|
if (hashBestHeader == 0)
|
|
return 0;
|
|
|
|
const std::vector<uint256> vPath = GetDownloadPath(hashBestHeader);
|
|
if (vPath.empty())
|
|
return 0;
|
|
|
|
std::vector<CNode*> vEligiblePeers;
|
|
{
|
|
LOCK(cs_vNodes);
|
|
for (CNode* pnode : vNodes)
|
|
{
|
|
if (!pnode->fClient && pnode->nVersion != 0 && !pnode->fDisconnect)
|
|
vEligiblePeers.push_back(pnode);
|
|
}
|
|
}
|
|
|
|
if (vEligiblePeers.empty())
|
|
return 0;
|
|
|
|
const int64_t nNow = GetTime() * 1000000;
|
|
unsigned int nInFlight = CountInFlight();
|
|
unsigned int nQueued = 0;
|
|
unsigned int nPeerIndex = 0;
|
|
|
|
std::sort(vEligiblePeers.begin(), vEligiblePeers.end(),
|
|
[](const CNode* a, const CNode* b) {
|
|
return a->nBlocksDelivered > b->nBlocksDelivered;
|
|
});
|
|
|
|
std::vector<CNode*> vWeightedPeers;
|
|
for (size_t i = 0; i < vEligiblePeers.size(); i++)
|
|
{
|
|
int nWeight = (i == 0) ? 3 : (i == 1) ? 2 : 1;
|
|
for (int w = 0; w < nWeight; w++)
|
|
vWeightedPeers.push_back(vEligiblePeers[i]);
|
|
}
|
|
|
|
int64_t nAdaptiveTimeout = HEADER_REQUEST_TIMEOUT_MICROS;
|
|
{
|
|
int64_t nTotalLatency = 0;
|
|
int nPeersWithLatency = 0;
|
|
for (const CNode* pnode : vEligiblePeers)
|
|
{
|
|
if (pnode->nAvgBlockLatencyUs > 0)
|
|
{
|
|
nTotalLatency += pnode->nAvgBlockLatencyUs;
|
|
++nPeersWithLatency;
|
|
}
|
|
}
|
|
if (nPeersWithLatency > 0)
|
|
{
|
|
int64_t nAvgLatency = nTotalLatency / nPeersWithLatency;
|
|
nAdaptiveTimeout = std::max((int64_t)(10 * 1000000),
|
|
std::min((int64_t)(60 * 1000000), nAvgLatency * 5));
|
|
}
|
|
}
|
|
|
|
for (std::vector<uint256>::const_iterator it = vPath.begin(); it != vPath.end(); ++it)
|
|
{
|
|
if (nInFlight + nQueued >= nWindow)
|
|
break;
|
|
|
|
std::map<uint256, HeaderNode>::iterator mi = mapHeaders.find(*it);
|
|
if (mi == mapHeaders.end())
|
|
continue;
|
|
|
|
bool fNeedsRequest = false;
|
|
if (!mi->second.fRequested)
|
|
fNeedsRequest = true;
|
|
else if (nNow - mi->second.nLastRequestTime >= nAdaptiveTimeout)
|
|
fNeedsRequest = true;
|
|
else if (nNow - mi->second.nLastRequestTime >= HEADER_REDUNDANT_REQUEST_MICROS)
|
|
fNeedsRequest = true;
|
|
|
|
if (!fNeedsRequest)
|
|
continue;
|
|
|
|
CNode* pnode = vWeightedPeers[nPeerIndex % vWeightedPeers.size()];
|
|
pnode->AskFor(CInv(MSG_BLOCK, *it));
|
|
|
|
if (IsInitialBlockDownload() &&
|
|
vWeightedPeers.size() >= 2 &&
|
|
vWeightedPeers.size() < HEADER_REDUNDANT_PEER_THRESHOLD &&
|
|
!mi->second.fRequested)
|
|
{
|
|
CNode* pnode2 = vWeightedPeers[(nPeerIndex + 1) % vWeightedPeers.size()];
|
|
if (pnode2 != pnode)
|
|
pnode2->AskFor(CInv(MSG_BLOCK, *it));
|
|
}
|
|
|
|
if (!mi->second.fRequested || nNow - mi->second.nLastRequestTime >= HEADER_REQUEST_TIMEOUT_MICROS)
|
|
{
|
|
if (!mi->second.fRequested)
|
|
mi->second.nFirstRequestTime = nNow;
|
|
mi->second.fRequested = true;
|
|
mi->second.nLastRequestTime = nNow;
|
|
}
|
|
|
|
++nQueued;
|
|
++nPeerIndex;
|
|
}
|
|
|
|
if (nQueued > 0)
|
|
printf("IBD-DIAG: sync manager queued %u blocks across %zu peers (window=%u, inflight=%u)\n",
|
|
nQueued, vEligiblePeers.size(), nWindow, nInFlight);
|
|
|
|
return nQueued;
|
|
}
|
|
|
|
bool CSyncManager::ProcessHeaders(CNode* pfrom, const std::vector<CBlock>& vHeaders)
|
|
{
|
|
if (vHeaders.size() > 2000)
|
|
{
|
|
pfrom->Misbehaving(20);
|
|
return error("message headers size() = %" PRIszu "", vHeaders.size());
|
|
}
|
|
|
|
uint256 hashChainTip = 0;
|
|
int nNewHeaders = 0;
|
|
for (const CBlock& header : vHeaders)
|
|
{
|
|
if (!header.vtx.empty())
|
|
{
|
|
pfrom->Misbehaving(20);
|
|
return error("headers message includes transactions");
|
|
}
|
|
|
|
const uint256 hashHeader = header.GetHash();
|
|
if (mapBlockIndex.count(hashHeader) || mapHeaders.count(hashHeader))
|
|
{
|
|
hashChainTip = hashHeader;
|
|
continue;
|
|
}
|
|
|
|
if (hashChainTip != 0)
|
|
{
|
|
if (header.hashPrevBlock != hashChainTip)
|
|
{
|
|
pfrom->Misbehaving(20);
|
|
return error("non-continuous headers sequence");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(header.hashPrevBlock);
|
|
if (miPrev == mapBlockIndex.end() && !mapHeaders.count(header.hashPrevBlock))
|
|
break;
|
|
}
|
|
|
|
if (!AddHeaderNode(header, hashHeader))
|
|
{
|
|
pfrom->Misbehaving(20);
|
|
return error("invalid header sequence");
|
|
}
|
|
|
|
hashChainTip = hashHeader;
|
|
nNewHeaders++;
|
|
}
|
|
|
|
int nRequested = 0;
|
|
if (hashBestHeader != 0)
|
|
nRequested = QueueBlocksParallel(HEADER_DOWNLOAD_WINDOW);
|
|
|
|
if (nNewHeaders > 0)
|
|
nLastNewHeaderTime = GetTime();
|
|
|
|
if (nNewHeaders > 0 || nRequested > 0)
|
|
printf("IBD-DIAG: accepted %d new headers, queued %d blocks from %zu headers (peer=%s bestHeader=%s)\n",
|
|
nNewHeaders, nRequested, vHeaders.size(), pfrom->addr.ToString().c_str(),
|
|
hashBestHeader.ToString().substr(0,20).c_str());
|
|
|
|
if (vHeaders.size() >= 2000)
|
|
{
|
|
if (IsInitialBlockDownload() && hashChainTip != 0)
|
|
ContinueHeaders(pfrom, hashChainTip);
|
|
else
|
|
pfrom->PushGetBlocks(pindexBest, uint256(0));
|
|
}
|
|
else if (IsInitialBlockDownload() && nNewHeaders > 0 && hashChainTip != 0)
|
|
{
|
|
ContinueHeaders(pfrom, hashChainTip);
|
|
}
|
|
else if (IsInitialBlockDownload())
|
|
{
|
|
const unsigned int nPlannerDepth = GetPlannerDepth();
|
|
if (nPlannerDepth <= HEADER_SYNC_LOW_WATER)
|
|
RequestRefill(
|
|
pfrom, (hashChainTip != 0) ? hashChainTip : hashBestHeader,
|
|
HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS,
|
|
(nPlannerDepth == 0) ? "headers planner empty" : "headers planner low-water");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CSyncManager::TrackBlockDelivery(CNode* pfrom, const uint256& hashBlock)
|
|
{
|
|
if (!pfrom)
|
|
return;
|
|
|
|
pfrom->nBlocksDelivered++;
|
|
if (nBestHeight > pfrom->nBestKnownHeight)
|
|
pfrom->nBestKnownHeight = nBestHeight;
|
|
|
|
int64_t nRequestTime = GetRequestTime(hashBlock);
|
|
if (nRequestTime > 0)
|
|
{
|
|
int64_t nLatency = GetTime() * 1000000 - nRequestTime;
|
|
if (nLatency > 0)
|
|
{
|
|
if (pfrom->nAvgBlockLatencyUs == 0)
|
|
pfrom->nAvgBlockLatencyUs = nLatency;
|
|
else
|
|
pfrom->nAvgBlockLatencyUs = (pfrom->nAvgBlockLatencyUs * 7 + nLatency) / 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSyncManager::Tick(CNode* pto, int nHighestInvWalk, const uint256& hashHighestInvWalk)
|
|
{
|
|
if (!pto || pto->fClient || pto->nVersion == 0 || !IsInitialBlockDownload())
|
|
return;
|
|
|
|
const int64_t nNowSec = GetTime();
|
|
const unsigned int nPlannerDepth = GetPlannerDepth();
|
|
const unsigned int nInFlight = CountInFlight();
|
|
static int64_t nLastHeaderPlannerControl = 0;
|
|
static int64_t nLastHeaderWatchdog = 0;
|
|
static int64_t nLastBlockPlannerControl = 0;
|
|
|
|
if (nLastNewHeaderTime == 0)
|
|
nLastNewHeaderTime = nNowSec;
|
|
|
|
if (nNowSec - nLastHeaderPlannerControl >= HEADER_SYNC_CONTROL_INTERVAL_SECONDS &&
|
|
nPlannerDepth < HEADER_SYNC_LOW_WATER &&
|
|
nInFlight < HEADER_SYNC_TARGET_INFLIGHT)
|
|
{
|
|
const unsigned int nRefilled = RequestRefillAllPeers(
|
|
hashBestHeader, HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS,
|
|
"control-loop");
|
|
if (nRefilled > 0)
|
|
printf("IBD-DIAG: control-loop refill from %u peers (plannerDepth=%u inflight=%u target=%u)\n",
|
|
nRefilled, nPlannerDepth, nInFlight, HEADER_SYNC_TARGET_INFLIGHT);
|
|
nLastHeaderPlannerControl = nNowSec;
|
|
}
|
|
|
|
if (nNowSec - nLastHeaderWatchdog >= HEADER_SYNC_CONTROL_INTERVAL_SECONDS &&
|
|
nNowSec - nLastNewHeaderTime >= HEADER_SYNC_WATCHDOG_SECONDS)
|
|
{
|
|
const unsigned int nRefilled = RequestRefillAllPeers(
|
|
hashBestHeader, HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS,
|
|
"headers-watchdog");
|
|
if (nRefilled > 0)
|
|
printf("IBD-DIAG: headers watchdog refill from %u peers after %llds without new headers (plannerDepth=%u inflight=%u)\n",
|
|
nRefilled,
|
|
(long long)(nNowSec - nLastNewHeaderTime),
|
|
nPlannerDepth,
|
|
nInFlight);
|
|
nLastHeaderWatchdog = nNowSec;
|
|
}
|
|
|
|
const int64_t nMinInterval = (mapHeaders.size() < HEADER_DOWNLOAD_WINDOW) ? 15 : 60;
|
|
if (nNowSec - pto->nLastIbdHeaderRequest >= nMinInterval)
|
|
RequestRefill(pto, hashBestHeader, nMinInterval, "heartbeat");
|
|
|
|
if (nNowSec - nLastBlockPlannerControl >= HEADER_SYNC_CONTROL_INTERVAL_SECONDS &&
|
|
hashBestHeader != 0 &&
|
|
nPlannerDepth > 0)
|
|
{
|
|
const unsigned int nRequeued = QueueBlocksParallel(HEADER_DOWNLOAD_WINDOW);
|
|
if (nRequeued > 0)
|
|
printf("IBD-DIAG: block-planner control queued %u block requests (plannerDepth=%u inflight=%u)\n",
|
|
nRequeued, nPlannerDepth, nInFlight);
|
|
nLastBlockPlannerControl = nNowSec;
|
|
}
|
|
|
|
if (hashBestHeader == 0 && nHighestInvWalk > nBestHeight &&
|
|
hashHighestInvWalk != 0 && mapBlockIndex.count(hashHighestInvWalk))
|
|
{
|
|
RequestRefill(pto, hashHighestInvWalk, HEADER_SYNC_REFILL_MIN_INTERVAL_SECONDS, "inv-walk bridge");
|
|
}
|
|
}
|