Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 1,238 Lines • ▼ Show 20 Lines | auto pushfunc = [&addr, &best, nRelayNodes, &insecure_rand] { | ||||
for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) { | for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) { | ||||
best[i].second->PushAddress(addr, insecure_rand); | best[i].second->PushAddress(addr, insecure_rand); | ||||
} | } | ||||
}; | }; | ||||
connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); | connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); | ||||
} | } | ||||
static void ProcessGetData(const Config &config, CNode *pfrom, | void static ProcessGetBlockData(const Config &config, CNode *pfrom, | ||||
CConnman *connman, | const CInv &inv, CConnman *connman, | ||||
const std::atomic<bool> &interruptMsgProc) { | const std::atomic<bool> &interruptMsgProc) { | ||||
const Consensus::Params &consensusParams = | const Consensus::Params &consensusParams = | ||||
config.GetChainParams().GetConsensus(); | config.GetChainParams().GetConsensus(); | ||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); | |||||
std::vector<CInv> vNotFound; | |||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | |||||
LOCK(cs_main); | |||||
while (it != pfrom->vRecvGetData.end()) { | |||||
// Don't bother if send buffer is too full to respond anyway. | |||||
if (pfrom->fPauseSend) { | |||||
break; | |||||
} | |||||
const CInv &inv = *it; | |||||
{ | |||||
if (interruptMsgProc) { | |||||
return; | |||||
} | |||||
it++; | LOCK(cs_main); | ||||
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || | |||||
inv.type == MSG_CMPCT_BLOCK) { | |||||
bool send = false; | bool send = false; | ||||
BlockMap::iterator mi = mapBlockIndex.find(inv.hash); | |||||
std::shared_ptr<const CBlock> a_recent_block; | std::shared_ptr<const CBlock> a_recent_block; | ||||
std::shared_ptr<const CBlockHeaderAndShortTxIDs> | std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block; | ||||
a_recent_compact_block; | |||||
{ | { | ||||
LOCK(cs_most_recent_block); | LOCK(cs_most_recent_block); | ||||
a_recent_block = most_recent_block; | a_recent_block = most_recent_block; | ||||
a_recent_compact_block = most_recent_compact_block; | a_recent_compact_block = most_recent_compact_block; | ||||
} | } | ||||
{ | |||||
BlockMap::iterator mi = mapBlockIndex.find(inv.hash); | |||||
if (mi != mapBlockIndex.end()) { | if (mi != mapBlockIndex.end()) { | ||||
if (mi->second->nChainTx && | if (mi->second->nChainTx && | ||||
!mi->second->IsValid(BlockValidity::SCRIPTS) && | !mi->second->IsValid(BlockValidity::SCRIPTS) && | ||||
mi->second->IsValid(BlockValidity::TREE)) { | mi->second->IsValid(BlockValidity::TREE)) { | ||||
// If we have the block and all of its parents, but have | // If we have the block and all of its parents, but have | ||||
// not yet validated it, we might be in the middle of | // not yet validated it, we might be in the middle of | ||||
// connecting it (ie in the unlock of cs_main before | // connecting it (ie in the unlock of cs_main before | ||||
// ActivateBestChain but after AcceptBlock). In this | // ActivateBestChain but after AcceptBlock). In this | ||||
// case, we need to run ActivateBestChain prior to | // case, we need to run ActivateBestChain prior to | ||||
// checking the relay conditions below. | // checking the relay conditions below. | ||||
CValidationState dummy; | CValidationState dummy; | ||||
ActivateBestChain(config, dummy, a_recent_block); | ActivateBestChain(config, dummy, a_recent_block); | ||||
} | } | ||||
} | |||||
} | |||||
BlockMap::iterator mi = mapBlockIndex.find(inv.hash); | |||||
if (mi != mapBlockIndex.end()) { | |||||
send = BlockRequestAllowed(mi->second, consensusParams); | send = BlockRequestAllowed(mi->second, consensusParams); | ||||
if (!send) { | if (!send) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"%s: ignoring request from peer=%i for old " | "%s: ignoring request from peer=%i for old " | ||||
"block that isn't in the main chain\n", | "block that isn't in the main chain\n", | ||||
__func__, pfrom->GetId()); | __func__, pfrom->GetId()); | ||||
} | } | ||||
} | } | ||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | |||||
// Disconnect node in case we have reached the outbound limit | // Disconnect node in case we have reached the outbound limit | ||||
// for serving historical blocks. | // for serving historical blocks. | ||||
// Never disconnect whitelisted nodes. | // Never disconnect whitelisted nodes. | ||||
if (send && connman->OutboundTargetReached(true) && | if (send && connman->OutboundTargetReached(true) && | ||||
(((pindexBestHeader != nullptr) && | (((pindexBestHeader != nullptr) && | ||||
(pindexBestHeader->GetBlockTime() - | (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > | ||||
mi->second->GetBlockTime() > | |||||
HISTORICAL_BLOCK_AGE)) || | HISTORICAL_BLOCK_AGE)) || | ||||
inv.type == MSG_FILTERED_BLOCK) && | inv.type == MSG_FILTERED_BLOCK) && | ||||
!pfrom->fWhitelisted) { | !pfrom->fWhitelisted) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"historical block serving limit reached, " | "historical block serving limit reached, " | ||||
"disconnect peer=%d\n", | "disconnect peer=%d\n", | ||||
pfrom->GetId()); | pfrom->GetId()); | ||||
// disconnect node | // disconnect node | ||||
pfrom->fDisconnect = true; | pfrom->fDisconnect = true; | ||||
send = false; | send = false; | ||||
} | } | ||||
// Avoid leaking prune-height by never sending blocks below the | // Avoid leaking prune-height by never sending blocks below the | ||||
// NODE_NETWORK_LIMITED threshold. | // NODE_NETWORK_LIMITED threshold. | ||||
// Add two blocks buffer extension for possible races | // Add two blocks buffer extension for possible races | ||||
if (send && !pfrom->fWhitelisted && | if (send && !pfrom->fWhitelisted && | ||||
((((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == | ((((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == | ||||
NODE_NETWORK_LIMITED) && | NODE_NETWORK_LIMITED) && | ||||
((pfrom->GetLocalServices() & NODE_NETWORK) != | ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && | ||||
NODE_NETWORK) && | |||||
(chainActive.Tip()->nHeight - mi->second->nHeight > | (chainActive.Tip()->nHeight - mi->second->nHeight > | ||||
(int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2)))) { | (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2)))) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Ignore block request below NODE_NETWORK_LIMITED " | "Ignore block request below NODE_NETWORK_LIMITED " | ||||
"threshold from peer=%d\n", | "threshold from peer=%d\n", | ||||
pfrom->GetId()); | pfrom->GetId()); | ||||
// disconnect node and prevent it from stalling (would | // disconnect node and prevent it from stalling (would | ||||
// otherwise wait for the missing block) | // otherwise wait for the missing block) | ||||
pfrom->fDisconnect = true; | pfrom->fDisconnect = true; | ||||
send = false; | send = false; | ||||
} | } | ||||
// Pruned nodes may have deleted the block, so check whether | // Pruned nodes may have deleted the block, so check whether | ||||
// it's available before trying to send. | // it's available before trying to send. | ||||
if (send && (mi->second->nStatus.hasData())) { | if (send && (mi->second->nStatus.hasData())) { | ||||
std::shared_ptr<const CBlock> pblock; | std::shared_ptr<const CBlock> pblock; | ||||
if (a_recent_block && a_recent_block->GetHash() == | if (a_recent_block && | ||||
(*mi).second->GetBlockHash()) { | a_recent_block->GetHash() == (*mi).second->GetBlockHash()) { | ||||
pblock = a_recent_block; | pblock = a_recent_block; | ||||
} else { | } else { | ||||
// Send block from disk | // Send block from disk | ||||
std::shared_ptr<CBlock> pblockRead = | std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); | ||||
std::make_shared<CBlock>(); | if (!ReadBlockFromDisk(*pblockRead, (*mi).second, config)) | ||||
if (!ReadBlockFromDisk(*pblockRead, (*mi).second, | |||||
config)) | |||||
assert(!"cannot load block from disk"); | assert(!"cannot load block from disk"); | ||||
pblock = pblockRead; | pblock = pblockRead; | ||||
} | } | ||||
if (inv.type == MSG_BLOCK) { | if (inv.type == MSG_BLOCK) { | ||||
connman->PushMessage( | connman->PushMessage(pfrom, | ||||
pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); | msgMaker.Make(NetMsgType::BLOCK, *pblock)); | ||||
} else if (inv.type == MSG_FILTERED_BLOCK) { | } else if (inv.type == MSG_FILTERED_BLOCK) { | ||||
bool sendMerkleBlock = false; | bool sendMerkleBlock = false; | ||||
CMerkleBlock merkleBlock; | CMerkleBlock merkleBlock; | ||||
{ | { | ||||
LOCK(pfrom->cs_filter); | LOCK(pfrom->cs_filter); | ||||
if (pfrom->pfilter) { | if (pfrom->pfilter) { | ||||
sendMerkleBlock = true; | sendMerkleBlock = true; | ||||
merkleBlock = | merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter); | ||||
CMerkleBlock(*pblock, *pfrom->pfilter); | |||||
} | } | ||||
} | } | ||||
if (sendMerkleBlock) { | if (sendMerkleBlock) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, | pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); | ||||
merkleBlock)); | |||||
// CMerkleBlock just contains hashes, so also push | // CMerkleBlock just contains hashes, so also push | ||||
// any transactions in the block the client did not | // any transactions in the block the client did not | ||||
// see. This avoids hurting performance by | // see. This avoids hurting performance by | ||||
// pointlessly requiring a round-trip. Note that | // pointlessly requiring a round-trip. Note that | ||||
// there is currently no way for a node to request | // there is currently no way for a node to request | ||||
// any single transactions we didn't send here - | // any single transactions we didn't send here - | ||||
// they must either disconnect and retry or request | // they must either disconnect and retry or request | ||||
// the full block. Thus, the protocol spec specified | // the full block. Thus, the protocol spec specified | ||||
// allows for us to provide duplicate txn here, | // allows for us to provide duplicate txn here, | ||||
// however we MUST always provide at least what the | // however we MUST always provide at least what the | ||||
// remote peer needs. | // remote peer needs. | ||||
typedef std::pair<unsigned int, uint256> PairType; | typedef std::pair<unsigned int, uint256> PairType; | ||||
for (PairType &pair : merkleBlock.vMatchedTxn) { | for (PairType &pair : merkleBlock.vMatchedTxn) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | pfrom, msgMaker.Make(NetMsgType::TX, | ||||
msgMaker.Make(NetMsgType::TX, | |||||
*pblock->vtx[pair.first])); | *pblock->vtx[pair.first])); | ||||
} | } | ||||
} | } | ||||
// else | // else | ||||
// no response | // no response | ||||
} else if (inv.type == MSG_CMPCT_BLOCK) { | } else if (inv.type == MSG_CMPCT_BLOCK) { | ||||
// If a peer is asking for old blocks, we're almost | // If a peer is asking for old blocks, we're almost | ||||
// guaranteed they won't have a useful mempool to match | // guaranteed they won't have a useful mempool to match | ||||
// against a compact block, and we don't feel like | // against a compact block, and we don't feel like | ||||
// constructing the object for them, so instead we | // constructing the object for them, so instead we | ||||
// respond with the full, non-compact block. | // respond with the full, non-compact block. | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
if (CanDirectFetch(consensusParams) && | if (CanDirectFetch(consensusParams) && | ||||
mi->second->nHeight >= | mi->second->nHeight >= | ||||
chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { | chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { | ||||
CBlockHeaderAndShortTxIDs cmpctblock(*pblock); | CBlockHeaderAndShortTxIDs cmpctblock(*pblock); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(nSendFlags, | pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, | ||||
NetMsgType::CMPCTBLOCK, | |||||
cmpctblock)); | cmpctblock)); | ||||
} else { | } else { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::BLOCK, | msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); | ||||
*pblock)); | |||||
} | } | ||||
} | } | ||||
// Trigger the peer node to send a getblocks request for the | // Trigger the peer node to send a getblocks request for the | ||||
// next batch of inventory. | // next batch of inventory. | ||||
if (inv.hash == pfrom->hashContinue) { | if (inv.hash == pfrom->hashContinue) { | ||||
// Bypass PushInventory, this must send even if | // Bypass PushInventory, this must send even if | ||||
// redundant, and we want it right after the last block | // redundant, and we want it right after the last block | ||||
// so they don't wait for other stuff first. | // so they don't wait for other stuff first. | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
vInv.push_back( | vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); | ||||
CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); | connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | ||||
connman->PushMessage( | |||||
pfrom, msgMaker.Make(NetMsgType::INV, vInv)); | |||||
pfrom->hashContinue.SetNull(); | pfrom->hashContinue.SetNull(); | ||||
} | } | ||||
} | } | ||||
} else if (inv.type == MSG_TX) { | } | ||||
static void ProcessGetData(const Config &config, CNode *pfrom, | |||||
CConnman *connman, | |||||
const std::atomic<bool> &interruptMsgProc) { | |||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); | |||||
std::vector<CInv> vNotFound; | |||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | |||||
{ | |||||
LOCK(cs_main); | |||||
while (it != pfrom->vRecvGetData.end() && it->type == MSG_TX) { | |||||
if (interruptMsgProc) { | |||||
return; | |||||
} | |||||
// Don't bother if send buffer is too full to respond anyway. | |||||
if (pfrom->fPauseSend) { | |||||
break; | |||||
} | |||||
const CInv &inv = *it; | |||||
it++; | |||||
// Send stream from relay memory | // Send stream from relay memory | ||||
bool push = false; | bool push = false; | ||||
auto mi = mapRelay.find(inv.hash); | auto mi = mapRelay.find(inv.hash); | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
if (mi != mapRelay.end()) { | if (mi != mapRelay.end()) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); | msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); | ||||
push = true; | push = true; | ||||
} else if (pfrom->timeLastMempoolReq) { | } else if (pfrom->timeLastMempoolReq) { | ||||
auto txinfo = g_mempool.info(inv.hash); | auto txinfo = g_mempool.info(inv.hash); | ||||
// To protect privacy, do not answer getdata using the | // To protect privacy, do not answer getdata using the | ||||
// mempool when that TX couldn't have been INVed in reply to | // mempool when that TX couldn't have been INVed in reply to | ||||
// a MEMPOOL request. | // a MEMPOOL request. | ||||
if (txinfo.tx && | if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { | ||||
txinfo.nTime <= pfrom->timeLastMempoolReq) { | |||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, | pfrom, | ||||
*txinfo.tx)); | msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); | ||||
push = true; | push = true; | ||||
} | } | ||||
} | } | ||||
if (!push) { | if (!push) { | ||||
vNotFound.push_back(inv); | vNotFound.push_back(inv); | ||||
} | } | ||||
} | |||||
// Track requests for our stuff. | // Track requests for our stuff. | ||||
GetMainSignals().Inventory(inv.hash); | GetMainSignals().Inventory(inv.hash); | ||||
} | |||||
if (it != pfrom->vRecvGetData.end()) { | |||||
const CInv &inv = *it; | |||||
it++; | |||||
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || | if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || | ||||
inv.type == MSG_CMPCT_BLOCK) { | inv.type == MSG_CMPCT_BLOCK) { | ||||
break; | ProcessGetBlockData(config, pfrom, inv, connman, | ||||
} | interruptMsgProc); | ||||
} | } | ||||
} | } | ||||
} // release cs_main | |||||
pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); | pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); | ||||
if (!vNotFound.empty()) { | if (!vNotFound.empty()) { | ||||
// Let the peer know that we didn't find what it asked for, so it | // Let the peer know that we didn't find what it asked for, so it | ||||
// doesn't have to wait around forever. Currently only SPV clients | // doesn't have to wait around forever. Currently only SPV clients | ||||
// actually care about this message: it's needed when they are | // actually care about this message: it's needed when they are | ||||
// recursively walking the dependencies of relevant unconfirmed | // recursively walking the dependencies of relevant unconfirmed | ||||
▲ Show 20 Lines • Show All 2,872 Lines • Show Last 20 Lines |