aboutsummaryrefslogtreecommitdiff
path: root/test/functional/test_framework
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/test_framework')
-rw-r--r--test/functional/test_framework/crypto/bip324_cipher.py8
-rwxr-xr-xtest/functional/test_framework/messages.py1
-rw-r--r--test/functional/test_framework/netutil.py9
-rwxr-xr-xtest/functional/test_framework/p2p.py30
-rwxr-xr-xtest/functional/test_framework/test_framework.py19
-rwxr-xr-xtest/functional/test_framework/test_node.py8
-rw-r--r--test/functional/test_framework/util.py17
7 files changed, 67 insertions, 25 deletions
diff --git a/test/functional/test_framework/crypto/bip324_cipher.py b/test/functional/test_framework/crypto/bip324_cipher.py
index 56190647f2..c9f0fa0151 100644
--- a/test/functional/test_framework/crypto/bip324_cipher.py
+++ b/test/functional/test_framework/crypto/bip324_cipher.py
@@ -25,6 +25,8 @@ def pad16(x):
def aead_chacha20_poly1305_encrypt(key, nonce, aad, plaintext):
"""Encrypt a plaintext using ChaCha20Poly1305."""
+ if plaintext is None:
+ return None
ret = bytearray()
msg_len = len(plaintext)
for i in range((msg_len + 63) // 64):
@@ -42,7 +44,7 @@ def aead_chacha20_poly1305_encrypt(key, nonce, aad, plaintext):
def aead_chacha20_poly1305_decrypt(key, nonce, aad, ciphertext):
"""Decrypt a ChaCha20Poly1305 ciphertext."""
- if len(ciphertext) < 16:
+ if ciphertext is None or len(ciphertext) < 16:
return None
msg_len = len(ciphertext) - 16
poly1305 = Poly1305(chacha20_block(key, nonce, 0)[:32])
@@ -191,11 +193,11 @@ class TestFrameworkAEAD(unittest.TestCase):
dec_aead = FSChaCha20Poly1305(key)
for _ in range(msg_idx):
- enc_aead.encrypt(b"", b"")
+ enc_aead.encrypt(b"", None)
ciphertext = enc_aead.encrypt(aad, plain)
self.assertEqual(hex_cipher, ciphertext.hex())
for _ in range(msg_idx):
- dec_aead.decrypt(b"", bytes(16))
+ dec_aead.decrypt(b"", None)
plaintext = dec_aead.decrypt(aad, ciphertext)
self.assertEqual(plain, plaintext)
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/p2p.py b/test/functional/test_framework/p2p.py
index dc04696114..ce76008c46 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -585,22 +585,22 @@ class P2PInterface(P2PConnection):
wait_until_helper_internal(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor)
- def wait_for_connect(self, timeout=60):
+ def wait_for_connect(self, *, timeout=60):
test_function = lambda: self.is_connected
self.wait_until(test_function, timeout=timeout, check_connected=False)
- def wait_for_disconnect(self, timeout=60):
+ def wait_for_disconnect(self, *, timeout=60):
test_function = lambda: not self.is_connected
self.wait_until(test_function, timeout=timeout, check_connected=False)
- def wait_for_reconnect(self, timeout=60):
+ def wait_for_reconnect(self, *, timeout=60):
def test_function():
return self.is_connected and self.last_message.get('version') and not self.supports_v2_p2p
self.wait_until(test_function, timeout=timeout, check_connected=False)
# Message receiving helper methods
- def wait_for_tx(self, txid, timeout=60):
+ def wait_for_tx(self, txid, *, timeout=60):
def test_function():
if not self.last_message.get('tx'):
return False
@@ -608,13 +608,13 @@ class P2PInterface(P2PConnection):
self.wait_until(test_function, timeout=timeout)
- def wait_for_block(self, blockhash, timeout=60):
+ def wait_for_block(self, blockhash, *, timeout=60):
def test_function():
return self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
self.wait_until(test_function, timeout=timeout)
- def wait_for_header(self, blockhash, timeout=60):
+ def wait_for_header(self, blockhash, *, timeout=60):
def test_function():
last_headers = self.last_message.get('headers')
if not last_headers:
@@ -623,7 +623,7 @@ class P2PInterface(P2PConnection):
self.wait_until(test_function, timeout=timeout)
- def wait_for_merkleblock(self, blockhash, timeout=60):
+ def wait_for_merkleblock(self, blockhash, *, timeout=60):
def test_function():
last_filtered_block = self.last_message.get('merkleblock')
if not last_filtered_block:
@@ -632,7 +632,7 @@ class P2PInterface(P2PConnection):
self.wait_until(test_function, timeout=timeout)
- def wait_for_getdata(self, hash_list, timeout=60):
+ def wait_for_getdata(self, hash_list, *, timeout=60):
"""Waits for a getdata message.
The object hashes in the inventory vector must match the provided hash_list."""
@@ -644,7 +644,7 @@ class P2PInterface(P2PConnection):
self.wait_until(test_function, timeout=timeout)
- def wait_for_getheaders(self, timeout=60):
+ def wait_for_getheaders(self, *, timeout=60):
"""Waits for a getheaders message.
Receiving any getheaders message will satisfy the predicate. the last_message["getheaders"]
@@ -656,7 +656,7 @@ class P2PInterface(P2PConnection):
self.wait_until(test_function, timeout=timeout)
- def wait_for_inv(self, expected_inv, timeout=60):
+ def wait_for_inv(self, expected_inv, *, timeout=60):
"""Waits for an INV message and checks that the first inv object in the message was as expected."""
if len(expected_inv) > 1:
raise NotImplementedError("wait_for_inv() will only verify the first inv object")
@@ -668,7 +668,7 @@ class P2PInterface(P2PConnection):
self.wait_until(test_function, timeout=timeout)
- def wait_for_verack(self, timeout=60):
+ def wait_for_verack(self, *, timeout=60):
def test_function():
return "verack" in self.last_message
@@ -681,11 +681,11 @@ class P2PInterface(P2PConnection):
self.send_message(self.on_connection_send_msg)
self.on_connection_send_msg = None # Never used again
- def send_and_ping(self, message, timeout=60):
+ def send_and_ping(self, message, *, timeout=60):
self.send_message(message)
self.sync_with_ping(timeout=timeout)
- def sync_with_ping(self, timeout=60):
+ def sync_with_ping(self, *, timeout=60):
"""Ensure ProcessMessages and SendMessages is called on this connection"""
# Sending two pings back-to-back, requires that the node calls
# `ProcessMessage` twice, and thus ensures `SendMessages` must have
@@ -726,7 +726,7 @@ class NetworkThread(threading.Thread):
"""Start the network thread."""
self.network_event_loop.run_forever()
- def close(self, timeout=10):
+ def close(self, *, timeout=10):
"""Close the connections and network event loop."""
self.network_event_loop.call_soon_threadsafe(self.network_event_loop.stop)
wait_until_helper_internal(lambda: not self.network_event_loop.is_running(), timeout=timeout)
@@ -933,7 +933,7 @@ class P2PTxInvStore(P2PInterface):
with p2p_lock:
return list(self.tx_invs_received.keys())
- def wait_for_broadcast(self, txns, timeout=60):
+ def wait_for_broadcast(self, txns, *, timeout=60):
"""Waits for the txns (list of txids) to complete initial broadcast.
The mempool should mark unbroadcast=False for these transactions.
"""
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index e7e4172cf6..a2f767cc98 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
@@ -191,6 +192,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser.add_argument("--timeout-factor", dest="timeout_factor", type=float, help="adjust test timeouts by a factor. Setting it to 0 disables all timeouts")
parser.add_argument("--v2transport", dest="v2transport", default=False, action="store_true",
help="use BIP324 v2 connections between all nodes by default")
+ parser.add_argument("--v1transport", dest="v1transport", default=False, action="store_true",
+ help="Explicitly use v1 transport (can be used to overwrite global --v2transport option)")
self.add_options(parser)
# Running TestShell in a Jupyter notebook causes an additional -f argument
@@ -206,6 +209,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
config = configparser.ConfigParser()
config.read_file(open(self.options.configfile))
self.config = config
+ if self.options.v1transport:
+ self.options.v2transport=False
if "descriptors" not in self.options:
# Wallet is not required by the test at all and the value of self.options.descriptors won't matter.
@@ -494,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:
@@ -577,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 3baa78fd79..67e0be5280 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -136,9 +136,7 @@ class TestNode():
self.args.append("-v2transport=1")
else:
self.args.append("-v2transport=0")
- else:
- # v2transport requested but not supported for node
- assert not v2transport
+ # if v2transport is requested via global flag but not supported for node version, ignore it
self.cli = TestNodeCLI(bitcoin_cli, self.datadir_path)
self.use_cli = use_cli
@@ -726,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.
@@ -773,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_framework/util.py b/test/functional/test_framework/util.py
index b4b05b1597..c5b69a3954 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -52,7 +52,24 @@ def assert_fee_amount(fee, tx_size, feerate_BTC_kvB):
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
+def summarise_dict_differences(thing1, thing2):
+ if not isinstance(thing1, dict) or not isinstance(thing2, dict):
+ return thing1, thing2
+ d1, d2 = {}, {}
+ for k in sorted(thing1.keys()):
+ if k not in thing2:
+ d1[k] = thing1[k]
+ elif thing1[k] != thing2[k]:
+ d1[k], d2[k] = summarise_dict_differences(thing1[k], thing2[k])
+ for k in sorted(thing2.keys()):
+ if k not in thing1:
+ d2[k] = thing2[k]
+ return d1, d2
+
def assert_equal(thing1, thing2, *args):
+ if thing1 != thing2 and not args and isinstance(thing1, dict) and isinstance(thing2, dict):
+ d1,d2 = summarise_dict_differences(thing1, thing2)
+ raise AssertionError("not(%s == %s)\n in particular not(%s == %s)" % (thing1, thing2, d1, d2))
if thing1 != thing2 or any(thing1 != arg for arg in args):
raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))