Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ BITCOIN_CORE_H = \
node/eviction.h \
node/miner.h \
node/minisketchwrapper.h \
node/peerman_args.h \
node/psbt.h \
node/transaction.h \
node/txreconciliation.h \
Expand Down Expand Up @@ -534,6 +535,7 @@ libbitcoin_node_a_SOURCES = \
node/interfaces.cpp \
node/miner.cpp \
node/minisketchwrapper.cpp \
node/peerman_args.cpp \
node/psbt.cpp \
node/transaction.cpp \
node/txreconciliation.cpp \
Expand Down
14 changes: 13 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@
#include <node/chainstate.h>
#include <node/context.h>
#include <node/interface_ui.h>
#include <node/kernel_notifications.h>
#include <node/mempool_args.h>
#include <node/mempool_persist_args.h>
#include <node/miner.h>
#include <node/peerman_args.h>
#include <node/txreconciliation.h>
#include <node/validation_cache_args.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
Expand Down Expand Up @@ -2130,11 +2136,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)

ChainstateManager& chainman = *Assert(node.chainman);


PeerManager::Options peerman_opts{
.ignore_incoming_txs = ignores_incoming_txs,
};
ApplyArgsManOptions(args, peerman_opts);

assert(!node.peerman);
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
chainman, *node.mempool, *node.mn_metaman, *node.mn_sync,
*node.govman, *node.sporkman, node.mn_activeman.get(), node.dmnman,
node.cj_ctx, node.llmq_ctx, ignores_incoming_txs);
node.cj_ctx, node.llmq_ctx, peerman_opts);
RegisterValidationInterface(node.peerman.get());

g_ds_notification_interface = std::make_unique<CDSNotificationInterface>(
Expand Down
102 changes: 83 additions & 19 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ class PeerManagerImpl final : public PeerManager
const std::unique_ptr<CDeterministicMNManager>& dmnman,
const std::unique_ptr<CJContext>& cj_ctx,
const std::unique_ptr<LLMQContext>& llmq_ctx,
bool ignore_incoming_txs);
Options opts);

/** Overridden from CValidationInterface. */
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override
Expand All @@ -627,8 +627,8 @@ class PeerManagerImpl final : public PeerManager
std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);;
bool IgnoresIncomingTxs() override { return m_opts.ignore_incoming_txs; }
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void PushInventory(NodeId nodeid, const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void RelayInv(CInv &inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void RelayInv(CInv &inv, const int minProtoVersion) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
Expand Down Expand Up @@ -786,8 +786,7 @@ class PeerManagerImpl final : public PeerManager
/** Next time to check for stale tip */
std::chrono::seconds m_stale_tip_check_time GUARDED_BY(cs_main){0s};

/** Whether this node is running in -blocksonly mode */
const bool m_ignore_incoming_txs;
const Options m_opts;

bool RejectIncomingTxs(const CNode& peer) const;

Expand Down Expand Up @@ -1245,7 +1244,7 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
// When in -blocksonly mode, never request high-bandwidth mode from peers. Our
// mempool will not contain the transactions necessary to reconstruct the
// compact block.
if (m_ignore_incoming_txs) return;
if (m_opts.ignore_incoming_txs) return;

CNodeState* nodestate = State(nodeid);
if (!nodestate || !nodestate->m_provides_cmpctblocks) {
Expand Down Expand Up @@ -1770,13 +1769,12 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c

void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
{
size_t max_extra_txn = gArgs.GetIntArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN);
if (max_extra_txn <= 0)
if (m_opts.max_extra_txs <= 0)
return;
if (!vExtraTxnForCompact.size())
vExtraTxnForCompact.resize(max_extra_txn);
vExtraTxnForCompact.resize(m_opts.max_extra_txs);
vExtraTxnForCompact[vExtraTxnForCompactIt] = std::make_pair(tx->GetHash(), tx);
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn;
vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % m_opts.max_extra_txs;
}

void PeerManagerImpl::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
Expand Down Expand Up @@ -1948,9 +1946,9 @@ std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams,
const CActiveMasternodeManager* const mn_activeman,
const std::unique_ptr<CDeterministicMNManager>& dmnman,
const std::unique_ptr<CJContext>& cj_ctx,
const std::unique_ptr<LLMQContext>& llmq_ctx, bool ignore_incoming_txs)
const std::unique_ptr<LLMQContext>& llmq_ctx, Options opts)
{
return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, mn_activeman, dmnman, cj_ctx, llmq_ctx, ignore_incoming_txs);
return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, mn_activeman, dmnman, cj_ctx, llmq_ctx, opts);
}

PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman, BanMan* banman,
Expand All @@ -1961,7 +1959,7 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
const std::unique_ptr<CDeterministicMNManager>& dmnman,
const std::unique_ptr<CJContext>& cj_ctx,
const std::unique_ptr<LLMQContext>& llmq_ctx,
bool ignore_incoming_txs)
Options opts)
: m_chainparams(chainparams),
m_connman(connman),
m_addrman(addrman),
Expand All @@ -1976,11 +1974,11 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_govman(govman),
m_sporkman(sporkman),
m_mn_activeman(mn_activeman),
m_ignore_incoming_txs(ignore_incoming_txs)
m_opts{opts}
{
// While Erlay support is incomplete, it must be enabled explicitly via -txreconciliation.
// This argument can go away after Erlay support is complete.
if (gArgs.GetBoolArg("-txreconciliation", DEFAULT_TXRECONCILIATION_ENABLE)) {
if (opts.reconcile_txs) {
m_txreconciliation = std::make_unique<TxReconciliationTracker>(TXRECONCILIATION_VERSION);
}
}
Expand Down Expand Up @@ -3056,7 +3054,7 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
}
if (vGetData.size() > 0) {
if (!m_ignore_incoming_txs &&
if (!m_opts.ignore_incoming_txs &&
nodestate->m_provides_cmpctblocks &&
vGetData.size() == 1 &&
mapBlocksInFlight.size() == 1 &&
Expand Down Expand Up @@ -3687,7 +3685,7 @@ void PeerManagerImpl::ProcessMessage(
// - we are not in -blocksonly mode.
const auto* tx_relay = peer->GetTxRelay();
if (tx_relay && WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs) &&
!pfrom.IsAddrFetchConn() && !m_ignore_incoming_txs) {
!pfrom.IsAddrFetchConn() && !m_opts.ignore_incoming_txs) {
const uint64_t recon_salt = m_txreconciliation->PreRegisterPeer(pfrom.GetId());
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDTXRCNCL,
TXRECONCILIATION_VERSION, recon_salt));
Expand Down Expand Up @@ -4548,6 +4546,9 @@ void PeerManagerImpl::ProcessMessage(
AddToCompactExtraTransactions(ptx);
}

// Once added to the orphan pool, a tx is considered AlreadyHave, and we shouldn't request it anymore.
m_txrequest.ForgetTxHash(tx.GetHash());

// DoS prevention: do not allow m_orphans to grow unbounded (see CVE-2012-3789)
unsigned int nMaxOrphanTxSize = (unsigned int)std::max((int64_t)0, gArgs.GetIntArg("-maxorphantxsize", DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE)) * 1000000;
unsigned int nEvicted = m_orphanage.LimitOrphans(nMaxOrphanTxSize);
Expand Down Expand Up @@ -5397,7 +5398,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
msg.m_recv.data()
);

if (gArgs.GetBoolArg("-capturemessages", false)) {
if (m_opts.capture_messages) {
CaptureMessage(pfrom->addr, msg.m_type, MakeUCharSpan(msg.m_recv), /*is_incoming=*/true);
}

Expand Down Expand Up @@ -5724,6 +5725,69 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
}
}

