Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reintroduce external signer support for Windows #29868

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions build_msvc/bitcoin_config.h.in
Expand Up @@ -41,6 +41,9 @@
/* Define this symbol to enable ZMQ functions */
#define ENABLE_ZMQ 1

/* Define if external signer support is enabled */
#define ENABLE_EXTERNAL_SIGNER 1

/* Define to 1 if you have the declaration of `fork', and to 0 if you don't.
*/
#define HAVE_DECL_FORK 0
Expand Down
2 changes: 1 addition & 1 deletion build_msvc/bitcoind/bitcoind.vcxproj
Expand Up @@ -84,7 +84,7 @@
<ReplaceInFile FilePath="$(ConfigIniOut)"
Replace="@ENABLE_ZMQ_TRUE@" By=""></ReplaceInFile>
<ReplaceInFile FilePath="$(ConfigIniOut)"
Replace="@ENABLE_EXTERNAL_SIGNER_TRUE@" By="#"></ReplaceInFile>
Replace="@ENABLE_EXTERNAL_SIGNER_TRUE@" By=""></ReplaceInFile>
<ReplaceInFile FilePath="$(ConfigIniOut)"
Replace="@ENABLE_USDT_TRACEPOINTS_TRUE@" By="#"></ReplaceInFile>
</Target>
Expand Down
2 changes: 1 addition & 1 deletion build_msvc/common.init.vcxproj.in
Expand Up @@ -90,7 +90,7 @@
<AdditionalOptions>/utf-8 /Zc:preprocessor /Zc:__cplusplus /std:c++20 %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>4018;4244;4267;4715;4805</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
<PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SECP256K1_STATIC;ZMQ_STATIC;NOMINMAX;WIN32;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;PROVIDE_FUZZ_MAIN_FUNCTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SECP256K1_STATIC;ZMQ_STATIC;NOMINMAX;WIN32;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;PROVIDE_FUZZ_MAIN_FUNCTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src;..\..\src\minisketch\include;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
Expand Down
2 changes: 1 addition & 1 deletion ci/test/03_test_script.sh
Expand Up @@ -119,7 +119,7 @@ if [ -n "$ANDROID_TOOLS_URL" ]; then
exit 0
fi

BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} --enable-external-signer --prefix=$BASE_OUTDIR"
BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} --prefix=$BASE_OUTDIR"

if [ -n "$CONFIG_SHELL" ]; then
"$CONFIG_SHELL" -c "./autogen.sh"
Expand Down
6 changes: 0 additions & 6 deletions configure.ac
Expand Up @@ -1397,12 +1397,6 @@ if test "$use_boost" = "yes"; then
fi
fi

case $host in
dnl Re-enable it after enabling Windows support in cpp-subprocess.
*mingw*)
use_external_signer="no"
;;
esac
if test "$use_external_signer" = "yes"; then
AC_DEFINE([ENABLE_EXTERNAL_SIGNER], [1], [Define if external signer support is enabled])
fi
Expand Down
32 changes: 31 additions & 1 deletion src/test/system_tests.cpp
Expand Up @@ -27,25 +27,44 @@ BOOST_AUTO_TEST_CASE(dummy)

