Skip to content

Commit fe16dd8

Browse files
committed
net: Add option -enablebip61 to configure sending of BIP61 notifications
This commit adds a boolean option `-enablebip61`, defaulting to `1`, that can be used to disable the sending of BIP61 `reject` messages. This functionality has been requested for various reasons: - security (DoS): reject messages can reveal internal state that can be used to target certain resources such as the mempool more easily. - bandwidth: a typical node sends lots of reject messages; this counts against upstream bandwidth. Also the reject messages tend to be larger than the message that was rejected. On the other hand, reject messages can be useful while developing client software (I found them indispensable while creating bitcoin-submittx), as well as for our own test cases, so whatever the default becomes on the long run, IMO the functionality should be retained as option. But that's a discussion for later.
1 parent ffa86af commit fe16dd8

File tree

4 files changed

+39
-10
lines changed

4 files changed

+39
-10
lines changed

src/init.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ void SetupServerArgs()
395395
gArgs.AddArg("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"), false, OptionsCategory::CONNECTION);
396396
gArgs.AddArg("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION);
397397
gArgs.AddArg("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)"), false, OptionsCategory::CONNECTION);
398+
gArgs.AddArg("-enablebip61", strprintf(_("Send reject messages per BIP61 (default: %u)"), DEFAULT_ENABLE_BIP61), false, OptionsCategory::CONNECTION);
398399
gArgs.AddArg("-externalip=<ip>", _("Specify your own public address"), false, OptionsCategory::CONNECTION);
399400
gArgs.AddArg("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED), false, OptionsCategory::CONNECTION);
400401
gArgs.AddArg("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"), false, OptionsCategory::CONNECTION);
@@ -1099,6 +1100,8 @@ bool AppInitParameterInteraction()
10991100
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
11001101
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
11011102

1103+
g_enable_bip61 = gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61);
1104+
11021105
if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
11031106
return InitError("rpcserialversion must be non-negative.");
11041107

src/net_processing.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#endif
3838

3939
std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
40+
bool g_enable_bip61 = DEFAULT_ENABLE_BIP61;
4041

4142
struct IteratorComparator
4243
{
@@ -1579,7 +1580,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
15791580
// Each connection can only send one version message
15801581
if (pfrom->nVersion != 0)
15811582
{
1582-
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
1583+
if (g_enable_bip61) {
1584+
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")));
1585+
}
15831586
LOCK(cs_main);
15841587
Misbehaving(pfrom->GetId(), 1);
15851588
return false;
@@ -1608,8 +1611,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
16081611
if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices))
16091612
{
16101613
LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices));
1611-
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
1612-
strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
1614+
if (g_enable_bip61) {
1615+
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD,
1616+
strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices))));
1617+
}
16131618
pfrom->fDisconnect = true;
16141619
return false;
16151620
}
@@ -1629,8 +1634,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
16291634
{
16301635
// disconnect from peers older than this proto version
16311636
LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion);
1632-
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
1633-
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
1637+
if (g_enable_bip61) {
1638+
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
1639+
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)));
1640+
}
16341641
pfrom->fDisconnect = true;
16351642
return false;
16361643
}
@@ -2328,9 +2335,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
23282335
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
23292336
pfrom->GetId(),
23302337
FormatStateMessage(state));
2331-
if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
2338+
if (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P
23322339
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
23332340
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
2341+
}
23342342
if (nDoS > 0) {
23352343
Misbehaving(pfrom->GetId(), nDoS);
23362344
}
@@ -2903,8 +2911,10 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman)
29032911
AssertLockHeld(cs_main);
29042912
CNodeState &state = *State(pnode->GetId());
29052913

2906-
for (const CBlockReject& reject : state.rejects) {
2907-
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
2914+
if (g_enable_bip61) {
2915+
for (const CBlockReject& reject : state.rejects) {
2916+
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock));
2917+
}
29082918
}
29092919
state.rejects.clear();
29102920

@@ -3011,7 +3021,9 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
30113021
}
30123022
catch (const std::ios_base::failure& e)
30133023
{
3014-
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
3024+
if (g_enable_bip61) {
3025+
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
3026+
}
30153027
if (strstr(e.what(), "end of data"))
30163028
{
30173029
// Allow exceptions from under-length message on vRecv

src/net_processing.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45;
3535
/** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */
3636
static constexpr int64_t MINIMUM_CONNECT_TIME = 30;
3737

38+
/** Default for BIP61 (sending reject messages) */
39+
static constexpr bool DEFAULT_ENABLE_BIP61 = true;
40+
/** Enable BIP61 (sending reject messages) */
41+
extern bool g_enable_bip61;
42+
3843
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
3944
private:
4045
CConnman* const connman;

test/functional/p2p_invalid_tx.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
)
2222

2323

24+
REJECT_INVALID = 16
25+
2426
class InvalidTxRequestTest(BitcoinTestFramework):
2527
def set_test_params(self):
2628
self.num_nodes = 1
@@ -71,7 +73,7 @@ def run_test(self):
7173
# and we get disconnected immediately
7274
self.log.info('Test a transaction that is rejected')
7375
tx1 = create_transaction(block1.vtx[0], 0, b'\x64' * 35, 50 * COIN - 12000)
74-
node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True)
76+
node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True, reject_code=REJECT_INVALID, reject_reason=b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)')
7577

7678
# Make two p2p connections to provide the node with orphans
7779
# * p2ps[0] will send valid orphan txs (one with low fee)
@@ -137,6 +139,13 @@ def run_test(self):
137139
wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected
138140
assert_equal(expected_mempool, set(node.getrawmempool()))
139141

142+
# restart node with sending BIP61 messages disabled, check that it disconnects without sending the reject message
143+
self.log.info('Test a transaction that is rejected, with BIP61 disabled')
144+
self.restart_node(0, ['-enablebip61=0','-persistmempool=0'])
145+
self.reconnect_p2p(num_connections=1)
146+
node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True)
147+
# send_txs_and_test will have waited for disconnect, so we can safely check that no reject has been received
148+
assert_equal(node.p2p.reject_code_received, None)
140149

141150
if __name__ == '__main__':
142151
InvalidTxRequestTest().main()

0 commit comments

Comments
 (0)