aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_abortnode.py2
-rwxr-xr-xtest/functional/feature_addrman.py7
-rwxr-xr-xtest/functional/feature_asmap.py15
-rwxr-xr-xtest/functional/feature_assumeutxo.py107
-rwxr-xr-xtest/functional/feature_cltv.py3
-rwxr-xr-xtest/functional/feature_csv_activation.py3
-rwxr-xr-xtest/functional/feature_dersig.py3
-rwxr-xr-xtest/functional/feature_fee_estimation.py9
-rwxr-xr-xtest/functional/feature_index_prune.py2
-rwxr-xr-xtest/functional/feature_proxy.py88
-rwxr-xr-xtest/functional/feature_reindex_readonly.py9
-rwxr-xr-xtest/functional/interface_rest.py3
-rwxr-xr-xtest/functional/interface_zmq.py19
-rwxr-xr-xtest/functional/mempool_accept.py6
-rwxr-xr-xtest/functional/mempool_accept_v3.py183
-rwxr-xr-xtest/functional/mempool_packages.py3
-rwxr-xr-xtest/functional/p2p_feefilter.py6
-rwxr-xr-xtest/functional/p2p_filter.py3
-rwxr-xr-xtest/functional/p2p_handshake.py92
-rwxr-xr-xtest/functional/p2p_invalid_block.py3
-rwxr-xr-xtest/functional/p2p_node_network_limited.py13
-rwxr-xr-xtest/functional/p2p_permissions.py7
-rwxr-xr-xtest/functional/p2p_segwit.py4
-rwxr-xr-xtest/functional/rpc_net.py245
-rwxr-xr-xtest/functional/rpc_packages.py33
-rwxr-xr-xtest/functional/rpc_rawtransaction.py5
-rwxr-xr-xtest/functional/test_framework/messages.py1
-rw-r--r--test/functional/test_framework/netutil.py9
-rwxr-xr-xtest/functional/test_framework/test_framework.py15
-rwxr-xr-xtest/functional/test_framework/test_node.py4
-rwxr-xr-xtest/functional/test_runner.py16
-rwxr-xr-xtest/functional/wallet_abandonconflict.py3
-rwxr-xr-xtest/functional/wallet_address_types.py5
-rwxr-xr-xtest/functional/wallet_assumeutxo.py2
-rwxr-xr-xtest/functional/wallet_avoid_mixing_output_types.py4
-rwxr-xr-xtest/functional/wallet_avoidreuse.py5
-rwxr-xr-xtest/functional/wallet_backup.py13
-rwxr-xr-xtest/functional/wallet_balance.py5
-rwxr-xr-xtest/functional/wallet_basic.py4
-rwxr-xr-xtest/functional/wallet_bumpfee.py3
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py5
-rwxr-xr-xtest/functional/wallet_groups.py3
-rwxr-xr-xtest/functional/wallet_hd.py3
-rwxr-xr-xtest/functional/wallet_import_rescan.py4
-rwxr-xr-xtest/functional/wallet_importdescriptors.py5
-rwxr-xr-xtest/functional/wallet_keypool_topup.py24
-rwxr-xr-xtest/functional/wallet_listreceivedby.py2
-rwxr-xr-xtest/functional/wallet_listsinceblock.py2
-rwxr-xr-xtest/functional/wallet_listtransactions.py6
-rwxr-xr-xtest/functional/wallet_send.py7
50 files changed, 767 insertions, 256 deletions
diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py
index 740d3b7f0e..01ba2834c4 100755
--- a/test/functional/feature_abortnode.py
+++ b/test/functional/feature_abortnode.py
@@ -36,7 +36,7 @@ class AbortNodeTest(BitcoinTestFramework):
# Check that node0 aborted
self.log.info("Waiting for crash")
- self.nodes[0].wait_until_stopped(timeout=5, expect_error=True, expected_stderr="Error: A fatal internal error occurred, see debug.log for details")
+ self.nodes[0].wait_until_stopped(timeout=5, expect_error=True, expected_stderr="Error: A fatal internal error occurred, see debug.log for details: Failed to disconnect block.")
self.log.info("Node crashed - now verifying restart fails")
self.nodes[0].assert_start_raises_init_error()
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index a7ce864fde..95d33d62ea 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -156,12 +156,7 @@ class AddrmanTest(BitcoinTestFramework):
)
self.log.info("Check that missing addrman is recreated")
- self.stop_node(0)
- os.remove(peers_dat)
- with self.nodes[0].assert_debug_log([
- f'Creating peers.dat because the file was not found ("{peers_dat}")',
- ]):
- self.start_node(0)
+ self.restart_node(0, clear_addrman=True)
assert_equal(self.nodes[0].getnodeaddresses(), [])
diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py
index ae483fe449..024a8fa18c 100755
--- a/test/functional/feature_asmap.py
+++ b/test/functional/feature_asmap.py
@@ -39,11 +39,12 @@ def expected_messages(filename):
class AsmapTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [["-checkaddrman=1"]] # Do addrman checks on all operations.
+ # Do addrman checks on all operations and use deterministic addrman
+ self.extra_args = [["-checkaddrman=1", "-test=addrman"]]
def fill_addrman(self, node_id):
- """Add 1 tried address to the addrman, followed by 1 new address."""
- for addr, tried in [[0, True], [1, False]]:
+ """Add 2 tried addresses to the addrman, followed by 2 new addresses."""
+ for addr, tried in [[0, True], [1, True], [2, False], [3, False]]:
self.nodes[node_id].addpeeraddress(address=f"101.{addr}.0.0", tried=tried, port=8333)
def test_without_asmap_arg(self):
@@ -84,12 +85,12 @@ class AsmapTest(BitcoinTestFramework):
self.log.info("Test bitcoind -asmap restart with addrman containing new and tried entries")
self.stop_node(0)
shutil.copyfile(self.asmap_raw, self.default_asmap)
- self.start_node(0, ["-asmap", "-checkaddrman=1"])
+ self.start_node(0, ["-asmap", "-checkaddrman=1", "-test=addrman"])
self.fill_addrman(node_id=0)
- self.restart_node(0, ["-asmap", "-checkaddrman=1"])
+ self.restart_node(0, ["-asmap", "-checkaddrman=1", "-test=addrman"])
with self.node.assert_debug_log(
expected_msgs=[
- "CheckAddrman: new 1, tried 1, total 2 started",
+ "CheckAddrman: new 2, tried 2, total 4 started",
"CheckAddrman: completed",
]
):
@@ -114,7 +115,7 @@ class AsmapTest(BitcoinTestFramework):
def test_asmap_health_check(self):
self.log.info('Test bitcoind -asmap logs ASMap Health Check with basic stats')
shutil.copyfile(self.asmap_raw, self.default_asmap)
- msg = "ASMap Health Check: 2 clearnet peers are mapped to 1 ASNs with 0 peers being unmapped"
+ msg = "ASMap Health Check: 4 clearnet peers are mapped to 3 ASNs with 0 peers being unmapped"
with self.node.assert_debug_log(expected_msgs=[msg]):
self.start_node(0, extra_args=['-asmap'])
os.remove(self.default_asmap)
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index 60dd751ff8..3e882f47b8 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -34,6 +34,7 @@ Interesting starting states could be loading a snapshot when the current chain t
"""
from shutil import rmtree
+from dataclasses import dataclass
from test_framework.messages import tx_from_hex
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -113,6 +114,12 @@ class AssumeutxoTest(BitcoinTestFramework):
f.write(valid_snapshot_contents[(32 + 8 + offset + len(content)):])
expected_error(log_msg=f"[snapshot] bad snapshot content hash: expected a4bf3407ccb2cc0145c49ebba8fa91199f8a3903daf0883875941497d2493c27, got {wrong_hash}")
+ def test_headers_not_synced(self, valid_snapshot_path):
+ for node in self.nodes[1:]:
+ assert_raises_rpc_error(-32603, "The base block header (3bb7ce5eba0be48939b7a521ac1ba9316afee2c7bada3a0cca24188e6d7d96c0) must appear in the headers chain. Make sure all headers are syncing, and call this RPC again.",
+ node.loadtxoutset,
+ valid_snapshot_path)
+
def test_invalid_chainstate_scenarios(self):
self.log.info("Test different scenarios of invalid snapshot chainstate in datadir")
@@ -127,7 +134,7 @@ class AssumeutxoTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log([log_msg]):
self.nodes[0].assert_start_raises_init_error(expected_msg=error_msg)
- expected_error_msg = f"Error: A fatal internal error occurred, see debug.log for details"
+ expected_error_msg = f"Error: A fatal internal error occurred, see debug.log for details: Assumeutxo data not found for the given blockhash '7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a'."
error_details = f"Assumeutxo data not found for the given blockhash"
expected_error(log_msg=error_details, error_msg=expected_error_msg)
@@ -166,26 +173,28 @@ class AssumeutxoTest(BitcoinTestFramework):
for n in self.nodes:
n.setmocktime(n.getblockheader(n.getbestblockhash())['time'])
- self.sync_blocks()
-
# Generate a series of blocks that `n0` will have in the snapshot,
- # but that n1 doesn't yet see. In order for the snapshot to activate,
- # though, we have to ferry over the new headers to n1 so that it
- # isn't waiting forever to see the header of the snapshot's base block
- # while disconnected from n0.
+ # but that n1 and n2 don't yet see.
+ assert n0.getblockcount() == START_HEIGHT
+ blocks = {START_HEIGHT: Block(n0.getbestblockhash(), 1, START_HEIGHT + 1)}
for i in range(100):
+ block_tx = 1
if i % 3 == 0:
self.mini_wallet.send_self_transfer(from_node=n0)
+ block_tx += 1
self.generate(n0, nblocks=1, sync_fun=self.no_op)
- newblock = n0.getblock(n0.getbestblockhash(), 0)
+ height = n0.getblockcount()
+ hash = n0.getbestblockhash()
+ blocks[height] = Block(hash, block_tx, blocks[height-1].chain_tx + block_tx)
+ if i == 4:
+ # Create a stale block that forks off the main chain before the snapshot.
+ temp_invalid = n0.getbestblockhash()
+ n0.invalidateblock(temp_invalid)
+ stale_hash = self.generateblock(n0, output="raw(aaaa)", transactions=[], sync_fun=self.no_op)["hash"]
+ n0.invalidateblock(stale_hash)
+ n0.reconsiderblock(temp_invalid)
+ stale_block = n0.getblock(stale_hash, 0)
- # make n1 aware of the new header, but don't give it the block.
- n1.submitheader(newblock)
- n2.submitheader(newblock)
-
- # Ensure everyone is seeing the same headers.
- for n in self.nodes:
- assert_equal(n.getblockchaininfo()["headers"], SNAPSHOT_BASE_HEIGHT)
self.log.info("-- Testing assumeutxo + some indexes + pruning")
@@ -195,10 +204,27 @@ class AssumeutxoTest(BitcoinTestFramework):
self.log.info(f"Creating a UTXO snapshot at height {SNAPSHOT_BASE_HEIGHT}")
dump_output = n0.dumptxoutset('utxos.dat')
+ self.log.info("Test loading snapshot when headers are not synced")
+ self.test_headers_not_synced(dump_output['path'])
+
+ # In order for the snapshot to activate, we have to ferry over the new
+ # headers to n1 and n2 so that they see the header of the snapshot's
+ # base block while disconnected from n0.
+ for i in range(1, 300):
+ block = n0.getblock(n0.getblockhash(i), 0)
+ # make n1 and n2 aware of the new header, but don't give them the
+ # block.
+ n1.submitheader(block)
+ n2.submitheader(block)
+
+ # Ensure everyone is seeing the same headers.
+ for n in self.nodes:
+ assert_equal(n.getblockchaininfo()["headers"], SNAPSHOT_BASE_HEIGHT)
+
assert_equal(
dump_output['txoutset_hash'],
"a4bf3407ccb2cc0145c49ebba8fa91199f8a3903daf0883875941497d2493c27")
- assert_equal(dump_output["nchaintx"], 334)
+ assert_equal(dump_output["nchaintx"], blocks[SNAPSHOT_BASE_HEIGHT].chain_tx)
assert_equal(n0.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)
# Mine more blocks on top of the snapshot that n1 hasn't yet seen. This
@@ -219,6 +245,29 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT)
assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
+ def check_tx_counts(final: bool) -> None:
+ """Check nTx and nChainTx intermediate values right after loading
+ the snapshot, and final values after the snapshot is validated."""
+ for height, block in blocks.items():
+ tx = n1.getblockheader(block.hash)["nTx"]
+ chain_tx = n1.getchaintxstats(nblocks=1, blockhash=block.hash)["txcount"]
+
+ # Intermediate nTx of the starting block should be set, but nTx of
+ # later blocks should be 0 before they are downloaded.
+ if final or height == START_HEIGHT:
+ assert_equal(tx, block.tx)
+ else:
+ assert_equal(tx, 0)
+
+ # Intermediate nChainTx of the starting block and snapshot block
+ # should be set, but others should be 0 until they are downloaded.
+ if final or height in (START_HEIGHT, SNAPSHOT_BASE_HEIGHT):
+ assert_equal(chain_tx, block.chain_tx)
+ else:
+ assert_equal(chain_tx, 0)
+
+ check_tx_counts(final=False)
+
normal, snapshot = n1.getchainstates()["chainstates"]
assert_equal(normal['blocks'], START_HEIGHT)
assert_equal(normal.get('snapshot_blockhash'), None)
@@ -229,6 +278,15 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(n1.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)
+ self.log.info("Submit a stale block that forked off the chain before the snapshot")
+ # Normally a block like this would not be downloaded, but if it is
+ # submitted early before the background chain catches up to the fork
+ # point, it winds up in m_blocks_unlinked and triggers a corner case
+ # that previously crashed CheckBlockIndex.
+ n1.submitblock(stale_block)
+ n1.getchaintips()
+ n1.getblock(stale_hash)
+
self.log.info("Submit a spending transaction for a snapshot chainstate coin to the mempool")
# spend the coinbase output of the first block that is not available on node1
spend_coin_blockhash = n1.getblockhash(START_HEIGHT + 1)
@@ -266,6 +324,16 @@ class AssumeutxoTest(BitcoinTestFramework):
self.log.info("Restarted node before snapshot validation completed, reloading...")
self.restart_node(1, extra_args=self.extra_args[1])
+
+ # Send snapshot block to n1 out of order. This makes the test less
+ # realistic because normally the snapshot block is one of the last
+ # blocks downloaded, but its useful to test because it triggers more
+ # corner cases in ReceivedBlockTransactions() and CheckBlockIndex()
+ # setting and testing nChainTx values, and it exposed previous bugs.
+ snapshot_hash = n0.getblockhash(SNAPSHOT_BASE_HEIGHT)
+ snapshot_block = n0.getblock(snapshot_hash, 0)
+ n1.submitblock(snapshot_block)
+
self.connect_nodes(0, 1)
self.log.info(f"Ensuring snapshot chain syncs to tip. ({FINAL_HEIGHT})")
@@ -282,6 +350,8 @@ class AssumeutxoTest(BitcoinTestFramework):
}
self.wait_until(lambda: n1.getindexinfo() == completed_idx_state)
+ self.log.info("Re-check nTx and nChainTx values")
+ check_tx_counts(final=True)
for i in (0, 1):
n = self.nodes[i]
@@ -356,6 +426,11 @@ class AssumeutxoTest(BitcoinTestFramework):
self.connect_nodes(0, 2)
self.wait_until(lambda: n2.getblockcount() == FINAL_HEIGHT)
+@dataclass
+class Block:
+ hash: str
+ tx: int
+ chain_tx: int
if __name__ == '__main__':
AssumeutxoTest().main()
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 8c45fb5a4d..fb3f662271 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -83,9 +83,10 @@ CLTV_HEIGHT = 111
class BIP65Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [[
f'-testactivationheight=cltv@{CLTV_HEIGHT}',
- '-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact reject reason for testing
'-acceptnonstdtxn=1', # cltv_invalidate is nonstandard
]]
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 92e4187f3c..bc1f9e8f2f 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -95,8 +95,9 @@ class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [[
- '-whitelist=noban@127.0.0.1',
f'-testactivationheight=csv@{CSV_ACTIVATION_HEIGHT}',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 44c12b2a59..035e7151ca 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -47,9 +47,10 @@ DERSIG_HEIGHT = 102
class BIP66Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [[
f'-testactivationheight=dersig@{DERSIG_HEIGHT}',
- '-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact log msg for testing
]]
self.setup_clean_chain = True
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 4f56d585d3..ffc87f8b8b 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -132,11 +132,12 @@ def make_tx(wallet, utxo, feerate):
class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
- # Force fSendTrickle to true (via whitelist.noban)
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [
- ["-whitelist=noban@127.0.0.1"],
- ["-whitelist=noban@127.0.0.1", "-blockmaxweight=68000"],
- ["-whitelist=noban@127.0.0.1", "-blockmaxweight=32000"],
+ [],
+ ["-blockmaxweight=68000"],
+ ["-blockmaxweight=32000"],
]
def setup_network(self):
diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py
index d6e802b399..b3bf35b524 100755
--- a/test/functional/feature_index_prune.py
+++ b/test/functional/feature_index_prune.py
@@ -128,7 +128,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("make sure we get an init error when starting the nodes again with the indices")
filter_msg = "Error: basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"
stats_msg = "Error: coinstatsindex best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"
- end_msg = f"{os.linesep}Error: Failed to start indexes, shutting down.."
+ end_msg = f"{os.linesep}Error: A fatal internal error occurred, see debug.log for details: Failed to start indexes, shutting down.."
for i, msg in enumerate([filter_msg, stats_msg, filter_msg]):
self.nodes[i].assert_start_raises_init_error(extra_args=self.extra_args[i], expected_msg=msg+end_msg)
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 662007d65e..7a6f639021 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -17,6 +17,7 @@ Test plan:
- support no authentication (other proxy)
- support no authentication + user/pass authentication (Tor)
- proxy on IPv6
+ - proxy over unix domain sockets
- Create various proxies (as threads)
- Create nodes that connect to them
@@ -39,7 +40,9 @@ addnode connect to a CJDNS address
- Test passing unknown -onlynet
"""
+import os
import socket
+import tempfile
from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType
from test_framework.test_framework import BitcoinTestFramework
@@ -47,7 +50,7 @@ from test_framework.util import (
assert_equal,
p2p_port,
)
-from test_framework.netutil import test_ipv6_local
+from test_framework.netutil import test_ipv6_local, test_unix_socket
# Networks returned by RPC getpeerinfo.
NET_UNROUTABLE = "not_publicly_routable"
@@ -60,14 +63,17 @@ NET_CJDNS = "cjdns"
# Networks returned by RPC getnetworkinfo, defined in src/rpc/net.cpp::GetNetworksInfo()
NETWORKS = frozenset({NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS})
+# Use the shortest temp path possible since UNIX sockets may have as little as 92-char limit
+socket_path = tempfile.NamedTemporaryFile().name
class ProxyTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 5
+ self.num_nodes = 7
self.setup_clean_chain = True
def setup_nodes(self):
self.have_ipv6 = test_ipv6_local()
+ self.have_unix_sockets = test_unix_socket()
# Create two proxies on different ports
# ... one unauthenticated
self.conf1 = Socks5Configuration()
@@ -89,6 +95,15 @@ class ProxyTest(BitcoinTestFramework):
else:
self.log.warning("Testing without local IPv6 support")
+ if self.have_unix_sockets:
+ self.conf4 = Socks5Configuration()
+ self.conf4.af = socket.AF_UNIX
+ self.conf4.addr = socket_path
+ self.conf4.unauth = True
+ self.conf4.auth = True
+ else:
+ self.log.warning("Testing without local unix domain sockets support")
+
self.serv1 = Socks5Server(self.conf1)
self.serv1.start()
self.serv2 = Socks5Server(self.conf2)
@@ -96,6 +111,9 @@ class ProxyTest(BitcoinTestFramework):
if self.have_ipv6:
self.serv3 = Socks5Server(self.conf3)
self.serv3.start()
+ if self.have_unix_sockets:
+ self.serv4 = Socks5Server(self.conf4)
+ self.serv4.start()
# We will not try to connect to this.
self.i2p_sam = ('127.0.0.1', 7656)
@@ -109,10 +127,15 @@ class ProxyTest(BitcoinTestFramework):
['-listen', f'-proxy={self.conf2.addr[0]}:{self.conf2.addr[1]}','-proxyrandomize=1'],
[],
['-listen', f'-proxy={self.conf1.addr[0]}:{self.conf1.addr[1]}','-proxyrandomize=1',
- '-cjdnsreachable']
+ '-cjdnsreachable'],
+ [],
+ []
]
if self.have_ipv6:
args[3] = ['-listen', f'-proxy=[{self.conf3.addr[0]}]:{self.conf3.addr[1]}','-proxyrandomize=0', '-noonion']
+ if self.have_unix_sockets:
+ args[5] = ['-listen', f'-proxy=unix:{socket_path}']
+ args[6] = ['-listen', f'-onion=unix:{socket_path}']
self.add_nodes(self.num_nodes, extra_args=args)
self.start_nodes()
@@ -124,7 +147,7 @@ class ProxyTest(BitcoinTestFramework):
def node_test(self, node, *, proxies, auth, test_onion, test_cjdns):
rv = []
addr = "15.61.23.23:1234"
- self.log.debug(f"Test: outgoing IPv4 connection through node for address {addr}")
+ self.log.debug(f"Test: outgoing IPv4 connection through node {node.index} for address {addr}")
node.addnode(addr, "onetry")
cmd = proxies[0].queue.get()
assert isinstance(cmd, Socks5Command)
@@ -140,7 +163,7 @@ class ProxyTest(BitcoinTestFramework):
if self.have_ipv6:
addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"
- self.log.debug(f"Test: outgoing IPv6 connection through node for address {addr}")
+ self.log.debug(f"Test: outgoing IPv6 connection through node {node.index} for address {addr}")
node.addnode(addr, "onetry")
cmd = proxies[1].queue.get()
assert isinstance(cmd, Socks5Command)
@@ -156,7 +179,7 @@ class ProxyTest(BitcoinTestFramework):
if test_onion:
addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
- self.log.debug(f"Test: outgoing onion connection through node for address {addr}")
+ self.log.debug(f"Test: outgoing onion connection through node {node.index} for address {addr}")
node.addnode(addr, "onetry")
cmd = proxies[2].queue.get()
assert isinstance(cmd, Socks5Command)
@@ -171,7 +194,7 @@ class ProxyTest(BitcoinTestFramework):
if test_cjdns:
addr = "[fc00:1:2:3:4:5:6:7]:8888"
- self.log.debug(f"Test: outgoing CJDNS connection through node for address {addr}")
+ self.log.debug(f"Test: outgoing CJDNS connection through node {node.index} for address {addr}")
node.addnode(addr, "onetry")
cmd = proxies[1].queue.get()
assert isinstance(cmd, Socks5Command)
@@ -185,7 +208,7 @@ class ProxyTest(BitcoinTestFramework):
self.network_test(node, addr, network=NET_CJDNS)
addr = "node.noumenon:8333"
- self.log.debug(f"Test: outgoing DNS name connection through node for address {addr}")
+ self.log.debug(f"Test: outgoing DNS name connection through node {node.index} for address {addr}")
node.addnode(addr, "onetry")
cmd = proxies[3].queue.get()
assert isinstance(cmd, Socks5Command)
@@ -230,6 +253,12 @@ class ProxyTest(BitcoinTestFramework):
proxies=[self.serv1, self.serv1, self.serv1, self.serv1],
auth=False, test_onion=True, test_cjdns=True)
+ if self.have_unix_sockets:
+ self.node_test(self.nodes[5],
+ proxies=[self.serv4, self.serv4, self.serv4, self.serv4],
+ auth=True, test_onion=True, test_cjdns=False)
+
+
def networks_dict(d):
r = {}
for x in d['networks']:
@@ -315,6 +344,37 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n4['i2p']['reachable'], False)
assert_equal(n4['cjdns']['reachable'], True)
+ if self.have_unix_sockets:
+ n5 = networks_dict(nodes_network_info[5])
+ assert_equal(NETWORKS, n5.keys())
+ for net in NETWORKS:
+ if net == NET_I2P:
+ expected_proxy = ''
+ expected_randomize = False
+ else:
+ expected_proxy = 'unix:' + self.conf4.addr # no port number
+ expected_randomize = True
+ assert_equal(n5[net]['proxy'], expected_proxy)
+ assert_equal(n5[net]['proxy_randomize_credentials'], expected_randomize)
+ assert_equal(n5['onion']['reachable'], True)
+ assert_equal(n5['i2p']['reachable'], False)
+ assert_equal(n5['cjdns']['reachable'], False)
+
+ n6 = networks_dict(nodes_network_info[6])
+ assert_equal(NETWORKS, n6.keys())
+ for net in NETWORKS:
+ if net != NET_ONION:
+ expected_proxy = ''
+ expected_randomize = False
+ else:
+ expected_proxy = 'unix:' + self.conf4.addr # no port number
+ expected_randomize = True
+ assert_equal(n6[net]['proxy'], expected_proxy)
+ assert_equal(n6[net]['proxy_randomize_credentials'], expected_randomize)
+ assert_equal(n6['onion']['reachable'], True)
+ assert_equal(n6['i2p']['reachable'], False)
+ assert_equal(n6['cjdns']['reachable'], False)
+
self.stop_node(1)
self.log.info("Test passing invalid -proxy hostname raises expected init error")
@@ -383,6 +443,18 @@ class ProxyTest(BitcoinTestFramework):
msg = "Error: Unknown network specified in -onlynet: 'abc'"
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+ self.log.info("Test passing too-long unix path to -proxy raises init error")
+ self.nodes[1].extra_args = [f"-proxy=unix:{'x' * 1000}"]
+ if self.have_unix_sockets:
+ msg = f"Error: Invalid -proxy address or hostname: 'unix:{'x' * 1000}'"
+ else:
+ # If unix sockets are not supported, the file path is incorrectly interpreted as host:port
+ msg = f"Error: Invalid port specified in -proxy: 'unix:{'x' * 1000}'"
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
+ # Cleanup socket path we established outside the individual test directory.
+ if self.have_unix_sockets:
+ os.unlink(socket_path)
if __name__ == '__main__':
ProxyTest().main()
diff --git a/test/functional/feature_reindex_readonly.py b/test/functional/feature_reindex_readonly.py
index dd99c3c4fa..25cff87a3b 100755
--- a/test/functional/feature_reindex_readonly.py
+++ b/test/functional/feature_reindex_readonly.py
@@ -24,6 +24,7 @@ class BlockstoreReindexTest(BitcoinTestFramework):
opreturn = "6a"
nulldata = fastprune_blockfile_size * "ff"
self.generateblock(self.nodes[0], output=f"raw({opreturn}{nulldata})", transactions=[])
+ block_count = self.nodes[0].getblockcount()
self.stop_node(0)
assert (self.nodes[0].chain_path / "blocks" / "blk00000.dat").exists()
@@ -73,10 +74,10 @@ class BlockstoreReindexTest(BitcoinTestFramework):
pass
if undo_immutable:
- self.log.info("Attempt to restart and reindex the node with the unwritable block file")
- with self.nodes[0].assert_debug_log(expected_msgs=['FlushStateToDisk', 'failed to open file'], unexpected_msgs=[]):
- self.nodes[0].assert_start_raises_init_error(extra_args=['-reindex', '-fastprune'],
- expected_msg="Error: A fatal internal error occurred, see debug.log for details")
+ self.log.debug("Attempt to restart and reindex the node with the unwritable block file")
+ with self.nodes[0].wait_for_debug_log([b"Reindexing finished"]):
+ self.start_node(0, extra_args=['-reindex', '-fastprune'])
+ assert block_count == self.nodes[0].getblockcount()
undo_immutable()
filename.chmod(0o777)
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index 05aa40bbfe..ae8d6b226d 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -53,8 +53,7 @@ class RESTTest (BitcoinTestFramework):
self.num_nodes = 2
self.extra_args = [["-rest", "-blockfilterindex=1"], []]
# whitelist peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
+ self.noban_tx_relay = True
self.supports_cli = False
def test_rest_request(
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 2358dd4387..3c3ff1e4a0 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -5,6 +5,7 @@
"""Test the ZMQ notification interface."""
import struct
from time import sleep
+from io import BytesIO
from test_framework.address import (
ADDRESS_BCRT1_P2WSH_OP_TRUE,
@@ -17,6 +18,7 @@ from test_framework.blocktools import (
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import (
+ CBlock,
hash256,
tx_from_hex,
)
@@ -104,9 +106,8 @@ class ZMQTestSetupBlock:
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- # This test isn't testing txn relay/timing, so set whitelist on the
- # peers for instant txn relay. This speeds up the test run time 2-3x.
- self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.zmq_port_base = p2p_port(self.num_nodes + 1)
def skip_test_if_missing_module(self):
@@ -138,8 +139,7 @@ class ZMQTest (BitcoinTestFramework):
socket.setsockopt(zmq.IPV6, 1)
subscribers.append(ZMQSubscriber(socket, topic.encode()))
- self.restart_node(0, [f"-zmqpub{topic}={address}" for topic, address in services] +
- self.extra_args[0])
+ self.restart_node(0, [f"-zmqpub{topic}={address}" for topic, address in services])
for i, sub in enumerate(subscribers):
sub.socket.connect(services[i][1])
@@ -203,8 +203,13 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(tx.hash, txid.hex())
# Should receive the generated raw block.
- block = rawblock.receive()
- assert_equal(genhashes[x], hash256_reversed(block[:80]).hex())
+ hex = rawblock.receive()
+ block = CBlock()
+ block.deserialize(BytesIO(hex))
+ assert block.is_valid()
+ assert_equal(block.vtx[0].hash, tx.hash)
+ assert_equal(len(block.vtx), 1)
+ assert_equal(genhashes[x], hash256_reversed(hex[:80]).hex())
# Should receive the generated block hash.
hash = hashblock.receive().hex()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 538e1fe053..272e932fcc 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -96,6 +96,12 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
rawtxs=[raw_tx_in_block],
maxfeerate=1,
))
+ # Check negative feerate
+ assert_raises_rpc_error(-3, "Amount out of range", lambda: self.check_mempool_result(
+ result_expected=None,
+ rawtxs=[raw_tx_in_block],
+ maxfeerate=-0.01,
+ ))
# ... 0.99 passes
self.check_mempool_result(
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known'}],
diff --git a/test/functional/mempool_accept_v3.py b/test/functional/mempool_accept_v3.py
index 7ac3c97c4b..1b55cd0a0d 100755
--- a/test/functional/mempool_accept_v3.py
+++ b/test/functional/mempool_accept_v3.py
@@ -15,10 +15,13 @@ from test_framework.util import (
assert_raises_rpc_error,
)
from test_framework.wallet import (
+ COIN,
DEFAULT_FEE,
MiniWallet,
)
+MAX_REPLACEMENT_CANDIDATES = 100
+
def cleanup(extra_args=None):
def decorator(func):
def wrapper(self):
@@ -290,8 +293,13 @@ class MempoolAcceptV3(BitcoinTestFramework):
self.check_mempool([tx_in_mempool["txid"]])
@cleanup(extra_args=["-acceptnonstdtxn=1"])
- def test_mempool_sibling(self):
- self.log.info("Test that v3 transaction cannot have mempool siblings")
+ def test_sibling_eviction_package(self):
+ """
+ When a transaction has a mempool sibling, it may be eligible for sibling eviction.
+ However, this option is only available in single transaction acceptance. It doesn't work in
+ a multi-testmempoolaccept (where RBF is disabled) or when doing package CPFP.
+ """
+ self.log.info("Test v3 sibling eviction in submitpackage and multi-testmempoolaccept")
node = self.nodes[0]
# Add a parent + child to mempool
tx_mempool_parent = self.wallet.send_self_transfer_multi(
@@ -307,26 +315,57 @@ class MempoolAcceptV3(BitcoinTestFramework):
)
self.check_mempool([tx_mempool_parent["txid"], tx_mempool_sibling["txid"]])
- tx_has_mempool_sibling = self.wallet.create_self_transfer(
+ tx_sibling_1 = self.wallet.create_self_transfer(
utxo_to_spend=tx_mempool_parent["new_utxos"][1],
- version=3
+ version=3,
+ fee_rate=DEFAULT_FEE*100,
)
- expected_error_mempool_sibling = f"v3-rule-violation, tx {tx_mempool_parent['txid']} (wtxid={tx_mempool_parent['wtxid']}) would exceed descendant count limit"
- assert_raises_rpc_error(-26, expected_error_mempool_sibling, node.sendrawtransaction, tx_has_mempool_sibling["hex"])
+ tx_has_mempool_uncle = self.wallet.create_self_transfer(utxo_to_spend=tx_sibling_1["new_utxo"], version=3)
- tx_has_mempool_uncle = self.wallet.create_self_transfer(utxo_to_spend=tx_has_mempool_sibling["new_utxo"], version=3)
+ tx_sibling_2 = self.wallet.create_self_transfer(
+ utxo_to_spend=tx_mempool_parent["new_utxos"][0],
+ version=3,
+ fee_rate=DEFAULT_FEE*200,
+ )
+
+ tx_sibling_3 = self.wallet.create_self_transfer(
+ utxo_to_spend=tx_mempool_parent["new_utxos"][1],
+ version=3,
+ fee_rate=0,
+ )
+ tx_bumps_parent_with_sibling = self.wallet.create_self_transfer(
+ utxo_to_spend=tx_sibling_3["new_utxo"],
+ version=3,
+ fee_rate=DEFAULT_FEE*300,
+ )
- # Also fails with another non-related transaction via testmempoolaccept
+ # Fails with another non-related transaction via testmempoolaccept
tx_unrelated = self.wallet.create_self_transfer(version=3)
- result_test_unrelated = node.testmempoolaccept([tx_has_mempool_sibling["hex"], tx_unrelated["hex"]])
+ result_test_unrelated = node.testmempoolaccept([tx_sibling_1["hex"], tx_unrelated["hex"]])
assert_equal(result_test_unrelated[0]["reject-reason"], "v3-rule-violation")
- result_test_1p1c = node.testmempoolaccept([tx_has_mempool_sibling["hex"], tx_has_mempool_uncle["hex"]])
+ # Fails in a package via testmempoolaccept
+ result_test_1p1c = node.testmempoolaccept([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])
assert_equal(result_test_1p1c[0]["reject-reason"], "v3-rule-violation")
- # Also fails with a child via submitpackage
- result_submitpackage = node.submitpackage([tx_has_mempool_sibling["hex"], tx_has_mempool_uncle["hex"]])
- assert_equal(result_submitpackage["tx-results"][tx_has_mempool_sibling['wtxid']]['error'], expected_error_mempool_sibling)
+ # Allowed when tx is submitted in a package and evaluated individually.
+ # Note that the child failed since it would be the 3rd generation.
+ result_package_indiv = node.submitpackage([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])
+ self.check_mempool([tx_mempool_parent["txid"], tx_sibling_1["txid"]])
+ expected_error_gen3 = f"v3-rule-violation, tx {tx_has_mempool_uncle['txid']} (wtxid={tx_has_mempool_uncle['wtxid']}) would have too many ancestors"
+
+ assert_equal(result_package_indiv["tx-results"][tx_has_mempool_uncle['wtxid']]['error'], expected_error_gen3)
+
+ # Allowed when tx is submitted in a package with in-mempool parent (which is deduplicated).
+ node.submitpackage([tx_mempool_parent["hex"], tx_sibling_2["hex"]])
+ self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])
+
+ # Child cannot pay for sibling eviction for parent, as it violates v3 topology limits
+ result_package_cpfp = node.submitpackage([tx_sibling_3["hex"], tx_bumps_parent_with_sibling["hex"]])
+ self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])
+ expected_error_cpfp = f"v3-rule-violation, tx {tx_mempool_parent['txid']} (wtxid={tx_mempool_parent['wtxid']}) would exceed descendant count limit"
+
+ assert_equal(result_package_cpfp["tx-results"][tx_sibling_3['wtxid']]['error'], expected_error_cpfp)
@cleanup(extra_args=["-datacarriersize=1000", "-acceptnonstdtxn=1"])
@@ -429,11 +468,123 @@ class MempoolAcceptV3(BitcoinTestFramework):
self.check_mempool([ancestor_tx["txid"], child_1_conflict["txid"], child_2["txid"]])
assert_equal(node.getmempoolentry(ancestor_tx["txid"])["descendantcount"], 3)
+ @cleanup(extra_args=["-acceptnonstdtxn=1"])
+ def test_v3_sibling_eviction(self):
+ self.log.info("Test sibling eviction for v3")
+ node = self.nodes[0]
+ tx_v3_parent = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2, version=3)
+ # This is the sibling to replace
+ tx_v3_child_1 = self.wallet.send_self_transfer(
+ from_node=node, utxo_to_spend=tx_v3_parent["new_utxos"][0], fee_rate=DEFAULT_FEE * 2, version=3
+ )
+ assert tx_v3_child_1["txid"] in node.getrawmempool()
+
+ self.log.info("Test tx must be higher feerate than sibling to evict it")
+ tx_v3_child_2_rule6 = self.wallet.create_self_transfer(
+ utxo_to_spend=tx_v3_parent["new_utxos"][1], fee_rate=DEFAULT_FEE, version=3
+ )
+ rule6_str = f"insufficient fee (including sibling eviction), rejecting replacement {tx_v3_child_2_rule6['txid']}; new feerate"
+ assert_raises_rpc_error(-26, rule6_str, node.sendrawtransaction, tx_v3_child_2_rule6["hex"])
+ self.check_mempool([tx_v3_parent['txid'], tx_v3_child_1['txid']])
+
+ self.log.info("Test tx must meet absolute fee rules to evict sibling")
+ tx_v3_child_2_rule4 = self.wallet.create_self_transfer(
+ utxo_to_spend=tx_v3_parent["new_utxos"][1], fee_rate=2 * DEFAULT_FEE + Decimal("0.00000001"), version=3
+ )
+ rule4_str = f"insufficient fee (including sibling eviction), rejecting replacement {tx_v3_child_2_rule4['txid']}, not enough additional fees to relay"
+ assert_raises_rpc_error(-26, rule4_str, node.sendrawtransaction, tx_v3_child_2_rule4["hex"])
+ self.check_mempool([tx_v3_parent['txid'], tx_v3_child_1['txid']])
+
+ self.log.info("Test tx cannot cause more than 100 evictions including RBF and sibling eviction")
+ # First add 4 groups of 25 transactions.
+ utxos_for_conflict = []
+ txids_v2_100 = []
+ for _ in range(4):
+ confirmed_utxo = self.wallet.get_utxo(confirmed_only=True)
+ utxos_for_conflict.append(confirmed_utxo)
+ # 25 is within descendant limits
+ chain_length = int(MAX_REPLACEMENT_CANDIDATES / 4)
+ chain = self.wallet.create_self_transfer_chain(chain_length=chain_length, utxo_to_spend=confirmed_utxo)
+ for item in chain:
+ txids_v2_100.append(item["txid"])
+ node.sendrawtransaction(item["hex"])
+ self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_1["txid"]])
+
+ # Replacing 100 transactions is fine
+ tx_v3_replacement_only = self.wallet.create_self_transfer_multi(utxos_to_spend=utxos_for_conflict, fee_per_output=4000000)
+ # Override maxfeerate - it costs a lot to replace these 100 transactions.
+ assert node.testmempoolaccept([tx_v3_replacement_only["hex"]], maxfeerate=0)[0]["allowed"]
+ # Adding another one exceeds the limit.
+ utxos_for_conflict.append(tx_v3_parent["new_utxos"][1])
+ tx_v3_child_2_rule5 = self.wallet.create_self_transfer_multi(utxos_to_spend=utxos_for_conflict, fee_per_output=4000000, version=3)
+ rule5_str = f"too many potential replacements (including sibling eviction), rejecting replacement {tx_v3_child_2_rule5['txid']}; too many potential replacements (101 > 100)"
+ assert_raises_rpc_error(-26, rule5_str, node.sendrawtransaction, tx_v3_child_2_rule5["hex"])
+ self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_1["txid"]])
+
+ self.log.info("Test sibling eviction is successful if it meets all RBF rules")
+ tx_v3_child_2 = self.wallet.create_self_transfer(
+ utxo_to_spend=tx_v3_parent["new_utxos"][1], fee_rate=DEFAULT_FEE*10, version=3
+ )
+ node.sendrawtransaction(tx_v3_child_2["hex"])
+ self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_2["txid"]])
+
+ self.log.info("Test that it's possible to do a sibling eviction and RBF at the same time")
+ utxo_unrelated_conflict = self.wallet.get_utxo(confirmed_only=True)
+ tx_unrelated_replacee = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=utxo_unrelated_conflict)
+ assert tx_unrelated_replacee["txid"] in node.getrawmempool()
+
+ fee_to_beat_child2 = int(tx_v3_child_2["fee"] * COIN)
+
+ tx_v3_child_3 = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=[tx_v3_parent["new_utxos"][0], utxo_unrelated_conflict], fee_per_output=fee_to_beat_child2*5, version=3
+ )
+ node.sendrawtransaction(tx_v3_child_3["hex"])
+ self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_3["txid"]])
+
+ @cleanup(extra_args=["-acceptnonstdtxn=1"])
+ def test_reorg_sibling_eviction_1p2c(self):
+ node = self.nodes[0]
+ self.log.info("Test that sibling eviction is not allowed when multiple siblings exist")
+
+ tx_with_multi_children = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=3, version=3, confirmed_only=True)
+ self.check_mempool([tx_with_multi_children["txid"]])
+
+ block_to_disconnect = self.generate(node, 1)[0]
+ self.check_mempool([])
+
+ tx_with_sibling1 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=tx_with_multi_children["new_utxos"][0])
+ tx_with_sibling2 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=tx_with_multi_children["new_utxos"][1])
+ self.check_mempool([tx_with_sibling1["txid"], tx_with_sibling2["txid"]])
+
+ # Create a reorg, bringing tx_with_multi_children back into the mempool with a descendant count of 3.
+ node.invalidateblock(block_to_disconnect)
+ self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling1["txid"], tx_with_sibling2["txid"]])
+ assert_equal(node.getmempoolentry(tx_with_multi_children["txid"])["descendantcount"], 3)
+
+ # Sibling eviction is not allowed because there are two siblings
+ tx_with_sibling3 = self.wallet.create_self_transfer(
+ version=3,
+ utxo_to_spend=tx_with_multi_children["new_utxos"][2],
+ fee_rate=DEFAULT_FEE*50
+ )
+ expected_error_2siblings = f"v3-rule-violation, tx {tx_with_multi_children['txid']} (wtxid={tx_with_multi_children['wtxid']}) would exceed descendant count limit"
+ assert_raises_rpc_error(-26, expected_error_2siblings, node.sendrawtransaction, tx_with_sibling3["hex"])
+
+ # However, an RBF (with conflicting inputs) is possible even if the resulting cluster size exceeds 2
+ tx_with_sibling3_rbf = self.wallet.send_self_transfer(
+ from_node=node,
+ version=3,
+ utxo_to_spend=tx_with_multi_children["new_utxos"][0],
+ fee_rate=DEFAULT_FEE*50
+ )
+ self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling3_rbf["txid"], tx_with_sibling2["txid"]])
+
+
def run_test(self):
self.log.info("Generate blocks to create UTXOs")
node = self.nodes[0]
self.wallet = MiniWallet(node)
- self.generate(self.wallet, 110)
+ self.generate(self.wallet, 120)
self.test_v3_acceptance()
self.test_v3_replacement()
self.test_v3_bip125()
@@ -441,10 +592,12 @@ class MempoolAcceptV3(BitcoinTestFramework):
self.test_nondefault_package_limits()
self.test_v3_ancestors_package()
self.test_v3_ancestors_package_and_mempool()
- self.test_mempool_sibling()
+ self.test_sibling_eviction_package()
self.test_v3_package_inheritance()
self.test_v3_in_testmempoolaccept()
self.test_reorg_2child_rbf()
+ self.test_v3_sibling_eviction()
+ self.test_reorg_sibling_eviction_1p2c()
if __name__ == "__main__":
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 95f7939412..dcb66b2ca1 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -27,10 +27,11 @@ assert CUSTOM_DESCENDANT_LIMIT >= CUSTOM_ANCESTOR_LIMIT
class MempoolPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [
[
"-maxorphantx=1000",
- "-whitelist=noban@127.0.0.1", # immediate tx relay
],
[
"-maxorphantx=1000",
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 6b03cdf877..bcba534f9a 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -46,16 +46,16 @@ class TestP2PConn(P2PInterface):
class FeeFilterTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
# We lower the various required feerates for this test
# to catch a corner-case where feefilter used to slightly undercut
# mempool and wallet feerate calculation based on GetFee
# rounding down 3 places, leading to stranded transactions.
# See issue #16499
- # grant noban permission to all peers to speed up tx relay / mempool sync
self.extra_args = [[
"-minrelaytxfee=0.00000100",
- "-mintxfee=0.00000100",
- "-whitelist=noban@127.0.0.1",
+ "-mintxfee=0.00000100"
]] * self.num_nodes
def run_test(self):
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 62d55cc101..7c8ed58e51 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -94,9 +94,10 @@ class P2PBloomFilter(P2PInterface):
class FilterTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [[
'-peerbloomfilters',
- '-whitelist=noban@127.0.0.1', # immediate tx relay
]]
def generatetoscriptpubkey(self, scriptpubkey):
diff --git a/test/functional/p2p_handshake.py b/test/functional/p2p_handshake.py
new file mode 100755
index 0000000000..f0b62e291d
--- /dev/null
+++ b/test/functional/p2p_handshake.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+# Copyright (c) 2024 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 P2P behaviour during the handshake phase (VERSION, VERACK messages).
+"""
+import itertools
+import time
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import (
+ NODE_NETWORK,
+ NODE_NETWORK_LIMITED,
+ NODE_NONE,
+ NODE_P2P_V2,
+ NODE_WITNESS,
+)
+from test_framework.p2p import P2PInterface
+
+
+# Desirable service flags for outbound non-pruned and pruned peers. Note that
+# the desirable service flags for pruned peers are dynamic and only apply if
+# 1. the peer's service flag NODE_NETWORK_LIMITED is set *and*
+# 2. the local chain is close to the tip (<24h)
+DESIRABLE_SERVICE_FLAGS_FULL = NODE_NETWORK | NODE_WITNESS
+DESIRABLE_SERVICE_FLAGS_PRUNED = NODE_NETWORK_LIMITED | NODE_WITNESS
+
+
+class P2PHandshakeTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def add_outbound_connection(self, node, connection_type, services, wait_for_disconnect):
+ peer = node.add_outbound_p2p_connection(
+ P2PInterface(), p2p_idx=0, wait_for_disconnect=wait_for_disconnect,
+ connection_type=connection_type, services=services,
+ supports_v2_p2p=self.options.v2transport, advertise_v2_p2p=self.options.v2transport)
+ if not wait_for_disconnect:
+ # check that connection is alive past the version handshake and disconnect manually
+ peer.sync_with_ping()
+ peer.peer_disconnect()
+ peer.wait_for_disconnect()
+
+ def test_desirable_service_flags(self, node, service_flag_tests, desirable_service_flags, expect_disconnect):
+ """Check that connecting to a peer either fails or succeeds depending on its offered
+ service flags in the VERSION message. The test is exercised for all relevant
+ outbound connection types where the desirable service flags check is done."""
+ CONNECTION_TYPES = ["outbound-full-relay", "block-relay-only", "addr-fetch"]
+ for conn_type, services in itertools.product(CONNECTION_TYPES, service_flag_tests):
+ if self.options.v2transport:
+ services |= NODE_P2P_V2
+ expected_result = "disconnect" if expect_disconnect else "connect"
+ self.log.info(f' - services 0x{services:08x}, type "{conn_type}" [{expected_result}]')
+ if expect_disconnect:
+ assert (services & desirable_service_flags) != desirable_service_flags
+ expected_debug_log = f'does not offer the expected services ' \
+ f'({services:08x} offered, {desirable_service_flags:08x} expected)'
+ with node.assert_debug_log([expected_debug_log]):
+ self.add_outbound_connection(node, conn_type, services, wait_for_disconnect=True)
+ else:
+ assert (services & desirable_service_flags) == desirable_service_flags
+ self.add_outbound_connection(node, conn_type, services, wait_for_disconnect=False)
+
+ def generate_at_mocktime(self, time):
+ self.nodes[0].setmocktime(time)
+ self.generate(self.nodes[0], 1)
+ self.nodes[0].setmocktime(0)
+
+ def run_test(self):
+ node = self.nodes[0]
+ self.log.info("Check that lacking desired service flags leads to disconnect (non-pruned peers)")
+ self.test_desirable_service_flags(node, [NODE_NONE, NODE_NETWORK, NODE_WITNESS],
+ DESIRABLE_SERVICE_FLAGS_FULL, expect_disconnect=True)
+ self.test_desirable_service_flags(node, [NODE_NETWORK | NODE_WITNESS],
+ DESIRABLE_SERVICE_FLAGS_FULL, expect_disconnect=False)
+
+ self.log.info("Check that limited peers are only desired if the local chain is close to the tip (<24h)")
+ self.generate_at_mocktime(int(time.time()) - 25 * 3600) # tip outside the 24h window, should fail
+ self.test_desirable_service_flags(node, [NODE_NETWORK_LIMITED | NODE_WITNESS],
+ DESIRABLE_SERVICE_FLAGS_FULL, expect_disconnect=True)
+ self.generate_at_mocktime(int(time.time()) - 23 * 3600) # tip inside the 24h window, should succeed
+ self.test_desirable_service_flags(node, [NODE_NETWORK_LIMITED | NODE_WITNESS],
+ DESIRABLE_SERVICE_FLAGS_PRUNED, expect_disconnect=False)
+
+ self.log.info("Check that feeler connections get disconnected immediately")
+ with node.assert_debug_log([f"feeler connection completed"]):
+ self.add_outbound_connection(node, "feeler", NODE_NONE, wait_for_disconnect=True)
+
+
+if __name__ == '__main__':
+ P2PHandshakeTest().main()
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index 806fd9c6cb..8ec62ae5ee 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -32,7 +32,8 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [["-whitelist=noban@127.0.0.1"]]
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
def run_test(self):
# Add p2p connection to node0
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index 05d227990e..467bbad09c 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -15,7 +15,6 @@ from test_framework.messages import (
NODE_P2P_V2,
NODE_WITNESS,
msg_getdata,
- msg_verack,
)
from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
@@ -47,7 +46,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [['-prune=550', '-addrmantest'], [], []]
+ self.extra_args = [['-prune=550'], [], []]
def disconnect_all(self):
self.disconnect_nodes(0, 1)
@@ -139,16 +138,6 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
self.log.info("Requesting block at height 2 (tip-289) must fail (ignored).")
node.send_getdata_for_block(blocks[0]) # first block outside of the 288+2 limit
node.wait_for_disconnect(5)
-
- self.log.info("Check local address relay, do a fresh connection.")
- self.nodes[0].disconnect_p2ps()
- node1 = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
- node1.send_message(msg_verack())
-
- node1.wait_for_addr()
- #must relay address with NODE_NETWORK_LIMITED
- assert_equal(node1.firstAddrnServices, expected_services)
-
self.nodes[0].disconnect_p2ps()
# connect unsynced node 2 with pruned NODE_NETWORK_LIMITED peer
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index 6153e4a156..80a27943fd 100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -83,7 +83,14 @@ class P2PPermissionsTests(BitcoinTestFramework):
["-whitelist=all@127.0.0.1"],
["forcerelay", "noban", "mempool", "bloomfilter", "relay", "download", "addr"])
+ for flag, permissions in [(["-whitelist=noban,out@127.0.0.1"], ["noban", "download"]), (["-whitelist=noban@127.0.0.1"], [])]:
+ self.restart_node(0, flag)
+ self.connect_nodes(0, 1)
+ peerinfo = self.nodes[0].getpeerinfo()[0]
+ assert_equal(peerinfo['permissions'], permissions)
+
self.stop_node(1)
+ self.nodes[1].assert_start_raises_init_error(["-whitelist=in,out@127.0.0.1"], "Only direction was set, no permissions", match=ErrorMatch.PARTIAL_REGEX)
self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX)
self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX)
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index d316c4b602..1c0c11d74c 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -213,9 +213,11 @@ class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1", "-par=1"],
+ ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-par=1"],
["-acceptnonstdtxn=0", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}"],
]
self.supports_cli = False
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index accb2439f2..2701d2471d 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -13,7 +13,6 @@ import platform
import time
import test_framework.messages
-from test_framework.netutil import ADDRMAN_NEW_BUCKET_COUNT, ADDRMAN_TRIED_BUCKET_COUNT, ADDRMAN_BUCKET_SIZE
from test_framework.p2p import (
P2PInterface,
P2P_SERVICES,
@@ -42,6 +41,24 @@ def assert_net_servicesnames(servicesflag, servicenames):
assert servicesflag_generated == servicesflag
+def seed_addrman(node):
+ """ Populate the addrman with addresses from different networks.
+ Here 2 ipv4, 2 ipv6, 1 cjdns, 2 onion and 1 i2p addresses are added.
+ """
+ # These addresses currently don't collide with a deterministic addrman.
+ # If the addrman positioning/bucketing is changed, these might collide
+ # and adding them fails.
+ success = { "success": True }
+ assert_equal(node.addpeeraddress(address="1.2.3.4", tried=True, port=8333), success)
+ assert_equal(node.addpeeraddress(address="2.0.0.0", port=8333), success)
+ assert_equal(node.addpeeraddress(address="1233:3432:2434:2343:3234:2345:6546:4534", tried=True, port=8333), success)
+ assert_equal(node.addpeeraddress(address="2803:0:1234:abcd::1", port=45324), success)
+ assert_equal(node.addpeeraddress(address="fc00:1:2:3:4:5:6:7", port=8333), success)
+ assert_equal(node.addpeeraddress(address="pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion", tried=True, port=8333), success)
+ assert_equal(node.addpeeraddress(address="nrfj6inpyf73gpkyool35hcmne5zwfmse3jl3aw23vk7chdemalyaqad.onion", port=45324, tried=True), success)
+ assert_equal(node.addpeeraddress(address="c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p", port=8333), success)
+
+
class NetTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
@@ -305,22 +322,16 @@ class NetTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Network not recognized: Foo", self.nodes[0].getnodeaddresses, 1, "Foo")
def test_addpeeraddress(self):
- """RPC addpeeraddress sets the source address equal to the destination address.
- If an address with the same /16 as an existing new entry is passed, it will be
- placed in the same new bucket and have a 1/64 chance of the bucket positions
- colliding (depending on the value of nKey in the addrman), in which case the
- new address won't be added. The probability of collision can be reduced to
- 1/2^16 = 1/65536 by using an address from a different /16. We avoid this here
- by first testing adding a tried table entry before testing adding a new table one.
- """
self.log.info("Test addpeeraddress")
- self.restart_node(1, ["-checkaddrman=1"])
+ # The node has an existing, non-deterministic addrman from a previous test.
+ # Clear it to have a deterministic addrman.
+ self.restart_node(1, ["-checkaddrman=1", "-test=addrman"], clear_addrman=True)
node = self.nodes[1]
- self.log.debug("Test that addpeerinfo is a hidden RPC")
+ self.log.debug("Test that addpeeraddress is a hidden RPC")
# It is hidden from general help, but its detailed help may be called directly.
- assert "addpeerinfo" not in node.help()
- assert "addpeerinfo" in node.help("addpeerinfo")
+ assert "addpeeraddress" not in node.help()
+ assert "unknown command: addpeeraddress" not in node.help("addpeeraddress")
self.log.debug("Test that adding an empty address fails")
assert_equal(node.addpeeraddress(address="", port=8333), {"success": False})
@@ -333,26 +344,50 @@ class NetTest(BitcoinTestFramework):
assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=-1)
assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=65536)
+ self.log.debug("Test that adding a valid address to the new table succeeds")
+ assert_equal(node.addpeeraddress(address="1.0.0.0", tried=False, port=8333), {"success": True})
+ addrman = node.getrawaddrman()
+ assert_equal(len(addrman["tried"]), 0)
+ new_table = list(addrman["new"].values())
+ assert_equal(len(new_table), 1)
+ assert_equal(new_table[0]["address"], "1.0.0.0")
+ assert_equal(new_table[0]["port"], 8333)
+
+ self.log.debug("Test that adding an already-present new address to the new and tried tables fails")
+ for value in [True, False]:
+ assert_equal(node.addpeeraddress(address="1.0.0.0", tried=value, port=8333), {"success": False, "error": "failed-adding-to-new"})
+ assert_equal(len(node.getnodeaddresses(count=0)), 1)
+
self.log.debug("Test that adding a valid address to the tried table succeeds")
- self.addr_time = int(time.time())
- node.setmocktime(self.addr_time)
assert_equal(node.addpeeraddress(address="1.2.3.4", tried=True, port=8333), {"success": True})
- with node.assert_debug_log(expected_msgs=["CheckAddrman: new 0, tried 1, total 1 started"]):
- addrs = node.getnodeaddresses(count=0) # getnodeaddresses re-runs the addrman checks
- assert_equal(len(addrs), 1)
- assert_equal(addrs[0]["address"], "1.2.3.4")
- assert_equal(addrs[0]["port"], 8333)
+ addrman = node.getrawaddrman()
+ assert_equal(len(addrman["new"]), 1)
+ tried_table = list(addrman["tried"].values())
+ assert_equal(len(tried_table), 1)
+ assert_equal(tried_table[0]["address"], "1.2.3.4")
+ assert_equal(tried_table[0]["port"], 8333)
+ node.getnodeaddresses(count=0) # getnodeaddresses re-runs the addrman checks
self.log.debug("Test that adding an already-present tried address to the new and tried tables fails")
for value in [True, False]:
- assert_equal(node.addpeeraddress(address="1.2.3.4", tried=value, port=8333), {"success": False})
- assert_equal(len(node.getnodeaddresses(count=0)), 1)
-
- self.log.debug("Test that adding a second address, this time to the new table, succeeds")
+ assert_equal(node.addpeeraddress(address="1.2.3.4", tried=value, port=8333), {"success": False, "error": "failed-adding-to-new"})
+ assert_equal(len(node.getnodeaddresses(count=0)), 2)
+
+ self.log.debug("Test that adding an address, which collides with the address in tried table, fails")
+ colliding_address = "1.2.5.45" # grinded address that produces a tried-table collision
+ assert_equal(node.addpeeraddress(address=colliding_address, tried=True, port=8333), {"success": False, "error": "failed-adding-to-tried"})
+ # When adding an address to the tried table, it's first added to the new table.
+ # As we fail to move it to the tried table, it remains in the new table.
+ addrman_info = node.getaddrmaninfo()
+ assert_equal(addrman_info["all_networks"]["tried"], 1)
+ assert_equal(addrman_info["all_networks"]["new"], 2)
+
+ self.log.debug("Test that adding an another address to the new table succeeds")
assert_equal(node.addpeeraddress(address="2.0.0.0", port=8333), {"success": True})
- with node.assert_debug_log(expected_msgs=["CheckAddrman: new 1, tried 1, total 2 started"]):
- addrs = node.getnodeaddresses(count=0) # getnodeaddresses re-runs the addrman checks
- assert_equal(len(addrs), 2)
+ addrman_info = node.getaddrmaninfo()
+ assert_equal(addrman_info["all_networks"]["tried"], 1)
+ assert_equal(addrman_info["all_networks"]["new"], 3)
+ node.getnodeaddresses(count=0) # getnodeaddresses re-runs the addrman checks
def test_sendmsgtopeer(self):
node = self.nodes[0]
@@ -390,30 +425,38 @@ class NetTest(BitcoinTestFramework):
def test_getaddrmaninfo(self):
self.log.info("Test getaddrmaninfo")
+ self.restart_node(1, extra_args=["-cjdnsreachable", "-test=addrman"], clear_addrman=True)
node = self.nodes[1]
+ seed_addrman(node)
+
+ expected_network_count = {
+ 'all_networks': {'new': 4, 'tried': 4, 'total': 8},
+ 'ipv4': {'new': 1, 'tried': 1, 'total': 2},
+ 'ipv6': {'new': 1, 'tried': 1, 'total': 2},
+ 'onion': {'new': 0, 'tried': 2, 'total': 2},
+ 'i2p': {'new': 1, 'tried': 0, 'total': 1},
+ 'cjdns': {'new': 1, 'tried': 0, 'total': 1},
+ }
- # current count of ipv4 addresses in addrman is {'new':1, 'tried':1}
- self.log.info("Test that count of addresses in addrman match expected values")
+ self.log.debug("Test that count of addresses in addrman match expected values")
res = node.getaddrmaninfo()
- assert_equal(res["ipv4"]["new"], 1)
- assert_equal(res["ipv4"]["tried"], 1)
- assert_equal(res["ipv4"]["total"], 2)
- assert_equal(res["all_networks"]["new"], 1)
- assert_equal(res["all_networks"]["tried"], 1)
- assert_equal(res["all_networks"]["total"], 2)
- for net in ["ipv6", "onion", "i2p", "cjdns"]:
- assert_equal(res[net]["new"], 0)
- assert_equal(res[net]["tried"], 0)
- assert_equal(res[net]["total"], 0)
+ for network, count in expected_network_count.items():
+ assert_equal(res[network]['new'], count['new'])
+ assert_equal(res[network]['tried'], count['tried'])
+ assert_equal(res[network]['total'], count['total'])
def test_getrawaddrman(self):
self.log.info("Test getrawaddrman")
+ self.restart_node(1, extra_args=["-cjdnsreachable", "-test=addrman"], clear_addrman=True)
node = self.nodes[1]
+ self.addr_time = int(time.time())
+ node.setmocktime(self.addr_time)
+ seed_addrman(node)
self.log.debug("Test that getrawaddrman is a hidden RPC")
# It is hidden from general help, but its detailed help may be called directly.
assert "getrawaddrman" not in node.help()
- assert "getrawaddrman" in node.help("getrawaddrman")
+ assert "unknown command: getrawaddrman" not in node.help("getrawaddrman")
def check_addr_information(result, expected):
"""Utility to compare a getrawaddrman result entry with an expected entry"""
@@ -430,88 +473,96 @@ class NetTest(BitcoinTestFramework):
getrawaddrman = node.getrawaddrman()
getaddrmaninfo = node.getaddrmaninfo()
for (table_name, table_info) in expected.items():
- assert_equal(len(getrawaddrman[table_name]), len(table_info["entries"]))
+ assert_equal(len(getrawaddrman[table_name]), len(table_info))
assert_equal(len(getrawaddrman[table_name]), getaddrmaninfo["all_networks"][table_name])
for bucket_position in getrawaddrman[table_name].keys():
- bucket = int(bucket_position.split("/")[0])
- position = int(bucket_position.split("/")[1])
-
- # bucket and position only be sanity checked here as the
- # test-addrman isn't deterministic
- assert 0 <= int(bucket) < table_info["bucket_count"]
- assert 0 <= int(position) < ADDRMAN_BUCKET_SIZE
-
entry = getrawaddrman[table_name][bucket_position]
- expected_entry = list(filter(lambda e: e["address"] == entry["address"], table_info["entries"]))[0]
+ expected_entry = list(filter(lambda e: e["address"] == entry["address"], table_info))[0]
+ assert bucket_position == expected_entry["bucket_position"]
check_addr_information(entry, expected_entry)
- # we expect one addrman new and tried table entry, which were added in a previous test
+ # we expect 4 new and 4 tried table entries in the addrman which were added using seed_addrman()
expected = {
- "new": {
- "bucket_count": ADDRMAN_NEW_BUCKET_COUNT,
- "entries": [
+ "new": [
{
+ "bucket_position": "82/8",
"address": "2.0.0.0",
"port": 8333,
"services": 9,
"network": "ipv4",
"source": "2.0.0.0",
"source_network": "ipv4",
+ },
+ {
+ "bucket_position": "336/24",
+ "address": "fc00:1:2:3:4:5:6:7",
+ "port": 8333,
+ "services": 9,
+ "network": "cjdns",
+ "source": "fc00:1:2:3:4:5:6:7",
+ "source_network": "cjdns",
+ },
+ {
+ "bucket_position": "963/46",
+ "address": "c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p",
+ "port": 8333,
+ "services": 9,
+ "network": "i2p",
+ "source": "c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p",
+ "source_network": "i2p",
+ },
+ {
+ "bucket_position": "613/6",
+ "address": "2803:0:1234:abcd::1",
+ "services": 9,
+ "network": "ipv6",
+ "source": "2803:0:1234:abcd::1",
+ "source_network": "ipv6",
+ "port": 45324,
}
- ]
- },
- "tried": {
- "bucket_count": ADDRMAN_TRIED_BUCKET_COUNT,
- "entries": [
+ ],
+ "tried": [
{
+ "bucket_position": "6/33",
"address": "1.2.3.4",
"port": 8333,
"services": 9,
"network": "ipv4",
"source": "1.2.3.4",
"source_network": "ipv4",
+ },
+ {
+ "bucket_position": "197/34",
+ "address": "1233:3432:2434:2343:3234:2345:6546:4534",
+ "port": 8333,
+ "services": 9,
+ "network": "ipv6",
+ "source": "1233:3432:2434:2343:3234:2345:6546:4534",
+ "source_network": "ipv6",
+ },
+ {
+ "bucket_position": "72/61",
+ "address": "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion",
+ "port": 8333,
+ "services": 9,
+ "network": "onion",
+ "source": "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion",
+ "source_network": "onion"
+ },
+ {
+ "bucket_position": "139/46",
+ "address": "nrfj6inpyf73gpkyool35hcmne5zwfmse3jl3aw23vk7chdemalyaqad.onion",
+ "services": 9,
+ "network": "onion",
+ "source": "nrfj6inpyf73gpkyool35hcmne5zwfmse3jl3aw23vk7chdemalyaqad.onion",
+ "source_network": "onion",
+ "port": 45324,
}
- ]
- }
+ ]
}
- self.log.debug("Test that the getrawaddrman contains information about the addresses added in a previous test")
- check_getrawaddrman_entries(expected)
-
- self.log.debug("Add one new address to each addrman table")
- expected["new"]["entries"].append({
- "address": "2803:0:1234:abcd::1",
- "services": 9,
- "network": "ipv6",
- "source": "2803:0:1234:abcd::1",
- "source_network": "ipv6",
- "port": -1, # set once addpeeraddress is successful
- })
- expected["tried"]["entries"].append({
- "address": "nrfj6inpyf73gpkyool35hcmne5zwfmse3jl3aw23vk7chdemalyaqad.onion",
- "services": 9,
- "network": "onion",
- "source": "nrfj6inpyf73gpkyool35hcmne5zwfmse3jl3aw23vk7chdemalyaqad.onion",
- "source_network": "onion",
- "port": -1, # set once addpeeraddress is successful
- })
-
- port = 0
- for (table_name, table_info) in expected.items():
- # There's a slight chance that the to-be-added address collides with an already
- # present table entry. To avoid this, we increment the port until an address has been
- # added. Incrementing the port changes the position in the new table bucket (bucket
- # stays the same) and changes both the bucket and the position in the tried table.
- while True:
- if node.addpeeraddress(address=table_info["entries"][1]["address"], port=port, tried=table_name == "tried")["success"]:
- table_info["entries"][1]["port"] = port
- self.log.debug(f"Added {table_info['entries'][1]['address']} to {table_name} table")
- break
- else:
- port += 1
-
- self.log.debug("Test that the newly added addresses appear in getrawaddrman")
+ self.log.debug("Test that getrawaddrman contains information about newly added addresses in each addrman table")
check_getrawaddrman_entries(expected)
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 664f2df3f1..029e368166 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -29,7 +29,8 @@ class RPCPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [["-whitelist=noban@127.0.0.1"]] # noban speeds up tx relay
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
def assert_testres_equal(self, package_hex, testres_expected):
"""Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only
@@ -81,6 +82,7 @@ class RPCPackagesTest(BitcoinTestFramework):
self.test_conflicting()
self.test_rbf()
self.test_submitpackage()
+ self.test_maxfeerate_maxburn_submitpackage()
def test_independent(self, coin):
self.log.info("Test multiple independent transactions in a package")
@@ -356,5 +358,34 @@ class RPCPackagesTest(BitcoinTestFramework):
assert_equal(res["tx-results"][sec_wtxid]["error"], "version")
peer.wait_for_broadcast([first_wtxid])
+ def test_maxfeerate_maxburn_submitpackage(self):
+ node = self.nodes[0]
+ # clear mempool
+ deterministic_address = node.get_deterministic_priv_key().address
+ self.generatetoaddress(node, 1, deterministic_address)
+
+ self.log.info("Submitpackage maxfeerate arg testing")
+ chained_txns = self.wallet.create_self_transfer_chain(chain_length=2)
+ minrate_btc_kvb = min([chained_txn["fee"] / chained_txn["tx"].get_vsize() * 1000 for chained_txn in chained_txns])
+ chain_hex = [t["hex"] for t in chained_txns]
+ pkg_result = node.submitpackage(chain_hex, maxfeerate=minrate_btc_kvb - Decimal("0.00000001"))
+ assert_equal(pkg_result["tx-results"][chained_txns[0]["wtxid"]]["error"], "max feerate exceeded")
+ assert_equal(pkg_result["tx-results"][chained_txns[1]["wtxid"]]["error"], "bad-txns-inputs-missingorspent")
+ assert_equal(node.getrawmempool(), [])
+
+ self.log.info("Submitpackage maxburnamount arg testing")
+ tx = tx_from_hex(chain_hex[1])
+ tx.vout[-1].scriptPubKey = b'a' * 10001 # scriptPubKey bigger than 10k IsUnspendable
+ chain_hex = [chain_hex[0], tx.serialize().hex()]
+ # burn test is run before any package evaluation; nothing makes it in and we get broader exception
+ assert_raises_rpc_error(-25, "Unspendable output exceeds maximum configured by user", node.submitpackage, chain_hex, 0, chained_txns[1]["new_utxo"]["value"] - Decimal("0.00000001"))
+ assert_equal(node.getrawmempool(), [])
+
+ # Relax the restrictions for both and send it; parent gets through as own subpackage
+ pkg_result = node.submitpackage(chain_hex, maxfeerate=minrate_btc_kvb, maxburnamount=chained_txns[1]["new_utxo"]["value"])
+ assert "error" not in pkg_result["tx-results"][chained_txns[0]["wtxid"]]
+ assert_equal(pkg_result["tx-results"][tx.getwtxid()]["error"], "scriptpubkey")
+ assert_equal(node.getrawmempool(), [chained_txns[0]["txid"]])
+
if __name__ == "__main__":
RPCPackagesTest().main()
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index c12865b5e3..12697e9d0c 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -73,9 +73,8 @@ class RawTransactionsTest(BitcoinTestFramework):
["-txindex"],
["-fastprune", "-prune=1"],
]
- # whitelist all peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.supports_cli = False
def setup_network(self):
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 1780678de1..4e496a9275 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -46,6 +46,7 @@ MAX_PROTOCOL_MESSAGE_LENGTH = 4000000 # Maximum length of incoming protocol mes
MAX_HEADERS_RESULTS = 2000 # Number of headers sent in one getheaders result
MAX_INV_SIZE = 50000 # Maximum number of entries in an 'inv' protocol message
+NODE_NONE = 0
NODE_NETWORK = (1 << 0)
NODE_BLOOM = (1 << 2)
NODE_WITNESS = (1 << 3)
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index 30a4a58d6f..08d41fe97f 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -158,3 +158,12 @@ def test_ipv6_local():
except socket.error:
have_ipv6 = False
return have_ipv6
+
+def test_unix_socket():
+ '''Return True if UNIX sockets are available on this platform.'''
+ try:
+ socket.AF_UNIX
+ except AttributeError:
+ return False
+ else:
+ return True
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 475a05eb15..c3884270da 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -96,6 +96,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
self.chain: str = 'regtest'
self.setup_clean_chain: bool = False
+ self.noban_tx_relay: bool = False
self.nodes: list[TestNode] = []
self.extra_args = None
self.network_thread = None
@@ -498,6 +499,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_confs = [[]] * num_nodes
if extra_args is None:
extra_args = [[]] * num_nodes
+ # Whitelist peers to speed up tx relay / mempool sync. Don't use it if testing tx relay or timing.
+ if self.noban_tx_relay:
+ for i in range(len(extra_args)):
+ extra_args[i] = extra_args[i] + ["-whitelist=noban,in,out@127.0.0.1"]
if versions is None:
versions = [None] * num_nodes
if binary is None:
@@ -581,10 +586,16 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Wait for nodes to stop
node.wait_until_stopped()
- def restart_node(self, i, extra_args=None):
+ def restart_node(self, i, extra_args=None, clear_addrman=False):
"""Stop and start a test node"""
self.stop_node(i)
- self.start_node(i, extra_args)
+ if clear_addrman:
+ peers_dat = self.nodes[i].chain_path / "peers.dat"
+ os.remove(peers_dat)
+ with self.nodes[i].assert_debug_log(expected_msgs=[f'Creating peers.dat because the file was not found ("{peers_dat}")']):
+ self.start_node(i, extra_args)
+ else:
+ self.start_node(i, extra_args)
def wait_for_node_exit(self, i, timeout):
self.nodes[i].process.wait(timeout)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index ef6b9dd5ea..67e0be5280 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -724,7 +724,7 @@ class TestNode():
return p2p_conn
- def add_outbound_p2p_connection(self, p2p_conn, *, wait_for_verack=True, p2p_idx, connection_type="outbound-full-relay", supports_v2_p2p=None, advertise_v2_p2p=None, **kwargs):
+ def add_outbound_p2p_connection(self, p2p_conn, *, wait_for_verack=True, wait_for_disconnect=False, p2p_idx, connection_type="outbound-full-relay", supports_v2_p2p=None, advertise_v2_p2p=None, **kwargs):
"""Add an outbound p2p connection from node. Must be an
"outbound-full-relay", "block-relay-only", "addr-fetch" or "feeler" connection.
@@ -771,7 +771,7 @@ class TestNode():
if reconnect:
p2p_conn.wait_for_reconnect()
- if connection_type == "feeler":
+ if connection_type == "feeler" or wait_for_disconnect:
# feeler connections are closed as soon as the node receives a `version` message
p2p_conn.wait_until(lambda: p2p_conn.message_count["version"] == 1, check_connected=False)
p2p_conn.wait_until(lambda: not p2p_conn.is_connected, check_connected=False)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 59931bc208..1408854e02 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -395,6 +395,8 @@ BASE_SCRIPTS = [
'rpc_getdescriptorinfo.py',
'rpc_mempool_info.py',
'rpc_help.py',
+ 'p2p_handshake.py',
+ 'p2p_handshake.py --v2transport',
'feature_dirsymlinks.py',
'feature_help.py',
'feature_shutdown.py',
@@ -614,14 +616,12 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
max_len_name = len(max(test_list, key=len))
test_count = len(test_list)
all_passed = True
- i = 0
- while i < test_count:
+ while not job_queue.done():
if failfast and not all_passed:
break
for test_result, testdir, stdout, stderr, skip_reason in job_queue.get_next():
test_results.append(test_result)
- i += 1
- done_str = "{}/{} - {}{}{}".format(i, test_count, BOLD[1], test_result.name, BOLD[0])
+ done_str = f"{len(test_results)}/{test_count} - {BOLD[1]}{test_result.name}{BOLD[0]}"
if test_result.status == "Passed":
logging.debug("%s passed, Duration: %s s" % (done_str, test_result.time))
elif test_result.status == "Skipped":
@@ -706,14 +706,15 @@ class TestHandler:
self.tmpdir = tmpdir
self.test_list = test_list
self.flags = flags
- self.num_running = 0
self.jobs = []
self.use_term_control = use_term_control
+ def done(self):
+ return not (self.jobs or self.test_list)
+
def get_next(self):
- while self.num_running < self.num_jobs and self.test_list:
+ while len(self.jobs) < self.num_jobs and self.test_list:
# Add tests
- self.num_running += 1
test = self.test_list.pop(0)
portseed = len(self.test_list)
portseed_arg = ["--portseed={}".format(portseed)]
@@ -757,7 +758,6 @@ class TestHandler:
skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1)
else:
status = "Failed"
- self.num_running -= 1
self.jobs.remove(job)
if self.use_term_control:
clearline = '\r' + (' ' * dot_count) + '\r'
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 2691507773..e69546bb82 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -28,8 +28,7 @@ class AbandonConflictTest(BitcoinTestFramework):
self.num_nodes = 2
self.extra_args = [["-minrelaytxfee=0.00001"], []]
# whitelist peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
+ self.noban_tx_relay = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index be5b3ebadb..6b27b32dea 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -79,9 +79,8 @@ class AddressTypeTest(BitcoinTestFramework):
["-changetype=p2sh-segwit"],
[],
]
- # whitelist all peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.supports_cli = False
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_assumeutxo.py b/test/functional/wallet_assumeutxo.py
index 3c1a997bd1..30396da015 100755
--- a/test/functional/wallet_assumeutxo.py
+++ b/test/functional/wallet_assumeutxo.py
@@ -62,8 +62,6 @@ class AssumeutxoTest(BitcoinTestFramework):
for n in self.nodes:
n.setmocktime(n.getblockheader(n.getbestblockhash())['time'])
- self.sync_blocks()
-
n0.createwallet('w')
w = n0.get_wallet_rpc("w")
diff --git a/test/functional/wallet_avoid_mixing_output_types.py b/test/functional/wallet_avoid_mixing_output_types.py
index 861765f452..66fbf780e5 100755
--- a/test/functional/wallet_avoid_mixing_output_types.py
+++ b/test/functional/wallet_avoid_mixing_output_types.py
@@ -112,15 +112,15 @@ class AddressInputTypeGrouping(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [
[
"-addresstype=bech32",
- "-whitelist=noban@127.0.0.1",
"-txindex",
],
[
"-addresstype=p2sh-segwit",
- "-whitelist=noban@127.0.0.1",
"-txindex",
],
]
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 9d3c55d6b6..4983bfda7f 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -69,9 +69,8 @@ class AvoidReuseTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- # This test isn't testing txn relay/timing, so set whitelist on the
- # peers for instant txn relay. This speeds up the test run time 2-3x.
- self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index eb3e0ae728..d03b08bcc4 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -50,13 +50,14 @@ class WalletBackupTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
- # nodes 1, 2,3 are spenders, let's give them a keypool=100
- # whitelist all peers to speed up tx relay / mempool sync
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
+ # nodes 1, 2, 3 are spenders, let's give them a keypool=100
self.extra_args = [
- ["-whitelist=noban@127.0.0.1", "-keypool=100"],
- ["-whitelist=noban@127.0.0.1", "-keypool=100"],
- ["-whitelist=noban@127.0.0.1", "-keypool=100"],
- ["-whitelist=noban@127.0.0.1"],
+ ["-keypool=100"],
+ ["-keypool=100"],
+ ["-keypool=100"],
+ [],
]
self.rpc_timeout = 120
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index af9270a321..c322ae52c1 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -53,15 +53,14 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [
# Limit mempool descendants as a hack to have wallet txs rejected from the mempool.
# Set walletrejectlongchains=0 so the wallet still creates the transactions.
['-limitdescendantcount=3', '-walletrejectlongchains=0'],
[],
]
- # whitelist peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index f798eee365..31d3c14e55 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -32,8 +32,10 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [[
- "-dustrelayfee=0", "-walletrejectlongchains=0", "-whitelist=noban@127.0.0.1"
+ "-dustrelayfee=0", "-walletrejectlongchains=0"
]] * self.num_nodes
self.setup_clean_chain = True
self.supports_cli = False
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index fea933a93b..5b7db55f45 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -55,11 +55,12 @@ class BumpFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.setup_clean_chain = True
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [[
"-walletrbf={}".format(i),
"-mintxfee=0.00002",
"-addresstype=bech32",
- "-whitelist=noban@127.0.0.1",
] for i in range(self.num_nodes)]
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index d886a59ac1..ff4648e638 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -45,9 +45,8 @@ class RawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
- # This test isn't testing tx relay. Set whitelist on the peers for
- # instant tx relay.
- self.extra_args = [['-whitelist=noban@127.0.0.1']] * self.num_nodes
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.rpc_timeout = 90 # to prevent timeouts in `test_transaction_too_large`
def skip_test_if_missing_module(self):
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index bdb9081261..3b407c285d 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -22,6 +22,8 @@ class WalletGroupTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 5
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [
[],
[],
@@ -31,7 +33,6 @@ class WalletGroupTest(BitcoinTestFramework):
]
for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1") # whitelist peers to speed up tx relay / mempool sync
args.append(f"-paytxfee={20 * 1e3 / 1e8}") # apply feerate of 20 sats/vB across all nodes
self.rpc_timeout = 480
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 0f4b7cfcb1..52161043ea 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -23,8 +23,7 @@ class WalletHDTest(BitcoinTestFramework):
self.num_nodes = 2
self.extra_args = [[], ['-keypool=0']]
# whitelist peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
+ self.noban_tx_relay = True
self.supports_cli = False
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index e647fb2d5c..2a9435b370 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -160,6 +160,8 @@ class ImportRescanTest(BitcoinTestFramework):
self.num_nodes = 2 + len(IMPORT_NODES)
self.supports_cli = False
self.rpc_timeout = 120
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -177,7 +179,7 @@ class ImportRescanTest(BitcoinTestFramework):
self.import_deterministic_coinbase_privkeys()
self.stop_nodes()
- self.start_nodes(extra_args=[["-whitelist=noban@127.0.0.1"]] * self.num_nodes)
+ self.start_nodes()
for i in range(1, self.num_nodes):
self.connect_nodes(i, 0)
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index 1f1f92589c..420bdffc49 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -36,12 +36,11 @@ class ImportDescriptorsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [["-addresstype=legacy"],
["-addresstype=bech32", "-keypool=5"]
]
- # whitelist peers to speed up tx relay / mempool sync
- for args in self.extra_args:
- args.append("-whitelist=noban@127.0.0.1")
self.setup_clean_chain = True
self.wallet_names = []
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index 48180e8294..e1bd85d8a9 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -25,8 +25,10 @@ class KeypoolRestoreTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 4
- self.extra_args = [[], ['-keypool=100'], ['-keypool=100'], ['-keypool=100']]
+ self.num_nodes = 5
+ self.extra_args = [[]]
+ for _ in range(self.num_nodes - 1):
+ self.extra_args.append(['-keypool=100'])
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -40,12 +42,13 @@ class KeypoolRestoreTest(BitcoinTestFramework):
self.stop_node(1)
shutil.copyfile(wallet_path, wallet_backup_path)
self.start_node(1, self.extra_args[1])
- self.connect_nodes(0, 1)
- self.connect_nodes(0, 2)
- self.connect_nodes(0, 3)
-
- for i, output_type in enumerate(["legacy", "p2sh-segwit", "bech32"]):
+ for i in [1, 2, 3, 4]:
+ self.connect_nodes(0, i)
+ output_types = ["legacy", "p2sh-segwit", "bech32"]
+ if self.options.descriptors:
+ output_types.append("bech32m")
+ for i, output_type in enumerate(output_types):
self.log.info("Generate keys for wallet with address type: {}".format(output_type))
idx = i+1
for _ in range(90):
@@ -59,9 +62,10 @@ class KeypoolRestoreTest(BitcoinTestFramework):
assert not address_details["isscript"] and not address_details["iswitness"]
elif i == 1:
assert address_details["isscript"] and not address_details["iswitness"]
- else:
+ elif i == 2:
assert not address_details["isscript"] and address_details["iswitness"]
-
+ elif i == 3:
+ assert address_details["isscript"] and address_details["iswitness"]
self.log.info("Send funds to wallet")
self.nodes[0].sendtoaddress(addr_oldpool, 10)
@@ -87,6 +91,8 @@ class KeypoolRestoreTest(BitcoinTestFramework):
assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/49h/1h/0h/0/110")
elif output_type == 'bech32':
assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/84h/1h/0h/0/110")
+ elif output_type == 'bech32m':
+ assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/86h/1h/0h/0/110")
else:
assert_equal(self.nodes[idx].getaddressinfo(self.nodes[idx].getnewaddress(address_type=output_type))['hdkeypath'], "m/0'/0'/110'")
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 8ec21484d1..d0f1336a5e 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -22,7 +22,7 @@ class ReceivedByTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
# whitelist peers to speed up tx relay / mempool sync
- self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
+ self.noban_tx_relay = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index a19a3ac2cb..fd586d546e 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -26,7 +26,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
self.num_nodes = 4
self.setup_clean_chain = True
# whitelist peers to speed up tx relay / mempool sync
- self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
+ self.noban_tx_relay = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py
index 064ce12108..c820eaa6f6 100755
--- a/test/functional/wallet_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -26,9 +26,9 @@ class ListTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
- # This test isn't testing txn relay/timing, so set whitelist on the
- # peers for instant txn relay. This speeds up the test run time 2-3x.
- self.extra_args = [["-whitelist=noban@127.0.0.1", "-walletrbf=0"]] * self.num_nodes
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
+ self.extra_args = [["-walletrbf=0"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index 580a9d2b22..e4ca341b49 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -30,10 +30,11 @@ class WalletSendTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- # whitelist all peers to speed up tx relay / mempool sync
+ # whitelist peers to speed up tx relay / mempool sync
+ self.noban_tx_relay = True
self.extra_args = [
- ["-whitelist=127.0.0.1","-walletrbf=1"],
- ["-whitelist=127.0.0.1","-walletrbf=1"],
+ ["-walletrbf=1"],
+ ["-walletrbf=1"]
]
getcontext().prec = 8 # Satoshi precision for Decimal