Merge bitcoin/bitcoin#32145: test: Add functional test for bitcoin-chainstate
Some checks are pending
CI / test each commit (push) Waiting to run
CI / macOS 14 native, arm64, no depends, sqlite only, gui (push) Waiting to run
CI / macOS 14 native, arm64, fuzz (push) Waiting to run
CI / Win64 native, VS 2022 (push) Waiting to run
CI / Win64 native fuzz, VS 2022 (push) Waiting to run
CI / ASan + LSan + UBSan + integer, no depends, USDT (push) Waiting to run

ca55613fd1 test: Add functional test for bitcoin-chainstate (TheCharlatan)
3f9c716e7f test: Fix docstring for cmake migration (TheCharlatan)

Pull request description:

  While the `bitcoin-chainstate` utility is not shipped in a release, it is the only current utility directly using the bitcoin kernel library. Adding a simple test for it is useful for checking that the library is actually usable. The test is also useful in future to demonstrate that the `bitcoin-chainstate` binary using the API for the kernel library introduced in #30595 actually works and offers similar features.

ACKs for top commit:
  laanwj:
    Code review ACK ca55613fd1
  maflcko:
    ACK ca55613fd1 🎭
  kevkevinpal:
    ACK ca55613fd1

Tree-SHA512: 282627f5fac868a84aab9962ef2cbd3a8d3941d9f9dc2a3f26db1e7706ffa8051637ab5f8372676800e426e077ca40449a9e3e42a003048472339d81ed81bca8
This commit is contained in:
Ryan Ofsky 2025-03-27 10:33:15 -04:00
commit 74c23f80ab
No known key found for this signature in database
GPG key ID: 46800E30FC748A66
5 changed files with 67 additions and 1 deletions

View file

@ -19,6 +19,7 @@ function(create_test_config)
set_configure_variable(WITH_BDB USE_BDB) set_configure_variable(WITH_BDB USE_BDB)
set_configure_variable(BUILD_CLI BUILD_BITCOIN_CLI) set_configure_variable(BUILD_CLI BUILD_BITCOIN_CLI)
set_configure_variable(BUILD_UTIL BUILD_BITCOIN_UTIL) set_configure_variable(BUILD_UTIL BUILD_BITCOIN_UTIL)
set_configure_variable(BUILD_UTIL_CHAINSTATE BUILD_BITCOIN_CHAINSTATE)
set_configure_variable(BUILD_WALLET_TOOL BUILD_BITCOIN_WALLET) set_configure_variable(BUILD_WALLET_TOOL BUILD_BITCOIN_WALLET)
set_configure_variable(BUILD_DAEMON BUILD_BITCOIND) set_configure_variable(BUILD_DAEMON BUILD_BITCOIND)
set_configure_variable(BUILD_FUZZ_BINARY ENABLE_FUZZ_BINARY) set_configure_variable(BUILD_FUZZ_BINARY ENABLE_FUZZ_BINARY)

View file

@ -14,11 +14,12 @@ EXEEXT=@EXEEXT@
RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
[components] [components]
# Which components are enabled. These are commented out by `configure` if they were disabled when running config. # Which components are enabled. These are commented out by cmake if they were disabled during configuration.
@ENABLE_WALLET_TRUE@ENABLE_WALLET=true @ENABLE_WALLET_TRUE@ENABLE_WALLET=true
@USE_BDB_TRUE@USE_BDB=true @USE_BDB_TRUE@USE_BDB=true
@BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true @BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true
@BUILD_BITCOIN_UTIL_TRUE@ENABLE_BITCOIN_UTIL=true @BUILD_BITCOIN_UTIL_TRUE@ENABLE_BITCOIN_UTIL=true
@BUILD_BITCOIN_CHAINSTATE_TRUE@ENABLE_BITCOIN_CHAINSTATE=true
@BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true @BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true
@ENABLE_FUZZ_BINARY_TRUE@ENABLE_FUZZ_BINARY=true @ENABLE_FUZZ_BINARY_TRUE@ENABLE_FUZZ_BINARY=true

View file

