Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 479 Lines • ▼ Show 20 Lines | static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); | std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); | ||||
if (it == mapNodeState.end()) { | if (it == mapNodeState.end()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return &it->second; | return &it->second; | ||||
} | } | ||||
static void UpdatePreferredDownload(CNode *node, CNodeState *state) | static void UpdatePreferredDownload(const CNode &node, CNodeState *state) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
nPreferredDownload -= state->fPreferredDownload; | nPreferredDownload -= state->fPreferredDownload; | ||||
// Whether this node should be marked as a preferred download node. | // Whether this node should be marked as a preferred download node. | ||||
state->fPreferredDownload = | state->fPreferredDownload = | ||||
(!node->fInbound || node->HasPermission(PF_NOBAN)) && !node->fOneShot && | (!node.fInbound || node.HasPermission(PF_NOBAN)) && !node.fOneShot && | ||||
!node->fClient; | !node.fClient; | ||||
nPreferredDownload += state->fPreferredDownload; | nPreferredDownload += state->fPreferredDownload; | ||||
} | } | ||||
static void PushNodeVersion(const Config &config, CNode *pnode, | static void PushNodeVersion(const Config &config, CNode &pnode, | ||||
CConnman *connman, int64_t nTime) { | CConnman *connman, int64_t nTime) { | ||||
// Note that pnode->GetLocalServices() is a reflection of the local | // Note that pnode.GetLocalServices() is a reflection of the local | ||||
// services we were offering when the CNode object was created for this | // services we were offering when the CNode object was created for this | ||||
// peer. | // peer. | ||||
ServiceFlags nLocalNodeServices = pnode->GetLocalServices(); | ServiceFlags nLocalNodeServices = pnode.GetLocalServices(); | ||||
uint64_t nonce = pnode->GetLocalNonce(); | uint64_t nonce = pnode.GetLocalNonce(); | ||||
int nNodeStartingHeight = pnode->GetMyStartingHeight(); | int nNodeStartingHeight = pnode.GetMyStartingHeight(); | ||||
NodeId nodeid = pnode->GetId(); | NodeId nodeid = pnode.GetId(); | ||||
CAddress addr = pnode->addr; | CAddress addr = pnode.addr; | ||||
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) | CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) | ||||
? addr | ? addr | ||||
: CAddress(CService(), addr.nServices)); | : CAddress(CService(), addr.nServices)); | ||||
CAddress addrMe = CAddress(CService(), nLocalNodeServices); | CAddress addrMe = CAddress(CService(), nLocalNodeServices); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pnode, CNetMsgMaker(INIT_PROTO_VERSION) | &pnode, CNetMsgMaker(INIT_PROTO_VERSION) | ||||
.Make(NetMsgType::VERSION, PROTOCOL_VERSION, | .Make(NetMsgType::VERSION, PROTOCOL_VERSION, | ||||
uint64_t(nLocalNodeServices), nTime, addrYou, addrMe, | uint64_t(nLocalNodeServices), nTime, addrYou, addrMe, | ||||
nonce, userAgent(config), nNodeStartingHeight, | nonce, userAgent(config), nNodeStartingHeight, | ||||
::g_relay_txes && pnode->m_tx_relay != nullptr)); | ::g_relay_txes && pnode.m_tx_relay != nullptr)); | ||||
if (fLogIPs) { | if (fLogIPs) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"send version message: version %d, blocks=%d, us=%s, them=%s, " | "send version message: version %d, blocks=%d, us=%s, them=%s, " | ||||
"peer=%d\n", | "peer=%d\n", | ||||
PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), | PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), | ||||
addrYou.ToString(), nodeid); | addrYou.ToString(), nodeid); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 405 Lines • ▼ Show 20 Lines | void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) { | ||||
CNodeState *state = State(node); | CNodeState *state = State(node); | ||||
if (state) { | if (state) { | ||||
state->m_last_block_announcement = time_in_seconds; | state->m_last_block_announcement = time_in_seconds; | ||||
} | } | ||||
} | } | ||||
// Returns true for outbound peers, excluding manual connections, feelers, and | // Returns true for outbound peers, excluding manual connections, feelers, and | ||||
// one-shots. | // one-shots. | ||||
static bool IsOutboundDisconnectionCandidate(const CNode *node) { | static bool IsOutboundDisconnectionCandidate(const CNode &node) { | ||||
return !(node->fInbound || node->m_manual_connection || node->fFeeler || | return !(node.fInbound || node.m_manual_connection || node.fFeeler || | ||||
node->fOneShot); | node.fOneShot); | ||||
} | } | ||||
void PeerLogicValidation::InitializeNode(const Config &config, CNode *pnode) { | void PeerLogicValidation::InitializeNode(const Config &config, CNode *pnode) { | ||||
CAddress addr = pnode->addr; | CAddress addr = pnode->addr; | ||||
std::string addrName = pnode->GetAddrName(); | std::string addrName = pnode->GetAddrName(); | ||||
NodeId nodeid = pnode->GetId(); | NodeId nodeid = pnode->GetId(); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
mapNodeState.emplace_hint( | mapNodeState.emplace_hint( | ||||
mapNodeState.end(), std::piecewise_construct, | mapNodeState.end(), std::piecewise_construct, | ||||
std::forward_as_tuple(nodeid), | std::forward_as_tuple(nodeid), | ||||
std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, | std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, | ||||
pnode->m_manual_connection)); | pnode->m_manual_connection)); | ||||
} | } | ||||
if (!pnode->fInbound) { | if (!pnode->fInbound) { | ||||
PushNodeVersion(config, pnode, connman, GetTime()); | PushNodeVersion(config, *pnode, connman, GetTime()); | ||||
} | } | ||||
} | } | ||||
void PeerLogicValidation::FinalizeNode(const Config &config, NodeId nodeid, | void PeerLogicValidation::FinalizeNode(const Config &config, NodeId nodeid, | ||||
bool &fUpdateConnectionTime) { | bool &fUpdateConnectionTime) { | ||||
fUpdateConnectionTime = false; | fUpdateConnectionTime = false; | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CNodeState *state = State(nodeid); | CNodeState *state = State(nodeid); | ||||
▲ Show 20 Lines • Show All 223 Lines • ▼ Show 20 Lines | void Misbehaving(NodeId pnode, int howmuch, const std::string &reason) | ||||
} else { | } else { | ||||
LogPrintf("%s: %s peer=%d (%d -> %d) reason: %s\n", __func__, | LogPrintf("%s: %s peer=%d (%d -> %d) reason: %s\n", __func__, | ||||
state->name, pnode, state->nMisbehavior - howmuch, | state->name, pnode, state->nMisbehavior - howmuch, | ||||
state->nMisbehavior, reason); | state->nMisbehavior, reason); | ||||
} | } | ||||
} | } | ||||
// overloaded variant of above to operate on CNode*s | // overloaded variant of above to operate on CNode*s | ||||
static void Misbehaving(CNode *node, int howmuch, const std::string &reason) | static void Misbehaving(const CNode &node, int howmuch, | ||||
const std::string &reason) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
Misbehaving(node->GetId(), howmuch, reason); | Misbehaving(node.GetId(), howmuch, reason); | ||||
} | } | ||||
/** | /** | ||||
* Returns true if the given validation state result may result in a peer | * Returns true if the given validation state result may result in a peer | ||||
* banning/disconnecting us. We use this to determine which unaccepted | * banning/disconnecting us. We use this to determine which unaccepted | ||||
* transactions from a whitelisted peer that we can safely relay. | * transactions from a whitelisted peer that we can safely relay. | ||||
*/ | */ | ||||
static bool TxRelayMayResultInDisconnect(const TxValidationState &state) { | static bool TxRelayMayResultInDisconnect(const TxValidationState &state) { | ||||
▲ Show 20 Lines • Show All 427 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 ProcessGetBlockData(const Config &config, CNode *pfrom, | static void ProcessGetBlockData(const Config &config, CNode &pfrom, | ||||
const CInv &inv, 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(); | ||||
const BlockHash hash(inv.hash); | const BlockHash hash(inv.hash); | ||||
bool send = false; | bool send = false; | ||||
Show All 33 Lines | static void ProcessGetBlockData(const Config &config, CNode &pfrom, | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CBlockIndex *pindex = LookupBlockIndex(hash); | const CBlockIndex *pindex = LookupBlockIndex(hash); | ||||
if (pindex) { | if (pindex) { | ||||
send = BlockRequestAllowed(pindex, consensusParams); | send = BlockRequestAllowed(pindex, 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()); | const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | ||||
// Disconnect node in case we have reached the outbound limit for serving | // Disconnect node in case we have reached the outbound limit for serving | ||||
// historical blocks. | // 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() - pindex->GetBlockTime() > | (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > | ||||
HISTORICAL_BLOCK_AGE)) || | HISTORICAL_BLOCK_AGE)) || | ||||
inv.type == MSG_FILTERED_BLOCK) && | inv.type == MSG_FILTERED_BLOCK) && | ||||
!pfrom->HasPermission(PF_NOBAN)) { | !pfrom.HasPermission(PF_NOBAN)) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"historical block serving limit reached, disconnect peer=%d\n", | "historical block serving limit reached, 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->HasPermission(PF_NOBAN) && | if (send && !pfrom.HasPermission(PF_NOBAN) && | ||||
((((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == | ((((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == | ||||
NODE_NETWORK_LIMITED) && | NODE_NETWORK_LIMITED) && | ||||
((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && | ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && | ||||
(::ChainActive().Tip()->nHeight - pindex->nHeight > | (::ChainActive().Tip()->nHeight - pindex->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 otherwise wait | // disconnect node and prevent it from stalling (would otherwise wait | ||||
// for the missing block) | // for the missing block) | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
send = false; | send = false; | ||||
} | } | ||||
// Pruned nodes may have deleted the block, so check whether it's available | // Pruned nodes may have deleted the block, so check whether it's available | ||||
// before trying to send. | // before trying to send. | ||||
if (send && pindex->nStatus.hasData()) { | if (send && pindex->nStatus.hasData()) { | ||||
std::shared_ptr<const CBlock> pblock; | std::shared_ptr<const CBlock> pblock; | ||||
if (a_recent_block && | if (a_recent_block && | ||||
a_recent_block->GetHash() == pindex->GetBlockHash()) { | a_recent_block->GetHash() == pindex->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::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); | ||||
if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams)) { | if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams)) { | ||||
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(pfrom, | connman->PushMessage(&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; | ||||
if (pfrom->m_tx_relay != nullptr) { | if (pfrom.m_tx_relay != nullptr) { | ||||
LOCK(pfrom->m_tx_relay->cs_filter); | LOCK(pfrom.m_tx_relay->cs_filter); | ||||
if (pfrom->m_tx_relay->pfilter) { | if (pfrom.m_tx_relay->pfilter) { | ||||
sendMerkleBlock = true; | sendMerkleBlock = true; | ||||
merkleBlock = | merkleBlock = | ||||
CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter); | CMerkleBlock(*pblock, *pfrom.m_tx_relay->pfilter); | ||||
} | } | ||||
} | } | ||||
if (sendMerkleBlock) { | if (sendMerkleBlock) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); | &pfrom, | ||||
msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); | |||||
// CMerkleBlock just contains hashes, so also push any | // CMerkleBlock just contains hashes, so also push any | ||||
// transactions in the block the client did not see. This avoids | // transactions in the block the client did not see. This avoids | ||||
// hurting performance by pointlessly requiring a round-trip. | // hurting performance by pointlessly requiring a round-trip. | ||||
// Note that there is currently no way for a node to request any | // Note that there is currently no way for a node to request any | ||||
// single transactions we didn't send here - they must either | // single transactions we didn't send here - they must either | ||||
// disconnect and retry or request the full block. Thus, the | // disconnect and retry or request the full block. Thus, the | ||||
// protocol spec specified allows for us to provide duplicate | // protocol spec specified allows for us to provide duplicate | ||||
// txn here, however we MUST always provide at least what the | // txn here, however we MUST always provide at least what the | ||||
// remote peer needs. | // remote peer needs. | ||||
typedef std::pair<size_t, uint256> PairType; | typedef std::pair<size_t, uint256> PairType; | ||||
for (PairType &pair : merkleBlock.vMatchedTxn) { | for (PairType &pair : merkleBlock.vMatchedTxn) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::TX, | &pfrom, 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 guaranteed they | // If a peer is asking for old blocks, we're almost guaranteed they | ||||
// won't have a useful mempool to match against a compact block, and | // won't have a useful mempool to match against a compact block, and | ||||
// we don't feel like constructing the object for them, so instead | // we don't feel like constructing the object for them, so instead | ||||
// we respond with the full, non-compact block. | // we respond with the full, non-compact block. | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
if (CanDirectFetch(consensusParams) && | if (CanDirectFetch(consensusParams) && | ||||
pindex->nHeight >= | pindex->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, NetMsgType::CMPCTBLOCK, | &pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, | ||||
cmpctblock)); | cmpctblock)); | ||||
} else { | } else { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | &pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); | msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); | ||||
} | } | ||||
} | } | ||||
// Trigger the peer node to send a getblocks request for the next batch | // Trigger the peer node to send a getblocks request for the next batch | ||||
// of inventory. | // of inventory. | ||||
if (hash == pfrom->hashContinue) { | if (hash == pfrom.hashContinue) { | ||||
// Bypass PushInventory, this must send even if redundant, and we | // Bypass PushInventory, this must send even if redundant, and we | ||||
// want it right after the last block so they don't wait for other | // want it right after the last block so they don't wait for other | ||||
// stuff first. | // 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 = BlockHash(); | pfrom.hashContinue = BlockHash(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static void ProcessGetData(const Config &config, CNode *pfrom, | static void ProcessGetData(const Config &config, CNode &pfrom, | ||||
CConnman *connman, | CConnman *connman, | ||||
const std::atomic<bool> &interruptMsgProc) | const std::atomic<bool> &interruptMsgProc) | ||||
LOCKS_EXCLUDED(cs_main) { | LOCKS_EXCLUDED(cs_main) { | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); | std::deque<CInv>::iterator it = pfrom.vRecvGetData.begin(); | ||||
std::vector<CInv> vNotFound; | std::vector<CInv> vNotFound; | ||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | ||||
// Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a | // Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a | ||||
// block-relay-only outbound peer, we will stop processing further getdata | // block-relay-only outbound peer, we will stop processing further getdata | ||||
// messages from this peer (likely resulting in our peer eventually | // messages from this peer (likely resulting in our peer eventually | ||||
// disconnecting us). | // disconnecting us). | ||||
if (pfrom->m_tx_relay != nullptr) { | if (pfrom.m_tx_relay != nullptr) { | ||||
// mempool entries added before this time have likely expired from | // mempool entries added before this time have likely expired from | ||||
// mapRelay | // mapRelay | ||||
const std::chrono::seconds longlived_mempool_time = | const std::chrono::seconds longlived_mempool_time = | ||||
GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; | GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; | ||||
const std::chrono::seconds mempool_req = | const std::chrono::seconds mempool_req = | ||||
pfrom->m_tx_relay->m_last_mempool_req.load(); | pfrom.m_tx_relay->m_last_mempool_req.load(); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
while (it != pfrom->vRecvGetData.end() && it->type == MSG_TX) { | while (it != pfrom.vRecvGetData.end() && it->type == MSG_TX) { | ||||
if (interruptMsgProc) { | if (interruptMsgProc) { | ||||
return; | return; | ||||
} | } | ||||
// Don't bother if send buffer is too full to respond anyway. | // Don't bother if send buffer is too full to respond anyway. | ||||
if (pfrom->fPauseSend) { | if (pfrom.fPauseSend) { | ||||
break; | break; | ||||
} | } | ||||
const CInv &inv = *it; | const CInv &inv = *it; | ||||
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 { | } else { | ||||
auto txinfo = g_mempool.info(TxId(inv.hash)); | auto txinfo = g_mempool.info(TxId(inv.hash)); | ||||
// To protect privacy, do not answer getdata using the mempool | // To protect privacy, do not answer getdata using the mempool | ||||
// when that TX couldn't have been INVed in reply to a MEMPOOL | // when that TX couldn't have been INVed in reply to a MEMPOOL | ||||
// request, or when it's too recent to have expired from | // request, or when it's too recent to have expired from | ||||
// mapRelay. | // mapRelay. | ||||
if (txinfo.tx && | if (txinfo.tx && | ||||
((mempool_req.count() && txinfo.m_time <= mempool_req) || | ((mempool_req.count() && txinfo.m_time <= mempool_req) || | ||||
(txinfo.m_time <= longlived_mempool_time))) { | (txinfo.m_time <= longlived_mempool_time))) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | &pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::TX, *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); | ||||
} | } | ||||
} | } | ||||
} // release cs_main | } // release cs_main | ||||
if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) { | if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) { | ||||
const CInv &inv = *it; | const CInv &inv = *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) { | ||||
it++; | it++; | ||||
ProcessGetBlockData(config, pfrom, inv, connman, interruptMsgProc); | ProcessGetBlockData(config, pfrom, inv, connman, interruptMsgProc); | ||||
} | } | ||||
} | } | ||||
// Unknown types in the GetData stay in vRecvGetData and block any future | // Unknown types in the GetData stay in vRecvGetData and block any future | ||||
// message from this peer, see vRecvGetData check in ProcessMessages(). | // message from this peer, see vRecvGetData check in ProcessMessages(). | ||||
// Depending on future p2p changes, we might either drop unknown getdata on | // Depending on future p2p changes, we might either drop unknown getdata on | ||||
// the floor or disconnect the peer. | // the floor or disconnect the peer. | ||||
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. SPV clients care about this | // doesn't have to wait around forever. SPV clients care about this | ||||
// message: it's needed when they are recursively walking the | // message: it's needed when they are recursively walking the | ||||
// dependencies of relevant unconfirmed transactions. SPV clients want | // dependencies of relevant unconfirmed transactions. SPV clients want | ||||
// to do that because they want to know about (and store and rebroadcast | // to do that because they want to know about (and store and rebroadcast | ||||
// and risk analyze) the dependencies of transactions relevant to them, | // and risk analyze) the dependencies of transactions relevant to them, | ||||
// without having to download the entire memory pool. Also, other nodes | // without having to download the entire memory pool. Also, other nodes | ||||
// can use these messages to automatically request a transaction from | // can use these messages to automatically request a transaction from | ||||
// some other peer that annnounced it, and stop waiting for us to | // some other peer that annnounced it, and stop waiting for us to | ||||
// respond. In normal operation, we often send NOTFOUND messages for | // respond. In normal operation, we often send NOTFOUND messages for | ||||
// parents of transactions that we relay; if a peer is missing a parent, | // parents of transactions that we relay; if a peer is missing a parent, | ||||
// they may assume we have them and request the parents from us. | // they may assume we have them and request the parents from us. | ||||
connman->PushMessage(pfrom, | connman->PushMessage(&pfrom, | ||||
msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); | msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); | ||||
} | } | ||||
} | } | ||||
inline static void SendBlockTransactions(const CBlock &block, | inline static void SendBlockTransactions(const CBlock &block, | ||||
const BlockTransactionsRequest &req, | const BlockTransactionsRequest &req, | ||||
CNode *pfrom, CConnman *connman) { | CNode &pfrom, CConnman *connman) { | ||||
BlockTransactions resp(req); | BlockTransactions resp(req); | ||||
for (size_t i = 0; i < req.indices.size(); i++) { | for (size_t i = 0; i < req.indices.size(); i++) { | ||||
if (req.indices[i] >= block.vtx.size()) { | if (req.indices[i] >= block.vtx.size()) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 100, "out-of-bound-tx-index"); | Misbehaving(pfrom, 100, "out-of-bound-tx-index"); | ||||
LogPrintf( | LogPrintf( | ||||
"Peer %d sent us a getblocktxn with out-of-bounds tx indices\n", | "Peer %d sent us a getblocktxn with out-of-bounds tx indices\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return; | return; | ||||
} | } | ||||
resp.txn[i] = block.vtx[req.indices[i]]; | resp.txn[i] = block.vtx[req.indices[i]]; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | ||||
int nSendFlags = 0; | int nSendFlags = 0; | ||||
connman->PushMessage(pfrom, | connman->PushMessage(&pfrom, | ||||
msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); | msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); | ||||
} | } | ||||
static bool ProcessHeadersMessage(const Config &config, CNode *pfrom, | static bool ProcessHeadersMessage(const Config &config, CNode &pfrom, | ||||
CConnman *connman, | CConnman *connman, | ||||
const std::vector<CBlockHeader> &headers, | const std::vector<CBlockHeader> &headers, | ||||
bool via_compact_block) { | bool via_compact_block) { | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | ||||
size_t nCount = headers.size(); | size_t nCount = headers.size(); | ||||
if (nCount == 0) { | if (nCount == 0) { | ||||
// Nothing interesting. Stop asking this peers for more headers. | // Nothing interesting. Stop asking this peers for more headers. | ||||
return true; | return true; | ||||
} | } | ||||
bool received_new_header = false; | bool received_new_header = false; | ||||
const CBlockIndex *pindexLast = nullptr; | const CBlockIndex *pindexLast = nullptr; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CNodeState *nodestate = State(pfrom->GetId()); | CNodeState *nodestate = State(pfrom.GetId()); | ||||
// If this looks like it could be a block announcement (nCount < | // If this looks like it could be a block announcement (nCount < | ||||
// MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that | // MAX_BLOCKS_TO_ANNOUNCE), use special logic for handling headers that | ||||
// don't connect: | // don't connect: | ||||
// - Send a getheaders message in response to try to connect the chain. | // - Send a getheaders message in response to try to connect the chain. | ||||
// - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that | // - The peer can send up to MAX_UNCONNECTING_HEADERS in a row that | ||||
// don't connect before giving DoS points | // don't connect before giving DoS points | ||||
// - Once a headers message is received that is valid and does connect, | // - Once a headers message is received that is valid and does connect, | ||||
// nUnconnectingHeaders gets reset back to 0. | // nUnconnectingHeaders gets reset back to 0. | ||||
if (!LookupBlockIndex(headers[0].hashPrevBlock) && | if (!LookupBlockIndex(headers[0].hashPrevBlock) && | ||||
nCount < MAX_BLOCKS_TO_ANNOUNCE) { | nCount < MAX_BLOCKS_TO_ANNOUNCE) { | ||||
nodestate->nUnconnectingHeaders++; | nodestate->nUnconnectingHeaders++; | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | &pfrom, | ||||
msgMaker.Make(NetMsgType::GETHEADERS, | msgMaker.Make(NetMsgType::GETHEADERS, | ||||
::ChainActive().GetLocator(pindexBestHeader), | ::ChainActive().GetLocator(pindexBestHeader), | ||||
uint256())); | uint256())); | ||||
LogPrint( | LogPrint( | ||||
BCLog::NET, | BCLog::NET, | ||||
"received header %s: missing prev block %s, sending getheaders " | "received header %s: missing prev block %s, sending getheaders " | ||||
"(%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", | "(%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", | ||||
headers[0].GetHash().ToString(), | headers[0].GetHash().ToString(), | ||||
headers[0].hashPrevBlock.ToString(), pindexBestHeader->nHeight, | headers[0].hashPrevBlock.ToString(), pindexBestHeader->nHeight, | ||||
pfrom->GetId(), nodestate->nUnconnectingHeaders); | pfrom.GetId(), nodestate->nUnconnectingHeaders); | ||||
// Set hashLastUnknownBlock for this peer, so that if we eventually | // Set hashLastUnknownBlock for this peer, so that if we eventually | ||||
// get the headers - even from a different peer - we can use this | // get the headers - even from a different peer - we can use this | ||||
// peer to download. | // peer to download. | ||||
UpdateBlockAvailability(pfrom->GetId(), headers.back().GetHash()); | UpdateBlockAvailability(pfrom.GetId(), headers.back().GetHash()); | ||||
if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == | if (nodestate->nUnconnectingHeaders % MAX_UNCONNECTING_HEADERS == | ||||
0) { | 0) { | ||||
// The peer is sending us many headers we can't connect. | // The peer is sending us many headers we can't connect. | ||||
Misbehaving(pfrom, 20, "too-many-unconnected-headers"); | Misbehaving(pfrom, 20, "too-many-unconnected-headers"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
Show All 13 Lines | const CBlockIndex *pindexLast = nullptr; | ||||
if (!LookupBlockIndex(hashLastBlock)) { | if (!LookupBlockIndex(hashLastBlock)) { | ||||
received_new_header = true; | received_new_header = true; | ||||
} | } | ||||
} | } | ||||
BlockValidationState state; | BlockValidationState state; | ||||
if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast)) { | if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast)) { | ||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, | MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, | ||||
"invalid header received"); | "invalid header received"); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CNodeState *nodestate = State(pfrom->GetId()); | CNodeState *nodestate = State(pfrom.GetId()); | ||||
if (nodestate->nUnconnectingHeaders > 0) { | if (nodestate->nUnconnectingHeaders > 0) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", | "peer=%d: resetting nUnconnectingHeaders (%d -> 0)\n", | ||||
pfrom->GetId(), nodestate->nUnconnectingHeaders); | pfrom.GetId(), nodestate->nUnconnectingHeaders); | ||||
} | } | ||||
nodestate->nUnconnectingHeaders = 0; | nodestate->nUnconnectingHeaders = 0; | ||||
assert(pindexLast); | assert(pindexLast); | ||||
UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); | UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash()); | ||||
// From here, pindexBestKnownBlock should be guaranteed to be non-null, | // From here, pindexBestKnownBlock should be guaranteed to be non-null, | ||||
// because it is set in UpdateBlockAvailability. Some nullptr checks are | // because it is set in UpdateBlockAvailability. Some nullptr checks are | ||||
// still present, however, as belt-and-suspenders. | // still present, however, as belt-and-suspenders. | ||||
if (received_new_header && | if (received_new_header && | ||||
pindexLast->nChainWork > ::ChainActive().Tip()->nChainWork) { | pindexLast->nChainWork > ::ChainActive().Tip()->nChainWork) { | ||||
nodestate->m_last_block_announcement = GetTime(); | nodestate->m_last_block_announcement = GetTime(); | ||||
} | } | ||||
if (nCount == MAX_HEADERS_RESULTS) { | if (nCount == MAX_HEADERS_RESULTS) { | ||||
// Headers message had its maximum size; the peer may have more | // Headers message had its maximum size; the peer may have more | ||||
// headers. | // headers. | ||||
// TODO: optimize: if pindexLast is an ancestor of | // TODO: optimize: if pindexLast is an ancestor of | ||||
// ::ChainActive().Tip or pindexBestHeader, continue from there | // ::ChainActive().Tip or pindexBestHeader, continue from there | ||||
// instead. | // instead. | ||||
LogPrint( | LogPrint( | ||||
BCLog::NET, | BCLog::NET, | ||||
"more getheaders (%d) to end to peer=%d (startheight:%d)\n", | "more getheaders (%d) to end to peer=%d (startheight:%d)\n", | ||||
pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); | pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::GETHEADERS, | &pfrom, msgMaker.Make(NetMsgType::GETHEADERS, | ||||
::ChainActive().GetLocator(pindexLast), | ::ChainActive().GetLocator(pindexLast), | ||||
uint256())); | uint256())); | ||||
} | } | ||||
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); | bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); | ||||
// If this set of headers is valid and ends in a block with at least as | // If this set of headers is valid and ends in a block with at least as | ||||
// much work as our tip, download as much as possible. | // much work as our tip, download as much as possible. | ||||
if (fCanDirectFetch && pindexLast->IsValid(BlockValidity::TREE) && | if (fCanDirectFetch && pindexLast->IsValid(BlockValidity::TREE) && | ||||
::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) { | ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) { | ||||
std::vector<const CBlockIndex *> vToFetch; | std::vector<const CBlockIndex *> vToFetch; | ||||
Show All 22 Lines | if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast)) { | ||||
// Download as much as possible, from earliest to latest. | // Download as much as possible, from earliest to latest. | ||||
for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { | for (const CBlockIndex *pindex : reverse_iterate(vToFetch)) { | ||||
if (nodestate->nBlocksInFlight >= | if (nodestate->nBlocksInFlight >= | ||||
MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | ||||
// Can't download any more from this peer | // Can't download any more from this peer | ||||
break; | break; | ||||
} | } | ||||
vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); | vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); | ||||
MarkBlockAsInFlight(config, pfrom->GetId(), | MarkBlockAsInFlight(config, pfrom.GetId(), | ||||
pindex->GetBlockHash(), | pindex->GetBlockHash(), | ||||
chainparams.GetConsensus(), pindex); | chainparams.GetConsensus(), pindex); | ||||
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", | LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", | ||||
pindex->GetBlockHash().ToString(), pfrom->GetId()); | pindex->GetBlockHash().ToString(), pfrom.GetId()); | ||||
} | } | ||||
if (vGetData.size() > 1) { | if (vGetData.size() > 1) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Downloading blocks toward %s (%d) via headers " | "Downloading blocks toward %s (%d) via headers " | ||||
"direct fetch\n", | "direct fetch\n", | ||||
pindexLast->GetBlockHash().ToString(), | pindexLast->GetBlockHash().ToString(), | ||||
pindexLast->nHeight); | pindexLast->nHeight); | ||||
} | } | ||||
if (vGetData.size() > 0) { | if (vGetData.size() > 0) { | ||||
if (nodestate->fSupportsDesiredCmpctVersion && | if (nodestate->fSupportsDesiredCmpctVersion && | ||||
vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && | vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && | ||||
pindexLast->pprev->IsValid(BlockValidity::CHAIN)) { | pindexLast->pprev->IsValid(BlockValidity::CHAIN)) { | ||||
// In any case, we want to download using a compact | // In any case, we want to download using a compact | ||||
// block, not a regular one. | // block, not a regular one. | ||||
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); | vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); | ||||
} | } | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); | &pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// If we're in IBD, we want outbound peers that will serve us a useful | // If we're in IBD, we want outbound peers that will serve us a useful | ||||
// chain. Disconnect peers that are on chains with insufficient work. | // chain. Disconnect peers that are on chains with insufficient work. | ||||
if (::ChainstateActive().IsInitialBlockDownload() && | if (::ChainstateActive().IsInitialBlockDownload() && | ||||
nCount != MAX_HEADERS_RESULTS) { | nCount != MAX_HEADERS_RESULTS) { | ||||
// When nCount < MAX_HEADERS_RESULTS, we know we have no more | // When nCount < MAX_HEADERS_RESULTS, we know we have no more | ||||
// headers to fetch from this peer. | // headers to fetch from this peer. | ||||
if (nodestate->pindexBestKnownBlock && | if (nodestate->pindexBestKnownBlock && | ||||
nodestate->pindexBestKnownBlock->nChainWork < | nodestate->pindexBestKnownBlock->nChainWork < | ||||
nMinimumChainWork) { | nMinimumChainWork) { | ||||
// This peer has too little work on their headers chain to help | // This peer has too little work on their headers chain to help | ||||
// us sync -- disconnect if using an outbound slot (unless | // us sync -- disconnect if using an outbound slot (unless | ||||
// whitelisted or addnode). | // whitelisted or addnode). | ||||
// Note: We compare their tip to nMinimumChainWork (rather than | // Note: We compare their tip to nMinimumChainWork (rather than | ||||
// ::ChainActive().Tip()) because we won't start block download | // ::ChainActive().Tip()) because we won't start block download | ||||
// until we have a headers chain that has at least | // until we have a headers chain that has at least | ||||
// nMinimumChainWork, even if a peer has a chain past our tip, | // nMinimumChainWork, even if a peer has a chain past our tip, | ||||
// as an anti-DoS measure. | // as an anti-DoS measure. | ||||
if (IsOutboundDisconnectionCandidate(pfrom)) { | if (IsOutboundDisconnectionCandidate(pfrom)) { | ||||
LogPrintf("Disconnecting outbound peer %d -- headers " | LogPrintf("Disconnecting outbound peer %d -- headers " | ||||
"chain has insufficient work\n", | "chain has insufficient work\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && | if (!pfrom.fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && | ||||
nodestate->pindexBestKnownBlock != nullptr && | nodestate->pindexBestKnownBlock != nullptr && | ||||
pfrom->m_tx_relay != nullptr) { | pfrom.m_tx_relay != nullptr) { | ||||
// If this is an outbound full-relay peer, check to see if we should | // If this is an outbound full-relay peer, check to see if we should | ||||
// protect it from the bad/lagging chain logic. Note that | // protect it from the bad/lagging chain logic. Note that | ||||
// block-relay-only peers are already implicitly protected, so we | // block-relay-only peers are already implicitly protected, so we | ||||
// only consider setting m_protect for the full-relay peers. | // only consider setting m_protect for the full-relay peers. | ||||
if (g_outbound_peers_with_protect_from_disconnect < | if (g_outbound_peers_with_protect_from_disconnect < | ||||
MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && | MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && | ||||
nodestate->pindexBestKnownBlock->nChainWork >= | nodestate->pindexBestKnownBlock->nChainWork >= | ||||
::ChainActive().Tip()->nChainWork && | ::ChainActive().Tip()->nChainWork && | ||||
!nodestate->m_chain_sync.m_protect) { | !nodestate->m_chain_sync.m_protect) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Protecting outbound peer=%d from eviction\n", | "Protecting outbound peer=%d from eviction\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
nodestate->m_chain_sync.m_protect = true; | nodestate->m_chain_sync.m_protect = true; | ||||
++g_outbound_peers_with_protect_from_disconnect; | ++g_outbound_peers_with_protect_from_disconnect; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 307 Lines • ▼ Show 20 Lines | static void ProcessGetCFCheckPt(CNode &pfrom, CDataStream &vRecv, | ||||
} | } | ||||
CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion()) | CSerializedNetMsg msg = CNetMsgMaker(pfrom.GetSendVersion()) | ||||
.Make(NetMsgType::CFCHECKPT, filter_type_ser, | .Make(NetMsgType::CFCHECKPT, filter_type_ser, | ||||
stop_index->GetBlockHash(), headers); | stop_index->GetBlockHash(), headers); | ||||
connman.PushMessage(&pfrom, std::move(msg)); | connman.PushMessage(&pfrom, std::move(msg)); | ||||
} | } | ||||
bool ProcessMessage(const Config &config, CNode *pfrom, | bool ProcessMessage(const Config &config, CNode &pfrom, | ||||
const std::string &msg_type, CDataStream &vRecv, | const std::string &msg_type, CDataStream &vRecv, | ||||
int64_t nTimeReceived, CConnman *connman, BanMan *banman, | int64_t nTimeReceived, CConnman *connman, BanMan *banman, | ||||
const std::atomic<bool> &interruptMsgProc) { | const std::atomic<bool> &interruptMsgProc) { | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", | LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", | ||||
SanitizeString(msg_type), vRecv.size(), pfrom->GetId()); | SanitizeString(msg_type), vRecv.size(), pfrom.GetId()); | ||||
if (gArgs.IsArgSet("-dropmessagestest") && | if (gArgs.IsArgSet("-dropmessagestest") && | ||||
GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) { | GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) { | ||||
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); | LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); | ||||
return true; | return true; | ||||
} | } | ||||
if (!(pfrom->GetLocalServices() & NODE_BLOOM) && | if (!(pfrom.GetLocalServices() & NODE_BLOOM) && | ||||
(msg_type == NetMsgType::FILTERLOAD || | (msg_type == NetMsgType::FILTERLOAD || | ||||
msg_type == NetMsgType::FILTERADD)) { | msg_type == NetMsgType::FILTERADD)) { | ||||
if (pfrom->nVersion >= NO_BLOOM_VERSION) { | if (pfrom.nVersion >= NO_BLOOM_VERSION) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 100, "no-bloom-version"); | Misbehaving(pfrom, 100, "no-bloom-version"); | ||||
return false; | return false; | ||||
} else { | } else { | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
if (msg_type == NetMsgType::VERSION) { | if (msg_type == NetMsgType::VERSION) { | ||||
// Each connection can only send one version message | // Each connection can only send one version message | ||||
if (pfrom->nVersion != 0) { | if (pfrom.nVersion != 0) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 1, "multiple-version"); | Misbehaving(pfrom, 1, "multiple-version"); | ||||
return false; | return false; | ||||
} | } | ||||
int64_t nTime; | int64_t nTime; | ||||
CAddress addrMe; | CAddress addrMe; | ||||
CAddress addrFrom; | CAddress addrFrom; | ||||
uint64_t nNonce = 1; | uint64_t nNonce = 1; | ||||
uint64_t nServiceInt; | uint64_t nServiceInt; | ||||
ServiceFlags nServices; | ServiceFlags nServices; | ||||
int nVersion; | int nVersion; | ||||
int nSendVersion; | int nSendVersion; | ||||
std::string cleanSubVer; | std::string cleanSubVer; | ||||
int nStartingHeight = -1; | int nStartingHeight = -1; | ||||
bool fRelay = true; | bool fRelay = true; | ||||
vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; | vRecv >> nVersion >> nServiceInt >> nTime >> addrMe; | ||||
nSendVersion = std::min(nVersion, PROTOCOL_VERSION); | nSendVersion = std::min(nVersion, PROTOCOL_VERSION); | ||||
nServices = ServiceFlags(nServiceInt); | nServices = ServiceFlags(nServiceInt); | ||||
if (!pfrom->fInbound) { | if (!pfrom.fInbound) { | ||||
connman->SetServices(pfrom->addr, nServices); | connman->SetServices(pfrom.addr, nServices); | ||||
} | } | ||||
if (!pfrom->fInbound && !pfrom->fFeeler && | if (!pfrom.fInbound && !pfrom.fFeeler && !pfrom.m_manual_connection && | ||||
!pfrom->m_manual_connection && | |||||
!HasAllDesirableServiceFlags(nServices)) { | !HasAllDesirableServiceFlags(nServices)) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"peer=%d does not offer the expected services " | "peer=%d does not offer the expected services " | ||||
"(%08x offered, %08x expected); disconnecting\n", | "(%08x offered, %08x expected); disconnecting\n", | ||||
pfrom->GetId(), nServices, | pfrom.GetId(), nServices, | ||||
GetDesirableServiceFlags(nServices)); | GetDesirableServiceFlags(nServices)); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return false; | return false; | ||||
} | } | ||||
if (nVersion < MIN_PEER_PROTO_VERSION) { | if (nVersion < MIN_PEER_PROTO_VERSION) { | ||||
// disconnect from peers older than this proto version | // disconnect from peers older than this proto version | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"peer=%d using obsolete version %i; disconnecting\n", | "peer=%d using obsolete version %i; disconnecting\n", | ||||
pfrom->GetId(), nVersion); | pfrom.GetId(), nVersion); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return false; | return false; | ||||
} | } | ||||
if (!vRecv.empty()) { | if (!vRecv.empty()) { | ||||
vRecv >> addrFrom >> nNonce; | vRecv >> addrFrom >> nNonce; | ||||
} | } | ||||
if (!vRecv.empty()) { | if (!vRecv.empty()) { | ||||
std::string strSubVer; | std::string strSubVer; | ||||
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH); | vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH); | ||||
cleanSubVer = SanitizeString(strSubVer); | cleanSubVer = SanitizeString(strSubVer); | ||||
} | } | ||||
if (!vRecv.empty()) { | if (!vRecv.empty()) { | ||||
vRecv >> nStartingHeight; | vRecv >> nStartingHeight; | ||||
} | } | ||||
if (!vRecv.empty()) { | if (!vRecv.empty()) { | ||||
vRecv >> fRelay; | vRecv >> fRelay; | ||||
} | } | ||||
// Disconnect if we connected to ourself | // Disconnect if we connected to ourself | ||||
if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce)) { | if (pfrom.fInbound && !connman->CheckIncomingNonce(nNonce)) { | ||||
LogPrintf("connected to self at %s, disconnecting\n", | LogPrintf("connected to self at %s, disconnecting\n", | ||||
pfrom->addr.ToString()); | pfrom.addr.ToString()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return true; | return true; | ||||
} | } | ||||
if (pfrom->fInbound && addrMe.IsRoutable()) { | if (pfrom.fInbound && addrMe.IsRoutable()) { | ||||
SeenLocal(addrMe); | SeenLocal(addrMe); | ||||
} | } | ||||
// Be shy and don't send version until we hear | // Be shy and don't send version until we hear | ||||
if (pfrom->fInbound) { | if (pfrom.fInbound) { | ||||
PushNodeVersion(config, pfrom, connman, GetAdjustedTime()); | PushNodeVersion(config, pfrom, connman, GetAdjustedTime()); | ||||
} | } | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); | &pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); | ||||
pfrom->nServices = nServices; | pfrom.nServices = nServices; | ||||
pfrom->SetAddrLocal(addrMe); | pfrom.SetAddrLocal(addrMe); | ||||
{ | { | ||||
LOCK(pfrom->cs_SubVer); | LOCK(pfrom.cs_SubVer); | ||||
pfrom->cleanSubVer = cleanSubVer; | pfrom.cleanSubVer = cleanSubVer; | ||||
} | } | ||||
pfrom->nStartingHeight = nStartingHeight; | pfrom.nStartingHeight = nStartingHeight; | ||||
// set nodes not relaying blocks and tx and not serving (parts) of the | // set nodes not relaying blocks and tx and not serving (parts) of the | ||||
// historical blockchain as "clients" | // historical blockchain as "clients" | ||||
pfrom->fClient = (!(nServices & NODE_NETWORK) && | pfrom.fClient = (!(nServices & NODE_NETWORK) && | ||||
!(nServices & NODE_NETWORK_LIMITED)); | !(nServices & NODE_NETWORK_LIMITED)); | ||||
// set nodes not capable of serving the complete blockchain history as | // set nodes not capable of serving the complete blockchain history as | ||||
// "limited nodes" | // "limited nodes" | ||||
pfrom->m_limited_node = | pfrom.m_limited_node = | ||||
(!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED)); | (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED)); | ||||
if (pfrom->m_tx_relay != nullptr) { | if (pfrom.m_tx_relay != nullptr) { | ||||
LOCK(pfrom->m_tx_relay->cs_filter); | LOCK(pfrom.m_tx_relay->cs_filter); | ||||
// set to true after we get the first filter* message | // set to true after we get the first filter* message | ||||
pfrom->m_tx_relay->fRelayTxes = fRelay; | pfrom.m_tx_relay->fRelayTxes = fRelay; | ||||
} | } | ||||
// Change version | // Change version | ||||
pfrom->SetSendVersion(nSendVersion); | pfrom.SetSendVersion(nSendVersion); | ||||
pfrom->nVersion = nVersion; | pfrom.nVersion = nVersion; | ||||
// Potentially mark this peer as a preferred download peer. | // Potentially mark this peer as a preferred download peer. | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
UpdatePreferredDownload(pfrom, State(pfrom->GetId())); | UpdatePreferredDownload(pfrom, State(pfrom.GetId())); | ||||
} | } | ||||
if (!pfrom->fInbound && pfrom->IsAddrRelayPeer()) { | if (!pfrom.fInbound && pfrom.IsAddrRelayPeer()) { | ||||
// Advertise our address | // Advertise our address | ||||
if (fListen && !::ChainstateActive().IsInitialBlockDownload()) { | if (fListen && !::ChainstateActive().IsInitialBlockDownload()) { | ||||
CAddress addr = | CAddress addr = | ||||
GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices()); | GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices()); | ||||
FastRandomContext insecure_rand; | FastRandomContext insecure_rand; | ||||
if (addr.IsRoutable()) { | if (addr.IsRoutable()) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"ProcessMessages: advertising address %s\n", | "ProcessMessages: advertising address %s\n", | ||||
addr.ToString()); | addr.ToString()); | ||||
pfrom->PushAddress(addr, insecure_rand); | pfrom.PushAddress(addr, insecure_rand); | ||||
} else if (IsPeerAddrLocalGood(pfrom)) { | } else if (IsPeerAddrLocalGood(&pfrom)) { | ||||
addr.SetIP(addrMe); | addr.SetIP(addrMe); | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"ProcessMessages: advertising address %s\n", | "ProcessMessages: advertising address %s\n", | ||||
addr.ToString()); | addr.ToString()); | ||||
pfrom->PushAddress(addr, insecure_rand); | pfrom.PushAddress(addr, insecure_rand); | ||||
} | } | ||||
} | } | ||||
// Get recent addresses | // Get recent addresses | ||||
if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || | if (pfrom.fOneShot || pfrom.nVersion >= CADDR_TIME_VERSION || | ||||
connman->GetAddressCount() < 1000) { | connman->GetAddressCount() < 1000) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, | &pfrom, | ||||
CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); | CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); | ||||
pfrom->fGetAddr = true; | pfrom.fGetAddr = true; | ||||
} | } | ||||
connman->MarkAddressGood(pfrom->addr); | connman->MarkAddressGood(pfrom.addr); | ||||
} | } | ||||
std::string remoteAddr; | std::string remoteAddr; | ||||
if (fLogIPs) { | if (fLogIPs) { | ||||
remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); | remoteAddr = ", peeraddr=" + pfrom.addr.ToString(); | ||||
} | } | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"receive version message: [%s] %s: version %d, blocks=%d, " | "receive version message: [%s] %s: version %d, blocks=%d, " | ||||
"us=%s, peer=%d%s\n", | "us=%s, peer=%d%s\n", | ||||
pfrom->addr.ToString(), cleanSubVer, pfrom->nVersion, | pfrom.addr.ToString(), cleanSubVer, pfrom.nVersion, | ||||
pfrom->nStartingHeight, addrMe.ToString(), pfrom->GetId(), | pfrom.nStartingHeight, addrMe.ToString(), pfrom.GetId(), | ||||
remoteAddr); | remoteAddr); | ||||
// Ignore time offsets that are improbable (before the Genesis block) | // Ignore time offsets that are improbable (before the Genesis block) | ||||
// and may underflow the nTimeOffset calculation. | // and may underflow the nTimeOffset calculation. | ||||
int64_t currentTime = GetTime(); | int64_t currentTime = GetTime(); | ||||
if (nTime >= int64_t(chainparams.GenesisBlock().nTime)) { | if (nTime >= int64_t(chainparams.GenesisBlock().nTime)) { | ||||
int64_t nTimeOffset = nTime - currentTime; | int64_t nTimeOffset = nTime - currentTime; | ||||
pfrom->nTimeOffset = nTimeOffset; | pfrom.nTimeOffset = nTimeOffset; | ||||
AddTimeData(pfrom->addr, nTimeOffset); | AddTimeData(pfrom.addr, nTimeOffset); | ||||
} else { | } else { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, | Misbehaving(pfrom, 20, | ||||
"Ignoring invalid timestamp in version message"); | "Ignoring invalid timestamp in version message"); | ||||
} | } | ||||
// Feeler connections exist only to verify if address is online. | // Feeler connections exist only to verify if address is online. | ||||
if (pfrom->fFeeler) { | if (pfrom.fFeeler) { | ||||
assert(pfrom->fInbound == false); | assert(pfrom.fInbound == false); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (pfrom->nVersion == 0) { | if (pfrom.nVersion == 0) { | ||||
// Must have a version message before anything else | // Must have a version message before anything else | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 10, "missing-version"); | Misbehaving(pfrom, 10, "missing-version"); | ||||
return false; | return false; | ||||
} | } | ||||
// At this point, the outgoing message serialization version can't change. | // At this point, the outgoing message serialization version can't change. | ||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); | const CNetMsgMaker msgMaker(pfrom.GetSendVersion()); | ||||
if (msg_type == NetMsgType::VERACK) { | if (msg_type == NetMsgType::VERACK) { | ||||
pfrom->SetRecvVersion( | pfrom.SetRecvVersion(std::min(pfrom.nVersion.load(), PROTOCOL_VERSION)); | ||||
std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); | |||||
if (!pfrom->fInbound) { | if (!pfrom.fInbound) { | ||||
// Mark this node as currently connected, so we update its timestamp | // Mark this node as currently connected, so we update its timestamp | ||||
// later. | // later. | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
State(pfrom->GetId())->fCurrentlyConnected = true; | State(pfrom.GetId())->fCurrentlyConnected = true; | ||||
LogPrintf( | LogPrintf( | ||||
"New outbound peer connected: version: %d, blocks=%d, " | "New outbound peer connected: version: %d, blocks=%d, " | ||||
"peer=%d%s (%s)\n", | "peer=%d%s (%s)\n", | ||||
pfrom->nVersion.load(), pfrom->nStartingHeight, pfrom->GetId(), | pfrom.nVersion.load(), pfrom.nStartingHeight, pfrom.GetId(), | ||||
(fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) | (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) | ||||
: ""), | : ""), | ||||
pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay"); | pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay"); | ||||
} | } | ||||
if (pfrom->nVersion >= SENDHEADERS_VERSION) { | if (pfrom.nVersion >= SENDHEADERS_VERSION) { | ||||
// Tell our peer we prefer to receive headers rather than inv's | // Tell our peer we prefer to receive headers rather than inv's | ||||
// We send this to non-NODE NETWORK peers as well, because even | // We send this to non-NODE NETWORK peers as well, because even | ||||
// non-NODE NETWORK peers can announce blocks (such as pruning | // non-NODE NETWORK peers can announce blocks (such as pruning | ||||
// nodes) | // nodes) | ||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); | connman->PushMessage(&pfrom, | ||||
msgMaker.Make(NetMsgType::SENDHEADERS)); | |||||
} | } | ||||
if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { | if (pfrom.nVersion >= SHORT_IDS_BLOCKS_VERSION) { | ||||
// Tell our peer we are willing to provide version 1 or 2 | // Tell our peer we are willing to provide version 1 or 2 | ||||
// cmpctblocks. However, we do not request new block announcements | // cmpctblocks. However, we do not request new block announcements | ||||
// using cmpctblock messages. We send this to non-NODE NETWORK peers | // using cmpctblock messages. We send this to non-NODE NETWORK peers | ||||
// as well, because they may wish to request compact blocks from us. | // as well, because they may wish to request compact blocks from us. | ||||
bool fAnnounceUsingCMPCTBLOCK = false; | bool fAnnounceUsingCMPCTBLOCK = false; | ||||
uint64_t nCMPCTBLOCKVersion = 1; | uint64_t nCMPCTBLOCKVersion = 1; | ||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, | connman->PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, | ||||
fAnnounceUsingCMPCTBLOCK, | fAnnounceUsingCMPCTBLOCK, | ||||
nCMPCTBLOCKVersion)); | nCMPCTBLOCKVersion)); | ||||
} | } | ||||
pfrom->fSuccessfullyConnected = true; | pfrom.fSuccessfullyConnected = true; | ||||
return true; | return true; | ||||
} | } | ||||
if (!pfrom->fSuccessfullyConnected) { | if (!pfrom.fSuccessfullyConnected) { | ||||
// Must have a verack message before anything else | // Must have a verack message before anything else | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 10, "missing-verack"); | Misbehaving(pfrom, 10, "missing-verack"); | ||||
return false; | return false; | ||||
} | } | ||||
if (msg_type == NetMsgType::ADDR) { | if (msg_type == NetMsgType::ADDR) { | ||||
std::vector<CAddress> vAddr; | std::vector<CAddress> vAddr; | ||||
vRecv >> vAddr; | vRecv >> vAddr; | ||||
// Don't want addr from older versions unless seeding | // Don't want addr from older versions unless seeding | ||||
if (pfrom->nVersion < CADDR_TIME_VERSION && | if (pfrom.nVersion < CADDR_TIME_VERSION && | ||||
connman->GetAddressCount() > 1000) { | connman->GetAddressCount() > 1000) { | ||||
return true; | return true; | ||||
} | } | ||||
if (!pfrom->IsAddrRelayPeer()) { | if (!pfrom.IsAddrRelayPeer()) { | ||||
return true; | return true; | ||||
} | } | ||||
if (vAddr.size() > 1000) { | if (vAddr.size() > 1000) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, "oversized-addr"); | Misbehaving(pfrom, 20, "oversized-addr"); | ||||
return error("message addr size() = %u", vAddr.size()); | return error("message addr size() = %u", vAddr.size()); | ||||
} | } | ||||
Show All 12 Lines | if (msg_type == NetMsgType::ADDR) { | ||||
if (!MayHaveUsefulAddressDB(addr.nServices) && | if (!MayHaveUsefulAddressDB(addr.nServices) && | ||||
!HasAllDesirableServiceFlags(addr.nServices)) { | !HasAllDesirableServiceFlags(addr.nServices)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) { | if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) { | ||||
addr.nTime = nNow - 5 * 24 * 60 * 60; | addr.nTime = nNow - 5 * 24 * 60 * 60; | ||||
} | } | ||||
pfrom->AddAddressKnown(addr); | pfrom.AddAddressKnown(addr); | ||||
// Do not process banned/discouraged addresses beyond remembering we | // Do not process banned/discouraged addresses beyond remembering we | ||||
// received them | // received them | ||||
if (banman->IsDiscouraged(addr)) { | if (banman->IsDiscouraged(addr)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (banman->IsBanned(addr)) { | if (banman->IsBanned(addr)) { | ||||
continue; | continue; | ||||
} | } | ||||
bool fReachable = IsReachable(addr); | bool fReachable = IsReachable(addr); | ||||
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && | if (addr.nTime > nSince && !pfrom.fGetAddr && vAddr.size() <= 10 && | ||||
addr.IsRoutable()) { | addr.IsRoutable()) { | ||||
// Relay to a limited number of other nodes | // Relay to a limited number of other nodes | ||||
RelayAddress(addr, fReachable, connman); | RelayAddress(addr, fReachable, connman); | ||||
} | } | ||||
// Do not store addresses outside our network | // Do not store addresses outside our network | ||||
if (fReachable) { | if (fReachable) { | ||||
vAddrOk.push_back(addr); | vAddrOk.push_back(addr); | ||||
} | } | ||||
} | } | ||||
connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); | connman->AddNewAddresses(vAddrOk, pfrom.addr, 2 * 60 * 60); | ||||
if (vAddr.size() < 1000) { | if (vAddr.size() < 1000) { | ||||
pfrom->fGetAddr = false; | pfrom.fGetAddr = false; | ||||
} | } | ||||
if (pfrom->fOneShot) { | if (pfrom.fOneShot) { | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::SENDHEADERS) { | if (msg_type == NetMsgType::SENDHEADERS) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
State(pfrom->GetId())->fPreferHeaders = true; | State(pfrom.GetId())->fPreferHeaders = true; | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::SENDCMPCT) { | if (msg_type == NetMsgType::SENDCMPCT) { | ||||
bool fAnnounceUsingCMPCTBLOCK = false; | bool fAnnounceUsingCMPCTBLOCK = false; | ||||
uint64_t nCMPCTBLOCKVersion = 0; | uint64_t nCMPCTBLOCKVersion = 0; | ||||
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; | vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; | ||||
if (nCMPCTBLOCKVersion == 1) { | if (nCMPCTBLOCKVersion == 1) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// fProvidesHeaderAndIDs is used to "lock in" version of compact | // fProvidesHeaderAndIDs is used to "lock in" version of compact | ||||
// blocks we send. | // blocks we send. | ||||
if (!State(pfrom->GetId())->fProvidesHeaderAndIDs) { | if (!State(pfrom.GetId())->fProvidesHeaderAndIDs) { | ||||
State(pfrom->GetId())->fProvidesHeaderAndIDs = true; | State(pfrom.GetId())->fProvidesHeaderAndIDs = true; | ||||
} | } | ||||
State(pfrom->GetId())->fPreferHeaderAndIDs = | State(pfrom.GetId())->fPreferHeaderAndIDs = | ||||
fAnnounceUsingCMPCTBLOCK; | fAnnounceUsingCMPCTBLOCK; | ||||
if (!State(pfrom->GetId())->fSupportsDesiredCmpctVersion) { | if (!State(pfrom.GetId())->fSupportsDesiredCmpctVersion) { | ||||
State(pfrom->GetId())->fSupportsDesiredCmpctVersion = true; | State(pfrom.GetId())->fSupportsDesiredCmpctVersion = true; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::INV) { | if (msg_type == NetMsgType::INV) { | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
vRecv >> vInv; | vRecv >> vInv; | ||||
if (vInv.size() > MAX_INV_SZ) { | if (vInv.size() > MAX_INV_SZ) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, "oversized-inv"); | Misbehaving(pfrom, 20, "oversized-inv"); | ||||
return error("message inv size() = %u", vInv.size()); | return error("message inv size() = %u", vInv.size()); | ||||
} | } | ||||
// We won't accept tx inv's if we're in blocks-only mode, or this is a | // We won't accept tx inv's if we're in blocks-only mode, or this is a | ||||
// block-relay-only peer | // block-relay-only peer | ||||
bool fBlocksOnly = !g_relay_txes || (pfrom->m_tx_relay == nullptr); | bool fBlocksOnly = !g_relay_txes || (pfrom.m_tx_relay == nullptr); | ||||
// Allow whitelisted peers to send data other than blocks in blocks only | // Allow whitelisted peers to send data other than blocks in blocks only | ||||
// mode if whitelistrelay is true | // mode if whitelistrelay is true | ||||
if (pfrom->HasPermission(PF_RELAY)) { | if (pfrom.HasPermission(PF_RELAY)) { | ||||
fBlocksOnly = false; | fBlocksOnly = false; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const auto current_time = GetTime<std::chrono::microseconds>(); | const auto current_time = GetTime<std::chrono::microseconds>(); | ||||
for (CInv &inv : vInv) { | for (CInv &inv : vInv) { | ||||
if (interruptMsgProc) { | if (interruptMsgProc) { | ||||
return true; | return true; | ||||
} | } | ||||
bool fAlreadyHave = AlreadyHave(inv); | bool fAlreadyHave = AlreadyHave(inv); | ||||
LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), | LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), | ||||
fAlreadyHave ? "have" : "new", pfrom->GetId()); | fAlreadyHave ? "have" : "new", pfrom.GetId()); | ||||
if (inv.type == MSG_BLOCK) { | if (inv.type == MSG_BLOCK) { | ||||
const BlockHash hash(inv.hash); | const BlockHash hash(inv.hash); | ||||
UpdateBlockAvailability(pfrom->GetId(), hash); | UpdateBlockAvailability(pfrom.GetId(), hash); | ||||
if (!fAlreadyHave && !fImporting && !fReindex && | if (!fAlreadyHave && !fImporting && !fReindex && | ||||
!mapBlocksInFlight.count(hash)) { | !mapBlocksInFlight.count(hash)) { | ||||
// We used to request the full block here, but since | // We used to request the full block here, but since | ||||
// headers-announcements are now the primary method of | // headers-announcements are now the primary method of | ||||
// announcement on the network, and since, in the case that | // announcement on the network, and since, in the case that | ||||
// a node fell back to inv we probably have a reorg which we | // a node fell back to inv we probably have a reorg which we | ||||
// should get the headers for first, we now only provide a | // should get the headers for first, we now only provide a | ||||
// getheaders response here. When we receive the headers, we | // getheaders response here. When we receive the headers, we | ||||
// will then ask for the blocks we need. | // will then ask for the blocks we need. | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make( | &pfrom, msgMaker.Make(NetMsgType::GETHEADERS, | ||||
NetMsgType::GETHEADERS, | ::ChainActive().GetLocator( | ||||
::ChainActive().GetLocator(pindexBestHeader), | pindexBestHeader), | ||||
hash)); | hash)); | ||||
LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", | LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", | ||||
pindexBestHeader->nHeight, hash.ToString(), | pindexBestHeader->nHeight, hash.ToString(), | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
} | } | ||||
} else { | } else { | ||||
pfrom->AddInventoryKnown(inv); | pfrom.AddInventoryKnown(inv); | ||||
if (fBlocksOnly) { | if (fBlocksOnly) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"transaction (%s) inv sent in violation of " | "transaction (%s) inv sent in violation of " | ||||
"protocol, disconnecting peer=%d\n", | "protocol, disconnecting peer=%d\n", | ||||
inv.hash.ToString(), pfrom->GetId()); | inv.hash.ToString(), pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return true; | return true; | ||||
} else if (!fAlreadyHave && !fImporting && !fReindex && | } else if (!fAlreadyHave && !fImporting && !fReindex && | ||||
!::ChainstateActive().IsInitialBlockDownload()) { | !::ChainstateActive().IsInitialBlockDownload()) { | ||||
RequestTx(State(pfrom->GetId()), TxId(inv.hash), | RequestTx(State(pfrom.GetId()), TxId(inv.hash), | ||||
current_time); | current_time); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETDATA) { | if (msg_type == NetMsgType::GETDATA) { | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
vRecv >> vInv; | vRecv >> vInv; | ||||
if (vInv.size() > MAX_INV_SZ) { | if (vInv.size() > MAX_INV_SZ) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, "too-many-inv"); | Misbehaving(pfrom, 20, "too-many-inv"); | ||||
return error("message getdata size() = %u", vInv.size()); | return error("message getdata size() = %u", vInv.size()); | ||||
} | } | ||||
LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", | LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", | ||||
vInv.size(), pfrom->GetId()); | vInv.size(), pfrom.GetId()); | ||||
if (vInv.size() > 0) { | if (vInv.size() > 0) { | ||||
LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", | LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", | ||||
vInv[0].ToString(), pfrom->GetId()); | vInv[0].ToString(), pfrom.GetId()); | ||||
} | } | ||||
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), | pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), | ||||
vInv.end()); | vInv.end()); | ||||
ProcessGetData(config, pfrom, connman, interruptMsgProc); | ProcessGetData(config, pfrom, connman, interruptMsgProc); | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETBLOCKS) { | if (msg_type == NetMsgType::GETBLOCKS) { | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
uint256 hashStop; | uint256 hashStop; | ||||
vRecv >> locator >> hashStop; | vRecv >> locator >> hashStop; | ||||
if (locator.vHave.size() > MAX_LOCATOR_SZ) { | if (locator.vHave.size() > MAX_LOCATOR_SZ) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"getblocks locator size %lld > %d, disconnect peer=%d\n", | "getblocks locator size %lld > %d, disconnect peer=%d\n", | ||||
locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId()); | locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return true; | return true; | ||||
} | } | ||||
// We might have announced the currently-being-connected tip using a | // We might have announced the currently-being-connected tip using a | ||||
// compact block, which resulted in the peer sending a getblocks | // compact block, which resulted in the peer sending a getblocks | ||||
// request, which we would otherwise respond to without the new block. | // request, which we would otherwise respond to without the new block. | ||||
// To avoid this situation we simply verify that we are on our best | // To avoid this situation we simply verify that we are on our best | ||||
// known chain now. This is super overkill, but we handle it better | // known chain now. This is super overkill, but we handle it better | ||||
Show All 21 Lines | if (msg_type == NetMsgType::GETBLOCKS) { | ||||
// Send the rest of the chain | // Send the rest of the chain | ||||
if (pindex) { | if (pindex) { | ||||
pindex = ::ChainActive().Next(pindex); | pindex = ::ChainActive().Next(pindex); | ||||
} | } | ||||
int nLimit = 500; | int nLimit = 500; | ||||
LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", | LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", | ||||
(pindex ? pindex->nHeight : -1), | (pindex ? pindex->nHeight : -1), | ||||
hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, | hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
for (; pindex; pindex = ::ChainActive().Next(pindex)) { | for (; pindex; pindex = ::ChainActive().Next(pindex)) { | ||||
if (pindex->GetBlockHash() == hashStop) { | if (pindex->GetBlockHash() == hashStop) { | ||||
LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", | LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
break; | break; | ||||
} | } | ||||
// If pruning, don't inv blocks unless we have on disk and are | // If pruning, don't inv blocks unless we have on disk and are | ||||
// likely to still have for some reasonable time window (1 hour) | // likely to still have for some reasonable time window (1 hour) | ||||
// that block relay might require. | // that block relay might require. | ||||
const int nPrunedBlocksLikelyToHave = | const int nPrunedBlocksLikelyToHave = | ||||
MIN_BLOCKS_TO_KEEP - | MIN_BLOCKS_TO_KEEP - | ||||
3600 / chainparams.GetConsensus().nPowTargetSpacing; | 3600 / chainparams.GetConsensus().nPowTargetSpacing; | ||||
if (fPruneMode && | if (fPruneMode && | ||||
(!pindex->nStatus.hasData() || | (!pindex->nStatus.hasData() || | ||||
pindex->nHeight <= ::ChainActive().Tip()->nHeight - | pindex->nHeight <= ::ChainActive().Tip()->nHeight - | ||||
nPrunedBlocksLikelyToHave)) { | nPrunedBlocksLikelyToHave)) { | ||||
LogPrint( | LogPrint( | ||||
BCLog::NET, | BCLog::NET, | ||||
" getblocks stopping, pruned or too old block at %d %s\n", | " getblocks stopping, pruned or too old block at %d %s\n", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
break; | break; | ||||
} | } | ||||
pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); | pfrom.PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); | ||||
if (--nLimit <= 0) { | if (--nLimit <= 0) { | ||||
// When this block is requested, we'll send an inv that'll | // When this block is requested, we'll send an inv that'll | ||||
// trigger the peer to getblocks the next batch of inventory. | // trigger the peer to getblocks the next batch of inventory. | ||||
LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", | LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
pfrom->hashContinue = pindex->GetBlockHash(); | pfrom.hashContinue = pindex->GetBlockHash(); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETBLOCKTXN) { | if (msg_type == NetMsgType::GETBLOCKTXN) { | ||||
BlockTransactionsRequest req; | BlockTransactionsRequest req; | ||||
Show All 14 Lines | if (msg_type == NetMsgType::GETBLOCKTXN) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CBlockIndex *pindex = LookupBlockIndex(req.blockhash); | const CBlockIndex *pindex = LookupBlockIndex(req.blockhash); | ||||
if (!pindex || !pindex->nStatus.hasData()) { | if (!pindex || !pindex->nStatus.hasData()) { | ||||
LogPrint( | LogPrint( | ||||
BCLog::NET, | BCLog::NET, | ||||
"Peer %d sent us a getblocktxn for a block we don't have\n", | "Peer %d sent us a getblocktxn for a block we don't have\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) { | if (pindex->nHeight < ::ChainActive().Height() - MAX_BLOCKTXN_DEPTH) { | ||||
// If an older block is requested (should never happen in practice, | // If an older block is requested (should never happen in practice, | ||||
// but can happen in tests) send a block response instead of a | // but can happen in tests) send a block response instead of a | ||||
// blocktxn response. Sending a full block response instead of a | // blocktxn response. Sending a full block response instead of a | ||||
// small blocktxn response is preferable in the case where a peer | // small blocktxn response is preferable in the case where a peer | ||||
// might maliciously send lots of getblocktxn requests to trigger | // might maliciously send lots of getblocktxn requests to trigger | ||||
// expensive disk reads, because it will require the peer to | // expensive disk reads, because it will require the peer to | ||||
// actually receive all the data read from disk over the network. | // actually receive all the data read from disk over the network. | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Peer %d sent us a getblocktxn for a block > %i deep\n", | "Peer %d sent us a getblocktxn for a block > %i deep\n", | ||||
pfrom->GetId(), MAX_BLOCKTXN_DEPTH); | pfrom.GetId(), MAX_BLOCKTXN_DEPTH); | ||||
CInv inv; | CInv inv; | ||||
inv.type = MSG_BLOCK; | inv.type = MSG_BLOCK; | ||||
inv.hash = req.blockhash; | inv.hash = req.blockhash; | ||||
pfrom->vRecvGetData.push_back(inv); | pfrom.vRecvGetData.push_back(inv); | ||||
// The message processing loop will go around again (without | // The message processing loop will go around again (without | ||||
// pausing) and we'll respond then (without cs_main) | // pausing) and we'll respond then (without cs_main) | ||||
return true; | return true; | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()); | bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()); | ||||
assert(ret); | assert(ret); | ||||
SendBlockTransactions(block, req, pfrom, connman); | SendBlockTransactions(block, req, pfrom, connman); | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETHEADERS) { | if (msg_type == NetMsgType::GETHEADERS) { | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
BlockHash hashStop; | BlockHash hashStop; | ||||
vRecv >> locator >> hashStop; | vRecv >> locator >> hashStop; | ||||
if (locator.vHave.size() > MAX_LOCATOR_SZ) { | if (locator.vHave.size() > MAX_LOCATOR_SZ) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"getheaders locator size %lld > %d, disconnect peer=%d\n", | "getheaders locator size %lld > %d, disconnect peer=%d\n", | ||||
locator.vHave.size(), MAX_LOCATOR_SZ, pfrom->GetId()); | locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return true; | return true; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (::ChainstateActive().IsInitialBlockDownload() && | if (::ChainstateActive().IsInitialBlockDownload() && | ||||
!pfrom->HasPermission(PF_NOBAN)) { | !pfrom.HasPermission(PF_NOBAN)) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Ignoring getheaders from peer=%d because node is in " | "Ignoring getheaders from peer=%d because node is in " | ||||
"initial block download\n", | "initial block download\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
CNodeState *nodestate = State(pfrom->GetId()); | CNodeState *nodestate = State(pfrom.GetId()); | ||||
const CBlockIndex *pindex = nullptr; | const CBlockIndex *pindex = nullptr; | ||||
if (locator.IsNull()) { | if (locator.IsNull()) { | ||||
// If locator is null, return the hashStop block | // If locator is null, return the hashStop block | ||||
pindex = LookupBlockIndex(hashStop); | pindex = LookupBlockIndex(hashStop); | ||||
if (!pindex) { | if (!pindex) { | ||||
return true; | return true; | ||||
} | } | ||||
if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) { | if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"%s: ignoring request from peer=%i for old block " | "%s: ignoring request from peer=%i for old block " | ||||
"header that isn't in the main chain\n", | "header that isn't in the main chain\n", | ||||
__func__, pfrom->GetId()); | __func__, pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
} else { | } else { | ||||
// Find the last block the caller has in the main chain | // Find the last block the caller has in the main chain | ||||
pindex = FindForkInGlobalIndex(::ChainActive(), locator); | pindex = FindForkInGlobalIndex(::ChainActive(), locator); | ||||
if (pindex) { | if (pindex) { | ||||
pindex = ::ChainActive().Next(pindex); | pindex = ::ChainActive().Next(pindex); | ||||
} | } | ||||
} | } | ||||
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx | // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx | ||||
// count at the end | // count at the end | ||||
std::vector<CBlock> vHeaders; | std::vector<CBlock> vHeaders; | ||||
int nLimit = MAX_HEADERS_RESULTS; | int nLimit = MAX_HEADERS_RESULTS; | ||||
LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", | LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", | ||||
(pindex ? pindex->nHeight : -1), | (pindex ? pindex->nHeight : -1), | ||||
hashStop.IsNull() ? "end" : hashStop.ToString(), | hashStop.IsNull() ? "end" : hashStop.ToString(), | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
for (; pindex; pindex = ::ChainActive().Next(pindex)) { | for (; pindex; pindex = ::ChainActive().Next(pindex)) { | ||||
vHeaders.push_back(pindex->GetBlockHeader()); | vHeaders.push_back(pindex->GetBlockHeader()); | ||||
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) { | if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// pindex can be nullptr either if we sent ::ChainActive().Tip() OR | // pindex can be nullptr either if we sent ::ChainActive().Tip() OR | ||||
// if our peer has ::ChainActive().Tip() (and thus we are sending an | // if our peer has ::ChainActive().Tip() (and thus we are sending an | ||||
// empty headers message). In both cases it's safe to update | // empty headers message). In both cases it's safe to update | ||||
// pindexBestHeaderSent to be our tip. | // pindexBestHeaderSent to be our tip. | ||||
// | // | ||||
// It is important that we simply reset the BestHeaderSent value here, | // It is important that we simply reset the BestHeaderSent value here, | ||||
// and not max(BestHeaderSent, newHeaderSent). We might have announced | // and not max(BestHeaderSent, newHeaderSent). We might have announced | ||||
// the currently-being-connected tip using a compact block, which | // the currently-being-connected tip using a compact block, which | ||||
// resulted in the peer sending a headers request, which we respond to | // resulted in the peer sending a headers request, which we respond to | ||||
// without the new block. By resetting the BestHeaderSent, we ensure we | // without the new block. By resetting the BestHeaderSent, we ensure we | ||||
// will re-announce the new block via headers (or compact blocks again) | // will re-announce the new block via headers (or compact blocks again) | ||||
// in the SendMessages logic. | // in the SendMessages logic. | ||||
nodestate->pindexBestHeaderSent = | nodestate->pindexBestHeaderSent = | ||||
pindex ? pindex : ::ChainActive().Tip(); | pindex ? pindex : ::ChainActive().Tip(); | ||||
connman->PushMessage(pfrom, | connman->PushMessage(&pfrom, | ||||
msgMaker.Make(NetMsgType::HEADERS, vHeaders)); | msgMaker.Make(NetMsgType::HEADERS, vHeaders)); | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::TX) { | if (msg_type == NetMsgType::TX) { | ||||
// Stop processing the transaction early if | // Stop processing the transaction early if | ||||
// We are in blocks only mode and peer is either not whitelisted or | // We are in blocks only mode and peer is either not whitelisted or | ||||
// whitelistrelay is off or if this peer is supposed to be a | // whitelistrelay is off or if this peer is supposed to be a | ||||
// block-relay-only peer | // block-relay-only peer | ||||
if ((!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) || | if ((!g_relay_txes && !pfrom.HasPermission(PF_RELAY)) || | ||||
(pfrom->m_tx_relay == nullptr)) { | (pfrom.m_tx_relay == nullptr)) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"transaction sent in violation of protocol peer=%d\n", | "transaction sent in violation of protocol peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
return true; | return true; | ||||
} | } | ||||
CTransactionRef ptx; | CTransactionRef ptx; | ||||
vRecv >> ptx; | vRecv >> ptx; | ||||
const CTransaction &tx = *ptx; | const CTransaction &tx = *ptx; | ||||
const TxId &txid = tx.GetId(); | const TxId &txid = tx.GetId(); | ||||
CInv inv(MSG_TX, txid); | CInv inv(MSG_TX, txid); | ||||
pfrom->AddInventoryKnown(inv); | pfrom.AddInventoryKnown(inv); | ||||
LOCK2(cs_main, g_cs_orphans); | LOCK2(cs_main, g_cs_orphans); | ||||
TxValidationState state; | TxValidationState state; | ||||
CNodeState *nodestate = State(pfrom->GetId()); | CNodeState *nodestate = State(pfrom.GetId()); | ||||
nodestate->m_tx_download.m_tx_announced.erase(txid); | nodestate->m_tx_download.m_tx_announced.erase(txid); | ||||
nodestate->m_tx_download.m_tx_in_flight.erase(txid); | nodestate->m_tx_download.m_tx_in_flight.erase(txid); | ||||
EraseTxRequest(txid); | EraseTxRequest(txid); | ||||
if (!AlreadyHave(inv) && | if (!AlreadyHave(inv) && | ||||
AcceptToMemoryPool(config, g_mempool, state, ptx, | AcceptToMemoryPool(config, g_mempool, state, ptx, | ||||
false /* bypass_limits */, | false /* bypass_limits */, | ||||
Amount::zero() /* nAbsurdFee */)) { | Amount::zero() /* nAbsurdFee */)) { | ||||
g_mempool.check(&::ChainstateActive().CoinsTip()); | g_mempool.check(&::ChainstateActive().CoinsTip()); | ||||
RelayTransaction(tx.GetId(), *connman); | RelayTransaction(tx.GetId(), *connman); | ||||
for (size_t i = 0; i < tx.vout.size(); i++) { | for (size_t i = 0; i < tx.vout.size(); i++) { | ||||
auto it_by_prev = | auto it_by_prev = | ||||
mapOrphanTransactionsByPrev.find(COutPoint(txid, i)); | mapOrphanTransactionsByPrev.find(COutPoint(txid, i)); | ||||
if (it_by_prev != mapOrphanTransactionsByPrev.end()) { | if (it_by_prev != mapOrphanTransactionsByPrev.end()) { | ||||
for (const auto &elem : it_by_prev->second) { | for (const auto &elem : it_by_prev->second) { | ||||
pfrom->orphan_work_set.insert(elem->first); | pfrom.orphan_work_set.insert(elem->first); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
pfrom->nLastTXTime = GetTime(); | pfrom.nLastTXTime = GetTime(); | ||||
LogPrint(BCLog::MEMPOOL, | LogPrint(BCLog::MEMPOOL, | ||||
"AcceptToMemoryPool: peer=%d: accepted %s " | "AcceptToMemoryPool: peer=%d: accepted %s " | ||||
"(poolsz %u txn, %u kB)\n", | "(poolsz %u txn, %u kB)\n", | ||||
pfrom->GetId(), tx.GetId().ToString(), g_mempool.size(), | pfrom.GetId(), tx.GetId().ToString(), g_mempool.size(), | ||||
g_mempool.DynamicMemoryUsage() / 1000); | g_mempool.DynamicMemoryUsage() / 1000); | ||||
// Recursively process any orphan transactions that depended on this | // Recursively process any orphan transactions that depended on this | ||||
// one | // one | ||||
ProcessOrphanTx(config, connman, pfrom->orphan_work_set); | ProcessOrphanTx(config, connman, pfrom.orphan_work_set); | ||||
} else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { | } else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { | ||||
// It may be the case that the orphans parents have all been | // It may be the case that the orphans parents have all been | ||||
// rejected. | // rejected. | ||||
bool fRejectedParents = false; | bool fRejectedParents = false; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
if (recentRejects->contains(txin.prevout.GetTxId())) { | if (recentRejects->contains(txin.prevout.GetTxId())) { | ||||
fRejectedParents = true; | fRejectedParents = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (!fRejectedParents) { | if (!fRejectedParents) { | ||||
const auto current_time = GetTime<std::chrono::microseconds>(); | const auto current_time = GetTime<std::chrono::microseconds>(); | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
// FIXME: MSG_TX should use a TxHash, not a TxId. | // FIXME: MSG_TX should use a TxHash, not a TxId. | ||||
const TxId _txid = txin.prevout.GetTxId(); | const TxId _txid = txin.prevout.GetTxId(); | ||||
CInv _inv(MSG_TX, _txid); | CInv _inv(MSG_TX, _txid); | ||||
pfrom->AddInventoryKnown(_inv); | pfrom.AddInventoryKnown(_inv); | ||||
if (!AlreadyHave(_inv)) { | if (!AlreadyHave(_inv)) { | ||||
RequestTx(State(pfrom->GetId()), _txid, current_time); | RequestTx(State(pfrom.GetId()), _txid, current_time); | ||||
} | } | ||||
} | } | ||||
AddOrphanTx(ptx, pfrom->GetId()); | AddOrphanTx(ptx, pfrom.GetId()); | ||||
// DoS prevention: do not allow mapOrphanTransactions to grow | // DoS prevention: do not allow mapOrphanTransactions to grow | ||||
// unbounded (see CVE-2012-3789) | // unbounded (see CVE-2012-3789) | ||||
unsigned int nMaxOrphanTx = (unsigned int)std::max( | unsigned int nMaxOrphanTx = (unsigned int)std::max( | ||||
int64_t(0), gArgs.GetArg("-maxorphantx", | int64_t(0), gArgs.GetArg("-maxorphantx", | ||||
DEFAULT_MAX_ORPHAN_TRANSACTIONS)); | DEFAULT_MAX_ORPHAN_TRANSACTIONS)); | ||||
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); | unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); | ||||
if (nEvicted > 0) { | if (nEvicted > 0) { | ||||
Show All 11 Lines | if (msg_type == NetMsgType::TX) { | ||||
} else { | } else { | ||||
assert(recentRejects); | assert(recentRejects); | ||||
recentRejects->insert(tx.GetId()); | recentRejects->insert(tx.GetId()); | ||||
if (RecursiveDynamicUsage(*ptx) < 100000) { | if (RecursiveDynamicUsage(*ptx) < 100000) { | ||||
AddToCompactExtraTransactions(ptx); | AddToCompactExtraTransactions(ptx); | ||||
} | } | ||||
if (pfrom->HasPermission(PF_FORCERELAY)) { | if (pfrom.HasPermission(PF_FORCERELAY)) { | ||||
// Always relay transactions received from whitelisted peers, | // Always relay transactions received from whitelisted peers, | ||||
// even if they were already in the mempool or rejected from it | // even if they were already in the mempool or rejected from it | ||||
// due to policy, allowing the node to function as a gateway for | // due to policy, allowing the node to function as a gateway for | ||||
// nodes hidden behind it. | // nodes hidden behind it. | ||||
// | // | ||||
// Never relay transactions that might result in being | // Never relay transactions that might result in being | ||||
// disconnected (or banned). | // disconnected (or banned). | ||||
if (state.IsInvalid() && TxRelayMayResultInDisconnect(state)) { | if (state.IsInvalid() && TxRelayMayResultInDisconnect(state)) { | ||||
LogPrintf("Not relaying invalid transaction %s from " | LogPrintf("Not relaying invalid transaction %s from " | ||||
"whitelisted peer=%d (%s)\n", | "whitelisted peer=%d (%s)\n", | ||||
tx.GetId().ToString(), pfrom->GetId(), | tx.GetId().ToString(), pfrom.GetId(), | ||||
state.ToString()); | state.ToString()); | ||||
} else { | } else { | ||||
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", | LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", | ||||
tx.GetId().ToString(), pfrom->GetId()); | tx.GetId().ToString(), pfrom.GetId()); | ||||
RelayTransaction(tx.GetId(), *connman); | RelayTransaction(tx.GetId(), *connman); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// If a tx has been detected by recentRejects, we will have reached | // If a tx has been detected by recentRejects, we will have reached | ||||
// this point and the tx will have been ignored. Because we haven't run | // this point and the tx will have been ignored. Because we haven't run | ||||
// the tx through AcceptToMemoryPool, we won't have computed a DoS | // the tx through AcceptToMemoryPool, we won't have computed a DoS | ||||
Show All 9 Lines | if (msg_type == NetMsgType::TX) { | ||||
// transactions, but any tx not accepted by the mempool, which may be | // transactions, but any tx not accepted by the mempool, which may be | ||||
// due to node policy (vs. consensus). So we can't blanket penalize a | // due to node policy (vs. consensus). So we can't blanket penalize a | ||||
// peer simply for relaying a tx that our recentRejects has caught, | // peer simply for relaying a tx that our recentRejects has caught, | ||||
// regardless of false positives. | // regardless of false positives. | ||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
LogPrint(BCLog::MEMPOOLREJ, | LogPrint(BCLog::MEMPOOLREJ, | ||||
"%s from peer=%d was not accepted: %s\n", | "%s from peer=%d was not accepted: %s\n", | ||||
tx.GetHash().ToString(), pfrom->GetId(), state.ToString()); | tx.GetHash().ToString(), pfrom.GetId(), state.ToString()); | ||||
MaybePunishNodeForTx(pfrom->GetId(), state); | MaybePunishNodeForTx(pfrom.GetId(), state); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::CMPCTBLOCK) { | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
// Ignore cmpctblock received while importing | // Ignore cmpctblock received while importing | ||||
if (fImporting || fReindex) { | if (fImporting || fReindex) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Unexpected cmpctblock message received from peer %d\n", | "Unexpected cmpctblock message received from peer %d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
CBlockHeaderAndShortTxIDs cmpctblock; | CBlockHeaderAndShortTxIDs cmpctblock; | ||||
vRecv >> cmpctblock; | vRecv >> cmpctblock; | ||||
bool received_new_header = false; | bool received_new_header = false; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { | if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { | ||||
// Doesn't connect (or is genesis), instead of DoSing in | // Doesn't connect (or is genesis), instead of DoSing in | ||||
// AcceptBlockHeader, request deeper headers | // AcceptBlockHeader, request deeper headers | ||||
if (!::ChainstateActive().IsInitialBlockDownload()) { | if (!::ChainstateActive().IsInitialBlockDownload()) { | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make( | &pfrom, msgMaker.Make(NetMsgType::GETHEADERS, | ||||
NetMsgType::GETHEADERS, | ::ChainActive().GetLocator( | ||||
::ChainActive().GetLocator(pindexBestHeader), | pindexBestHeader), | ||||
uint256())); | uint256())); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (!LookupBlockIndex(cmpctblock.header.GetHash())) { | if (!LookupBlockIndex(cmpctblock.header.GetHash())) { | ||||
received_new_header = true; | received_new_header = true; | ||||
} | } | ||||
} | } | ||||
const CBlockIndex *pindex = nullptr; | const CBlockIndex *pindex = nullptr; | ||||
BlockValidationState state; | BlockValidationState state; | ||||
if (!ProcessNewBlockHeaders(config, {cmpctblock.header}, state, | if (!ProcessNewBlockHeaders(config, {cmpctblock.header}, state, | ||||
&pindex)) { | &pindex)) { | ||||
if (state.IsInvalid()) { | if (state.IsInvalid()) { | ||||
MaybePunishNodeForBlock(pfrom->GetId(), state, | MaybePunishNodeForBlock(pfrom.GetId(), state, | ||||
/*via_compact_block*/ true, | /*via_compact_block*/ true, | ||||
"invalid header via cmpctblock"); | "invalid header via cmpctblock"); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
// When we succeed in decoding a block's txids from a cmpctblock | // When we succeed in decoding a block's txids from a cmpctblock | ||||
// message we typically jump to the BLOCKTXN handling code, with a | // message we typically jump to the BLOCKTXN handling code, with a | ||||
Show All 11 Lines | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
// below) | // below) | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
bool fBlockReconstructed = false; | bool fBlockReconstructed = false; | ||||
{ | { | ||||
LOCK2(cs_main, g_cs_orphans); | LOCK2(cs_main, g_cs_orphans); | ||||
// If AcceptBlockHeader returned true, it set pindex | // If AcceptBlockHeader returned true, it set pindex | ||||
assert(pindex); | assert(pindex); | ||||
UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); | UpdateBlockAvailability(pfrom.GetId(), pindex->GetBlockHash()); | ||||
CNodeState *nodestate = State(pfrom->GetId()); | CNodeState *nodestate = State(pfrom.GetId()); | ||||
// If this was a new header with more work than our tip, update the | // If this was a new header with more work than our tip, update the | ||||
// peer's last block announcement time | // peer's last block announcement time | ||||
if (received_new_header && | if (received_new_header && | ||||
pindex->nChainWork > ::ChainActive().Tip()->nChainWork) { | pindex->nChainWork > ::ChainActive().Tip()->nChainWork) { | ||||
nodestate->m_last_block_announcement = GetTime(); | nodestate->m_last_block_announcement = GetTime(); | ||||
} | } | ||||
Show All 16 Lines | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
// We had this block at some point, but pruned it | // We had this block at some point, but pruned it | ||||
if (fAlreadyInFlight) { | if (fAlreadyInFlight) { | ||||
// We requested this block for some reason, but our mempool | // We requested this block for some reason, but our mempool | ||||
// will probably be useless so we just grab the block via | // will probably be useless so we just grab the block via | ||||
// normal getdata. | // normal getdata. | ||||
std::vector<CInv> vInv(1); | std::vector<CInv> vInv(1); | ||||
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); | vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); | &pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
// If we're not close to tip yet, give up and let parallel block | // If we're not close to tip yet, give up and let parallel block | ||||
// fetch work its magic. | // fetch work its magic. | ||||
if (!fAlreadyInFlight && | if (!fAlreadyInFlight && | ||||
!CanDirectFetch(chainparams.GetConsensus())) { | !CanDirectFetch(chainparams.GetConsensus())) { | ||||
return true; | return true; | ||||
} | } | ||||
// We want to be a bit conservative just to be extra careful about | // We want to be a bit conservative just to be extra careful about | ||||
// DoS possibilities in compact block processing... | // DoS possibilities in compact block processing... | ||||
if (pindex->nHeight <= ::ChainActive().Height() + 2) { | if (pindex->nHeight <= ::ChainActive().Height() + 2) { | ||||
if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < | if ((!fAlreadyInFlight && nodestate->nBlocksInFlight < | ||||
MAX_BLOCKS_IN_TRANSIT_PER_PEER) || | MAX_BLOCKS_IN_TRANSIT_PER_PEER) || | ||||
(fAlreadyInFlight && | (fAlreadyInFlight && | ||||
blockInFlightIt->second.first == pfrom->GetId())) { | blockInFlightIt->second.first == pfrom.GetId())) { | ||||
std::list<QueuedBlock>::iterator *queuedBlockIt = nullptr; | std::list<QueuedBlock>::iterator *queuedBlockIt = nullptr; | ||||
if (!MarkBlockAsInFlight(config, pfrom->GetId(), | if (!MarkBlockAsInFlight(config, pfrom.GetId(), | ||||
pindex->GetBlockHash(), | pindex->GetBlockHash(), | ||||
chainparams.GetConsensus(), pindex, | chainparams.GetConsensus(), pindex, | ||||
&queuedBlockIt)) { | &queuedBlockIt)) { | ||||
if (!(*queuedBlockIt)->partialBlock) { | if (!(*queuedBlockIt)->partialBlock) { | ||||
(*queuedBlockIt) | (*queuedBlockIt) | ||||
->partialBlock.reset( | ->partialBlock.reset( | ||||
new PartiallyDownloadedBlock(config, | new PartiallyDownloadedBlock(config, | ||||
&g_mempool)); | &g_mempool)); | ||||
Show All 10 Lines | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
*(*queuedBlockIt)->partialBlock; | *(*queuedBlockIt)->partialBlock; | ||||
ReadStatus status = | ReadStatus status = | ||||
partialBlock.InitData(cmpctblock, vExtraTxnForCompact); | partialBlock.InitData(cmpctblock, vExtraTxnForCompact); | ||||
if (status == READ_STATUS_INVALID) { | if (status == READ_STATUS_INVALID) { | ||||
// Reset in-flight state in case of whitelist | // Reset in-flight state in case of whitelist | ||||
MarkBlockAsReceived(pindex->GetBlockHash()); | MarkBlockAsReceived(pindex->GetBlockHash()); | ||||
Misbehaving(pfrom, 100, "invalid-cmpctblk"); | Misbehaving(pfrom, 100, "invalid-cmpctblk"); | ||||
LogPrintf("Peer %d sent us invalid compact block\n", | LogPrintf("Peer %d sent us invalid compact block\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} else if (status == READ_STATUS_FAILED) { | } else if (status == READ_STATUS_FAILED) { | ||||
// Duplicate txindices, the block is now in-flight, so | // Duplicate txindices, the block is now in-flight, so | ||||
// just request it. | // just request it. | ||||
std::vector<CInv> vInv(1); | std::vector<CInv> vInv(1); | ||||
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); | vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); | &pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); | ||||
return true; | return true; | ||||
} | } | ||||
BlockTransactionsRequest req; | BlockTransactionsRequest req; | ||||
for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { | for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) { | ||||
if (!partialBlock.IsTxAvailable(i)) { | if (!partialBlock.IsTxAvailable(i)) { | ||||
req.indices.push_back(i); | req.indices.push_back(i); | ||||
} | } | ||||
} | } | ||||
if (req.indices.empty()) { | if (req.indices.empty()) { | ||||
// Dirty hack to jump to BLOCKTXN code (TODO: move | // Dirty hack to jump to BLOCKTXN code (TODO: move | ||||
// message handling into their own functions) | // message handling into their own functions) | ||||
BlockTransactions txn; | BlockTransactions txn; | ||||
txn.blockhash = cmpctblock.header.GetHash(); | txn.blockhash = cmpctblock.header.GetHash(); | ||||
blockTxnMsg << txn; | blockTxnMsg << txn; | ||||
fProcessBLOCKTXN = true; | fProcessBLOCKTXN = true; | ||||
} else { | } else { | ||||
req.blockhash = pindex->GetBlockHash(); | req.blockhash = pindex->GetBlockHash(); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); | &pfrom, | ||||
msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); | |||||
} | } | ||||
} else { | } else { | ||||
// This block is either already in flight from a different | // This block is either already in flight from a different | ||||
// peer, or this peer has too many blocks outstanding to | // peer, or this peer has too many blocks outstanding to | ||||
// download from. Optimistically try to reconstruct anyway | // download from. Optimistically try to reconstruct anyway | ||||
// since we might be able to without any round trips. | // since we might be able to without any round trips. | ||||
PartiallyDownloadedBlock tempBlock(config, &g_mempool); | PartiallyDownloadedBlock tempBlock(config, &g_mempool); | ||||
ReadStatus status = | ReadStatus status = | ||||
Show All 11 Lines | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
} else { | } else { | ||||
if (fAlreadyInFlight) { | if (fAlreadyInFlight) { | ||||
// We requested this block, but its far into the future, so | // We requested this block, but its far into the future, so | ||||
// our mempool will probably be useless - request the block | // our mempool will probably be useless - request the block | ||||
// normally. | // normally. | ||||
std::vector<CInv> vInv(1); | std::vector<CInv> vInv(1); | ||||
vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); | vInv[0] = CInv(MSG_BLOCK, cmpctblock.header.GetHash()); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); | &pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); | ||||
return true; | return true; | ||||
} else { | } else { | ||||
// If this was an announce-cmpctblock, we want the same | // If this was an announce-cmpctblock, we want the same | ||||
// treatment as a header message. | // treatment as a header message. | ||||
fRevertToHeaderProcessing = true; | fRevertToHeaderProcessing = true; | ||||
} | } | ||||
} | } | ||||
} // cs_main | } // cs_main | ||||
Show All 16 Lines | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
} | } | ||||
if (fBlockReconstructed) { | if (fBlockReconstructed) { | ||||
// If we got here, we were able to optimistically reconstruct a | // If we got here, we were able to optimistically reconstruct a | ||||
// block that is in flight from some other peer. | // block that is in flight from some other peer. | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
mapBlockSource.emplace(pblock->GetHash(), | mapBlockSource.emplace(pblock->GetHash(), | ||||
std::make_pair(pfrom->GetId(), false)); | std::make_pair(pfrom.GetId(), false)); | ||||
} | } | ||||
bool fNewBlock = false; | bool fNewBlock = false; | ||||
// Setting fForceProcessing to true means that we bypass some of | // Setting fForceProcessing to true means that we bypass some of | ||||
// our anti-DoS protections in AcceptBlock, which filters | // our anti-DoS protections in AcceptBlock, which filters | ||||
// unrequested blocks that might be trying to waste our resources | // unrequested blocks that might be trying to waste our resources | ||||
// (eg disk space). Because we only try to reconstruct blocks when | // (eg disk space). Because we only try to reconstruct blocks when | ||||
// we're close to caught up (via the CanDirectFetch() requirement | // we're close to caught up (via the CanDirectFetch() requirement | ||||
// above, combined with the behavior of not requesting blocks until | // above, combined with the behavior of not requesting blocks until | ||||
// we have a chain with at least nMinimumChainWork), and we ignore | // we have a chain with at least nMinimumChainWork), and we ignore | ||||
// compact blocks with less work than our tip, it is safe to treat | // compact blocks with less work than our tip, it is safe to treat | ||||
// reconstructed compact blocks as having been requested. | // reconstructed compact blocks as having been requested. | ||||
ProcessNewBlock(config, pblock, /*fForceProcessing=*/true, | ProcessNewBlock(config, pblock, /*fForceProcessing=*/true, | ||||
&fNewBlock); | &fNewBlock); | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
pfrom->nLastBlockTime = GetTime(); | pfrom.nLastBlockTime = GetTime(); | ||||
} else { | } else { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
mapBlockSource.erase(pblock->GetHash()); | mapBlockSource.erase(pblock->GetHash()); | ||||
} | } | ||||
// hold cs_main for CBlockIndex::IsValid() | // hold cs_main for CBlockIndex::IsValid() | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS)) { | if (pindex->IsValid(BlockValidity::TRANSACTIONS)) { | ||||
// Clear download state for this block, which is in process from | // Clear download state for this block, which is in process from | ||||
// some other peer. We do this after calling. ProcessNewBlock so | // some other peer. We do this after calling. ProcessNewBlock so | ||||
// that a malleated cmpctblock announcement can't be used to | // that a malleated cmpctblock announcement can't be used to | ||||
// interfere with block relay. | // interfere with block relay. | ||||
MarkBlockAsReceived(pblock->GetHash()); | MarkBlockAsReceived(pblock->GetHash()); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::BLOCKTXN) { | if (msg_type == NetMsgType::BLOCKTXN) { | ||||
// Ignore blocktxn received while importing | // Ignore blocktxn received while importing | ||||
if (fImporting || fReindex) { | if (fImporting || fReindex) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Unexpected blocktxn message received from peer %d\n", | "Unexpected blocktxn message received from peer %d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
BlockTransactions resp; | BlockTransactions resp; | ||||
vRecv >> resp; | vRecv >> resp; | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
bool fBlockRead = false; | bool fBlockRead = false; | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
std::map<BlockHash, | std::map<BlockHash, | ||||
std::pair<NodeId, std::list<QueuedBlock>::iterator>>:: | std::pair<NodeId, std::list<QueuedBlock>::iterator>>:: | ||||
iterator it = mapBlocksInFlight.find(resp.blockhash); | iterator it = mapBlocksInFlight.find(resp.blockhash); | ||||
if (it == mapBlocksInFlight.end() || | if (it == mapBlocksInFlight.end() || | ||||
!it->second.second->partialBlock || | !it->second.second->partialBlock || | ||||
it->second.first != pfrom->GetId()) { | it->second.first != pfrom.GetId()) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Peer %d sent us block transactions for block " | "Peer %d sent us block transactions for block " | ||||
"we weren't expecting\n", | "we weren't expecting\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
PartiallyDownloadedBlock &partialBlock = | PartiallyDownloadedBlock &partialBlock = | ||||
*it->second.second->partialBlock; | *it->second.second->partialBlock; | ||||
ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); | ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); | ||||
if (status == READ_STATUS_INVALID) { | if (status == READ_STATUS_INVALID) { | ||||
// Reset in-flight state in case of whitelist. | // Reset in-flight state in case of whitelist. | ||||
MarkBlockAsReceived(resp.blockhash); | MarkBlockAsReceived(resp.blockhash); | ||||
Misbehaving(pfrom, 100, "invalid-cmpctblk-txns"); | Misbehaving(pfrom, 100, "invalid-cmpctblk-txns"); | ||||
LogPrintf("Peer %d sent us invalid compact block/non-matching " | LogPrintf("Peer %d sent us invalid compact block/non-matching " | ||||
"block transactions\n", | "block transactions\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} else if (status == READ_STATUS_FAILED) { | } else if (status == READ_STATUS_FAILED) { | ||||
// Might have collided, fall back to getdata now :( | // Might have collided, fall back to getdata now :( | ||||
std::vector<CInv> invs; | std::vector<CInv> invs; | ||||
invs.push_back(CInv(MSG_BLOCK, resp.blockhash)); | invs.push_back(CInv(MSG_BLOCK, resp.blockhash)); | ||||
connman->PushMessage(pfrom, | connman->PushMessage(&pfrom, | ||||
msgMaker.Make(NetMsgType::GETDATA, invs)); | msgMaker.Make(NetMsgType::GETDATA, invs)); | ||||
} else { | } else { | ||||
// Block is either okay, or possibly we received | // Block is either okay, or possibly we received | ||||
// READ_STATUS_CHECKBLOCK_FAILED. | // READ_STATUS_CHECKBLOCK_FAILED. | ||||
// Note that CheckBlock can only fail for one of a few reasons: | // Note that CheckBlock can only fail for one of a few reasons: | ||||
// 1. bad-proof-of-work (impossible here, because we've already | // 1. bad-proof-of-work (impossible here, because we've already | ||||
// accepted the header) | // accepted the header) | ||||
// 2. merkleroot doesn't match the transactions given (already | // 2. merkleroot doesn't match the transactions given (already | ||||
Show All 14 Lines | if (msg_type == NetMsgType::BLOCKTXN) { | ||||
fBlockRead = true; | fBlockRead = true; | ||||
// mapBlockSource is used for potentially punishing peers and | // mapBlockSource is used for potentially punishing peers and | ||||
// updating which peers send us compact blocks, so the race | // updating which peers send us compact blocks, so the race | ||||
// between here and cs_main in ProcessNewBlock is fine. | // between here and cs_main in ProcessNewBlock is fine. | ||||
// BIP 152 permits peers to relay compact blocks after | // BIP 152 permits peers to relay compact blocks after | ||||
// validating the header only; we should not punish peers | // validating the header only; we should not punish peers | ||||
// if the block turns out to be invalid. | // if the block turns out to be invalid. | ||||
mapBlockSource.emplace(resp.blockhash, | mapBlockSource.emplace(resp.blockhash, | ||||
std::make_pair(pfrom->GetId(), false)); | std::make_pair(pfrom.GetId(), false)); | ||||
} | } | ||||
} // Don't hold cs_main when we call into ProcessNewBlock | } // Don't hold cs_main when we call into ProcessNewBlock | ||||
if (fBlockRead) { | if (fBlockRead) { | ||||
bool fNewBlock = false; | bool fNewBlock = false; | ||||
// Since we requested this block (it was in mapBlocksInFlight), | // Since we requested this block (it was in mapBlocksInFlight), | ||||
// force it to be processed, even if it would not be a candidate for | // force it to be processed, even if it would not be a candidate for | ||||
// new tip (missing previous block, chain not long enough, etc) | // new tip (missing previous block, chain not long enough, etc) | ||||
// This bypasses some anti-DoS logic in AcceptBlock (eg to prevent | // This bypasses some anti-DoS logic in AcceptBlock (eg to prevent | ||||
// disk-space attacks), but this should be safe due to the | // disk-space attacks), but this should be safe due to the | ||||
// protections in the compact block handler -- see related comment | // protections in the compact block handler -- see related comment | ||||
// in compact block optimistic reconstruction handling. | // in compact block optimistic reconstruction handling. | ||||
ProcessNewBlock(config, pblock, /*fForceProcessing=*/true, | ProcessNewBlock(config, pblock, /*fForceProcessing=*/true, | ||||
&fNewBlock); | &fNewBlock); | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
pfrom->nLastBlockTime = GetTime(); | pfrom.nLastBlockTime = GetTime(); | ||||
} else { | } else { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
mapBlockSource.erase(pblock->GetHash()); | mapBlockSource.erase(pblock->GetHash()); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::HEADERS) { | if (msg_type == NetMsgType::HEADERS) { | ||||
// Ignore headers received while importing | // Ignore headers received while importing | ||||
if (fImporting || fReindex) { | if (fImporting || fReindex) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Unexpected headers message received from peer %d\n", | "Unexpected headers message received from peer %d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
std::vector<CBlockHeader> headers; | std::vector<CBlockHeader> headers; | ||||
// Bypass the normal CBlock deserialization, as we don't want to risk | // Bypass the normal CBlock deserialization, as we don't want to risk | ||||
// deserializing 2000 full blocks. | // deserializing 2000 full blocks. | ||||
unsigned int nCount = ReadCompactSize(vRecv); | unsigned int nCount = ReadCompactSize(vRecv); | ||||
Show All 13 Lines | if (msg_type == NetMsgType::HEADERS) { | ||||
/*via_compact_block=*/false); | /*via_compact_block=*/false); | ||||
} | } | ||||
if (msg_type == NetMsgType::BLOCK) { | if (msg_type == NetMsgType::BLOCK) { | ||||
// Ignore block received while importing | // Ignore block received while importing | ||||
if (fImporting || fReindex) { | if (fImporting || fReindex) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Unexpected block message received from peer %d\n", | "Unexpected block message received from peer %d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
vRecv >> *pblock; | vRecv >> *pblock; | ||||
LogPrint(BCLog::NET, "received block %s peer=%d\n", | LogPrint(BCLog::NET, "received block %s peer=%d\n", | ||||
pblock->GetHash().ToString(), pfrom->GetId()); | pblock->GetHash().ToString(), pfrom.GetId()); | ||||
// Process all blocks from whitelisted peers, even if not requested, | // Process all blocks from whitelisted peers, even if not requested, | ||||
// unless we're still syncing with the network. Such an unrequested | // unless we're still syncing with the network. Such an unrequested | ||||
// block may still be processed, subject to the conditions in | // block may still be processed, subject to the conditions in | ||||
// AcceptBlock(). | // AcceptBlock(). | ||||
bool forceProcessing = pfrom->HasPermission(PF_NOBAN) && | bool forceProcessing = pfrom.HasPermission(PF_NOBAN) && | ||||
!::ChainstateActive().IsInitialBlockDownload(); | !::ChainstateActive().IsInitialBlockDownload(); | ||||
const BlockHash hash = pblock->GetHash(); | const BlockHash hash = pblock->GetHash(); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// Also always process if we requested the block explicitly, as we | // Also always process if we requested the block explicitly, as we | ||||
// may need it even though it is not a candidate for a new best tip. | // may need it even though it is not a candidate for a new best tip. | ||||
forceProcessing |= MarkBlockAsReceived(hash); | forceProcessing |= MarkBlockAsReceived(hash); | ||||
// mapBlockSource is only used for punishing peers and setting | // mapBlockSource is only used for punishing peers and setting | ||||
// which peers send us compact blocks, so the race between here and | // which peers send us compact blocks, so the race between here and | ||||
// cs_main in ProcessNewBlock is fine. | // cs_main in ProcessNewBlock is fine. | ||||
mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true)); | mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true)); | ||||
} | } | ||||
bool fNewBlock = false; | bool fNewBlock = false; | ||||
ProcessNewBlock(config, pblock, forceProcessing, &fNewBlock); | ProcessNewBlock(config, pblock, forceProcessing, &fNewBlock); | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
pfrom->nLastBlockTime = GetTime(); | pfrom.nLastBlockTime = GetTime(); | ||||
} else { | } else { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
mapBlockSource.erase(hash); | mapBlockSource.erase(hash); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
// Ignore avalanche requests while importing | // Ignore avalanche requests while importing | ||||
if (msg_type == NetMsgType::AVAPOLL && !fImporting && !fReindex && | if (msg_type == NetMsgType::AVAPOLL && !fImporting && !fReindex && | ||||
g_avalanche && | g_avalanche && | ||||
gArgs.GetBoolArg("-enableavalanche", AVALANCHE_DEFAULT_ENABLED)) { | gArgs.GetBoolArg("-enableavalanche", AVALANCHE_DEFAULT_ENABLED)) { | ||||
auto now = std::chrono::steady_clock::now(); | auto now = std::chrono::steady_clock::now(); | ||||
int64_t cooldown = | int64_t cooldown = | ||||
gArgs.GetArg("-avacooldown", AVALANCHE_DEFAULT_COOLDOWN); | gArgs.GetArg("-avacooldown", AVALANCHE_DEFAULT_COOLDOWN); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
auto &node_state = State(pfrom->GetId())->m_avalanche_state; | auto &node_state = State(pfrom.GetId())->m_avalanche_state; | ||||
if (now < | if (now < | ||||
node_state.last_poll + std::chrono::milliseconds(cooldown)) { | node_state.last_poll + std::chrono::milliseconds(cooldown)) { | ||||
Misbehaving(pfrom, 20, "avapool-cooldown"); | Misbehaving(pfrom, 20, "avapool-cooldown"); | ||||
} | } | ||||
node_state.last_poll = now; | node_state.last_poll = now; | ||||
} | } | ||||
uint64_t round; | uint64_t round; | ||||
Unserialize(vRecv, round); | Unserialize(vRecv, round); | ||||
unsigned int nCount = ReadCompactSize(vRecv); | unsigned int nCount = ReadCompactSize(vRecv); | ||||
if (nCount > AVALANCHE_MAX_ELEMENT_POLL) { | if (nCount > AVALANCHE_MAX_ELEMENT_POLL) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, "too-many-ava-poll"); | Misbehaving(pfrom, 20, "too-many-ava-poll"); | ||||
return error("poll message size = %u", nCount); | return error("poll message size = %u", nCount); | ||||
} | } | ||||
std::vector<avalanche::Vote> votes; | std::vector<avalanche::Vote> votes; | ||||
votes.reserve(nCount); | votes.reserve(nCount); | ||||
LogPrint(BCLog::NET, "received avalanche poll from peer=%d\n", | LogPrint(BCLog::NET, "received avalanche poll from peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (unsigned int n = 0; n < nCount; n++) { | for (unsigned int n = 0; n < nCount; n++) { | ||||
CInv inv; | CInv inv; | ||||
vRecv >> inv; | vRecv >> inv; | ||||
uint32_t error = -1; | uint32_t error = -1; | ||||
if (inv.type == MSG_BLOCK) { | if (inv.type == MSG_BLOCK) { | ||||
auto blockIndex = LookupBlockIndex(BlockHash(inv.hash)); | auto blockIndex = LookupBlockIndex(BlockHash(inv.hash)); | ||||
if (blockIndex) { | if (blockIndex) { | ||||
error = ::ChainActive().Contains(blockIndex) ? 0 : 1; | error = ::ChainActive().Contains(blockIndex) ? 0 : 1; | ||||
} | } | ||||
} | } | ||||
votes.emplace_back(error, inv.hash); | votes.emplace_back(error, inv.hash); | ||||
} | } | ||||
} | } | ||||
// Send the query to the node. | // Send the query to the node. | ||||
g_avalanche->sendResponse( | g_avalanche->sendResponse( | ||||
pfrom, avalanche::Response(round, cooldown, std::move(votes))); | &pfrom, avalanche::Response(round, cooldown, std::move(votes))); | ||||
return true; | return true; | ||||
} | } | ||||
// Ignore avalanche requests while importing | // Ignore avalanche requests while importing | ||||
if (msg_type == NetMsgType::AVARESPONSE && !fImporting && !fReindex && | if (msg_type == NetMsgType::AVARESPONSE && !fImporting && !fReindex && | ||||
g_avalanche && | g_avalanche && | ||||
gArgs.GetBoolArg("-enableavalanche", AVALANCHE_DEFAULT_ENABLED)) { | gArgs.GetBoolArg("-enableavalanche", AVALANCHE_DEFAULT_ENABLED)) { | ||||
// As long as QUIC is not implemented, we need to sign response and | // As long as QUIC is not implemented, we need to sign response and | ||||
// verify response's signatures in order to avoid any manipulation of | // verify response's signatures in order to avoid any manipulation of | ||||
// messages at the transport level. | // messages at the transport level. | ||||
CHashVerifier<CDataStream> verifier(&vRecv); | CHashVerifier<CDataStream> verifier(&vRecv); | ||||
avalanche::Response response; | avalanche::Response response; | ||||
verifier >> response; | verifier >> response; | ||||
if (!g_avalanche->forNode( | if (!g_avalanche->forNode(pfrom.GetId(), [&](const avalanche::Node &n) { | ||||
pfrom->GetId(), [&](const avalanche::Node &n) { | |||||
std::array<uint8_t, 64> sig; | std::array<uint8_t, 64> sig; | ||||
vRecv >> sig; | vRecv >> sig; | ||||
// Unfortunately, the verify API require a vector. | // Unfortunately, the verify API require a vector. | ||||
std::vector<uint8_t> vchSig{sig.begin(), sig.end()}; | std::vector<uint8_t> vchSig{sig.begin(), sig.end()}; | ||||
return n.pubkey.VerifySchnorr(verifier.GetHash(), vchSig); | return n.pubkey.VerifySchnorr(verifier.GetHash(), vchSig); | ||||
})) { | })) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 100, "invalid-ava-response-signature"); | Misbehaving(pfrom, 100, "invalid-ava-response-signature"); | ||||
return true; | return true; | ||||
} | } | ||||
std::vector<avalanche::BlockUpdate> updates; | std::vector<avalanche::BlockUpdate> updates; | ||||
if (!g_avalanche->registerVotes(pfrom->GetId(), response, updates)) { | if (!g_avalanche->registerVotes(pfrom.GetId(), response, updates)) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 100, "invalid-ava-response-content"); | Misbehaving(pfrom, 100, "invalid-ava-response-content"); | ||||
return true; | return true; | ||||
} | } | ||||
if (updates.size()) { | if (updates.size()) { | ||||
for (avalanche::BlockUpdate &u : updates) { | for (avalanche::BlockUpdate &u : updates) { | ||||
CBlockIndex *pindex = u.getBlockIndex(); | CBlockIndex *pindex = u.getBlockIndex(); | ||||
Show All 27 Lines | |||||
if (msg_type == NetMsgType::GETADDR) { | if (msg_type == NetMsgType::GETADDR) { | ||||
// This asymmetric behavior for inbound and outbound connections was | // This asymmetric behavior for inbound and outbound connections was | ||||
// introduced to prevent a fingerprinting attack: an attacker can send | // introduced to prevent a fingerprinting attack: an attacker can send | ||||
// specific fake addresses to users' AddrMan and later request them by | // specific fake addresses to users' AddrMan and later request them by | ||||
// sending getaddr messages. Making nodes which are behind NAT and can | // sending getaddr messages. Making nodes which are behind NAT and can | ||||
// only make outgoing connections ignore the getaddr message mitigates | // only make outgoing connections ignore the getaddr message mitigates | ||||
// the attack. | // the attack. | ||||
if (!pfrom->fInbound) { | if (!pfrom.fInbound) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Ignoring \"getaddr\" from outbound connection. peer=%d\n", | "Ignoring \"getaddr\" from outbound connection. peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
if (!pfrom->IsAddrRelayPeer()) { | if (!pfrom.IsAddrRelayPeer()) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"Ignoring \"getaddr\" from block-relay-only connection. " | "Ignoring \"getaddr\" from block-relay-only connection. " | ||||
"peer=%d\n", | "peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
// Only send one GetAddr response per connection to reduce resource | // Only send one GetAddr response per connection to reduce resource | ||||
// waste and discourage addr stamping of INV announcements. | // waste and discourage addr stamping of INV announcements. | ||||
if (pfrom->fSentAddr) { | if (pfrom.fSentAddr) { | ||||
LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", | LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
pfrom->fSentAddr = true; | pfrom.fSentAddr = true; | ||||
pfrom->vAddrToSend.clear(); | pfrom.vAddrToSend.clear(); | ||||
std::vector<CAddress> vAddr = connman->GetAddresses(); | std::vector<CAddress> vAddr = connman->GetAddresses(); | ||||
FastRandomContext insecure_rand; | FastRandomContext insecure_rand; | ||||
for (const CAddress &addr : vAddr) { | for (const CAddress &addr : vAddr) { | ||||
if (!banman->IsDiscouraged(addr) && !banman->IsBanned(addr)) { | if (!banman->IsDiscouraged(addr) && !banman->IsBanned(addr)) { | ||||
pfrom->PushAddress(addr, insecure_rand); | pfrom.PushAddress(addr, insecure_rand); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::MEMPOOL) { | if (msg_type == NetMsgType::MEMPOOL) { | ||||
if (!(pfrom->GetLocalServices() & NODE_BLOOM) && | if (!(pfrom.GetLocalServices() & NODE_BLOOM) && | ||||
!pfrom->HasPermission(PF_MEMPOOL)) { | !pfrom.HasPermission(PF_MEMPOOL)) { | ||||
if (!pfrom->HasPermission(PF_NOBAN)) { | if (!pfrom.HasPermission(PF_NOBAN)) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"mempool request with bloom filters disabled, " | "mempool request with bloom filters disabled, " | ||||
"disconnect peer=%d\n", | "disconnect peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (connman->OutboundTargetReached(false) && | if (connman->OutboundTargetReached(false) && | ||||
!pfrom->HasPermission(PF_MEMPOOL)) { | !pfrom.HasPermission(PF_MEMPOOL)) { | ||||
if (!pfrom->HasPermission(PF_NOBAN)) { | if (!pfrom.HasPermission(PF_NOBAN)) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"mempool request with bandwidth limit reached, " | "mempool request with bandwidth limit reached, " | ||||
"disconnect peer=%d\n", | "disconnect peer=%d\n", | ||||
pfrom->GetId()); | pfrom.GetId()); | ||||
pfrom->fDisconnect = true; | pfrom.fDisconnect = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (pfrom->m_tx_relay != nullptr) { | if (pfrom.m_tx_relay != nullptr) { | ||||
LOCK(pfrom->m_tx_relay->cs_tx_inventory); | LOCK(pfrom.m_tx_relay->cs_tx_inventory); | ||||
pfrom->m_tx_relay->fSendMempool = true; | pfrom.m_tx_relay->fSendMempool = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::PING) { | if (msg_type == NetMsgType::PING) { | ||||
if (pfrom->nVersion > BIP0031_VERSION) { | if (pfrom.nVersion > BIP0031_VERSION) { | ||||
uint64_t nonce = 0; | uint64_t nonce = 0; | ||||
vRecv >> nonce; | vRecv >> nonce; | ||||
// Echo the message back with the nonce. This allows for two useful | // Echo the message back with the nonce. This allows for two useful | ||||
// features: | // features: | ||||
// | // | ||||
// 1) A remote node can quickly check if the connection is | // 1) A remote node can quickly check if the connection is | ||||
// operational. | // operational. | ||||
// 2) Remote nodes can measure the latency of the network thread. If | // 2) Remote nodes can measure the latency of the network thread. If | ||||
// this node is overloaded it won't respond to pings quickly and the | // this node is overloaded it won't respond to pings quickly and the | ||||
// remote node can avoid sending us more work, like chain download | // remote node can avoid sending us more work, like chain download | ||||
// requests. | // requests. | ||||
// | // | ||||
// The nonce stops the remote getting confused between different | // The nonce stops the remote getting confused between different | ||||
// pings: without it, if the remote node sends a ping once per | // pings: without it, if the remote node sends a ping once per | ||||
// second and this node takes 5 seconds to respond to each, the 5th | // second and this node takes 5 seconds to respond to each, the 5th | ||||
// ping the remote sends would appear to return very quickly. | // ping the remote sends would appear to return very quickly. | ||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); | connman->PushMessage(&pfrom, | ||||
msgMaker.Make(NetMsgType::PONG, nonce)); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::PONG) { | if (msg_type == NetMsgType::PONG) { | ||||
int64_t pingUsecEnd = nTimeReceived; | int64_t pingUsecEnd = nTimeReceived; | ||||
uint64_t nonce = 0; | uint64_t nonce = 0; | ||||
size_t nAvail = vRecv.in_avail(); | size_t nAvail = vRecv.in_avail(); | ||||
bool bPingFinished = false; | bool bPingFinished = false; | ||||
std::string sProblem; | std::string sProblem; | ||||
if (nAvail >= sizeof(nonce)) { | if (nAvail >= sizeof(nonce)) { | ||||
vRecv >> nonce; | vRecv >> nonce; | ||||
// Only process pong message if there is an outstanding ping (old | // Only process pong message if there is an outstanding ping (old | ||||
// ping without nonce should never pong) | // ping without nonce should never pong) | ||||
if (pfrom->nPingNonceSent != 0) { | if (pfrom.nPingNonceSent != 0) { | ||||
if (nonce == pfrom->nPingNonceSent) { | if (nonce == pfrom.nPingNonceSent) { | ||||
// Matching pong received, this ping is no longer | // Matching pong received, this ping is no longer | ||||
// outstanding | // outstanding | ||||
bPingFinished = true; | bPingFinished = true; | ||||
int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; | int64_t pingUsecTime = pingUsecEnd - pfrom.nPingUsecStart; | ||||
if (pingUsecTime > 0) { | if (pingUsecTime > 0) { | ||||
// Successful ping time measurement, replace previous | // Successful ping time measurement, replace previous | ||||
pfrom->nPingUsecTime = pingUsecTime; | pfrom.nPingUsecTime = pingUsecTime; | ||||
pfrom->nMinPingUsecTime = std::min( | pfrom.nMinPingUsecTime = std::min( | ||||
pfrom->nMinPingUsecTime.load(), pingUsecTime); | pfrom.nMinPingUsecTime.load(), pingUsecTime); | ||||
} else { | } else { | ||||
// This should never happen | // This should never happen | ||||
sProblem = "Timing mishap"; | sProblem = "Timing mishap"; | ||||
} | } | ||||
} else { | } else { | ||||
// Nonce mismatches are normal when pings are overlapping | // Nonce mismatches are normal when pings are overlapping | ||||
sProblem = "Nonce mismatch"; | sProblem = "Nonce mismatch"; | ||||
if (nonce == 0) { | if (nonce == 0) { | ||||
Show All 11 Lines | if (msg_type == NetMsgType::PONG) { | ||||
// cancel this ping | // cancel this ping | ||||
bPingFinished = true; | bPingFinished = true; | ||||
sProblem = "Short payload"; | sProblem = "Short payload"; | ||||
} | } | ||||
if (!(sProblem.empty())) { | if (!(sProblem.empty())) { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, | ||||
"pong peer=%d: %s, %x expected, %x received, %u bytes\n", | "pong peer=%d: %s, %x expected, %x received, %u bytes\n", | ||||
pfrom->GetId(), sProblem, pfrom->nPingNonceSent, nonce, | pfrom.GetId(), sProblem, pfrom.nPingNonceSent, nonce, | ||||
nAvail); | nAvail); | ||||
} | } | ||||
if (bPingFinished) { | if (bPingFinished) { | ||||
pfrom->nPingNonceSent = 0; | pfrom.nPingNonceSent = 0; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::FILTERLOAD) { | if (msg_type == NetMsgType::FILTERLOAD) { | ||||
CBloomFilter filter; | CBloomFilter filter; | ||||
vRecv >> filter; | vRecv >> filter; | ||||
if (!filter.IsWithinSizeConstraints()) { | if (!filter.IsWithinSizeConstraints()) { | ||||
// There is no excuse for sending a too-large filter | // There is no excuse for sending a too-large filter | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 100, "oversized-bloom-filter"); | Misbehaving(pfrom, 100, "oversized-bloom-filter"); | ||||
} else if (pfrom->m_tx_relay != nullptr) { | } else if (pfrom.m_tx_relay != nullptr) { | ||||
LOCK(pfrom->m_tx_relay->cs_filter); | LOCK(pfrom.m_tx_relay->cs_filter); | ||||
pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter)); | pfrom.m_tx_relay->pfilter.reset(new CBloomFilter(filter)); | ||||
pfrom->m_tx_relay->pfilter->UpdateEmptyFull(); | pfrom.m_tx_relay->pfilter->UpdateEmptyFull(); | ||||
pfrom->m_tx_relay->fRelayTxes = true; | pfrom.m_tx_relay->fRelayTxes = true; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::FILTERADD) { | if (msg_type == NetMsgType::FILTERADD) { | ||||
std::vector<uint8_t> vData; | std::vector<uint8_t> vData; | ||||
vRecv >> vData; | vRecv >> vData; | ||||
// Nodes must NEVER send a data item > 520 bytes (the max size for a | // Nodes must NEVER send a data item > 520 bytes (the max size for a | ||||
// script data object, and thus, the maximum size any matched object can | // script data object, and thus, the maximum size any matched object can | ||||
// have) in a filteradd message. | // have) in a filteradd message. | ||||
bool bad = false; | bool bad = false; | ||||
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { | if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { | ||||
bad = true; | bad = true; | ||||
} else if (pfrom->m_tx_relay != nullptr) { | } else if (pfrom.m_tx_relay != nullptr) { | ||||
LOCK(pfrom->m_tx_relay->cs_filter); | LOCK(pfrom.m_tx_relay->cs_filter); | ||||
if (pfrom->m_tx_relay->pfilter) { | if (pfrom.m_tx_relay->pfilter) { | ||||
pfrom->m_tx_relay->pfilter->insert(vData); | pfrom.m_tx_relay->pfilter->insert(vData); | ||||
} else { | } else { | ||||
bad = true; | bad = true; | ||||
} | } | ||||
} | } | ||||
if (bad) { | if (bad) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// The structure of this code doesn't really allow for a good error | // The structure of this code doesn't really allow for a good error | ||||
// code. We'll go generic. | // code. We'll go generic. | ||||
Misbehaving(pfrom, 100, "invalid-filteradd"); | Misbehaving(pfrom, 100, "invalid-filteradd"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::FILTERCLEAR) { | if (msg_type == NetMsgType::FILTERCLEAR) { | ||||
if (pfrom->m_tx_relay == nullptr) { | if (pfrom.m_tx_relay == nullptr) { | ||||
return true; | return true; | ||||
} | } | ||||
LOCK(pfrom->m_tx_relay->cs_filter); | LOCK(pfrom.m_tx_relay->cs_filter); | ||||
if (pfrom->GetLocalServices() & NODE_BLOOM) { | if (pfrom.GetLocalServices() & NODE_BLOOM) { | ||||
pfrom->m_tx_relay->pfilter.reset(new CBloomFilter()); | pfrom.m_tx_relay->pfilter.reset(new CBloomFilter()); | ||||
} | } | ||||
pfrom->m_tx_relay->fRelayTxes = true; | pfrom.m_tx_relay->fRelayTxes = true; | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::FEEFILTER) { | if (msg_type == NetMsgType::FEEFILTER) { | ||||
Amount newFeeFilter = Amount::zero(); | Amount newFeeFilter = Amount::zero(); | ||||
vRecv >> newFeeFilter; | vRecv >> newFeeFilter; | ||||
if (MoneyRange(newFeeFilter)) { | if (MoneyRange(newFeeFilter)) { | ||||
if (pfrom->m_tx_relay != nullptr) { | if (pfrom.m_tx_relay != nullptr) { | ||||
LOCK(pfrom->m_tx_relay->cs_feeFilter); | LOCK(pfrom.m_tx_relay->cs_feeFilter); | ||||
pfrom->m_tx_relay->minFeeFilter = newFeeFilter; | pfrom.m_tx_relay->minFeeFilter = newFeeFilter; | ||||
} | } | ||||
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", | LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", | ||||
CFeeRate(newFeeFilter).ToString(), pfrom->GetId()); | CFeeRate(newFeeFilter).ToString(), pfrom.GetId()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETCFILTERS) { | if (msg_type == NetMsgType::GETCFILTERS) { | ||||
ProcessGetCFilters(*pfrom, vRecv, chainparams, *connman); | ProcessGetCFilters(pfrom, vRecv, chainparams, *connman); | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETCFHEADERS) { | if (msg_type == NetMsgType::GETCFHEADERS) { | ||||
ProcessGetCFHeaders(*pfrom, vRecv, chainparams, *connman); | ProcessGetCFHeaders(pfrom, vRecv, chainparams, *connman); | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::GETCFCHECKPT) { | if (msg_type == NetMsgType::GETCFCHECKPT) { | ||||
ProcessGetCFCheckPt(*pfrom, vRecv, chainparams, *connman); | ProcessGetCFCheckPt(pfrom, vRecv, chainparams, *connman); | ||||
return true; | return true; | ||||
} | } | ||||
if (msg_type == NetMsgType::NOTFOUND) { | if (msg_type == NetMsgType::NOTFOUND) { | ||||
// Remove the NOTFOUND transactions from the peer | // Remove the NOTFOUND transactions from the peer | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CNodeState *state = State(pfrom->GetId()); | CNodeState *state = State(pfrom.GetId()); | ||||
std::vector<CInv> vInv; | std::vector<CInv> vInv; | ||||
vRecv >> vInv; | vRecv >> vInv; | ||||
if (vInv.size() <= | if (vInv.size() <= | ||||
MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | MAX_PEER_TX_IN_FLIGHT + MAX_BLOCKS_IN_TRANSIT_PER_PEER) { | ||||
for (CInv &inv : vInv) { | for (CInv &inv : vInv) { | ||||
if (inv.type == MSG_TX) { | if (inv.type == MSG_TX) { | ||||
const TxId txid(inv.hash); | const TxId txid(inv.hash); | ||||
// If we receive a NOTFOUND message for a txid we requested, | // If we receive a NOTFOUND message for a txid we requested, | ||||
Show All 11 Lines | if (msg_type == NetMsgType::NOTFOUND) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
// Ignore unknown commands for extensibility | // Ignore unknown commands for extensibility | ||||
LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", | LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", | ||||
SanitizeString(msg_type), pfrom->GetId()); | SanitizeString(msg_type), pfrom.GetId()); | ||||
return true; | return true; | ||||
} | } | ||||
bool PeerLogicValidation::CheckIfBanned(CNode *pnode) { | bool PeerLogicValidation::CheckIfBanned(CNode &pnode) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
CNodeState &state = *State(pnode->GetId()); | CNodeState &state = *State(pnode.GetId()); | ||||
if (state.m_should_discourage) { | if (state.m_should_discourage) { | ||||
state.m_should_discourage = false; | state.m_should_discourage = false; | ||||
if (pnode->HasPermission(PF_NOBAN)) { | if (pnode.HasPermission(PF_NOBAN)) { | ||||
LogPrintf("Warning: not punishing whitelisted peer %s!\n", | LogPrintf("Warning: not punishing whitelisted peer %s!\n", | ||||
pnode->addr.ToString()); | pnode.addr.ToString()); | ||||
} else if (pnode->m_manual_connection) { | } else if (pnode.m_manual_connection) { | ||||
LogPrintf("Warning: not punishing manually-connected peer %s!\n", | LogPrintf("Warning: not punishing manually-connected peer %s!\n", | ||||
pnode->addr.ToString()); | pnode.addr.ToString()); | ||||
} else if (pnode->addr.IsLocal()) { | } else if (pnode.addr.IsLocal()) { | ||||
// Disconnect but don't discourage this local node | // Disconnect but don't discourage this local node | ||||
LogPrintf( | LogPrintf( | ||||
"Warning: disconnecting but not discouraging local peer %s!\n", | "Warning: disconnecting but not discouraging local peer %s!\n", | ||||
pnode->addr.ToString()); | pnode.addr.ToString()); | ||||
pnode->fDisconnect = true; | pnode.fDisconnect = true; | ||||
} else { | } else { | ||||
// Disconnect and discourage all nodes sharing the address | // Disconnect and discourage all nodes sharing the address | ||||
LogPrintf("Disconnecting and discouraging peer %s!\n", | LogPrintf("Disconnecting and discouraging peer %s!\n", | ||||
pnode->addr.ToString()); | pnode.addr.ToString()); | ||||
if (m_banman) { | if (m_banman) { | ||||
m_banman->Discourage(pnode->addr); | m_banman->Discourage(pnode.addr); | ||||
} | } | ||||
connman->DisconnectNode(pnode->addr); | connman->DisconnectNode(pnode.addr); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
bool PeerLogicValidation::ProcessMessages(const Config &config, CNode *pfrom, | bool PeerLogicValidation::ProcessMessages(const Config &config, CNode *pfrom, | ||||
std::atomic<bool> &interruptMsgProc) { | std::atomic<bool> &interruptMsgProc) { | ||||
// | // | ||||
// Message format | // Message format | ||||
// (4) message start | // (4) message start | ||||
// (12) command | // (12) command | ||||
// (4) size | // (4) size | ||||
// (4) checksum | // (4) checksum | ||||
// (x) data | // (x) data | ||||
// | // | ||||
bool fMoreWork = false; | bool fMoreWork = false; | ||||
if (!pfrom->vRecvGetData.empty()) { | if (!pfrom->vRecvGetData.empty()) { | ||||
ProcessGetData(config, pfrom, connman, interruptMsgProc); | ProcessGetData(config, *pfrom, connman, interruptMsgProc); | ||||
} | } | ||||
if (!pfrom->orphan_work_set.empty()) { | if (!pfrom->orphan_work_set.empty()) { | ||||
LOCK2(cs_main, g_cs_orphans); | LOCK2(cs_main, g_cs_orphans); | ||||
ProcessOrphanTx(config, connman, pfrom->orphan_work_set); | ProcessOrphanTx(config, connman, pfrom->orphan_work_set); | ||||
} | } | ||||
if (pfrom->fDisconnect) { | if (pfrom->fDisconnect) { | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | if (!msg.m_valid_checksum) { | ||||
} | } | ||||
connman->DisconnectNode(pfrom->addr); | connman->DisconnectNode(pfrom->addr); | ||||
return fMoreWork; | return fMoreWork; | ||||
} | } | ||||
// Process message | // Process message | ||||
bool fRet = false; | bool fRet = false; | ||||
try { | try { | ||||
fRet = ProcessMessage(config, pfrom, msg_type, vRecv, msg.m_time, | fRet = ProcessMessage(config, *pfrom, msg_type, vRecv, msg.m_time, | ||||
connman, m_banman, interruptMsgProc); | connman, m_banman, interruptMsgProc); | ||||
if (interruptMsgProc) { | if (interruptMsgProc) { | ||||
return false; | return false; | ||||
} | } | ||||
if (!pfrom->vRecvGetData.empty()) { | if (!pfrom->vRecvGetData.empty()) { | ||||
fMoreWork = true; | fMoreWork = true; | ||||
} | } | ||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", | LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", | ||||
__func__, SanitizeString(msg_type), nMessageSize, e.what(), | __func__, SanitizeString(msg_type), nMessageSize, e.what(), | ||||
typeid(e).name()); | typeid(e).name()); | ||||
} catch (...) { | } catch (...) { | ||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", | LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", | ||||
__func__, SanitizeString(msg_type), nMessageSize); | __func__, SanitizeString(msg_type), nMessageSize); | ||||
} | } | ||||
if (!fRet) { | if (!fRet) { | ||||
LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, | LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, | ||||
SanitizeString(msg_type), nMessageSize, pfrom->GetId()); | SanitizeString(msg_type), nMessageSize, pfrom->GetId()); | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CheckIfBanned(pfrom); | CheckIfBanned(*pfrom); | ||||
return fMoreWork; | return fMoreWork; | ||||
} | } | ||||
void PeerLogicValidation::ConsiderEviction(CNode *pto, | void PeerLogicValidation::ConsiderEviction(CNode &pto, | ||||
int64_t time_in_seconds) { | int64_t time_in_seconds) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
CNodeState &state = *State(pto->GetId()); | CNodeState &state = *State(pto.GetId()); | ||||
const CNetMsgMaker msgMaker(pto->GetSendVersion()); | const CNetMsgMaker msgMaker(pto.GetSendVersion()); | ||||
if (!state.m_chain_sync.m_protect && | if (!state.m_chain_sync.m_protect && | ||||
IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) { | IsOutboundDisconnectionCandidate(pto) && state.fSyncStarted) { | ||||
// This is an outbound peer subject to disconnection if they don't | // This is an outbound peer subject to disconnection if they don't | ||||
// announce a block with as much work as the current tip within | // announce a block with as much work as the current tip within | ||||
// CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if their | // CHAIN_SYNC_TIMEOUT + HEADERS_RESPONSE_TIME seconds (note: if their | ||||
// chain has more work than ours, we should sync to it, unless it's | // chain has more work than ours, we should sync to it, unless it's | ||||
// invalid, in which case we should find that out and disconnect from | // invalid, in which case we should find that out and disconnect from | ||||
Show All 24 Lines | if (!state.m_chain_sync.m_protect && | ||||
// equal to that of our tip, when we first detected it was behind. | // equal to that of our tip, when we first detected it was behind. | ||||
// Send a single getheaders message to give the peer a chance to | // Send a single getheaders message to give the peer a chance to | ||||
// update us. | // update us. | ||||
if (state.m_chain_sync.m_sent_getheaders) { | if (state.m_chain_sync.m_sent_getheaders) { | ||||
// They've run out of time to catch up! | // They've run out of time to catch up! | ||||
LogPrintf( | LogPrintf( | ||||
"Disconnecting outbound peer %d for old chain, best known " | "Disconnecting outbound peer %d for old chain, best known " | ||||
"block = %s\n", | "block = %s\n", | ||||
pto->GetId(), | pto.GetId(), | ||||
state.pindexBestKnownBlock != nullptr | state.pindexBestKnownBlock != nullptr | ||||
? state.pindexBestKnownBlock->GetBlockHash().ToString() | ? state.pindexBestKnownBlock->GetBlockHash().ToString() | ||||
: "<none>"); | : "<none>"); | ||||
pto->fDisconnect = true; | pto.fDisconnect = true; | ||||
} else { | } else { | ||||
assert(state.m_chain_sync.m_work_header); | assert(state.m_chain_sync.m_work_header); | ||||
LogPrint( | LogPrint( | ||||
BCLog::NET, | BCLog::NET, | ||||
"sending getheaders to outbound peer=%d to verify chain " | "sending getheaders to outbound peer=%d to verify chain " | ||||
"work (current best known block:%s, benchmark blockhash: " | "work (current best known block:%s, benchmark blockhash: " | ||||
"%s)\n", | "%s)\n", | ||||
pto->GetId(), | pto.GetId(), | ||||
state.pindexBestKnownBlock != nullptr | state.pindexBestKnownBlock != nullptr | ||||
? state.pindexBestKnownBlock->GetBlockHash().ToString() | ? state.pindexBestKnownBlock->GetBlockHash().ToString() | ||||
: "<none>", | : "<none>", | ||||
state.m_chain_sync.m_work_header->GetBlockHash() | state.m_chain_sync.m_work_header->GetBlockHash() | ||||
.ToString()); | .ToString()); | ||||
connman->PushMessage( | connman->PushMessage( | ||||
pto, | &pto, | ||||
msgMaker.Make(NetMsgType::GETHEADERS, | msgMaker.Make(NetMsgType::GETHEADERS, | ||||
::ChainActive().GetLocator( | ::ChainActive().GetLocator( | ||||
state.m_chain_sync.m_work_header->pprev), | state.m_chain_sync.m_work_header->pprev), | ||||
uint256())); | uint256())); | ||||
state.m_chain_sync.m_sent_getheaders = true; | state.m_chain_sync.m_sent_getheaders = true; | ||||
// 2 minutes | // 2 minutes | ||||
constexpr int64_t HEADERS_RESPONSE_TIME = 120; | constexpr int64_t HEADERS_RESPONSE_TIME = 120; | ||||
// Bump the timeout to allow a response, which could clear the | // Bump the timeout to allow a response, which could clear the | ||||
Show All 21 Lines | void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) { | ||||
// ties broken by choosing the more recent connection (higher node id) | // ties broken by choosing the more recent connection (higher node id) | ||||
NodeId worst_peer = -1; | NodeId worst_peer = -1; | ||||
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); | int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); | ||||
connman->ForEachNode([&](CNode *pnode) { | connman->ForEachNode([&](CNode *pnode) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Ignore non-outbound peers, or nodes marked for disconnect already | // Ignore non-outbound peers, or nodes marked for disconnect already | ||||
if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) { | if (!IsOutboundDisconnectionCandidate(*pnode) || pnode->fDisconnect) { | ||||
return; | return; | ||||
} | } | ||||
CNodeState *state = State(pnode->GetId()); | CNodeState *state = State(pnode->GetId()); | ||||
if (state == nullptr) { | if (state == nullptr) { | ||||
// shouldn't be possible, but just in case | // shouldn't be possible, but just in case | ||||
return; | return; | ||||
} | } | ||||
// Don't evict our protected peers | // Don't evict our protected peers | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | if (pingSend) { | ||||
} | } | ||||
} | } | ||||
TRY_LOCK(cs_main, lockMain); | TRY_LOCK(cs_main, lockMain); | ||||
if (!lockMain) { | if (!lockMain) { | ||||
return true; | return true; | ||||
} | } | ||||
if (CheckIfBanned(pto)) { | if (CheckIfBanned(*pto)) { | ||||
return true; | return true; | ||||
} | } | ||||
CNodeState &state = *State(pto->GetId()); | CNodeState &state = *State(pto->GetId()); | ||||
// Address refresh broadcast | // Address refresh broadcast | ||||
int64_t nNow = GetTimeMicros(); | int64_t nNow = GetTimeMicros(); | ||||
auto current_time = GetTime<std::chrono::microseconds>(); | auto current_time = GetTime<std::chrono::microseconds>(); | ||||
▲ Show 20 Lines • Show All 494 Lines • ▼ Show 20 Lines | if (state.fSyncStarted && | ||||
// After we've caught up once, reset the timeout so we can't trigger | // After we've caught up once, reset the timeout so we can't trigger | ||||
// disconnect later. | // disconnect later. | ||||
state.nHeadersSyncTimeout = std::numeric_limits<int64_t>::max(); | state.nHeadersSyncTimeout = std::numeric_limits<int64_t>::max(); | ||||
} | } | ||||
} | } | ||||
// Check that outbound peers have reasonable chains GetTime() is used by | // Check that outbound peers have reasonable chains GetTime() is used by | ||||
// this anti-DoS logic so we can test this using mocktime. | // this anti-DoS logic so we can test this using mocktime. | ||||
ConsiderEviction(pto, GetTime()); | ConsiderEviction(*pto, GetTime()); | ||||
// | // | ||||
// Message: getdata (blocks) | // Message: getdata (blocks) | ||||
// | // | ||||
std::vector<CInv> vGetData; | std::vector<CInv> vGetData; | ||||
if (!pto->fClient && | if (!pto->fClient && | ||||
((fFetch && !pto->m_limited_node) || | ((fFetch && !pto->m_limited_node) || | ||||
!::ChainstateActive().IsInitialBlockDownload()) && | !::ChainstateActive().IsInitialBlockDownload()) && | ||||
▲ Show 20 Lines • Show All 148 Lines • Show Last 20 Lines |