Compare commits

...

7 Commits

Author SHA1 Message Date
Ryan Ofsky
32a617ebd1
Merge 8f5228d9239464ece06f3a56372aeec29685801c into 5f4422d68dc3530c353af1f87499de1c864b60ad 2025-03-17 09:50:15 +07:00
merge-script
5f4422d68d
Merge bitcoin/bitcoin#32010: qa: Fix TxIndex race conditions
3301d2cbe8c3b76c97285d75fa59637cb6952d0b qa: Wait for txindex to avoid race condition (Hodlinator)
9bfb0d75ba10591cc6c9620f9fd1ecc0e55e7a48 qa: Remove unnecessary -txindex args (Hodlinator)
7ac281c19cd3d11f316dbbb3308eabf1ad4f26d6 qa: Add missing coverage of corrupt indexes (Hodlinator)

Pull request description:

  - Add synchronization in 3 places where if the Transaction Index happens to be slow, we get rare test failures when querying it for transactions (one such case experienced on Windows, prompting investigation).
  - Remove unnecessary TxIndex initialization in some tests.
  - Add some test coverage where TxIndex aspect could be tested in feature_init.py.

ACKs for top commit:
  fjahr:
    re-ACK 3301d2cbe8c3b76c97285d75fa59637cb6952d0b
  mzumsande:
    Code Review ACK 3301d2cbe8c3b76c97285d75fa59637cb6952d0b
  furszy:
    Code review ACK 3301d2cbe8c3b76c97285d75fa59637cb6952d0b
  Prabhat1308:
    Concept ACK [`3301d2c`](3301d2cbe8)

Tree-SHA512: 7c2019e38455f344856aaf6b381faafbd88d53dc88d13309deb718c1dcfbee4ccca7c7f1b66917395503a6f94c3b216a007ad432cc8b93d0309db9805f38d602
2025-03-17 10:28:14 +08:00
Hodlinator
3301d2cbe8
qa: Wait for txindex to avoid race condition
Can be verified to be necessary through adding std::this_thread::sleep_for(0.5s) at the beginning of TxIndex::CustomAppend.
2025-03-10 15:24:16 +01:00
Hodlinator
9bfb0d75ba
qa: Remove unnecessary -txindex args
(Parent commit ensured indexes in feature_init.py are actually used, otherwise they would be removed here as well).
2025-03-07 22:22:31 +01:00
Hodlinator
7ac281c19c
qa: Add missing coverage of corrupt indexes 2025-03-07 22:22:31 +01:00
Ryan Ofsky
8f5228d923 test, contrib: Fix signer/miner command line escaping
Pass bitcoin binary command lines from test framework to signet/miner utility
using shell escaping so they are unambigous and don't get mangled if they
contain spaces.

This change is not needed for tests to pass currently, but is a useful change
to avoid CI failures in followup PR
https://github.com/bitcoin/bitcoin/pull/31375 and to avoid other bugs.
2025-02-14 08:19:12 -05:00
Ryan Ofsky
e60ff180c2 test, refactor: Add TestNode.binaries to hold binary paths
Add new TestNode.binaries object to manage paths to bitcoin binaries.

Having this object makes it possible for the test framework to exercise the
bitcoin wrapper executable introduced in
https://github.com/bitcoin/bitcoin/pull/31375 and also makes it easier to add
new binaries and options and environment variables controlling how they are
invoked, because logic for invoking them that was previously spread out is now
consolidated in one place.

Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
2025-02-14 08:19:12 -05:00
12 changed files with 113 additions and 47 deletions

View File