@ -88,6 +88,10 @@ class Binaries:
"Return argv array that should be used to invoke bitcoin-wallet" "Return argv array that should be used to invoke bitcoin-wallet"
return self._argv(self.paths.bitcoinwallet) return self._argv(self.paths.bitcoinwallet)
def chainstate_argv(self):
"Return argv array that should be used to invoke bitcoin-chainstate"
return self._argv(self.paths.bitcoinchainstate)
def _argv(self, bin_path): def _argv(self, bin_path):
"""Return argv array that should be used to invoke the command. """Return argv array that should be used to invoke the command.
Normally this will return binary paths directly from the paths object, Normally this will return binary paths directly from the paths object,
@ -291,6 +295,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"bitcoind": ("bitcoind", "BITCOIND"), "bitcoind": ("bitcoind", "BITCOIND"),
"bitcoin-cli": ("bitcoincli", "BITCOINCLI"), "bitcoin-cli": ("bitcoincli", "BITCOINCLI"),
"bitcoin-util": ("bitcoinutil", "BITCOINUTIL"), "bitcoin-util": ("bitcoinutil", "BITCOINUTIL"),
"bitcoin-chainstate": ("bitcoinchainstate", "BITCOINCHAINSTATE"),
"bitcoin-wallet": ("bitcoinwallet", "BITCOINWALLET"), "bitcoin-wallet": ("bitcoinwallet", "BITCOINWALLET"),
} }
for binary, [attribute_name, env_variable_name] in binaries.items(): for binary, [attribute_name, env_variable_name] in binaries.items():
@ -1022,6 +1027,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
if not self.is_bitcoin_util_compiled(): if not self.is_bitcoin_util_compiled():
raise SkipTest("bitcoin-util has not been compiled") raise SkipTest("bitcoin-util has not been compiled")
def skip_if_no_bitcoin_chainstate(self):
"""Skip the running test if bitcoin-chainstate has not been compiled."""
if not self.is_bitcoin_chainstate_compiled():
raise SkipTest("bitcoin-chainstate has not been compiled")
def skip_if_no_cli(self): def skip_if_no_cli(self):
"""Skip the running test if bitcoin-cli has not been compiled.""" """Skip the running test if bitcoin-cli has not been compiled."""
if not self.is_cli_compiled(): if not self.is_cli_compiled():
@ -1073,6 +1083,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"""Checks whether bitcoin-util was compiled.""" """Checks whether bitcoin-util was compiled."""
return self.config["components"].getboolean("ENABLE_BITCOIN_UTIL") return self.config["components"].getboolean("ENABLE_BITCOIN_UTIL")
def is_bitcoin_chainstate_compiled(self):
"""Checks whether bitcoin-chainstate was compiled."""
return self.config["components"].getboolean("ENABLE_BITCOIN_CHAINSTATE")
def is_zmq_compiled(self): def is_zmq_compiled(self):
"""Checks whether the zmq module was compiled.""" """Checks whether the zmq module was compiled."""
return self.config["components"].getboolean("ENABLE_ZMQ") return self.config["components"].getboolean("ENABLE_ZMQ")

View file

@ -193,6 +193,7 @@ BASE_SCRIPTS = [
'feature_bind_extra.py', 'feature_bind_extra.py',
'mempool_resurrect.py', 'mempool_resurrect.py',
'wallet_txn_doublespend.py --mineblock', 'wallet_txn_doublespend.py --mineblock',
'tool_bitcoin_chainstate.py',
'tool_wallet.py --legacy-wallet', 'tool_wallet.py --legacy-wallet',
'tool_wallet.py --legacy-wallet --bdbro', 'tool_wallet.py --legacy-wallet --bdbro',
'tool_wallet.py --legacy-wallet --bdbro --swap-bdb-endian', 'tool_wallet.py --legacy-wallet --bdbro --swap-bdb-endian',

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
# Copyright (c) 2022-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import subprocess
from test_framework.test_framework import BitcoinTestFramework
class BitcoinChainstateTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_bitcoin_chainstate()
def set_test_params(self):
self.setup_clean_chain = True
self.chain = ""
self.num_nodes = 1
# Set prune to avoid disk space warning.
self.extra_args = [["-prune=550"]]
def add_block(self, datadir, input, expected_stderr):
proc = subprocess.Popen(
self.get_binaries().chainstate_argv() + [datadir],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = proc.communicate(input=input + "\n", timeout=5)
self.log.debug("STDOUT: {0}".format(stdout.strip("\n")))
self.log.info("STDERR: {0}".format(stderr.strip("\n")))
if expected_stderr not in stderr:
raise AssertionError(f"Expected stderr output {expected_stderr} does not partially match stderr:\n{stderr}")
def run_test(self):
node = self.nodes[0]
datadir = node.cli.datadir
node.stop_node()
self.log.info(f"Testing bitcoin-chainstate {self.get_binaries().chainstate_argv()} with datadir: {datadir}")
block_one = "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000"
self.add_block(datadir, block_one, "Block has not yet been rejected")
self.add_block(datadir, block_one, "duplicate")
self.add_block(datadir, "00", "Block decode failed")
self.add_block(datadir, "", "Empty line found")
if __name__ == "__main__":
BitcoinChainstateTest(__file__).main()