Add bitcoin wrapper executable (interfaces)
https://github.com/bitcoin/bitcoin/pull/31375
Host: ryanofsky -
Notes
Motivation & context
- For years Bitcoin Core has shipped five separate user‑facing binaries.
The upcoming multiprocess work would add at least two more (
bitcoin‑node
,bitcoin‑gui
). Reviewers feared an explosion of filenames and user confusion. -
The PR introduces a single command‑line front‑end called
bitcoin
that does no consensus or wallet work itself – it simply chooses andexec()
’s the appropriate helper binary:bitcoin
sub‑commandTraditional binary Multiprocess binary ( -m
)bitcoin gui …
bitcoin‑qt
bitcoin‑gui
bitcoin daemon …
bitcoind
bitcoin‑node
bitcoin rpc …
bitcoin‑cli -named …
bitcoin‑cli -named …
bitcoin wallet …
bitcoin‑wallet
bitcoin-wallet
bitcoin tx …
bitcoin‑tx
bitcoin-tx
The
bitcoin
wrapper therefore accomplishes the “side‑binaries + unified entry point” idea discussed in issue #30983.
New util helpers
util::ExecVp()
– thin, cross‑platformexecvp
replacement.- POSIX: directly forwards to
execvp
. - Windows: builds a quoted & escaped command line that
CommandLineToArgvW
in the child process will parse identically to POSIX argv rules. - Escaping rules follow the MSVCRT specification: backslashes are doubled only when they precede a quote, and every internal quote is back‑slash‑escaped.
- POSIX: directly forwards to
util::GetExePath()
– attempts to resolveargv[0]
into the executable file path.- On Unix: uses either the literal
argv[0]
(if it contains a slash) or searches each element of$PATH
until a regular file is found. - On Windows: uses
GetModuleFileNameW(nullptr, …)
.
- On Unix: uses either the literal
Wrapper lookup logic (ExecCommand
)
- Determine the directory of the wrapper itself (resolves symlinks).
- Try possible candidate paths for the target binary, in descending priority:
- libexec dir –
${prefix}/libexec/<target>
if wrapper is in${prefix}/bin/
- Windows installer “daemon” sub‑dir
${wrapper_dir}/daemon/<target>
- Sibling –
${wrapper_dir}/<target>
- Finally, rely on the system PATH only if the wrapper itself was
invoked via PATH search (mitigates accidentally running an old system
bitcoind
while testing a local build).
- libexec dir –
- Call
util::ExecVp()
with each candidate, moving onto the next candidate if it returnsENOENT
(“No such file or directory”) and raising an exception if a different error is returned or if there is no next candidate.
Build‑system & test changes
- CMake option
BUILD_BITCOIN_BIN
(ON by default) builds/installs the wrapper. - Functional test framework understands
BITCOIN_CMD="bitcoin -m"
so the entire suite can be driven through the new CLI. - CI jobs for the multiprocess build now export that variable.
- Static‑analysis suppression: the wrapper intentionally contains no FORTIFY
functions;
security-check.py
is taught to ignore it.
Documentation updates
Numerous docs now mention that bitcoin rpc
, bitcoin daemon
, etc. are
synonyms for the traditional commands, improving discoverability for new
users while remaining fully backwards‑compatible.
Questions
-
Did you review the PR? Concept ACK, approach ACK, tested ACK, or NACK? What was your review approach?
-
Review approach – did you test the wrapper? Did you try both monolithic (
bitcoin daemon
) and multiprocess (bitcoin -m daemon
) modes? (requires-DENABLE_IPC=ON
cmake option). Attempt to run one of thestrace
ordtrace
tracing commands suggested inbitcoin.cpp
? Any cross‑platform checks? -
From issue #30983, four packaging strategies were listed. Which specific drawbacks of the “side‑binaries” approach does this PR address?
-
In
util::ExecVp()
(Windows branch) why is a secondstd::vector
escaped_args
needed instead of modifyingargv
in‑place? -
Walk through the escaping algorithm in
util::ExecVp
for the argumentC:\Program Files\Bitcoin\bitcoin-qt
. What exact string is passed to_execvp()
? -
GetExePath()
does not usereadlink("/proc/self/exe")
on Linux even though it would be more direct. What advantages does the current implementation have? What corner cases might it miss? -
In
ExecCommand
, explain the purpose of thefallback_os_search
Boolean. Under what circumstances is it better to avoid letting the OS search for the binary on the PATH? -
The wrapper searches
${prefix}/libexec
only when it detects that it is running from an installedbin/
directory. Why not always searchlibexec
? -
The functional test layer now conditionally prepends
bitcoin -m
to every command. How does this interact with backwards‑compatibility testing where older releases are run in the same test suite? -
The PR adds an exemption in
security-check.py
because the wrapper contains no fortified glibc calls. Why does it not contain them, and would adding a trivialprintf
tobitcoin.cpp
break reproducible builds under the current rules? -
Discuss an alternative design: linking a static table of sub‑commands to absolute paths at build time instead of computing them at run time. What trade‑offs (deployment, relocatability, reproducibility) influenced the chosen design?
-
Suppose a user installs only
bitcoin
(wrapper) and forgets to installbitcoin-cli
. Describe the failure mode when they runbitcoin rpc getblockcount
. Would it be better for the wrapper to pre‑check the availability of the target binary? -
(Forward‑looking) Once
bitcoin-gui
actually spawnsbitcoin-node
automatically (after #10102 lands), what additional command‑line options or UX changes might the wrapper need? -
Typing
bitcoin --version
prints wrapper metadata, notbitcoind
’s orbitcoin‑qt
’s. Is that the right UX? Propose a mechanism for the wrapper to forward--version
and--help
to the underlying sub‑command when one is specified (e.g.bitcoin --version daemon
). -
The wrapper is agnostic to options such as
-ipcbind
passed down tobitcoin‑node
. Should the wrapper eventually enforce a policy (e.g. refuse to forward-ipcconnect
unless-m
is given)? What might go wrong if a user mixes monolithic binaries with IPC flags? -
BITCOIN_CMD="bitcoin -m"
is parsed withshlex
; spaces inside quotes are preserved. Should the framework use an explicit list instead of shell parsing? -
Would it ever make sense to ship only the wrapper in
bin/
and relocate all other executables tolibexec/
to tidy PATH?