@ -9,7 +9,7 @@ import logging
import math import math
import os import os
import re import re
import struct import shlex
import sys import sys
import time import time
import subprocess import subprocess
@ -86,7 +86,7 @@ def finish_block(block, signet_solution, grind_cmd):
block.solve() block.solve()
else: else:
headhex = CBlockHeader.serialize(block).hex() headhex = CBlockHeader.serialize(block).hex()
cmd = grind_cmd.split(" ") + [headhex] cmd = shlex.split(grind_cmd) + [headhex]
newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip() newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip()
newhead = from_hex(CBlockHeader(), newheadhex.decode('utf8')) newhead = from_hex(CBlockHeader(), newheadhex.decode('utf8'))
block.nNonce = newhead.nNonce block.nNonce = newhead.nNonce
@ -479,7 +479,7 @@ def do_calibrate(args):
header.nTime = i header.nTime = i
header.nNonce = 0 header.nNonce = 0
headhex = header.serialize().hex() headhex = header.serialize().hex()
cmd = args.grind_cmd.split(" ") + [headhex] cmd = shlex.split(args.grind_cmd) + [headhex]
newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip() newheadhex = subprocess.run(cmd, stdout=subprocess.PIPE, input=b"", check=True).stdout.strip()
avg = (time.time() - start) * 1.0 / TRIALS avg = (time.time() - start) * 1.0 / TRIALS
@ -549,7 +549,7 @@ def main():
args = parser.parse_args(sys.argv[1:]) args = parser.parse_args(sys.argv[1:])
args.bcli = lambda *a, input=b"", **kwargs: bitcoin_cli(args.cli.split(" "), list(a), input=input, **kwargs) args.bcli = lambda *a, input=b"", **kwargs: bitcoin_cli(shlex.split(args.cli), list(a), input=input, **kwargs)
if hasattr(args, "address") and hasattr(args, "descriptor"): if hasattr(args, "address") and hasattr(args, "descriptor"):
args.derived_addresses = {} args.derived_addresses = {}

View File

@ -88,7 +88,7 @@ class InitTest(BitcoinTestFramework):
args = ['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1'] args = ['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1']
for terminate_line in lines_to_terminate_after: for terminate_line in lines_to_terminate_after:
self.log.info(f"Starting node and will exit after line {terminate_line}") self.log.info(f"Starting node and will terminate after line {terminate_line}")
with node.busy_wait_for_debug_log([terminate_line]): with node.busy_wait_for_debug_log([terminate_line]):
if platform.system() == 'Windows': if platform.system() == 'Windows':
# CREATE_NEW_PROCESS_GROUP is required in order to be able # CREATE_NEW_PROCESS_GROUP is required in order to be able
@ -108,12 +108,22 @@ class InitTest(BitcoinTestFramework):
'blocks/index/*.ldb': 'Error opening block database.', 'blocks/index/*.ldb': 'Error opening block database.',
'chainstate/*.ldb': 'Error opening coins database.', 'chainstate/*.ldb': 'Error opening coins database.',
'blocks/blk*.dat': 'Error loading block database.', 'blocks/blk*.dat': 'Error loading block database.',
'indexes/txindex/MANIFEST*': 'LevelDB error: Corruption: CURRENT points to a non-existent file',
# Removing these files does not result in a startup error:
# 'indexes/blockfilter/basic/*.dat', 'indexes/blockfilter/basic/db/*.*', 'indexes/coinstats/db/*.*',
# 'indexes/txindex/*.log', 'indexes/txindex/CURRENT', 'indexes/txindex/LOCK'
} }
files_to_perturb = { files_to_perturb = {
'blocks/index/*.ldb': 'Error loading block database.', 'blocks/index/*.ldb': 'Error loading block database.',
'chainstate/*.ldb': 'Error opening coins database.', 'chainstate/*.ldb': 'Error opening coins database.',
'blocks/blk*.dat': 'Corrupted block database detected.', 'blocks/blk*.dat': 'Corrupted block database detected.',
'indexes/blockfilter/basic/db/*.*': 'LevelDB error: Corruption',
'indexes/coinstats/db/*.*': 'LevelDB error: Corruption',
'indexes/txindex/*.log': 'LevelDB error: Corruption',
'indexes/txindex/CURRENT': 'LevelDB error: Corruption',
# Perturbing these files does not result in a startup error:
# 'indexes/blockfilter/basic/*.dat', 'indexes/txindex/MANIFEST*', 'indexes/txindex/LOCK'
} }
for file_patt, err_fragment in files_to_delete.items(): for file_patt, err_fragment in files_to_delete.items():
@ -135,9 +145,10 @@ class InitTest(BitcoinTestFramework):
self.stop_node(0) self.stop_node(0)
self.log.info("Test startup errors after perturbing certain essential files") self.log.info("Test startup errors after perturbing certain essential files")
dirs = ["blocks", "chainstate", "indexes"]
for file_patt, err_fragment in files_to_perturb.items(): for file_patt, err_fragment in files_to_perturb.items():
shutil.copytree(node.chain_path / "blocks", node.chain_path / "blocks_bak") for dir in dirs:
shutil.copytree(node.chain_path / "chainstate", node.chain_path / "chainstate_bak") shutil.copytree(node.chain_path / dir, node.chain_path / f"{dir}_bak")
target_files = list(node.chain_path.glob(file_patt)) target_files = list(node.chain_path.glob(file_patt))
for target_file in target_files: for target_file in target_files:
@ -151,10 +162,9 @@ class InitTest(BitcoinTestFramework):
start_expecting_error(err_fragment) start_expecting_error(err_fragment)
shutil.rmtree(node.chain_path / "blocks") for dir in dirs:
shutil.rmtree(node.chain_path / "chainstate") shutil.rmtree(node.chain_path / dir)
shutil.move(node.chain_path / "blocks_bak", node.chain_path / "blocks") shutil.move(node.chain_path / f"{dir}_bak", node.chain_path / dir)
shutil.move(node.chain_path / "chainstate_bak", node.chain_path / "chainstate")
def init_pid_test(self): def init_pid_test(self):
BITCOIN_PID_FILENAME_CUSTOM = "my_fancy_bitcoin_pid_file.foobar" BITCOIN_PID_FILENAME_CUSTOM = "my_fancy_bitcoin_pid_file.foobar"

