mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-31 10:41:08 +00:00
The tests were written assuming transaction orphans would persist for a time beyond the test peer's disconnection. After #31829 this no longer holds, so as a minimal fix we modify the test to wait until the orphans are removed before continuing with the final transaction submissions.
148 lines
6.3 KiB
Python
Executable File
148 lines
6.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2024-present 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 that 1p1c package submission allows a 1p1c package to propagate in a "network" of nodes. Send
|
|
various packages from different nodes on a network in which some nodes have already received some of
|
|
the transactions (and submitted them to mempool, kept them as orphans or rejected them as
|
|
too-low-feerate transactions). The packages should be received and accepted by all nodes.
|
|
"""
|
|
|
|
from decimal import Decimal
|
|
|
|
from test_framework.mempool_util import (
|
|
DEFAULT_MIN_RELAY_TX_FEE,
|
|
)
|
|
from test_framework.messages import (
|
|
COIN,
|
|
msg_tx,
|
|
)
|
|
from test_framework.p2p import (
|
|
P2PInterface,
|
|
)
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.util import (
|
|
assert_equal,
|
|
)
|
|
from test_framework.wallet import (
|
|
MiniWallet,
|
|
MiniWalletMode,
|
|
)
|
|
|
|
class PackageRelayTest(BitcoinTestFramework):
|
|
def set_test_params(self):
|
|
self.setup_clean_chain = True
|
|
self.num_nodes = 4
|
|
# hugely speeds up the test, as it involves multiple hops of tx relay.
|
|
self.noban_tx_relay = True
|
|
|
|
def create_basic_1p1c(self, wallet):
|
|
low_fee_parent = wallet.create_self_transfer(fee_rate=0, confirmed_only=True)
|
|
high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*Decimal(DEFAULT_MIN_RELAY_TX_FEE)/ COIN)
|
|
package_hex_basic = [low_fee_parent["hex"], high_fee_child["hex"]]
|
|
return package_hex_basic, low_fee_parent["tx"], high_fee_child["tx"]
|
|
|
|
def create_package_2outs(self, wallet):
|
|
# First create a tester tx to see the vsize, and then adjust the fees
|
|
utxo_for_2outs = wallet.get_utxo(confirmed_only=True)
|
|
|
|
low_fee_parent_2outs = wallet.create_self_transfer_multi(
|
|
utxos_to_spend=[utxo_for_2outs],
|
|
num_outputs=2,
|
|
fee_per_output=0,
|
|
)
|
|
|
|
# Now create the child
|
|
high_fee_child_2outs = wallet.create_self_transfer_multi(
|
|
utxos_to_spend=low_fee_parent_2outs["new_utxos"][::-1],
|
|
fee_per_output=10_000,
|
|
)
|
|
return [low_fee_parent_2outs["hex"], high_fee_child_2outs["hex"]], low_fee_parent_2outs["tx"], high_fee_child_2outs["tx"]
|
|
|
|
def create_package_2p1c(self, wallet):
|
|
parent1 = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN * 10, confirmed_only=True)
|
|
parent2 = wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN * 20, confirmed_only=True)
|
|
child = wallet.create_self_transfer_multi(
|
|
utxos_to_spend=[parent1["new_utxo"], parent2["new_utxo"]],
|
|
fee_per_output=999*parent1["tx"].get_vsize(),
|
|
)
|
|
return [parent1["hex"], parent2["hex"], child["hex"]], parent1["tx"], parent2["tx"], child["tx"]
|
|
|
|
def create_packages(self):
|
|
# 1: Basic 1-parent-1-child package, parent 0sat/vB, child 999sat/vB
|
|
package_hex_1, parent_1, child_1 = self.create_basic_1p1c(self.wallet)
|
|
|
|
# 2: same as 1, parent's txid is the same as its wtxid.
|
|
package_hex_2, parent_2, child_2 = self.create_basic_1p1c(self.wallet_nonsegwit)
|
|
|
|
# 3: 2-parent-1-child package. Both parents are above mempool min feerate. No package submission happens.
|
|
# We require packages to be child-with-parents and only allow 1-parent-1-child packages.
|
|
package_hex_3, parent_31, _parent_32, child_3 = self.create_package_2p1c(self.wallet)
|
|
|
|
# 4: parent + child package where the child spends 2 different outputs from the parent.
|
|
package_hex_4, parent_4, child_4 = self.create_package_2outs(self.wallet)
|
|
|
|
# Assemble return results
|
|
packages_to_submit = [package_hex_1, package_hex_2, package_hex_3, package_hex_4]
|
|
# node0: sender
|
|
# node1: pre-received the children (orphans, will be dropped on peer disconnect)
|
|
# node3: pre-received the parents (too low fee)
|
|
# All nodes receive parent_31 ahead of time.
|
|
txns_to_send = [
|
|
[],
|
|
[child_1, child_2, parent_31, child_3, child_4],
|
|
[parent_31],
|
|
[parent_1, parent_2, parent_31, parent_4]
|
|
]
|
|
|
|
return packages_to_submit, txns_to_send
|
|
|
|
def run_test(self):
|
|
self.wallet = MiniWallet(self.nodes[1])
|
|
self.wallet_nonsegwit = MiniWallet(self.nodes[2], mode=MiniWalletMode.RAW_P2PK)
|
|
self.generate(self.wallet_nonsegwit, 10)
|
|
self.generate(self.wallet, 120)
|
|
|
|
# Create the transactions.
|
|
self.wallet.rescan_utxos(include_mempool=True)
|
|
packages_to_submit, transactions_to_presend = self.create_packages()
|
|
|
|
self.peers = [self.nodes[i].add_p2p_connection(P2PInterface()) for i in range(self.num_nodes)]
|
|
|
|
self.log.info("Pre-send some transactions to nodes")
|
|
for (i, peer) in enumerate(self.peers):
|
|
for tx in transactions_to_presend[i]:
|
|
peer.send_and_ping(msg_tx(tx))
|
|
|
|
# The fee-having parent should be the only thing in mempools
|
|
self.sync_mempools()
|
|
sufficient_parent = transactions_to_presend[2][0].txid_hex
|
|
for i, node in enumerate(self.nodes):
|
|
# node1 has non-empty orphanage as well
|
|
if i == 1:
|
|
assert_equal(len(self.nodes[i].getorphantxs()), 4)
|
|
else:
|
|
assert_equal(self.nodes[i].getorphantxs(), [])
|
|
|
|
assert_equal(node.getrawmempool(), [sufficient_parent])
|
|
|
|
# Disconnect python peers to clear outstanding orphan requests with them, avoiding timeouts.
|
|
# We are only interested in the syncing behavior between real nodes.
|
|
for i in range(self.num_nodes):
|
|
self.nodes[i].disconnect_p2ps()
|
|
self.wait_until(lambda: len(self.nodes[i].getorphantxs()) == 0)
|
|
|
|
self.log.info("Submit full packages to node0")
|
|
for package_hex in packages_to_submit:
|
|
submitpackage_result = self.nodes[0].submitpackage(package_hex)
|
|
assert_equal(submitpackage_result["package_msg"], "success")
|
|
|
|
self.log.info("Wait for mempools to sync")
|
|
self.wait_until(lambda: len(self.nodes[0].getrawmempool()) == 9)
|
|
self.sync_mempools()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
PackageRelayTest(__file__).main()
|