Skip to content

Commit 67c0d93

Browse files
committed
Merge bitcoin#29827: test: p2p: add test for rejected tx request logic (m_recent_rejects filter)
60ca5d5 test: p2p: add test for rejected tx request logic (`m_recent_rejects` filter) (Sebastian Falbesoner) e9dc511 fixup: get all utxos up front in fill_mempool, discourage wallet mixing (glozow) Pull request description: Motivated by the discussion in bitcoin#28970 (bitcoin#28970 (comment)), this PR adds test coverage for the logic around the `m_recent_rejects` filter, in particular that the filter is cleared after a new block comes in: https://github.com/bitcoin/bitcoin/blob/f0794cbd405636a7f528a60f2873050b865cf7e8/src/net_processing.cpp#L2199-L2206 As expected, the second part of the test fails if the following patch is applied: ```diff diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6996af38cb..5cb1090e70 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2202,7 +2202,7 @@ bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid) // or a double-spend. Reset the rejects filter and give those // txs a second chance. hashRecentRejectsChainTip = m_chainman.ActiveChain().Tip()->GetBlockHash(); - m_recent_rejects.reset(); + //m_recent_rejects.reset(); } const uint256& hash = gtxid.GetHash(); ``` I'm still not sure in which file this test fits best, and if there is already test coverage for the first part of the test somewhere. Happy for any suggestions. ACKs for top commit: maflcko: ACK 60ca5d5 🍳 glozow: code review ACK 60ca5d5 instagibbs: ACK 60ca5d5 Tree-SHA512: 9cab43858e8f84db04a708151e6775c9cfc68c20ff53096220eac0b2c406f31aaf9223e8e04be345e95bf0a3f6dd15efac50b0ebeb1582a48a4560b3ab0bcba5
2 parents c05c214 + 60ca5d5 commit 67c0d93

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

test/functional/p2p_tx_download.py

