diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd2087ad..535e1d9d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,6 +21,7 @@ cmake_policy(SET CMP0074 NEW)
option(STATIC "Link libraries statically, requires static Qt" OFF)
option(SELF_CONTAINED "Disable when building Feather for packages" OFF)
option(TOR_DIR "Directory containing Tor binaries to embed inside Feather" OFF)
+option(WOWNEROD_DIR "Directory containing the wownerod binary to embed inside wowlet" OFF)
option(CHECK_UPDATES "Enable checking for application updates" OFF)
option(PLATFORM_INSTALLER "Built-in updater fetches installer (windows-only)" OFF)
option(USE_DEVICE_TREZOR "Trezor support compilation" ON)
@@ -137,6 +138,7 @@ endif()
include(GenerateDocs)
include(TorQrcGenerator)
+include(WownerodQrcGenerator)
# To build Feather with embedded (and static) Tor, pass CMake -DTOR_DIR=/path/to/tor/
if(TOR_DIR)
@@ -163,6 +165,26 @@ else()
message(STATUS "Skipping Tor inclusion because -DTOR_DIR=Off")
endif()
+# wowlet: copy the wownerod binary into src/assets/wownerod so it gets embedded — single self-contained
+# file with the node inside. WOWNEROD_DIR points at a dir containing a `wownerod` (or wownerod.exe).
+if(WOWNEROD_DIR)
+ execute_process(COMMAND bash -c "mkdir -p ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/wownerod")
+ if (REPRODUCIBLE)
+ set(WOWNEROD_COPY_CMD "cp -a ${WOWNEROD_DIR}/* ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/wownerod")
+ else()
+ set(WOWNEROD_COPY_CMD "cp -au ${WOWNEROD_DIR}/* ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/wownerod")
+ endif()
+
+ execute_process(COMMAND bash -c "${WOWNEROD_COPY_CMD}" RESULT_VARIABLE ret)
+ if(ret EQUAL "1")
+ message(FATAL_ERROR "wownerod copy failure: ${WOWNEROD_COPY_CMD}")
+ endif()
+
+ message(STATUS "Embedding wownerod binary from ${WOWNEROD_DIR}")
+else()
+ message(STATUS "Skipping wownerod inclusion because -DWOWNEROD_DIR=Off")
+endif()
+
message(STATUS "Using Boost include dir at ${Boost_INCLUDE_DIRS}")
message(STATUS "Using Boost libraries at ${Boost_LIBRARIES}")
diff --git a/cmake/WownerodQrcGenerator.cmake b/cmake/WownerodQrcGenerator.cmake
new file mode 100644
index 00000000..5855ef33
--- /dev/null
+++ b/cmake/WownerodQrcGenerator.cmake
@@ -0,0 +1,24 @@
+# Generates src/assets_wownerod.qrc embedding the wownerod binary from WOWNEROD_DIR, so wowlet ships
+# as a single self-contained file with its node inside. Mirror of TorQrcGenerator.cmake. When
+# WOWNEROD_DIR is OFF the generated qrc is empty (no embedded daemon) and HAS_WOWNEROD_BIN is unset.
+set(QRC_LIST)
+
+if (WOWNEROD_DIR)
+ FILE(GLOB WOWNEROD_FILES LIST_DIRECTORIES false ${WOWNEROD_DIR}/*)
+
+ foreach(FILE ${WOWNEROD_FILES})
+ cmake_path(GET FILE FILENAME FILE_REL)
+ list(APPEND QRC_LIST " assets/wownerod/${FILE_REL}")
+
+ if (FILE_REL STREQUAL "wownerod" OR FILE_REL STREQUAL "wownerod.exe")
+ set(WOWNEROD_BIN_FOUND 1)
+ endif()
+ endforeach()
+
+ if (NOT WOWNEROD_BIN_FOUND)
+ message(FATAL_ERROR "WOWNEROD_DIR was specified but the wownerod binary could not be found")
+ endif()
+endif()
+
+list(JOIN QRC_LIST "\n" QRC_DATA)
+configure_file("cmake/assets_wownerod.qrc" "${CMAKE_CURRENT_SOURCE_DIR}/src/assets_wownerod.qrc")
diff --git a/cmake/assets_wownerod.qrc b/cmake/assets_wownerod.qrc
new file mode 100644
index 00000000..f5363d50
--- /dev/null
+++ b/cmake/assets_wownerod.qrc
@@ -0,0 +1,5 @@
+
+
+@QRC_DATA@
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1ad529ca..eca618a0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,7 +31,7 @@ if (CHECK_UPDATES)
add_subdirectory(openpgp)
endif()
-qt_add_resources(RESOURCES assets.qrc assets_tor.qrc assets_docs.qrc)
+qt_add_resources(RESOURCES assets.qrc assets_tor.qrc assets_wownerod.qrc assets_docs.qrc)
# Compile source files (.h/.cpp)
file(GLOB SOURCE_FILES
@@ -213,6 +213,10 @@ if(TOR_DIR)
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
endif()
+if(WOWNEROD_DIR)
+ target_compile_definitions(feather PRIVATE HAS_WOWNEROD_BIN=1)
+endif()
+
if(WITH_SCANNER)
target_compile_definitions(feather PRIVATE WITH_SCANNER=1)
endif()
diff --git a/src/main.cpp b/src/main.cpp
index f19ec565..c2302d0e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -71,6 +71,10 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(assets_tor);
#endif
+#if defined(HAS_WOWNEROD_BIN)
+ Q_INIT_RESOURCE(assets_wownerod);
+#endif
+
#ifdef _WIN32
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
freopen("CONOUT$", "w", stdout);
diff --git a/src/utils/DaemonManager.cpp b/src/utils/DaemonManager.cpp
index 603adf1f..ae29c025 100644
--- a/src/utils/DaemonManager.cpp
+++ b/src/utils/DaemonManager.cpp
@@ -16,16 +16,8 @@ DaemonManager::DaemonManager(QObject *parent)
: QObject(parent)
, m_process(new QProcess(this))
{
+ // The embedded wownerod is unpacked here (writable, outside any read-only app bundle).
this->daemonDir = Config::defaultConfigDir().filePath("node");
-#if defined(Q_OS_MACOS)
- // On macOS the binary ships next to the app inside the bundle.
- {
- QDir appBinaryDir(QCoreApplication::applicationDirPath());
- appBinaryDir.cd("..");
- this->daemonDir = appBinaryDir.filePath("bin");
- }
-#endif
-
this->blockchainDir = Config::defaultConfigDir().filePath("node/blockchain");
m_process->setProcessChannelMode(QProcess::MergedChannels);
@@ -157,12 +149,23 @@ bool DaemonManager::unpackBins() {
daemonBin += ".exe";
#endif
- this->daemonPath = QDir(this->daemonDir).filePath(daemonBin);
+ // 1. Explicit path the user configured (or already-running node they point us at).
+ QString configured = conf()->get(Config::wownerodPath).toString();
+ if (!configured.isEmpty() && QFileInfo(configured).isFile()) {
+ this->daemonPath = configured;
+ return true;
+ }
-#if defined(Q_OS_MACOS)
- // Shipped next to the app bundle; nothing to unpack.
- return QFileInfo(this->daemonPath).isFile();
-#endif
+ // 2. Shipped next to the wowlet executable — the packaging default (monero-GUI ships monerod
+ // the same way). Avoids bloating the app binary by embedding a ~15 MB daemon in resources.
+ QString sibling = QDir(QCoreApplication::applicationDirPath()).filePath(daemonBin);
+ if (QFileInfo(sibling).isFile()) {
+ this->daemonPath = sibling;
+ return true;
+ }
+
+ // 3. Unpack the embedded copy into the (writable) data dir — the single-file default, all platforms.
+ this->daemonPath = QDir(this->daemonDir).filePath(daemonBin);
if (m_unpacked)
return true;
diff --git a/src/utils/config.cpp b/src/utils/config.cpp
index ca2188bb..e7d5f33e 100644
--- a/src/utils/config.cpp
+++ b/src/utils/config.cpp
@@ -67,6 +67,7 @@ static const QHash configStrings = {
{Config::skin,{QS("skin"), "Wownero"}},
{Config::runLocalNode,{QS("runLocalNode"), true}}, // wowlet: run own node by default
{Config::pruneBlockchain,{QS("pruneBlockchain"), false}}, // full archival by default
+ {Config::wownerodPath,{QS("wownerodPath"), ""}}, // optional override; bundled by default
{Config::amountPrecision, {QS("amountPrecision"), 12}},
{Config::dateFormat, {QS("dateFormat"), "yyyy-MM-dd"}},
{Config::timeFormat, {QS("timeFormat"), "HH:mm"}},
diff --git a/src/utils/config.h b/src/utils/config.h
index 82ad94d4..191dbd73 100644
--- a/src/utils/config.h
+++ b/src/utils/config.h
@@ -75,6 +75,7 @@ public:
// Embedded node (wowlet)
runLocalNode,
pruneBlockchain,
+ wownerodPath,
// Network -> Proxy
proxy,