mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-07-13 03:26:53 +02:00
Merge bitcoin/bitcoin#31375: multiprocess: Add bitcoin wrapper executable
a5ac43d98d
doc: Add release notes describing bitcoin wrapper executable (Ryan Ofsky)258bda80c0
doc: Mention bitcoin wrapper executable in documentation (Ryan Ofsky)d2739d75c9
build: add bitcoin.exe to windows installer (Sjors Provoost)ba649c0006
ci: Run multiprocess tests through wrapper executable (Ryan Ofsky)29bdd743bb
test: Support BITCOIN_CMD environment variable (Ryan Ofsky)9c8c68891b
multiprocess: Add bitcoin wrapper executable (Ryan Ofsky)5076d20fdb
util: Add cross-platform ExecVp and GetExePath functions (Ryan Ofsky) Pull request description: Intended to make bitcoin command line features more discoverable and allow installing new multiprocess binaries in libexec/ instead of bin/ so they don't cause confusion. Idea and implementation of this were discussed in https://github.com/bitcoin/bitcoin/issues/30983. --- Initial implementation of this feature is deliberately minimal so the UX can evolve in response to feedback and there are not too many details to debate and discuss in a single PR. But many improvements are possible or planned: - Adding manpage and bash completions. - Showing nicer error messages that detect if an executable isn't installed and suggest how to fix [(comment)](https://github.com/bitcoin/bitcoin/pull/31375#discussion_r2073194474) - Showing wrapper command lines in subcommand in help output [(comment)](https://github.com/bitcoin/bitcoin/pull/31375#discussion_r2077800405). This could be done conditionally as suggested in the comment or be unconditional. - Showing wrapper command lines in subcommand error output. There is a bitcoin-cli error pointed out in [(comment)](https://github.com/bitcoin/bitcoin/pull/31375#discussion_r2091152243) that is needlessly confusing. - Integrating help so `bitcoin help subcommand` invokes `bitcoin subcommand -h`. `bitcoin -h subcommand` should also be supported and be equivalent [(comment)](https://github.com/bitcoin/bitcoin/pull/31375#discussion_r2093116725) - Adding support for `bitcoin-util` subcommands. Ideal interface would probably be more like `bitcoin grind` not `bitcoin util grind` but this has been punted for now. Supporting subcommands directly would require some ArgsManager modifications - Adding a dedicated python functional test for the wrapper. Right now there is some CI coverage by setting the `BITCOIN_CMD` variable, but this doesn't cover things like the help output and version output, and support for different directory layouts. - Better `--multiprocess` (`-m`) / `--monolithic` (`-M`) default selection. Right now, default is monolithic but it probably makes sense to chose more intelligently depending on whether -ipc options are enabled and what binaries are available. - Maybe parsing `bitcoin.conf` and supporting options to control wrapper behavior like custom locations or preferences or aliases. - Better command command line usability. Allow combining short options like (`-ah`). Allow fuzzy matching of subcommands or suggestions if you misspell. (suggested by stickies in review club) - Not directly related to this PR but `bitcoin-cli named` implementation used by the wrapper should do a better job disambiguating named arguments from base64 arguments ending in = as pointed out in [(comment)](https://github.com/bitcoin/bitcoin/pull/31375#discussion_r2091886628) --- This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/issues/28722). A review club meeting for it took place in https://bitcoincore.reviews/31375 ACKs for top commit: Sjors: utACKa5ac43d98d
achow101: ACKa5ac43d98d
vasild: ACKa5ac43d98d
theStack: ACKa5ac43d98d
ismaelsadeeq: fwiw my last review implied an ACKa5ac43d98d
hodlinator: ACKa5ac43d98d
Tree-SHA512: 570e6a4ff8bd79ef6554da3d01f36c0a7c6d2dd7dace8f8732eca98f4a8bc2284474a9beadeba783114fe2f3dd08b2041b3da7753bae0b7f881ec50668cb821f
This commit is contained in:
@ -94,6 +94,7 @@ endif()
|
||||
#=============================
|
||||
include(CMakeDependentOption)
|
||||
# When adding a new option, end the <help_text> with a full stop for consistency.
|
||||
option(BUILD_BITCOIN_BIN "Build bitcoin executable." ON)
|
||||
option(BUILD_DAEMON "Build bitcoind executable." ON)
|
||||
option(BUILD_GUI "Build bitcoin-qt executable." OFF)
|
||||
option(BUILD_CLI "Build bitcoin-cli executable." ON)
|
||||
@ -202,6 +203,7 @@ target_link_libraries(core_interface INTERFACE
|
||||
|
||||
if(BUILD_FOR_FUZZING)
|
||||
message(WARNING "BUILD_FOR_FUZZING=ON will disable all other targets and force BUILD_FUZZ_BINARY=ON.")
|
||||
set(BUILD_BITCOIN_BIN OFF)
|
||||
set(BUILD_DAEMON OFF)
|
||||
set(BUILD_CLI OFF)
|
||||
set(BUILD_TX OFF)
|
||||
@ -647,6 +649,7 @@ message("\n")
|
||||
message("Configure summary")
|
||||
message("=================")
|
||||
message("Executables:")
|
||||
message(" bitcoin ............................. ${BUILD_BITCOIN_BIN}")
|
||||
message(" bitcoind ............................ ${BUILD_DAEMON}")
|
||||
if(BUILD_DAEMON AND ENABLE_IPC)
|
||||
set(bitcoin_daemon_status ON)
|
||||
|
@ -20,4 +20,4 @@ export BITCOIN_CONFIG="\
|
||||
-DCMAKE_CXX_COMPILER='clang++;-m32' \
|
||||
-DAPPEND_CPPFLAGS='-DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE' \
|
||||
"
|
||||
export BITCOIND=bitcoin-node # Used in functional tests
|
||||
export BITCOIN_CMD="bitcoin -m" # Used in functional tests
|
||||
|
@ -7,6 +7,7 @@ function(generate_setup_nsi)
|
||||
set(abs_top_builddir ${PROJECT_BINARY_DIR})
|
||||
set(CLIENT_URL ${PROJECT_HOMEPAGE_URL})
|
||||
set(CLIENT_TARNAME "bitcoin")
|
||||
set(BITCOIN_WRAPPER_NAME "bitcoin")
|
||||
set(BITCOIN_GUI_NAME "bitcoin-qt")
|
||||
set(BITCOIN_DAEMON_NAME "bitcoind")
|
||||
set(BITCOIN_CLI_NAME "bitcoin-cli")
|
||||
|
@ -23,7 +23,7 @@ function(add_maintenance_targets)
|
||||
return()
|
||||
endif()
|
||||
|
||||
foreach(target IN ITEMS bitcoind bitcoin-qt bitcoin-cli bitcoin-tx bitcoin-util bitcoin-wallet test_bitcoin bench_bitcoin)
|
||||
foreach(target IN ITEMS bitcoin bitcoind bitcoin-qt bitcoin-cli bitcoin-tx bitcoin-util bitcoin-wallet test_bitcoin bench_bitcoin)
|
||||
if(TARGET ${target})
|
||||
list(APPEND executables $<TARGET_FILE:${target}>)
|
||||
endif()
|
||||
@ -43,7 +43,7 @@ function(add_maintenance_targets)
|
||||
endfunction()
|
||||
|
||||
function(add_windows_deploy_target)
|
||||
if(MINGW AND TARGET bitcoin-qt AND TARGET bitcoind AND TARGET bitcoin-cli AND TARGET bitcoin-tx AND TARGET bitcoin-wallet AND TARGET bitcoin-util AND TARGET test_bitcoin)
|
||||
if(MINGW AND TARGET bitcoin AND TARGET bitcoin-qt AND TARGET bitcoind AND TARGET bitcoin-cli AND TARGET bitcoin-tx AND TARGET bitcoin-wallet AND TARGET bitcoin-util AND TARGET test_bitcoin)
|
||||
find_program(MAKENSIS_EXECUTABLE makensis)
|
||||
if(NOT MAKENSIS_EXECUTABLE)
|
||||
add_custom_target(deploy
|
||||
@ -59,6 +59,7 @@ function(add_windows_deploy_target)
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_BINARY_DIR}/bitcoin-win64-setup.exe
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/release
|
||||
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:bitcoin> -o ${PROJECT_BINARY_DIR}/release/$<TARGET_FILE_NAME:bitcoin>
|
||||
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:bitcoin-qt> -o ${PROJECT_BINARY_DIR}/release/$<TARGET_FILE_NAME:bitcoin-qt>
|
||||
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:bitcoind> -o ${PROJECT_BINARY_DIR}/release/$<TARGET_FILE_NAME:bitcoind>
|
||||
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:bitcoin-cli> -o ${PROJECT_BINARY_DIR}/release/$<TARGET_FILE_NAME:bitcoin-cli>
|
||||
|
@ -126,6 +126,9 @@ def check_ELF_FORTIFY(binary) -> bool:
|
||||
# bitcoin-util does not currently contain any fortified functions
|
||||
if 'Bitcoin Core bitcoin-util utility version ' in binary.strings:
|
||||
return True
|
||||
# bitcoin wrapper does not currently contain any fortified functions
|
||||
if '--monolithic' in binary.strings:
|
||||
return True
|
||||
|
||||
chk_funcs = set()
|
||||
|
||||
|
@ -62,6 +62,8 @@ bitcoin-cli -named createwallet wallet_name=mywallet load_on_startup=true
|
||||
bitcoin-cli -named createwallet mywallet load_on_startup=true
|
||||
```
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli -named`, and is a newer alternative.
|
||||
|
||||
## Versioning
|
||||
|
||||
The RPC interface might change from one major version of Bitcoin Core to the
|
||||
|
@ -17,6 +17,9 @@ Unpack the files into a directory and run:
|
||||
|
||||
- `bin/bitcoin-qt` (GUI) or
|
||||
- `bin/bitcoind` (headless)
|
||||
- `bin/bitcoin` (wrapper command)
|
||||
|
||||
The `bitcoin` command supports subcommands like `bitcoin gui`, `bitcoin node`, and `bitcoin rpc` exposing different functionality. Subcommands can be listed with `bitcoin help`.
|
||||
|
||||
### Windows
|
||||
|
||||
|
@ -199,6 +199,10 @@ cmake --build build --target deploy
|
||||
Bitcoin Core should now be available at `./build/bin/bitcoind`.
|
||||
If you compiled support for the GUI, it should be available at `./build/bin/bitcoin-qt`.
|
||||
|
||||
There is also a multifunction command line interface at `./build/bin/bitcoin`
|
||||
supporting subcommands like `bitcoin node`, `bitcoin gui`, `bitcoin rpc`, and
|
||||
others that can be listed with `bitcoin help`.
|
||||
|
||||
The first time you run `bitcoind` or `bitcoin-qt`, it will start downloading the blockchain.
|
||||
This process could take many hours, or even days on slower than average systems.
|
||||
|
||||
|
@ -174,4 +174,5 @@ This example lists the steps necessary to setup and build a command line only di
|
||||
cmake --build build
|
||||
ctest --test-dir build
|
||||
./build/bin/bitcoind
|
||||
./build/bin/bitcoin help
|
||||
|
||||
|
@ -113,3 +113,5 @@ To see which CJDNS peers your node is connected to, use `bitcoin-cli -netinfo 4`
|
||||
or the `getpeerinfo` RPC (i.e. `bitcoin-cli getpeerinfo`).
|
||||
|
||||
You can use the `getnodeaddresses` RPC to fetch a number of CJDNS peers known to your node; run `bitcoin-cli help getnodeaddresses` for details.
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli`.
|
||||
|
@ -14,6 +14,8 @@ Start Bitcoin Core:
|
||||
$ bitcoind -signer=../HWI/hwi.py
|
||||
```
|
||||
|
||||
`bitcoin node` can also be substituted for `bitcoind`.
|
||||
|
||||
### Device setup
|
||||
|
||||
Follow the hardware manufacturers instructions for the initial device setup, as well as their instructions for creating a backup. Alternatively, for some devices, you can use the `setup`, `restore` and `backup` commands provided by [HWI](https://github.com/bitcoin-core/HWI).
|
||||
@ -40,6 +42,8 @@ Create a wallet, this automatically imports the public keys:
|
||||
$ bitcoin-cli createwallet "hww" true true "" true true true
|
||||
```
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli`.
|
||||
|
||||
### Verify an address
|
||||
|
||||
Display an address on the device:
|
||||
|
@ -15,6 +15,8 @@ The following command, for example, creates a descriptor wallet. More informatio
|
||||
$ bitcoin-cli createwallet "wallet-01"
|
||||
```
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli`.
|
||||
|
||||
By default, wallets are created in the `wallets` folder of the data directory, which varies by operating system, as shown below. The user can change the default by using the `-datadir` or `-walletdir` initialization parameters.
|
||||
|
||||
| Operating System | Default wallet directory |
|
||||
|
@ -25,8 +25,8 @@ make -C depends NO_QT=1 MULTIPROCESS=1
|
||||
HOST_PLATFORM="x86_64-pc-linux-gnu"
|
||||
cmake -B build --toolchain=depends/$HOST_PLATFORM/toolchain.cmake
|
||||
cmake --build build
|
||||
build/bin/bitcoin-node -regtest -printtoconsole -debug=ipc
|
||||
BITCOIND=$(pwd)/build/bin/bitcoin-node build/test/functional/test_runner.py
|
||||
build/bin/bitcoin -m node -regtest -printtoconsole -debug=ipc
|
||||
BITCOIN_CMD="bitcoin -m" build/test/functional/test_runner.py
|
||||
```
|
||||
|
||||
The `cmake` build will pick up settings and library locations from the depends directory, so there is no need to pass `-DENABLE_IPC=ON` as a separate flag when using the depends system (it's controlled by the `MULTIPROCESS=1` option).
|
||||
@ -41,6 +41,11 @@ By default when `-DENABLE_IPC=ON` is enabled, the libmultiprocess sources at [..
|
||||
|
||||
## Usage
|
||||
|
||||
`bitcoin-node` is a drop-in replacement for `bitcoind`, and `bitcoin-gui` is a drop-in replacement for `bitcoin-qt`, and there are no differences in use or external behavior between the new and old executables. But internally after [#10102](https://github.com/bitcoin/bitcoin/pull/10102), `bitcoin-gui` will spawn a `bitcoin-node` process to run P2P and RPC code, communicating with it across a socket pair, and `bitcoin-node` will spawn `bitcoin-wallet` to run wallet code, also communicating over a socket pair. This will let node, wallet, and GUI code run in separate address spaces for better isolation, and allow future improvements like being able to start and stop components independently on different machines and environments.
|
||||
[#19460](https://github.com/bitcoin/bitcoin/pull/19460) also adds a new `bitcoin-node` `-ipcbind` option and a `bitcoind-wallet` `-ipcconnect` option to allow new wallet processes to connect to an existing node process.
|
||||
And [#19461](https://github.com/bitcoin/bitcoin/pull/19461) adds a new `bitcoin-gui` `-ipcconnect` option to allow new GUI processes to connect to an existing node process.
|
||||
Recommended way to use multiprocess binaries is to invoke `bitcoin` CLI like `bitcoin -m node -debug=ipc` or `bitcoin -m gui -printtoconsole -debug=ipc`.
|
||||
|
||||
When the `-m` (`--multiprocess`) option is used the `bitcoin` command will execute multiprocess binaries instead of monolithic ones (`bitcoin-node` instead of `bitcoind`, and `bitcoin-gui` instead of `bitcoin-qt`). The multiprocess binaries can also be invoked directly, but this is not recommended as they may change or be renamed in the future, and they are not installed in the PATH.
|
||||
|
||||
The multiprocess binaries currently function the same as the monolithic binaries, except they support an `-ipcbind` option.
|
||||
|
||||
In the future, after [#10102](https://github.com/bitcoin/bitcoin/pull/10102) they will have other differences. Specifically `bitcoin-gui` will spawn a `bitcoin-node` process to run P2P and RPC code, communicating with it across a socket pair, and `bitcoin-node` will spawn `bitcoin-wallet` to run wallet code, also communicating over a socket pair. This will let node, wallet, and GUI code run in separate address spaces for better isolation, and allow future improvements like being able to start and stop components independently on different machines and environments. [#19460](https://github.com/bitcoin/bitcoin/pull/19460) also adds a new `bitcoin-wallet -ipcconnect` option to allow new wallet processes to connect to an existing node process.
|
||||
And [#19461](https://github.com/bitcoin/bitcoin/pull/19461) adds a new `bitcoin-gui -ipcconnect` option to allow new GUI processes to connect to an existing node process.
|
||||
|
@ -31,6 +31,8 @@ do
|
||||
done
|
||||
```
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli`.
|
||||
|
||||
Extract the xpub of each wallet. To do this, the `listdescriptors` RPC is used. By default, Bitcoin Core single-sig wallets are created using path `m/44'/1'/0'` for PKH, `m/84'/1'/0'` for WPKH, `m/49'/1'/0'` for P2WPKH-nested-in-P2SH and `m/86'/1'/0'` for P2TR based accounts. Each of them uses the chain 0 for external addresses and chain 1 for internal ones, as shown in the example below.
|
||||
|
||||
```
|
||||
|
@ -34,6 +34,8 @@ We are going to first create an `offline_wallet` on the offline host. We will th
|
||||
}
|
||||
```
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli`.
|
||||
|
||||
> [!NOTE]
|
||||
> The use of a passphrase is crucial to encrypt the wallet.dat file. This encryption ensures that even if an unauthorized individual gains access to the offline host, they won't be able to access the wallet's contents. Further details about securing your wallet can be found in [Managing the Wallet](https://github.com/bitcoin/bitcoin/blob/master/doc/managing-wallets.md#12-encrypting-the-wallet)
|
||||
|
||||
|
11
doc/release-notes-31375.md
Normal file
11
doc/release-notes-31375.md
Normal file
@ -0,0 +1,11 @@
|
||||
New command line interface
|
||||
--------------------------
|
||||
|
||||
A new `bitcoin` command line tool has been added to make features more
|
||||
discoverable and convenient to use. The `bitcoin` tool just calls other
|
||||
executables and does not implement any functionality on its own. Specifically
|
||||
`bitcoin node` is a synonym for `bitcoind`, `bitcoin gui` is a synonym for
|
||||
`bitcoin-qt`, and `bitcoin rpc` is a synonym for `bitcoin-cli -named`. Other
|
||||
commands and options can be listed with `bitcoin help`. The new tool does not
|
||||
replace other tools, so all existing commands should continue working and there
|
||||
are no plans to deprecate them.
|
@ -27,6 +27,8 @@ e.g. for `-onlynet=onion`.
|
||||
|
||||
You can use the `getnodeaddresses` RPC to fetch a number of onion peers known to your node; run `bitcoin-cli help getnodeaddresses` for details.
|
||||
|
||||
`bitcoin rpc` can also be substituted for `bitcoin-cli`.
|
||||
|
||||
## 1. Run Bitcoin Core behind a Tor proxy
|
||||
|
||||
The first step is running Bitcoin Core behind a Tor proxy. This will already anonymize all
|
||||
@ -64,6 +66,8 @@ In a typical situation, this suffices to run behind a Tor proxy:
|
||||
|
||||
./bitcoind -proxy=127.0.0.1:9050
|
||||
|
||||
`bitcoin node` or `bitcoin gui` can also be substituted for `bitcoind`.
|
||||
|
||||
## 2. Automatically create a Bitcoin Core onion service
|
||||
|
||||
Bitcoin Core makes use of Tor's control socket API to create and destroy
|
||||
|
@ -87,6 +87,8 @@ For instance:
|
||||
-zmqpubrawtx=ipc:///tmp/bitcoind.tx.raw \
|
||||
-zmqpubhashtxhwm=10000
|
||||
|
||||
`bitcoin node` or `bitcoin gui` can also be substituted for `bitcoind`.
|
||||
|
||||
Notification types correspond to message topics (details in next section). For instance,
|
||||
for the notification `-zmqpubhashtx` the topic is `hashtx`. These options can also be
|
||||
provided in bitcoin.conf.
|
||||
|
@ -73,6 +73,7 @@ Section -Main SEC0000
|
||||
SetOutPath $INSTDIR
|
||||
SetOverwrite on
|
||||
File @abs_top_builddir@/release/@BITCOIN_GUI_NAME@@EXEEXT@
|
||||
File @abs_top_builddir@/release/@BITCOIN_WRAPPER_NAME@@EXEEXT@
|
||||
File /oname=COPYING.txt @abs_top_srcdir@/COPYING
|
||||
File /oname=readme.txt @abs_top_srcdir@/doc/README_windows.txt
|
||||
File @abs_top_srcdir@/share/examples/bitcoin.conf
|
||||
@ -129,6 +130,7 @@ done${UNSECTION_ID}:
|
||||
|
||||
# Uninstaller sections
|
||||
Section /o -un.Main UNSEC0000
|
||||
Delete /REBOOTOK $INSTDIR\@BITCOIN_WRAPPER_NAME@@EXEEXT@
|
||||
Delete /REBOOTOK $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@
|
||||
Delete /REBOOTOK $INSTDIR\COPYING.txt
|
||||
Delete /REBOOTOK $INSTDIR\readme.txt
|
||||
|
@ -332,6 +332,12 @@ target_link_libraries(bitcoin_node
|
||||
$<TARGET_NAME_IF_EXISTS:USDT::headers>
|
||||
)
|
||||
|
||||
# Bitcoin wrapper executable that can call other executables.
|
||||
if(BUILD_BITCOIN_BIN)
|
||||
add_executable(bitcoin bitcoin.cpp)
|
||||
target_link_libraries(bitcoin core_interface bitcoin_util)
|
||||
install_binary_component(bitcoin)
|
||||
endif()
|
||||
|
||||
# Bitcoin Core bitcoind.
|
||||
if(BUILD_DAEMON)
|
||||
|
204
src/bitcoin.cpp
Normal file
204
src/bitcoin.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2025 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/exec.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tinyformat.h>
|
||||
#include <vector>
|
||||
|
||||
const TranslateFn G_TRANSLATION_FUN{nullptr};
|
||||
|
||||
static constexpr auto HELP_USAGE = R"(Usage: %s [OPTIONS] COMMAND...
|
||||
|
||||
Options:
|
||||
-m, --multiprocess Run multiprocess binaries bitcoin-node, bitcoin-gui.
|
||||
-M, --monolithic Run monolithic binaries bitcoind, bitcoin-qt. (Default behavior)
|
||||
-v, --version Show version information
|
||||
-h, --help Show this help message
|
||||
|
||||
Commands:
|
||||
gui [ARGS] Start GUI, equivalent to running 'bitcoin-qt [ARGS]' or 'bitcoin-gui [ARGS]'.
|
||||
node [ARGS] Start node, equivalent to running 'bitcoind [ARGS]' or 'bitcoin-node [ARGS]'.
|
||||
rpc [ARGS] Call RPC method, equivalent to running 'bitcoin-cli -named [ARGS]'.
|
||||
wallet [ARGS] Call wallet command, equivalent to running 'bitcoin-wallet [ARGS]'.
|
||||
tx [ARGS] Manipulate hex-encoded transactions, equivalent to running 'bitcoin-tx [ARGS]'.
|
||||
help [-a] Show this help message. Include -a or --all to show additional commands.
|
||||
)";
|
||||
|
||||
static constexpr auto HELP_EXTRA = R"(
|
||||
Additional less commonly used commands:
|
||||
bench [ARGS] Run bench command, equivalent to running 'bench_bitcoin [ARGS]'.
|
||||
chainstate [ARGS] Run bitcoin kernel chainstate util, equivalent to running 'bitcoin-chainstate [ARGS]'.
|
||||
test [ARGS] Run unit tests, equivalent to running 'test_bitcoin [ARGS]'.
|
||||
test-gui [ARGS] Run GUI unit tests, equivalent to running 'test_bitcoin-qt [ARGS]'.
|
||||
)";
|
||||
|
||||
struct CommandLine {
|
||||
bool use_multiprocess{false};
|
||||
bool show_version{false};
|
||||
bool show_help{false};
|
||||
bool show_help_all{false};
|
||||
std::string_view command;
|
||||
std::vector<const char*> args;
|
||||
};
|
||||
|
||||
CommandLine ParseCommandLine(int argc, char* argv[]);
|
||||
static void ExecCommand(const std::vector<const char*>& args, std::string_view argv0);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try {
|
||||
CommandLine cmd{ParseCommandLine(argc, argv)};
|
||||
if (cmd.show_version) {
|
||||
tfm::format(std::cout, "%s version %s\n%s", CLIENT_NAME, FormatFullVersion(), FormatParagraph(LicenseInfo()));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<const char*> args;
|
||||
if (cmd.show_help || cmd.command.empty()) {
|
||||
tfm::format(std::cout, HELP_USAGE, argv[0]);
|
||||
if (cmd.show_help_all) tfm::format(std::cout, HELP_EXTRA);
|
||||
return cmd.show_help ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
} else if (cmd.command == "gui") {
|
||||
args.emplace_back(cmd.use_multiprocess ? "bitcoin-gui" : "bitcoin-qt");
|
||||
} else if (cmd.command == "node") {
|
||||
args.emplace_back(cmd.use_multiprocess ? "bitcoin-node" : "bitcoind");
|
||||
} else if (cmd.command == "rpc") {
|
||||
args.emplace_back("bitcoin-cli");
|
||||
// Since "bitcoin rpc" is a new interface that doesn't need to be
|
||||
// backward compatible, enable -named by default so it is convenient
|
||||
// for callers to use a mix of named and unnamed parameters. Callers
|
||||
// can override this by specifying -nonamed, but should not need to
|
||||
// unless they are passing string values containing '=' characters
|
||||
// as unnamed parameters.
|
||||
args.emplace_back("-named");
|
||||
} else if (cmd.command == "wallet") {
|
||||
args.emplace_back("bitcoin-wallet");
|
||||
} else if (cmd.command == "tx") {
|
||||
args.emplace_back("bitcoin-tx");
|
||||
} else if (cmd.command == "bench") {
|
||||
args.emplace_back("bench_bitcoin");
|
||||
} else if (cmd.command == "chainstate") {
|
||||
args.emplace_back("bitcoin-chainstate");
|
||||
} else if (cmd.command == "test") {
|
||||
args.emplace_back("test_bitcoin");
|
||||
} else if (cmd.command == "test-gui") {
|
||||
args.emplace_back("test_bitcoin-qt");
|
||||
} else if (cmd.command == "util") {
|
||||
args.emplace_back("bitcoin-util");
|
||||
} else {
|
||||
throw std::runtime_error(strprintf("Unrecognized command: '%s'", cmd.command));
|
||||
}
|
||||
if (!args.empty()) {
|
||||
args.insert(args.end(), cmd.args.begin(), cmd.args.end());
|
||||
ExecCommand(args, argv[0]);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
tfm::format(std::cerr, "Error: %s\nTry '%s --help' for more information.\n", e.what(), argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
CommandLine ParseCommandLine(int argc, char* argv[])
|
||||
{
|
||||
CommandLine cmd;
|
||||
cmd.args.reserve(argc);
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string_view arg = argv[i];
|
||||
if (!cmd.command.empty()) {
|
||||
cmd.args.emplace_back(argv[i]);
|
||||
} else if (arg == "-m" || arg == "--multiprocess") {
|
||||
cmd.use_multiprocess = true;
|
||||
} else if (arg == "-M" || arg == "--monolithic") {
|
||||
cmd.use_multiprocess = false;
|
||||
} else if (arg == "-v" || arg == "--version") {
|
||||
cmd.show_version = true;
|
||||
} else if (arg == "-h" || arg == "--help" || arg == "help") {
|
||||
cmd.show_help = true;
|
||||
} else if (cmd.show_help && (arg == "-a" || arg == "--all")) {
|
||||
cmd.show_help_all = true;
|
||||
} else if (arg.starts_with("-")) {
|
||||
throw std::runtime_error(strprintf("Unknown option: %s", arg));
|
||||
} else if (!arg.empty()) {
|
||||
cmd.command = arg;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
//! Execute the specified bitcoind, bitcoin-qt or other command line in `args`
|
||||
//! using src, bin and libexec directory paths relative to this executable, where
|
||||
//! the path to this executable is specified in `wrapper_argv0`.
|
||||
//!
|
||||
//! @param args Command line arguments to execute, where first argument should
|
||||
//! be a relative path to a bitcoind, bitcoin-qt or other executable
|
||||
//! that will be located on the PATH or relative to wrapper_argv0.
|
||||
//!
|
||||
//! @param wrapper_argv0 String containing first command line argument passed to
|
||||
//! main() to run the current executable. This is used to
|
||||
//! help determine the path to the current executable and
|
||||
//! how to look for new executables.
|
||||
//
|
||||
//! @note This function doesn't currently print anything but can be debugged
|
||||
//! from the command line using strace or dtrace like:
|
||||
//!
|
||||
//! strace -e trace=execve -s 10000 build/bin/bitcoin ...
|
||||
//! dtrace -n 'proc:::exec-success /pid == $target/ { trace(curpsinfo->pr_psargs); }' -c ...
|
||||
static void ExecCommand(const std::vector<const char*>& args, std::string_view wrapper_argv0)
|
||||
{
|
||||
// Construct argument string for execvp
|
||||
std::vector<const char*> exec_args{args};
|
||||
exec_args.emplace_back(nullptr);
|
||||
|
||||
// Try to call ExecVp with given exe path.
|
||||
auto try_exec = [&](fs::path exe_path, bool allow_notfound = true) {
|
||||
std::string exe_path_str{fs::PathToString(exe_path)};
|
||||
exec_args[0] = exe_path_str.c_str();
|
||||
if (util::ExecVp(exec_args[0], (char*const*)exec_args.data()) == -1) {
|
||||
if (allow_notfound && errno == ENOENT) return false;
|
||||
throw std::system_error(errno, std::system_category(), strprintf("execvp failed to execute '%s'", exec_args[0]));
|
||||
}
|
||||
throw std::runtime_error("execvp returned unexpectedly");
|
||||
};
|
||||
|
||||
// Get the wrapper executable path.
|
||||
const fs::path wrapper_path{util::GetExePath(wrapper_argv0)};
|
||||
|
||||
// Try to resolve any symlinks and figure out the directory containing the wrapper executable.
|
||||
std::error_code ec;
|
||||
fs::path wrapper_dir{fs::weakly_canonical(wrapper_path, ec)};
|
||||
if (wrapper_dir.empty()) wrapper_dir = wrapper_path; // Restore previous path if weakly_canonical failed.
|
||||
wrapper_dir = wrapper_dir.parent_path();
|
||||
|
||||
// Get path of the executable to be invoked.
|
||||
const fs::path arg0{fs::PathFromString(args[0])};
|
||||
|
||||
// Decide whether to fall back to the operating system to search for the
|
||||
// specified executable. Avoid doing this if it looks like the wrapper
|
||||
// executable was invoked by path, rather than by search, to avoid
|
||||
// unintentionally launching system executables in a local build.
|
||||
// (https://github.com/bitcoin/bitcoin/pull/31375#discussion_r1861814807)
|
||||
const bool fallback_os_search{!fs::PathFromString(std::string{wrapper_argv0}).has_parent_path()};
|
||||
|
||||
// If wrapper is installed in a bin/ directory, look for target executable
|
||||
// in libexec/
|
||||
(wrapper_dir.filename() == "bin" && try_exec(fs::path{wrapper_dir.parent_path()} / "libexec" / arg0.filename())) ||
|
||||
#ifdef WIN32
|
||||
// Otherwise check the "daemon" subdirectory in a windows install.
|
||||
(!wrapper_dir.empty() && try_exec(wrapper_dir / "daemon" / arg0.filename())) ||
|
||||
#endif
|
||||
// Otherwise look for target executable next to current wrapper
|
||||
(!wrapper_dir.empty() && try_exec(wrapper_dir / arg0.filename(), fallback_os_search)) ||
|
||||
// Otherwise just look on the system path.
|
||||
(fallback_os_search && try_exec(arg0.filename(), false));
|
||||
}
|
@ -9,6 +9,7 @@ add_library(bitcoin_util STATIC EXCLUDE_FROM_ALL
|
||||
bytevectorhash.cpp
|
||||
chaintype.cpp
|
||||
check.cpp
|
||||
exec.cpp
|
||||
exception.cpp
|
||||
feefrac.cpp
|
||||
fs.cpp
|
||||
|
75
src/util/exec.cpp
Normal file
75
src/util/exec.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2025 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <util/exec.h>
|
||||
|
||||
#include <util/fs.h>
|
||||
#include <util/subprocess.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace util {
|
||||
int ExecVp(const char* file, char* const argv[])
|
||||
{
|
||||
#ifndef WIN32
|
||||
return execvp(file, argv);
|
||||
#else
|
||||
std::vector<std::wstring> escaped_args;
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
for (char* const* arg_ptr{argv}; *arg_ptr; ++arg_ptr) {
|
||||
subprocess::util::quote_argument(converter.from_bytes(*arg_ptr), escaped_args.emplace_back(), false);
|
||||
}
|
||||
|
||||
std::vector<const wchar_t*> new_argv;
|
||||
new_argv.reserve(escaped_args.size() + 1);
|
||||
for (const auto& s : escaped_args) new_argv.push_back(s.c_str());
|
||||
new_argv.push_back(nullptr);
|
||||
return _wexecvp(converter.from_bytes(file).c_str(), new_argv.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
fs::path GetExePath(std::string_view argv0)
|
||||
{
|
||||
// Try to figure out where executable is located. This does a simplified
|
||||
// search that won't work perfectly on every platform and doesn't need to,
|
||||
// as it is only currently being used in a convenience wrapper binary to try
|
||||
// to prioritize locally built or installed executables over system
|
||||
// executables.
|
||||
const fs::path argv0_path{fs::PathFromString(std::string{argv0})};
|
||||
fs::path path{argv0_path};
|
||||
std::error_code ec;
|
||||
#ifndef WIN32
|
||||
// If argv0 doesn't contain a path separator, it was invoked from the system
|
||||
// PATH and can be searched for there.
|
||||
if (!argv0_path.has_parent_path()) {
|
||||
if (const char* path_env = std::getenv("PATH")) {
|
||||
size_t start{0}, end{0};
|
||||
for (std::string_view paths{path_env}; end != std::string_view::npos; start = end + 1) {
|
||||
end = paths.find(':', start);
|
||||
fs::path candidate = fs::path(paths.substr(start, end - start)) / argv0_path;
|
||||
if (fs::is_regular_file(candidate, ec)) {
|
||||
path = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
wchar_t module_path[MAX_PATH];
|
||||
if (GetModuleFileNameW(nullptr, module_path, MAX_PATH) > 0) {
|
||||
path = fs::path{module_path};
|
||||
}
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace util
|
23
src/util/exec.h
Normal file
23
src/util/exec.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2025 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_UTIL_EXEC_H
|
||||
#define BITCOIN_UTIL_EXEC_H
|
||||
|
||||
#include <util/fs.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace util {
|
||||
//! Cross-platform wrapper for POSIX execvp function.
|
||||
//! Arguments and return value are the same as for POSIX execvp, and the argv
|
||||
//! array should consist of null terminated strings and be null terminated
|
||||
//! itself, like the POSIX function.
|
||||
int ExecVp(const char* file, char* const argv[]);
|
||||
//! Return path to current executable assuming it was invoked with argv0.
|
||||
//! If path could not be determined, returns an empty path.
|
||||
fs::path GetExePath(std::string_view argv0);
|
||||
} // namespace util
|
||||
|
||||
#endif // BITCOIN_UTIL_EXEC_H
|
@ -13,6 +13,7 @@ import platform
|
||||
import pdb
|
||||
import random
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
@ -73,33 +74,37 @@ class Binaries:
|
||||
self.paths = paths
|
||||
self.bin_dir = bin_dir
|
||||
|
||||
def daemon_argv(self):
|
||||
def node_argv(self):
|
||||
"Return argv array that should be used to invoke bitcoind"
|
||||
return self._argv(self.paths.bitcoind)
|
||||
return self._argv("node", self.paths.bitcoind)
|
||||
|
||||
def rpc_argv(self):
|
||||
"Return argv array that should be used to invoke bitcoin-cli"
|
||||
return self._argv(self.paths.bitcoincli)
|
||||
# Add -nonamed because "bitcoin rpc" enables -named by default, but bitcoin-cli doesn't
|
||||
return self._argv("rpc", self.paths.bitcoincli) + ["-nonamed"]
|
||||
|
||||
def util_argv(self):
|
||||
"Return argv array that should be used to invoke bitcoin-util"
|
||||
return self._argv(self.paths.bitcoinutil)
|
||||
return self._argv("util", self.paths.bitcoinutil)
|
||||
|
||||
def wallet_argv(self):
|
||||
"Return argv array that should be used to invoke bitcoin-wallet"
|
||||
return self._argv(self.paths.bitcoinwallet)
|
||||
return self._argv("wallet", self.paths.bitcoinwallet)
|
||||
|
||||
def chainstate_argv(self):
|
||||
"Return argv array that should be used to invoke bitcoin-chainstate"
|
||||
return self._argv(self.paths.bitcoinchainstate)
|
||||
return self._argv("chainstate", self.paths.bitcoinchainstate)
|
||||
|
||||
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."""
|
||||
def _argv(self, command, bin_path):
|
||||
"""Return argv array that should be used to invoke the command. It
|
||||
either uses the bitcoin wrapper executable (if BITCOIN_CMD is set), or
|
||||
the direct binary path (bitcoind, etc). When bin_dir is set (by tests
|
||||
calling binaries from previous releases) it always uses the direct
|
||||
path."""
|
||||
if self.bin_dir is not None:
|
||||
return [os.path.join(self.bin_dir, os.path.basename(bin_path))]
|
||||
elif self.paths.bitcoin_cmd is not None:
|
||||
return self.paths.bitcoin_cmd + [command]
|
||||
else:
|
||||
return [bin_path]
|
||||
|
||||
@ -293,6 +298,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
binary + self.config["environment"]["EXEEXT"],
|
||||
)
|
||||
setattr(paths, attribute_name, os.getenv(env_variable_name, default=default_filename))
|
||||
# BITCOIN_CMD environment variable can be specified to invoke bitcoin
|
||||
# wrapper binary instead of other executables.
|
||||
paths.bitcoin_cmd = shlex.split(os.getenv("BITCOIN_CMD", "")) or None
|
||||
return paths
|
||||
|
||||
def get_binaries(self, bin_dir=None):
|
||||
@ -538,7 +546,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
bins_missing = False
|
||||
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())):
|
||||
for argv in (binaries.node_argv(), binaries.rpc_argv())):
|
||||
if shutil.which(bin_path) is None:
|
||||
self.log.error(f"Binary not found: {bin_path}")
|
||||
bins_missing = True
|
||||
|
@ -107,7 +107,7 @@ class TestNode():
|
||||
# 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
|
||||
# spam debug.log.
|
||||
self.args = self.binaries.daemon_argv() + [
|
||||
self.args = self.binaries.node_argv() + [
|
||||
f"-datadir={self.datadir_path}",
|
||||
"-logtimemicros",
|
||||
"-debug",
|
||||
|
Reference in New Issue
Block a user