View File

@ -45,6 +45,7 @@ from test_framework.util import (
assert_equal, assert_equal,
assert_greater_than, assert_greater_than,
assert_raises_rpc_error, assert_raises_rpc_error,
sync_txindex,
) )
from test_framework.wallet import MiniWallet from test_framework.wallet import MiniWallet
from test_framework.wallet_util import generate_keypair from test_framework.wallet_util import generate_keypair
@ -270,6 +271,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('A coinbase transaction') self.log.info('A coinbase transaction')
# Pick the input of the first tx we created, so it has to be a coinbase tx # Pick the input of the first tx we created, so it has to be a coinbase tx
sync_txindex(self, node)
raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid']) raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid'])
tx = tx_from_hex(raw_tx_coinbase_spent) tx = tx_from_hex(raw_tx_coinbase_spent)
self.check_mempool_result( self.check_mempool_result(

View File

@ -34,6 +34,7 @@ from test_framework.util import (
assert_equal, assert_equal,
assert_greater_than, assert_greater_than,
assert_raises_rpc_error, assert_raises_rpc_error,
sync_txindex,
) )
from test_framework.wallet import ( from test_framework.wallet import (
getnewdestination, getnewdestination,
@ -70,7 +71,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.num_nodes = 3 self.num_nodes = 3
self.extra_args = [ self.extra_args = [
["-txindex"], ["-txindex"],
["-txindex"], [],
["-fastprune", "-prune=1"], ["-fastprune", "-prune=1"],
] ]
# whitelist peers to speed up tx relay / mempool sync # whitelist peers to speed up tx relay / mempool sync
@ -109,6 +110,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.log.info(f"Test getrawtransaction {'with' if n == 0 else 'without'} -txindex") self.log.info(f"Test getrawtransaction {'with' if n == 0 else 'without'} -txindex")
if n == 0: if n == 0:
sync_txindex(self, self.nodes[n])
# With -txindex. # With -txindex.
# 1. valid parameters - only supply txid # 1. valid parameters - only supply txid
assert_equal(self.nodes[n].getrawtransaction(txId), tx['hex']) assert_equal(self.nodes[n].getrawtransaction(txId), tx['hex'])

View File

@ -12,6 +12,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
assert_raises_rpc_error, assert_raises_rpc_error,
sync_txindex,
) )
from test_framework.wallet import MiniWallet from test_framework.wallet import MiniWallet
@ -77,6 +78,7 @@ class MerkleBlockTest(BitcoinTestFramework):
assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2]))), sorted(txlist)) assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid1, txid2]))), sorted(txlist))
assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid2, txid1]))), sorted(txlist)) assert_equal(sorted(self.nodes[0].verifytxoutproof(self.nodes[0].gettxoutproof([txid2, txid1]))), sorted(txlist))
# We can always get a proof if we have a -txindex # We can always get a proof if we have a -txindex
sync_txindex(self, self.nodes[1])
assert_equal(self.nodes[0].verifytxoutproof(self.nodes[1].gettxoutproof([txid_spent])), [txid_spent]) assert_equal(self.nodes[0].verifytxoutproof(self.nodes[1].gettxoutproof([txid_spent])), [txid_spent])
# We can't get a proof if we specify transactions from different blocks # We can't get a proof if we specify transactions from different blocks
assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[0].gettxoutproof, [txid1, txid3]) assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[0].gettxoutproof, [txid1, txid3])

