From 87205f26b59a5ffd3940a339780c6084e47affb0 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:29:28 +0000 Subject: [PATCH] merge bitcoin#21327: ignore transactions while in IBD `p2p_ibd_txrelay.py` was introduced in bitcoin#19423 but not backported as Dash doesn't have feefilter capabilities but this backport has the test check for additional cases which are within Dash's capabilities, so the test has been committed in with the feefilter portions minimally stripped out --- src/net_processing.cpp | 5 ++ test/functional/p2p_ibd_txrelay.py | 78 +++++++++++++++++++++++++++ test/functional/test_framework/p2p.py | 2 + test/functional/test_runner.py | 1 + 4 files changed, 86 insertions(+) create mode 100755 test/functional/p2p_ibd_txrelay.py diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 8d6489d5c6..99f793881c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -4053,6 +4053,11 @@ void PeerManagerImpl::ProcessMessage( } if (msg_type == NetMsgType::TX || msg_type == NetMsgType::DSTX) { + // Stop processing the transaction early if we are still in IBD since we don't + // have enough information to validate it yet. Sending unsolicited transactions + // is not considered a protocol violation, so don't punish the peer. + if (m_chainman.ActiveChainstate().IsInitialBlockDownload()) return; + CTransactionRef ptx; CCoinJoinBroadcastTx dstx; int nInvType = MSG_TX; diff --git a/test/functional/p2p_ibd_txrelay.py b/test/functional/p2p_ibd_txrelay.py new file mode 100755 index 0000000000..1af89d6a2f --- /dev/null +++ b/test/functional/p2p_ibd_txrelay.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test transaction relay behavior during IBD: +- Don't request transactions +- Ignore all transaction messages +""" + +from decimal import Decimal +import time + +from test_framework.messages import ( + CInv, + COIN, + CTransaction, + from_hex, + msg_inv, + msg_tx, + MSG_TX, +) +from test_framework.p2p import ( + NONPREF_PEER_TX_DELAY, + P2PDataStore, + P2PInterface, + p2p_lock +) +from test_framework.test_framework import BitcoinTestFramework + +NORMAL_FEE_FILTER = Decimal(100) / COIN + +class P2PIBDTxRelayTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.disable_mocktime = True + self.num_nodes = 2 + self.extra_args = [ + ["-minrelaytxfee={}".format(NORMAL_FEE_FILTER)], + ["-minrelaytxfee={}".format(NORMAL_FEE_FILTER)], + ] + + def run_test(self): + self.log.info("Check that nodes don't send getdatas for transactions while still in IBD") + peer_inver = self.nodes[0].add_p2p_connection(P2PDataStore()) + txid = 0xdeadbeef + peer_inver.send_and_ping(msg_inv([CInv(t=MSG_TX, h=txid)])) + # The node should not send a getdata, but if it did, it would first delay 2 seconds + self.nodes[0].setmocktime(int(time.time() + NONPREF_PEER_TX_DELAY)) + peer_inver.sync_send_with_ping() + with p2p_lock: + assert txid not in peer_inver.getdata_requests + self.nodes[0].disconnect_p2ps() + + self.log.info("Check that nodes don't process unsolicited transactions while still in IBD") + # A transaction hex pulled from tx_valid.json. There are no valid transactions since no UTXOs + # exist yet, but it should be a well-formed transaction. + rawhex = "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff473" + \ + "04402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e168" + \ + "1a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696a" + \ + "d990364e555c271ad504b88ac00000000" + assert self.nodes[1].decoderawtransaction(rawhex) # returns a dict, should not throw + tx = from_hex(CTransaction(), rawhex) + peer_txer = self.nodes[0].add_p2p_connection(P2PInterface()) + with self.nodes[0].assert_debug_log(expected_msgs=["received: tx"], unexpected_msgs=["was not accepted"]): + peer_txer.send_and_ping(msg_tx(tx)) + self.nodes[0].disconnect_p2ps() + + # Come out of IBD by generating a block + self.nodes[0].generate(1) + self.sync_all() + + self.log.info("Check that nodes process the same transaction, even when unsolicited, when no longer in IBD") + peer_txer = self.nodes[0].add_p2p_connection(P2PInterface()) + with self.nodes[0].assert_debug_log(expected_msgs=["was not accepted"]): + peer_txer.send_and_ping(msg_tx(tx)) + +if __name__ == '__main__': + P2PIBDTxRelayTest().main() diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 4e88e5268a..1c9e9a2958 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -99,6 +99,8 @@ P2P_SERVICES = NODE_NETWORK | NODE_HEADERS_COMPRESSED P2P_SUBVERSION = "/python-p2p-tester:0.0.3%s/" # Value for relay that this test framework sends in its `version` message P2P_VERSION_RELAY = 1 +# Delay after receiving a tx inv before requesting transactions from non-preferred peers, in seconds +NONPREF_PEER_TX_DELAY = 2 MESSAGEMAP = { b"addr": msg_addr, diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 80bd7abfb7..4ecef85f37 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -306,6 +306,7 @@ BASE_SCRIPTS = [ 'rpc_estimatefee.py', 'p2p_unrequested_blocks.py', # NOTE: needs dash_hash to pass 'feature_shutdown.py', + 'p2p_ibd_txrelay.py', 'rpc_coinjoin.py', 'rpc_masternode.py', 'rpc_mnauth.py',