From 2ae851c89df660751429905209415ef04469a1e2 Mon Sep 17 00:00:00 2001 From: Lee *!* Clagett Date: Fri, 23 Jan 2026 11:25:37 -0500 Subject: [PATCH] Updates to get lwsf building in a mingw (Windows) environment (#6) --- .github/workflows/run_tests.yml | 96 +++++++++++++++++++++++++++++++++ CMakeLists.txt | 3 +- src/CMakeLists.txt | 26 +++++---- src/backend.cpp | 31 +++++------ src/backend.h | 5 +- src/{rpc.cpp => lwsf_rpc.cpp} | 8 ++- src/{rpc.h => lwsf_rpc.h} | 0 src/pending_transaction.cpp | 1 + src/transaction_history.cpp | 4 +- src/transaction_info.cpp | 1 + src/wallet.cpp | 36 +++++++++---- src/wallet.h | 2 +- src/wallet_manager.cpp | 4 +- 13 files changed, 172 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/run_tests.yml rename src/{rpc.cpp => lwsf_rpc.cpp} (99%) rename src/{rpc.h => lwsf_rpc.h} (100%) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml new file mode 100644 index 0000000..09efa46 --- /dev/null +++ b/.github/workflows/run_tests.yml @@ -0,0 +1,96 @@ +name: ci + +on: + push: + branches: [ "main", "release-*" ] + pull_request: + branches: [ "main", "release-*" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + APT_INSTALL_LINUX: 'sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler' + APT_SET_CONF: | + echo "Acquire::Retries \"3\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom + echo "Acquire::http::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom + echo "Acquire::ftp::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom + BREW_INSTALL_MAC: 'HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm miniupnpc expat libunwind-headers protobuf unbound' + CONFIGURE: 'cmake -B "${{github.workspace}}/lwsf/build" -DCMAKE_BUILD_TYPE=Release -DMONERO_SOURCE_DIR="${{github.workspace}}/monero" -DLWSF_MASTER_ENABLE=ON -DLWSF_BUILD_TESTS=ON "${{github.workspace}}/lwsf"' + BUILD: 'cmake --build "${{github.workspace}}/lwsf/build" -j$(nproc)' + RUN: 'ctest --test-dir "${{github.workspace}}/lwsf/build"' + +jobs: + unix-build-and-test: + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest] + steps: + - name: set apt conf (Debian based Linux) + if: matrix.os == 'ubuntu-latest' + run: ${{env.APT_SET_CONF}} + - name: update apt (Debian based Linux) + if: matrix.os == 'ubuntu-latest' + run: sudo apt update + - name: Install dependencies (Debian based Linux) + if: matrix.os == 'ubuntu-latest' + run: ${{env.APT_INSTALL_LINUX}} + - name: Install dependencies (MacOS) + if: matrix.os == 'macos-latest' + run: ${{env.BREW_INSTALL_MAC}} + - name: Checkout Monero Source + uses: actions/checkout@v4 + with: + repository: monero-project/monero + path: ${{github.workspace}}/monero/ + submodules: recursive + - name: Checkout LWSF Source + uses: actions/checkout@v4 + with: + path: ${{github.workspace}}/lwsf + submodules: recursive + - name: Configure LWS + run: ${{env.CONFIGURE}} + - name: Build LWSF + run: ${{env.BUILD}} + - name: Run Tests + run: ${{env.RUN}} + windows-build-and-test: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + steps: + - name: Install dependencies + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: > + mingw-w64-x86_64-boost + mingw-w64-x86_64-cmake + mingw-w64-x86_64-gcc + mingw-w64-x86_64-hidapi + mingw-w64-x86_64-miniupnpc + mingw-w64-x86_64-openssl + mingw-w64-x86_64-unbound + mingw-w64-x86_64-zeromq + - name: Checkout Monero Source + uses: actions/checkout@v4 + with: + repository: monero-project/monero + path: ${{github.workspace}}/monero/ + submodules: recursive + - name: Checkout LWSF Source + uses: actions/checkout@v4 + with: + path: ${{github.workspace}}/lwsf + submodules: recursive + - name: Configure LWS + run: ${{env.CONFIGURE}} + - name: Build LWSF + run: ${{env.BUILD}} + - name: Run Tests + run: ${{env.RUN}} diff --git a/CMakeLists.txt b/CMakeLists.txt index bc9d730..d2713bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,9 @@ if (NOT MONERO_SOURCE_DIR) message(FATAL_ERROR "The argument -DMONERO_SOURCE_DIR must specify a location of a monero source tree") endif() +cmake_path(CONVERT "${MONERO_SOURCE_DIR}" TO_CMAKE_PATH_LIST MONERO_SOURCE_DIR_CONVERTED) include(FetchContent) -FetchContent_Declare(monero SOURCE_DIR ${MONERO_SOURCE_DIR} EXCLUDE_FROM_ALL) +FetchContent_Declare(monero SOURCE_DIR ${MONERO_SOURCE_DIR_CONVERTED} EXCLUDE_FROM_ALL) if (NOT monero_POPULATED) FetchContent_MakeAvailable(monero) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 463c895..ff831d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,8 +33,8 @@ set(lwsf_sources address_book.cpp backend.cpp error.cpp + lwsf_rpc.cpp pending_transaction.cpp - rpc.cpp subaddress_account.cpp subaddress_minor.cpp transaction_history.cpp @@ -47,8 +47,8 @@ set(lwsf_headers backend.h error.h lwsf_config.h + lwsf_rpc.h pending_transaction.h - rpc.h transaction_history.h transaction_info.h utils/encrypted_file.h @@ -80,6 +80,11 @@ target_include_directories(lwsf-api "." ) +set(SSP "") +if (WIN32) + set(SSP "-lssp") +endif() + target_link_libraries(lwsf-api PUBLIC monero::libraries @@ -93,14 +98,15 @@ target_link_libraries(lwsf-api ${sodium_LIBRARY_RELEASE} ${LWSF_POLYSEED_LIBRARY} Threads::Threads + ${SSP} ) - -add_executable(lwsf-ledger ledger_main.cpp) -target_link_libraries(lwsf-ledger - PRIVATE - lwsf-api - ${Boost_PROGRAM_OPTIONS_LIBRARY} -) - +if (NOT WIN32) + add_executable(lwsf-ledger ledger_main.cpp) + target_link_libraries(lwsf-ledger + PRIVATE + lwsf-api + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ) +endif() diff --git a/src/backend.cpp b/src/backend.cpp index f0d178b..8a4e4bb 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -26,6 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "net/http_client.h" // monero/contrib/epee/include #include "backend.h" #include @@ -820,7 +821,7 @@ namespace lwsf { namespace internal { namespace backend wallet::wallet() : listener(nullptr), - client(), + client(std::make_unique()), primary{}, per_byte_fee(), refresh_error(), @@ -925,7 +926,7 @@ namespace lwsf { namespace internal { namespace backend }; lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, login); + const auto response = rpc::invoke(*client, client_prefix, login); if (!response) { if (response == rpc_unapproved) @@ -956,7 +957,7 @@ namespace lwsf { namespace internal { namespace backend rpc::login login{primary.address, primary.view.sec}; { lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, login).value(); + const auto response = rpc::invoke(*client, client_prefix, login).value(); lock.lock(); if (update_lookaheads(*this, response.all_subaddrs)) return new_account; @@ -967,7 +968,7 @@ namespace lwsf { namespace internal { namespace backend fill_upsert(*this, request.subaddrs_); lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, request).value(); + const auto response = rpc::invoke(*client, client_prefix, request).value(); lock.lock(); update_lookaheads(*this, response.all_subaddrs); } @@ -1025,11 +1026,11 @@ namespace lwsf { namespace internal { namespace backend } expect outs_response{common_error::kInvalidArgument}; - const auto txs_response = rpc::invoke(client, client_prefix, login); + const auto txs_response = rpc::invoke(*client, client_prefix, login); if (txs_response) { const rpc::get_unspent_outs_request request{login, rpc::uint64_string(0), 0, true}; - outs_response = rpc::invoke(client, client_prefix, request); + outs_response = rpc::invoke(*client, client_prefix, request); } lock.lock(); @@ -1061,7 +1062,7 @@ namespace lwsf { namespace internal { namespace backend fill_upsert(*this, request.subaddrs_); lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, request); + const auto response = rpc::invoke(*client, client_prefix, request); lock.lock(); if (response && update_lookaheads(*this, response->all_subaddrs)) lookahead_error = {}; @@ -1083,7 +1084,7 @@ namespace lwsf { namespace internal { namespace backend }; lock.unlock(); - const auto upsert_response = rpc::invoke(client, client_prefix, upsert_request); + const auto upsert_response = rpc::invoke(*client, client_prefix, upsert_request); lock.lock(); if (upsert_response) @@ -1105,7 +1106,7 @@ namespace lwsf { namespace internal { namespace backend }; lock.unlock(); - const auto provision_response = rpc::invoke(client, client_prefix, provision_request); + const auto provision_response = rpc::invoke(*client, client_prefix, provision_request); lock.lock(); if (provision_response) @@ -1184,7 +1185,7 @@ namespace lwsf { namespace internal { namespace backend }; lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, request); + const auto response = rpc::invoke(*client, client_prefix, request); lock.lock(); if (response) server_lookahead = std::max(server_lookahead, needed_maj_i); @@ -1224,7 +1225,7 @@ namespace lwsf { namespace internal { namespace backend }; lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, request); + const auto response = rpc::invoke(*client, client_prefix, request); lock.lock(); if (response) @@ -1266,7 +1267,7 @@ namespace lwsf { namespace internal { namespace backend fill_upsert(*this, request.subaddrs_); lock.unlock(); - const auto response = rpc::invoke(client, client_prefix, request); + const auto response = rpc::invoke(*client, client_prefix, request); lock.lock(); if (!response) return (lookahead_error = handle_lookahead_error(response.error())); @@ -1306,7 +1307,7 @@ namespace lwsf { namespace internal { namespace backend rpc::import_request login{{primary.address, primary.view.sec}, height}; lock.unlock(); - auto import = rpc::invoke(client, client_prefix, login); + auto import = rpc::invoke(*client, client_prefix, login); lock.lock(); if (!import) @@ -1364,7 +1365,7 @@ namespace lwsf { namespace internal { namespace backend expect> wallet::get_decoys(const rpc::get_random_outs_request& req) { - auto resp = rpc::invoke(client, client_prefix, req); + auto resp = rpc::invoke(*client, client_prefix, req); if (!resp) return resp.error(); return {std::move(resp->amount_outs)}; @@ -1373,6 +1374,6 @@ namespace lwsf { namespace internal { namespace backend std::error_code wallet::send_tx(epee::byte_slice tx_bytes) { const rpc::submit_raw_tx_request request{std::move(tx_bytes)}; - return rpc::invoke(client, client_prefix, request).error(); + return rpc::invoke(*client, client_prefix, request).error(); } }}} // lwsf // internal // backend diff --git a/src/backend.h b/src/backend.h index 8d94c3c..dbe6210 100644 --- a/src/backend.h +++ b/src/backend.h @@ -50,9 +50,8 @@ #include "cryptonote_config.h" // monero/src #include "cryptonote_basic/account.h" // monero/src #include "lwsf_config.h" -#include "net/http_client.h" // monero/contrib/epee/include +#include "lwsf_rpc.h" #include "ringct/rctTypes.h" // monero/src -#include "rpc.h" #include "wallet/api/wallet2_api.h" // monero/src #include "wire/fwd.h" @@ -233,7 +232,7 @@ namespace lwsf { namespace internal { namespace backend struct wallet { Monero::WalletListener* listener; - rpc::http_client client; + const std::unique_ptr client; account primary; std::string client_prefix; std::vector per_byte_fee; //!< by priority level diff --git a/src/rpc.cpp b/src/lwsf_rpc.cpp similarity index 99% rename from src/rpc.cpp rename to src/lwsf_rpc.cpp index efd7b83..5bea427 100644 --- a/src/rpc.cpp +++ b/src/lwsf_rpc.cpp @@ -26,7 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "rpc.h" +#include "net/http_client.h" // monero/contrib/epee/include +#include "lwsf_rpc.h" #include #include @@ -35,7 +36,6 @@ #include #include "hex.h" // monero/contrib/epee/include #include "lwsf_config.h" -#include "net/http_client.h" // monero/contrib/epee/include #include "ringct/rctOps.h" // monero/contrib/epee/include #include "wire.h" #include "wire/adapted/array.h" @@ -47,6 +47,10 @@ #include "wire/wrapper/trusted_array.h" #include "wire/wrappers_impl.h" +#ifdef _WIN32 + #define timegm _mkgmtime +#endif + namespace lwsf { namespace internal { namespace rpc { namespace diff --git a/src/rpc.h b/src/lwsf_rpc.h similarity index 100% rename from src/rpc.h rename to src/lwsf_rpc.h diff --git a/src/pending_transaction.cpp b/src/pending_transaction.cpp index 43a5d62..6b1fb8a 100644 --- a/src/pending_transaction.cpp +++ b/src/pending_transaction.cpp @@ -29,6 +29,7 @@ #include "pending_transaction.h" #include +#include #include #include #include "backend.h" diff --git a/src/transaction_history.cpp b/src/transaction_history.cpp index fa6e2d8..7bbd590 100644 --- a/src/transaction_history.cpp +++ b/src/transaction_history.cpp @@ -72,7 +72,7 @@ namespace lwsf { namespace internal Monero::TransactionInfo* transaction_history::transaction(const std::string &id) const { crypto::hash binary_id{}; - if (!epee::string_tools::hex_to_pod(id, binary_id)) + if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(binary_id), id)) return nullptr; // hold the lock during the map lookup, this is const and should be thread-safe @@ -128,7 +128,7 @@ namespace lwsf { namespace internal void transaction_history::setTxNote(const std::string &txid, const std::string ¬e) { crypto::hash binary_id{}; - if (!epee::string_tools::hex_to_pod(txid, binary_id)) + if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(binary_id), txid)) return; const boost::lock_guard lock{data_->sync}; diff --git a/src/transaction_info.cpp b/src/transaction_info.cpp index fca5a0a..91b006c 100644 --- a/src/transaction_info.cpp +++ b/src/transaction_info.cpp @@ -29,6 +29,7 @@ #include "transaction_info.h" #include +#include #include #include "hex.h" diff --git a/src/wallet.cpp b/src/wallet.cpp index e6b0da7..ff40514 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -25,11 +25,15 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + +#include "net/http_client.h" // monero/contrib/epee/include #include "wallet.h" #if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) #include +#elif defined(_WIN32) + #include + #include #endif #include @@ -206,6 +210,20 @@ namespace lwsf { namespace internal close(fd); return rc; } +#elif defined(_WIN32) + bool atomic_file_write(const boost::filesystem::path& filename, const boost::filesystem::path&, epee::byte_slice contents) noexcept + { + HANDLE file = CreateFileW(filename.c_str(), (GENERIC_READ | GENERIC_WRITE), 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (file == INVALID_HANDLE_VALUE) + return false; + + bool rc = false; + if (WriteFile(file, contents.data(), contents.size(), nullptr, nullptr)) + rc = FlushFileBuffers(file); + + rc &= CloseHandle(file); + return rc; + } #else #error atomic_file_write not implemented for this platform #endif @@ -834,7 +852,7 @@ namespace lwsf { namespace internal } const boost::unique_lock lock{data_->sync}; - data_->client.set_server(std::move(url.host), std::to_string(url.port), std::move(login), std::move(options)); + data_->client->set_server(std::move(url.host), std::to_string(url.port), std::move(login), std::move(options)); data_->passed_login = false; data_->client_prefix = std::move(url.uri); } @@ -867,12 +885,12 @@ namespace lwsf { namespace internal bool wallet::connectToDaemon() { - const bool connected = data_->client.is_connected(); + const bool connected = data_->client->is_connected(); boost::unique_lock lock{data_->sync}; if (connected && data_->passed_login) return true; - if (connected || data_->client.connect(config::connect_timeout)) + if (connected || data_->client->connect(config::connect_timeout)) { lock.unlock(); return set_error(data_->login()); @@ -882,7 +900,7 @@ namespace lwsf { namespace internal Monero::Wallet::ConnectionStatus wallet::connected() const { - if (!data_->client.is_connected()) + if (!data_->client->is_connected()) return ConnectionStatus_Disconnected; const boost::lock_guard lock{data_->sync}; @@ -892,18 +910,18 @@ namespace lwsf { namespace internal bool wallet::setProxy(const std::string &address) { - data_->client.disconnect(); + data_->client->disconnect(); if (address.empty()) - data_->client.set_connector(epee::net_utils::direct_connect{}); + data_->client->set_connector(epee::net_utils::direct_connect{}); else { auto endpoint = net::get_tcp_endpoint(address); if (!endpoint) { - data_->client.set_connector(null_connector{}); + data_->client->set_connector(null_connector{}); return set_error(endpoint.error()); } - data_->client.set_connector(net::socks::connector{std::move(*endpoint)}); + data_->client->set_connector(net::socks::connector{std::move(*endpoint)}); } return true; } diff --git a/src/wallet.h b/src/wallet.h index 0d61846..eb9cbf5 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -40,8 +40,8 @@ #include #include #include +#include "byte_slice.h" // monero/contrib/epee/include #include "crypto/crypto.h" // monero/src -#include "net/http_client.h" // monero/contrib/epee/include #include "wallet/api/wallet2_api.h" // monero/src namespace lwsf diff --git a/src/wallet_manager.cpp b/src/wallet_manager.cpp index 624e15e..53dc20f 100644 --- a/src/wallet_manager.cpp +++ b/src/wallet_manager.cpp @@ -26,6 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "net/http_client.h" // monero/contrib/epee/include #include "lws_frontend.h" #include @@ -38,15 +39,14 @@ #include "common/dns_utils.h" // monero/src #include "cryptonote_basic/cryptonote_format_utils.h" // monero/src #include "lwsf_config.h" +#include "lwsf_rpc.h" #include "lws_frontend.h" #ifdef LWSF_POLYSEED_ENABLE #include "polyseed.h" #endif -#include "net/http_client.h" // monero/contrib/epee/include #include "net/parse.h" // monero/src #include "net/socks_connect.h" // monero/src #include "QrCode.hpp" // monero/src/external -#include "rpc.h" #include "wallet.h" #include "wallet/api/wallet2_api.h" // monero/src