qa: port 12 wallet/mining tests to python3 and fix three framework blockers

Ports qa/rpc-tests from 6 python3 files to 18. Ran every ported test against
the freshly built v1.3.0 dragonxd. Results, honestly:

  PASS (1)            getblocktemplate_proposals.py
  NOT APPLICABLE (7)  wallet.py, walletbackup.py, wallet_protectcoinbase.py,
                      wallet_listnotes.py, wallet_mergetoaddress.py,
                      getblocktemplate.py, wallet_shieldcoinbase.py
  BLOCKED (4)         wallet_sapling, wallet_nullifiers, wallet_persistence,
                      wallet_treestate

The "not applicable" seven are inherited Zcash/Hush-era tests that exercise
features DragonX deliberately removed. They assume transparent t->t value
transfer, but ASSETCHAINS_PRIVATE=1 (hush_utils.h:1826) makes sendtoaddress
and sendmany consensus-refuse; they assume Sprout joinsplits, which are gone
from the RPC layer entirely; and they hardcode Bitcoin economics (10 coin/block,
100-block maturity) against DragonX's 3 DRGX and COINBASE_MATURITY=1. They are
ported and left in place rather than deleted, but they cannot pass on this chain
without being rewritten around z_shieldcoinbase/autoshield.

Three framework fixes in test_framework/util.py, each of which broke every
multi-node test:
  - initialize_chain() passed -connect=0, and init.cpp soft-sets -listen=0 when
    -connect is present, so cache node0 never opened its p2p port and nodes 1-3
    could never sync to it -- initialize_chain() hung forever in sync_blocks().
    Now passes -listen=1 -bind=127.0.0.1 -dnsseed=0 explicitly (an explicit arg
    beats SoftSetBoolArg) while keeping the cache nodes off the public network.
  - cache cleanup removed files from <datadir> when dragonxd writes them one
    level deeper into the net-specific <datadir>/regtest.
  - set_node_times() did print("..." + t) with t an int -> TypeError.
  - default binary paths corrected to src/dragonxd and src/dragonx-cli.

The four BLOCKED tests are blocked by a daemon assert, not by the port; see the
follow-up commit/report on BLOCK_VALID_CONTEXT.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU87LdsJZiZkfq1eXubpeo
This commit is contained in:
2026-08-31 01:53:43 -05:00
parent e2e10f6ef8
commit c5fde12485
13 changed files with 485 additions and 132 deletions

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# Copyright (c) 2016-2024 The Hush developers
# Copyright (c) 2018 The Zcash developers
# Distributed under the GPLv3 software license, see the accompanying
@@ -8,21 +8,99 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import (
assert_equal,
start_nodes,
initialize_chain_clean,
p2p_port,
set_node_times,
start_node,
sync_blocks,
wait_and_assert_operationid_status,
)
import os
import stat
from decimal import Decimal
# Test wallet behaviour with Sapling addresses
class WalletSaplingTest(BitcoinTestFramework):
def setup_chain(self):
# The shared initialize_chain() cache builder in test_framework/util.py has not been
# repaired for DragonX (it spawns the cache nodes by bare name off PATH, drives the
# CLI without -regtest so it dials the assetchain RPC port, and deletes debug.log
# from the non-net-specific datadir). Build the same starting state here instead --
# see _generate_starting_chain() -- so this test does not depend on it.
print("Initializing test directory " + self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
# !!! test_framework/util.py:start_node() hardcodes "-connect=0" into every regtest
# node's argv. Modern Bitcoin Core special-cases that value to mean "make no automatic
# connections", but THIS codebase does not (net.cpp ThreadOpenConnections just iterates
# mapMultiArgs["-connect"]), so "0" is dialled as a hostname: it resolves to 0.0.0.0,
# which on Linux connects to localhost on Params().GetDefaultPort() -- 21768, the live
# DRAGONX p2p port. Observed directly: a regtest node started by the unmodified
# framework peered with the production dragonxd on this host and with seven public
# mainnet nodes (heights ~3.25M) and began ingesting mainnet headers. "-connect" also
# soft-sets "-listen=0", so the framework's own connect_nodes_bi() can never establish
# the local links a multi-node test needs.
#
# Both problems are in the shared framework, which this port is not allowed to touch, so
# they are worked around per-node here: the daemon is launched through a tiny wrapper
# that strips the "-connect=0" argument, and the real topology/listening flags are passed
# as extra_args (which start_node appends after its own).
def _daemon_wrapper(self):
srcdir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "..", "..", "src", "dragonxd")
path = os.path.join(self.options.tmpdir, "dragonxd-no-connect0")
with open(path, "w") as f:
f.write("#!/usr/bin/env bash\n")
f.write("args=()\n")
f.write('for a in "$@"; do\n')
f.write(' if [ "$a" = "-connect=0" ]; then continue; fi\n')
f.write(' args+=("$a")\n')
f.write("done\n")
f.write('exec %s "${args[@]}"\n' % os.path.realpath(srcdir))
os.chmod(path, os.stat(path).st_mode | stat.S_IXUSR)
return path
def setup_nodes(self):
return start_nodes(4, self.options.tmpdir, [[
#'-nuparams=5ba81b19:201', # Overwinter
#'-nuparams=76b809bb:203', # Sapling
#'-experimentalfeatures', '-zmergetoaddress',
]] * 4)
binary = self._daemon_wrapper()
nodes = []
for i in range(4):
extra_args = [
#'-nuparams=5ba81b19:201', # Overwinter
#'-nuparams=76b809bb:203', # Sapling
#'-experimentalfeatures', '-zmergetoaddress',
# Listen on this test's PID-keyed port so the nodes can actually peer with
# each other, and only ever dial each other -- never the public network.
'-listen=1',
'-bind=127.0.0.1',
'-port=%d' % p2p_port(i),
] + ['-connect=127.0.0.1:%d' % p2p_port(j) for j in range(4) if j != i]
nodes.append(start_node(i, self.options.tmpdir, extra_args, binary=binary))
return nodes
def setup_network(self, split=False):
super(WalletSaplingTest, self).setup_network(split)
self._generate_starting_chain()
def _generate_starting_chain(self):
# Equivalent of test_framework.util.initialize_chain(): a 200-block chain where each
# of the 4 nodes mined 25 blocks twice, so every node holds 25 mature and 25 immature
# coinbases. Block timestamps are 10 minutes apart starting 1 Jan 2014, as there.
block_time = 1388534400
for _round in range(2):
for peer in range(4):
for _j in range(25):
set_node_times(self.nodes, block_time)
self.nodes[peer].generate(1)
block_time += 10 * 75
# Must sync before next peer starts generating blocks
sync_blocks(self.nodes)
# Drop back to wall-clock time: initialize_chain() stops the cache nodes and the test
# then runs against freshly started nodes that have no mocktime set.
set_node_times(self.nodes, 0)
self.sync_all()
def run_test(self):
# Sanity-check the test harness