Changeset View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 1,236 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 Consensus::Params &consensusParams, | ||||
jasonbcox: consensusParams is redundant with the config that's already passed in | |||||
const CInv &inv, CConnman *connman, | |||||
const std::atomic<bool> &interruptMsgProc) { | const std::atomic<bool> &interruptMsgProc) { | ||||
const Consensus::Params &consensusParams = | |||||
config.GetChainParams().GetConsensus(); | |||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); | |||||
std::vector<CInv> vNotFound; | |||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | |||||
LOCK(cs_main); | 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++; | |||||
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); | 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. | ||||
std::shared_ptr<const CBlock> a_recent_block; | std::shared_ptr<const CBlock> a_recent_block; | ||||
jasonbcoxUnsubmitted Not Done Inline ActionsThis was moved in the commit you referenced: https://github.com/bitcoin/bitcoin/pull/11824/commits/66aa1d58a158991a8014a91335b5bc9c00062f56 I noticed D2607 actually moves it but refers to a different commit. Can you comment on if this is a full backport of commit 66aa1d or just parts of it? jasonbcox: This was moved in the commit you referenced: https://github. | |||||
FabienAuthorUnsubmitted Done Inline ActionsThis is a full backport of commit 66aa1d5. You're right when you say that I move it in D2607. This is because there is a functional change in this commit which require a larger scope for a_recent_block. Fabien: This is a full backport of commit 66aa1d5.
This part is not really "moved above" in the… | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsI see now. That PR is super hard to read on Github. The rest of what you said cleared it up. Cheers jasonbcox: I see now. That PR is super hard to read on Github. The rest of what you said cleared it up. | |||||
{ | { | ||||
LOCK(cs_most_recent_block); | LOCK(cs_most_recent_block); | ||||
a_recent_block = most_recent_block; | a_recent_block = most_recent_block; | ||||
} | } | ||||
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())) { | ||||
// Send block from disk | // Send block from disk | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, (*mi).second, config)) { | if (!ReadBlockFromDisk(block, (*mi).second, config)) { | ||||
assert(!"cannot load block from disk"); | assert(!"cannot load block from disk"); | ||||
} | } | ||||
if (inv.type == MSG_BLOCK) { | if (inv.type == MSG_BLOCK) { | ||||
connman->PushMessage( | connman->PushMessage(pfrom, | ||||
pfrom, msgMaker.Make(NetMsgType::BLOCK, block)); | msgMaker.Make(NetMsgType::BLOCK, block)); | ||||
} 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(block, *pfrom->pfilter); | ||||
CMerkleBlock(block, *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, *block.vtx[pair.first])); | ||||
*block.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(block); | CBlockHeaderAndShortTxIDs cmpctblock(block); | ||||
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, msgMaker.Make(nSendFlags, | pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, block)); | ||||
NetMsgType::BLOCK, block)); | |||||
} | } | ||||
} | } | ||||
// 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) { | |||||
const Consensus::Params &consensusParams = | |||||
config.GetChainParams().GetConsensus(); | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsRemove since it's not used in this function if you apply my above comment jasonbcox: Remove since it's not used in this function if you apply my above comment | |||||
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, consensusParams, 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 |
consensusParams is redundant with the config that's already passed in