View File

@ -18,6 +18,7 @@ import subprocess
import sys import sys
import tempfile import tempfile
import time import time
import types
from .address import create_deterministic_address_bcrt1_p2tr_op_true from .address import create_deterministic_address_bcrt1_p2tr_op_true
from .authproxy import JSONRPCException from .authproxy import JSONRPCException
@ -56,6 +57,48 @@ class SkipTest(Exception):
self.message = message self.message = message
class Binaries:
"""Helper class to provide information about bitcoin binaries
Attributes:
paths: Object returned from get_binary_paths() containing information
which binaries and command lines to use from environment variables and
the config file.
bin_dir: An optional string containing a directory path to look for
binaries, which takes precedence over the paths above, if specified.
This is used by tests calling binaries from previous releases.
"""
def __init__(self, paths, bin_dir):
self.paths = paths
self.bin_dir = bin_dir
def daemon_argv(self):
"Return argv array that should be used to invoke bitcoind"
return self._argv(self.paths.bitcoind)
def rpc_argv(self):
"Return argv array that should be used to invoke bitcoin-cli"
return self._argv(self.paths.bitcoincli)
def util_argv(self):
"Return argv array that should be used to invoke bitcoin-util"
return self._argv(self.paths.bitcoinutil)
def wallet_argv(self):
"Return argv array that should be used to invoke bitcoin-wallet"
return self._argv(self.paths.bitcoinwallet)
def _argv(self, bin_path):
"""Return argv array that should be used to invoke the command.
Normally this will return binary paths directly from the paths object,
but when bin_dir is set (by tests calling binaries from previous
releases) it will return paths relative to bin_dir instead."""
if self.bin_dir is not None:
return [os.path.join(self.bin_dir, os.path.basename(bin_path))]
else:
return [bin_path]
class BitcoinTestMetaClass(type): class BitcoinTestMetaClass(type):
"""Metaclass for BitcoinTestFramework. """Metaclass for BitcoinTestFramework.
@ -220,6 +263,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read_file(open(self.options.configfile)) config.read_file(open(self.options.configfile))
self.config = config self.config = config
self.binary_paths = self.get_binary_paths()
if self.options.v1transport: if self.options.v1transport:
self.options.v2transport=False self.options.v2transport=False
@ -239,9 +283,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
PortSeed.n = self.options.port_seed PortSeed.n = self.options.port_seed
def set_binary_paths(self): def get_binary_paths(self):
"""Update self.options with the paths of all binaries from environment variables or their default values""" """Get paths of all binaries from environment variables or their default values"""
paths = types.SimpleNamespace()
binaries = { binaries = {
"bitcoind": ("bitcoind", "BITCOIND"), "bitcoind": ("bitcoind", "BITCOIND"),
"bitcoin-cli": ("bitcoincli", "BITCOINCLI"), "bitcoin-cli": ("bitcoincli", "BITCOINCLI"),
@ -254,7 +299,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"bin", "bin",
binary + self.config["environment"]["EXEEXT"], binary + self.config["environment"]["EXEEXT"],
) )
setattr(self.options, attribute_name, os.getenv(env_variable_name, default=default_filename)) setattr(paths, attribute_name, os.getenv(env_variable_name, default=default_filename))
return paths
def get_binaries(self, bin_dir=None):
return Binaries(self.binary_paths, bin_dir)
def setup(self): def setup(self):
"""Call this method to start up the test framework object with options set.""" """Call this method to start up the test framework object with options set."""
@ -265,8 +314,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
config = self.config config = self.config
self.set_binary_paths()
os.environ['PATH'] = os.pathsep.join([ os.environ['PATH'] = os.pathsep.join([
os.path.join(config['environment']['BUILDDIR'], 'bin'), os.path.join(config['environment']['BUILDDIR'], 'bin'),
os.environ['PATH'] os.environ['PATH']
@ -473,14 +520,14 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
group.add_argument("--legacy-wallet", action='store_const', const=False, **kwargs, group.add_argument("--legacy-wallet", action='store_const', const=False, **kwargs,
help="Run test using legacy wallets", dest='descriptors') help="Run test using legacy wallets", dest='descriptors')
def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None): def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, versions=None):
"""Instantiate TestNode objects. """Instantiate TestNode objects.
Should only be called once after the nodes have been specified in Should only be called once after the nodes have been specified in
set_test_params().""" set_test_params()."""
def get_bin_from_version(version, bin_name, bin_default): def bin_dir_from_version(version):
if not version: if not version:
return bin_default return None
if version > 219999: if version > 219999:
# Starting at client version 220000 the first two digits represent # Starting at client version 220000 the first two digits represent
# the major version, e.g. v22.0 instead of v0.22.0. # the major version, e.g. v22.0 instead of v0.22.0.
@ -498,7 +545,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
), ),
), ),
'bin', 'bin',
bin_name,
) )
if self.bind_to_localhost_only: if self.bind_to_localhost_only:
@ -513,13 +559,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args[i] = extra_args[i] + ["-whitelist=noban,in,out@127.0.0.1"] extra_args[i] = extra_args[i] + ["-whitelist=noban,in,out@127.0.0.1"]
if versions is None: if versions is None:
versions = [None] * num_nodes versions = [None] * num_nodes
if binary is None: bin_dirs = [bin_dir_from_version(v) for v in versions]
binary = [get_bin_from_version(v, 'bitcoind', self.options.bitcoind) for v in versions]
if binary_cli is None:
binary_cli = [get_bin_from_version(v, 'bitcoin-cli', self.options.bitcoincli) for v in versions]
# Fail test if any of the needed release binaries is missing # Fail test if any of the needed release binaries is missing
bins_missing = False bins_missing = False
for bin_path in binary + binary_cli: for bin_path in (argv[0] for bin_dir in bin_dirs
for binaries in (self.get_binaries(bin_dir),)
for argv in (binaries.daemon_argv(), binaries.rpc_argv())):
if shutil.which(bin_path) is None: if shutil.which(bin_path) is None:
self.log.error(f"Binary not found: {bin_path}") self.log.error(f"Binary not found: {bin_path}")
bins_missing = True bins_missing = True
@ -529,8 +574,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
assert_equal(len(extra_confs), num_nodes) assert_equal(len(extra_confs), num_nodes)
assert_equal(len(extra_args), num_nodes) assert_equal(len(extra_args), num_nodes)
assert_equal(len(versions), num_nodes) assert_equal(len(versions), num_nodes)
assert_equal(len(binary), num_nodes) assert_equal(len(bin_dirs), num_nodes)
assert_equal(len(binary_cli), num_nodes)
for i in range(num_nodes): for i in range(num_nodes):
args = list(extra_args[i]) args = list(extra_args[i])
test_node_i = TestNode( test_node_i = TestNode(
@ -540,8 +584,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
rpchost=rpchost, rpchost=rpchost,
timewait=self.rpc_timeout, timewait=self.rpc_timeout,
timeout_factor=self.options.timeout_factor, timeout_factor=self.options.timeout_factor,
bitcoind=binary[i], binaries=self.get_binaries(bin_dirs[i]),
bitcoin_cli=binary_cli[i],
version=versions[i], version=versions[i],
coverage_dir=self.options.coveragedir, coverage_dir=self.options.coveragedir,
cwd=self.options.tmpdir, cwd=self.options.tmpdir,
@ -852,8 +895,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
rpchost=None, rpchost=None,
timewait=self.rpc_timeout, timewait=self.rpc_timeout,
timeout_factor=self.options.timeout_factor, timeout_factor=self.options.timeout_factor,
bitcoind=self.options.bitcoind, binaries=self.get_binaries(),
bitcoin_cli=self.options.bitcoincli,
coverage_dir=None, coverage_dir=None,
cwd=self.options.tmpdir, cwd=self.options.tmpdir,
descriptors=self.options.descriptors, descriptors=self.options.descriptors,

View File

@ -76,7 +76,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection.""" be dispatched to the RPC connection."""
def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False): def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, binaries, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False):
""" """
Kwargs: Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as start_perf (bool): If True, begin profiling the node with `perf` as soon as
@ -92,7 +92,7 @@ class TestNode():
self.chain = chain self.chain = chain
self.rpchost = rpchost self.rpchost = rpchost
self.rpc_timeout = timewait self.rpc_timeout = timewait
self.binary = bitcoind self.binaries = binaries
self.coverage_dir = coverage_dir self.coverage_dir = coverage_dir
self.cwd = cwd self.cwd = cwd
self.descriptors = descriptors self.descriptors = descriptors
@ -109,8 +109,7 @@ class TestNode():
# Configuration for logging is set as command-line args rather than in the bitcoin.conf file. # Configuration for logging is set as command-line args rather than in the bitcoin.conf file.
# This means that starting a bitcoind using the temp dir to debug a failed test won't # This means that starting a bitcoind using the temp dir to debug a failed test won't
# spam debug.log. # spam debug.log.
self.args = [ self.args = self.binaries.daemon_argv() + [
self.binary,
f"-datadir={self.datadir_path}", f"-datadir={self.datadir_path}",
"-logtimemicros", "-logtimemicros",
"-debug", "-debug",
@ -149,7 +148,7 @@ class TestNode():
self.args.append("-v2transport=0") self.args.append("-v2transport=0")
# if v2transport is requested via global flag but not supported for node version, ignore it # if v2transport is requested via global flag but not supported for node version, ignore it
self.cli = TestNodeCLI(bitcoin_cli, self.datadir_path) self.cli = TestNodeCLI(binaries, self.datadir_path)
self.use_cli = use_cli self.use_cli = use_cli
self.start_perf = start_perf self.start_perf = start_perf
@ -870,16 +869,16 @@ def arg_to_cli(arg):
class TestNodeCLI(): class TestNodeCLI():
"""Interface to bitcoin-cli for an individual node""" """Interface to bitcoin-cli for an individual node"""
def __init__(self, binary, datadir): def __init__(self, binaries, datadir):
self.options = [] self.options = []
self.binary = binary self.binaries = binaries
self.datadir = datadir self.datadir = datadir
self.input = None self.input = None
self.log = logging.getLogger('TestFramework.bitcoincli') self.log = logging.getLogger('TestFramework.bitcoincli')
def __call__(self, *options, input=None): def __call__(self, *options, input=None):
# TestNodeCLI is callable with bitcoin-cli command-line options # TestNodeCLI is callable with bitcoin-cli command-line options
cli = TestNodeCLI(self.binary, self.datadir) cli = TestNodeCLI(self.binaries, self.datadir)
cli.options = [str(o) for o in options] cli.options = [str(o) for o in options]
cli.input = input cli.input = input
return cli return cli
@ -900,7 +899,7 @@ class TestNodeCLI():
"""Run bitcoin-cli command. Deserializes returned string as python object.""" """Run bitcoin-cli command. Deserializes returned string as python object."""
pos_args = [arg_to_cli(arg) for arg in args] pos_args = [arg_to_cli(arg) for arg in args]
named_args = [str(key) + "=" + arg_to_cli(value) for (key, value) in kwargs.items()] named_args = [str(key) + "=" + arg_to_cli(value) for (key, value) in kwargs.items()]
p_args = [self.binary, f"-datadir={self.datadir}"] + self.options p_args = self.binaries.rpc_argv() + [f"-datadir={self.datadir}"] + self.options
if named_args: if named_args:
p_args += ["-named"] p_args += ["-named"]
if clicommand is not None: if clicommand is not None:
@ -916,7 +915,7 @@ class TestNodeCLI():
code, message = match.groups() code, message = match.groups()
raise JSONRPCException(dict(code=int(code), message=message)) raise JSONRPCException(dict(code=int(code), message=message))
# Ignore cli_stdout, raise with cli_stderr # Ignore cli_stdout, raise with cli_stderr
raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr) raise subprocess.CalledProcessError(returncode, p_args, output=cli_stderr)
try: try:
return json.loads(cli_stdout, parse_float=decimal.Decimal) return json.loads(cli_stdout, parse_float=decimal.Decimal)
except (json.JSONDecodeError, decimal.InvalidOperation): except (json.JSONDecodeError, decimal.InvalidOperation):

View File

@ -592,3 +592,10 @@ def find_vout_for_address(node, txid, addr):
if addr == tx["vout"][i]["scriptPubKey"]["address"]: if addr == tx["vout"][i]["scriptPubKey"]["address"]:
return i return i
raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr)) raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr))
def sync_txindex(test_framework, node):
test_framework.log.debug("Waiting for node txindex to sync")
sync_start = int(time.time())
test_framework.wait_until(lambda: node.getindexinfo("txindex")["txindex"]["synced"])
test_framework.log.debug(f"Synced in {time.time() - sync_start} seconds")

