mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-30 00:38:12 +02:00
Merge bitcoin/bitcoin#27675: p2p: Drop m_recently_announced_invs bloom filter
fb02ba3c5fmempool_entry: improve struct packing (Anthony Towns)1a118062fbnet_processing: Clean up INVENTORY_BROADCAST_MAX constants (Anthony Towns)6fa49937e4test: Check tx from disconnected block is immediately requestable (glozow)e4ffabbffanet_processing: don't add txids to m_tx_inventory_known_filter (Anthony Towns)6ec1809d33net_processing: drop m_recently_announced_invs bloom filter (Anthony Towns)a70beafdb2validation: when adding txs due to a block reorg, allow immediate relay (Anthony Towns)1e9684f39fmempool_entry: add mempool entry sequence number (Anthony Towns) Pull request description: This PR replaces the `m_recently_announced_invs` bloom filter with a simple sequence number tracking the mempool state when we last considered sending an INV message to a node. This saves 33kB per peer (or more if we raise the rate at which we relay transactions over the network, in which case we would need to increase the size of the bloom filter proportionally). The philosophy here (compare with #18861 and #19109) is that we consider the rate limiting on INV messages to only be about saving bandwidth and not protecting privacy, and therefore after you receive an INV message, it's immediately fair game to request any transaction that was in the mempool at the time the INV message was sent. We likewise consider the BIP 133 feefilter and BIP 37 bloom filters to be bandwidth optimisations here, and treat transactions as requestable if they would have been announced without those filters. Given that philosophy, tracking the timestamp of the last INV message and comparing that against the mempool entry time allows removal of each of `m_recently_announced_invs`, `m_last_mempool_req` and `UNCONDITIONAL_RELAY_DELAY` and associated logic. ACKs for top commit: naumenkogs: ACKfb02ba3c5famitiuttarwar: review ACKfb02ba3c5fglozow: reACKfb02ba3c5fTree-SHA512: cbba5ee04c86df26b6057f3654c00a2b45ec94d354f4f157a769cecdaa0b509edaac02b3128afba39b023e82473fc5e28c915a787f84457ffe66638c6ac9c2d4
This commit is contained in:
@@ -8,6 +8,17 @@ Test re-org scenarios with a mempool that contains transactions
|
||||
that spend (directly or indirectly) coinbase transactions.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from test_framework.messages import (
|
||||
CInv,
|
||||
MSG_WTX,
|
||||
msg_getdata,
|
||||
)
|
||||
from test_framework.p2p import (
|
||||
P2PTxInvStore,
|
||||
p2p_lock,
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal, assert_raises_rpc_error
|
||||
from test_framework.wallet import MiniWallet
|
||||
@@ -22,8 +33,84 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
|
||||
[]
|
||||
]
|
||||
|
||||
def test_reorg_relay(self):
|
||||
self.log.info("Test that transactions from disconnected blocks are available for relay immediately")
|
||||
# Prevent time from moving forward
|
||||
self.nodes[1].setmocktime(int(time.time()))
|
||||
self.connect_nodes(0, 1)
|
||||
self.generate(self.wallet, 3)
|
||||
|
||||
# Disconnect node0 and node1 to create different chains.
|
||||
self.disconnect_nodes(0, 1)
|
||||
# Connect a peer to node1, which doesn't have immediate tx relay
|
||||
peer1 = self.nodes[1].add_p2p_connection(P2PTxInvStore())
|
||||
|
||||
# Create a transaction that is included in a block.
|
||||
tx_disconnected = self.wallet.send_self_transfer(from_node=self.nodes[1])
|
||||
self.generate(self.nodes[1], 1, sync_fun=self.no_op)
|
||||
|
||||
# Create a transaction and submit it to node1's mempool.
|
||||
tx_before_reorg = self.wallet.send_self_transfer(from_node=self.nodes[1])
|
||||
|
||||
# Create a child of that transaction and submit it to node1's mempool.
|
||||
tx_child = self.wallet.send_self_transfer(utxo_to_spend=tx_disconnected["new_utxo"], from_node=self.nodes[1])
|
||||
assert_equal(self.nodes[1].getmempoolentry(tx_child["txid"])["ancestorcount"], 1)
|
||||
assert_equal(len(peer1.get_invs()), 0)
|
||||
|
||||
# node0 has a longer chain in which tx_disconnected was not confirmed.
|
||||
self.generate(self.nodes[0], 3, sync_fun=self.no_op)
|
||||
|
||||
# Reconnect the nodes and sync chains. node0's chain should win.
|
||||
self.connect_nodes(0, 1)
|
||||
self.sync_blocks()
|
||||
|
||||
# Child now has an ancestor from the disconnected block
|
||||
assert_equal(self.nodes[1].getmempoolentry(tx_child["txid"])["ancestorcount"], 2)
|
||||
assert_equal(self.nodes[1].getmempoolentry(tx_before_reorg["txid"])["ancestorcount"], 1)
|
||||
|
||||
# peer1 should not have received an inv for any of the transactions during this time, as not
|
||||
# enough time has elapsed for those transactions to be announced. Likewise, it cannot
|
||||
# request very recent, unanounced transactions.
|
||||
assert_equal(len(peer1.get_invs()), 0)
|
||||
# It's too early to request these two transactions
|
||||
requests_too_recent = msg_getdata([CInv(t=MSG_WTX, h=int(tx["tx"].getwtxid(), 16)) for tx in [tx_before_reorg, tx_child]])
|
||||
peer1.send_and_ping(requests_too_recent)
|
||||
for _ in range(len(requests_too_recent.inv)):
|
||||
peer1.sync_with_ping()
|
||||
with p2p_lock:
|
||||
assert "tx" not in peer1.last_message
|
||||
assert "notfound" in peer1.last_message
|
||||
|
||||
# Request the tx from the disconnected block
|
||||
request_disconnected_tx = msg_getdata([CInv(t=MSG_WTX, h=int(tx_disconnected["tx"].getwtxid(), 16))])
|
||||
peer1.send_and_ping(request_disconnected_tx)
|
||||
|
||||
# The tx from the disconnected block was never announced, and it entered the mempool later
|
||||
# than the transactions that are too recent.
|
||||
assert_equal(len(peer1.get_invs()), 0)
|
||||
with p2p_lock:
|
||||
# However, the node will answer requests for the tx from the recently-disconnected block.
|
||||
assert_equal(peer1.last_message["tx"].tx.getwtxid(),tx_disconnected["tx"].getwtxid())
|
||||
|
||||
self.nodes[1].setmocktime(int(time.time()) + 30)
|
||||
peer1.sync_with_ping()
|
||||
# the transactions are now announced
|
||||
assert_equal(len(peer1.get_invs()), 3)
|
||||
for _ in range(3):
|
||||
# make sure all tx requests have been responded to
|
||||
peer1.sync_with_ping()
|
||||
last_tx_received = peer1.last_message["tx"]
|
||||
|
||||
tx_after_reorg = self.wallet.send_self_transfer(from_node=self.nodes[1])
|
||||
request_after_reorg = msg_getdata([CInv(t=MSG_WTX, h=int(tx_after_reorg["tx"].getwtxid(), 16))])
|
||||
assert tx_after_reorg["txid"] in self.nodes[1].getrawmempool()
|
||||
peer1.send_and_ping(request_after_reorg)
|
||||
with p2p_lock:
|
||||
assert_equal(peer1.last_message["tx"], last_tx_received)
|
||||
|
||||
def run_test(self):
|
||||
wallet = MiniWallet(self.nodes[0])
|
||||
self.wallet = MiniWallet(self.nodes[0])
|
||||
wallet = self.wallet
|
||||
|
||||
# Start with a 200 block chain
|
||||
assert_equal(self.nodes[0].getblockcount(), 200)
|
||||
@@ -103,6 +190,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
|
||||
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
||||
self.sync_all()
|
||||
|
||||
self.test_reorg_relay()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
MempoolCoinbaseTest().main()
|
||||
|
||||
Reference in New Issue
Block a user