|
|
@ -195,15 +195,10 @@ namespace { |
|
|
|
struct QueuedBlock { |
|
|
|
struct QueuedBlock { |
|
|
|
uint256 hash; |
|
|
|
uint256 hash; |
|
|
|
CBlockIndex* pindex; //!< Optional.
|
|
|
|
CBlockIndex* pindex; //!< Optional.
|
|
|
|
int64_t nTime; //!< Time of "getdata" request in microseconds.
|
|
|
|
|
|
|
|
bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
|
|
|
|
bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request.
|
|
|
|
int64_t nTimeDisconnect; //!< The timeout for this block request (for disconnecting a slow peer)
|
|
|
|
|
|
|
|
}; |
|
|
|
}; |
|
|
|
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; |
|
|
|
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; |
|
|
|
|
|
|
|
|
|
|
|
/** Number of blocks in flight with validated headers. */ |
|
|
|
|
|
|
|
int nQueuedValidatedHeaders = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Number of preferable block download peers. */ |
|
|
|
/** Number of preferable block download peers. */ |
|
|
|
int nPreferredDownload = 0; |
|
|
|
int nPreferredDownload = 0; |
|
|
|
|
|
|
|
|
|
|
@ -212,6 +207,9 @@ namespace { |
|
|
|
|
|
|
|
|
|
|
|
/** Dirty block file entries. */ |
|
|
|
/** Dirty block file entries. */ |
|
|
|
set<int> setDirtyFileInfo; |
|
|
|
set<int> setDirtyFileInfo; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Number of peers from which we're downloading blocks. */ |
|
|
|
|
|
|
|
int nPeersWithValidatedDownloads = 0; |
|
|
|
} // anon namespace
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
@ -259,6 +257,8 @@ struct CNodeState { |
|
|
|
//! Since when we're stalling block download progress (in microseconds), or 0.
|
|
|
|
//! Since when we're stalling block download progress (in microseconds), or 0.
|
|
|
|
int64_t nStallingSince; |
|
|
|
int64_t nStallingSince; |
|
|
|
list<QueuedBlock> vBlocksInFlight; |
|
|
|
list<QueuedBlock> vBlocksInFlight; |
|
|
|
|
|
|
|
//! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty.
|
|
|
|
|
|
|
|
int64_t nDownloadingSince; |
|
|
|
int nBlocksInFlight; |
|
|
|
int nBlocksInFlight; |
|
|
|
int nBlocksInFlightValidHeaders; |
|
|
|
int nBlocksInFlightValidHeaders; |
|
|
|
//! Whether we consider this a preferred download peer.
|
|
|
|
//! Whether we consider this a preferred download peer.
|
|
|
@ -276,6 +276,7 @@ struct CNodeState { |
|
|
|
pindexBestHeaderSent = NULL; |
|
|
|
pindexBestHeaderSent = NULL; |
|
|
|
fSyncStarted = false; |
|
|
|
fSyncStarted = false; |
|
|
|
nStallingSince = 0; |
|
|
|
nStallingSince = 0; |
|
|
|
|
|
|
|
nDownloadingSince = 0; |
|
|
|
nBlocksInFlight = 0; |
|
|
|
nBlocksInFlight = 0; |
|
|
|
nBlocksInFlightValidHeaders = 0; |
|
|
|
nBlocksInFlightValidHeaders = 0; |
|
|
|
fPreferredDownload = false; |
|
|
|
fPreferredDownload = false; |
|
|
@ -310,12 +311,6 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) |
|
|
|
nPreferredDownload += state->fPreferredDownload; |
|
|
|
nPreferredDownload += state->fPreferredDownload; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Returns time at which to timeout block request (nTime in microseconds)
|
|
|
|
|
|
|
|
int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void InitializeNode(NodeId nodeid, const CNode *pnode) { |
|
|
|
void InitializeNode(NodeId nodeid, const CNode *pnode) { |
|
|
|
LOCK(cs_main); |
|
|
|
LOCK(cs_main); |
|
|
|
CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; |
|
|
|
CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; |
|
|
@ -335,13 +330,21 @@ void FinalizeNode(NodeId nodeid) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) { |
|
|
|
BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) { |
|
|
|
nQueuedValidatedHeaders -= entry.fValidatedHeaders; |
|
|
|
|
|
|
|
mapBlocksInFlight.erase(entry.hash); |
|
|
|
mapBlocksInFlight.erase(entry.hash); |
|
|
|
} |
|
|
|
} |
|
|
|
EraseOrphansFor(nodeid); |
|
|
|
EraseOrphansFor(nodeid); |
|
|
|
nPreferredDownload -= state->fPreferredDownload; |
|
|
|
nPreferredDownload -= state->fPreferredDownload; |
|
|
|
|
|
|
|
nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); |
|
|
|
|
|
|
|
assert(nPeersWithValidatedDownloads >= 0); |
|
|
|
|
|
|
|
|
|
|
|
mapNodeState.erase(nodeid); |
|
|
|
mapNodeState.erase(nodeid); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mapNodeState.empty()) { |
|
|
|
|
|
|
|
// Do a consistency check after the last peer is removed.
|
|
|
|
|
|
|
|
assert(mapBlocksInFlight.empty()); |
|
|
|
|
|
|
|
assert(nPreferredDownload == 0); |
|
|
|
|
|
|
|
assert(nPeersWithValidatedDownloads == 0); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Requires cs_main.
|
|
|
|
// Requires cs_main.
|
|
|
@ -350,8 +353,15 @@ bool MarkBlockAsReceived(const uint256& hash) { |
|
|
|
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); |
|
|
|
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); |
|
|
|
if (itInFlight != mapBlocksInFlight.end()) { |
|
|
|
if (itInFlight != mapBlocksInFlight.end()) { |
|
|
|
CNodeState *state = State(itInFlight->second.first); |
|
|
|
CNodeState *state = State(itInFlight->second.first); |
|
|
|
nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; |
|
|
|
|
|
|
|
state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; |
|
|
|
state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; |
|
|
|
|
|
|
|
if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { |
|
|
|
|
|
|
|
// Last validated block on the queue was received.
|
|
|
|
|
|
|
|
nPeersWithValidatedDownloads--; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (state->vBlocksInFlight.begin() == itInFlight->second.second) { |
|
|
|
|
|
|
|
// First block on the queue was received, update the start download time for the next one
|
|
|
|
|
|
|
|
state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); |
|
|
|
|
|
|
|
} |
|
|
|
state->vBlocksInFlight.erase(itInFlight->second.second); |
|
|
|
state->vBlocksInFlight.erase(itInFlight->second.second); |
|
|
|
state->nBlocksInFlight--; |
|
|
|
state->nBlocksInFlight--; |
|
|
|
state->nStallingSince = 0; |
|
|
|
state->nStallingSince = 0; |
|
|
@ -369,12 +379,17 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa |
|
|
|
// Make sure it's not listed somewhere already.
|
|
|
|
// Make sure it's not listed somewhere already.
|
|
|
|
MarkBlockAsReceived(hash); |
|
|
|
MarkBlockAsReceived(hash); |
|
|
|
|
|
|
|
|
|
|
|
int64_t nNow = GetTimeMicros(); |
|
|
|
QueuedBlock newentry = {hash, pindex, pindex != NULL}; |
|
|
|
QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; |
|
|
|
|
|
|
|
nQueuedValidatedHeaders += newentry.fValidatedHeaders; |
|
|
|
|
|
|
|
list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); |
|
|
|
list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); |
|
|
|
state->nBlocksInFlight++; |
|
|
|
state->nBlocksInFlight++; |
|
|
|
state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; |
|
|
|
state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; |
|
|
|
|
|
|
|
if (state->nBlocksInFlight == 1) { |
|
|
|
|
|
|
|
// We're starting a block download (batch) from this peer.
|
|
|
|
|
|
|
|
state->nDownloadingSince = GetTimeMicros(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) { |
|
|
|
|
|
|
|
nPeersWithValidatedDownloads++; |
|
|
|
|
|
|
|
} |
|
|
|
mapBlocksInFlight[hash] = std::make_pair(nodeid, it); |
|
|
|
mapBlocksInFlight[hash] = std::make_pair(nodeid, it); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -3896,7 +3911,6 @@ void UnloadBlockIndex() |
|
|
|
nBlockSequenceId = 1; |
|
|
|
nBlockSequenceId = 1; |
|
|
|
mapBlockSource.clear(); |
|
|
|
mapBlockSource.clear(); |
|
|
|
mapBlocksInFlight.clear(); |
|
|
|
mapBlocksInFlight.clear(); |
|
|
|
nQueuedValidatedHeaders = 0; |
|
|
|
|
|
|
|
nPreferredDownload = 0; |
|
|
|
nPreferredDownload = 0; |
|
|
|
setDirtyBlockIndex.clear(); |
|
|
|
setDirtyBlockIndex.clear(); |
|
|
|
setDirtyFileInfo.clear(); |
|
|
|
setDirtyFileInfo.clear(); |
|
|
@ -5813,24 +5827,15 @@ bool SendMessages(CNode* pto) |
|
|
|
LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); |
|
|
|
LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); |
|
|
|
pto->fDisconnect = true; |
|
|
|
pto->fDisconnect = true; |
|
|
|
} |
|
|
|
} |
|
|
|
// In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval
|
|
|
|
// In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval
|
|
|
|
// (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to
|
|
|
|
// (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout.
|
|
|
|
// timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link
|
|
|
|
// We compensate for other peers to prevent killing off peers due to our own downstream link
|
|
|
|
// being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
|
|
|
|
// being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes
|
|
|
|
// to unreasonably increase our timeout.
|
|
|
|
// to unreasonably increase our timeout.
|
|
|
|
// We also compare the block download timeout originally calculated against the time at which we'd disconnect
|
|
|
|
|
|
|
|
// if we assumed the block were being requested now (ignoring blocks we've requested from this peer, since we're
|
|
|
|
|
|
|
|
// only looking at this peer's oldest request). This way a large queue in the past doesn't result in a
|
|
|
|
|
|
|
|
// permanently large window for this block to be delivered (ie if the number of blocks in flight is decreasing
|
|
|
|
|
|
|
|
// more quickly than once every 5 minutes, then we'll shorten the download window for this block).
|
|
|
|
|
|
|
|
if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { |
|
|
|
if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { |
|
|
|
QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); |
|
|
|
QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); |
|
|
|
int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders, consensusParams); |
|
|
|
int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); |
|
|
|
if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { |
|
|
|
if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { |
|
|
|
LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); |
|
|
|
|
|
|
|
queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (queuedBlock.nTimeDisconnect < nNow) { |
|
|
|
|
|
|
|
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); |
|
|
|
LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); |
|
|
|
pto->fDisconnect = true; |
|
|
|
pto->fDisconnect = true; |
|
|
|
} |
|
|
|
} |
|
|
|