void PeerManagerImpl::MaybeSendSendHeaders(CNode& node, Peer& peer)
{
// Delay sending SENDHEADERS (BIP 130) until we're done with an
// initial-headers-sync with this peer. Receiving headers announcements for
// new blocks while trying to sync their headers chain is problematic,
// because of the state tracking done.
if (!peer.m_sent_sendheaders && node.GetCommonVersion() >= SENDHEADERS_VERSION) {
LOCK(cs_main);
CNodeState &state = *State(node.GetId());
if (state.pindexBestKnownBlock != nullptr &&
state.pindexBestKnownBlock->nChainWork > m_chainman.MinimumChainWork()) {
// Tell our peer we prefer to receive headers rather than inv's
// We send this to non-NODE NETWORK peers as well, because even
// non-NODE NETWORK peers can announce blocks (such as pruning
// nodes)
m_connman.PushMessage(&node, CNetMsgMaker(node.GetCommonVersion()).Make(NetMsgType::SENDHEADERS));
peer.m_sent_sendheaders = true;
}
}
}

void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::microseconds current_time)
{
if (m_opts.ignore_incoming_txs) return;
if (pto.GetCommonVersion() < FEEFILTER_VERSION) return;
// peers with the forcerelay permission should not filter txs to us
if (pto.HasPermission(NetPermissionFlags::ForceRelay)) return;
// Don't send feefilter messages to outbound block-relay-only peers since they should never announce
// transactions to us, regardless of feefilter state.
if (pto.IsBlockOnlyConn()) return;

CAmount currentFilter = m_mempool.GetMinFee().GetFeePerK();
static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};