BOOST_AUTO_TEST_CASE(run_command)
{
#ifdef WIN32
// https://www.winehq.org/pipermail/wine-devel/2008-September/069387.html
auto hntdll = GetModuleHandleA("ntdll.dll");
assert(hntdll);
const bool wine_runtime = GetProcAddress(hntdll, "wine_get_version");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check will fail on Windows. I'm guessing that's expected but just checking?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal is to detect that binaries, which are cross-compiled for Windows, are running in Wine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean this PR is intended to be a fix for Windows or for wine?

Copy link
Member Author

@hebasto hebasto May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For both. Wine is used to run test suite on Linux after cross-compiling for Windows. This need roots from the fact that Wine and Windows runtimes produce different error messages:

const std::string expected{wine_runtime ? "File not found." : "File Not Found"};

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, the same behaviour was before #29489.

#endif

{
const UniValue result = RunCommandParseJSON("");
BOOST_CHECK(result.isNull());
}
{
#ifdef WIN32
const UniValue result = RunCommandParseJSON("cmd.exe /c echo {\"success\": true}");
#else
const UniValue result = RunCommandParseJSON("echo {\"success\": true}");
#endif
BOOST_CHECK(result.isObject());
const UniValue& success = result.find_value("success");
BOOST_CHECK(!success.isNull());
BOOST_CHECK_EQUAL(success.get_bool(), true);
}
{
// An invalid command is handled by cpp-subprocess
#ifdef WIN32
const std::string expected{"CreateProcess failed: "};
#else
const std::string expected{"execve failed: "};
#endif
BOOST_CHECK_EXCEPTION(RunCommandParseJSON("invalid_command"), subprocess::CalledProcessError, HasReason(expected));
}
{
// Return non-zero exit code, no output to stderr
#ifdef WIN32
const std::string command{"cmd.exe /c exit 1"};
#else
const std::string command{"false"};
#endif
BOOST_CHECK_EXCEPTION(RunCommandParseJSON(command), std::runtime_error, [&](const std::runtime_error& e) {
const std::string what{e.what()};
BOOST_CHECK(what.find(strprintf("RunCommandParseJSON error: process(%s) returned 1: \n", command)) != std::string::npos);
Expand All @@ -54,8 +73,13 @@ BOOST_AUTO_TEST_CASE(run_command)
}
{
// Return non-zero exit code, with error message for stderr
#ifdef WIN32
const std::string command{"cmd.exe /c dir nosuchfile"};
const std::string expected{wine_runtime ? "File not found." : "File Not Found"};
#else
const std::string command{"ls nosuchfile"};
const std::string expected{"No such file or directory"};
#endif
BOOST_CHECK_EXCEPTION(RunCommandParseJSON(command), std::runtime_error, [&](const std::runtime_error& e) {
const std::string what(e.what());
BOOST_CHECK(what.find(strprintf("RunCommandParseJSON error: process(%s) returned", command)) != std::string::npos);
Expand All @@ -65,17 +89,23 @@ BOOST_AUTO_TEST_CASE(run_command)
}
{
// Unable to parse JSON
#ifdef WIN32
const std::string command{"cmd.exe /c echo {"};
#else
const std::string command{"echo {"};
#endif
BOOST_CHECK_EXCEPTION(RunCommandParseJSON(command), std::runtime_error, HasReason("Unable to parse JSON: {"));
}
// Test std::in
#ifndef WIN32
{
// Test std::in
const UniValue result = RunCommandParseJSON("cat", "{\"success\": true}");
BOOST_CHECK(result.isObject());
const UniValue& success = result.find_value("success");
BOOST_CHECK(!success.isNull());
BOOST_CHECK_EQUAL(success.get_bool(), true);
}
#endif
}
#endif // ENABLE_EXTERNAL_SIGNER

Expand Down
47 changes: 25 additions & 22 deletions src/util/subprocess.h
Expand Up @@ -55,17 +55,13 @@ Documentation for C++ subprocessing library.
#include <string>
#include <vector>

#if (defined _MSC_VER) || (defined __MINGW32__)
#define __USING_WINDOWS__
#endif

#ifdef __USING_WINDOWS__
#ifdef WIN32
#include <codecvt>
#endif

extern "C" {
#ifdef __USING_WINDOWS__
#include <Windows.h>
#ifdef WIN32
#include <windows.h>
#include <io.h>
#include <cwchar>

Expand Down Expand Up @@ -169,7 +165,7 @@ namespace util
//

if (force == false && argument.empty() == false &&
argument.find_first_of(L" \t\n\v\"") == argument.npos) {
argument.find_first_of(L" \t\n\v") == argument.npos) {
command_line.append(argument);
}
else {
Expand Down Expand Up @@ -219,7 +215,7 @@ namespace util
}
}

#ifdef __USING_WINDOWS__
#ifdef WIN32
inline std::string get_last_error(DWORD errorMessageID)
{
if (errorMessageID == 0)
Expand Down Expand Up @@ -320,7 +316,7 @@ namespace util
}


#ifndef __USING_WINDOWS__
#ifndef WIN32
/*!
* Function: set_clo_on_exec
* Sets/Resets the FD_CLOEXEC flag on the provided file descriptor
Expand Down Expand Up @@ -408,7 +404,7 @@ namespace util
static inline
int read_atmost_n(FILE* fp, char* buf, size_t read_upto)
{
#ifdef __USING_WINDOWS__
#ifdef WIN32
return (int)fread(buf, 1, read_upto, fp);
#else
int fd = fileno(fp);
Expand Down Expand Up @@ -481,7 +477,7 @@ namespace util
return total_bytes_read;
}

#ifndef __USING_WINDOWS__
#ifndef WIN32
/*!
* Function: wait_for_child_exit
* Waits for the process with pid `pid` to exit
Expand Down Expand Up @@ -582,7 +578,7 @@ struct input
}
explicit input(IOTYPE typ) {
assert (typ == PIPE && "STDOUT/STDERR not allowed");
#ifndef __USING_WINDOWS__
#ifndef WIN32
std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
#endif
}
Expand Down Expand Up @@ -615,7 +611,7 @@ struct output
}
explicit output(IOTYPE typ) {
assert (typ == PIPE && "STDOUT/STDERR not allowed");
#ifndef __USING_WINDOWS__
#ifndef WIN32
std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
#endif
}
Expand Down Expand Up @@ -647,7 +643,7 @@ struct error
explicit error(IOTYPE typ) {
assert ((typ == PIPE || typ == STDOUT) && "STDERR not allowed");
if (typ == PIPE) {
#ifndef __USING_WINDOWS__
#ifndef WIN32
std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
#endif
} else {
Expand Down Expand Up @@ -858,7 +854,7 @@ class Streams
std::shared_ptr<FILE> output_ = nullptr;
std::shared_ptr<FILE> error_ = nullptr;

#ifdef __USING_WINDOWS__
#ifdef WIN32
HANDLE g_hChildStd_IN_Rd = nullptr;
HANDLE g_hChildStd_IN_Wr = nullptr;
HANDLE g_hChildStd_OUT_Rd = nullptr;
Expand Down Expand Up @@ -999,7 +995,7 @@ class Popen
private:
detail::Streams stream_;

#ifdef __USING_WINDOWS__
#ifdef WIN32
HANDLE process_handle_;
std::future<void> cleanup_future_;
#endif
Expand Down Expand Up @@ -1040,10 +1036,17 @@ inline void Popen::populate_c_argv()

inline int Popen::wait() noexcept(false)
{
#ifdef __USING_WINDOWS__
#ifdef WIN32
int ret = WaitForSingleObject(process_handle_, INFINITE);
if (ret != WAIT_OBJECT_0) return -1;

return 0;
DWORD dretcode_;
if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
throw OSError("GetExitCodeProcess", 0);

CloseHandle(process_handle_);

return (int)dretcode_;
#else
int ret, status;
std::tie(ret, status) = util::wait_for_child_exit(child_pid_);
Expand All @@ -1061,7 +1064,7 @@ inline int Popen::wait() noexcept(false)

inline void Popen::execute_process() noexcept(false)
{
#ifdef __USING_WINDOWS__
#ifdef WIN32
if (exe_name_.length()) {
this->vargs_.insert(this->vargs_.begin(), this->exe_name_);
this->populate_c_argv();
Expand Down Expand Up @@ -1235,7 +1238,7 @@ namespace detail {


inline void Child::execute_child() {
#ifndef __USING_WINDOWS__
#ifndef WIN32
int sys_ret = -1;
auto& stream = parent_->stream_;

Expand Down Expand Up @@ -1301,7 +1304,7 @@ namespace detail {

inline void Streams::setup_comm_channels()
{
#ifdef __USING_WINDOWS__
#ifdef WIN32
util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
this->write_to_child_ = _fileno(this->input());
Expand Down