aboutsummaryrefslogtreecommitdiff
path: root/src/net_processing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net_processing.cpp')
-rw-r--r--src/net_processing.cpp345
1 files changed, 148 insertions, 197 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 34d349e8e9..783404bcec 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -7,7 +7,6 @@
#include <addrman.h>
#include <banman.h>
-#include <arith_uint256.h>
#include <blockencodings.h>
#include <chainparams.h>
#include <consensus/validation.h>
@@ -30,6 +29,7 @@
#include <util/validation.h>
#include <memory>
+#include <typeinfo>
#if defined(NDEBUG)
# error "Bitcoin cannot be compiled without assertions."
@@ -39,6 +39,8 @@
static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
/** Minimum time between orphan transactions expire time checks in seconds */
static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
+/** How long to cache transactions in mapRelay for normal relay */
+static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME{15 * 60};
/** Headers download timeout expressed in microseconds
* Timeout = base + per_header * (expected number of headers) */
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
@@ -117,8 +119,8 @@ namespace {
int nSyncStarted GUARDED_BY(cs_main) = 0;
/**
- * Sources of received blocks, saved to be able to send them reject
- * messages or ban them when processing happens afterwards.
+ * Sources of received blocks, saved to be able punish them when processing
+ * happens afterwards.
* Set mapBlockSource[hash].second to false if the node should not be
* punished if the block is invalid.
*/
@@ -193,12 +195,6 @@ namespace {
} // namespace
namespace {
-struct CBlockReject {
- unsigned char chRejectCode;
- std::string strRejectReason;
- uint256 hashBlock;
-};
-
/**
* Maintain validation-specific state about nodes, protected by cs_main, instead
* by CNode's own locks. This simplifies asynchronous operation, where
@@ -216,8 +212,6 @@ struct CNodeState {
bool fShouldBan;
//! String name of this peer (debugging/logging purposes).
const std::string name;
- //! List of asynchronously-determined block rejections to notify this peer about.
- std::vector<CBlockReject> rejects;
//! The best known block we know this peer has announced.
const CBlockIndex *pindexBestKnownBlock;
//! The hash of the last unknown block this peer has announced.
@@ -989,14 +983,12 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
* banning/disconnecting us. We use this to determine which unaccepted
* transactions from a whitelisted peer that we can safely relay.
*/
-static bool TxRelayMayResultInDisconnect(const CValidationState& state)
-{
- assert(IsTransactionReason(state.GetReason()));
- return state.GetReason() == ValidationInvalidReason::CONSENSUS;
+static bool TxRelayMayResultInDisconnect(const TxValidationState& state) {
+ return state.GetResult() == TxValidationResult::TX_CONSENSUS;
}
/**
- * Potentially ban a node based on the contents of a CValidationState object
+ * Potentially ban a node based on the contents of a BlockValidationState object
*
* @param[in] via_compact_block: this bool is passed in because net_processing should
* punish peers differently depending on whether the data was provided in a compact
@@ -1004,23 +996,21 @@ static bool TxRelayMayResultInDisconnect(const CValidationState& state)
* txs, the peer should not be punished. See BIP 152.
*
* @return Returns true if the peer was punished (probably disconnected)
- *
- * Changes here may need to be reflected in TxRelayMayResultInDisconnect().
*/
-static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
- switch (state.GetReason()) {
- case ValidationInvalidReason::NONE:
+static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = "") {
+ switch (state.GetResult()) {
+ case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
// The node is providing invalid data:
- case ValidationInvalidReason::CONSENSUS:
- case ValidationInvalidReason::BLOCK_MUTATED:
+ case BlockValidationResult::BLOCK_CONSENSUS:
+ case BlockValidationResult::BLOCK_MUTATED:
if (!via_compact_block) {
LOCK(cs_main);
Misbehaving(nodeid, 100, message);
return true;
}
break;
- case ValidationInvalidReason::CACHED_INVALID:
+ case BlockValidationResult::BLOCK_CACHED_INVALID:
{
LOCK(cs_main);
CNodeState *node_state = State(nodeid);
@@ -1036,30 +1026,24 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
}
break;
}
- case ValidationInvalidReason::BLOCK_INVALID_HEADER:
- case ValidationInvalidReason::BLOCK_CHECKPOINT:
- case ValidationInvalidReason::BLOCK_INVALID_PREV:
+ case BlockValidationResult::BLOCK_INVALID_HEADER:
+ case BlockValidationResult::BLOCK_CHECKPOINT:
+ case BlockValidationResult::BLOCK_INVALID_PREV:
{
LOCK(cs_main);
Misbehaving(nodeid, 100, message);
}
return true;
// Conflicting (but not necessarily invalid) data or different policy:
- case ValidationInvalidReason::BLOCK_MISSING_PREV:
+ case BlockValidationResult::BLOCK_MISSING_PREV:
{
// TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
LOCK(cs_main);
Misbehaving(nodeid, 10, message);
}
return true;
- case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
- case ValidationInvalidReason::BLOCK_TIME_FUTURE:
- case ValidationInvalidReason::TX_NOT_STANDARD:
- case ValidationInvalidReason::TX_MISSING_INPUTS:
- case ValidationInvalidReason::TX_PREMATURE_SPEND:
- case ValidationInvalidReason::TX_WITNESS_MUTATED:
- case ValidationInvalidReason::TX_CONFLICT:
- case ValidationInvalidReason::TX_MEMPOOL_POLICY:
+ case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
+ case BlockValidationResult::BLOCK_TIME_FUTURE:
break;
}
if (message != "") {
@@ -1068,6 +1052,39 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
return false;
}
+/**
+ * Potentially ban a node based on the contents of a TxValidationState object
+ *
+ * @return Returns true if the peer was punished (probably disconnected)
+ *
+ * Changes here may need to be reflected in TxRelayMayResultInDisconnect().
+ */
+static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "") {
+ switch (state.GetResult()) {
+ case TxValidationResult::TX_RESULT_UNSET:
+ break;
+ // The node is providing invalid data:
+ case TxValidationResult::TX_CONSENSUS:
+ {
+ LOCK(cs_main);
+ Misbehaving(nodeid, 100, message);
+ return true;
+ }
+ // Conflicting (but not necessarily invalid) data or different policy:
+ case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
+ case TxValidationResult::TX_NOT_STANDARD:
+ case TxValidationResult::TX_MISSING_INPUTS:
+ case TxValidationResult::TX_PREMATURE_SPEND:
+ case TxValidationResult::TX_WITNESS_MUTATED:
+ case TxValidationResult::TX_CONFLICT:
+ case TxValidationResult::TX_MEMPOOL_POLICY:
+ break;
+ }
+ if (message != "") {
+ LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message);
+ }
+ return false;
+}
@@ -1093,8 +1110,9 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler &scheduler, bool enable_bip61)
- : connman(connmanIn), m_banman(banman), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, BanMan* banman, CScheduler& scheduler)
+ : connman(connmanIn), m_banman(banman), m_stale_tip_check_time(0)
+{
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -1235,19 +1253,18 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
* Handle invalid block rejection and consequent peer banning, maintain which
* peers announce compact blocks.
*/
-void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) {
+void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) {
LOCK(cs_main);
const uint256 hash(block.GetHash());
std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
- if (state.IsInvalid()) {
- // Don't send reject message with code 0 or an internal reject code.
- if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
- CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
- State(it->second.first)->rejects.push_back(reject);
- MaybePunishNode(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);
- }
+ // If the block failed validation, we know where it came from and we're still connected
+ // to that peer, maybe punish.
+ if (state.IsInvalid() &&
+ it != mapBlockSource.end() &&
+ State(it->second.first)) {
+ MaybePunishNodeForBlock(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);
}
// Check that:
// 1. The block is valid
@@ -1324,7 +1341,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma
// Relay to a limited number of other nodes
// Use deterministic randomness to send to the same nodes for 24 hours
- // at a time so the addrKnowns of the chosen nodes prevent repeats
+ // at a time so the m_addr_knowns of the chosen nodes prevent repeats
uint64_t hashAddr = addr.GetHash();
const CSipHasher hasher = connman->GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
FastRandomContext insecure_rand;
@@ -1385,7 +1402,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
}
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
- CValidationState state;
+ BlockValidationState state;
if (!ActivateBestChain(state, Params(), a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
}
@@ -1522,6 +1539,10 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
// messages from this peer (likely resulting in our peer eventually
// disconnecting us).
if (pfrom->m_tx_relay != nullptr) {
+ // mempool entries added before this time have likely expired from mapRelay
+ const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
+ const std::chrono::seconds mempool_req = pfrom->m_tx_relay->m_last_mempool_req.load();
+
LOCK(cs_main);
while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
@@ -1541,11 +1562,15 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
if (mi != mapRelay.end()) {
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
push = true;
- } else if (pfrom->m_tx_relay->timeLastMempoolReq) {
+ } else {
auto txinfo = mempool.info(inv.hash);
// To protect privacy, do not answer getdata using the mempool when
- // that TX couldn't have been INVed in reply to a MEMPOOL request.
- if (txinfo.tx && txinfo.nTime <= pfrom->m_tx_relay->timeLastMempoolReq) {
+ // that TX couldn't have been INVed in reply to a MEMPOOL request,
+ // or when it's too recent to have expired from mapRelay.
+ if (txinfo.tx && (
+ (mempool_req.count() && txinfo.m_time <= mempool_req)
+ || (txinfo.m_time <= longlived_mempool_time)))
+ {
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
push = true;
}
@@ -1673,11 +1698,10 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
}
- CValidationState state;
- CBlockHeader first_invalid_header;
- if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
+ BlockValidationState state;
+ if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
if (state.IsInvalid()) {
- MaybePunishNode(pfrom->GetId(), state, via_compact_block, "invalid header received");
+ MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, "invalid header received");
return false;
}
}
@@ -1813,14 +1837,13 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
const CTransactionRef porphanTx = orphan_it->second.tx;
const CTransaction& orphanTx = *porphanTx;
NodeId fromPeer = orphan_it->second.fromPeer;
- bool fMissingInputs2 = false;
- // Use a new CValidationState because orphans come from different peers (and we call
- // MaybePunishNode based on the source peer from the orphan map, not based on the peer
+ // Use a new TxValidationState because orphans come from different peers (and we call
+ // MaybePunishNodeForTx based on the source peer from the orphan map, not based on the peer
// that relayed the previous transaction).
- CValidationState orphan_state;
+ TxValidationState orphan_state;
if (setMisbehaving.count(fromPeer)) continue;
- if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &fMissingInputs2, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanHash, *connman);
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
@@ -1833,10 +1856,10 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
}
EraseOrphanTx(orphanHash);
done = true;
- } else if (!fMissingInputs2) {
+ } else if (orphan_state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
if (orphan_state.IsInvalid()) {
// Punish peer that gave us an invalid orphan tx
- if (MaybePunishNode(fromPeer, orphan_state, /*via_compact_block*/ false)) {
+ if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
setMisbehaving.insert(fromPeer);
}
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
@@ -1844,8 +1867,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
- assert(IsTransactionReason(orphan_state.GetReason()));
- if (!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
+ if (!orphanTx.HasWitness() && orphan_state.GetResult() != TxValidationResult::TX_WITNESS_MUTATED) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
@@ -1859,7 +1881,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
}
}
-bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc, bool enable_bip61)
+bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -1883,38 +1905,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
}
- if (strCommand == NetMsgType::REJECT)
- {
- if (LogAcceptCategory(BCLog::NET)) {
- try {
- std::string strMsg; unsigned char ccode; std::string strReason;
- vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
-
- std::ostringstream ss;
- ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
-
- if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX)
- {
- uint256 hash;
- vRecv >> hash;
- ss << ": hash " << hash.ToString();
- }
- LogPrint(BCLog::NET, "Reject %s\n", SanitizeString(ss.str()));
- } catch (const std::ios_base::failure&) {
- // Avoid feedback loops by preventing reject messages from triggering a new reject message.
- LogPrint(BCLog::NET, "Unparseable reject message received\n");
- }
- }
- return true;
- }
-
if (strCommand == NetMsgType::VERSION) {
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
- if (enable_bip61) {
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
- }
LOCK(cs_main);
Misbehaving(pfrom->GetId(), 1);
return false;
@@ -1942,10 +1936,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
{
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
- if (enable_bip61) {
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
- strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
- }
pfrom->fDisconnect = true;
return false;
}
@@ -1953,10 +1943,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (nVersion < MIN_PEER_PROTO_VERSION) {
// disconnect from peers older than this proto version
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
- if (enable_bip61) {
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
- strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
- }
pfrom->fDisconnect = true;
return false;
}
@@ -2171,7 +2157,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr);
- if (g_banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
+ if (banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
@@ -2326,7 +2312,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_most_recent_block);
a_recent_block = most_recent_block;
}
- CValidationState state;
+ BlockValidationState state;
if (!ActivateBestChain(state, Params(), a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
}
@@ -2506,8 +2492,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK2(cs_main, g_cs_orphans);
- bool fMissingInputs = false;
- CValidationState state;
+ TxValidationState state;
CNodeState* nodestate = State(pfrom->GetId());
nodestate->m_tx_download.m_tx_announced.erase(inv.hash);
@@ -2517,7 +2502,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
std::list<CTransactionRef> lRemovedTxn;
if (!AlreadyHave(inv) &&
- AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ AcceptToMemoryPool(mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
mempool.check(&::ChainstateActive().CoinsTip());
RelayTransaction(tx.GetHash(), *connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
@@ -2539,7 +2524,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// Recursively process any orphan transactions that depended on this one
ProcessOrphanTx(connman, pfrom->orphan_work_set, lRemovedTxn);
}
- else if (fMissingInputs)
+ else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected
for (const CTxIn& txin : tx.vin) {
@@ -2572,8 +2557,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
recentRejects->insert(tx.GetHash());
}
} else {
- assert(IsTransactionReason(state.GetReason()));
- if (!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
+ if (!tx.HasWitness() && state.GetResult() != TxValidationResult::TX_WITNESS_MUTATED) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
@@ -2628,11 +2612,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom->GetId(),
FormatStateMessage(state));
- if (enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
- connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
- state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
- }
- MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false);
+ MaybePunishNodeForTx(pfrom->GetId(), state);
}
return true;
}
@@ -2666,10 +2646,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
const CBlockIndex *pindex = nullptr;
- CValidationState state;
+ BlockValidationState state;
if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
if (state.IsInvalid()) {
- MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
+ MaybePunishNodeForBlock(pfrom->GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
return true;
}
}
@@ -2811,7 +2791,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
} // cs_main
if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc, enable_bip61);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, banman, interruptMsgProc);
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -2909,11 +2889,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// been run). This is handled below, so just treat this as
// though the block was successfully read, and rely on the
// handling in ProcessNewBlock to ensure the block index is
- // updated, reject messages go out, etc.
+ // updated, etc.
MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
fBlockRead = true;
- // mapBlockSource is only used for sending reject messages and DoS scores,
- // so the race between here and cs_main in ProcessNewBlock is fine.
+ // mapBlockSource is used for potentially punishing peers and
+ // updating which peers send us compact blocks, so the race
+ // between here and cs_main in ProcessNewBlock is fine.
// BIP 152 permits peers to relay compact blocks after validating
// the header only; we should not punish peers if the block turns
// out to be invalid.
@@ -2985,8 +2966,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
// 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.
forceProcessing |= MarkBlockAsReceived(hash);
- // mapBlockSource is only used for sending reject messages and DoS scores,
- // so the race between here and cs_main in ProcessNewBlock is fine.
+ // mapBlockSource is only used for punishing peers and setting
+ // which peers send us compact blocks, so the race between here and
+ // cs_main in ProcessNewBlock is fine.
mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
}
bool fNewBlock = false;
@@ -3027,7 +3009,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
std::vector<CAddress> vAddr = connman->GetAddresses();
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
- if (!g_banman->IsBanned(addr)) {
+ if (!banman->IsBanned(addr)) {
pfrom->PushAddress(addr, insecure_rand);
}
}
@@ -3238,18 +3220,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
-bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool PeerLogicValidation::CheckIfBanned(CNode* pnode)
{
AssertLockHeld(cs_main);
CNodeState &state = *State(pnode->GetId());
- if (enable_bip61) {
- for (const CBlockReject& reject : state.rejects) {
- connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
- }
- }
- state.rejects.clear();
-
if (state.fShouldBan) {
state.fShouldBan = false;
if (pnode->HasPermission(PF_NOBAN))
@@ -3316,41 +3291,37 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return false;
// Just take one message
msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin());
- pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE;
+ pfrom->nProcessQueueSize -= msgs.front().m_raw_message_size;
pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize();
fMoreWork = !pfrom->vProcessMsg.empty();
}
CNetMessage& msg(msgs.front());
msg.SetVersion(pfrom->GetRecvVersion());
- // Scan for message start
- if (memcmp(msg.hdr.pchMessageStart, chainparams.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
- LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->GetId());
+ // Check network magic
+ if (!msg.m_valid_netmagic) {
+ LogPrint(BCLog::NET, "PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
pfrom->fDisconnect = true;
return false;
}
- // Read header
- CMessageHeader& hdr = msg.hdr;
- if (!hdr.IsValid(chainparams.MessageStart()))
+ // Check header
+ if (!msg.m_valid_header)
{
- LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->GetId());
+ LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
return fMoreWork;
}
- std::string strCommand = hdr.GetCommand();
+ const std::string& strCommand = msg.m_command;
// Message size
- unsigned int nMessageSize = hdr.nMessageSize;
+ unsigned int nMessageSize = msg.m_message_size;
// Checksum
- CDataStream& vRecv = msg.vRecv;
- const uint256& hash = msg.GetMessageHash();
- if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0)
+ CDataStream& vRecv = msg.m_recv;
+ if (!msg.m_valid_checksum)
{
- LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__,
- SanitizeString(strCommand), nMessageSize,
- HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE),
- HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE));
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR peer=%d\n", __func__,
+ SanitizeString(strCommand), nMessageSize, pfrom->GetId());
return fMoreWork;
}
@@ -3358,40 +3329,15 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc, m_enable_bip61);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.m_time, chainparams, connman, m_banman, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
fMoreWork = true;
- }
- catch (const std::ios_base::failure& e)
- {
- if (m_enable_bip61) {
- connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
- }
- if (strstr(e.what(), "end of data")) {
- // Allow exceptions from under-length message on vRecv
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- } else if (strstr(e.what(), "size too large")) {
- // Allow exceptions from over-long size
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- } else if (strstr(e.what(), "non-canonical ReadCompactSize()")) {
- // Allow exceptions from non-canonical encoding
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- } else if (strstr(e.what(), "Superfluous witness record")) {
- // Allow exceptions from illegal witness encoding
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- } else if (strstr(e.what(), "Unknown transaction optional data")) {
- // Allow exceptions from unknown witness encoding
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
- } else {
- PrintExceptionContinue(&e, "ProcessMessages()");
- }
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "ProcessMessages()");
+ } catch (const std::exception& e) {
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what(), typeid(e).name());
} catch (...) {
- PrintExceptionContinue(nullptr, "ProcessMessages()");
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(strCommand), nMessageSize);
}
if (!fRet) {
@@ -3399,7 +3345,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
LOCK(cs_main);
- SendRejectsAndCheckIfBanned(pfrom, m_enable_bip61);
+ CheckIfBanned(pfrom);
return fMoreWork;
}
@@ -3598,15 +3544,18 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
}
- TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
+ TRY_LOCK(cs_main, lockMain);
if (!lockMain)
return true;
- if (SendRejectsAndCheckIfBanned(pto, m_enable_bip61)) return true;
+ if (CheckIfBanned(pto)) return true;
+
CNodeState &state = *State(pto->GetId());
// Address refresh broadcast
int64_t nNow = GetTimeMicros();
+ auto current_time = GetTime<std::chrono::microseconds>();
+
if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
AdvertiseLocal(pto);
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
@@ -3619,11 +3568,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
+ assert(pto->m_addr_known);
for (const CAddress& addr : pto->vAddrToSend)
{
- if (!pto->addrKnown.contains(addr.GetKey()))
+ if (!pto->m_addr_known->contains(addr.GetKey()))
{
- pto->addrKnown.insert(addr.GetKey());
+ pto->m_addr_known->insert(addr.GetKey());
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
@@ -3827,13 +3777,13 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LOCK(pto->m_tx_relay->cs_tx_inventory);
// Check whether periodic sends should happen
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
- if (pto->m_tx_relay->nNextInvSend < nNow) {
+ if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
if (pto->fInbound) {
- pto->m_tx_relay->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
+ pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)};
} else {
// Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->m_tx_relay->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
+ pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1});
}
}
@@ -3847,10 +3797,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
auto vtxinfo = mempool.infoAll();
pto->m_tx_relay->fSendMempool = false;
- CAmount filterrate = 0;
+ CFeeRate filterrate;
{
LOCK(pto->m_tx_relay->cs_feeFilter);
- filterrate = pto->m_tx_relay->minFeeFilter;
+ filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter);
}
LOCK(pto->m_tx_relay->cs_filter);
@@ -3859,9 +3809,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
const uint256& hash = txinfo.tx->GetHash();
CInv inv(MSG_TX, hash);
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
- if (filterrate) {
- if (txinfo.feeRate.GetFeePerK() < filterrate)
- continue;
+ // Don't send transactions that peers will not put into their mempool
+ if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
+ continue;
}
if (pto->m_tx_relay->pfilter) {
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
@@ -3873,7 +3823,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
vInv.clear();
}
}
- pto->m_tx_relay->timeLastMempoolReq = GetTime();
+ pto->m_tx_relay->m_last_mempool_req = GetTime<std::chrono::seconds>();
}
// Determine transactions to relay
@@ -3884,10 +3834,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
vInvTx.push_back(it);
}
- CAmount filterrate = 0;
+ CFeeRate filterrate;
{
LOCK(pto->m_tx_relay->cs_feeFilter);
- filterrate = pto->m_tx_relay->minFeeFilter;
+ filterrate = CFeeRate(pto->m_tx_relay->minFeeFilter);
}
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
// A heap is used so that not all items need sorting if only a few are being sent.
@@ -3914,7 +3864,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
if (!txinfo.tx) {
continue;
}
- if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
+ // Peer told you to not send transactions at that feerate? Don't bother sending it.
+ if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
continue;
}
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
@@ -3931,7 +3882,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
if (ret.second) {
- vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
+ vRelayExpiration.push_back(std::make_pair(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret.first));
}
}
if (vInv.size() == MAX_INV_SZ) {
@@ -3947,7 +3898,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
- const auto current_time = GetTime<std::chrono::microseconds>();
+ current_time = GetTime<std::chrono::microseconds>();
// nNow is the current system time (GetTimeMicros is not mockable) and
// should be replaced by the mockable current_time eventually
nNow = GetTimeMicros();