+29-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66
Test transaction download behavior
77
"""
8+
from decimal import Decimal
89
import time
910

1011
from test_framework.messages import (
@@ -14,6 +15,7 @@
1415
MSG_WTX,
1516
msg_inv,
1617
msg_notfound,
18+
msg_tx,
1719
)
1820
from test_framework.p2p import (
1921
P2PInterface,
@@ -22,6 +24,7 @@
2224
from test_framework.test_framework import BitcoinTestFramework
2325
from test_framework.util import (
2426
assert_equal,
27+
fill_mempool,
2528
)
2629
from test_framework.wallet import MiniWallet
2730

@@ -54,6 +57,7 @@ def on_getdata(self, message):
5457
class TxDownloadTest(BitcoinTestFramework):
5558
def set_test_params(self):
5659
self.num_nodes = 2
60+
self.extra_args= [['-datacarriersize=100000', '-maxmempool=5', '-persistmempool=0']] * self.num_nodes
5761

5862
def test_tx_requests(self):
5963
self.log.info("Test that we request transactions from all our peers, eventually")
@@ -241,6 +245,29 @@ def test_spurious_notfound(self):
241245
self.log.info('Check that spurious notfound is ignored')
242246
self.nodes[0].p2ps[0].send_message(msg_notfound(vec=[CInv(MSG_TX, 1)]))
243247

248+
def test_rejects_filter_reset(self):
249+
self.log.info('Check that rejected tx is not requested again')
250+
node = self.nodes[0]
251+
fill_mempool(self, node, self.wallet)
252+
self.wallet.rescan_utxos()
253+
mempoolminfee = node.getmempoolinfo()['mempoolminfee']
254+
peer = node.add_p2p_connection(TestP2PConn())
255+
low_fee_tx = self.wallet.create_self_transfer(fee_rate=Decimal("0.9")*mempoolminfee)
256+
assert_equal(node.testmempoolaccept([low_fee_tx['hex']])[0]["reject-reason"], "mempool min fee not met")
257+
peer.send_and_ping(msg_tx(low_fee_tx['tx']))
258+
peer.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=int(low_fee_tx['wtxid'], 16))]))
259+
node.setmocktime(int(time.time()))
260+
node.bumpmocktime(MAX_GETDATA_INBOUND_WAIT)
261+
peer.sync_with_ping()
262+
assert_equal(peer.tx_getdata_count, 0)
263+
264+
self.log.info('Check that rejection filter is cleared after new block comes in')
265+
self.generate(self.wallet, 1, sync_fun=self.no_op)
266+
peer.sync_with_ping()
267+
peer.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=int(low_fee_tx['wtxid'], 16))]))
268+
node.bumpmocktime(MAX_GETDATA_INBOUND_WAIT)
269+
peer.wait_for_getdata([int(low_fee_tx['wtxid'], 16)])
270+
244271
def run_test(self):
245272
self.wallet = MiniWallet(self.nodes[0])
246273

@@ -257,7 +284,8 @@ def run_test(self):
257284

258285
# Run each test against new bitcoind instances, as setting mocktimes has long-term effects on when
259286
# the next trickle relay event happens.
260-
for test in [self.test_in_flight_max, self.test_inv_block, self.test_tx_requests]:
287+
for test in [self.test_in_flight_max, self.test_inv_block, self.test_tx_requests,
288+
self.test_rejects_filter_reset]:
261289
self.stop_nodes()
262290
self.start_nodes()
263291
self.connect_nodes(1, 0)

test/functional/rpc_packages.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -386,12 +386,14 @@ def test_maxfeerate_submitpackage(self):
386386
"-maxmempool=5",
387387
"-persistmempool=0",
388388
])
389+
self.wallet.rescan_utxos()
389390

390391
fill_mempool(self, node, self.wallet)
391392

392393
minrelay = node.getmempoolinfo()["minrelaytxfee"]
393394
parent = self.wallet.create_self_transfer(
394395
fee_rate=minrelay,
396+
confirmed_only=True,
395397
)
396398

397399
child = self.wallet.create_self_transfer(
@@ -411,6 +413,7 @@ def test_maxfeerate_submitpackage(self):
411413

412414
# Reset maxmempool, datacarriersize, reset dynamic mempool minimum feerate, and empty mempool.
413415
self.restart_node(0)
416+
self.wallet.rescan_utxos()
414417

415418
assert_equal(node.getrawmempool(), [])
416419

@@ -420,7 +423,10 @@ def test_maxburn_submitpackage(self):
420423
assert_equal(node.getrawmempool(), [])
421424

422425
self.log.info("Submitpackage maxburnamount arg testing")
423-
chained_txns_burn = self.wallet.create_self_transfer_chain(chain_length=2)
426+
chained_txns_burn = self.wallet.create_self_transfer_chain(
427+
chain_length=2,
428+
utxo_to_spend=self.wallet.get_utxo(confirmed_only=True),
429+
)
424430
chained_burn_hex = [t["hex"] for t in chained_txns_burn]
425431

426432
tx = tx_from_hex(chained_burn_hex[1])

test/functional/test_framework/util.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@ def fill_mempool(test_framework, node, miniwallet):
505505
It will not ensure mempools become synced as it
506506
is based on a single node and assumes -minrelaytxfee
507507
is 1 sat/vbyte.
508+
To avoid unintentional tx dependencies, it is recommended to use separate miniwallets for
509+
mempool filling vs transactions in tests.
508510
"""
509511
test_framework.log.info("Fill the mempool until eviction is triggered and the mempoolminfee rises")
510512
txouts = gen_return_txouts()
@@ -522,8 +524,14 @@ def fill_mempool(test_framework, node, miniwallet):
522524
# Mine COINBASE_MATURITY - 1 blocks so that the UTXOs are allowed to be spent
523525
test_framework.generate(node, 100 - 1)
524526

527+
# Get all UTXOs up front to ensure none of the transactions spend from each other, as that may
528+
# change their effective feerate and thus the order in which they are selected for eviction.
529+
confirmed_utxos = [miniwallet.get_utxo(confirmed_only=True) for _ in range(num_of_batches * tx_batch_size + 1)]
530+
assert_equal(len(confirmed_utxos), num_of_batches * tx_batch_size + 1)
531+
525532
test_framework.log.debug("Create a mempool tx that will be evicted")
526-
tx_to_be_evicted_id = miniwallet.send_self_transfer(from_node=node, fee_rate=relayfee)["txid"]
533+
tx_to_be_evicted_id = miniwallet.send_self_transfer(from_node=node, utxo_to_spend=confirmed_utxos[0], fee_rate=relayfee)["txid"]
534+
del confirmed_utxos[0]
527535

528536
# Increase the tx fee rate to give the subsequent transactions a higher priority in the mempool
529537
# The tx has an approx. vsize of 65k, i.e. multiplying the previous fee rate (in sats/kvB)
@@ -534,7 +542,9 @@ def fill_mempool(test_framework, node, miniwallet):
534542
with node.assert_debug_log(["rolling minimum fee bumped"]):
535543
for batch_of_txid in range(num_of_batches):
536544
fee = (batch_of_txid + 1) * base_fee
537-
create_lots_of_big_transactions(miniwallet, node, fee, tx_batch_size, txouts)
545+
utxos = confirmed_utxos[:tx_batch_size]
546+
create_lots_of_big_transactions(miniwallet, node, fee, tx_batch_size, txouts, utxos)
547+
del confirmed_utxos[:tx_batch_size]
538548

539549
test_framework.log.debug("The tx should be evicted by now")
540550
# The number of transactions created should be greater than the ones present in the mempool

0 commit comments

Comments
 (0)