diff --git a/qa/rpc-tests/regtest_block_relay.py b/qa/rpc-tests/regtest_block_relay.py new file mode 100755 index 000000000..6d6519ec2 --- /dev/null +++ b/qa/rpc-tests/regtest_block_relay.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026 The Zcash developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . +# +# Regression test for https://github.com/ZcashFoundation/zebra/issues/10332 +# +# Verifies that blocks mined on node0 propagate through a linear A-B-C topology +# to node2, which neither mines blocks nor is directly connected to node0. +# Previously, node1 would receive the block but fail to rebroadcast it to node2. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, connect_nodes + +class RegtestBlockRelayTest(BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.cache_behavior = 'clean' + self.num_nodes = 3 + self.num_wallets = 0 + + def setup_network(self): + # Start nodes without connecting them; run_test manages the topology. + self.nodes = self.setup_nodes() + self.zainos = self.setup_indexers() + self.wallets = self.setup_wallets() + self.is_network_split = False + + def run_test(self): + # Build linear topology: node0 -- node1 -- node2 + # node0 mines, node1 relays, node2 only receives. + connect_nodes(self.nodes[0], 1) + connect_nodes(self.nodes[1], 2) + + self.nodes[0].generate(10) + self.sync_all(False) + + for i in range(len(self.nodes)): + assert_equal(self.nodes[i].getbestblockheightandhash()['height'], 10) + +if __name__ == '__main__': + RegtestBlockRelayTest().main() \ No newline at end of file diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 13c4d68f0..983ac4a67 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -377,30 +377,6 @@ def rebuild_cache(): block_time += PRE_BLOSSOM_BLOCK_TARGET_SPACING # Must sync before next peer starts generating blocks sync_blocks(rpcs) - # Shut down and restart every zebrad node. - # This works around a zebrad problem where it won't broadcast - # received blocks to other connected nodes, and is a workaround - # for zebrad not supporting `addnode remove`. - # TODO: Remove this workaround once either of the following is resolved: - # - https://github.com/ZcashFoundation/zebra/issues/10329 - # - https://github.com/ZcashFoundation/zebra/issues/10332 - stop_nodes(rpcs) - wait_bitcoinds() - for i in range(MAX_NODES): - config = zebrad_config(node_dir(cachedir, i)) - args = [ zcashd_binary(), "-c="+config, "start" ] - bitcoind_processes[i] = subprocess.Popen(args) - if os.getenv("PYTHON_DEBUG", ""): - print("initialize_chain: %s started, waiting for RPC to come up" % (zcashd_binary(),)) - wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i) - if os.getenv("PYTHON_DEBUG", ""): - print("initialize_chain: RPC successfully started") - for i in range(MAX_NODES): - try: - rpcs.append(get_rpc_proxy(rpc_url(i), i)) - except: - sys.stderr.write("Error connecting to "+rpc_url(i)+"\n") - sys.exit(1) # Check that local time isn't going backwards assert_greater_than(time.time() + 1, block_time) @@ -705,15 +681,18 @@ def wait_bitcoinds(): def connect_nodes(from_connection, node_num): ip_port = "127.0.0.1:"+str(p2p_port(node_num)) # TODO: Replace `add` with `onetry` if zebrad implements it. - from_connection.addnode(ip_port, "add") + # zebrad may reject a duplicate `addnode add` for a peer already in its + # addnode list (it doesn't support `addnode remove`), so ignore that error. + try: + from_connection.addnode(ip_port, "add") + except JSONRPCException: + pass # poll until version handshake complete to avoid race conditions # with transaction relaying while True: - for peer in from_connection.getpeerinfo(): - if peer['addr'] == ip_port: - return - else: - time.sleep(1) + if any(peer['addr'] == ip_port for peer in from_connection.getpeerinfo()): + return + time.sleep(1) def connect_nodes_bi(nodes, a, b): connect_nodes(nodes[a], b)