|
|
|
|
@@ -35,17 +35,34 @@
|
|
|
|
|
# error "Bitcoin cannot be compiled without assertions."
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
|
|
|
|
|
bool g_enable_bip61 = DEFAULT_ENABLE_BIP61;
|
|
|
|
|
|
|
|
|
|
struct IteratorComparator
|
|
|
|
|
{
|
|
|
|
|
template<typename I>
|
|
|
|
|
bool operator()(const I& a, const I& b) const
|
|
|
|
|
{
|
|
|
|
|
return &(*a) < &(*b);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
/** Expiration time for orphan transactions in seconds */
|
|
|
|
|
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;
|
|
|
|
|
/** 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
|
|
|
|
|
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header
|
|
|
|
|
/** Protect at least this many outbound peers from disconnection due to slow/
|
|
|
|
|
* behind headers chain.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
|
|
|
|
|
/** Timeout for (unprotected) outbound peers to sync to our chainwork, in seconds */
|
|
|
|
|
static constexpr int64_t CHAIN_SYNC_TIMEOUT = 20 * 60; // 20 minutes
|
|
|
|
|
/** How frequently to check for stale tips, in seconds */
|
|
|
|
|
static constexpr int64_t STALE_CHECK_INTERVAL = 10 * 60; // 10 minutes
|
|
|
|
|
/** How frequently to check for extra outbound peers and disconnect, in seconds */
|
|
|
|
|
static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
|
|
|
|
|
/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
|
|
|
|
|
static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
|
|
|
|
|
/** SHA256("main address relay")[0:8] */
|
|
|
|
|
static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL;
|
|
|
|
|
/// Age after which a stale block will no longer be served if requested as
|
|
|
|
|
/// protection against fingerprinting. Set to one month, denominated in seconds.
|
|
|
|
|
static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
|
|
|
|
|
/// Age after which a block is considered historical for purposes of rate
|
|
|
|
|
/// limiting block relay. Set to one week, denominated in seconds.
|
|
|
|
|
static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
|
|
|
|
|
|
|
|
|
|
struct COrphanTx {
|
|
|
|
|
// When modifying, adapt the copy of this definition in tests/DoS_tests.
|
|
|
|
|
@@ -55,21 +72,11 @@ struct COrphanTx {
|
|
|
|
|
};
|
|
|
|
|
static CCriticalSection g_cs_orphans;
|
|
|
|
|
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
|
|
|
|
|
std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
|
|
|
|
|
|
|
|
|
|
void EraseOrphansFor(NodeId peer);
|
|
|
|
|
|
|
|
|
|
static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
|
|
|
|
|
static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
|
|
|
|
|
|
|
|
|
|
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
|
|
|
|
|
|
|
|
|
|
/// Age after which a stale block will no longer be served if requested as
|
|
|
|
|
/// protection against fingerprinting. Set to one month, denominated in seconds.
|
|
|
|
|
static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
|
|
|
|
|
|
|
|
|
|
/// Age after which a block is considered historical for purposes of rate
|
|
|
|
|
/// limiting block relay. Set to one week, denominated in seconds.
|
|
|
|
|
static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
|
|
|
|
|
/** Increase a node's misbehavior score. */
|
|
|
|
|
void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
|
|
|
|
|
|
|
|
|
|
// Internal stuff
|
|
|
|
|
namespace {
|
|
|
|
|
@@ -137,10 +144,24 @@ namespace {
|
|
|
|
|
MapRelay mapRelay;
|
|
|
|
|
/** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
|
|
|
|
|
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
|
|
|
|
|
|
|
|
|
|
std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
|
|
|
|
|
|
|
|
|
|
struct IteratorComparator
|
|
|
|
|
{
|
|
|
|
|
template<typename I>
|
|
|
|
|
bool operator()(const I& a, const I& b) const
|
|
|
|
|
{
|
|
|
|
|
return &(*a) < &(*b);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
|
|
|
|
|
|
|
|
|
|
static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
|
|
|
|
|
static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
struct CBlockReject {
|
|
|
|
|
unsigned char chRejectCode;
|
|
|
|
|
std::string strRejectReason;
|
|
|
|
|
@@ -267,10 +288,10 @@ struct CNodeState {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Map maintaining per-node state. Requires cs_main. */
|
|
|
|
|
std::map<NodeId, CNodeState> mapNodeState;
|
|
|
|
|
static std::map<NodeId, CNodeState> mapNodeState;
|
|
|
|
|
|
|
|
|
|
// Requires cs_main.
|
|
|
|
|
CNodeState *State(NodeId pnode) {
|
|
|
|
|
static CNodeState *State(NodeId pnode) {
|
|
|
|
|
std::map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
|
|
|
|
|
if (it == mapNodeState.end())
|
|
|
|
|
return nullptr;
|
|
|
|
|
@@ -809,7 +830,9 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
|
|
|
|
|
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
|
|
|
|
|
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler, bool enable_bip61)
|
|
|
|
|
: connman(connmanIn), m_stale_tip_check_time(0), m_enable_bip61(enable_bip61) {
|
|
|
|
|
|
|
|
|
|
// Initialize global variables that cannot be constructed at startup.
|
|
|
|
|
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
|
|
|
|
|
|
|
|
|
|
@@ -1534,7 +1557,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 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)
|
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
@@ -1588,7 +1611,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|
|
|
|
// Each connection can only send one version message
|
|
|
|
|
if (pfrom->nVersion != 0)
|
|
|
|
|
{
|
|
|
|
|
if (g_enable_bip61) {
|
|
|
|
|
if (enable_bip61) {
|
|
|
|
|
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
|
|
|
|
|
}
|
|
|
|
|
LOCK(cs_main);
|
|
|
|
|
@@ -1619,7 +1642,7 @@ 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 (g_enable_bip61) {
|
|
|
|
|
if (enable_bip61) {
|
|
|
|
|
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
|
|
|
|
|
strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
|
|
|
|
|
}
|
|
|
|
|
@@ -1642,7 +1665,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|
|
|
|
{
|
|
|
|
|
// disconnect from peers older than this proto version
|
|
|
|
|
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
|
|
|
|
|
if (g_enable_bip61) {
|
|
|
|
|
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)));
|
|
|
|
|
}
|
|
|
|
|
@@ -2340,7 +2363,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 (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
@@ -2525,7 +2548,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);
|
|
|
|
|
return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman, interruptMsgProc, enable_bip61);
|
|
|
|
|
|
|
|
|
|
if (fRevertToHeaderProcessing) {
|
|
|
|
|
// Headers received from HB compact block peers are permitted to be
|
|
|
|
|
@@ -2911,12 +2934,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman)
|
|
|
|
|
static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61)
|
|
|
|
|
{
|
|
|
|
|
AssertLockHeld(cs_main);
|
|
|
|
|
CNodeState &state = *State(pnode->GetId());
|
|
|
|
|
|
|
|
|
|
if (g_enable_bip61) {
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
@@ -3018,7 +3041,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
|
|
|
|
|
bool fRet = false;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc);
|
|
|
|
|
fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman, interruptMsgProc, m_enable_bip61);
|
|
|
|
|
if (interruptMsgProc)
|
|
|
|
|
return false;
|
|
|
|
|
if (!pfrom->vRecvGetData.empty())
|
|
|
|
|
@@ -3026,7 +3049,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
|
|
|
|
|
}
|
|
|
|
|
catch (const std::ios_base::failure& e)
|
|
|
|
|
{
|
|
|
|
|
if (g_enable_bip61) {
|
|
|
|
|
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"))
|
|
|
|
|
@@ -3060,7 +3083,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCK(cs_main);
|
|
|
|
|
SendRejectsAndCheckIfBanned(pfrom, connman);
|
|
|
|
|
SendRejectsAndCheckIfBanned(pfrom, connman, m_enable_bip61);
|
|
|
|
|
|
|
|
|
|
return fMoreWork;
|
|
|
|
|
}
|
|
|
|
|
@@ -3195,6 +3218,7 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
class CompareInvMempoolOrder
|
|
|
|
|
{
|
|
|
|
|
CTxMemPool *mp;
|
|
|
|
|
@@ -3211,6 +3235,7 @@ public:
|
|
|
|
|
return mp->CompareDepthAndScore(*b, *a);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PeerLogicValidation::SendMessages(CNode* pto)
|
|
|
|
|
{
|
|
|
|
|
@@ -3256,7 +3281,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|
|
|
|
if (!lockMain)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (SendRejectsAndCheckIfBanned(pto, connman))
|
|
|
|
|
if (SendRejectsAndCheckIfBanned(pto, connman, m_enable_bip61))
|
|
|
|
|
return true;
|
|
|
|
|
CNodeState &state = *State(pto->GetId());
|
|
|
|
|
|
|
|
|
|
|