View File

@ -5,6 +5,7 @@
"""Test signet miner tool""" """Test signet miner tool"""
import os.path import os.path
import shlex
import subprocess import subprocess
import sys import sys
import time import time
@ -49,13 +50,15 @@ class SignetMinerTest(BitcoinTestFramework):
# generate block with signet miner tool # generate block with signet miner tool
base_dir = self.config["environment"]["SRCDIR"] base_dir = self.config["environment"]["SRCDIR"]
signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner") signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")
rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.cli.datadir}"]
util_argv = node.binaries.util_argv() + ["grind"]
subprocess.run([ subprocess.run([
sys.executable, sys.executable,
signet_miner_path, signet_miner_path,
f'--cli={node.cli.binary} -datadir={node.cli.datadir}', f'--cli={shlex.join(rpc_argv)}',
'generate', 'generate',
f'--address={node.getnewaddress()}', f'--address={node.getnewaddress()}',
f'--grind-cmd={self.options.bitcoinutil} grind', f'--grind-cmd={shlex.join(util_argv)}',
f'--nbits={DIFF_1_N_BITS:08x}', f'--nbits={DIFF_1_N_BITS:08x}',
f'--set-block-time={int(time.time())}', f'--set-block-time={int(time.time())}',
'--poolnum=99', '--poolnum=99',

View File

@ -48,7 +48,7 @@ class ToolWalletTest(BitcoinTestFramework):
if "dump" in args and self.options.bdbro: if "dump" in args and self.options.bdbro:
default_args.append("-withinternalbdb") default_args.append("-withinternalbdb")
return subprocess.Popen([self.options.bitcoinwallet] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) return subprocess.Popen(self.get_binaries().wallet_argv() + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def assert_raises_tool_error(self, error, *args): def assert_raises_tool_error(self, error, *args):
p = self.bitcoin_wallet_process(*args) p = self.bitcoin_wallet_process(*args)

View File

@ -117,7 +117,6 @@ class AddressInputTypeGrouping(BitcoinTestFramework):
self.extra_args = [ self.extra_args = [
[ [
"-addresstype=bech32", "-addresstype=bech32",
"-txindex",
], ],
[ [
"-addresstype=p2sh-segwit", "-addresstype=p2sh-segwit",

View File

@ -112,7 +112,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
def do_wallet_tool(*args): def do_wallet_tool(*args):
proc = subprocess.Popen( proc = subprocess.Popen(
[self.options.bitcoinwallet, f"-datadir={self.nodes[0].datadir_path}", f"-chain={self.chain}"] + list(args), self.get_binaries().wallet_argv() + [f"-datadir={self.nodes[0].datadir_path}", f"-chain={self.chain}"] + list(args),
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,