diff --git a/doc/bips.md b/doc/bips.md --- a/doc/bips.md +++ b/doc/bips.md @@ -15,7 +15,7 @@ * [`BIP 35`](https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki): The 'mempool' protocol message (and the protocol version bump to 60002) has been implemented since **v0.7.0** ([PR #1641](https://github.com/bitcoin/bitcoin/pull/1641)). * [`BIP 37`](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki): The bloom filtering for transaction relaying, partial merkle trees for blocks, and the protocol version bump to 70001 (enabling low-bandwidth SPV clients) has been implemented since **v0.8.0** ([PR #1795](https://github.com/bitcoin/bitcoin/pull/1795)). * [`BIP 42`](https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki): The bug that would have caused the subsidy schedule to resume after block 13440000 was fixed in **v0.9.2** ([PR #3842](https://github.com/bitcoin/bitcoin/pull/3842)). -* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). +* [`BIP 61`](https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki): The 'reject' protocol message (and the protocol version bump to 70002) was added in **v0.9.0** ([PR #3185](https://github.com/bitcoin/bitcoin/pull/3185)). Starting *v0.17.0*, whether to send reject messages can be configured with the `-enablebip61` option. * [`BIP 65`](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki): The CHECKLOCKTIMEVERIFY softfork was merged in **v0.12.0** ([PR #6351](https://github.com/bitcoin/bitcoin/pull/6351)), and backported to **v0.11.2** and **v0.10.4**. Mempool-only CLTV was added in [PR #6124](https://github.com/bitcoin/bitcoin/pull/6124). * [`BIP 66`](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki): The strict DER rules and associated version 3 blocks have been implemented since **v0.10.0** ([PR #5713](https://github.com/bitcoin/bitcoin/pull/5713)). * [`BIP 68`](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki): Sequence locks have been implemented as of **v0.12.1** ([PR #7184](https://github.com/bitcoin/bitcoin/pull/7184)), and have been activated since *block 419328*. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -539,6 +539,11 @@ _("Query for peer addresses via DNS lookup, if low on " "addresses (default: 1 unless -connect/-noconnect)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-enablebip61", + strprintf(_("Send reject messages per BIP61 (default: %u)"), + DEFAULT_ENABLE_BIP61), + false, OptionsCategory::CONNECTION); + gArgs.AddArg("-externalip=", _("Specify your own public address"), false, OptionsCategory::CONNECTION); gArgs.AddArg( @@ -1766,6 +1771,8 @@ // to differentiate the network nodes. nLocalServices = ServiceFlags(nLocalServices | NODE_BITCOIN_CASH); + g_enable_bip61 = gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61); + nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); return true; diff --git a/src/net_processing.h b/src/net_processing.h --- a/src/net_processing.h +++ b/src/net_processing.h @@ -58,6 +58,11 @@ */ static constexpr int64_t MINIMUM_CONNECT_TIME = 30; +/** Default for BIP61 (sending reject messages) */ +static constexpr bool DEFAULT_ENABLE_BIP61 = true; +/** Enable BIP61 (sending reject messages) */ +extern bool g_enable_bip61; + class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface { private: diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -41,6 +41,7 @@ // Used only to inform the wallet of when we last received a block. std::atomic nTimeBestReceived(0); +bool g_enable_bip61 = DEFAULT_ENABLE_BIP61; struct IteratorComparator { template bool operator()(const I &a, const I &b) { @@ -1834,11 +1835,13 @@ else if (strCommand == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom->nVersion != 0) { - connman->PushMessage( - pfrom, - CNetMsgMaker(INIT_PROTO_VERSION) - .Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, - std::string("Duplicate version message"))); + if (g_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, 1, "multiple-version"); return false; @@ -1871,12 +1874,15 @@ "(%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); - connman->PushMessage( - pfrom, - CNetMsgMaker(INIT_PROTO_VERSION) - .Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, - strprintf("Expected to offer services %08x", - GetDesirableServiceFlags(nServices)))); + if (g_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; } @@ -1886,12 +1892,14 @@ LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); - connman->PushMessage( - pfrom, - CNetMsgMaker(INIT_PROTO_VERSION) - .Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", - MIN_PEER_PROTO_VERSION))); + if (g_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; } @@ -2656,7 +2664,7 @@ tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state)); // Never send AcceptToMemoryPool's internal codes over P2P. - if (state.GetRejectCode() > 0 && + if (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { connman->PushMessage( pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, @@ -3313,12 +3321,15 @@ AssertLockHeld(cs_main); CNodeState &state = *State(pnode->GetId()); - 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)); + if (g_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(); @@ -3451,17 +3462,20 @@ fMoreWork = true; } } catch (const std::ios_base::failure &e) { - connman->PushMessage( - pfrom, CNetMsgMaker(INIT_PROTO_VERSION) - .Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, - std::string("error parsing message"))); + if (g_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()); + 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", diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -28,6 +28,10 @@ ) +REJECT_INVALID = 16 +REJECT_PUSHONLY = b'mandatory-script-verify-flag-failed (Only push operators allowed in signature scripts)' + + class InvalidTxRequestTest(BitcoinTestFramework): def set_test_params(self): @@ -78,9 +82,10 @@ # Transaction will be rejected with code 16 (REJECT_INVALID) # and we get disconnected immediately self.log.info('Test a transaction that is rejected') - tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) - node.p2p.send_txs_and_test( - [tx1], node, success=False, expect_disconnect=True) + tx1 = create_transaction( + block1.vtx[0], 0, b'\x64' * 35, 50 * COIN - 12000) + node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True, + reject_code=REJECT_INVALID, reject_reason=REJECT_PUSHONLY) # Make two p2p connections to provide the node with orphans # * p2ps[0] will send valid orphan txs (one with low fee) @@ -164,6 +169,16 @@ wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) assert_equal(expected_mempool, set(node.getrawmempool())) + # restart node with sending BIP61 messages disabled, check that it disconnects without sending the reject message + self.log.info( + 'Test a transaction that is rejected, with BIP61 disabled') + self.restart_node(0, ['-enablebip61=0', '-persistmempool=0']) + self.reconnect_p2p(num_connections=1) + node.p2p.send_txs_and_test( + [tx1], node, success=False, expect_disconnect=True) + # send_txs_and_test will have waited for disconnect, so we can safely check that no reject has been received + assert_equal(node.p2p.reject_code_received, None) + if __name__ == '__main__': InvalidTxRequestTest().main()