|
|
@ -111,6 +111,16 @@ uint32_t nBlockSequenceId = 1; |
|
|
|
// Sources of received blocks, to be able to send them reject messages or ban
|
|
|
|
// Sources of received blocks, to be able to send them reject messages or ban
|
|
|
|
// them, if processing happens afterwards. Protected by cs_main.
|
|
|
|
// them, if processing happens afterwards. Protected by cs_main.
|
|
|
|
map<uint256, NodeId> mapBlockSource; |
|
|
|
map<uint256, NodeId> mapBlockSource; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Blocks that are in flight, and that are in the queue to be downloaded.
|
|
|
|
|
|
|
|
// Protected by cs_main.
|
|
|
|
|
|
|
|
struct QueuedBlock { |
|
|
|
|
|
|
|
uint256 hash; |
|
|
|
|
|
|
|
int64_t nTime; // Time of "getdata" request in microseconds.
|
|
|
|
|
|
|
|
int nQueuedBefore; // Number of blocks in flight at the time of request.
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; |
|
|
|
|
|
|
|
map<uint256, pair<NodeId, list<uint256>::iterator> > mapBlocksToDownload; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
@ -194,10 +204,20 @@ struct CNodeState { |
|
|
|
std::string name; |
|
|
|
std::string name; |
|
|
|
// List of asynchronously-determined block rejections to notify this peer about.
|
|
|
|
// List of asynchronously-determined block rejections to notify this peer about.
|
|
|
|
std::vector<CBlockReject> rejects; |
|
|
|
std::vector<CBlockReject> rejects; |
|
|
|
|
|
|
|
list<QueuedBlock> vBlocksInFlight; |
|
|
|
|
|
|
|
int nBlocksInFlight; |
|
|
|
|
|
|
|
list<uint256> vBlocksToDownload; |
|
|
|
|
|
|
|
int nBlocksToDownload; |
|
|
|
|
|
|
|
int64_t nLastBlockReceive; |
|
|
|
|
|
|
|
int64_t nLastBlockProcess; |
|
|
|
|
|
|
|
|
|
|
|
CNodeState() { |
|
|
|
CNodeState() { |
|
|
|
nMisbehavior = 0; |
|
|
|
nMisbehavior = 0; |
|
|
|
fShouldBan = false; |
|
|
|
fShouldBan = false; |
|
|
|
|
|
|
|
nBlocksToDownload = 0; |
|
|
|
|
|
|
|
nBlocksInFlight = 0; |
|
|
|
|
|
|
|
nLastBlockReceive = 0; |
|
|
|
|
|
|
|
nLastBlockProcess = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -226,8 +246,71 @@ void InitializeNode(NodeId nodeid, const CNode *pnode) { |
|
|
|
|
|
|
|
|
|
|
|
void FinalizeNode(NodeId nodeid) { |
|
|
|
void FinalizeNode(NodeId nodeid) { |
|
|
|
LOCK(cs_main); |
|
|
|
LOCK(cs_main); |
|
|
|
|
|
|
|
CNodeState *state = State(nodeid); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) |
|
|
|
|
|
|
|
mapBlocksInFlight.erase(entry.hash); |
|
|
|
|
|
|
|
BOOST_FOREACH(const uint256& hash, state->vBlocksToDownload) |
|
|
|
|
|
|
|
mapBlocksToDownload.erase(hash); |
|
|
|
|
|
|
|
|
|
|
|
mapNodeState.erase(nodeid); |
|
|
|
mapNodeState.erase(nodeid); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Requires cs_main.
|
|
|
|
|
|
|
|
void MarkBlockAsReceived(const uint256 &hash, NodeId nodeFrom = -1) { |
|
|
|
|
|
|
|
map<uint256, pair<NodeId, list<uint256>::iterator> >::iterator itToDownload = mapBlocksToDownload.find(hash); |
|
|
|
|
|
|
|
if (itToDownload != mapBlocksToDownload.end()) { |
|
|
|
|
|
|
|
CNodeState *state = State(itToDownload->second.first); |
|
|
|
|
|
|
|
state->vBlocksToDownload.erase(itToDownload->second.second); |
|
|
|
|
|
|
|
state->nBlocksToDownload--; |
|
|
|
|
|
|
|
mapBlocksToDownload.erase(itToDownload); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); |
|
|
|
|
|
|
|
if (itInFlight != mapBlocksInFlight.end()) { |
|
|
|
|
|
|
|
CNodeState *state = State(itInFlight->second.first); |
|
|
|
|
|
|
|
state->vBlocksInFlight.erase(itInFlight->second.second); |
|
|
|
|
|
|
|
state->nBlocksInFlight--; |
|
|
|
|
|
|
|
if (itInFlight->second.first == nodeFrom) |
|
|
|
|
|
|
|
state->nLastBlockReceive = GetTimeMicros(); |
|
|
|
|
|
|
|
mapBlocksInFlight.erase(itInFlight); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Requires cs_main.
|
|
|
|
|
|
|
|
bool AddBlockToQueue(NodeId nodeid, const uint256 &hash) { |
|
|
|
|
|
|
|
if (mapBlocksToDownload.count(hash) || mapBlocksInFlight.count(hash)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CNodeState *state = State(nodeid); |
|
|
|
|
|
|
|
if (state == NULL) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
list<uint256>::iterator it = state->vBlocksToDownload.insert(state->vBlocksToDownload.end(), hash); |
|
|
|
|
|
|
|
state->nBlocksToDownload++; |
|
|
|
|
|
|
|
if (state->nBlocksToDownload > 5000) |
|
|
|
|
|
|
|
Misbehaving(nodeid, 10); |
|
|
|
|
|
|
|
mapBlocksToDownload[hash] = std::make_pair(nodeid, it); |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Requires cs_main.
|
|
|
|
|
|
|
|
void MarkBlockAsInFlight(NodeId nodeid, const uint256 &hash) { |
|
|
|
|
|
|
|
CNodeState *state = State(nodeid); |
|
|
|
|
|
|
|
assert(state != NULL); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure it's not listed somewhere already.
|
|
|
|
|
|
|
|
MarkBlockAsReceived(hash); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QueuedBlock newentry = {hash, GetTimeMicros(), state->nBlocksInFlight}; |
|
|
|
|
|
|
|
if (state->nBlocksInFlight == 0) |
|
|
|
|
|
|
|
state->nLastBlockReceive = newentry.nTime; // Reset when a first request is sent.
|
|
|
|
|
|
|
|
list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); |
|
|
|
|
|
|
|
state->nBlocksInFlight++; |
|
|
|
|
|
|
|
mapBlocksInFlight[hash] = std::make_pair(nodeid, it); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { |
|
|
|
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { |
|
|
@ -1310,6 +1393,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) |
|
|
|
CheckForkWarningConditions(); |
|
|
|
CheckForkWarningConditions(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Requires cs_main.
|
|
|
|
void Misbehaving(NodeId pnode, int howmuch) |
|
|
|
void Misbehaving(NodeId pnode, int howmuch) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (howmuch == 0) |
|
|
|
if (howmuch == 0) |
|
|
@ -2049,7 +2133,6 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos |
|
|
|
pindexNew->nSequenceId = nBlockSequenceId++; |
|
|
|
pindexNew->nSequenceId = nBlockSequenceId++; |
|
|
|
} |
|
|
|
} |
|
|
|
assert(pindexNew); |
|
|
|
assert(pindexNew); |
|
|
|
mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); |
|
|
|
|
|
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; |
|
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; |
|
|
|
pindexNew->phashBlock = &((*mi).first); |
|
|
|
pindexNew->phashBlock = &((*mi).first); |
|
|
|
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); |
|
|
|
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); |
|
|
@ -2400,11 +2483,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl |
|
|
|
return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate"); |
|
|
|
return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate"); |
|
|
|
|
|
|
|
|
|
|
|
// Preliminary checks
|
|
|
|
// Preliminary checks
|
|
|
|
if (!CheckBlock(*pblock, state)) { |
|
|
|
if (!CheckBlock(*pblock, state)) |
|
|
|
if (state.CorruptionPossible()) |
|
|
|
|
|
|
|
mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); |
|
|
|
|
|
|
|
return error("ProcessBlock() : CheckBlock FAILED"); |
|
|
|
return error("ProcessBlock() : CheckBlock FAILED"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); |
|
|
|
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); |
|
|
|
if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) |
|
|
|
if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) |
|
|
@ -3274,7 +3354,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
State(pfrom->GetId())->nLastBlockProcess = GetTimeMicros(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3477,15 +3557,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) |
|
|
|
return error("message inv size() = %"PRIszu"", vInv.size()); |
|
|
|
return error("message inv size() = %"PRIszu"", vInv.size()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// find last block in inv vector
|
|
|
|
|
|
|
|
unsigned int nLastBlock = (unsigned int)(-1); |
|
|
|
|
|
|
|
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { |
|
|
|
|
|
|
|
if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { |
|
|
|
|
|
|
|
nLastBlock = vInv.size() - 1 - nInv; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOCK(cs_main); |
|
|
|
LOCK(cs_main); |
|
|
|
|
|
|
|
|
|
|
|
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) |
|
|
|
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) |
|
|
@ -3499,17 +3570,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) |
|
|
|
LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new"); |
|
|
|
LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new"); |
|
|
|
|
|
|
|
|
|
|
|
if (!fAlreadyHave) { |
|
|
|
if (!fAlreadyHave) { |
|
|
|
if (!fImporting && !fReindex) |
|
|
|
if (!fImporting && !fReindex) { |
|
|
|
pfrom->AskFor(inv); |
|
|
|
if (inv.type == MSG_BLOCK) |
|
|
|
|
|
|
|
AddBlockToQueue(pfrom->GetId(), inv.hash); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
pfrom->AskFor(inv); |
|
|
|
|
|
|
|
} |
|
|
|
} else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { |
|
|
|
} else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { |
|
|
|
PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(inv.hash)); |
|
|
|
PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(inv.hash)); |
|
|
|
} else if (nInv == nLastBlock) { |
|
|
|
|
|
|
|
// In case we are on a very long side-chain, it is possible that we already have
|
|
|
|
|
|
|
|
// the last block in an inv bundle sent in response to getblocks. Try to detect
|
|
|
|
|
|
|
|
// this situation and push another getblocks to continue.
|
|
|
|
|
|
|
|
PushGetBlocks(pfrom, mapBlockIndex[inv.hash], uint256(0)); |
|
|
|
|
|
|
|
if (fDebug) |
|
|
|
|
|
|
|
LogPrintf("force request: %s\n", inv.ToString()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Track requests for our stuff
|
|
|
|
// Track requests for our stuff
|
|
|
@ -3716,6 +3784,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) |
|
|
|
LOCK(cs_main); |
|
|
|
LOCK(cs_main); |
|
|
|
// Remember who we got this block from.
|
|
|
|
// Remember who we got this block from.
|
|
|
|
mapBlockSource[inv.hash] = pfrom->GetId(); |
|
|
|
mapBlockSource[inv.hash] = pfrom->GetId(); |
|
|
|
|
|
|
|
MarkBlockAsReceived(inv.hash, pfrom->GetId()); |
|
|
|
|
|
|
|
|
|
|
|
CValidationState state; |
|
|
|
CValidationState state; |
|
|
|
ProcessBlock(state, pfrom, &block); |
|
|
|
ProcessBlock(state, pfrom, &block); |
|
|
@ -4243,12 +4312,38 @@ bool SendMessages(CNode* pto, bool fSendTrickle) |
|
|
|
pto->PushMessage("inv", vInv); |
|
|
|
pto->PushMessage("inv", vInv); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Detect stalled peers. Require that blocks are in flight, we haven't
|
|
|
|
|
|
|
|
// received a (requested) block in one minute, and that all blocks are
|
|
|
|
|
|
|
|
// in flight for over two minutes, since we first had a chance to
|
|
|
|
|
|
|
|
// process an incoming block.
|
|
|
|
|
|
|
|
int64_t nNow = GetTimeMicros(); |
|
|
|
|
|
|
|
if (!pto->fDisconnect && state.nBlocksInFlight && |
|
|
|
|
|
|
|
state.nLastBlockReceive < state.nLastBlockProcess - BLOCK_DOWNLOAD_TIMEOUT*1000000 && |
|
|
|
|
|
|
|
state.vBlocksInFlight.front().nTime < state.nLastBlockProcess - 2*BLOCK_DOWNLOAD_TIMEOUT*1000000) { |
|
|
|
|
|
|
|
LogPrintf("Peer %s is stalling block download, disconnecting\n", state.name.c_str()); |
|
|
|
|
|
|
|
pto->fDisconnect = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Message: getdata
|
|
|
|
// Message: getdata (blocks)
|
|
|
|
//
|
|
|
|
//
|
|
|
|
vector<CInv> vGetData; |
|
|
|
vector<CInv> vGetData; |
|
|
|
int64_t nNow = GetTime() * 1000000; |
|
|
|
while (!pto->fDisconnect && state.nBlocksToDownload && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { |
|
|
|
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) |
|
|
|
uint256 hash = state.vBlocksToDownload.front(); |
|
|
|
|
|
|
|
vGetData.push_back(CInv(MSG_BLOCK, hash)); |
|
|
|
|
|
|
|
MarkBlockAsInFlight(pto->GetId(), hash); |
|
|
|
|
|
|
|
LogPrint("net", "Requesting block %s from %s\n", hash.ToString().c_str(), state.name.c_str()); |
|
|
|
|
|
|
|
if (vGetData.size() >= 1000) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
pto->PushMessage("getdata", vGetData); |
|
|
|
|
|
|
|
vGetData.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Message: getdata (non-blocks)
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) |
|
|
|
{ |
|
|
|
{ |
|
|
|
const CInv& inv = (*pto->mapAskFor.begin()).second; |
|
|
|
const CInv& inv = (*pto->mapAskFor.begin()).second; |
|
|
|
if (!AlreadyHave(inv)) |
|
|
|
if (!AlreadyHave(inv)) |
|
|
|