if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
// Received tx-inv messages are discarded when the active
// chainstate is in IBD, so tell the peer to not send them.
currentFilter = MAX_MONEY;
} else {
static const CAmount MAX_FILTER{g_filter_rounder.round(MAX_MONEY)};
if (peer.m_fee_filter_sent == MAX_FILTER) {
// Send the current filter if we sent MAX_FILTER previously
// and made it out of IBD.
peer.m_next_send_feefilter = 0us;
}
}
if (current_time > peer.m_next_send_feefilter) {
CAmount filterToSend = g_filter_rounder.round(currentFilter);
// We always have a fee filter of at least the min relay fee
filterToSend = std::max(filterToSend, m_mempool.m_min_relay_feerate.GetFeePerK());
if (filterToSend != peer.m_fee_filter_sent) {
m_connman.PushMessage(&pto, CNetMsgMaker(pto.GetCommonVersion()).Make(NetMsgType::FEEFILTER, filterToSend));
peer.m_fee_filter_sent = filterToSend;
}
peer.m_next_send_feefilter = GetExponentialRand(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL);
}
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < peer.m_next_send_feefilter &&
(currentFilter < 3 * peer.m_fee_filter_sent / 4 || currentFilter > 4 * peer.m_fee_filter_sent / 3)) {
peer.m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
}
}
namespace {
class CompareInvMempoolOrder
{
Expand All @@ -5749,7 +5813,7 @@ bool PeerManagerImpl::RejectIncomingTxs(const CNode& peer) const
if (peer.IsBlockOnlyConn()) return true;
if (peer.IsFeelerConn()) return true;
// In -blocksonly mode, peers need the 'relay' permission to send txs to us
if (m_ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true;
if (m_opts.ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true;
return false;
}

Expand Down
15 changes: 14 additions & 1 deletion src/net_processing.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ class CTransaction;
struct CJContext;
struct LLMQContext;

/** Whether transaction reconciliation protocol should be enabled by default. */
static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false};
/** Default for -maxorphantxsize, maximum size in megabytes the orphan map can grow before entries are removed */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE = 10; // this allows around 100 TXs of max size (and many more of normal size)
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
static const bool DEFAULT_PEERBLOOMFILTERS = true;
Expand All @@ -54,14 +58,23 @@ struct CNodeStateStats {
class PeerManager : public CValidationInterface, public NetEventsInterface
{
public:
struct Options {
/** Whether this node is running in -blocksonly mode */
bool ignore_incoming_txs{DEFAULT_BLOCKSONLY};
bool reconcile_txs{DEFAULT_TXRECONCILIATION_ENABLE};
uint32_t max_orphan_txs{DEFAULT_MAX_ORPHAN_TRANSACTIONS};
size_t max_extra_txs{DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN};
bool capture_messages{false};
};

static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync,
CGovernanceManager& govman, CSporkManager& sporkman,
const CActiveMasternodeManager* const mn_activeman,
const std::unique_ptr<CDeterministicMNManager>& dmnman,
const std::unique_ptr<CJContext>& cj_ctx,
const std::unique_ptr<LLMQContext>& llmq_ctx, bool ignore_incoming_txs);
const std::unique_ptr<LLMQContext>& llmq_ctx, Options opts);
virtual ~PeerManager() { }

/**
Expand Down
24 changes: 24 additions & 0 deletions src/node/peerman_args.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <node/peerman_args.h>

#include <common/args.h>
#include <net_processing.h>

namespace node {

void ApplyArgsManOptions(const ArgsManager& argsman, PeerManager::Options& options)
{
if (auto value{argsman.GetBoolArg("-txreconciliation")}) options.reconcile_txs = *value;

if (auto value{argsman.GetIntArg("-maxorphantx")}) {
options.max_orphan_txs = uint32_t(std::max(int64_t{0}, *value));
}

if (auto value{argsman.GetIntArg("-blockreconstructionextratxn")}) {
options.max_extra_txs = size_t(std::max(int64_t{0}, *value));
}

if (auto value{argsman.GetBoolArg("-capturemessages")}) options.capture_messages = *value;
}

} // namespace node

12 changes: 12 additions & 0 deletions src/node/peerman_args.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef BITCOIN_NODE_PEERMAN_ARGS_H
#define BITCOIN_NODE_PEERMAN_ARGS_H

#include <net_processing.h>

class ArgsManager;

namespace node {
void ApplyArgsManOptions(const ArgsManager& argsman, PeerManager::Options& options);
} // namespace node

#endif // BITCOIN_NODE_PEERMAN_ARGS_H
2 changes: 0 additions & 2 deletions src/node/txreconciliation.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
#include <memory>
#include <tuple>

/** Whether transaction reconciliation protocol should be enabled by default. */
static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false};
/** Supported transaction reconciliation protocol version */
static constexpr uint32_t TXRECONCILIATION_VERSION{1};

Expand Down
8 changes: 4 additions & 4 deletions src/test/denialofservice_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
m_node.cj_ctx, m_node.llmq_ctx, /* ignore_incoming_txs = */ false);
m_node.cj_ctx, m_node.llmq_ctx, {});

constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
CConnman::Options options;
Expand Down Expand Up @@ -253,7 +253,7 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction)
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
m_node.cj_ctx, m_node.llmq_ctx, /* ignore_incoming_txs = */ false);
m_node.cj_ctx, m_node.llmq_ctx, {});

constexpr int max_outbound_block_relay{MAX_BLOCK_RELAY_ONLY_CONNECTIONS};
constexpr int64_t MINIMUM_CONNECT_TIME{30};
Expand Down Expand Up @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
m_node.cj_ctx, m_node.llmq_ctx, /* ignore_incoming_txs = */ false);
m_node.cj_ctx, m_node.llmq_ctx, {});

CNetAddr tor_netaddr;
BOOST_REQUIRE(
Expand Down Expand Up @@ -425,7 +425,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
m_node.cj_ctx, m_node.llmq_ctx, /* ignore_incoming_txs = */ false);
m_node.cj_ctx, m_node.llmq_ctx, {});

banman->ClearBanned();
int64_t nStartTime = GetTime();
Expand Down
Loading