Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 929 Lines • ▼ Show 20 Lines | while (mapOrphanTransactions.size() > nMaxOrphans) { | ||||
++nEvicted; | ++nEvicted; | ||||
} | } | ||||
return nEvicted; | return nEvicted; | ||||
} | } | ||||
/** | /** | ||||
* Mark a misbehaving peer to be banned depending upon the value of `-banscore`. | * Mark a misbehaving peer to be banned depending upon the value of `-banscore`. | ||||
*/ | */ | ||||
void Misbehaving(NodeId pnode, int howmuch, const std::string &reason) | void Misbehaving(NodeId pnode, int howmuch, const std::string &message) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
if (howmuch == 0) { | if (howmuch == 0) { | ||||
return; | return; | ||||
} | } | ||||
CNodeState *state = State(pnode); | CNodeState *state = State(pnode); | ||||
if (state == nullptr) { | if (state == nullptr) { | ||||
return; | return; | ||||
} | } | ||||
state->nMisbehavior += howmuch; | state->nMisbehavior += howmuch; | ||||
int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); | int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); | ||||
std::string message_prefixed = message.empty() ? "" : (": " + message); | |||||
if (state->nMisbehavior >= banscore && | if (state->nMisbehavior >= banscore && | ||||
state->nMisbehavior - howmuch < banscore) { | state->nMisbehavior - howmuch < banscore) { | ||||
LogPrintf( | LogPrint(BCLog::NET, | ||||
"%s: %s peer=%d (%d -> %d) reason: %s BAN THRESHOLD EXCEEDED\n", | "%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED%s\n", | ||||
__func__, state->name, pnode, state->nMisbehavior - howmuch, | __func__, state->name, pnode, state->nMisbehavior - howmuch, | ||||
state->nMisbehavior, reason.c_str()); | state->nMisbehavior, message_prefixed); | ||||
state->fShouldBan = true; | state->fShouldBan = true; | ||||
} else { | } else { | ||||
LogPrintf("%s: %s peer=%d (%d -> %d) reason: %s\n", __func__, | LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, | ||||
state->name, pnode, state->nMisbehavior - howmuch, | state->name, pnode, state->nMisbehavior - howmuch, | ||||
state->nMisbehavior, reason.c_str()); | state->nMisbehavior, message_prefixed); | ||||
} | } | ||||
} | } | ||||
// 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(CNode *node, int howmuch, const std::string &message) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | ||||
Misbehaving(node->GetId(), howmuch, reason); | Misbehaving(node->GetId(), howmuch, message); | ||||
} | } | ||||
////////////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////////////// | ||||
// | // | ||||
// blockchain -> download logic notification | // blockchain -> download logic notification | ||||
// | // | ||||
// To prevent fingerprinting attacks, only send blocks/headers outside of the | // To prevent fingerprinting attacks, only send blocks/headers outside of the | ||||
▲ Show 20 Lines • Show All 574 Lines • ▼ Show 20 Lines | |||||
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, | ||||
LogPrintf( | strprintf("Peer sent us a getblocktxn with " | ||||
"Peer %d sent us a getblocktxn with out-of-bounds tx indices\n", | "out-of-bounds tx indices")); | ||||
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, | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | const CBlockIndex *pindexLast = nullptr; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
uint256 hashLastBlock; | uint256 hashLastBlock; | ||||
for (const CBlockHeader &header : headers) { | for (const CBlockHeader &header : headers) { | ||||
if (!hashLastBlock.IsNull() && | if (!hashLastBlock.IsNull() && | ||||
header.hashPrevBlock != hashLastBlock) { | header.hashPrevBlock != hashLastBlock) { | ||||
Misbehaving(pfrom, 20, "disconnected-header"); | Misbehaving(pfrom, 20, "non-continuous headers sequence"); | ||||
return error("non-continuous headers sequence"); | return false; | ||||
} | } | ||||
hashLastBlock = header.GetHash(); | hashLastBlock = header.GetHash(); | ||||
} | } | ||||
// If we don't have the last header, then they'll have given us | // If we don't have the last header, then they'll have given us | ||||
// something new (if these headers are valid). | // something new (if these headers are valid). | ||||
if (!LookupBlockIndex(hashLastBlock)) { | if (!LookupBlockIndex(hashLastBlock)) { | ||||
received_new_header = true; | received_new_header = true; | ||||
} | } | ||||
} | } | ||||
CValidationState state; | CValidationState state; | ||||
CBlockHeader first_invalid_header; | CBlockHeader first_invalid_header; | ||||
if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast, | if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast, | ||||
&first_invalid_header)) { | &first_invalid_header)) { | ||||
int nDoS; | int nDoS; | ||||
if (state.IsInvalid(nDoS)) { | if (state.IsInvalid(nDoS)) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (nDoS > 0) { | if (nDoS > 0) { | ||||
Misbehaving(pfrom, nDoS, state.GetRejectReason()); | Misbehaving(pfrom, nDoS, | ||||
strprintf("invalid header received (%s)", | |||||
state.GetRejectReason())); | |||||
} else { | |||||
LogPrint(BCLog::NET, "peer=%d: invalid header received (%s)\n", | |||||
pfrom->GetId(), state.GetRejectReason()); | |||||
} | } | ||||
if (punish_duplicate_invalid && | if (punish_duplicate_invalid && | ||||
LookupBlockIndex(first_invalid_header.GetHash())) { | LookupBlockIndex(first_invalid_header.GetHash())) { | ||||
// Goal: don't allow outbound peers to use up our outbound | // Goal: don't allow outbound peers to use up our outbound | ||||
// connection slots if they are on incompatible chains. | // connection slots if they are on incompatible chains. | ||||
// | // | ||||
// We ask the caller to set punish_invalid appropriately based | // We ask the caller to set punish_invalid appropriately based | ||||
// on the peer and the method of header delivery (compact blocks | // on the peer and the method of header delivery (compact blocks | ||||
Show All 20 Lines | if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast, | ||||
// TODO: update the DoS logic (or, rather, rewrite the | // TODO: update the DoS logic (or, rather, rewrite the | ||||
// DoS-interface between validation and net_processing) so that | // DoS-interface between validation and net_processing) so that | ||||
// the interface is cleaner, and so that we disconnect on all | // the interface is cleaner, and so that we disconnect on all | ||||
// the reasons that a peer's headers chain is incompatible with | // the reasons that a peer's headers chain is incompatible with | ||||
// ours (eg block->nVersion softforks, MTP violations, etc), and | // ours (eg block->nVersion softforks, MTP violations, etc), and | ||||
// not just the duplicate-invalid case. | // not just the duplicate-invalid case. | ||||
pfrom->fDisconnect = true; | pfrom->fDisconnect = true; | ||||
} | } | ||||
return error("invalid header received"); | 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, | ||||
▲ Show 20 Lines • Show All 464 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::ADDR) { | ||||
// 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 (vAddr.size() > 1000) { | if (vAddr.size() > 1000) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, "oversized-addr"); | Misbehaving(pfrom, 20, | ||||
return error("message addr size() = %u", vAddr.size()); | strprintf("oversized-addr: message addr size() = %u", | ||||
vAddr.size())); | |||||
return false; | |||||
} | } | ||||
// Store the new addresses | // Store the new addresses | ||||
std::vector<CAddress> vAddrOk; | std::vector<CAddress> vAddrOk; | ||||
int64_t nNow = GetAdjustedTime(); | int64_t nNow = GetAdjustedTime(); | ||||
int64_t nSince = nNow - 10 * 60; | int64_t nSince = nNow - 10 * 60; | ||||
for (CAddress &addr : vAddr) { | for (CAddress &addr : vAddr) { | ||||
if (interruptMsgProc) { | if (interruptMsgProc) { | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::SENDCMPCT) { | ||||
} | } | ||||
} | } | ||||
else if (strCommand == NetMsgType::INV) { | else if (strCommand == 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, | ||||
return error("message inv size() = %u", vInv.size()); | strprintf("oversized-inv: message inv size() = %u", | ||||
vInv.size())); | |||||
return false; | |||||
} | } | ||||
bool fBlocksOnly = !fRelayTxes; | bool fBlocksOnly = !fRelayTxes; | ||||
// 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->fWhitelisted && | if (pfrom->fWhitelisted && | ||||
gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) { | gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) { | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::INV) { | ||||
} | } | ||||
} | } | ||||
else if (strCommand == NetMsgType::GETDATA) { | else if (strCommand == 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, | ||||
return error("message getdata size() = %u", vInv.size()); | strprintf("too-many-inv: message getdata size() = %u", | ||||
vInv.size())); | |||||
return false; | |||||
} | } | ||||
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()); | ||||
▲ Show 20 Lines • Show All 450 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { | ||||
} | } | ||||
const CBlockIndex *pindex = nullptr; | const CBlockIndex *pindex = nullptr; | ||||
CValidationState state; | CValidationState state; | ||||
if (!ProcessNewBlockHeaders(config, {cmpctblock.header}, state, | if (!ProcessNewBlockHeaders(config, {cmpctblock.header}, state, | ||||
&pindex)) { | &pindex)) { | ||||
int nDoS; | int nDoS; | ||||
if (state.IsInvalid(nDoS)) { | if (state.IsInvalid(nDoS)) { | ||||
const std::string logMessage = strprintf( | |||||
"Peer %d sent us invalid header via cmpctblock (%s)", | |||||
pfrom->GetId(), state.GetRejectReason()); | |||||
if (nDoS > 0) { | if (nDoS > 0) { | ||||
LogPrintf("Peer %d sent us invalid header via cmpctblock\n", | |||||
pfrom->GetId()); | |||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, nDoS, state.GetRejectReason()); | Misbehaving(pfrom, nDoS, logMessage); | ||||
} else { | } else { | ||||
LogPrint(BCLog::NET, | LogPrint(BCLog::NET, logMessage); | ||||
"Peer %d sent us invalid header via cmpctblock\n", | |||||
pfrom->GetId()); | |||||
} | } | ||||
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 | ||||
// dummy (empty) BLOCKTXN message, to re-use the logic there in | // dummy (empty) BLOCKTXN message, to re-use the logic there in | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) { | ||||
PartiallyDownloadedBlock &partialBlock = | PartiallyDownloadedBlock &partialBlock = | ||||
*(*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, | ||||
LogPrintf("Peer %d sent us invalid compact block\n", | strprintf("invalid-cmpctblk: Peer sent us " | ||||
pfrom->GetId()); | "invalid compact block")); | ||||
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)); | ||||
▲ Show 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && | ||||
} | } | ||||
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( | ||||
LogPrintf("Peer %d sent us invalid compact block/non-matching " | pfrom, 100, | ||||
"block transactions\n", | strprintf("invalid-cmpctblk-txns: Peer sent us invalid " | ||||
pfrom->GetId()); | "compact block/non-matching block transactions")); | ||||
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 { | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) { | else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) { | ||||
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); | ||||
if (nCount > MAX_HEADERS_RESULTS) { | if (nCount > MAX_HEADERS_RESULTS) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
Misbehaving(pfrom, 20, "too-many-headers"); | Misbehaving(pfrom, 20, | ||||
return error("headers message size = %u", nCount); | strprintf("too-many-headers: headers message size = %u", | ||||
nCount)); | |||||
return false; | |||||
} | } | ||||
headers.resize(nCount); | headers.resize(nCount); | ||||
for (unsigned int n = 0; n < nCount; n++) { | for (unsigned int n = 0; n < nCount; n++) { | ||||
vRecv >> headers[n]; | vRecv >> headers[n]; | ||||
// Ignore tx count; assume it is 0. | // Ignore tx count; assume it is 0. | ||||
ReadCompactSize(vRecv); | ReadCompactSize(vRecv); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,319 Lines • Show Last 20 Lines |