Compare commits

...

54 Commits

Author SHA1 Message Date
dsc 330edacab9 lost seed script
1 month ago
dsc ec6474a840 new restore heights
4 months ago
dsc 69e1749856 Bump version to 4.1.1
8 months ago
dsc e93488af9b Stopped support for Mac OS as I lack the resources to deal with the various problems that come up during release engineering and/or testing. Qt + wownero + Mac OS is not a happy combination.
8 months ago
dsc a8861c62ea bump minor version 4.1.0
8 months ago
dsc e6651d55ff remove linux activation lol
8 months ago
dsc 9c58913ee2 update onion URL to new server, fix forum listing
8 months ago
dsc 24ff2b7120 Merge pull request 'remove ded explorer and add muchwow.lol' (#110) from wowario/wowlet:remove into master
1 year ago
dsc 7c4f99c85d update submodule, bump version
1 year ago
wowario 7cc5fd880e
add muchwow.lol explorer
1 year ago
wowario ed9edb5ad6
remove ded explorer
1 year ago
dsc c0323ee329 Merge pull request 'bump to beta-6, 3.2.0 - fix ring error, update Dockerfile build' (#109) from ringfix into master
1 year ago
dsc 40493475ba bump to beta-6, 3.2.0 - fix ring error, update Dockerfile build
1 year ago
dsc 4098e8c0e5 Merge pull request 'wowify seed' (#105) from wownero-seed into master
2 years ago
dsc ca234008b9 wowify seed
2 years ago
dsc 2ebb41a371 Merge pull request 'Linux activation' (#104) from activate into master
2 years ago
dsc 6cf4299f78 Linux activation
2 years ago
dsc df0459da69 Merge pull request 'mining: dont exit when binding fails - we dont need to bind to any ports when we just want to mine' (#102) from mining/no-bind into master
2 years ago
dsc 46accb1077 mining: dont exit when binding fails - we dont need to bind to any ports when we just want to mine
2 years ago
dsc 7a91ba5a84 CMake: improve error message
2 years ago
dsc f83ceb2a96 Fix builds for Windows
2 years ago
dsc d332121d7c Include QtQuick (QML) inside the Linux buildbot and get rid of the alpha warning
2 years ago
dsc d151b47895 Merge pull request 'Bunch of changes' (#100) from m1 into master
2 years ago
dsc caa8731410 - Bumped to version 3.1.0
2 years ago
dsc dc3ee66e3b Merge pull request 'Fixes fiat balance, adds fiat columns to history table' (#99) from fix-historical-fiat-prices into master
2 years ago
dsc 289f9ab1d2 The balance fiat display does not 'round to ceiling' anymore, instead introduced some more decimals:
2 years ago
dsc 73747e05a7 Merge pull request 'Contact widget: New contact button' (#98) from new-contact into master
2 years ago
dsc 3051ce5118 This commit introduces a button to create a new contact because when the contact table is full of contacts, you cannot do 'right-click -> New contact'.
2 years ago
dsc fb32fa2fd2 Update README
2 years ago
dsc b3eab6085f Merge pull request 'Settings node selection: Remove double click to connect, it is bugged' (#96) from nodes-remove-double-click into master
2 years ago
dsc 835aecb79d Settings node selection: Remove double click to connect, it is bugged
2 years ago
dsc 6cba5d0487 Merge pull request 'Kryfi as default block explorer' (#95) from kryfi-block-explorer into master
2 years ago
dsc 50b78cee51 Kryfi as default block explorer
2 years ago
dsc 1b6f648a0b Merge pull request 'Solo mining' (#89) from solo-mining into master
2 years ago
dsc 6b2f8f847e Introduce the QML mining interface
2 years ago
dsc 917f8b5812 Make sure wownerod is stopped on application quit
2 years ago
dsc a62fb95fbf Fix Windows build
2 years ago
dsc c3b0d00a72 Move mining tab a few positions
2 years ago
dsc d6dfd678b8 Brings back the mining tab and adds support for solo mining.
2 years ago
dsc 8b215c1e73 Merge pull request 'hide on close' (#90) from hide-on-close into master
2 years ago
dsc 96295a52de Default disabled
2 years ago
dsc ccd0e8e64b hide on close
2 years ago
dsc 3b3ec89306 truncate the yellowpages suffix for contacts
2 years ago
dsc 373fe8e02a Merge pull request 'YellWOWpages integration' (#88) from yellowpages into master
2 years ago
dsc 14d9793193 Automatically populate the contacts widget via [YellWOWPages](yellow.wownero.com/). One may still add custom contacts - they will get saved normally like before.
2 years ago
dsc 65ceab6323 Merge pull request 'Fix building the base image' (#82) from bruh/wowlet:bruh-patch-new-boost-link into master
3 years ago
bruh 6b2118ecf6 Fix building the base image
3 years ago
dsc ca78025735 Merge pull request 'docs: adding Qt build hints for mac os - only v 5.15.1 worked' (#80) from leonardgit6/wowlet:update-docs into master
3 years ago
leonardgit6 c5eb13145f docs: fixing Qt configure command
3 years ago
leonardgit6 946443bf8c docs: adding Qt build hints for mac os
3 years ago
dsc ae33c2f1b0 Merge pull request '3.0.0 beta-4 (bulletproof+, solo-mining)' (#79) from v3 into master
3 years ago
dsc c97c0d597b beta-4 (bulletproof+, solo-mining)
3 years ago
dsc ee3713b16b Merge pull request 'Bump version to v2.1.0 and fix compile' (#75) from dsc/wowlet:bump-version3 into master
3 years ago
dsc ae39e71061 Bump version to v2.1.0 and fix compile
3 years ago

7
.gitmodules vendored

@ -1,10 +1,9 @@
[submodule "contrib/KDMacTouchBar"] [submodule "contrib/KDMacTouchBar"]
path = contrib/KDMacTouchBar path = contrib/KDMacTouchBar
url = https://github.com/KDAB/KDMacTouchBar.git url = https://github.com/KDAB/KDMacTouchBar.git
[submodule "monero"]
path = monero
url = https://git.wownero.com/wownero/wownero
branch = wowlet
[submodule "contrib/quirc"] [submodule "contrib/quirc"]
path = contrib/quirc path = contrib/quirc
url = https://github.com/dlbeer/quirc.git url = https://github.com/dlbeer/quirc.git
[submodule "wownero"]
path = wownero
url = https://git.wownero.com/wownero/wownero.git

@ -4,21 +4,18 @@ project(wowlet)
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}") message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
set(VERSION_MAJOR "2") set(VERSION_MAJOR "4")
set(VERSION_MINOR "0") set(VERSION_MINOR "1")
set(VERSION_REVISION "0") set(VERSION_REVISION "1")
set(VERSION "beta-2") set(VERSION "beta-8")
option(FETCH_DEPS "Download dependencies if they are not found" ON) option(FETCH_DEPS "Download dependencies if they are not found" ON)
option(XMRIG "Include XMRig module" ON)
option(OPENVR "Include OpenVR support") option(OPENVR "Include OpenVR support")
option(QML "Include QtQuick (QML)")
option(ANDROID "Android deployment") option(ANDROID "Android deployment")
option(ANDROID_DEBUG "View the Android app on desktop") option(ANDROID_DEBUG "View the Android app on desktop")
option(TOR_BIN "Path to Tor binary to embed inside WOWlet") option(TOR_BIN "Path to Tor binary to embed inside WOWlet")
option(STATIC "Link libraries statically, requires static Qt") option(STATIC "Link libraries statically, requires static Qt")
option(USE_DEVICE_TREZOR "Trezor support compilation") option(USE_DEVICE_TREZOR "Trezor support compilation")
option(DONATE_BEG "Prompt donation window every once in a while" ON)
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake") list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
include(CheckCCompilerFlag) include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
@ -28,14 +25,11 @@ include(FindCcache)
include(CheckIncludeFile) include(CheckIncludeFile)
include(CheckSymbolExists) include(CheckSymbolExists)
set(WOWNERO_HEAD "f611d5c9e32bc62f1735f6571b0bdb95cc020531") set(WOWNERO_HEAD "a21819cc22587e16af00e2c3d8f70156c11310a0")
set(BUILD_GUI_DEPS ON) set(BUILD_GUI_DEPS ON)
set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries") set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries")
set(INSTALL_VENDORED_LIBUNBOUND ${STATIC}) set(INSTALL_VENDORED_LIBUNBOUND ${STATIC})
set(USE_SINGLE_BUILDDIR ON) set(USE_SINGLE_BUILDDIR ON)
if(OPENVR OR ANDROID_DEBUG)
set(QML ON)
endif()
# Are we in debug mode? # Are we in debug mode?
set(_CMAKE_BUILD_TYPE "") set(_CMAKE_BUILD_TYPE "")
@ -57,7 +51,7 @@ if(STATIC)
# manually set the unbound submodule the right commit that has the fix. # manually set the unbound submodule the right commit that has the fix.
# This only works with -DMANUAL_SUBMODULES=1 # This only works with -DMANUAL_SUBMODULES=1
message(STATUS "applying unbound static build fix contrib/unbound_static.patch") message(STATUS "applying unbound static build fix contrib/unbound_static.patch")
execute_process(COMMAND bash -c "git -C ${CMAKE_SOURCE_DIR}/monero/external/unbound apply ${CMAKE_SOURCE_DIR}/contrib/unbound_static.patch") execute_process(COMMAND bash -c "git -C ${CMAKE_SOURCE_DIR}/wownero/external/unbound apply ${CMAKE_SOURCE_DIR}/contrib/unbound_static.patch")
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON) set(Boost_USE_STATIC_RUNTIME ON)
@ -90,21 +84,9 @@ function (add_linker_flag_if_supported flag var)
endfunction() endfunction()
find_package(Git) find_package(Git)
if(GIT_FOUND)
message(STATUS "Initializing submodules")
execute_process(COMMAND git "submodule" "update" "--init" "--recursive")
execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero OUTPUT_VARIABLE _WOWNERO_HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _WOWNERO_HEAD STREQUAL WOWNERO_HEAD)
message(FATAL_ERROR "[submodule] Monero HEAD was at ${_WOWNERO_HEAD} but should be at ${WOWNERO_HEAD}")
else()
message(STATUS "[submodule] Wownero HEAD @ ${WOWNERO_HEAD}")
endif()
endif()
add_subdirectory(monero) add_subdirectory(wownero)
set_property(TARGET wallet_merged PROPERTY FOLDER "monero") get_directory_property(ARCH_WIDTH DIRECTORY "wownero" DEFINITION ARCH_WIDTH)
get_directory_property(ARCH_WIDTH DIRECTORY "monero" DEFINITION ARCH_WIDTH)
get_directory_property(UNBOUND_LIBRARY DIRECTORY "monero" DEFINITION UNBOUND_LIBRARY)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
include(VersionMonero) include(VersionMonero)
@ -114,9 +96,6 @@ include_directories(${EASYLOGGING_INCLUDE})
link_directories(${EASYLOGGING_LIBRARY_DIRS}) link_directories(${EASYLOGGING_LIBRARY_DIRS})
# OpenSSL # OpenSSL
if(APPLE AND NOT OPENSSL_ROOT_DIR)
execute_process(COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}") message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}") message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
@ -129,49 +108,28 @@ message(STATUS "libsodium: libraries at ${SODIUM_LIBRARY}")
# HIDApi # HIDApi
set(HIDAPI_FOUND OFF) set(HIDAPI_FOUND OFF)
# Unbound
find_package(Unbound REQUIRED)
# QrEncode # QrEncode
find_package(QREncode REQUIRED) find_package(QREncode REQUIRED)
# Tevador 14 word Monero seed # Tevador 14 word seed (https://git.wownero.com/wowlet/wownero-seed)
find_package(monero-seed CONFIG) find_package(wownero-seed CONFIG REQUIRED)
if(NOT monero-seed_FOUND)
if(FETCH_DEPS)
FetchContent_Declare(monero-seed
GIT_REPOSITORY https://git.wownero.com/wowlet/monero-seed.git)
FetchContent_GetProperties(monero-seed)
if(NOT monero-seed_POPULATED)
message(STATUS "Fetching monero-seed")
FetchContent_Populate(monero-seed)
add_subdirectory(${monero-seed_SOURCE_DIR} ${monero-seed_BINARY_DIR})
endif()
add_library(monero-seed::monero-seed ALIAS monero-seed)
else()
message(FATAL_ERROR "monero-seed was not installed and fetching deps is disabled")
endif()
endif()
# Boost # Boost
if(DEBUG) if(DEBUG)
set(Boost_DEBUG ON) set(Boost_DEBUG ON)
endif() endif()
if(APPLE AND NOT BOOST_ROOT)
execute_process(COMMAND brew --prefix boost OUTPUT_VARIABLE BOOST_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(MINGW) if(MINGW)
set(Boost_THREADAPI win32) set(Boost_THREADAPI win32)
endif() endif()
find_package(Boost 1.58 REQUIRED COMPONENTS set(_BOOST_COMPONENTS system filesystem thread date_time chrono regex serialization program_options locale)
system
filesystem find_package(Boost 1.58 REQUIRED COMPONENTS ${_BOOST_COMPONENTS})
thread
date_time if(UNIX AND NOT ANDROID)
chrono
regex
serialization
program_options
locale)
if(UNIX AND NOT APPLE AND NOT ANDROID)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
# https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446 # https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446
set(CMAKE_SKIP_RPATH ON) set(CMAKE_SKIP_RPATH ON)
@ -204,40 +162,46 @@ if(UNIX)
endif() endif()
endif() endif()
# To build WOWlet with embedded (and static) Tor, pass CMake -DTOR_BIN=/path/to/tor
if(TOR_BIN) if(TOR_BIN)
if(APPLE) # To build WOWlet with embedded & static Tor, pass CMake -DTOR_BIN=/path/to/tor_executable
execute_process(COMMAND bash -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib") # The CMake below will copy the Tor binary into src/assets/exec
endif() #
# For release:
execute_process(COMMAND bash -c "${TOR_BIN} --version --quiet" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE out RESULT_VARIABLE ret) # ## Linux / Window
if (ret EQUAL "0") # on the buildbot(s) Tor is baked into the image
set(TOR_VERSION "${out}")
endif()
message(STATUS "${TOR_VERSION}")
configure_file("cmake/config-wowlet.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-wowlet.h")
# on the buildbot Tor is baked into the image
# - linux: See `Dockerfile` # - linux: See `Dockerfile`
# - windows: See `Dockerfile.windows` # - windows: See `Dockerfile.windows`
# - macos: taken from Tor Browser official release if(NOT EXISTS "${TOR_BIN}")
if(REPRODUCIBLE) # Always copy Tor when doing a reproducible build to prevent old versions from getting included message(FATAL_ERROR "TOR_BIN is set, but file does not exist: '${TOR_BIN}'")
set(TOR_COPY_CMD "cp ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
else()
set(TOR_COPY_CMD "cp -u ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
endif() endif()
message(STATUS "${TOR_COPY_CMD}")
# copy the Tor executable over
set(TOR_COPY_CMD "cp ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
message(STATUS "Tor cmd: ${TOR_COPY_CMD}")
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret) execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
if(ret EQUAL "1") if(ret EQUAL "1")
message(FATAL_ERROR "Tor copy failure: ${TOR_COPY_CMD}") message(FATAL_ERROR "Tor copy failure: ${TOR_COPY_CMD}")
endif() endif()
# get Tor version while we're at it
if(NOT TOR_VERSION)
execute_process(COMMAND bash -c "${TOR_BIN} --version --quiet | head -n1" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE out RESULT_VARIABLE ret)
if (ret EQUAL "0")
set(TOR_VERSION "${out}")
endif()
endif()
message(STATUS "Tor version: ${TOR_VERSION}")
configure_file("cmake/config-wowlet.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-wowlet.h")
message(STATUS "Embedding Tor binary at ${TOR_BIN}") message(STATUS "Embedding Tor binary at ${TOR_BIN}")
else() else()
message(STATUS "Skipping Tor inclusion because -DTOR_BIN=Off") message(STATUS "Skipping Tor inclusion because -DTOR_BIN=Off")
endif() endif()
if(MINGW) if(MINGW)
find_package(Iconv REQUIRED)
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}") string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
message(STATUS "MSYS location: ${msys2_install_path}") message(STATUS "MSYS location: ${msys2_install_path}")
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include") set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
@ -262,8 +226,6 @@ if(MINGW)
else() else()
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv) set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
endif() endif()
elseif(APPLE)
set(EXTRA_LIBRARIES "-framework AppKit")
elseif(OPENBSD) elseif(OPENBSD)
set(EXTRA_LIBRARIES "") set(EXTRA_LIBRARIES "")
elseif(FREEBSD) elseif(FREEBSD)
@ -280,23 +242,6 @@ endif()
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
if(APPLE)
include_directories(SYSTEM /usr/include/malloc)
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
endif()
if (APPLE AND NOT IOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
endif()
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
endif()
# warnings # warnings
# @TODO: enable these 2 for migration to Qt 6 # @TODO: enable these 2 for migration to Qt 6
#add_c_flag_if_supported(-Werror C_SECURITY_FLAGS) #add_c_flag_if_supported(-Werror C_SECURITY_FLAGS)
@ -331,12 +276,7 @@ if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VER
endif() endif()
# linker # linker
if (APPLE) if (NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
add_linker_flag_if_supported(-Wl,-bind_at_load LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-dead_strip LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-dead_strip_dylibs LD_SECURITY_FLAGS)
endif()
if (NOT APPLE AND NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
# Windows binaries die on startup with PIE when compiled with GCC # Windows binaries die on startup with PIE when compiled with GCC
add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS) add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS)
endif() endif()
@ -366,6 +306,11 @@ if(STATIC)
endif() endif()
endif() endif()
if(LINUX_ACTIVATION)
find_package(Cairo REQUIRED)
find_package(Xfixes REQUIRED)
endif()
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that # With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
# is fixed in the code (Issue #847), force compiler to be conservative. # is fixed in the code (Issue #847), force compiler to be conservative.
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS) add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
@ -382,10 +327,6 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
if(APPLE)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/KDMacTouchBar")
endif()
if(OPENVR) if(OPENVR)
# Add contrib/openvr as library # Add contrib/openvr as library
add_definitions(-DVR_API_PUBLIC) add_definitions(-DVR_API_PUBLIC)

@ -17,7 +17,6 @@ RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
cd openssl-1.1.1i && \ cd openssl-1.1.1i && \
./config no-shared no-dso --prefix=/usr/local/openssl && \ ./config no-shared no-dso --prefix=/usr/local/openssl && \
make -j$THREADS && \ make -j$THREADS && \
make test && \
make -j$THREADS install_sw && \ make -j$THREADS install_sw && \
rm -rf $(pwd) rm -rf $(pwd)
@ -66,10 +65,10 @@ RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git &&
rm -rf $(pwd) && \ rm -rf $(pwd) && \
strip -s -D /usr/local/tor/bin/tor strip -s -D /usr/local/tor/bin/tor
FROM ubuntu:16.04 FROM ubuntu:18.04
ARG THREADS=1 ARG THREADS=1
ARG QT_VERSION=5.15.2 ARG QT_VERSION=v5.15.2
ENV CFLAGS="-fPIC" ENV CFLAGS="-fPIC"
ENV CPPFLAGS="-fPIC" ENV CPPFLAGS="-fPIC"
@ -95,7 +94,7 @@ RUN apt-get update && \
# libusb # libusb
libudev-dev \ libudev-dev \
# fontconfig # fontconfig
autopoint gettext gperf libpng12-dev \ autopoint gettext gperf libpng-dev \
# libxcb # libxcb
libpthread-stubs0-dev \ libpthread-stubs0-dev \
# xorgproto # xorgproto
@ -161,7 +160,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-util && \ cd libxcb-util && \
git reset --hard acf790d7752f36e450d476ad79807d4012ec863b && \ git reset --hard acf790d7752f36e450d476ad79807d4012ec863b && \
git submodule init && \ git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \ git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \ git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \ ./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \ make -j$THREADS && \
@ -172,7 +171,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-image && \ cd libxcb-image && \
git reset --hard d882052fb2ce439c6483fce944ba8f16f7294639 && \ git reset --hard d882052fb2ce439c6483fce944ba8f16f7294639 && \
git submodule init && \ git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \ git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \ git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \ ./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \ make -j$THREADS && \
@ -183,7 +182,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-keysyms && \ cd libxcb-keysyms && \
git reset --hard 0e51ee5570a6a80bdf98770b975dfe8a57f4eeb1 && \ git reset --hard 0e51ee5570a6a80bdf98770b975dfe8a57f4eeb1 && \
git submodule init && \ git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \ git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \ git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \ ./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \ make -j$THREADS && \
@ -194,7 +193,7 @@ RUN git clone -b 0.3.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-render-util && \ cd libxcb-render-util && \
git reset --hard 0317caf63de532fd7a0493ed6afa871a67253747 && \ git reset --hard 0317caf63de532fd7a0493ed6afa871a67253747 && \
git submodule init && \ git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \ git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \ git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \ ./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \ make -j$THREADS && \
@ -205,7 +204,7 @@ RUN git clone -b 0.4.1 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-wm && \ cd libxcb-wm && \
git reset --hard 24eb17df2e1245885e72c9d4bbb0a0f69f0700f2 && \ git reset --hard 24eb17df2e1245885e72c9d4bbb0a0f69f0700f2 && \
git submodule init && \ git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \ git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \ git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \ ./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \ make -j$THREADS && \
@ -262,7 +261,7 @@ RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \
make -j$THREADS install && \ make -j$THREADS install && \
rm -rf $(pwd) rm -rf $(pwd)
RUN wget https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.gz && \ RUN wget https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.tar.gz && \
echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" | sha256sum -c && \ echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" | sha256sum -c && \
tar -xzf boost_1_73_0.tar.gz && \ tar -xzf boost_1_73_0.tar.gz && \
rm boost_1_73_0.tar.gz && \ rm boost_1_73_0.tar.gz && \
@ -278,22 +277,46 @@ RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
cd openssl-1.1.1i && \ cd openssl-1.1.1i && \
./config no-shared no-dso --prefix=/usr/local/openssl && \ ./config no-shared no-dso --prefix=/usr/local/openssl && \
make -j$THREADS && \ make -j$THREADS && \
make test && \
make -j$THREADS install_sw && \ make -j$THREADS install_sw && \
rm -rf $(pwd) rm -rf $(pwd)
RUN wget https://github.com/libexpat/libexpat/releases/download/R_2_4_8/expat-2.4.8.tar.bz2 && \
echo "a247a7f6bbb21cf2ca81ea4cbb916bfb9717ca523631675f99b3d4a5678dcd16 expat-2.4.8.tar.bz2" | sha256sum -c && \
tar -xf expat-2.4.8.tar.bz2 && \
rm expat-2.4.8.tar.bz2 && \
cd expat-2.4.8 && \
./configure --enable-static --disable-shared --prefix=/usr/local/expat/ && \
make -j$THREADS && \
make -j$THREADS install && \
rm -rf $(pwd)
RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.16.2.tar.gz && \
echo "2e32f283820c24c51ca1dd8afecfdb747c7385a137abe865c99db4b257403581 unbound-1.16.2.tar.gz" | sha256sum -c && \
tar -xzf unbound-1.16.2.tar.gz && \
rm unbound-1.16.2.tar.gz && \
cd unbound-1.16.2 && \
./configure --disable-shared --enable-static --without-pyunbound --with-libexpat=/usr/local/expat/ --with-ssl=/usr/local/openssl/ --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only --with-pic && \
make -j$THREADS && \
make -j$THREADS install && \
rm -rf $(pwd)
RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \ RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \
rm /usr/lib/x86_64-linux-gnu/libXext.a && \ rm /usr/lib/x86_64-linux-gnu/libXext.a && \
rm /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \ rm /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \
git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
cd qt5 && \ cd qt5 && \
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtdeclarative.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtgraphicaleffects.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols2.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttools.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qttools.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtx11extras.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qtx11extras.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtxmlpatterns.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtwebsockets.git -b ${QT_VERSION} --depth 1 && \ git clone git://code.qt.io/qt/qtwebsockets.git -b ${QT_VERSION} --depth 1 && \
sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \ sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \
sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \ sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \
@ -301,18 +324,14 @@ RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \
sed -i s/\\/usr\\/X11R6\\/lib64/\\/usr\\/local\\/lib/ qtbase/mkspecs/linux-g++-64/qmake.conf && \ sed -i s/\\/usr\\/X11R6\\/lib64/\\/usr\\/local\\/lib/ qtbase/mkspecs/linux-g++-64/qmake.conf && \
OPENSSL_LIBS="-lssl -lcrypto -lpthread -ldl" \ OPENSSL_LIBS="-lssl -lcrypto -lpthread -ldl" \
./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \ ./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \
-no-opengl -qpa xcb --xcb -xcb-xlib -feature-xlib -openssl-linked -I /usr/local/openssl/include \ -opengl desktop -qpa xcb -xcb -xcb-xlib -feature-xlib -system-freetype -fontconfig -glib \
-L /usr/local/openssl/lib -system-freetype -fontconfig -glib \ -no-dbus -no-feature-qml-worker-script -no-linuxfb -no-openssl -no-sql-sqlite -no-kms -no-use-gold-linker \
-no-dbus -no-sql-sqlite -no-use-gold-linker -no-kms \
-qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \ -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
-skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \ -skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \
-skip qtdoc -skip qtquickcontrols -skip qtquickcontrols2 -skip qtspeech -skip qtgamepad \ -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing \
-skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing -optimize-size \
-skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools \ -skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools \
-skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebview \ -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebview \
-skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \ -skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
-skip qtdeclarative \
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
-nomake examples -nomake tests -nomake tools && \ -nomake examples -nomake tests -nomake tools && \
make -j$THREADS && \ make -j$THREADS && \
make -j$THREADS install && \ make -j$THREADS install && \
@ -392,9 +411,9 @@ RUN git clone -b v4.0.2 --depth 1 https://github.com/fukuchi/libqrencode.git &&
make -j$THREADS install && \ make -j$THREADS install && \
rm -rf $(pwd) rm -rf $(pwd)
RUN git clone https://git.wownero.com/wowlet/monero-seed.git && \ RUN git clone https://git.wownero.com/wowlet/wownero-seed.git && \
cd monero-seed && \ cd wownero-seed && \
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \ git reset --hard ef6910b6bb3b61757c36e2e5db0927d75f1731c8 && \
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \ cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
make -Cbuild -j$THREADS && \ make -Cbuild -j$THREADS && \
make -Cbuild install && \ make -Cbuild install && \
@ -414,3 +433,7 @@ RUN mkdir linuxdeployqt && \
chmod +x linuxdeployqt-7-x86_64.AppImage && \ chmod +x linuxdeployqt-7-x86_64.AppImage && \
./linuxdeployqt-7-x86_64.AppImage --appimage-extract && \ ./linuxdeployqt-7-x86_64.AppImage --appimage-extract && \
rm linuxdeployqt-7-x86_64.AppImage rm linuxdeployqt-7-x86_64.AppImage
RUN apt-get update && \
apt-get -o Dpkg::Options::="--force-confold" install -q -y --force-yes libcairo2-dev libxinerama-dev
RUN git config --global --add safe.directory /wowlet

@ -237,7 +237,6 @@ CMD set -ex \
-DUSE_SINGLE_BUILDDIR=ON \ -DUSE_SINGLE_BUILDDIR=ON \
-DMANUAL_SUBMODULES=1 \ -DMANUAL_SUBMODULES=1 \
-DUSE_SINGLE_BUILDDIR=ON \ -DUSE_SINGLE_BUILDDIR=ON \
-DQML=ON \
-DANDROID=ON \ -DANDROID=ON \
../../.. \ ../../.. \
&& PATH=${HOST_PATH} make generate_translations_header \ && PATH=${HOST_PATH} make generate_translations_header \

@ -1,11 +1,12 @@
FROM ubuntu:20.04 FROM ubuntu:20.04
ARG THREADS=1 ARG THREADS=1
ARG QT_VERSION=5.15.2 ARG QT_VERSION=v5.15.2
ENV SOURCE_DATE_EPOCH=1397818193 ENV SOURCE_DATE_EPOCH=1397818193
ENV OPENSSL_ROOT_DIR=/usr/local/openssl/ ENV OPENSSL_ROOT_DIR=/usr/local/openssl/
ENV TOR_BIN=/usr/local/tor/bin/tor.exe ENV TOR_BIN=/usr/local/tor/bin/tor.exe
ENV TOR_VERSION='tor-0.4.5.7'
RUN apt update && \ RUN apt update && \
DEBIAN_FRONTEND=noninteractive apt install -y curl nano wget zip automake build-essential cmake gcc-mingw-w64 g++-mingw-w64 gettext git libtool pkg-config \ DEBIAN_FRONTEND=noninteractive apt install -y curl nano wget zip automake build-essential cmake gcc-mingw-w64 g++-mingw-w64 gettext git libtool pkg-config \
@ -15,9 +16,9 @@ RUN apt update && \
RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \ RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \
update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix) update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix)
RUN git clone -b v0.17.1.9 --depth 1 https://github.com/monero-project/monero && \ RUN git clone -b v0.18.2.0 --depth 1 https://github.com/monero-project/monero && \
cd monero && \ cd monero && \
git reset --hard 8fef32e45c80aec41f25be9d1d8fb75adc883c64 && \ git reset --hard 99be9a044f3854f339548e2d99c539c18d7b1b01 && \
cp -a contrib/depends / && \ cp -a contrib/depends / && \
cd .. && \ cd .. && \
rm -rf monero rm -rf monero
@ -95,7 +96,7 @@ RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
rm -rf $(pwd) rm -rf $(pwd)
# libpng -> libqrencode # libpng -> libqrencode
RUN git clone -b libpng16 --depth 1 https://github.com/glennrp/libpng.git && \ RUN git clone -b libpng16 https://github.com/glennrp/libpng.git && \
cd libpng && \ cd libpng && \
git reset --hard a37d4836519517bdce6cb9d956092321eca3e73b && \ git reset --hard a37d4836519517bdce6cb9d956092321eca3e73b && \
CPPFLAGS="-I/depends/x86_64-w64-mingw32/include" LDFLAGS="-L/depends/x86_64-w64-mingw32/lib" \ CPPFLAGS="-I/depends/x86_64-w64-mingw32/include" LDFLAGS="-L/depends/x86_64-w64-mingw32/lib" \
@ -174,9 +175,9 @@ RUN git clone -b tor-0.4.5.7 --depth 1 https://git.torproject.org/tor.git && \
rm -rf $(pwd) && \ rm -rf $(pwd) && \
strip -s -D /usr/local/tor/bin/tor.exe strip -s -D /usr/local/tor/bin/tor.exe
RUN git clone https://git.wownero.com/wowlet/monero-seed.git && \ RUN git clone https://git.wownero.com/wowlet/wownero-seed.git && \
cd monero-seed && \ cd wownero-seed && \
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \ git reset --hard ef6910b6bb3b61757c36e2e5db0927d75f1731c8 && \
cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \ cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=/depends/x86_64-w64-mingw32/share/toolchain.cmake -Bbuild && \ -DCMAKE_TOOLCHAIN_FILE=/depends/x86_64-w64-mingw32/share/toolchain.cmake -Bbuild && \
@ -184,3 +185,5 @@ RUN git clone https://git.wownero.com/wowlet/monero-seed.git && \
make -Cbuild install && \ make -Cbuild install && \
rm -rf $(pwd) rm -rf $(pwd)
RUN git config --global --add safe.directory /wowlet
RUN git config --global --add safe.directory /wowlet/wownero

@ -1,13 +0,0 @@
# this image is used internally for the buildbot
FROM ubuntu:20.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt clean && apt update
RUN apt install -y git build-essential wget curl ngrep unzip file ssh zip
RUN cat /dev/zero | ssh-keygen -q -N ""
RUN cat ~/.ssh/id_rsa.pub
RUN printf "Host *\n StrictHostKeyChecking no" > ~/.ssh/config

@ -27,12 +27,9 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
CMAKEFLAGS = \ CMAKEFLAGS = \
-DARCH=x86_64 \
-DBUILD_64=On \ -DBUILD_64=On \
-DBUILD_TESTS=Off \ -DBUILD_TESTS=Off \
-DOPENVR=Off \ -DOPENVR=Off \
-DQML=Off \
-DXMRIG=Off \
-DTOR_BIN=Off \ -DTOR_BIN=Off \
-DCMAKE_CXX_STANDARD=11 \ -DCMAKE_CXX_STANDARD=11 \
-DCMAKE_VERBOSE_MAKEFILE=On \ -DCMAKE_VERBOSE_MAKEFILE=On \
@ -43,7 +40,8 @@ CMAKEFLAGS = \
$(CMAKEFLAGS_EXTRA) $(CMAKEFLAGS_EXTRA)
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64" release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
release-static: CMAKEFLAGS += -DXMRIG=ON release-static: CMAKEFLAGS += -DXMRIG=OFF
release-static: CMAKEFLAGS += -DARCH=x86-64
release-static: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF) release-static: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF)
@ -57,7 +55,7 @@ depends:
windows: windows:
mkdir -p build/$(target)/release mkdir -p build/$(target)/release
cd build/$(target)/release && cmake -D STATIC=ON -DZLIB_ROOT=/usr/x86_64-w64-mingw32/ -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DOPENVR=ON -DQML=ON -DWITH_SCANNER=ON -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Debug -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE) cd build/$(target)/release && cmake -D STATIC=ON -DZLIB_ROOT=/usr/x86_64-w64-mingw32/ -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DOPENVR=ON -DWITH_SCANNER=ON -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64" windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
@ -75,12 +73,3 @@ windows-mxe-debug: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Debug
windows-mxe-debug: windows-mxe-debug:
cmake -Bbuild $(CMAKEFLAGS) cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild $(MAKE) -Cbuild
mac-release: CMAKEFLAGS += -DSTATIC=Off
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
mac-release:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
$(MAKE) -Cbuild deploy

@ -1,12 +1,14 @@
[![Build Status](https://ci.wownero.com/api/badges/wowlet/wowlet/status.svg)](https://ci.wownero.com/wowlet/wowlet)
# WOWlet- a free Wownero desktop wallet # WOWlet- a free Wownero desktop wallet
WOWlet is a free, open-source Wownero client for Linux, Mac OS, and Windows.
WOWlet is a free, open-source Wownero client for Linux with ports for Mac OS and Windows. ![https://i.imgur.com/l7fUf0f.png](https://i.imgur.com/l7fUf0f.png)
## Development resources ## Development resources
* Git: [git.wownero.com/wowlet/wowlet](https://git.wownero.com/wowlet/wowlet) * Git: [git.wownero.com/wowlet/wowlet](https://git.wownero.com/wowlet/wowlet)
* IRC: `#wownero` on Freenode * IRC: `#wownero-dev` on [OFTC](https://oftc.net/)
* [Building WOWlet from source](https://git.wownero.com/wowlet/wowlet/src/branch/master/docs/BUILDING.md)
* [Working on WOWlet](https://git.wownero.com/wowlet/wowlet/src/branch/master/docs/HACKING.md)
Copyright (c) 2020-2021 The Monero Project. Copyright (c) 2020-2021 The Monero Project.

@ -2,27 +2,4 @@ if(APPLE OR (WIN32 AND NOT STATIC))
add_custom_target(deploy) add_custom_target(deploy)
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION) get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
endif()
if(APPLE AND NOT IOS)
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}")
add_custom_command(TARGET deploy
POST_BUILD
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "$<TARGET_FILE_DIR:wowlet>/../.." -always-overwrite
COMMENT "Running macdeployqt..."
)
# workaround for a Qt bug that requires manually adding libqsvg.dylib to bundle
find_file(_qt_svg_dylib "libqsvg.dylib" PATHS "${CMAKE_PREFIX_PATH}/plugins/imageformats" NO_DEFAULT_PATH)
if(_qt_svg_dylib)
add_custom_command(TARGET deploy
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${_qt_svg_dylib} $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtGui.framework/Versions/5/QtGui" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtWidgets.framework/Versions/5/QtWidgets" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtSvg.framework/Versions/5/QtSvg" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtCore.framework/Versions/5/QtCore" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMENT "Copying libqsvg.dylib, running install_name_tool"
)
endif()
endif()
endif()

@ -0,0 +1,81 @@
# - Try to find Cairo
# Once done, this will define
#
# CAIRO_FOUND - system has Cairo
# CAIRO_INCLUDE_DIRS - the Cairo include directories
# CAIRO_LIBRARIES - link these to use Cairo
#
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS 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.
FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(PC_CAIRO cairo) # FIXME: After we require CMake 2.8.2 we can pass QUIET to this call.
FIND_PATH(CAIRO_INCLUDE_DIRS
NAMES cairo.h
HINTS ${PC_CAIRO_INCLUDEDIR}
${PC_CAIRO_INCLUDE_DIRS}
PATH_SUFFIXES cairo
)
FIND_LIBRARY(CAIRO_LIBRARIES
NAMES cairo
HINTS ${PC_CAIRO_LIBDIR}
${PC_CAIRO_LIBRARY_DIRS}
)
IF (CAIRO_INCLUDE_DIRS)
IF (EXISTS "${CAIRO_INCLUDE_DIRS}/cairo-version.h")
FILE(READ "${CAIRO_INCLUDE_DIRS}/cairo-version.h" CAIRO_VERSION_CONTENT)
STRING(REGEX MATCH "#define +CAIRO_VERSION_MAJOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
SET(CAIRO_VERSION_MAJOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "#define +CAIRO_VERSION_MINOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
SET(CAIRO_VERSION_MINOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "#define +CAIRO_VERSION_MICRO +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
SET(CAIRO_VERSION_MICRO "${CMAKE_MATCH_1}")
SET(CAIRO_VERSION "${CAIRO_VERSION_MAJOR}.${CAIRO_VERSION_MINOR}.${CAIRO_VERSION_MICRO}")
ENDIF ()
ENDIF ()
# FIXME: Should not be needed anymore once we start depending on CMake 2.8.3
SET(VERSION_OK TRUE)
IF (Cairo_FIND_VERSION)
IF (Cairo_FIND_VERSION_EXACT)
IF ("${Cairo_FIND_VERSION}" VERSION_EQUAL "${CAIRO_VERSION}")
# FIXME: Use IF (NOT ...) with CMake 2.8.2+ to get rid of the ELSE block
ELSE ()
SET(VERSION_OK FALSE)
ENDIF ()
ELSE ()
IF ("${Cairo_FIND_VERSION}" VERSION_GREATER "${CAIRO_VERSION}")
SET(VERSION_OK FALSE)
ENDIF ()
ENDIF ()
ENDIF ()
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cairo DEFAULT_MSG CAIRO_INCLUDE_DIRS CAIRO_LIBRARIES VERSION_OK)

@ -0,0 +1,26 @@
# - Find XFixes
# Find the XFixes libraries
#
# This module defines the following variables:
# XFIXES_FOUND - 1 if XFIXES_INCLUDE_DIR & XFIXES_LIBRARY are found, 0 otherwise
# XFIXES_INCLUDE_DIR - where to find Xlib.h, etc.
# XFIXES_LIBRARY - the X11 library
#
find_path( XFIXES_INCLUDE_DIR
NAMES X11/extensions/Xfixes.h
PATH_SUFFIXES X11/extensions
DOC "The XFixes include directory" )
find_library( XFIXES_LIBRARY
NAMES Xfixes
PATHS /usr/lib /lib
DOC "The XFixes library" )
if( XFIXES_INCLUDE_DIR AND XFIXES_LIBRARY )
set( XFIXES_FOUND 1 )
else()
set( XFIXES_FOUND 0 )
endif()
mark_as_advanced( XFIXES_INCLUDE_DIR XFIXES_LIBRARY )

@ -35,7 +35,7 @@ if(RET)
message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.") message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.")
set(VERSIONTAG "unknown") set(VERSIONTAG "unknown")
set(VERSION_IS_RELEASE "false") set(VERSION_IS_RELEASE "false")
configure_file("monero/src/version.cpp.in" "${TO}") configure_file("wownero/src/version.cpp.in" "${TO}")
else() else()
string(SUBSTRING ${COMMIT} 0 9 COMMIT) string(SUBSTRING ${COMMIT} 0 9 COMMIT)
message(STATUS "You are currently on commit ${COMMIT}") message(STATUS "You are currently on commit ${COMMIT}")
@ -61,5 +61,5 @@ else()
set(VERSION_IS_RELEASE "false") set(VERSION_IS_RELEASE "false")
endif() endif()
endif() endif()
configure_file("monero/src/version.cpp.in" "${TO}") configure_file("wownero/src/version.cpp.in" "${TO}")
endif() endif()

@ -4,7 +4,7 @@
find_package(Git QUIET) find_package(Git QUIET)
# Check what commit we're on # Check what commit we're on
execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --short=9 HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --short=9 HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/wownero)
if(RET) if(RET)
# Something went wrong, set the version tag to -unknown # Something went wrong, set the version tag to -unknown
@ -37,7 +37,7 @@ endif()
# Check latest tagged release # Check latest tagged release
execute_process(COMMAND "${GIT_EXECUTABLE}" describe --abbrev=0 RESULT_VARIABLE RET OUTPUT_VARIABLE TAG OUTPUT_STRIP_TRAILING_WHITESPACE execute_process(COMMAND "${GIT_EXECUTABLE}" describe --abbrev=0 RESULT_VARIABLE RET OUTPUT_VARIABLE TAG OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/wownero)
if(RET) if(RET)
message(WARNING "Cannot determine most recent tag. Make sure that you are building either from a Git working tree or from a source archive.") message(WARNING "Cannot determine most recent tag. Make sure that you are building either from a Git working tree or from a source archive.")

@ -1,33 +0,0 @@
#!/bin/bash
set -e
unset SOURCE_DATE_EPOCH
APPDIR="$PWD/wowlet.AppDir"
mkdir -p "$APPDIR"
mkdir -p "$APPDIR/usr/share/applications/"
mkdir -p "$APPDIR/usr/bin"
cp "$PWD/src/assets/org.wowlet.wowlet.desktop" "$APPDIR/usr/share/applications/org.wowlet.wowlet.desktop"
cp "$PWD/src/assets/images/appicons/64x64.png" "$APPDIR/wowlet.png"
cp "$PWD/build/bin/wowlet" "$APPDIR/usr/bin/wowlet"
LD_LIBRARY_PATH=/usr/local/lib /linuxdeployqt/squashfs-root/AppRun wowlet.AppDir/usr/share/applications/org.wowlet.wowlet.desktop -bundle-non-qt-libs
find wowlet.AppDir/ -exec touch -h -a -m -t 202101010100.00 {} \;
# Manually create AppImage (reproducibly)
# download runtime
wget -nc https://github.com/AppImage/AppImageKit/releases/download/12/runtime-x86_64
echo "24da8e0e149b7211cbfb00a545189a1101cb18d1f27d4cfc1895837d2c30bc30 runtime-x86_64" | sha256sum -c
mksquashfs wowlet.AppDir wowlet.squashfs -info -root-owned -no-xattrs -noappend -fstime 0
# mksquashfs writes a timestamp to the header
printf '\x00\x00\x00\x00' | dd conv=notrunc of=wowlet.squashfs bs=1 seek=$((0x8))
rm -f wowlet.AppImage
cat runtime-x86_64 >> wowlet.AppImage
cat wowlet.squashfs >> wowlet.AppImage
chmod a+x wowlet.AppImage

@ -43,4 +43,4 @@ cp "$PWD/src/assets/images/appicons/256x256.png" "$DEBDIR/usr/share/icons/hicolo
# Build deb package # Build deb package
dpkg-deb --build $DEBDIR dpkg-deb --build $DEBDIR
mv wowlet.DebDir.deb wowlet_2.0_amd64.deb mv wowlet.DebDir.deb wowlet_2.1_amd64.deb

Binary file not shown.

@ -1,5 +1,5 @@
Package: wowlet Package: wowlet
Version: 2.0 Version: 2.1
Section: net Section: net
Priority: optional Priority: optional
Architecture: amd64 Architecture: amd64

@ -1,108 +1,71 @@
## Buildbot builds # Building WOWlet
The docker build bins can be found here: https://build.wownero.org/files/ Building for Linux and Windows via Docker is done in 3 steps:
## Docker static builds
Static builds via Docker are done in 3 steps:
1. Cloning this repository (+submodules) 1. Cloning this repository (+submodules)
2. Creating a base Docker image 2. Creating a base Docker image
3. Using the base image to compile a build 3. Using the base image to compile a build
### Linux (reproducible) **important:** you only have to do step 2 (base docker image) once.
For Mac OS, scroll down.
# Linux
The docker image for reproducible Linux static builds uses Ubuntu 16.04 and compiles the required libraries statically For more information, check the Dockerfile: `Dockerfile`.
so that the resulting `wowlet` binary is static. For more information, check the Dockerfile: `Dockerfile`.
#### 1. Clone ### 1. Clone
```bash ```bash
git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git
cd wowlet cd wowlet
``` ```
Replace `master` with the desired version tag (e.g. `beta-4`) to build the release binary. Replace `master` with the desired version tag (e.g. `v3.1.0`) to build the release binary.
#### 2. Base image ### 2. Base image
```bash ```bash
docker build --tag wowlet:linux --build-arg THREADS=4 . docker build --tag wowlet:linux --build-arg THREADS=6 .
``` ```
Building the base image takes a while. You only need to build the base image once. Building the base image takes a while. **You only need to build the base image once.**
#### 3. Build ### 3. Build
##### Standalone binary
```bash ```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:linux sh -c 'make release-static -j4' docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:linux sh -c 'make release-static -j6'
``` ```
If you're re-running a build make sure to `rm -rf build/` first. If you're re-running a build make sure to `rm -rf build/` first.
The resulting binary can be found in `build/bin/wowlet`. The resulting binary can be found in `build/bin/wowlet`.
##### AppImage # Windows
First create the standalone binary using the Docker command in the previous step. ### 1. Clone
```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:linux contrib/build-appimage.sh
```
### Windows (reproducible)
#### 1. Clone
```bash ```bash
git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git
cd wowlet cd wowlet
``` ```
Replace `master` with the desired version tag (e.g. `beta-4`) to build the release binary. Replace `master` with the desired version tag (e.g. `v3.1.0`) to build the release binary.
#### 2. Base image
### 2. Base image
```bash ```bash
docker build -f Dockerfile.windows --tag wowlet:win --build-arg THREADS=4 . docker build -f Dockerfile.windows --tag wowlet:win --build-arg THREADS=6 .
``` ```
Building the base image takes a while. You only need to build the base image once. Building the base image takes a while. **You only need to build the base image once.**
#### 3. Build ### 3. Build
```bash ```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:win sh -c 'make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j4' docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:win sh -c 'make windows root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j6'
``` ```
If you're re-running a build make sure to `rm -rf build/` first. If you're re-running a build make sure to `rm -rf build/` first.
The resulting binary can be found in `build/x86_64-w64-mingw32/release/bin/wowlet.exe`. The resulting binary can be found in `build/x86_64-w64-mingw32/release/bin/wowlet.exe`.
## macOS
For MacOS it's easiest to leverage [brew](https://brew.sh) to install the required dependencies.
```bash
HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" \
brew install boost zmq openssl libpgm miniupnpc libsodium expat libunwind-headers protobuf libgcrypt qrencode ccache cmake pkgconfig git
```
Clone the repository.
```bash
git clone --recursive https://git.wownero.com/wowlet/wowlet.git
```
Get the latest LTS from here: https://www.qt.io/offline-installers and install.
Build WOWlet.
```bash
CMAKE_PREFIX_PATH=~/Qt5.15.1/5.15.1/clang_64 make mac-release
```
The resulting Mac OS application can be found `build/bin/wowlet.app` and will **not** have Tor embedded.

@ -22,6 +22,8 @@ by running this command: `pandoc wowlet.1.md -s -t man -o wowlet.1 && gzip wowle
## Requirements ## Requirements
(Possibly out-of-date)
### Ubuntu/Debian ### Ubuntu/Debian
```bash ```bash
@ -31,38 +33,22 @@ libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev
libprotobuf-dev protobuf-compiler libgcrypt20-dev libpng-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev libpng-dev
``` ```
## Mac OS
```bash
brew install boost zmq openssl libpgm miniupnpc libsodium expat libunwind-headers \
protobuf libgcrypt qrencode ccache cmake pkgconfig git
```
## CMake ## CMake
After installing Qt you might have a folder called `/home/$user/Qt/`. You need to pass this to CMake After installing Qt you might have a folder called `/home/$USER/Qt/`. You need to pass this to CMake
via the `CMAKE_PREFIX_PATH` definition. via the `CMAKE_PREFIX_PATH` definition.
``` ```
-DCMAKE_PREFIX_PATH=/home/$user/QtNew/5.15.0/gcc_64 -DCMAKE_PREFIX_PATH=/home/$USER/QtFooBar/5.15.0/gcc_64
``` ```
There are some Wownero/WOWlet related options/definitions that you may pass: There are some Wownero/WOWlet related options/definitions that you may pass, see also `CMakeLists.txt`.
- `-DXMRIG=OFF` - disable XMRig feature
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside WOWlet
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
And: At a bare minimum, recommended:
``` `-DMANUAL_SUBMODULES=1 -DUSE_DEVICE_TREZOR=OFF -DUSE_SINGLE_BUILDDIR=ON -DDEV_MODE=ON`
-DMANUAL_SUBMODULES=1
-DUSE_DEVICE_TREZOR=OFF
-DUSE_SINGLE_BUILDDIR=ON
-DDEV_MODE=ON
```
If you have OpenSSL installed in a custom location, try: If you have OpenSSL installed at a custom location, try:
``` ```
-DOPENSSL_INCLUDE_DIR=/usr/local/lib/openssl-1.1.1g/include -DOPENSSL_INCLUDE_DIR=/usr/local/lib/openssl-1.1.1g/include
@ -85,7 +71,7 @@ Enable debugging symbols:
## Wowlet ## Wowlet
It's best to install Tor locally as a service and start `wowlet` with `--use-local-tor`, this It's best to install Tor locally as a service and start `wowlet` with `--use-local-tor`, this
prevents the child process from starting up and saves time. prevents the child process from starting up each time you launch WOWlet and thus saves time.
#### Ubuntu/Debian #### Ubuntu/Debian
@ -94,13 +80,6 @@ apt install -y tor
sudo service tor start sudo service tor start
``` ```
#### Mac OS
```bash
brew install tor
brew services start tor
```
To skip the wizards and open a wallet directly use `--wallet-file`: To skip the wizards and open a wallet directly use `--wallet-file`:
```bash ```bash

@ -1 +0,0 @@
Subproject commit f611d5c9e32bc62f1735f6571b0bdb95cc020531

@ -2,14 +2,9 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
# pthread
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
if(QML) find_package(PNG REQUIRED)
# PNG
find_package(ZLIB REQUIRED)
find_package(PNG REQUIRED)
endif()
# Compile these source files (.h/.cpp) # Compile these source files (.h/.cpp)
file(GLOB SOURCE_FILES file(GLOB SOURCE_FILES
@ -39,11 +34,7 @@ file(GLOB SOURCE_FILES
"dialog/*.cpp" "dialog/*.cpp"
) )
if(QML) find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick QuickWidgets Qml QuickControls2 QuickCompiler QmlImportScanner Multimedia)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick Qml QuickControls2 QmlImportScanner Multimedia)
else()
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Multimedia)
endif()
if(OPENVR) if(OPENVR)
# include some extra files # include some extra files
@ -71,7 +62,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-declarations
add_subdirectory(libwalletqt) add_subdirectory(libwalletqt)
add_subdirectory(model) add_subdirectory(model)
add_subdirectory(utils) add_subdirectory(utils)
add_subdirectory(openpgp)
if(WITH_SCANNER) if(WITH_SCANNER)
add_subdirectory(QR-Code-scanner) add_subdirectory(QR-Code-scanner)
@ -80,11 +70,7 @@ endif()
qt5_add_resources(RESOURCES assets.qrc) qt5_add_resources(RESOURCES assets.qrc)
if(TOR_BIN) if(TOR_BIN)
if(APPLE) set(ASSETS_TOR "assets_tor.qrc")
set(ASSETS_TOR "assets_tor_macos.qrc")
else()
set(ASSETS_TOR "assets_tor.qrc")
endif()
endif() endif()
set(EXECUTABLE_FLAG) set(EXECUTABLE_FLAG)
@ -136,10 +122,10 @@ file(GLOB_RECURSE SRC_HEADERS *.h)
target_include_directories(wowlet PUBLIC target_include_directories(wowlet PUBLIC
${CMAKE_BINARY_DIR}/src/wowlet_autogen/include ${CMAKE_BINARY_DIR}/src/wowlet_autogen/include
${CMAKE_SOURCE_DIR}/monero/include ${CMAKE_SOURCE_DIR}/wownero/include
${CMAKE_SOURCE_DIR}/monero/src ${CMAKE_SOURCE_DIR}/wownero/src
${CMAKE_SOURCE_DIR}/monero/external/easylogging++ ${CMAKE_SOURCE_DIR}/wownero/external/easylogging++
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include ${CMAKE_SOURCE_DIR}/wownero/contrib/epee/include
${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/daemon ${CMAKE_CURRENT_SOURCE_DIR}/daemon
@ -149,7 +135,9 @@ target_include_directories(wowlet PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/tor ${CMAKE_CURRENT_SOURCE_DIR}/tor
${CMAKE_CURRENT_SOURCE_DIR}/qrcode ${CMAKE_CURRENT_SOURCE_DIR}/qrcode
${X11_INCLUDE_DIR} ${X11_INCLUDE_DIR}
${QRENCODE_INCLUDE_DIR}
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${Iconv_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}
${Qt5Core_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS}
${Qt5Widgets_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS}
@ -160,22 +148,21 @@ target_include_directories(wowlet PUBLIC
${Qt5WebSockets_INCLUDE_DIRS} ${Qt5WebSockets_INCLUDE_DIRS}
) )
if(OPENVR) if(LINUX_ACTIVATION)
target_include_directories(wowlet PUBLIC ${CMAKE_SOURCE_DIR}/contrib/) target_include_directories(wowlet PUBLIC
${CAIRO_INCLUDE_DIRS}
${XFIXES_INCLUDE_DIR}
)
endif() endif()
if(DONATE_BEG) if(OPENVR)
target_compile_definitions(wowlet PRIVATE DONATE_BEG=1) target_include_directories(wowlet PUBLIC ${CMAKE_SOURCE_DIR}/contrib/)
endif() endif()
if(TOR_BIN) if(TOR_BIN)
target_compile_definitions(wowlet PRIVATE HAS_TOR_BIN=1) target_compile_definitions(wowlet PRIVATE HAS_TOR_BIN=1)
endif() endif()
if(XMRIG)
target_compile_definitions(wowlet PRIVATE HAS_XMRIG=1)
endif()
if(ANDROID) if(ANDROID)
target_compile_definitions(wowlet PRIVATE HAS_ANDROID=1) target_compile_definitions(wowlet PRIVATE HAS_ANDROID=1)
endif() endif()
@ -197,8 +184,8 @@ if(STATIC)
target_compile_definitions(wowlet PRIVATE STATIC=1) target_compile_definitions(wowlet PRIVATE STATIC=1)
endif() endif()
if(STATIC) if(LINUX_ACTIVATION)
target_compile_definitions(wowlet PRIVATE STATIC=1) target_compile_definitions(wowlet PRIVATE LINUX_ACTIVATION=1)
endif() endif()
if("$ENV{DRONE}" STREQUAL "true") if("$ENV{DRONE}" STREQUAL "true")
@ -216,10 +203,7 @@ endif()
target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC) target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC)
if(QML) qt5_import_qml_plugins(${PROJECT_NAME})
qt5_import_qml_plugins(${PROJECT_NAME})
target_compile_definitions(wowlet PRIVATE HAS_QML=1)
endif()
target_compile_definitions(wowlet target_compile_definitions(wowlet
PUBLIC PUBLIC
@ -234,12 +218,10 @@ target_compile_definitions(wowlet
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
if(UNIX AND NOT APPLE) if(UNIX)
# https://stackoverflow.com/questions/57766620/cmake-add-library-doesnt-initialize-static-global-variable # https://stackoverflow.com/questions/57766620/cmake-add-library-doesnt-initialize-static-global-variable
# so that contrib/monero-seed/src/gf_elem.cpp properly initializes. A better solution is welcome. # so that contrib/monero-seed/src/gf_elem.cpp properly initializes. A better solution is welcome.
target_link_libraries(wowlet PUBLIC -Wl,--whole-archive monero-seed::monero-seed -Wl,--no-whole-archive) target_link_libraries(wowlet PUBLIC -Wl,--whole-archive wownero-seed::wownero-seed -Wl,--no-whole-archive)
else()
target_link_libraries(wowlet PUBLIC monero-seed::monero-seed)
endif() endif()
if(ANDROID) if(ANDROID)
@ -251,44 +233,32 @@ endif()
# Link Wownero core libraries # Link Wownero core libraries
target_link_libraries(wowlet PUBLIC target_link_libraries(wowlet PUBLIC
wallet_merged
${LMDB_LIBRARY}
epee epee
${UNBOUND_LIBRARY} wallet_api
${SODIUM_LIBRARY}
easylogging easylogging
blockchain_db
hardforks
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
${Iconv_LIBRARIES}
${UNBOUND_LIBRARIES}
# /usr/local/lib/libunbound.a
${EXTRA_LIBRARIES}) ${EXTRA_LIBRARIES})
# Link Qt libraries # Link Qt libraries
if(QML) target_link_libraries(wowlet PUBLIC
target_link_libraries(wowlet PUBLIC Qt5::Core
Qt5::Core Qt5::Widgets
Qt5::Widgets Qt5::Gui
Qt5::Gui Qt5::Network
Qt5::Network Qt5::Svg
Qt5::Svg Qt5::QSvgPlugin
Qt5::QSvgPlugin Qt5::QSvgIconPlugin
Qt5::QSvgIconPlugin Qt5::Xml
Qt5::Xml Qt5::WebSockets
Qt5::WebSockets Qt5::Quick
Qt5::Quick Qt5::Qml
Qt5::Qml Qt5::QuickControls2
Qt5::QuickControls2) Qt5::QuickWidgets)
else()
target_link_libraries(wowlet PUBLIC
Qt5::Core
Qt5::Widgets
Qt5::Gui
Qt5::Network
Qt5::Svg
Qt5::Xml
Qt5::WebSockets)
endif()
if(ANDROID) if(ANDROID)
# yolo some hardcoded paths # yolo some hardcoded paths
@ -319,12 +289,19 @@ endif()
# Link random other stuff # Link random other stuff
target_link_libraries(wowlet PUBLIC target_link_libraries(wowlet PUBLIC
${ICU_LIBRARIES}
openpgp
Threads::Threads Threads::Threads
${QRENCODE_LIBRARY} ${QRENCODE_LIBRARY}
) )
# Link Cairo and Xfixes
if(LINUX_ACTIVATION)
target_link_libraries(wowlet PUBLIC
${CAIRO_LIBRARIES}
${XFIXES_LIBRARY}
${X11_Xinerama_LIB}
)
endif()
# Link scanner # Link scanner
if(WITH_SCANNER) if(WITH_SCANNER)
target_link_libraries(wowlet PUBLIC qrdecoder qrscanner) target_link_libraries(wowlet PUBLIC qrdecoder qrscanner)
@ -349,26 +326,16 @@ if(OPENVR)
endif() endif()
endif() endif()
if(APPLE) target_link_libraries(wowlet PUBLIC
target_link_libraries(wowlet PUBLIC Qt5::QSvgIconPlugin
KDMacTouchBar Qt5::QSvgPlugin
) )
target_include_directories(wowlet
PUBLIC ../contrib/KDMacTouchBar)
endif()
if(NOT APPLE)
target_link_libraries(wowlet PUBLIC
Qt5::QSvgIconPlugin
Qt5::QSvgPlugin
)
endif()
if(STATIC) if(STATIC)
target_link_libraries(wowlet PUBLIC target_link_libraries(wowlet PUBLIC
Qt5::QSvgIconPlugin Qt5::QSvgIconPlugin
Qt5::QSvgPlugin) Qt5::QSvgPlugin)
if(UNIX AND NOT APPLE) if(UNIX)
target_link_libraries(wowlet PUBLIC target_link_libraries(wowlet PUBLIC
Qt5::QXcbIntegrationPlugin) Qt5::QXcbIntegrationPlugin)
endif() endif()
@ -378,26 +345,54 @@ if(X11_FOUND)
target_link_libraries(wowlet PUBLIC ${X11_LIBRARIES}) target_link_libraries(wowlet PUBLIC ${X11_LIBRARIES})
endif() endif()
if(APPLE)
include(Deploy)
endif()
install(TARGETS wowlet install(TARGETS wowlet
DESTINATION ${CMAKE_INSTALL_PREFIX} DESTINATION ${CMAKE_INSTALL_PREFIX}
) )
message(STATUS "=============================================") message(STATUS "\n====================================== SUMMARY")
message(STATUS "VERSION_MAJOR: ${VERSION_MAJOR}") if(GIT_FOUND)
message(STATUS "VERSION_MINOR: ${VERSION_MINOR}") execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/wownero OUTPUT_VARIABLE _WOWNERO_HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "VERSION_REVISION: ${VERSION_REVISION}") if(NOT _WOWNERO_HEAD STREQUAL WOWNERO_HEAD)
message(STATUS "STATIC: ${STATIC}") message(STATUS "[+] WOWNERO HEAD: ${_WOWNERO_HEAD} ... while CMake requested ${WOWNERO_HEAD}")
message(STATUS "Include QtQuick (QML): ${QML}") else()
message(STATUS "VERSION: ${VERSION}") message(STATUS "[+] WOWNERO HEAD: ${WOWNERO_HEAD}")
message(STATUS "Include the XMRIG tab: ${XMRIG}") endif()
message(STATUS "Include Valve's OpenVR library: ${OPENVR}") endif()
message(STATUS "This build is for Android: ${ANDROID}") message(STATUS "[+] VERSION: ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}-${VERSION}")
message(STATUS "This build is for testing the Android app on desktop: ${ANDROID_DEBUG}") message(STATUS "[+] STATIC: ${STATIC}")
message(STATUS "TOR_BIN: ${TOR_BIN}") message(STATUS "[+] Include Valve's OpenVR library: ${OPENVR}")
message(STATUS "DONATE_BEG: ${DONATE_BEG}") message(STATUS "[+] This build is for Android: ${ANDROID}")
message(STATUS "=============================================") message(STATUS "[+] This build is for testing the Android app on desktop: ${ANDROID_DEBUG}")
message(STATUS "[+] TOR_BIN: ${TOR_BIN}")
message(STATUS "[+] LINUX_ACTIVATION: ${LINUX_ACTIVATION}")
message(STATUS "[+] OpenSSL")
message(STATUS " - version: ${OPENSSL_VERSION}")
message(STATUS " - dirs: ${OPENSSL_INCLUDE_DIR}")
message(STATUS " - libs: ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}")
if(CAIRO_FOUND)
message(STATUS "[+] Cairo")
message(STATUS " - version: ${CAIRO_VERSION}")
message(STATUS " - dirs: ${CAIRO_INCLUDE_DIRS}")
message(STATUS " - libs: ${CAIRO_LIBRARIES}")
endif()
if(XFIXES_FOUND)
message(STATUS "[+] Xfixes")
message(STATUS " - dirs: ${XFIXES_INCLUDE_DIR}")
message(STATUS " - libs: ${XFIXES_LIBRARY}")
endif()
message(STATUS "[+] Boost")
message(STATUS " - version: ${Boost_VERSION}")
message(STATUS " - dirs: ${Boost_INCLUDE_DIRS}")
message(STATUS " - libs: ${Boost_LIBRARIES}")
if(Iconv_FOUND)
message(STATUS "[+] Iconv")
message(STATUS " - version: ${Iconv_VERSION}")
message(STATUS " - libs: ${Iconv_LIBRARIES}")
message(STATUS " - dirs: ${Iconv_INCLUDE_DIRS}")
endif()

@ -100,8 +100,21 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged); connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes); connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
// Tor & socks proxy // init backend URLs
this->ws = new WSClient(this, wsUrl); if(cmdargs->isSet("backend-host"))
this->backendHost = cmdargs->value("backend-host");
if(cmdargs->isSet("backend-host"))
this->backendPort = cmdargs->value("backend-port").toUInt();
if(cmdargs->isSet("backend-tls"))
this->backendTLS = true;
backendWSUrl = this->backendTLS ? "wss://" : "ws://";
backendWSUrl += QString("%1:%2").arg(this->backendHost).arg(this->backendPort);
backendHTTPUrl = this->backendTLS ? "https://" : "http://";
backendHTTPUrl += QString("%1:%2").arg(this->backendHost).arg(this->backendPort);
// init websocket client
this->ws = new WSClient(this);
connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage); connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
connect(this->ws, &WSClient::connectionEstablished, this, &AppContext::wsConnected); connect(this->ws, &WSClient::connectionEstablished, this, &AppContext::wsConnected);
connect(this->ws, &WSClient::closed, this, &AppContext::wsDisconnected); connect(this->ws, &WSClient::closed, this, &AppContext::wsDisconnected);
@ -150,10 +163,8 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
AppContext::prices = new Prices(); AppContext::prices = new Prices();
// XMRig // XMRig
#ifdef HAS_XMRIG
this->XMRig = new XmRig(this->configDirectory, this); this->XMRig = new XmRig(this->configDirectory, this);
this->XMRig->prepare(); this->XMRig->prepare();
#endif
this->walletManager = WalletManager::instance(); this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDirectory); QString logPath = QString("%1/daemon.log").arg(configDirectory);
@ -171,17 +182,25 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// libwallet connects // libwallet connects
connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened); connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened);
// hideOnClose
auto hideOnClose = config()->get(Config::hideOnClose).toBool();
if(hideOnClose)
QApplication::setQuitOnLastWindowClosed(false);
} }
void AppContext::initTor() { void AppContext::initTor() {
this->tor = new Tor(this, this); this->tor = new Tor(this, this);
this->tor->start(); this->tor->start();
if (!isWhonix && wsUrl.contains(".onion")) { if (!isWhonix && !backendHost.contains(".onion")) {
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort); qDebug() << "'backend-host' did not contain '.onion' - running without Tor proxy.";
this->network->setProxy(*networkProxy); return;
this->ws->webSocket.setProxy(*networkProxy);
} }
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy);
this->ws->webSocket.setProxy(*networkProxy);
} }
void AppContext::initWS() { void AppContext::initWS() {
@ -315,6 +334,7 @@ void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
auto *model = this->currentWallet->transactionHistoryModel(); auto *model = this->currentWallet->transactionHistoryModel();
if(model != nullptr) { if(model != nullptr) {
model->preferredFiatSymbol = symbol; model->preferredFiatSymbol = symbol;
this->currentWallet->transactionHistoryModel()->transactionHistory()->calcFiatInfo();
} }
} }
} }
@ -360,6 +380,13 @@ void AppContext::onWalletOpened(Wallet *wallet) {
connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed); connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed);
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated); connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
this->currentWallet->historyModel(); // load historyModel
auto *txHistory = this->currentWallet->history();
txHistory->refresh(this->currentWallet->currentSubaddressAccount());
connect(AppContext::prices, &Prices::fiatPricesUpdated, txHistory, &TransactionHistory::calcFiatInfo);
connect(AppContext::prices, &Prices::cryptoPricesUpdated, txHistory, &TransactionHistory::calcFiatInfo);
emit walletOpened(wallet); emit walletOpened(wallet);
connect(this->currentWallet, &Wallet::connectionStatusChanged, [this]{ connect(this->currentWallet, &Wallet::connectionStatusChanged, [this]{
@ -368,10 +395,6 @@ void AppContext::onWalletOpened(Wallet *wallet) {
this->nodes->connectToNode(); this->nodes->connectToNode();
this->updateBalance(); this->updateBalance();
#ifdef DONATE_BEG
this->donateBeg();
#endif
// force trigger preferredFiat signal for history model // force trigger preferredFiat signal for history model
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString()); this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
this->setWindowTitle(); this->setWindowTitle();
@ -421,15 +444,19 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
if(changed) if(changed)
emit blockHeightWSUpdated(this->heights); emit blockHeightWSUpdated(this->heights);
} }
else if(cmd == "yellwow") {
this->yellowPagesData = msg.value("data").toArray();
emit yellowUpdated();
}
else if(cmd == "rpc_nodes") { else if(cmd == "rpc_nodes") {
this->onWSNodes(msg.value("data").toArray()); this->onWSNodes(msg.value("data").toArray());
} }
#if defined(HAS_XMRIG)
else if(cmd == "xmrig") { else if(cmd == "xmrig") {
this->XMRigDownloads(msg.value("data").toObject()); this->XMRigDownloads(msg.value("data").toObject());
} }
#endif else if(cmd == "wownerod_releases") {
emit WownerodDownloads(msg.value("data").toObject());
}
else if(cmd == "crypto_rates") { else if(cmd == "crypto_rates") {
QJsonArray crypto_rates = msg.value("data").toArray(); QJsonArray crypto_rates = msg.value("data").toArray();
AppContext::prices->cryptoPricesReceived(crypto_rates); AppContext::prices->cryptoPricesReceived(crypto_rates);
@ -527,10 +554,10 @@ void AppContext::onWSForum(const QJsonArray& forum_data) {
for (auto &&entry: forum_data) { for (auto &&entry: forum_data) {
auto obj = entry.toObject(); auto obj = entry.toObject();
auto forumPost = new ForumPost( auto forumPost = new ForumPost(
obj.value("title").toString(), obj.value("thread").toString(),
obj.value("author").toString(), obj.value("member_name").toString(),
obj.value("permalink").toString(), obj.value("permalink").toString(),
obj.value("comments").toInt()); obj.value("member_name").toString());
QSharedPointer<ForumPost> r = QSharedPointer<ForumPost>(forumPost); QSharedPointer<ForumPost> r = QSharedPointer<ForumPost>(forumPost);
l.append(r); l.append(r);
} }
@ -633,7 +660,7 @@ void AppContext::createWallet(WowletSeed seed, const QString &path, const QStrin
wallet = this->walletManager->createDeterministicWalletFromSpendKey(path, password, seed.language, this->networkType, seed.spendKey, seed.restoreHeight, this->kdfRounds); wallet = this->walletManager->createDeterministicWalletFromSpendKey(path, password, seed.language, this->networkType, seed.spendKey, seed.restoreHeight, this->kdfRounds);
wallet->setCacheAttribute("wowlet.seed", seed.mnemonic.join(" ")); wallet->setCacheAttribute("wowlet.seed", seed.mnemonic.join(" "));
} }
if (seed.seedType == SeedType::MONERO) { if (seed.seedType == SeedType::WOWNERO) {
wallet = this->walletManager->recoveryWallet(path, password, seed.mnemonic.join(" "), "", this->networkType, seed.restoreHeight, this->kdfRounds); wallet = this->walletManager->recoveryWallet(path, password, seed.mnemonic.join(" "), "", this->networkType, seed.restoreHeight, this->kdfRounds);
} }
@ -755,21 +782,6 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) {
emit openAliasResolveError(msg); emit openAliasResolveError(msg);
} }
void AppContext::donateBeg() {
if(this->currentWallet == nullptr) return;
if(this->networkType != NetworkType::Type::MAINNET) return;
if(this->currentWallet->viewOnly()) return;
auto donationCounter = config()->get(Config::donateBeg).toInt();
if(donationCounter == -1)
return; // previously donated
donationCounter += 1;
if (donationCounter % m_donationBoundary == 0)
emit donationNag();
config()->set(Config::donateBeg, donationCounter);
}
AppContext::~AppContext() {} AppContext::~AppContext() {}
// ############################################## LIBWALLET QT ######################################################### // ############################################## LIBWALLET QT #########################################################
@ -844,12 +856,6 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q
} }
void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) { void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) {
for (auto &addr : address) {
if (addr == this->donationAddress) {
this->donationSending = true;
}
}
// Let UI know that the transaction was constructed // Let UI know that the transaction was constructed
emit endTransaction(); emit endTransaction();
@ -939,12 +945,6 @@ void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, con
this->updateBalance(); this->updateBalance();
emit transactionCommitted(status, tx, txid); emit transactionCommitted(status, tx, txid);
// this tx was a donation to WOWlet, stop our nagging
if(this->donationSending) {
this->donationSending = false;
config()->set(Config::donateBeg, -1);
}
} }
void AppContext::storeWallet() { void AppContext::storeWallet() {

@ -51,7 +51,6 @@ public:
// Donation config // Donation config
const QString donationAddress = "Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP"; const QString donationAddress = "Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP";
const int donationAmount = 25; // euro const int donationAmount = 25; // euro
bool donationSending = false;
QCommandLineParser *cmdargs; QCommandLineParser *cmdargs;
@ -66,7 +65,13 @@ public:
QString defaultWalletDir; QString defaultWalletDir;
QString defaultWalletDirRoot; QString defaultWalletDirRoot;
QString tmpTxDescription; QString tmpTxDescription;
QString wsUrl = "6wku2m4zrv6j666crlo7lzofv6ud6enzllyhou3ijeigpukymi37caad.onion";
// https://git.wownero.com/wowlet/wowlet-backend/
QString backendHost = "l3hkasj5nnrh24yzj4acj5dgqlscq56o5xjvvqsftj55fkonqly5aiid.onion";
unsigned int backendPort = 80;
bool backendTLS = false;
QString backendWSUrl;
QString backendHTTPUrl;
QString walletPath; QString walletPath;
QString walletPassword = ""; QString walletPassword = "";
@ -106,6 +111,7 @@ public:
static QMap<QString, QString> txDescriptionCache; static QMap<QString, QString> txDescriptionCache;
static QMap<QString, QString> txCache; static QMap<QString, QString> txCache;
static TxFiatHistory *txFiatHistory; static TxFiatHistory *txFiatHistory;
QJsonArray yellowPagesData;
QJsonObject versionPending; QJsonObject versionPending;
// libwalletqt // libwalletqt
@ -122,7 +128,6 @@ public:
Q_INVOKABLE void initTor(); Q_INVOKABLE void initTor();
Q_INVOKABLE void initWS(); Q_INVOKABLE void initWS();
void initRestoreHeights(); void initRestoreHeights();
void donateBeg();
void refreshModels(); void refreshModels();
void setWindowTitle(bool mining = false); void setWindowTitle(bool mining = false);
@ -205,8 +210,10 @@ signals:
void nodesUpdated(QList<QSharedPointer<WowletNode>> &nodes); void nodesUpdated(QList<QSharedPointer<WowletNode>> &nodes);
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries); void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
void suchWowUpdated(const QJsonArray &such_data); void suchWowUpdated(const QJsonArray &such_data);
void yellowUpdated();
void nodeSourceChanged(NodeSource nodeSource); void nodeSourceChanged(NodeSource nodeSource);
void XMRigDownloads(const QJsonObject &data); void XMRigDownloads(const QJsonObject &data);
void WownerodDownloads(const QJsonObject &data);
void pinLookupReceived(QString address, QString pin); void pinLookupReceived(QString address, QString pin);
void pinLookupErrorReceived(); void pinLookupErrorReceived();
void pinReceived(QString pin); void pinReceived(QString pin);

@ -28,6 +28,8 @@
<file>assets/images/confirmed.svg</file> <file>assets/images/confirmed.svg</file>
<file>assets/images/connect.svg</file> <file>assets/images/connect.svg</file>
<file>assets/images/copy.png</file> <file>assets/images/copy.png</file>
<file>assets/images/dog_running.gif</file>
<file>assets/images/dog_sitting.gif</file>
<file>assets/images/edit.png</file> <file>assets/images/edit.png</file>
<file>assets/images/exchange.png</file> <file>assets/images/exchange.png</file>
<file>assets/images/exchange_white.png</file> <file>assets/images/exchange_white.png</file>
@ -37,6 +39,7 @@
<file>assets/images/eye_blind.png</file> <file>assets/images/eye_blind.png</file>
<file>assets/images/wowlet.png</file> <file>assets/images/wowlet.png</file>
<file>assets/images/file.png</file> <file>assets/images/file.png</file>
<file>assets/images/fire.png</file>
<file>assets/images/gnome-calc.png</file> <file>assets/images/gnome-calc.png</file>
<file>assets/images/history.png</file> <file>assets/images/history.png</file>
<file>assets/images/info.png</file> <file>assets/images/info.png</file>
@ -225,5 +228,34 @@
<file>assets/images/zoom.png</file> <file>assets/images/zoom.png</file>
<file>assets/mnemonic_25_english.txt</file> <file>assets/mnemonic_25_english.txt</file>
<file>assets/restore_heights_wownero_mainnet.txt</file> <file>assets/restore_heights_wownero_mainnet.txt</file>
<file alias="mining/bottom_center_console.png">assets/images/mining/bottom_center_console.png</file>
<file alias="mining/intel.png">assets/images/mining/intel.png</file>
<file alias="mining/amd.png">assets/images/mining/amd.png</file>
<file alias="mining/overlay.png">assets/images/mining/overlay.png</file>
<file alias="mining/mining_gradient.png">assets/images/mining/mining_gradient.png</file>
<file alias="mining/bg1.gif">assets/images/mining/bg1.gif</file>
<file alias="mining/lowerleft_circle.png">assets/images/mining/lowerleft_circle.png</file>
<file alias="mining/lowerleft.png">assets/images/mining/lowerleft.png</file>
<file alias="mining/lower_repeat.png">assets/images/mining/lower_repeat.png</file>
<file alias="mining/lowerright.png">assets/images/mining/lowerright.png</file>
<file alias="mining/r_bottom.png">assets/images/mining/r_bottom.png</file>
<file alias="mining/r_left.png">assets/images/mining/r_left.png</file>
<file alias="mining/r_right.png">assets/images/mining/r_right.png</file>
<file alias="mining/topleft.png">assets/images/mining/topleft.png</file>
<file alias="mining/topright_bar.png">assets/images/mining/topright_bar.png</file>
<file alias="mining/topright_left.png">assets/images/mining/topright_left.png</file>
<file alias="mining/topright_middle.png">assets/images/mining/topright_middle.png</file>
<file alias="mining/topright_right.png">assets/images/mining/topright_right.png</file>
<file alias="mining/warning.png">assets/images/mining/warning.png</file>
<file alias="mining/axe.png">assets/images/mining/axe.png</file>
<file alias="mining/lowerleft_btn.png">assets/images/mining/lowerleft_btn.png</file>
<file alias="mining/elmo.gif">assets/images/mining/elmo.gif</file>
<file alias="mining/bubble.png">assets/images/mining/bubble.png</file>
<file alias="mining/mining.webp">assets/images/mining/mining.webp</file>
<file alias="fonts/ComicMono.ttf">assets/fonts/ComicMono.ttf</file>
<file alias="fonts/ComicMono-Bold.ttf">assets/fonts/ComicMono-Bold.ttf</file>
<file alias="mining.qml">ui/qml/mining.qml</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 148 KiB

@ -0,0 +1,49 @@
in Conversations::Conversations(), results into `/tmp/results.txt`
QString fn_wallet_temp = "/tmp/lol";
QString fn_wallet_temp_keys = "/tmp/lol.keys";
QString fn_results = "/tmp/results.txt";
QFile f_results(fn_results);
f_results.open(QIODevice::Append);
QTextStream f_results_stream(&f_results);
QFile f_newseeds("/tmp/new_seeds.txt"); // seeds to test for validness
int i = 0;
int offset = 0;
if (f_newseeds.open(QIODevice::ReadOnly))
{
QTextStream f_newseeds_stream(&f_newseeds);
while (!f_newseeds_stream.atEnd()) {
if(i < offset) {
i++;
continue;
}
QFile::remove(fn_wallet_temp);
QFile::remove(fn_wallet_temp_keys);
QString seed = f_newseeds_stream.readLine().trimmed();
qWarning() << "[" << QString::number(i) << "]" << seed;
auto wallet = this->walletManager->recoveryWallet(fn_wallet_temp, "", seed, "", this->networkType, 0, this->kdfRounds);
auto wallet_status = wallet->status();
if(wallet_status == Wallet::Status::Status_Ok) {
//QString addr = QString::fromStdString(wallet->address(0, 0));
auto addr = wallet->address(0, 0);
auto result_line = QString("%1 : %2").arg(addr, seed);
qWarning() << result_line;
f_results_stream << result_line << "\n";
f_results_stream.flush();
}
delete wallet;
i++;
}
}
f_results.close();

@ -1,13 +1,35 @@
{ {
"mainnet": { "mainnet": {
"tor": [ "tor": [
"a2b6ain7ozgpxhzxkhl7vrnphrlvn4cqvibyhpcvyfbbgare6bm5m2yd.onion:18081" "v2admi6gbeprxnk6i2oscizhgy4v5ixu6iezkhj5udiwbfjjs2w7dnid.onion:34568",
"awbibkoaa67jhuaqes4n2243gd6vtidjzqj2djukrubp2eudrmxr5mid.onion:34568",
"7ftpbpp6rbgqi5kjmhyin46essnh3eqb3m3rhfi7r2fr33iwkeuer3yd.onion:34568",
"j7rf2jcccizcp47y5moehguyuqdpg4lusk642sw4nayuruitqaqbc7ad.onion:34568",
"aje53o5z5twne5q2ljw44zkahhsuhjtwaxuburxddbf7n4pfsj4rj6qd.onion:34568",
"nepc4lxndsooj2akn7ofrj3ooqc25242obchcag6tw3f2mxrms2uuvyd.onion:34568",
"666l2ajxqjgj5lskvbokvworjysgvqag4oitokjuy7wz6juisul4jqad.onion:34568",
"ty7ppqozzodz75audgvkprekiiqsovbyrkfdjwadrkbe3etyzloatxad.onion:34568",
"ewynwpnprbgqllv2syn3drjdrqkw7ehoeg73znelm6mevvmpddexsoqd.onion:34568",
"mqkiqwmhhpqtzlrf26stv7jvtaudbyzkbg3lttkmvvvauzgtxm62tgyd.onion:34568",
"zao3w6isidntdbnyee5ufs7fyzmv7wzchpw32i3uo5eldjwmo4bxg2qd.onion:34568"
], ],
"clearnet": [ "clearnet": [
"wownero.fyi:34568", "global.wownodes.com:34568",
"wow.pwned.systems:34568", "super.fast.node.xmr.pm:34568",
"de1.wownodes.com:34568", "node.wownero.club:34568",
"global.wownodes.com:34568" "node.suchwow.xyz:34568",
"eu-west-1.wow.xmr.pm:34568",
"eu-west-2.wow.xmr.pm:34568",
"eu-west-3.wow.xmr.pm:34568",
"eu-west-4.wow.xmr.pm:34568",
"eu-west-5.wow.xmr.pm:34568",
"eu-west-6.wow.xmr.pm:34568",
"na-west-1.wow.xmr.pm:34568",
"much.wow.such.money:34568",
"very.wow.such.money:34568",
"169.119.33.174:34568",
"wow.bot.tips:34568",
"idontwanttogototoronto.wow.fail:34568"
] ]
}, },
"stagenet": { "stagenet": {

@ -168,4 +168,312 @@
1601257433:250500 1601257433:250500
1601710572:252000 1601710572:252000
1602154921:253500 1602154921:253500
1609825674:279148 1609825674:279148
1609816860:279048
1610116800:280048
1610420700:281048
1610715120:282048
1611017160:283048
1611314940:284048
1611616080:285048
1611911760:286048
1612211940:287048
1612515660:288048
1612810500:289048
1613115540:290048
1613414820:291048
1613709300:292048
1614008280:293048
1614307380:294048
1614603060:295048
1614902280:296048
1615204740:297048
1615505100:298048
1615807020:299048
1616108820:300048
1616402280:301048
1616706720:302048
1617003960:303048
1617611100:305048
1617911640:306048
1618191900:307048
1618522560:308048
1618816620:309048
1619098740:310048
1619390520:311048
1619695500:312048
1619985120:313048
1620290220:314048
1620586260:315048
1620885360:316048
1621183620:317048
1621482300:318048
1621782840:319048
1622079960:320048
1622380260:321048
1622678460:322048
1622974380:323048
1623269760:324048
1623567420:325048
1623869820:326048
1624167180:327048
1624473000:328048
1624767600:329048
1625065980:330048
1625366940:331048
1625439000:332048
1625706420:333048
1626101160:334048
1626365940:335048
1626660360:336048
1626966840:337048
1627231740:338048
1627541700:339048
1627810440:340048
1628078820:341048
1628406780:342048
1628709420:343048
1629011100:344048
1629283260:345048
1629582540:346048
1629874620:347048
1630168440:348048
1630465980:349048
1630756800:350048
1631060460:351048
1631393400:352048
1631699460:353048
1632012480:354048
1632289920:355048
1632602820:356048
1632923220:357048
1633203180:358048
1633491000:359048
1633811400:360048
1634113140:361048
1634430300:362048
1634722260:363048
1635035460:364048
1635368760:365048
1635639660:366048
1635957840:367048
1636245240:368048
1636535820:369048
1636863900:370048
1637160360:371048
1637457780:372048
1637793240:373048
1638062460:374048
1638407580:375048
1638675240:376048
1638981360:377048
1639273140:378048
1639612740:379048
1639928760:380048
1640242500:381048
1640530620:382048
1640838000:383048
1641136800:384048
1641437400:385048
1641716040:386048
1642030680:387048
1642330500:388048
1642714260:389048
1643040360:390048
1643344560:391048
1643612040:392048
1643913360:393048
1644223920:394048
1644491820:395048
1644794760:396048
1645110660:397048
1645379400:398048
1645693500:399048
1645981080:400048
1646301360:401048
1646576220:402048
1646919420:403048
1647208320:404048
1647506640:405048
1647768840:406048
1648082520:407048
1648391700:408048
1648686240:409048
1649002860:410048
1649282820:411048
1649599860:412048
1649959980:413048
1650248280:414048
1650528840:415048
1650809340:416048
1651118280:417048
1651432260:418048
1651740360:419048
1652031600:420048
1652351400:421048
1652639520:422048
1652954640:423048
1653252600:424048
1653549840:425048
1653862740:426048
1654161060:427048
1654461900:428048
1654782600:429048
1655076960:430048
1655441820:431048
1655740500:432048
1656030240:433048
1656324720:434048
1656624060:435048
1656925920:436048
1657236840:437048
1657526700:438048
1657816740:439048
1658135940:440048
1658434440:441048
1658715780:442048
1659010080:443048
1659318120:444048
1659611040:445048
1659914940:446048
1660204260:447048
1660505220:448048
1660831740:449048
1661114940:450048
1661397720:451048
1661708760:452048
1661997180:453048
1662335640:454048
1662630120:455048
1662908820:456048
1663213380:457048
1663501140:458048
1663832400:459048
1664109480:460048
1664412600:461048
1664724720:462048
1665004620:463048
1665305460:464048
1665600480:465048
1665894960:466048
1666213620:467048
1666527600:468048
1666816380:469048
1667123160:470048
1667427180:471048
1667720640:472048
1668021000:473048
1668334740:474048
1668649440:475048
1668946680:476048
1669247100:477048
1669560960:478048
1669852800:479048
1670164020:480048
1670467860:481048
1670767440:482048
1671063480:483048
1671363960:484048
1671639900:485048
1671950940:486048
1672248180:487048
1672566480:488048
1672859400:489048
1673179080:490048
1673451120:491048
1673759520:492048
1674053100:493048
1674374100:494048
1674658440:495048
1674978420:496048
1675249380:497048
1675559460:498048
1675847160:499048
1676178900:500048
1676463600:501048
1676782680:502048
1677074640:503048
1677385020:504048
1677686160:505048
1677996420:506048
1678277700:507048
1678564680:508048
1678852920:509048
1679178960:510048
1679473500:511048
1679780220:512048
1680075120:513048
1680415620:514048
1680715500:515048
1681014240:516048
1681314240:517048
1681628940:518048
1681919940:519048
1682226660:520048
1682528460:521048
1682838000:522048
1683149040:523048
1683448680:524048
1683750540:525048
1684050540:526048
1684346940:527048
1684659780:528048
1684960560:529048
1685261880:530048
1685565420:531048
1685865660:532048
1686178020:533048
1686480060:534048
1686776820:535048
1687078860:536048
1687379580:537048
1687685100:538048
1687993320:539048
1688293380:540048
1688598420:541048
1688898120:542048
1689208500:543048
1689503940:544048
1689802800:545048
1690109040:546048
1690414320:547048
1690721280:548048
1691025480:549048
1691325720:550048
1691633940:551048
1691931480:552048
1692233280:553048
1692532620:554048
1692843420:555048
1693147020:556048
1693453140:557048
1693749540:558048
1694054460:559048
1694348940:560048
1694661120:561048
1694961240:562048
1695270600:563048
1695572880:564048
1695891600:565048
1696201200:566048
1696506300:567048
1696833360:568048
1697119860:569048
1697424660:570048
1697728620:571048
1698030720:572048
1698328680:573048
1698640800:574048
1698955800:575048
1699261560:576048
1699561560:577048
1699873800:578048
1700185680:579048
1700481900:580048
1700801820:581048
1701103860:582048
1701414900:583048
1701721080:584048
1702027260:585048
1702332300:586048
1702641960:587048

@ -32,17 +32,26 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
this->newContact(); this->newContact();
}); });
// row context menu connect(ui->btn_addContact, &QPushButton::pressed, [this]{
m_rowMenu = new QMenu(ui->contacts); this->newContact();
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress); });
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){ connect(ui->contacts, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
QModelIndex index = ui->contacts->indexAt(point); QModelIndex index = ui->contacts->indexAt(point);
if (index.isValid()) { if (index.isValid()) {
auto username = index.model()->data(index.siblingAtColumn(AddressBookModel::Description), Qt::UserRole).toString();
m_rowMenu = new QMenu(ui->contacts);
if(username.contains("(yp)"))
m_rowMenu->addAction(QIcon(":/assets/images/network.png"), "Visit user's YellWOWPage", this, &ContactsWidget::visitYellowPage);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy address", this, &ContactsWidget::copyAddress);
m_rowMenu->addAction(QIcon(":/assets/images/copy.png"), "Copy name", this, &ContactsWidget::copyName);
m_rowMenu->addAction("Pay to", this, &ContactsWidget::payTo);
m_rowMenu->addAction("Delete", this, &ContactsWidget::deleteContact);
m_rowMenu->exec(ui->contacts->viewport()->mapToGlobal(point)); m_rowMenu->exec(ui->contacts->viewport()->mapToGlobal(point));
m_rowMenu->deleteLater();
} }
else { else {
m_contextMenu->exec(ui->contacts->viewport()->mapToGlobal(point)); m_contextMenu->exec(ui->contacts->viewport()->mapToGlobal(point));
@ -52,6 +61,68 @@ ContactsWidget::ContactsWidget(QWidget *parent) :
connect(ui->search, &QLineEdit::textChanged, this, &ContactsWidget::setSearchFilter); connect(ui->search, &QLineEdit::textChanged, this, &ContactsWidget::setSearchFilter);
} }
QMap<QString, QString> ContactsWidget::data() {
auto rtn = QMap<QString, QString>();
for (int i = 0; i < m_ctx->currentWallet->addressBook()->count(); i++) {
m_ctx->currentWallet->addressBook()->getRow(i, [&rtn](const AddressBookInfo &entry) {
rtn[entry.description()] = entry.address();
});
}
return rtn;
}
unsigned int ContactsWidget::rowIndex(const QString &name) {
// name -> row index lookup
int result = -1;
for (int i = 0; i < m_ctx->currentWallet->addressBook()->count(); i++) {
m_ctx->currentWallet->addressBook()->getRow(i, [i, name, &result](const AddressBookInfo &entry) {
if(entry.description() == name) result = i;
return;
});
if(result != -1)
return result;
}
return result;
}
void ContactsWidget::loadYellowPages() {
if (m_ctx->currentWallet == nullptr || m_ctx->yellowPagesData.empty())
return;
auto contacts = this->data();
for (auto item: m_ctx->yellowPagesData) {
auto obj = item.toObject();
const auto username = QString("%1 (yp)").arg(obj.value("username").toString());
const auto address = obj.value("address").toString();
if(contacts.contains(username)) {
if(contacts[username] == address) continue;
// update the address
auto idx = this->rowIndex(username);
if(idx == -1) continue;
m_model->deleteRow((int)idx);
}
bool addressValid = WalletManager::addressValid(address, m_ctx->currentWallet->nettype());
if (!addressValid) {
continue;
}
m_ctx->currentWallet->addressBook()->addRow(address, "", username);
}
}
void ContactsWidget::visitYellowPage() {
auto index = ui->contacts->currentIndex();
auto username = index.model()->data(
index.siblingAtColumn(AddressBookModel::Description),
Qt::UserRole).toString();
username = username.replace(" (yp)", "").trimmed();
Utils::externalLinkWarning(this, QString("https://yellow.wownero.com/user/%1").arg(username));
}
void ContactsWidget::copyAddress() { void ContactsWidget::copyAddress() {
QModelIndex index = ui->contacts->currentIndex(); QModelIndex index = ui->contacts->currentIndex();
ModelUtils::copyColumn(&index, AddressBookModel::Address); ModelUtils::copyColumn(&index, AddressBookModel::Address);

@ -22,6 +22,7 @@ class ContactsWidget : public QWidget
public: public:
explicit ContactsWidget(QWidget *parent = nullptr); explicit ContactsWidget(QWidget *parent = nullptr);
void setModel(AddressBookModel * model); void setModel(AddressBookModel * model);
QMap<QString, QString> data();
~ContactsWidget() override; ~ContactsWidget() override;
public slots: public slots:
@ -30,9 +31,11 @@ public slots:
void payTo(); void payTo();
void newContact(QString address = "", QString name = ""); void newContact(QString address = "", QString name = "");
void deleteContact(); void deleteContact();
void visitYellowPage();
void setShowFullAddresses(bool show); void setShowFullAddresses(bool show);
void setSearchFilter(const QString &filter); void setSearchFilter(const QString &filter);
void resetModel(); void resetModel();
void loadYellowPages();
signals: signals:
void fillAddress(QString &address); void fillAddress(QString &address);
@ -50,6 +53,8 @@ private:
QMenu *m_headerMenu; QMenu *m_headerMenu;
AddressBookModel * m_model; AddressBookModel * m_model;
AddressBookProxyModel * m_proxyModel; AddressBookProxyModel * m_proxyModel;
unsigned int rowIndex(const QString &name);
}; };
#endif // CONTACTSWIDGET_H #endif // CONTACTSWIDGET_H

@ -6,17 +6,14 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>589</width> <width>310</width>
<height>416</height> <height>283</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -29,14 +26,38 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <property name="verticalSpacing">
<widget class="QLineEdit" name="search"> <number>9</number>
<property name="placeholderText"> </property>
<string>Search contacts...</string> <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>9</number>
</property> </property>
</widget> <item>
<widget class="QLineEdit" name="search">
<property name="placeholderText">
<string>Search contacts...</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_addContact">
<property name="text">
<string>New contact</string>
</property>
</widget>
</item>
</layout>
</item> </item>
<item> <item row="1" column="0">
<widget class="QTreeView" name="contacts"> <widget class="QTreeView" name="contacts">
<property name="rootIsDecorated"> <property name="rootIsDecorated">
<bool>false</bool> <bool>false</bool>

@ -50,6 +50,7 @@ void DebugInfoDialog::updateInfo() {
ui->label_synchronized->setText(m_ctx->currentWallet->synchronized() ? "True" : "False"); ui->label_synchronized->setText(m_ctx->currentWallet->synchronized() ? "True" : "False");
auto node = m_ctx->nodes->connection(); auto node = m_ctx->nodes->connection();
ui->label_websocketURL->setText(m_ctx->backendWSUrl);
ui->label_remoteNode->setText(node.full); ui->label_remoteNode->setText(node.full);
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus())); ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
ui->label_torStatus->setText(torStatus); ui->label_torStatus->setText(torStatus);
@ -101,6 +102,7 @@ void DebugInfoDialog::copyToClipboad() {
text += QString("Remote node: %1 \n").arg(ui->label_remoteNode->text()); text += QString("Remote node: %1 \n").arg(ui->label_remoteNode->text());
text += QString("Wallet status: %1 \n").arg(ui->label_walletStatus->text()); text += QString("Wallet status: %1 \n").arg(ui->label_walletStatus->text());
text += QString("Tor status: %1 \n").arg(ui->label_torStatus->text()); text += QString("Tor status: %1 \n").arg(ui->label_torStatus->text());
text += QString("Websocket URL: %1 \n").arg(ui->label_websocketURL->text());
text += QString("Websocket status: %1 \n").arg(ui->label_websocketStatus->text()); text += QString("Websocket status: %1 \n").arg(ui->label_websocketStatus->text());
text += QString("Network type: %1 \n").arg(ui->label_netType->text()); text += QString("Network type: %1 \n").arg(ui->label_netType->text());

@ -183,14 +183,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0"> <item row="13" column="0">
<widget class="QLabel" name="label_19"> <widget class="QLabel" name="label_19">
<property name="text"> <property name="text">
<string>Websocket status:</string> <string>Websocket status:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="13" column="1">
<widget class="QLabel" name="label_websocketStatus"> <widget class="QLabel" name="label_websocketStatus">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -200,21 +200,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="14" column="1">
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="0"> <item row="15" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Network type:</string> <string>Network type:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="15" column="1">
<widget class="QLabel" name="label_netType"> <widget class="QLabel" name="label_netType">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -224,14 +224,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="0"> <item row="16" column="0">
<widget class="QLabel" name="label_23"> <widget class="QLabel" name="label_23">
<property name="text"> <property name="text">
<string>Seed type:</string> <string>Seed type:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="1"> <item row="16" column="1">
<widget class="QLabel" name="label_seedType"> <widget class="QLabel" name="label_seedType">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -241,14 +241,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="0"> <item row="17" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>View only:</string> <string>View only:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="17" column="1">
<widget class="QLabel" name="label_viewOnly"> <widget class="QLabel" name="label_viewOnly">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -258,14 +258,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="20" column="0"> <item row="21" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Timestamp:</string> <string>Timestamp:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="20" column="1"> <item row="21" column="1">
<widget class="QLabel" name="label_timestamp"> <widget class="QLabel" name="label_timestamp">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -275,14 +275,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="0"> <item row="20" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Operating system:</string> <string>Operating system:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="1"> <item row="20" column="1">
<widget class="QLabel" name="label_OS"> <widget class="QLabel" name="label_OS">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -292,7 +292,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="1"> <item row="19" column="1">
<widget class="Line" name="line_4"> <widget class="Line" name="line_4">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -316,20 +316,34 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="1"> <item row="18" column="1">
<widget class="QLabel" name="label_primaryOnly"> <widget class="QLabel" name="label_primaryOnly">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="0"> <item row="18" column="0">
<widget class="QLabel" name="label_11"> <widget class="QLabel" name="label_11">
<property name="text"> <property name="text">
<string>Primary only:</string> <string>Primary only:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Websocket URL:</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="label_websocketURL">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>

@ -9,6 +9,23 @@
namespace globals namespace globals
{ {
const qreal cdiv = 1e11; const qreal cdiv = 1e11;
enum Tabs {
HOME = 0,
HISTORY,
SEND,
RECEIVE,
COINS,
CALC,
XMRIG
};
enum TabsHome {
FORUM,
REDDIT,
SUCHWOW,
WFS
};
} }
#endif //WOWLET_GLOBALS_H #endif //WOWLET_GLOBALS_H

@ -32,6 +32,12 @@ TransactionInfo* TransactionHistory::transaction(const QString &id)
return itr != m_tinfo.end() ? *itr : nullptr; return itr != m_tinfo.end() ? *itr : nullptr;
} }
void TransactionHistory::calcFiatInfo() {
for(const auto &tx: m_tinfo) {
tx->calcFiatInfo();
}
}
void TransactionHistory::refresh(quint32 accountIndex) void TransactionHistory::refresh(quint32 accountIndex)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)

@ -39,6 +39,9 @@ public:
quint64 minutesToUnlock() const; quint64 minutesToUnlock() const;
bool locked() const; bool locked() const;
public slots:
void calcFiatInfo();
signals: signals:
void refreshStarted() const; void refreshStarted() const;
void refreshFinished() const; void refreshFinished() const;

@ -5,6 +5,8 @@
#include "libwalletqt/WalletManager.h" #include "libwalletqt/WalletManager.h"
#include "Transfer.h" #include "Transfer.h"
#include "Ring.h" #include "Ring.h"
#include "globals.h"
#include "appcontext.h"
TransactionInfo::Direction TransactionInfo::direction() const TransactionInfo::Direction TransactionInfo::direction() const
{ {
@ -109,6 +111,21 @@ QDateTime TransactionInfo::timestamp() const
return m_timestamp; return m_timestamp;
} }
QString TransactionInfo::currentPriceStr() const
{
return m_currentPriceStr;
}
QString TransactionInfo::historicalRateStr() const
{
return m_historicalRateStr;
}
QString TransactionInfo::historicalPriceStr() const
{
return m_historicalPriceStr;
}
QString TransactionInfo::date() const QString TransactionInfo::date() const
{ {
return timestamp().date().toString(Qt::ISODate); return timestamp().date().toString(Qt::ISODate);
@ -161,6 +178,37 @@ QString TransactionInfo::rings_formatted() const
return rings; return rings;
} }
void TransactionInfo::calcFiatInfo() {
auto const hash = this->hash();
auto timestamp = this->timestamp().toString("yyyyMMdd");
if(!AppContext::prices->markets.contains("WOW"))
return;
double fiat_rate = AppContext::prices->markets["WOW"].price_usd;
double historical_fiat_rate = AppContext::txFiatHistory->get(timestamp);
if (historical_fiat_rate == 0.0)
return;
auto const preferredFiat = config()->get(Config::preferredFiatCurrency).toString();
if(preferredFiat != "USD") {
historical_fiat_rate = AppContext::prices->convert(
"USD", preferredFiat, historical_fiat_rate);
fiat_rate = AppContext::prices->convert(
"USD", preferredFiat, fiat_rate);
}
double balance = (this->balanceDelta() / globals::cdiv);
m_historicalRate = historical_fiat_rate;
m_historicalPrice = historical_fiat_rate * balance;
m_currentPrice = fiat_rate * balance;
m_historicalPriceStr = Utils::amountToCurrencyString(m_historicalPrice, preferredFiat, 2);
m_historicalRateStr = Utils::amountToCurrencyString(m_historicalRate, preferredFiat, 5);
m_currentPriceStr = Utils::amountToCurrencyString(m_currentPrice, preferredFiat, 2);
}
TransactionInfo::TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent) TransactionInfo::TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent)
: QObject(parent) : QObject(parent)
, m_amount(pimpl->amount()) , m_amount(pimpl->amount())
@ -193,4 +241,6 @@ TransactionInfo::TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *
{ {
m_subaddrIndex.insert(i); m_subaddrIndex.insert(i);
} }
this->calcFiatInfo();
} }

@ -71,6 +71,9 @@ public:
QDateTime timestamp() const; QDateTime timestamp() const;
QString date() const; QString date() const;
QString time() const; QString time() const;
QString currentPriceStr() const;
QString historicalRateStr() const;
QString historicalPriceStr() const;
QString paymentId() const; QString paymentId() const;
//! only applicable for output transactions //! only applicable for output transactions
//! used in tx details popup //! used in tx details popup
@ -79,6 +82,9 @@ public:
QList<Transfer*> transfers() const; QList<Transfer*> transfers() const;
QString rings_formatted() const; QString rings_formatted() const;
public slots:
void calcFiatInfo();
private: private:
explicit TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr); explicit TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *parent = nullptr);
private: private:
@ -102,6 +108,14 @@ private:
QDateTime m_timestamp; QDateTime m_timestamp;
quint64 m_unlockTime; quint64 m_unlockTime;
bool m_coinbase; bool m_coinbase;
double m_historicalPrice = 0.0;
double m_historicalRate = 0.0;
double m_currentPrice = 0.0;
QString m_currentPriceStr = "?";
QString m_historicalRateStr = "?";
QString m_historicalPriceStr = "?";
}; };
#endif // TRANSACTIONINFO_H #endif // TRANSACTIONINFO_H

@ -31,6 +31,7 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
Q_INIT_RESOURCE(assets); Q_INIT_RESOURCE(assets);
qputenv("QML_DISABLE_DISK_CACHE", "1");
#if defined(Q_OS_MAC) && defined(HAS_TOR_BIN) #if defined(Q_OS_MAC) && defined(HAS_TOR_BIN)
Q_INIT_RESOURCE(assets_tor_macos); Q_INIT_RESOURCE(assets_tor_macos);
@ -107,6 +108,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
QCommandLineOption androidDebugOption(QStringList() << "android-debug", "Start the Android interface without actually running on Android - for debugging purposes. Requires -DANDROID_DEBUG=ON CMake definition."); QCommandLineOption androidDebugOption(QStringList() << "android-debug", "Start the Android interface without actually running on Android - for debugging purposes. Requires -DANDROID_DEBUG=ON CMake definition.");
parser.addOption(androidDebugOption); parser.addOption(androidDebugOption);
QCommandLineOption backendHostOption(QStringList() << "backend-host", "specify your own `wowlet-backend` host", "backend-host");
parser.addOption(backendHostOption);
QCommandLineOption backendPortOption(QStringList() << "backend-port", "specify your own `wowlet-backend` port", "backend-port");
parser.addOption(backendPortOption);
QCommandLineOption backendTLS(QStringList() << "backend-tls", "`wowlet-backend` is running via TLS? 'wss://' and 'https://' will be used.", "backend-tls");
parser.addOption(backendTLS);
auto parsed = parser.parse(argv_); auto parsed = parser.parse(argv_);
if(!parsed) { if(!parsed) {
qCritical() << parser.errorText(); qCritical() << parser.errorText();
@ -131,21 +141,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
#endif #endif
qRegisterMetaType<QVector<QString>>(); qRegisterMetaType<QVector<QString>>();
#ifdef HAS_QML #ifdef __ANDROID__
qputenv("QML_DISABLE_DISK_CACHE", "1");
#endif
if(android || androidDebug) { if(android || androidDebug) {
#ifndef HAS_QML
qCritical() << "Wowlet compiled without QML support. Try -DQML=ON";
return 1;
#endif
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication mobile_app(argc, argv); QGuiApplication mobile_app(argc, argv);
auto *ctx = new AppContext(&parser); auto *ctx = new AppContext(&parser);
auto *mobile = new mobile::Mobile(ctx, &parser, &mobile_app); auto *mobile = new mobile::Mobile(ctx, &parser, &mobile_app);
return mobile_app.exec(); return mobile_app.exec();
} }
#endif
if(openVREnabled) { if(openVREnabled) {
#ifdef HAS_OPENVR #ifdef HAS_OPENVR

@ -4,6 +4,7 @@
#include <QPixmap> #include <QPixmap>
#include <QMessageBox> #include <QMessageBox>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDesktopWidget>
#include <QCoreApplication> #include <QCoreApplication>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QMessageBox> #include <QMessageBox>
@ -93,8 +94,8 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
}); });
connect(ui->actionReport_bug, &QAction::triggered, [this](){ connect(ui->actionReport_bug, &QAction::triggered, [this](){
QMessageBox::information(this, "Reporting Bugs", QMessageBox::information(this, "Reporting Bugs",
"<body>Please report any bugs as issues on our git repo:<br>\n" "<body>Please report any bugs as issues on the forum:<br>\n"
"<a href=\"https://git.wownero.com/wowlet/wowlet/issues\" style=\"color: #33A4DF\">https://git.wownero.com/wowlet/wowlet/issues</a><br/><br/>" "<a href=\"https://forum.wownero.com\" style=\"color: #33A4DF\">https://forum.wownero.com/</a><br/><br/>"
"\n" "\n"
"Before reporting a bug, upgrade to the most recent version of WOWlet " "Before reporting a bug, upgrade to the most recent version of WOWlet "
"(latest release or git HEAD), and include the version number in your report. " "(latest release or git HEAD), and include the version number in your report. "
@ -108,11 +109,14 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png")); m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png"));
m_trayIcon->show(); m_trayIcon->show();
m_trayActionHome = new QAction("Show", this);
m_trayActionHome->setStatusTip("Show");
m_trayActionCalc = new QAction("Calc", this); m_trayActionCalc = new QAction("Calc", this);
m_trayActionCalc->setStatusTip("Calculator"); m_trayActionCalc->setStatusTip("Calculator");
m_trayActionSend = new QAction("Send", this); m_trayActionSend = new QAction("Send", this);
m_trayActionSend->setStatusTip("Send WOW payment"); m_trayActionSend->setStatusTip("Send a WOW payment");
m_trayActionHistory = new QAction("History", this); m_trayActionHistory = new QAction("History", this);
m_trayActionHistory->setStatusTip("View incoming transfers"); m_trayActionHistory->setStatusTip("View incoming transfers");
@ -120,6 +124,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
m_trayActionExit = new QAction("Quit", this); m_trayActionExit = new QAction("Quit", this);
m_trayActionExit->setStatusTip("Exit application"); m_trayActionExit->setStatusTip("Exit application");
m_trayMenu.addAction(m_trayActionHome);
m_trayMenu.addAction(m_trayActionSend); m_trayMenu.addAction(m_trayActionSend);
m_trayMenu.addAction(m_trayActionHistory); m_trayMenu.addAction(m_trayActionHistory);
m_trayMenu.addAction(m_trayActionCalc); m_trayMenu.addAction(m_trayActionCalc);
@ -127,6 +132,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
m_trayIcon->setContextMenu(&m_trayMenu); m_trayIcon->setContextMenu(&m_trayMenu);
// @TODO: only init tray *after* boot // @TODO: only init tray *after* boot
connect(m_trayActionHome, &QAction::triggered, this, &MainWindow::showHomeWindow);
connect(m_trayActionCalc, &QAction::triggered, this, &MainWindow::showCalcWindow); connect(m_trayActionCalc, &QAction::triggered, this, &MainWindow::showCalcWindow);
connect(m_trayActionSend, &QAction::triggered, this, &MainWindow::showSendTab); connect(m_trayActionSend, &QAction::triggered, this, &MainWindow::showSendTab);
connect(m_trayActionHistory, &QAction::triggered, this, &MainWindow::showHistoryTab); connect(m_trayActionHistory, &QAction::triggered, this, &MainWindow::showHistoryTab);
@ -166,22 +172,25 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage); connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage);
// XMRig // XMRig
#ifdef HAS_XMRIG
m_xmrig = new XMRigWidget(m_ctx, this); m_xmrig = new XMRigWidget(m_ctx, this);
ui->xmrRigLayout->addWidget(m_xmrig); ui->xmrRigLayout->addWidget(m_xmrig);
connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::onProcessOutput); connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::onProcessOutput);
connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::onProcessError); connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::onProcessError);
connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::daemonOutput);
connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::daemonOutput);
connect(m_ctx->XMRig, &XmRig::blockReward, m_xmrig, &XMRigWidget::onBlockReward);
connect(m_ctx->XMRig, &XmRig::hashrate, m_xmrig, &XMRigWidget::onHashrate); connect(m_ctx->XMRig, &XmRig::hashrate, m_xmrig, &XMRigWidget::onHashrate);
connect(m_ctx->XMRig, &XmRig::daemonStateChanged, m_xmrig, &XMRigWidget::onDaemonStateChanged);
connect(m_ctx->XMRig, &XmRig::syncStatus, m_xmrig, &XMRigWidget::onSyncStatus);
connect(m_ctx->XMRig, &XmRig::uptimeChanged, m_xmrig, &XMRigWidget::onUptimeChanged);
connect(m_ctx->XMRig, &XmRig::daemonStateChanged, [=](DaemonMiningState state) {
m_ctx->setWindowTitle(state >= DaemonMiningState::mining);
});
connect(m_ctx, &AppContext::walletClosed, m_xmrig, &XMRigWidget::onWalletClosed); connect(m_ctx, &AppContext::walletClosed, m_xmrig, &XMRigWidget::onWalletClosed);
connect(m_ctx, &AppContext::walletOpened, m_xmrig, &XMRigWidget::onWalletOpened); connect(m_ctx, &AppContext::walletOpened, m_xmrig, &XMRigWidget::onWalletOpened);
connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onDownloads); connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onRigDownloads);
connect(m_ctx, &AppContext::WownerodDownloads, m_xmrig, &XMRigWidget::onWownerodDownloads);
connect(m_xmrig, &XMRigWidget::miningStarted, [=]{ m_ctx->setWindowTitle(true); });
connect(m_xmrig, &XMRigWidget::miningEnded, [=]{ m_ctx->setWindowTitle(false); });
#else
ui->tabWidget->setTabVisible(Tabs::XMRIG, false);
#endif
connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen); connect(ui->ccsWidget, &CCSWidget::selected, this, &MainWindow::showSendScreen);
connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries); connect(m_ctx, &AppContext::ccsUpdated, ui->ccsWidget->model(), &CCSModel::updateEntries);
@ -192,8 +201,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(ui->redditWidget, &RedditWidget::setStatusText, this, &MainWindow::setStatusText); connect(ui->redditWidget, &RedditWidget::setStatusText, this, &MainWindow::setStatusText);
connect(ui->tabWidget, &QTabWidget::currentChanged, m_xmrig, &XMRigWidget::onMenuTabChanged);
connect(ui->tabHomeWidget, &QTabWidget::currentChanged, [](int index){ connect(ui->tabHomeWidget, &QTabWidget::currentChanged, [](int index){
config()->set(Config::homeWidget, TabsHome(index)); config()->set(Config::homeWidget, globals::TabsHome(index));
}); });
connect(m_ctx, &AppContext::donationNag, [=]{ connect(m_ctx, &AppContext::donationNag, [=]{
@ -264,15 +274,11 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
} }
} }
if(config()->get(Config::warnOnAlpha).toBool()) {
QString warning = "WOWlet is currently in beta.\n\nPlease report any bugs "
"you encounter on our Git repository, IRC freenode #wownero or on /r/Wownero.";
QMessageBox::warning(this, "Beta Warning", warning);
config()->set(Config::warnOnAlpha, false);
}
// settings connects // settings connects
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, this, &MainWindow::onUpdateFiatBalanceWidget); connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, this, &MainWindow::onUpdateFiatBalanceWidget);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, this, &MainWindow::onUpdateWowWidget);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, this, &MainWindow::onUpdateBTCWidget);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, this, &MainWindow::onUpdateXMRWidget);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged); connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->suchWowWidget, &SuchWowWidget::onPreferredFiatCurrencyChanged); connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->suchWowWidget, &SuchWowWidget::onPreferredFiatCurrencyChanged);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged)); connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
@ -292,7 +298,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
}); });
connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) { connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) {
ui->historyWidget->setSearchText(text); ui->historyWidget->setSearchText(text);
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
}); });
// History // History
@ -303,6 +309,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
// Contacts // Contacts
connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress); connect(ui->contactWidget, &ContactsWidget::fillAddress, ui->sendWidget, &SendWidget::fillAddress);
connect(m_ctx, &AppContext::yellowUpdated, ui->contactWidget, &ContactsWidget::loadYellowPages);
// Open alias // Open alias
connect(ui->sendWidget, &SendWidget::resolveOpenAlias, m_ctx, &AppContext::onOpenAliasResolve); connect(ui->sendWidget, &SendWidget::resolveOpenAlias, m_ctx, &AppContext::onOpenAliasResolve);
@ -328,9 +335,9 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_ctx, &AppContext::walletAboutToClose, [=]{ connect(m_ctx, &AppContext::walletAboutToClose, [=]{
if (!config()->get(Config::showTabHome).toBool()) if (!config()->get(Config::showTabHome).toBool())
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
else else
ui->tabWidget->setCurrentIndex(Tabs::HOME); ui->tabWidget->setCurrentIndex(globals::Tabs::HOME);
// Clear all tables when wallet is closed // Clear all tables when wallet is closed
ui->historyWidget->resetModel(); ui->historyWidget->resetModel();
@ -415,13 +422,9 @@ void MainWindow::initMenu() {
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc); m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc"); m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
#if defined(HAS_XMRIG)
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map)); connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["Mining"] = new ToggleTab(ui->tabXmrRig, "Mining", "Mining", ui->actionShow_XMRig, Config::showTabXMRig); m_tabShowHideMapper["Mining"] = new ToggleTab(ui->tabXmrRig, "Mining", "Mining", ui->actionShow_XMRig, Config::showTabXMRig);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "Mining"); m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "Mining");
#else
ui->actionShow_XMRig->setVisible(false);
#endif
for (const auto &key: m_tabShowHideMapper.keys()) { for (const auto &key: m_tabShowHideMapper.keys()) {
const auto toggleTab = m_tabShowHideMapper.value(key); const auto toggleTab = m_tabShowHideMapper.value(key);
@ -520,7 +523,7 @@ void MainWindow::menuToggleTabVisible(const QString &key){
void MainWindow::initWidgets() { void MainWindow::initWidgets() {
int homeWidget = config()->get(Config::homeWidget).toInt(); int homeWidget = config()->get(Config::homeWidget).toInt();
ui->tabHomeWidget->setCurrentIndex(TabsHome(homeWidget)); ui->tabHomeWidget->setCurrentIndex(globals::TabsHome(homeWidget));
} }
WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){ WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
@ -632,6 +635,7 @@ void MainWindow::onWalletOpened(Wallet *wallet) {
// contacts widget // contacts widget
ui->contactWidget->setModel(m_ctx->currentWallet->addressBookModel()); ui->contactWidget->setModel(m_ctx->currentWallet->addressBookModel());
ui->contactWidget->loadYellowPages();
// coins page // coins page
m_ctx->currentWallet->coins()->refresh(m_ctx->currentWallet->currentSubaddressAccount()); m_ctx->currentWallet->coins()->refresh(m_ctx->currentWallet->currentSubaddressAccount());
@ -966,7 +970,6 @@ void MainWindow::menuNewRestoreClicked() {
void MainWindow::menuQuitClicked() { void MainWindow::menuQuitClicked() {
cleanupBeforeClose(); cleanupBeforeClose();
QCoreApplication::quit(); QCoreApplication::quit();
} }
@ -1036,9 +1039,16 @@ void MainWindow::skinChanged(const QString &skinName) {
} }
void MainWindow::closeEvent(QCloseEvent *event) { void MainWindow::closeEvent(QCloseEvent *event) {
cleanupBeforeClose(); auto hideOnClose = config()->get(Config::hideOnClose).toBool();
if(hideOnClose && !this->isHidden()) {
this->hide();
event->ignore();
return;
}
cleanupBeforeClose();
QWidget::closeEvent(event); QWidget::closeEvent(event);
QApplication::exit();
} }
void MainWindow::donateButtonClicked() { void MainWindow::donateButtonClicked() {
@ -1047,25 +1057,35 @@ void MainWindow::donateButtonClicked() {
donation = 0.1337; donation = 0.1337;
ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the WOWlet development team", donation); ui->sendWidget->fill(m_ctx->donationAddress, "Donation to the WOWlet development team", donation);
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::showHistoryTab() { void MainWindow::showHistoryTab() {
this->raise(); this->raise();
ui->tabWidget->setCurrentIndex(Tabs::HISTORY); this->show();
ui->tabWidget->setCurrentIndex(globals::Tabs::HISTORY);
} }
void MainWindow::showSendTab() { void MainWindow::showSendTab() {
this->raise(); this->raise();
ui->tabWidget->setCurrentIndex(Tabs::SEND); this->show();
ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
}
void MainWindow::showHomeWindow() {
this->raise();
this->show();
ui->tabWidget->setCurrentIndex(globals::Tabs::HOME);
} }
void MainWindow::showCalcWindow() { void MainWindow::showCalcWindow() {
this->raise();
this->show();
m_windowCalc->show(); m_windowCalc->show();
} }
void MainWindow::payToMany() { void MainWindow::payToMany() {
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
ui->sendWidget->payToMany(); ui->sendWidget->payToMany();
QMessageBox::information(this, "Pay to many", "Enter a list of outputs in the 'Pay to' field.\n" QMessageBox::information(this, "Pay to many", "Enter a list of outputs in the 'Pay to' field.\n"
"One output per line.\n" "One output per line.\n"
@ -1075,7 +1095,7 @@ void MainWindow::payToMany() {
void MainWindow::showSendScreen(const CCSEntry &entry) { void MainWindow::showSendScreen(const CCSEntry &entry) {
ui->sendWidget->fill(entry); ui->sendWidget->fill(entry);
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::suchDonate(const QString address) { void MainWindow::suchDonate(const QString address) {
@ -1083,12 +1103,15 @@ void MainWindow::suchDonate(const QString address) {
QString preferredCurrency = config()->get(Config::preferredFiatCurrency).toString(); QString preferredCurrency = config()->get(Config::preferredFiatCurrency).toString();
double donation = AppContext::prices->convert(preferredCurrency, "WOW", tipAmount); double donation = AppContext::prices->convert(preferredCurrency, "WOW", tipAmount);
ui->sendWidget->fill(address, "SuchWow contribution :-)", donation); ui->sendWidget->fill(address, "SuchWow contribution :-)", donation);
ui->tabWidget->setCurrentIndex(Tabs::SEND); ui->tabWidget->setCurrentIndex(globals::Tabs::SEND);
} }
void MainWindow::onViewOnBlockExplorer(const QString &txid) { void MainWindow::onViewOnBlockExplorer(const QString &txid) {
QString blockExplorerLink = Utils::blockExplorerLink(config()->get(Config::blockExplorer).toString(), m_ctx->networkType, txid); QString blockExplorerLink = Utils::blockExplorerLink(txid);
Utils::externalLinkWarning(this, blockExplorerLink); if(!blockExplorerLink.isEmpty())
Utils::externalLinkWarning(this, blockExplorerLink);
else
QMessageBox::warning(this, "Error", "Could not generate block explorer URL");
} }
void MainWindow::onResendTransaction(const QString &txid) { void MainWindow::onResendTransaction(const QString &txid) {
@ -1260,6 +1283,7 @@ void MainWindow::importOutputs() {
void MainWindow::cleanupBeforeClose() { void MainWindow::cleanupBeforeClose() {
m_ctx->closeWallet(false, true); m_ctx->closeWallet(false, true);
m_ctx->tor->stop(); m_ctx->tor->stop();
m_ctx->XMRig->stop();
this->saveGeo(); this->saveGeo();
} }
@ -1384,7 +1408,12 @@ void MainWindow::onUpdateWowWidget() {
} }
auto wowObj = AppContext::prices->markets["WOW"]; auto wowObj = AppContext::prices->markets["WOW"];
auto currencyText = Utils::amountToCurrencyString(wowObj.price_usd, fiatCurrency);
double amount = wowObj.price_usd;
if(fiatCurrency != "USD")
amount = AppContext::prices->convert("USD", fiatCurrency, amount);
auto currencyText = Utils::amountToCurrencyString(amount, fiatCurrency, 5);
m_tickerWOW->setFiatText(currencyText); m_tickerWOW->setFiatText(currencyText);
auto pct24h = AppContext::prices->markets["WOW"].price_usd_change_pct_24h; auto pct24h = AppContext::prices->markets["WOW"].price_usd_change_pct_24h;

@ -33,6 +33,7 @@
#include "utils/config.h" #include "utils/config.h"
#include "wizard/walletwizard.h" #include "wizard/walletwizard.h"
#include "settings.h" #include "settings.h"
#include "globals.h"
#include "dialog/aboutdialog.h" #include "dialog/aboutdialog.h"
#include "dialog/signverifydialog.h" #include "dialog/signverifydialog.h"
#include "dialog/verifyproofdialog.h" #include "dialog/verifyproofdialog.h"
@ -74,23 +75,6 @@ public:
qreal screenDpiPhysical; qreal screenDpiPhysical;
qreal screenRatio; qreal screenRatio;
enum Tabs {
HOME = 0,
HISTORY,
SEND,
RECEIVE,
COINS,
CALC,
XMRIG
};
enum TabsHome {
FORUM,
REDDIT,
SUCHWOW,
WFS
};
public slots: public slots:
void initWidgets(); void initWidgets();
void initMenu(); void initMenu();
@ -109,6 +93,7 @@ public slots:
void showViewOnlyDialog(); void showViewOnlyDialog();
void donateButtonClicked(); void donateButtonClicked();
void showCalcWindow(); void showCalcWindow();
void showHomeWindow();
void payToMany(); void payToMany();
void showWalletCacheDebugDialog(); void showWalletCacheDebugDialog();
void showSendTab(); void showSendTab();
@ -200,6 +185,7 @@ private:
QSystemTrayIcon *m_trayIcon; QSystemTrayIcon *m_trayIcon;
QMenu m_trayMenu; QMenu m_trayMenu;
QAction *m_trayActionHome;
QAction *m_trayActionCalc; QAction *m_trayActionCalc;
QAction *m_trayActionExit; QAction *m_trayActionExit;
QAction *m_trayActionSend; QAction *m_trayActionSend;

@ -316,31 +316,31 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabCalc"> <widget class="QWidget" name="tabXmrRig">
<attribute name="icon"> <attribute name="icon">
<iconset resource="assets.qrc"> <iconset resource="assets.qrc">
<normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset> <normaloff>:/assets/images/mining.png</normaloff>:/assets/images/mining.png</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Calc</string> <string>Mining</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="CalcWidget" name="conversionWidget" native="true"/> <layout class="QGridLayout" name="xmrRigLayout"/>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabXmrRig"> <widget class="QWidget" name="tabCalc">
<attribute name="icon"> <attribute name="icon">
<iconset resource="assets.qrc"> <iconset resource="assets.qrc">
<normaloff>:/assets/images/mining.png</normaloff>:/assets/images/mining.png</iconset> <normaloff>:/assets/images/gnome-calc.png</normaloff>:/assets/images/gnome-calc.png</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Mining</string> <string>Calc</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0"> <item row="0" column="0">
<layout class="QGridLayout" name="xmrRigLayout"/> <widget class="CalcWidget" name="conversionWidget" native="true"/>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -56,15 +56,15 @@ QVariant ForumModel::data(const QModelIndex &index, int role) const
return post->title; return post->title;
case Author: case Author:
return post->author; return post->author;
case Comments: case DateAdded:
return QString::number(post->comments); return post->date_added;
default: default:
return QVariant(); return QVariant();
} }
} }
else if (role == Qt::TextAlignmentRole) { else if (role == Qt::TextAlignmentRole) {
switch(index.column()) { switch(index.column()) {
case Comments: case DateAdded:
return Qt::AlignRight; return Qt::AlignRight;
default: default:
return QVariant(); return QVariant();
@ -85,8 +85,8 @@ QVariant ForumModel::headerData(int section, Qt::Orientation orientation, int ro
return QString("Forum Post"); return QString("Forum Post");
case Author: case Author:
return QString("Author"); return QString("Author");
case Comments: case DateAdded:
return QString("Comments"); return QString("Date");
default: default:
return QVariant(); return QVariant();
} }

@ -18,7 +18,7 @@ public:
{ {
Title = 0, Title = 0,
Author, Author,
Comments, DateAdded,
COUNT COUNT
}; };

@ -53,10 +53,10 @@ int TransactionHistoryModel::columnCount(const QModelIndex &parent) const {
// When wowlet is in QtWidgets mode, it will only use the first 5 columns, // When wowlet is in QtWidgets mode, it will only use the first 5 columns,
// the rest should be hidden, because it shows in the GUI. So by default we'll // the rest should be hidden, because it shows in the GUI. So by default we'll
// use 5 as column count. When in QtQuick (QML) mode, we want to expose more columns // use 6 as column count. When in QtQuick (QML) mode, we want to expose more columns
// so we can change the column count here. // so we can change the column count here.
return AppContext::isQML ? this->COUNT : 5; return AppContext::isQML ? this->COUNT : 7;
} }
QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const { QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const {
@ -76,7 +76,9 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
else if (role == Qt::TextAlignmentRole) { else if (role == Qt::TextAlignmentRole) {
switch (index.column()) { switch (index.column()) {
case TransactionInfoRole::Amount: case TransactionInfoRole::Amount:
case TransactionInfoRole::FiatAmount: case TransactionInfoRole::HistoricalPrice:
case TransactionInfoRole::HistoricalRate:
case TransactionInfoRole::CurrentPrice:
result = Qt::AlignRight; result = Qt::AlignRight;
} }
} }
@ -118,7 +120,6 @@ QVariant TransactionHistoryModel::data(const QModelIndex &index, int role) const
} }
else if (role == Qt::ForegroundRole) { else if (role == Qt::ForegroundRole) {
switch(index.column()) { switch(index.column()) {
case TransactionInfoRole::FiatAmount:
case TransactionInfoRole::Amount: case TransactionInfoRole::Amount:
{ {
if (tInfo.direction() == TransactionInfo::Direction_Out) { if (tInfo.direction() == TransactionInfo::Direction_Out) {
@ -170,18 +171,16 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
} }
case TransactionInfoRole::TxID: case TransactionInfoRole::TxID:
return tInfo.hash(); return tInfo.hash();
case TransactionInfoRole::FiatAmount: case TransactionInfoRole::HistoricalRate: {
return tInfo.historicalRateStr();
}
case TransactionInfoRole::HistoricalPrice:
{ {
double usd_price = AppContext::txFiatHistory->get(tInfo.timestamp().toString("yyyyMMdd")); return tInfo.historicalPriceStr();
if (usd_price == 0.0) }
return QVariant("?"); case TransactionInfoRole::CurrentPrice:
{
double usd_amount = usd_price * (tInfo.balanceDelta() / globals::cdiv); return tInfo.currentPriceStr();
if(this->preferredFiatSymbol != "USD")
usd_amount = AppContext::prices->convert("USD", this->preferredFiatSymbol, usd_amount);
double fiat_rounded = ceil(Utils::roundSignificant(usd_amount, 3) * 100.0) / 100.0;
return QString("%1").arg(Utils::amountToCurrencyString(fiat_rounded, this->preferredFiatSymbol));
} }
default: default:
{ {
@ -206,8 +205,12 @@ QVariant TransactionHistoryModel::headerData(int section, Qt::Orientation orient
return QString("Amount"); return QString("Amount");
case TransactionInfoRole::TxID: case TransactionInfoRole::TxID:
return QString("Txid"); return QString("Txid");
case TransactionInfoRole::FiatAmount: case TransactionInfoRole::HistoricalPrice:
return QString("Fiat"); return QString("Historical price");
case TransactionInfoRole::HistoricalRate:
return QString("Historical rate");
case TransactionInfoRole::CurrentPrice:
return QString("Current price");
default: default:
return QVariant(); return QVariant();
} }

@ -27,7 +27,9 @@ public:
Description, Description,
Amount, Amount,
TxID, TxID,
FiatAmount, HistoricalPrice,
HistoricalRate,
CurrentPrice,
TransactionIsOutRole, TransactionIsOutRole,
TransactionFailedRole, TransactionFailedRole,
TransactionPendingRole, TransactionPendingRole,

@ -1,20 +0,0 @@
file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE HEADERS *.h)
find_library(GCRYPT_LIBRARY gcrypt)
find_library(GPG_ERROR_LIBRARY gpg-error)
add_library(openpgp
${SOURCES}
${HEADERS})
find_package(GCrypt)
target_include_directories(openpgp PUBLIC
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include
${GCRYPT_INCLUDE_DIRS}
)
target_link_libraries(openpgp
PUBLIC
${GCRYPT_LIBRARY}
${GPG_ERROR_LIBRARY})

@ -1,107 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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.
#pragma once
#include <vector>
#include <gcrypt.h>
#include <span.h>
namespace openpgp
{
class hash
{
public:
enum algorithm : uint8_t
{
sha256 = 8,
};
hash(const hash &) = delete;
hash &operator=(const hash &) = delete;
hash(uint8_t algorithm)
: algo(algorithm)
, consumed(0)
{
if (gcry_md_open(&md, algo, 0) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to create message digest object");
}
}
~hash()
{
gcry_md_close(md);
}
hash &operator<<(uint8_t byte)
{
gcry_md_putc(md, byte);
++consumed;
return *this;
}
hash &operator<<(const epee::span<const uint8_t> &bytes)
{
gcry_md_write(md, &bytes[0], bytes.size());
consumed += bytes.size();
return *this;
}
hash &operator<<(const std::vector<uint8_t> &bytes)
{
return *this << epee::to_span(bytes);
}
std::vector<uint8_t> finish() const
{
std::vector<uint8_t> result(gcry_md_get_algo_dlen(algo));
const void *digest = gcry_md_read(md, algo);
if (digest == nullptr)
{
throw std::runtime_error("failed to read the digest");
}
memcpy(&result[0], digest, result.size());
return result;
}
size_t consumed_bytes() const
{
return consumed;
}
private:
const uint8_t algo;
gcry_md_hd_t md;
size_t consumed;
};
}

@ -1,78 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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.
#pragma once
#include <gcrypt.h>
namespace openpgp
{
class mpi
{
public:
mpi(const mpi &) = delete;
mpi &operator=(const mpi &) = delete;
mpi(mpi &&other)
: data(other.data)
{
other.data = nullptr;
}
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
mpi(const byte_container &buffer, gcry_mpi_format format = GCRYMPI_FMT_USG)
: mpi(&buffer[0], buffer.size(), format)
{
}
mpi(const void *buffer, size_t size, gcry_mpi_format format = GCRYMPI_FMT_USG)
{
if (gcry_mpi_scan(&data, format, buffer, size, nullptr) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to read mpi from buffer");
}
}
~mpi()
{
gcry_mpi_release(data);
}
const gcry_mpi_t &get() const
{
return data;
}
private:
gcry_mpi_t data;
};
} // namespace openpgp

@ -1,378 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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 "openpgp.h"
#include <locale>
#include <string_coding.h>
#include "hash.h"
#include "mpi.h"
#include "packet_stream.h"
namespace openpgp
{
namespace
{
std::string::const_iterator find_next_line(std::string::const_iterator begin, const std::string::const_iterator &end)
{
begin = std::find(begin, end, '\n');
return begin != end ? ++begin : end;
}
std::string::const_iterator find_line_starting_with(
std::string::const_iterator it,
const std::string::const_iterator &end,
const std::string &starts_with)
{
for (std::string::const_iterator next_line; it != end; it = next_line)
{
next_line = find_next_line(it, end);
const size_t line_length = static_cast<size_t>(std::distance(it, next_line));
if (line_length >= starts_with.size() && std::equal(starts_with.begin(), starts_with.end(), it))
{
return it;
}
}
return end;
}
std::string::const_iterator find_empty_line(std::string::const_iterator it, const std::string::const_iterator &end)
{
for (; it != end && *it != '\r' && *it != '\n'; it = find_next_line(it, end))
{
}
return it;
}
std::string get_armored_block_contents(const std::string &text, const std::string &block_name)
{
static constexpr const char dashes[] = "-----";
const std::string armor_header = dashes + block_name + dashes;
auto block_start = find_line_starting_with(text.begin(), text.end(), armor_header);
auto block_headers = find_next_line(block_start, text.end());
auto block_end = find_line_starting_with(block_headers, text.end(), dashes);
auto contents_begin = find_next_line(find_empty_line(block_headers, block_end), block_end);
if (contents_begin == block_end)
{
throw std::runtime_error("armored block not found");
}
return std::string(contents_begin, block_end);
}
} // namespace
public_key_rsa::public_key_rsa(s_expression expression, size_t bits)
: m_expression(std::move(expression))
, m_bits(bits)
{
}
const gcry_sexp_t &public_key_rsa::get() const
{
return m_expression.get();
}
size_t public_key_rsa::bits() const
{
return m_bits;
}
public_key_block::public_key_block(const std::string &armored)
: public_key_block(epee::to_byte_span(epee::to_span(epee::string_encoding::base64_decode(
strip_line_breaks(get_armored_block_contents(armored, "BEGIN PGP PUBLIC KEY BLOCK"))))))
{
}
// TODO: Public-Key expiration, User ID and Public-Key certification, Subkey binding checks
public_key_block::public_key_block(const epee::span<const uint8_t> buffer)
{
packet_stream packets(buffer);
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::user_id);
if (data == nullptr)
{
throw std::runtime_error("user id is missing");
}
m_user_id.assign(data->begin(), data->end());
const auto append_public_key = [this](const std::vector<uint8_t> &data) {
deserializer<std::vector<uint8_t>> serialized(data);
const auto version = serialized.read_big_endian<uint8_t>();
if (version != 4)
{
throw std::runtime_error("unsupported public key version");
}
/* const auto timestamp = */ serialized.read_big_endian<uint32_t>();
const auto algorithm = serialized.read_big_endian<uint8_t>();
if (algorithm != openpgp::algorithm::rsa)
{
throw std::runtime_error("unsupported public key algorithm");
}
{
const mpi public_key_n = serialized.read_mpi();
const mpi public_key_e = serialized.read_mpi();
emplace_back(
s_expression("(public-key (rsa (n %m) (e %m)))", public_key_n.get(), public_key_e.get()),
gcry_mpi_get_nbits(public_key_n.get()));
}
};
data = packets.find_first(packet_tag::type::public_key);
if (data == nullptr)
{
throw std::runtime_error("public key is missing");
}
append_public_key(*data);
packets.for_each(packet_tag::type::public_subkey, append_public_key);
}
std::string public_key_block::user_id() const
{
return m_user_id;
}
// TODO: Signature expiration check
signature_rsa::signature_rsa(
uint8_t algorithm,
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
type type,
s_expression signature,
uint8_t version)
: m_hash_algorithm(hash_algorithm)
, m_hash_leftmost_bytes(hash_leftmost_bytes)
, m_hashed_appendix(format_hashed_appendix(algorithm, hash_algorithm, hashed_data, type, version))
, m_signature(std::move(signature))
, m_type(type)
{
}
signature_rsa signature_rsa::from_armored(const std::string &armored_signed_message)
{
return from_base64(get_armored_block_contents(armored_signed_message, "BEGIN PGP SIGNATURE"));
}
signature_rsa signature_rsa::from_base64(const std::string &base64)
{
std::string decoded = epee::string_encoding::base64_decode(strip_line_breaks(base64));
epee::span<const uint8_t> buffer(reinterpret_cast<const uint8_t *>(&decoded[0]), decoded.size());
return from_buffer(buffer);
}
signature_rsa signature_rsa::from_buffer(const epee::span<const uint8_t> input)
{
packet_stream packets(input);
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::signature);
if (data == nullptr)
{
throw std::runtime_error("signature is missing");
}
deserializer<std::vector<uint8_t>> buffer(*data);
const auto version = buffer.read_big_endian<uint8_t>();
if (version != 4)
{
throw std::runtime_error("unsupported signature version");
}
const auto signature_type = static_cast<type>(buffer.read_big_endian<uint8_t>());
const auto algorithm = buffer.read_big_endian<uint8_t>();
if (algorithm != openpgp::algorithm::rsa)
{
throw std::runtime_error("unsupported signature algorithm");
}
const auto hash_algorithm = buffer.read_big_endian<uint8_t>();
const auto hashed_data_length = buffer.read_big_endian<uint16_t>();
std::vector<uint8_t> hashed_data = buffer.read(hashed_data_length);
const auto unhashed_data_length = buffer.read_big_endian<uint16_t>();
buffer.read_span(unhashed_data_length);
std::pair<uint8_t, uint8_t> hash_leftmost_bytes{buffer.read_big_endian<uint8_t>(), buffer.read_big_endian<uint8_t>()};
const mpi signature = buffer.read_mpi();
return signature_rsa(
algorithm,
std::move(hash_leftmost_bytes),
hash_algorithm,
hashed_data,
signature_type,
s_expression("(sig-val (rsa (s %m)))", signature.get()),
version);
}
bool signature_rsa::verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const
{
const s_expression signed_data = hash_message(message, public_key.bits());
return gcry_pk_verify(m_signature.get(), signed_data.get(), public_key.get()) == 0;
}
s_expression signature_rsa::hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const
{
switch (m_type)
{
case type::binary_document:
return hash_bytes(message, public_key_bits);
case type::canonical_text_document:
{
std::vector<uint8_t> crlf_formatted;
crlf_formatted.reserve(message.size());
const size_t message_size = message.size();
for (size_t offset = 0; offset < message_size; ++offset)
{
const auto &character = message[offset];
if (character == '\r')
{
continue;
}
if (character == '\n')
{
const bool skip_last_crlf = offset + 1 == message_size;
if (skip_last_crlf)
{
break;
}
crlf_formatted.push_back('\r');
}
crlf_formatted.push_back(character);
}
return hash_bytes(epee::to_span(crlf_formatted), public_key_bits);
}
default:
throw std::runtime_error("unsupported signature type");
}
}
std::vector<uint8_t> signature_rsa::hash_asn_object_id() const
{
size_t size;
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, nullptr, &size) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID) size");
}
std::vector<uint8_t> asn_object_id(size);
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, &asn_object_id[0], &size) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID)");
}
return asn_object_id;
}
s_expression signature_rsa::hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const
{
const std::vector<uint8_t> plain_hash = (hash(m_hash_algorithm) << message << m_hashed_appendix).finish();
if (plain_hash.size() < 2)
{
throw std::runtime_error("insufficient message hash size");
}
if (plain_hash[0] != m_hash_leftmost_bytes.first || plain_hash[1] != m_hash_leftmost_bytes.second)
{
throw std::runtime_error("signature checksum doesn't match the expected value");
}
std::vector<uint8_t> asn_object_id = hash_asn_object_id();
const size_t public_key_bytes = bits_to_bytes(public_key_bits);
if (public_key_bytes < plain_hash.size() + asn_object_id.size() + 11)
{
throw std::runtime_error("insufficient public key bit length");
}
std::vector<uint8_t> emsa_pkcs1_v1_5_encoded;
emsa_pkcs1_v1_5_encoded.reserve(public_key_bytes);
emsa_pkcs1_v1_5_encoded.push_back(0);
emsa_pkcs1_v1_5_encoded.push_back(1);
const size_t ps_size = public_key_bytes - plain_hash.size() - asn_object_id.size() - 3;
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), ps_size, 0xff);
emsa_pkcs1_v1_5_encoded.push_back(0);
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), asn_object_id.begin(), asn_object_id.end());
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), plain_hash.begin(), plain_hash.end());
mpi value(emsa_pkcs1_v1_5_encoded);
return s_expression("(data (flags raw) (value %m))", value.get());
}
std::vector<uint8_t> signature_rsa::format_hashed_appendix(
uint8_t algorithm,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
uint8_t type,
uint8_t version)
{
const uint16_t hashed_data_size = static_cast<uint16_t>(hashed_data.size());
const uint32_t hashed_pefix_size = sizeof(version) + sizeof(type) + sizeof(algorithm) + sizeof(hash_algorithm) +
sizeof(hashed_data_size) + hashed_data.size();
std::vector<uint8_t> appendix;
appendix.reserve(hashed_pefix_size + sizeof(version) + sizeof(uint8_t) + sizeof(hashed_pefix_size));
appendix.push_back(version);
appendix.push_back(type);
appendix.push_back(algorithm);
appendix.push_back(hash_algorithm);
appendix.push_back(static_cast<uint8_t>(hashed_data_size >> 8));
appendix.push_back(static_cast<uint8_t>(hashed_data_size));
appendix.insert(appendix.end(), hashed_data.begin(), hashed_data.end());
appendix.push_back(version);
appendix.push_back(0xff);
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 24));
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 16));
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 8));
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size));
return appendix;
}
message_armored::message_armored(const std::string &message_armored)
: m_message(get_armored_block_contents(message_armored, "BEGIN PGP SIGNED MESSAGE"))
{
}
message_armored::operator epee::span<const uint8_t>() const
{
return epee::to_byte_span(epee::to_span(m_message));
}
} // namespace openpgp

@ -1,127 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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.
#pragma once
#include <vector>
#include <gcrypt.h>
#include <span.h>
#include "s_expression.h"
namespace openpgp
{
enum algorithm : uint8_t
{
rsa = 1,
};
class public_key_rsa
{
public:
public_key_rsa(s_expression expression, size_t bits);
size_t bits() const;
const gcry_sexp_t &get() const;
private:
s_expression m_expression;
size_t m_bits;
};
class public_key_block : public std::vector<public_key_rsa>
{
public:
public_key_block(const std::string &armored);
public_key_block(const epee::span<const uint8_t> buffer);
std::string user_id() const;
private:
std::string m_user_id;
};
class signature_rsa
{
public:
enum type : uint8_t
{
binary_document = 0,
canonical_text_document = 1,
};
signature_rsa(
uint8_t algorithm,
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
type type,
s_expression signature,
uint8_t version);
static signature_rsa from_armored(const std::string &armored_signed_message);
static signature_rsa from_base64(const std::string &base64);
static signature_rsa from_buffer(const epee::span<const uint8_t> input);
bool verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const;
private:
s_expression hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const;
std::vector<uint8_t> hash_asn_object_id() const;
s_expression hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const;
static std::vector<uint8_t> format_hashed_appendix(
uint8_t algorithm,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
uint8_t type,
uint8_t version);
private:
uint8_t m_hash_algorithm;
std::pair<uint8_t, uint8_t> m_hash_leftmost_bytes;
std::vector<uint8_t> m_hashed_appendix;
s_expression m_signature;
type m_type;
};
class message_armored
{
public:
message_armored(const std::string &message_armored);
operator epee::span<const uint8_t>() const;
private:
std::string m_message;
};
} // namespace openpgp

@ -1,88 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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.
#pragma once
#include <vector>
#include <span.h>
#include "serialization.h"
namespace openpgp
{
class packet_stream
{
public:
packet_stream(const epee::span<const uint8_t> buffer)
: packet_stream(deserializer<epee::span<const uint8_t>>(buffer))
{
}
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
packet_stream(deserializer<byte_container> buffer)
{
while (!buffer.empty())
{
packet_tag tag = buffer.read_packet_tag();
packets.push_back({std::move(tag), buffer.read(tag.length)});
}
}
const std::vector<uint8_t> *find_first(packet_tag::type type) const
{
for (const auto &packet : packets)
{
if (packet.first.packet_type == type)
{
return &packet.second;
}
}
return nullptr;
}
template <typename Callback>
void for_each(packet_tag::type type, Callback &callback) const
{
for (const auto &packet : packets)
{
if (packet.first.packet_type == type)
{
callback(packet.second);
}
}
}
private:
std::vector<std::pair<packet_tag, std::vector<uint8_t>>> packets;
};
} // namespace openpgp

@ -1,78 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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.
#pragma once
#include <algorithm>
#include <stdexcept>
#include <gcrypt.h>
namespace openpgp
{
class s_expression
{
public:
s_expression(const s_expression &) = delete;
s_expression &operator=(const s_expression &) = delete;
template <typename... Args>
s_expression(Args... args)
{
if (gcry_sexp_build(&data, nullptr, args...) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to build S-expression");
}
}
s_expression(s_expression &&other)
{
std::swap(data, other.data);
}
s_expression(gcry_sexp_t data)
: data(data)
{
}
~s_expression()
{
gcry_sexp_release(data);
}
const gcry_sexp_t &get() const
{
return data;
}
private:
gcry_sexp_t data = nullptr;
};
} // namespace openpgp

@ -1,172 +0,0 @@
// Copyright (c) 2020-2021, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// 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.
#pragma once
#include "mpi.h"
namespace openpgp
{
size_t bits_to_bytes(size_t bits)
{
constexpr const uint16_t bits_in_byte = 8;
return (bits + bits_in_byte - 1) / bits_in_byte;
}
std::string strip_line_breaks(const std::string &string)
{
std::string result;
result.reserve(string.size());
for (const auto &character : string)
{
if (character != '\r' && character != '\n')
{
result.push_back(character);
}
}
return result;
}
struct packet_tag
{
enum type : uint8_t
{
signature = 2,
public_key = 6,
user_id = 13,
public_subkey = 14,
};
const type packet_type;
const size_t length;
};
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
class deserializer
{
public:
deserializer(byte_container buffer)
: buffer(std::move(buffer))
, cursor(0)
{
}
bool empty() const
{
return buffer.size() - cursor == 0;
}
packet_tag read_packet_tag()
{
const auto tag = read_big_endian<uint8_t>();
constexpr const uint8_t format_mask = 0b11000000;
constexpr const uint8_t format_old_tag = 0b10000000;
if ((tag & format_mask) != format_old_tag)
{
throw std::runtime_error("invalid packet tag");
}
const packet_tag::type packet_type = static_cast<packet_tag::type>((tag & 0b00111100) >> 2);
const uint8_t length_type = tag & 0b00000011;
size_t length;
switch (length_type)
{
case 0:
length = read_big_endian<uint8_t>();
break;
case 1:
length = read_big_endian<uint16_t>();
break;
case 2:
length = read_big_endian<uint32_t>();
break;
default:
throw std::runtime_error("unsupported packet length type");
}
return {packet_type, length};
}
mpi read_mpi()
{
const size_t bit_length = read_big_endian<uint16_t>();
return mpi(read_span(bits_to_bytes(bit_length)));
}
std::vector<uint8_t> read(size_t size)
{
if (buffer.size() - cursor < size)
{
throw std::runtime_error("insufficient buffer size");
}
const size_t offset = cursor;
cursor += size;
return {&buffer[offset], &buffer[cursor]};
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
T read_big_endian()
{
if (buffer.size() - cursor < sizeof(T))
{
throw std::runtime_error("insufficient buffer size");
}
T result = 0;
for (size_t read = 0; read < sizeof(T); ++read)
{
result = (result << 8) | static_cast<uint8_t>(buffer[cursor++]);
}
return result;
}
epee::span<const uint8_t> read_span(size_t size)
{
if (buffer.size() - cursor < size)
{
throw std::runtime_error("insufficient buffer size");
}
const size_t offset = cursor;
cursor += size;
return {reinterpret_cast<const uint8_t *>(&buffer[offset]), size};
}
private:
byte_container buffer;
size_t cursor;
};
} // namespace openpgp

@ -18,4 +18,5 @@ set(qrcode_SOURCES
) )
add_library(qrcode STATIC ${qrcode_SOURCES}) add_library(qrcode STATIC ${qrcode_SOURCES})
target_include_directories(qrcode PUBLIC ${QRENCODE_INCLUDE_DIR})
target_link_libraries(qrcode Qt5::Core Qt5::Widgets Qt5::Svg ${QRENCODE_LIBRARY}) target_link_libraries(qrcode Qt5::Core Qt5::Widgets Qt5::Svg ${QRENCODE_LIBRARY})

@ -3,9 +3,11 @@
#include "settings.h" #include "settings.h"
#include "ui_settings.h" #include "ui_settings.h"
#include "mainwindow.h" #include "mainwindow.h"
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox>
Settings::Settings(QWidget *parent) : Settings::Settings(QWidget *parent) :
QDialog(parent), QDialog(parent),
@ -30,6 +32,11 @@ Settings::Settings(QWidget *parent) :
m_ctx->updateBalance(); m_ctx->updateBalance();
}); });
connect(ui->checkBox_hideOnClose, &QCheckBox::toggled, [this](bool toggled){
config()->set(Config::hideOnClose, toggled);
QApplication::setQuitOnLastWindowClosed(toggled);
});
connect(ui->closeButton, &QDialogButtonBox::accepted, this, &Settings::close); connect(ui->closeButton, &QDialogButtonBox::accepted, this, &Settings::close);
// nodes // nodes
@ -40,6 +47,7 @@ Settings::Settings(QWidget *parent) :
// setup checkboxes // setup checkboxes
ui->checkBox_externalLink->setChecked(config()->get(Config::warnOnExternalLink).toBool()); ui->checkBox_externalLink->setChecked(config()->get(Config::warnOnExternalLink).toBool());
ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool()); ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool());
ui->checkBox_hideOnClose->setChecked(config()->get(Config::hideOnClose).toBool());
// setup comboboxes // setup comboboxes
this->setupSkinCombobox(); this->setupSkinCombobox();

@ -17,7 +17,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>5</number>
</property> </property>
<widget class="QWidget" name="tab_general"> <widget class="QWidget" name="tab_general">
<attribute name="title"> <attribute name="title">
@ -146,7 +146,7 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>blockchair.com</string> <string>muchwow.lol</string>
</property> </property>
</item> </item>
</widget> </widget>
@ -198,6 +198,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkBox_hideOnClose">
<property name="text">
<string>Hide application on close</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_node"> <widget class="QWidget" name="tab_node">

@ -0,0 +1,730 @@
import QtQuick 2.7
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.3
Rectangle {
id: root
color: "#181725"
anchors.fill: parent
property variant buffer: []
property int bufferMaxLength: 12
// state: 0:idle 1:startup 2:syncing 3:mining
signal startMining();
signal stopMining();
Component.onCompleted: {
calcAvailableHeightConsoleLines();
}
onHeightChanged: {
calcAvailableHeightConsoleLines();
}
function calcAvailableHeightConsoleLines() {
var max_lines = parseInt(textContainer.height / 20);
if(root.bufferMaxLength != max_lines && max_lines >= 4)
root.bufferMaxLength = max_lines;
}
// width: 980
// height: 480
Column {
FontLoader { id: comicMono; source: "qrc:/fonts/ComicMono.ttf" }
FontLoader { id: comicMonoBold; source: "qrc:/fonts/ComicMono-Bold.ttf" }
}
AnimatedImage {
//visible: mining.daemonMiningState === 0
source: "qrc:/mining/bg1.gif"
fillMode: Image.PreserveAspectCrop
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.bottomMargin: 92
anchors.topMargin: 112
}
Image {
source: "qrc:/mining/overlay.png"
fillMode: Image.PreserveAspectCrop
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.bottomMargin: 92
anchors.topMargin: 112
smooth: false
}
Image {
id: miningGradient
opacity: 0.0
source: "qrc:/mining/mining_gradient.png"
fillMode: Image.PreserveAspectCrop
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.bottomMargin: 92
anchors.topMargin: 112
smooth: false
states: [
State { when: mining.daemonMiningState !== 0;
PropertyChanges { target: miningGradient; opacity: 1.0 }},
State { when: mining.daemonMiningState === 0;
PropertyChanges { target: miningGradient; opacity: 0.0 }}
]
transitions: [ Transition { NumberAnimation { property: "opacity"; duration: 750}} ]
}
ColumnLayout {
width: parent.width
height: parent.height
spacing: 0
RowLayout {
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: 128
Image {
source: "qrc:/mining/topleft.png"
Layout.preferredWidth: 435
Layout.preferredHeight: 128
smooth: false
// top-left monitors
ColumnLayout {
width: 102
height: 74
anchors.left: parent.left
anchors.leftMargin: 14
anchors.top: parent.top
anchors.topMargin: 42
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
text: "Hashrate"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 14
font.family: comicMonoBold.name;
antialiasing: false
color: "#41FF00"
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
id: hashRateText
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 16
font.family: comicMono.name;
antialiasing: false
color: "#41FF00"
}
}
}
ColumnLayout {
width: 102
height: 74
anchors.left: parent.left
anchors.leftMargin: 142
anchors.top: parent.top
anchors.topMargin: 42
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
text: "uptime"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 14
font.family: comicMonoBold.name;
antialiasing: false
color: "#41FF00"
}
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
Text {
id: miningUptime
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 4
font.pointSize: 12
font.family: comicMono.name;
antialiasing: false
color: "#41FF00"
}
}
}
AnimatedImage {
visible: mining.daemonMiningState !== 0
source: "qrc:/mining/mining.webp"
fillMode: Image.PreserveAspectCrop
width: 115
height: 86
anchors.left: parent.left
anchors.leftMargin: 263
anchors.top: parent.top
anchors.topMargin: 34
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.preferredHeight: 128
spacing: 0
Image {
source: "qrc:/mining/warning.png"
Layout.fillWidth: true
Layout.preferredHeight: 15
fillMode: Image.TileHorizontally
smooth: false
}
Image {
source: "qrc:/mining/topright_bar.png"
Layout.fillWidth: true
Layout.preferredHeight: 4
fillMode: Image.TileHorizontally
smooth: false
}
RowLayout {
spacing: 0
Layout.fillHeight: true
Layout.preferredHeight: 102
Image {
Layout.preferredWidth: 5
Layout.preferredHeight: parent.height
source: "qrc:/mining/topright_left.png"
smooth: false
}
Image {
Layout.fillWidth: true
Layout.preferredHeight: parent.height
source: "qrc:/mining/topright_middle.png"
fillMode: Image.TileHorizontally
smooth: false
RowLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: 6
anchors.right: parent.right
anchors.rightMargin: 8
anchors.topMargin: 12
height: 78
spacing: 16
ColumnLayout {
Layout.minimumWidth: 200
Layout.maximumWidth: 260
Layout.fillHeight: true
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
text: "Block Height"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 8
font.pointSize: 20
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
id: heightText
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 8
font.pointSize: 18
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
}
ColumnLayout {
Layout.minimumWidth: 200
Layout.maximumWidth: 260
Layout.fillHeight: true
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
text: "Sync Progress"
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 8
font.pointSize: 20
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "transparent"
Text {
id: syncPctText
text: "-"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 18
font.family: comicMonoBold.name;
color: "#41FF00"
antialiasing: false
}
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}
Image {
Layout.preferredWidth: 5
Layout.preferredHeight: parent.height
source: "qrc:/mining/topright_right.png"
smooth: false
}
}
Item {
Layout.preferredHeight: 7 // 15 + 4 + 102 + 7 = 128
Layout.fillWidth: true
}
}
}
RowLayout {
spacing: 0
//Layout.preferredHeight: 128
Layout.fillHeight: true
Layout.fillWidth: true
Image {
Layout.preferredWidth: 6
Layout.fillHeight: true
source: "qrc:/mining/r_left.png"
fillMode: Image.TileVertically
smooth: false
}
Item {
// text container
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
id: textContainer
color: "transparent"
height: parent.height - 20
width: parent.width - 32
clip: true
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Text {
id: cons
anchors.margins: 4
anchors.fill: parent
text: {
if(mining.daemonMiningState === 0) {
return "Press the pick-axe to start mining!";
} else {
return "";
}
}
font.pointSize: 14
font.family: comicMono.name;
wrapMode: Text.WordWrap
color: "white"
}
}
}
Image {
Layout.preferredWidth: 6
Layout.fillHeight: true
source: "qrc:/mining/r_right.png"
fillMode: Image.TileVertically
smooth: false
}
}
RowLayout {
spacing: 0
Layout.preferredHeight: 140
Layout.fillWidth: true
Image {
Layout.preferredWidth: 306
Layout.preferredHeight: 140
source: "qrc:/mining/lowerleft.png"
smooth: false
AnimatedImage {
source: mining.daemonMiningState === 0 ? "qrc:/assets/images/dog_sitting.gif" : "qrc:/assets/images/dog_running.gif"
width: 80
height: 60
anchors.bottom: parent.bottom
anchors.bottomMargin: 22
anchors.left: parent.left
anchors.leftMargin: 22
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
showBubble();
}
onExited: {
hideBubble();
}
}
}
}
Image {
Layout.fillWidth: true
Layout.preferredHeight: 140
source: "qrc:/mining/lower_repeat.png"
fillMode: Image.TileHorizontally
smooth: false
}
Image {
Layout.preferredWidth: 236
Layout.preferredHeight: 140
source: "qrc:/mining/bottom_center_console.png"
smooth: false
Rectangle {
// middle console clock
anchors.left: parent.left
anchors.leftMargin: 100
anchors.top: parent.top
anchors.topMargin: 8
color: "transparent"
width: 54
height: 16
Text {
id: clock
text: ""
antialiasing: false
font.pointSize: 9
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.family: comicMonoBold.name;
color: "#41FF00";
Component.onCompleted: {
root.setClock();
}
}
}
Image {
source: "qrc:/mining/intel.png"
width: 100
height: 100
fillMode: Image.Pad
smooth: false
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
Image {
Layout.fillWidth: true
Layout.preferredHeight: 140
source: "qrc:/mining/lower_repeat.png"
fillMode: Image.TileHorizontally
smooth: false
}
Image {
Layout.preferredWidth: 308
Layout.preferredHeight: 140
source: "qrc:/mining/lowerright.png"
smooth: false
Rectangle {
// lower-right button container
color: "transparent"
width: 106
height: 100
anchors.right: parent.right
anchors.rightMargin: 11
anchors.top: parent.top
anchors.topMargin: 34
Image {
id: imgAxe
visible: mining.daemonMiningState === 0
source: "qrc:/mining/axe.png"
width: 73
height: 75
smooth: false
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
AnimatedImage {
visible: mining.daemonMiningState !== 0
source: "qrc:/mining/elmo.gif"
width: 106
height: 100
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Text {
id: stopMiningBtn
visible: true
text: "Stop Mining"
font.pointSize: 10
z: parent.z + 1
color: "black"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 28
font.family: comicMonoBold.name;
antialiasing: false
}
}
MouseArea {
anchors.fill: parent
z: parent.z + 1
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if(mining.daemonMiningState === 0) {
root.startMining();
root.calcAvailableHeightConsoleLines();
}
else
root.stopMining();
}
onEntered: {
imgAxe.height = 64
imgAxe.width = 64
}
onExited: {
imgAxe.height = 75
imgAxe.width = 73
}
}
}
}
}
}
Image {
id: bubble
visible: false
source: "qrc:/mining/bubble.png"
width: 200
height: 60
anchors.bottom: parent.bottom
anchors.bottomMargin: 64
anchors.left: parent.left
anchors.leftMargin: 48
Rectangle {
anchors.top: parent.top
anchors.topMargin: 6
anchors.left: parent.left
anchors.leftMargin: 10
height: 26
color: "transparent"
width: 183
z: parent.z + 1
Text {
id: bubbleText
text: ""
color: "black"
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pointSize: 14
font.family: ComicMonoBold.name;
}
}
}
Timer {
id: setClockTimer
interval: 1000*60
running: true
repeat: true
onTriggered: setClock()
}
Timer {
id: dogBubbleTimer
interval: 1000*30
running: true
repeat: true
onTriggered: {
if(Math.random() >= 0.5) return;
if(bubble.visible) return;
root.dogBubbleRemoval.stop();
root.dogBubbleRemoval.start();
var msg = root.bubbleMessage();
bubbleText.text = msg;
bubble.visible = true;
}
}
Timer {
id: dogBubbleRemoval
interval: 2500
running: false
repeat: false
onTriggered: bubble.visible = false;
}
function setClock() {
var now = new Date();
var hours = now.getHours();
var minutes = ('0'+now.getMinutes()).slice(-2);
clock.text = hours + ":" + minutes;
}
function resetComponents() {
hashRateText.text = "-";
miningUptime.text = "-";
syncPctText.text = "-";
heightText.text = "-";
}
function consoleAppend(line) {
if(root.buffer.length >= root.bufferMaxLength)
root.buffer.shift()
root.buffer.push(line);
cons.text = "";
for(var i = 0; i != root.bufferMaxLength; i++) {
if(root.buffer[i])
cons.text += root.buffer[i] + "\n";
}
}
Connections {
target: mining
function onDaemonOutput(line) {
root.consoleAppend(line);
}
function onSyncStatus(from, to, pct) {
syncPctText.text = pct + "%";
heightText.text = from + "/" + to;
}
function onUptimeChanged(uptime) {
miningUptime.text = uptime;
}
function onHashrate(hashrate) {
hashRateText.text = hashrate;
}
}
function showBubble() {
if(bubble.visible) return;
var msg = root.bubbleMessage();
bubbleText.text = msg;
bubble.visible = true;
}
function hideBubble() {
bubble.visible = false;
}
function bubbleMessage() {
var active = ["such work!", "mining WOW!", "woof woof!", "I am tired!", "mining :@", "weeeee", "blocks everywhere!", "wooohoo", "working, twerkin'", "looking4blocks", "mining blocks"];
var inactive = ["doing nothing!", "ZzZZzzZ", "wen mining?!", "ETA mining?!", "zZZzzZZz", "omg so bored", "so bored!!", "much idle, many zZz", "lets go!", "i'm ready!"];
var syncing = ["wen 1gbit", "syncin'", "zZzz", "ETA sync ready?!", "downloading blocks", "fetching blocks"];
var msg = "";
if(mining.daemonMiningState === 0) {
return inactive[Math.floor(Math.random()*inactive.length)];
} else if (mining.daemonMiningState === 2) {
return syncing[Math.floor(Math.random()*syncing.length)];
} else {
return active[Math.floor(Math.random()*active.length)];
}
}
}

@ -7,7 +7,7 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <monero_seed/monero_seed.hpp> #include <wownero_seed/wownero_seed.hpp>
#include "networktype.h" #include "networktype.h"
#include "utils/utils.h" #include "utils/utils.h"

@ -8,7 +8,7 @@
#include "RestoreHeightLookup.h" #include "RestoreHeightLookup.h"
enum SeedType { enum SeedType {
MONERO = 0, // 25 word seeds WOWNERO = 0, // 25 word seeds
TEVADOR // 14 word seeds TEVADOR // 14 word seeds
}; };
@ -28,7 +28,7 @@ struct WowletSeed {
QString errorString; QString errorString;
explicit WowletSeed(RestoreHeightLookup *lookup, explicit WowletSeed(RestoreHeightLookup *lookup,
const QString &coin = "monero", const QString &coin = "wownero",
const QString &language = "English", const QString &language = "English",
const QStringList &mnemonic = {}) const QStringList &mnemonic = {})
: lookup(lookup), coin(coin), language(language), mnemonic(mnemonic) : lookup(lookup), coin(coin), language(language), mnemonic(mnemonic)
@ -36,7 +36,7 @@ struct WowletSeed {
// Generate a new mnemonic if none was given // Generate a new mnemonic if none was given
if (this->mnemonic.length() == 0) { if (this->mnemonic.length() == 0) {
this->time = std::time(nullptr); this->time = std::time(nullptr);
monero_seed seed(this->time, coin.toStdString()); wownero_seed seed(this->time, coin.toStdString());
std::stringstream buffer; std::stringstream buffer;
buffer << seed; buffer << seed;
@ -50,7 +50,7 @@ struct WowletSeed {
} }
if (this->mnemonic.length() == 25) { if (this->mnemonic.length() == 25) {
this->seedType = SeedType::MONERO; this->seedType = SeedType::WOWNERO;
} }
else if (this->mnemonic.length() == 14) { else if (this->mnemonic.length() == 14) {
this->seedType = SeedType::TEVADOR; this->seedType = SeedType::TEVADOR;
@ -61,7 +61,7 @@ struct WowletSeed {
if (seedType == SeedType::TEVADOR) { if (seedType == SeedType::TEVADOR) {
try { try {
monero_seed seed(this->mnemonic.join(" ").toStdString(), coin.toStdString()); wownero_seed seed(this->mnemonic.join(" ").toStdString(), coin.toStdString());
this->time = seed.date(); this->time = seed.date();
this->setRestoreHeight(); this->setRestoreHeight();

@ -21,36 +21,37 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::checkForAppUpdates,{QS("checkForAppUpdates"), true}}, {Config::checkForAppUpdates,{QS("checkForAppUpdates"), true}},
{Config::warnOnStagenet,{QS("warnOnStagenet"), true}}, {Config::warnOnStagenet,{QS("warnOnStagenet"), true}},
{Config::warnOnTestnet,{QS("warnOnTestnet"), true}}, {Config::warnOnTestnet,{QS("warnOnTestnet"), true}},
{Config::warnOnAlpha,{QS("warnOnAlpha"), true}},
{Config::homeWidget,{QS("homeWidget"), 0}}, {Config::homeWidget,{QS("homeWidget"), 0}},
{Config::donateBeg,{QS("donateBeg"), 1}},
{Config::skin,{QS("skin"), "light"}}, {Config::skin,{QS("skin"), "light"}},
{Config::openVRSkin,{QS("openVRSkin"), "default"}}, {Config::openVRSkin,{QS("openVRSkin"), "default"}},
{Config::openVRStreamerMode,{QS("openVRStreamerMode"), false}}, {Config::openVRStreamerMode,{QS("openVRStreamerMode"), false}},
{Config::preferredFiatCurrency,{QS("preferredFiatCurrency"), "USD"}}, {Config::preferredFiatCurrency,{QS("preferredFiatCurrency"), "USD"}},
{Config::blockExplorer,{QS("blockExplorer"), "explore.wownero.com"}}, {Config::blockExplorer,{QS("blockExplorer"), "muchwow.lol"}},
{Config::walletDirectory,{QS("walletDirectory"), ""}}, {Config::walletDirectory,{QS("walletDirectory"), ""}},
{Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}}, {Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}},
{Config::walletPath,{QS("walletPath"), ""}}, {Config::walletPath,{QS("walletPath"), ""}},
{Config::xmrigPath,{QS("xmrigPath"), ""}}, {Config::xmrigPath,{QS("xmrigPath"), ""}},
{Config::xmrigPool,{QS("xmrigPool"), "cryptonote.social:2223"}}, {Config::wownerodPath, {QS("wownerodPath"), ""}},
{Config::nodes,{QS("nodes"), "{}"}}, {Config::nodes,{QS("nodes"), "{}"}},
{Config::websocketEnabled,{QS("websocketEnabled"), true}}, {Config::websocketEnabled,{QS("websocketEnabled"), true}},
{Config::nodeSource,{QS("nodeSource"), 0}}, {Config::nodeSource,{QS("nodeSource"), 0}},
{Config::useOnionNodes,{QS("useOnionNodes"), false}}, {Config::useOnionNodes,{QS("useOnionNodes"), false}},
{Config::showTabHome,{QS("showTabHome"), true}}, {Config::showTabHome,{QS("showTabHome"), true}},
{Config::showTabCoins,{QS("showTabCoins"), false}}, {Config::showTabCoins,{QS("showTabCoins"), false}},
{Config::showTabXMRig,{QS("showTabXMRig"), false}}, {Config::showTabXMRig,{QS("showTabXMRig"), true}},
{Config::showTabCalc,{QS("showTabCalc"), false}}, {Config::showTabCalc,{QS("showTabCalc"), false}},
{Config::geometry, {QS("geometry"), {}}}, {Config::geometry, {QS("geometry"), {}}},
{Config::windowState, {QS("windowState"), {}}}, {Config::windowState, {QS("windowState"), {}}},
{Config::firstRun,{QS("firstRun"), false}}, {Config::firstRun,{QS("firstRun"), false}},
{Config::hideBalance, {QS("hideBalance"), false}}, {Config::hideBalance, {QS("hideBalance"), false}},
{Config::hideOnClose, {QS("hideOnClose"), false}},
{Config::simplifiedMiningInterface, {QS("simplifiedMiningInterface"), false}},
{Config::hideFiatBalance, {QS("hideFiatBalance"), false}}, {Config::hideFiatBalance, {QS("hideFiatBalance"), false}},
{Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}}, {Config::redditFrontend, {QS("redditFrontend"), "old.reddit.com"}},
{Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}}, {Config::showHistorySyncNotice, {QS("showHistorySyncNotice"), true}},
{Config::ignoreUpdateWarning, {QS("ignoreUpdateWarning"), ""}}, {Config::ignoreUpdateWarning, {QS("ignoreUpdateWarning"), ""}},
{Config::suchWowTipAmount, {QS("suchWowTipAmount"), 0.75}} {Config::suchWowTipAmount, {QS("suchWowTipAmount"), 0.75}},
{Config::LinuxActivated, {QS("linuxActivated"), ""}}
}; };

@ -23,9 +23,7 @@ public:
checkForAppUpdates, checkForAppUpdates,
warnOnStagenet, warnOnStagenet,
warnOnTestnet, warnOnTestnet,
warnOnAlpha,
homeWidget, homeWidget,
donateBeg,
autoOpenWalletPath, autoOpenWalletPath,
skin, skin,
openVRSkin, openVRSkin,
@ -35,7 +33,7 @@ public:
walletDirectory, walletDirectory,
walletPath, walletPath,
xmrigPath, xmrigPath,
xmrigPool, wownerodPath,
nodes, nodes,
websocketEnabled, websocketEnabled,
nodeSource, nodeSource,
@ -49,10 +47,13 @@ public:
firstRun, firstRun,
hideBalance, hideBalance,
hideFiatBalance, hideFiatBalance,
hideOnClose,
simplifiedMiningInterface,
redditFrontend, redditFrontend,
showHistorySyncNotice, showHistorySyncNotice,
ignoreUpdateWarning, ignoreUpdateWarning,
suchWowTipAmount suchWowTipAmount,
LinuxActivated
}; };
~Config() override; ~Config() override;

@ -238,8 +238,13 @@ QString Utils::copyFromClipboard() {
return clipboard->text(); return clipboard->text();
} }
QString Utils::blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid) { QString Utils::blockExplorerLink(const QString &txid) {
return QString("https://explore.wownero.com/tx/%1").arg(txid); auto explorer = config()->get(Config::blockExplorer).toString();
if(explorer.startsWith("muchwow.lol")) {
return QString("https://muchwow.lol/tx?id=%1").arg(txid);
} else {
return QString("https://explore.wownero.com/tx/%1").arg(txid);
}
} }
QStandardItem *Utils::qStandardItem(const QString& text) { QStandardItem *Utils::qStandardItem(const QString& text) {
@ -413,14 +418,14 @@ double Utils::roundUp(double value, int decimal_places) {
return std::ceil(value * multiplier) / multiplier; return std::ceil(value * multiplier) / multiplier;
} }
QString Utils::amountToCurrencyString(double amount, const QString &currencyCode) { QString Utils::amountToCurrencyString(double amount, const QString &currencyCode, int precision) {
QLocale locale = getCurrencyLocale(currencyCode); QLocale locale = getCurrencyLocale(currencyCode);
// \xC2\xA0 = UTF-8 non-breaking space, it looks off. // \xC2\xA0 = UTF-8 non-breaking space, it looks off.
if (currencyCode == "USD") if (currencyCode == "USD")
return locale.toCurrencyString(amount, "$").remove("\xC2\xA0"); return locale.toCurrencyString(amount, "$", precision).remove("\xC2\xA0");
return locale.toCurrencyString(amount).remove("\xC2\xA0"); return locale.toCurrencyString(amount, nullptr, precision).remove("\xC2\xA0");
} }
int Utils::maxLength(const QVector<QString> &array) { int Utils::maxLength(const QVector<QString> &array) {
@ -434,6 +439,18 @@ int Utils::maxLength(const QVector<QString> &array) {
return maxLength; return maxLength;
} }
unsigned int Utils::countAlphaNum(const QByteArray &line) {
QRegularExpression re("([a-zA-Z0-9])");
QRegularExpressionMatchIterator iterator = re.globalMatch(line);
int count = 0;
while (iterator.hasNext()){
QRegularExpressionMatch match = iterator.next();
count += 1;
}
return count;
}
bool Utils::versionOutdated(const QString &current_version, const QString &newest_version) { bool Utils::versionOutdated(const QString &current_version, const QString &newest_version) {
// True when major or minor version changed // True when major or minor version changed
auto cver = current_version.split('.'); auto cver = current_version.split('.');

@ -12,7 +12,7 @@
#include <QtAndroid> #include <QtAndroid>
#endif #endif
#include <monero_seed/monero_seed.hpp> #include <wownero_seed/wownero_seed.hpp>
#include "networktype.h" #include "networktype.h"
#include "libwalletqt/Wallet.h" #include "libwalletqt/Wallet.h"
@ -64,18 +64,19 @@ public:
static QStandardItem *qStandardItem(const QString &text, QFont &font); static QStandardItem *qStandardItem(const QString &text, QFont &font);
static void copyToClipboard(const QString &string); static void copyToClipboard(const QString &string);
static QString copyFromClipboard(); static QString copyFromClipboard();
static QString blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid); static QString blockExplorerLink(const QString &txid);
static QString getUnixAccountName(); static QString getUnixAccountName();
static QString xdgDesktopEntry(); static QString xdgDesktopEntry();
static bool xdgDesktopEntryWrite(const QString &path); static bool xdgDesktopEntryWrite(const QString &path);
static void xdgRefreshApplications(); static void xdgRefreshApplications();
static bool xdgDesktopEntryRegister(); static bool xdgDesktopEntryRegister();
static unsigned int countAlphaNum(const QByteArray &line);
static bool pixmapWrite(const QString &path, const QPixmap &pixmap); static bool pixmapWrite(const QString &path, const QPixmap &pixmap);
static QFont relativeFont(int delta); static QFont relativeFont(int delta);
static double roundSignificant(double N, double n); static double roundSignificant(double N, double n);
static QString formatBytes(quint64 bytes); static QString formatBytes(quint64 bytes);
static QLocale getCurrencyLocale(const QString &currencyCode); static QLocale getCurrencyLocale(const QString &currencyCode);
static QString amountToCurrencyString(double amount, const QString &currencyCode); static QString amountToCurrencyString(double amount, const QString &currencyCode, int precision = 2);
static int maxLength(const QVector<QString> &array); static int maxLength(const QVector<QString> &array);
static double roundUp(double value, int decimal_places); static double roundUp(double value, int decimal_places);
static QMap<QString, QLocale> localeCache; static QMap<QString, QLocale> localeCache;

@ -8,18 +8,18 @@
#include "wsclient.h" #include "wsclient.h"
#include "appcontext.h" #include "appcontext.h"
WSClient::WSClient(AppContext *ctx, const QString &url, QObject *parent) : WSClient::WSClient(AppContext *ctx, QObject *parent) :
QObject(parent), QObject(parent),
m_ctx(ctx) { m_ctx(ctx) {
// this class connects to `https://git.wownero.com/wowlet/wowlet-backend/`
connect(&this->webSocket, &QWebSocket::binaryMessageReceived, this, &WSClient::onbinaryMessageReceived); connect(&this->webSocket, &QWebSocket::binaryMessageReceived, this, &WSClient::onbinaryMessageReceived);
connect(&this->webSocket, &QWebSocket::connected, this, &WSClient::onConnected); connect(&this->webSocket, &QWebSocket::connected, this, &WSClient::onConnected);
connect(&this->webSocket, &QWebSocket::disconnected, this, &WSClient::closed); connect(&this->webSocket, &QWebSocket::disconnected, this, &WSClient::closed);
connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError); connect(&this->webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WSClient::onError);
m_tor = url.contains(".onion"); m_tor = m_ctx->backendHost.contains(".onion");
this->url = QString("ws://%1/ws").arg(url);
// Keep websocket connection alive // keep-alive
connect(&m_pingTimer, &QTimer::timeout, [this]{ connect(&m_pingTimer, &QTimer::timeout, [this]{
if (this->webSocket.state() == QAbstractSocket::ConnectedState) if (this->webSocket.state() == QAbstractSocket::ConnectedState)
this->webSocket.ping(); this->webSocket.ping();
@ -40,7 +40,7 @@ void WSClient::start() {
qDebug() << "WebSocket connect:" << url.url(); qDebug() << "WebSocket connect:" << url.url();
#endif #endif
if((m_tor && this->m_ctx->tor->torConnected) || !m_tor) if((m_tor && this->m_ctx->tor->torConnected) || !m_tor)
this->webSocket.open(QUrl(this->url)); this->webSocket.open(QString("%1/ws").arg(m_ctx->backendWSUrl));
if(!this->m_connectionTimer.isActive()) { if(!this->m_connectionTimer.isActive()) {
connect(&this->m_connectionTimer, &QTimer::timeout, this, &WSClient::checkConnection); connect(&this->m_connectionTimer, &QTimer::timeout, this, &WSClient::checkConnection);

@ -14,11 +14,10 @@ class WSClient : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit WSClient(AppContext *ctx, const QString &url, QObject *parent = nullptr); explicit WSClient(AppContext *ctx, QObject *parent = nullptr);
void start(); void start();
void sendMsg(const QByteArray &data); void sendMsg(const QByteArray &data);
QWebSocket webSocket; QWebSocket webSocket;
QString url;
signals: signals:
void closed(); void closed();

@ -7,17 +7,24 @@
#include "utils/utils.h" #include "utils/utils.h"
#include "utils/xmrig.h" #include "utils/xmrig.h"
#include "appcontext.h" #include "mainwindow.h"
XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) { XmRig::XmRig(const QString &configDir, QObject *parent) :
this->rigDir = QDir(configDir).filePath("xmrig"); QObject(parent),
m_statusTimer(new QTimer(this))
{
m_statusTimer->setInterval(5000);
connect(m_statusTimer, &QTimer::timeout, [this]{
if(daemonMiningState == DaemonMiningState::mining && m_process.state() == QProcess::Running)
m_process.write("status\n");
});
} }
void XmRig::prepare() { void XmRig::prepare() {
m_process.setProcessChannelMode(QProcess::MergedChannels); m_process.setProcessChannelMode(QProcess::MergedChannels);
connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput); connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::onHandleProcessOutput);
connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError); connect(&m_process, &QProcess::errorOccurred, this, &XmRig::onHandleProcessError);
connect(&m_process, &QProcess::stateChanged, this, &XmRig::stateChanged); connect(&m_process, &QProcess::stateChanged, this, &XmRig::onProcessStateChanged);
} }
void XmRig::stop() { void XmRig::stop() {
@ -28,74 +35,156 @@ void XmRig::stop() {
m_process.terminate(); m_process.terminate();
#endif #endif
} }
m_statusTimer->stop();
} }
void XmRig::start(const QString &path, bool XmRig::start(const QString &path, int threads) {
int threads, m_ctx = MainWindow::getContext();
const QString &address,
const QString &username,
const QString &password,
bool tor, bool tls) {
auto state = m_process.state(); auto state = m_process.state();
if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting) { if (state == QProcess::ProcessState::Running ||
emit error("Can't start XMRig, already running or starting"); state == QProcess::ProcessState::Starting ||
return; daemonMiningState != DaemonMiningState::idle) {
emit error("Can't start wownerod, already running or starting");
return false;
} }
if(path.isEmpty()) { if(path.isEmpty()) {
emit error("XmRig->Start path parameter missing."); emit error("wownerod path seems to be empty. Go to the mining settings tab and point towards the wownerod executable!");
return; return false;
} }
if(!Utils::fileExists(path)) { if(!Utils::fileExists(path)) {
emit error(QString("Path to XMRig binary invalid; file does not exist: %1").arg(path)); emit error(QString("Path to wownerod binary is invalid; file does not exist: %1").arg(path));
return; return false;
} }
auto privateSpendKey = m_ctx->currentWallet->getSecretSpendKey();
QStringList arguments; QStringList arguments;
arguments << "-o" << address;
arguments << "-a" << "rx/wow"; // dont exit when binding fails - we dont need
arguments << "-u" << username; // to bind to any ports when we just want to mine
if(!password.isEmpty()) arguments << "--no-zmq";
arguments << "-p" << password; arguments << "--rpc-ignore-ipv4";
arguments << "--no-color"; arguments << "--p2p-ignore-ipv4";
arguments << "-t" << QString::number(threads);
if(tor) arguments << "--mining-threads" << QString::number(threads);
arguments << "-x" << QString("%1:%2").arg(Tor::torHost).arg(Tor::torPort); arguments << "--start-mining" << m_ctx->currentWallet->address(0, 0);
if(tls) arguments << "--spendkey" << privateSpendKey;
arguments << "--tls";
arguments << "--donate-level" << "1";
QString cmd = QString("%1 %2").arg(path, arguments.join(" ")); QString cmd = QString("%1 %2").arg(path, arguments.join(" "));
cmd = cmd.replace(privateSpendKey, "[redacted]");
emit output(cmd.toUtf8()); emit output(cmd.toUtf8());
m_process.start(path, arguments); m_process.start(path, arguments);
m_statusTimer->start();
return true;
} }
void XmRig::stateChanged(QProcess::ProcessState state) { void XmRig::onProcessStateChanged(QProcess::ProcessState state) {
if(state == QProcess::ProcessState::Running) if(state == QProcess::ProcessState::Running) {
emit output("XMRig started"); emit output("wownerod started");
else if (state == QProcess::ProcessState::NotRunning) changeDaemonState(DaemonMiningState::startup);
emit output("XMRig stopped"); }
else if (state == QProcess::ProcessState::NotRunning) {
emit output("wownerod stopped");
changeDaemonState(DaemonMiningState::idle);
}
} }
void XmRig::handleProcessOutput() { void XmRig::onHandleProcessOutput() {
QByteArray _output = m_process.readAllStandardOutput(); QByteArray data = m_process.readAllStandardOutput();
if(_output.contains("miner") && _output.contains("speed")) {
// detect hashrate for(auto &line: data.split('\n')) {
auto str = Utils::barrayToString(_output); // remove timestamp
auto spl = str.mid(str.indexOf("speed")).split(" "); if(line.indexOf("\tI") >= 20)
auto rate = spl.at(2) + "H/s"; line = line.remove(0, line.indexOf("\tI") + 2);
qDebug() << "mining hashrate: " << rate; line = line.trimmed();
emit hashrate(rate);
// sad attempt at removing ANSI color codes
// yes this is stupid, no i dont care
// works remarkably well so far lmao
auto ansi_start = QByteArray("\x1b\x5b");
line = line.replace(ansi_start, "");
line = line.replace("0;36m", "");
line = line.replace("0;35m", "");
line = line.replace("0;34m", "");
line = line.replace("0;33m", "");
line = line.replace("0;32m", "");
line = line.replace("1;32m", "");
line = line.replace("1;33m", "");
line = line.replace("1;34m", "");
line = line.replace("1;35m", "");
line = line.replace("1;36m", "");
if(line.startsWith("0m")) continue;
auto lower = line.toLower();
if(lower.isEmpty() || lower.startsWith("status")) continue;
// skip ascii/ansi art
unsigned int printable_chars_pct = 100 * Utils::countAlphaNum(lower) / lower.length();
if(printable_chars_pct < 60) continue;
if(lower.startsWith("the daemon will start synchronizing")) {
changeDaemonState(DaemonMiningState::startup);
} else if(lower.startsWith("synchronization started")) {
changeDaemonState(DaemonMiningState::syncing);
} else if(lower.startsWith("synced") && lower.contains("left")) {
if(daemonMiningState < DaemonMiningState::syncing) changeDaemonState(DaemonMiningState::syncing);
QRegularExpression re("synced (\\d+)\\/(\\d+) \\((\\d+)%, (\\d+) left");
QRegularExpressionMatch match = re.match(lower);
if (match.hasMatch()) {
auto from = match.captured(1);
auto to = match.captured(2);
auto pct = match.captured(3);
m_from = from.toUInt();
m_to = to.toUInt();
emit syncStatus(m_from, m_to, pct.toInt());
}
} else if(lower.contains("mining started. good luck")) {
emit syncStatus(m_to, m_to, 100);
changeDaemonState(DaemonMiningState::mining);
}
else if(lower.contains("you won a block reward"))
emit blockReward();
else if(lower.contains("mining at")) {
QRegularExpression re("Height\\: (\\d+)\\/(\\d+) \\((.*)\\) on mainnet, mining at (.*), net hash .*, uptime (.*)");
QRegularExpressionMatch match = re.match(line);
if (match.hasMatch()) {
m_from = match.captured(1).toUInt();
m_to = match.captured(2).toUInt();
unsigned int pct = match.captured(3).replace("%", "").toDouble();
auto rate = match.captured(4);
auto uptime = match.captured(5).replace(" ", "");
emit uptimeChanged(uptime);
emit syncStatus(m_to, m_to, pct);
emit hashrate(rate);
line = line.remove(0, line.indexOf("mining at"));
}
}
emit output(line.trimmed());
} }
}
emit output(_output); void XmRig::changeDaemonState(const DaemonMiningState state) {
if(daemonMiningState == state) return;
daemonMiningState = state;
emit daemonStateChanged(daemonMiningState);
} }
void XmRig::handleProcessError(QProcess::ProcessError err) { void XmRig::onHandleProcessError(QProcess::ProcessError err) {
if (err == QProcess::ProcessError::Crashed) if (err == QProcess::ProcessError::Crashed)
emit error("XMRig crashed or killed"); emit error("wownerod crashed or killed");
else if (err == QProcess::ProcessError::FailedToStart) { else if (err == QProcess::ProcessError::FailedToStart) {
auto path = config()->get(Config::xmrigPath).toString(); auto path = config()->get(Config::wownerodPath).toString();
emit error(QString("XMRig binary failed to start: %1").arg(path)); emit error(QString("wownerod binary failed to start: %1").arg(path));
} }
changeDaemonState(DaemonMiningState::idle);
} }

@ -14,6 +14,14 @@
#include "utils/childproc.h" #include "utils/childproc.h"
enum DaemonMiningState {
idle = 0,
startup,
syncing,
mining
};
class AppContext;
class XmRig : public QObject class XmRig : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -22,24 +30,33 @@ public:
explicit XmRig(const QString &configDir, QObject *parent = nullptr); explicit XmRig(const QString &configDir, QObject *parent = nullptr);
void prepare(); void prepare();
void start(const QString &path, int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true); bool start(const QString &path, int threads);
void stop(); void stop();
QString rigDir; DaemonMiningState daemonMiningState = DaemonMiningState::idle;
QString rigPath;
signals: signals:
void error(const QString &msg); void error(const QString &msg);
void output(const QByteArray &data); void output(const QByteArray &data);
void blockReward();
void syncStatus(unsigned int from, unsigned int to, unsigned int pct);
void hashrate(const QString &rate); void hashrate(const QString &rate);
void daemonStateChanged(DaemonMiningState state);
void uptimeChanged(QString &uptime);
private slots: private slots:
void stateChanged(QProcess::ProcessState); void onProcessStateChanged(QProcess::ProcessState);
void handleProcessOutput(); void onHandleProcessOutput();
void handleProcessError(QProcess::ProcessError error); void onHandleProcessError(QProcess::ProcessError error);
private: private:
void changeDaemonState(DaemonMiningState state);
ChildProcess m_process; ChildProcess m_process;
AppContext *m_ctx;
QTimer *m_statusTimer;
unsigned int m_to;
unsigned int m_from;
}; };
#endif //WOWLET_XMRIG_H #endif //WOWLET_XMRIG_H

@ -7,13 +7,13 @@
#include <QString> #include <QString>
struct ForumPost { struct ForumPost {
ForumPost(const QString &title, const QString &author, const QString &permalink, int comments) ForumPost(const QString &title, const QString &author, const QString &permalink, const QString date_added)
: title(title), author(author), permalink(permalink), comments(comments){}; : title(title), author(author), permalink(permalink), date_added(date_added){};
QString title; QString title;
QString author; QString author;
QString permalink; QString permalink;
int comments; QString date_added;
}; };
#endif //WOWLET_FORUMPOST_H #endif //WOWLET_FORUMPOST_H

@ -42,9 +42,6 @@ NodeWidget::NodeWidget(QWidget *parent)
ui->customView->setContextMenuPolicy(Qt::CustomContextMenu); ui->customView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu); connect(ui->wsView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowWSContextMenu);
connect(ui->customView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowCustomContextMenu); connect(ui->customView, &QTreeView::customContextMenuRequested, this, &NodeWidget::onShowCustomContextMenu);
connect(ui->customView, &QTreeView::doubleClicked, this, &NodeWidget::onContextConnect);
connect(ui->wsView, &QTreeView::doubleClicked, this, &NodeWidget::onContextConnect);
} }
void NodeWidget::onShowWSContextMenu(const QPoint &pos) { void NodeWidget::onShowWSContextMenu(const QPoint &pos) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save