Compare commits

...

89 Commits

Author SHA1 Message Date
Synku 2272aa5b63 Update 'CMakeLists.txt'
3 years ago
Synku cba5e6f170 Update '.gitmodules'
3 years ago
Synku a29f3fd248 Update '.gitmodules'
3 years ago
Synku 97a8256d83 Update '.gitmodules'
3 years ago
dsc 4ec452a668 Wownero fork
3 years ago
mrdeveloper ac2f00c027 Contacts: import via csv file
3 years ago
mrdeveloper 3957fa1385 Contacts: import via csv file
3 years ago
dsc 0440dca330 Commit cd1cd5cb75
3 years ago
dsc a13dbf1faf Commit cd1cd5cb75
3 years ago
mrdeveloper 67b20a2185 Contacts: import via csv file
3 years ago
dsc bd4151b666 Commit cd1cd5cb75
3 years ago
dsc c6f03e507d Commit cd1cd5cb75
3 years ago
tobtoht ea3059004e Improve row background color contrast for QDarkStyle
3 years ago
tobtoht 9cb3bf9f53 MorphToken: Add Qr code for deposit address
3 years ago
wowario 3cffb4e9b4 update PKGBUILD
3 years ago
tobtoht c0cd4c456f XMRig: don't disable mining on Tails
3 years ago
tobtoht 3d08d3f996 Settings: temporarily remove warn on update checkbox
3 years ago
tobtoht 8a516f8c74 Menu: add shortcuts
3 years ago
tobtoht 935d8ad12d SeedDialog: show restore height for 25 word seeds
3 years ago
tobtoht 01b06eef1d MorphToken: get rates
3 years ago
tobtoht 0e317ae7e3 TickerWidget: Format fiat currency using locale
3 years ago
tobtoht f90f309382 Send: Always include pref. currency in combobox
3 years ago
tobtoht ee458b5c9e VerifyProofDialog: minor fixes
3 years ago
tobtoht becaee56b2 WalletInfoDialog: fix open wallet directory on macOS
3 years ago
tobtoht 2a4d567bbe Settings: raise window
3 years ago
tobtoht dd482687c1 PasswordDialog: show warning if password incorrect
3 years ago
tobtoht 105fb22f9a Misc networking fixes
3 years ago
tobtoht 5c5b2979d9 DebugInfoDialog: add target height
3 years ago
tobtoht 25c46ac593 Menu: remove About QT
3 years ago
tobtoht b0f5710727 Mitigate target_height attack
3 years ago
tobtoht f0c3497e66 Change websocket .onion
3 years ago
tobtoht 0634426c50 Revert "Syncing hotfix"
3 years ago
tobtoht a94dba15f1 Syncing hotfix
3 years ago
tobtoht 97be1c80a5 Nodes: don't connect to out of sync nodes in wsmode
3 years ago
tobtoht f735d6e323 SeedDialog: add 25 word seed toggle
3 years ago
tobtoht bceaf3436e MorphToken: allow disabling module
3 years ago
tobtoht afb3c88e26 Initial MorphToken support
3 years ago
tobtoht ac9a9e901b Import transaction
3 years ago
mrdeveloper 5882acaf3c Remove unused declared method
3 years ago
mrdeveloper a320e6da17 History: don't show copy spend proof on incoming transactions
3 years ago
tobtoht c0b48af17d TransactionInfoDialog: add copy spendproof, in/outproof
3 years ago
tobtoht 6c6b77b0e7 FutureScheduler: update with upstream v0.17.1.3
3 years ago
tobtoht 06c46d95b2 Libwalletqt: update with upstream
3 years ago
mrdeveloper 74e2cfca63 Added cryptonote-social mining pool to XMRig
3 years ago
tobtoht 56ae31c0ad Update for v0.17.1.3
3 years ago
tobtoht 115bb84805 Stylesheets: Breeze: misc fixes
3 years ago
tobtoht e5983d9200 AppContext: updateBalance: don't throw runtime error
3 years ago
tobtoht cd053206e3 Settings: add hide balance feature
3 years ago
tobtoht 5690aef7db Utils: add blockchair.com block explorer
3 years ago
tobtoht b41a100e62 Coins: numerical sort for amount column
3 years ago
tobtoht 15067dd08f Move cdiv to globals.h
3 years ago
tobtoht e7a8c1fffe CCSWidget: fix misspelling
3 years ago
mrdeveloper 9d4ed50607 Simplify and remove white space on recipient (Pay to)
3 years ago
mrdeveloper f932ecae1d MacOS: add touchbar support
3 years ago
mrdeveloper e5105d395e BugFix: show message box if no wallet is selected
3 years ago
mrdeveloper 632c906b46 Check if given node URL has https so we can append https for get_info call
3 years ago
tobtoht cc122137dd Coins: disable sweep instead of hide when output unconfirmed
3 years ago
dsc 7c54b72a15 Include QtMultimedia for buildbots
3 years ago
dsc cb73367534 Write proper .exe on Windows
3 years ago
dsc 3b6d203e25 XMRig: Donate level 1
3 years ago
tobtoht 22ddf6f876 Mainwindow: fix tab order and stylization
3 years ago
tobtoht 248fdc3fbc XMRig: fix stop on windows
3 years ago
mrdeveloper 0f8e0179ec Make format csv import as same as export
3 years ago
mrdeveloper 86dcb51ada Contacts: import via csv file
3 years ago
mrdeveloper 47967f2def PasswordDialog: Show wallet name
3 years ago
tobtoht 63fc7cbcb4 TransactionInfoDialog: add tx key
3 years ago
tobtoht 4c79ea92c0 Update for v0.17.1.1
3 years ago
tobtoht a03be6e835 Offline transaction signing
3 years ago
dsc 2e6b8ddf75 - Embed XMRig executable (baked into buildbot image)
3 years ago
dsc 0191ad77c9 Xmrig: add support for TLS. Tor default off. Save username/password in wallet cache.
3 years ago
tobtoht fd2c0eb086 Bugfix: fix wallet closing issues by partially reverting #63
3 years ago
tobtoht 603d4eaa37 Prices: add support for NZD
3 years ago
tobtoht 2ab3dc2288 Fix indefinite hang when closing wallet via File -> Quit
3 years ago
tobtoht 6fed6956bb Drone: fix AppImage builds
3 years ago
dsc 0ad8ce5ea7 XMRig tab - Simple mining GUI to download and run XMRig releases.
3 years ago
tobtoht 5b47e63510 Send: show fiat conversion when XMR is selected
3 years ago
tobtoht 878239246e Tails: fix slow wallet open
3 years ago
tobtoht e20eb238c7 DebugInfoDialog: Add Tails version
3 years ago
tobtoht ac3f846ffd Fix wallet update regression
3 years ago
tobtoht 4899a440cc Export/Import KeyImages/Outputs
3 years ago
tobtoht 494e61fe71 Close wallet on exit
3 years ago
tobtoht 9ff44b7837 AboutDialog: minor layout fix
3 years ago
tobtoht 46fc2b4ec0 Coins: add key image icons for view only wallets
3 years ago
tobtoht d0659cffd0 Libwalletqt: integrate changes from upstream
3 years ago
tobtoht 30dbd543f8 Rebase to Monero Core v0.17.1.0
3 years ago
dsc 59480738d4 Import wallet from keys (view-only)
3 years ago
tobtoht 94db6933ec Fix segfault on close
3 years ago
tobtoht c1cd945d30 Fix misc image scaling hangs
3 years ago
dsc 9250846ba0 Commit cd1cd5cb75
3 years ago

@ -14,11 +14,9 @@ steps:
commands:
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
- git submodule update --init --depth 50 contrib/tor
- git submodule update --init --depth 50 contrib/torsocks
- git submodule update --init --depth 120 monero
- git submodule update --init --depth 120 --recursive monero
- make -j8 release-static
- TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make -j6 release-static
environment:
OPENSSL_ROOT_DIR: /usr/local/openssl/
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
@ -30,21 +28,21 @@ steps:
- name: files_linux_release
path: /files
commands:
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export FN="feather-wow-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
- strip -s build/bin/feather
- zip -j "$TARGET_DIR/$FN" build/feather.log build/bin/feather
- echo "[*] written to https://build.featherwallet.org/files/linux-release/$DRONE_SOURCE_BRANCH/$FN"
- strip -s build/bin/feather-wow
- zip -j "$TARGET_DIR/$FN" build/feather.log build/bin/feather-wow
- echo "[*] written to https://build.featherwallet.org/files-wow/linux-release/$DRONE_SOURCE_BRANCH/$FN"
volumes:
- name: ccache_linux_release
host:
path: /var/drone/ccache_linux_release/
path: /var/drone/ccache_wow_linux_release/
- name: files_linux_release
host:
path: /mnt/storage1/feather_files/files/linux-release/
path: /build/feather-wow_files/files-wow/linux-release/
---
@ -56,7 +54,7 @@ steps:
- name: build
image: feather:appimage
commands:
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export FN="feather-wow-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export BRANCH="$DRONE_SOURCE_BRANCH"
- bash ./contrib/build-appimage.sh
- name: deploy
@ -65,17 +63,18 @@ steps:
- name: files_linux_appimage
path: /files
commands:
- export FN="feather-`git rev-parse --short HEAD`.AppImage"
- find /drone/src/
- export FN="feather-wow-`git rev-parse --short HEAD`.AppImage"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
- mv "Feather-1.0-x86_64.AppImage" "$TARGET_DIR/$FN"
- echo "[*] written to https://build.featherwallet.org/files/linux-release-appimage/$DRONE_SOURCE_BRANCH/$FN"
- mv "Feather-WOW-1.0-x86_64.AppImage" "$TARGET_DIR/$FN"
- echo "[*] written to https://build.featherwallet.org/files-wow/linux-release-appimage/$DRONE_SOURCE_BRANCH/$FN"
volumes:
- name: files_linux_appimage
host:
path: /mnt/storage1/feather_files/files/linux-release-appimage/
path: /build/feather-wow_files/files-wow/linux-release-appimage/
---
@ -94,11 +93,9 @@ steps:
commands:
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
- git submodule update --init --depth 50 contrib/tor
- git submodule update --init --depth 50 contrib/torsocks
- git submodule update --init --depth 120 monero
- git submodule update --init --depth 120 --recursive monero
- PATH=/mxe/usr/bin/:$PATH make -j8 windows-mxe-release
- PATH="/mxe/usr/bin/:$PATH" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make -j6 windows-mxe-release
environment:
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
- name: deploy
@ -109,20 +106,20 @@ steps:
- name: files_win_release
path: /files
commands:
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export FN="feather-wow-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
- zip -j "$TARGET_DIR/$FN" build/feather.log build/bin/feather.exe
- echo "[*] written to https://build.featherwallet.org/files/windows-mxe-release/$DRONE_SOURCE_BRANCH/$FN"
- zip -j "$TARGET_DIR/$FN" build/feather.log build/bin/feather-wow.exe
- echo "[*] written to https://build.featherwallet.org/files-wow/windows-mxe-release/$DRONE_SOURCE_BRANCH/$FN"
volumes:
- name: ccache_win_release
host:
path: /var/drone/ccache_win_release/
path: /var/drone/ccache_wow_win_release/
- name: files_win_release
host:
path: /mnt/storage1/feather_files/files/windows-mxe-release/
path: /build/feather-wow_files/files-wow/windows-mxe-release/
---
@ -138,28 +135,28 @@ steps:
path: /files
commands:
- mkdir -p build
- scp -P22 utils/build_macos.sh administrator@steve.jobs.xmr.pm:build_macos.sh
- ssh administrator@steve.jobs.xmr.pm "chmod +x build_macos.sh && PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin ~/build_macos.sh $DRONE_COMMIT_SHA"
- scp -P22 administrator@steve.jobs.xmr.pm:feather.zip build/feather.zip
- scp -P22 utils/build_macos.sh administrator@steve.jobs.xmr.pm:build_wow_macos.sh
- ssh administrator@steve.jobs.xmr.pm "chmod +x build_wow_macos.sh && PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin ~/build_wow_macos.sh $DRONE_COMMIT_SHA"
- scp -P22 administrator@steve.jobs.xmr.pm:feather-wow.zip build/feather-wow.zip
- name: deploy
image: feather:mac
volumes:
- name: files_mac_release
path: /files
commands:
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export FN="feather-wow-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
- mv build/feather.zip "$TARGET_DIR/$FN"
- echo "[*] written to https://build.featherwallet.org/files/mac-release/$DRONE_SOURCE_BRANCH/$FN"
- mv build/feather-wow.zip "$TARGET_DIR/$FN"
- echo "[*] written to https://build.featherwallet.org/files-wow/mac-release/$DRONE_SOURCE_BRANCH/$FN"
volumes:
- name: files_mac_release
host:
path: /mnt/storage1/feather_files/files/mac-release/
path: /build/feather-wow_files/files-wow/mac-release/
---
kind: signature
hmac: 91e773a27d27f29ea62f5df500664b733df641b7c31202c8e5558174fd046fba
hmac: 527d334190a8a824b3b781a05ae4c7d87f4fa2bc37ebc53a96db91f925fa4a52
...

5
.gitmodules vendored

@ -1,9 +1,12 @@
[submodule "monero"]
path = monero
url = https://git.wownero.com/feather/monero.git
url = https://git.wownero.com/feather/wownero.git
[submodule "contrib/torsocks"]
path = contrib/torsocks
url = https://git.torproject.org/torsocks.git
[submodule "contrib/tor"]
path = contrib/tor
url = https://git.torproject.org/tor.git
[submodule "contrib/KDMacTouchBar"]
path = contrib/KDMacTouchBar
url = https://github.com/KDAB/KDMacTouchBar.git

@ -35,7 +35,7 @@ Note: You only need to build the base image once.
#### 3. Build
```bash
docker run --rm -it -v /tmp/ccache:/root/.ccache -v /root/feather:/feather -w /feather feather:win /bin/bash -c 'PATH=/mxe/usr/bin/:$PATH make windows-mxe-release -j8'
docker run --rm -it -v /tmp/ccache:/root/.ccache -v /root/feather:/feather -w /feather feather:win /bin/bash -c 'PATH="/mxe/usr/bin/:$PATH" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make windows-mxe-release -j8'
```
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
@ -68,7 +68,7 @@ Note: You only need to build the base image once.
#### 3. Build
```bash
docker run --env OPENSSL_ROOT_DIR=/usr/local/openssl/ --rm -it -v /tmp/ccache:/root/.ccache -v PATH_TO_FEATHER:/feather -w /feather feather:linux sh -c 'make release-static -j8'
docker run --env OPENSSL_ROOT_DIR=/usr/local/openssl/ --rm -it -v /tmp/ccache:/root/.ccache -v PATH_TO_FEATHER:/feather -w /feather feather:linux sh -c 'TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make release-static -j8'
```
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
@ -98,4 +98,4 @@ Build Feather.
CMAKE_PREFIX_PATH=~/Qt5.15.1/5.15.1/clang_64 make mac-release
```
The resulting Mac OS application can be found `build/bin/feather.app`.
The resulting Mac OS application can be found `build/bin/feather.app` and will **not** have Tor embedded.

@ -10,8 +10,10 @@ set(VERSION_REVISION "0")
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
option(FETCH_DEPS "Download dependencies if they are not found" ON)
option(XMRTO "Include Xmr.To module" ON)
option(BUILD_TOR "Build Tor" OFF)
option(XMRTO "Include Xmr.To module" OFF)
option(XMRIG "Path to XMRig binary to embed inside Feather" OFF)
option(TOR "Path to Tor binary to embed inside Feather" OFF)
option(TOR_VERSION "Optional git hash or tag of embedded Tor version" "tor-0.4.3.5")
option(STATIC "Link libraries statically, requires static Qt")
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
option(DONATE_BEG "Prompt donation window every once in a while" ON)
@ -28,7 +30,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
set(MONERO_HEAD "a1404e92cb439ba0f120e7c4a579ed0b9a0372a4")
set(WOWNERO_HEAD "9a58ac48d2f1ebdc94f168e63fdc30604058a7ca")
set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64")
set(BUILD_64 ON)
@ -81,11 +83,11 @@ endfunction()
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero OUTPUT_VARIABLE _MONERO_HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _MONERO_HEAD STREQUAL MONERO_HEAD)
message(FATAL_ERROR "[submodule] Monero HEAD was at ${_MONERO_HEAD} but should be at ${MONERO_HEAD}")
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] Monero HEAD @ ${MONERO_HEAD}")
message(STATUS "[submodule] Monero HEAD @ ${WOWNERO_HEAD}")
endif()
endif()
@ -158,6 +160,7 @@ find_package(Boost 1.58 REQUIRED COMPONENTS
if(UNIX AND NOT APPLE)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
# https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446
set(CMAKE_SKIP_RPATH ON)
endif()
@ -173,47 +176,48 @@ if(UNIX AND NOT APPLE)
endif()
endif()
# Tor/torsocks
set(TOR_TAG "tor-0.4.3.5")
set(TOR_DIR "${CMAKE_SOURCE_DIR}/contrib/tor")
if("$ENV{DRONE}" STREQUAL "true")
message(STATUS "We are inside a static compile with Drone CI")
endif()
if(BUILD_TOR AND APPLE)
execute_process(COMMAND bash -c "touch ${CMAKE_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
ENDIF()
# To build Feather with embedded (and static) Tor, pass CMake -DTOR=/path/to/tor
if(TOR)
if(APPLE)
execute_process(COMMAND bash -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
endif()
if(UNIX AND NOT APPLE)
set(TOR_LIB "libtorsocks.so")
elseif(APPLE)
set(TOR_LIB "libtorsocks.dylib")
# on the buildbot Tor is baked into the image
# - linux: See `Dockerfile`
# - windows: https://github.com/mxe/mxe/blob/1024dc7d2db5eb7d5d3c64a2c12b5f592572f1ce/plugins/apps/tor.mk
# - macos: taken from Tor Browser official release
set(TOR_COPY_CMD "cp ${TOR} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
message(STATUS "${TOR_COPY_CMD}")
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
if(ret EQUAL "1")
message(FATAL_ERROR "Tor copy failure: ${TOR_COPY_CMD}")
endif()
message(STATUS "Embedding Tor binary at ${TOR}")
else()
message(STATUS "Skipping Tor inclusion because -DTOR=Off")
endif()
if("$ENV{DRONE}" STREQUAL "true" AND APPLE)
message(STATUS "We are inside a static compile with Drone CI")
# @TODO: taken from Tor Browser official release for now
execute_process(COMMAND bash -c "cp ~/tor/libevent-2.1.7.dylib ${CMAKE_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
execute_process(COMMAND bash -c "cp ~/tor/tor ${CMAKE_SOURCE_DIR}/src/tor/tor")
elseif("$ENV{DRONE}" STREQUAL "true" AND BUILD_TOR)
message(STATUS "We are inside a static compile with Drone CI")
if(MINGW)
execute_process(COMMAND bash -c "cp /mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe ${CMAKE_SOURCE_DIR}/src/tor/tor.exe")
elseif(UNIX AND NOT APPLE)
execute_process(COMMAND bash -c "cp /usr/local/tor/bin/tor ${CMAKE_SOURCE_DIR}/src/tor/tor")
execute_process(COMMAND bash -c "cp /usr/local/torsocks/lib/torsocks/* ${CMAKE_SOURCE_DIR}/src/tor/")
# To build Feather with embedded (and static) XMRig, pass CMake -DXMRIG=/path/to/xmrig
if(XMRIG)
# on the buildbot XMRig is baked into the image
# - linux: See `Dockerfile`
# - windows: See `Dockerfile_windows`
# - macos: manually downloaded an official release
set(XMRIG_COPY_CMD "cp ${XMRIG} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/xmrig")
message(STATUS "${XMRIG_COPY_CMD}")
execute_process(COMMAND bash -c "${XMRIG_COPY_CMD}" RESULT_VARIABLE ret)
if(ret EQUAL "1")
message(FATAL_ERROR "XMRig copy failure: ${XMRIG_COPY_CMD}")
endif()
message(STATUS "Embedding XMRig binary at ${XMRIG}")
else()
if(BUILD_TOR)
if(UNIX OR APPLE)
execute_process(COMMAND bash -c "ls -al src/tor/${TOR_LIB} 2>/dev/null" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE TOR_FOUND OUTPUT_STRIP_TRAILING_WHITESPACE)
if(TOR_FOUND)
message(STATUS "${TOR_LIB} found, skipping Tor build")
else()
message(STATUS "${TOR_LIB} not found, building Tor")
execute_process(COMMAND bash -c "bash build_tor.sh ${TOR_TAG} ${CMAKE_SOURCE_DIR} 'ON'" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/contrib)
endif()
endif()
else()
message(STATUS "Skipping Tor build because -DBUILD_TOR=OFF")
endif()
message(STATUS "Skipping XMRig inclusion because -DXMRIG=Off")
endif()
if(MINGW)

@ -191,7 +191,7 @@ RUN cd /qt-everywhere-src-5.15.0 && \
-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 qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
-skip qtdeclarative -skip qtmultimedia \
-skip qtdeclarative \
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
-nomake examples -nomake tests -nomake tools
@ -305,3 +305,9 @@ RUN git clone https://git.wownero.com/feather/monero-seed.git && \
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
make -Cbuild -j$THREADS && \
make -Cbuild install
RUN apt install -y curl && \
curl -LO "https://github.com/xmrig/xmrig/releases/download/v6.3.5/xmrig-6.3.5-linux-static-x64.tar.gz" && \
echo "24d4f07cf5850f00ab513b228f95769a5a5ed68d35808d98f9959b58d97985a0 xmrig-6.3.5-linux-static-x64.tar.gz" > hashsum.txt && \
sha256sum -c hashsum.txt && \
tar xvf xmrig-6.3.5-linux-static-x64.tar.gz --one-top-level=/xmrig --strip 1

@ -50,7 +50,7 @@ RUN apt install -y \
RUN git clone -b feather-patch --depth 1 https://git.wownero.com/feather/mxe.git && \
cd mxe && \
make -j$THREADS MXE_TARGETS='x86_64-w64-mingw32.static' gcc libqrencode pkgconf libgpg_error libgcrypt cmake libsodium lzma readline libzmq boost qtbase qtsvg qtwebsockets qtimageformats
make -j$THREADS MXE_TARGETS='x86_64-w64-mingw32.static' gcc libqrencode pkgconf libgpg_error libgcrypt cmake libsodium lzma readline libzmq boost qtbase qtsvg qtwebsockets qtimageformats qtmultimedia
# plugins
RUN cd mxe && make -j$THREADS MXE_PLUGIN_DIRS='/mxe/plugins/apps/' MXE_TARGETS='x86_64-w64-mingw32.static' tor
@ -72,3 +72,10 @@ RUN git clone https://git.wownero.com/feather/monero-seed.git && \
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
make -Cbuild -j$THREADS && \
make -Cbuild install
RUN apt install -y curl && \
curl -LO "https://github.com/xmrig/xmrig/releases/download/v6.3.5/xmrig-6.3.5-gcc-win64.zip" && \
echo "e45915ada7e6e30f6ab40abf33831056449d5914307d7706bb0ad439b6d64c12 xmrig-6.3.5-gcc-win64.zip" > hashsum.txt && \
sha256sum -c hashsum.txt && \
unzip -q xmrig-6.3.5-gcc-win64.zip -d /xmrig && \
mv /xmrig/xmrig-6.3.5/* /xmrig/

@ -42,7 +42,9 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
There are some Monero/Feather related options/definitions that you may pass:
- `-DXMRTO=OFF` - disable Xmr.To feature
- `-DBUILD_TOR=OFF` - disable embedded Tor
- `-DMORPHTOKEN=OFF` - diable MorphToken feature
- `-DTOR=/path/to/tor` - Embed a Tor executable inside Feather
- `-DXMRIG=/path/to/xmrig` - Embed a XMRig executable inside Feather
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
And:

@ -30,7 +30,9 @@ CMAKEFLAGS = \
-DARCH=x86_64 \
-DBUILD_64=On \
-DBUILD_TESTS=Off \
-DXMRTO=ON \
-DXMRTO=Off \
-DXMRIG=Off \
-DTOR=Off \
-DCMAKE_CXX_STANDARD=11 \
-DCMAKE_VERBOSE_MAKEFILE=On \
-DINSTALL_VENDORED_LIBUNBOUND=Off \
@ -40,31 +42,35 @@ CMAKEFLAGS = \
$(CMAKEFLAGS_EXTRA)
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
release-static: CMAKEFLAGS += -DBUILD_TOR=On
release-static: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
release-static: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
release-static:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
windows-mxe-release: CMAKEFLAGS += -DBUILD_TOR=On
windows-mxe-release: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
windows-mxe-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
windows-mxe-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
windows-mxe-release:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TAG="win-x64"
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TOR=Off
windows-mxe-debug: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
windows-mxe-debug: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
windows-mxe-debug: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Debug
windows-mxe-debug:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
mac-release: CMAKEFLAGS += -DSTATIC=Off
mac-release: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
mac-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
mac-release: CMAKEFLAGS += -DBUILD_TOR=Off
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
mac-release:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
$(MAKE) -Cbuild deploy
$(MAKE) -Cbuild deploy

@ -1,16 +1,15 @@
# Maintainer: wowario <wowario[at]protonmail[dot]com>
# Contributor: wowario <wowario[at]protonmail[dot]com>
pkgbase=('monero-feather-git')
pkgname=('monero-feather-git')
pkgver=v0.1.0.0.cd1cd5cb75
pkgrel=1
pkgdesc="a free Monero desktop wallet"
pkgbase='monero-feather-git'
pkgname='monero-feather-git'
pkgver='v0.1.0.0.ca5b1df7df'
pkgrel='1'
pkgdesc='a free Monero desktop wallet'
license=('BSD')
arch=('x86_64')
url="https://featherwallet.org"
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'zeromq' 'pcsclite' 'hidapi' 'protobuf' 'miniupnpc'
'libgcrypt' 'qrencode' 'ccache' 'libsodium' 'libpgm' 'expat' 'base-devel' 'qt5-base')
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'zeromq' 'pcsclite' 'hidapi' 'protobuf' 'miniupnpc' 'libgcrypt' 'qrencode' 'ccache' 'libsodium' 'libpgm' 'expat' 'qt5-base' 'qt5-websockets' 'tor')
makedepends=('git' 'cmake' 'boost')
provides=('monero-feather-git')
@ -18,17 +17,16 @@ source=("${pkgname}"::"git+https://git.wownero.com/feather/feather")
sha256sums=('SKIP')
pkgver() {
cd "$srcdir/$pkgname"
git describe --long --tags | sed 's/\([^-]*-\)g/r\1/;s/-/./g'
}
build() {
cd "${srcdir}/${pkgname}"
mkdir build && cd build && cmake .. && make release-static -j2
git submodule update --init --recursive
mkdir build
cd build
cmake ..
make -j2
}
package_feather-git() {
package_monero-feather-git() {
install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
install -Dm755 "${srcdir}/${pkgname}/build/bin/feather" "${pkgdir}/usr/bin/feather"
}

@ -1,35 +1,13 @@
# Feather - a free Monero desktop wallet
# Feather-WOW - a free Wownero desktop wallet
[![Build Status](https://build.featherwallet.org/api/badges/feather/feather/status.svg)](https://build.featherwallet.org/feather/feather)
[![Build Status](https://build.featherwallet.org/api/badges/feather/feather-wow/status.svg)](https://build.featherwallet.org/feather/feather-wow)
Feather is a free, open-source Monero client Linux with ports for Mac OS and Windows written in C++ with the Qt framework. It is created and maintained by [dsc](dsc@xmr.pm) and [tobtoht](thotbot@protonmail.com).
Feather is a free, open-source Wownero client for Linux with ports for Mac OS and Windows.
## Development resources
* Web: [featherwallet.org](https://featherwallet.org)
* Git: [git.wownero.com/feather/feather](https://git.wownero.com/feather/feather)
* Git: [git.wownero.com/feather/feather-wow](https://git.wownero.com/feather/feather-wow)
* IRC: `#feather` on OFTC
* Development builds: [build.featherwallet.org/files](https://build.featherwallet.org/files/)
* Development builds: [build.featherwallet.org/files-wow](https://build.featherwallet.org/files-wow/)
Copyright (c) 2020-2021 The Monero Project.
## Compiling Feather from source
Feather uses Monero, as such it requires the same dependencies as outlined in [Monero's README](https://github.com/monero-project/monero#compiling-monero-from-source). Additionally, Feather uses:
- Qt 5.15.0
- libqrencode
- openpgp
See [BUILDING.md](https://git.wownero.com/feather/feather/src/branch/master/BUILDING.md) for information on how to compile a build.
## Supporting the project
Feather is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially.
`47ntfT2Z5384zku39pTM6hGcnLnvpRYW2Azm87GiAAH2bcTidtq278TL6HmwyL8yjMeERqGEBs3cqC8vvHPJd1cWQrGC65f`
## Developers
See [HACKING.md](https://git.wownero.com/feather/feather/src/branch/master/HACKING.md) for useful development resources.
It is HIGHLY recommended that you join the `#feather` IRC channel on OFTC if you are hacking on Feather. Due to the nature of this open source software project, joining this channel and idling is the best way to stay updated on best practices and new developments.

@ -7,7 +7,7 @@ if(APPLE OR (WIN32 AND NOT STATIC))
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}")
add_custom_command(TARGET deploy
POST_BUILD
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "$<TARGET_FILE_DIR:feather>/../.." -always-overwrite
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "$<TARGET_FILE_DIR:feather-wow>/../.." -always-overwrite
COMMENT "Running macdeployqt..."
)
@ -16,11 +16,11 @@ if(APPLE OR (WIN32 AND NOT STATIC))
if(_qt_svg_dylib)
add_custom_command(TARGET deploy
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${_qt_svg_dylib} $<TARGET_FILE_DIR:feather>/../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:feather>/../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:feather>/../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:feather>/../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:feather>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_COMMAND} -E copy ${_qt_svg_dylib} $<TARGET_FILE_DIR:feather-wow>/../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:feather-wow>/../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:feather-wow>/../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:feather-wow>/../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:feather-wow>/../PlugIns/imageformats/libqsvg.dylib
COMMENT "Copying libqsvg.dylib, running install_name_tool"
)
endif()

@ -0,0 +1 @@
Subproject commit 470c4316460bb8c3e23bfa37c79c8621ef3f1b4c

@ -21,20 +21,20 @@ function download_if_not_exist() {
fi
}
APPDIR="$PWD/feather.AppDir"
APPDIR="$PWD/feather-wow.AppDir"
mkdir -p "$APPDIR"
mkdir -p "$APPDIR/usr/share/applications/"
mkdir -p "$APPDIR/usr/bin"
echo "Downloading dependencies"
download_if_not_exist "feather.zip" "https://build.featherwallet.org/files/linux-release/$BRANCH/$FN"
unzip -q feather.zip
download_if_not_exist "feather-wow.zip" "https://build.featherwallet.org/files-wow/linux-release/$BRANCH/$FN"
unzip -q feather-wow.zip
cp "$PWD/src/assets/feather.desktop" "$APPDIR/usr/share/applications/feather.desktop"
cp "$PWD/src/assets/images/appicons/64x64.png" "$APPDIR/feather.png"
cp "$PWD/feather" "$APPDIR/usr/bin/feather"
cp "$PWD/src/assets/feather-wow.desktop" "$APPDIR/usr/share/applications/feather-wow.desktop"
cp "$PWD/src/assets/images/appicons/64x64.png" "$APPDIR/feather-wow.png"
cp "$PWD/feather-wow" "$APPDIR/usr/bin/feather-wow"
/appimagetool deploy "$APPDIR/usr/share/applications/feather.desktop"
VERSION=1.0 /appimagetool ./feather.AppDir
/appimagetool deploy "$APPDIR/usr/share/applications/feather-wow.desktop"
VERSION=1.0 /appimagetool ./feather-wow.AppDir

@ -1 +1 @@
Subproject commit a1404e92cb439ba0f120e7c4a579ed0b9a0372a4
Subproject commit 9a58ac48d2f1ebdc94f168e63fdc30604058a7ca

@ -42,14 +42,16 @@ file(GLOB SOURCE_FILES
"dialog/*.cpp"
)
if((APPLE AND BUILD_TOR) OR (APPLE AND "$ENV{DRONE}" STREQUAL "true"))
set(ASSETS_OS "assets_macos_tor.qrc")
elseif(UNIX AND NOT APPLE AND BUILD_TOR)
set(ASSETS_OS "assets_linux_tor.qrc")
elseif(MINGW AND BUILD_TOR)
set(ASSETS_OS "assets_windows_tor.qrc")
else()
message(STATUS "Building without embedded Tor")
if(TOR)
if(APPLE)
set(ASSETS_TOR "assets_tor_macos.qrc")
else()
set(ASSETS_TOR "assets_tor.qrc")
endif()
endif()
if(XMRIG)
set(ASSETS_XMRIG "assets_mining.qrc")
endif()
set(EXECUTABLE_FLAG)
@ -71,28 +73,29 @@ if(APPLE)
list(APPEND RESOURCES ${ICON})
endif()
add_executable(feather ${EXECUTABLE_FLAG} main.cpp
add_executable(feather-wow ${EXECUTABLE_FLAG} main.cpp
${SOURCE_FILES}
${RESOURCES}
${ASSETS_OS}
${ASSETS_TOR}
${ASSETS_XMRIG}
)
# mac os bundle
set_target_properties(feather PROPERTIES
set_target_properties(feather-wow PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/utils/Info.plist"
)
set_property(TARGET feather PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set_property(TARGET feather-wow PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
target_include_directories(feather PUBLIC ${OPENGL_INCLUDE_DIR})
target_include_directories(feather PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
target_include_directories(feather-wow PUBLIC ${OPENGL_INCLUDE_DIR})
target_include_directories(feather-wow PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
file(GLOB_RECURSE SRC_SOURCES *.cpp)
file(GLOB_RECURSE SRC_HEADERS *.h)
target_include_directories(feather PUBLIC
target_include_directories(feather-wow PUBLIC
${CMAKE_BINARY_DIR}/src/feather_autogen/include
${CMAKE_SOURCE_DIR}/monero/include
${CMAKE_SOURCE_DIR}/monero/src
@ -119,34 +122,46 @@ target_include_directories(feather PUBLIC
)
if(DONATE_BEG)
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
target_compile_definitions(feather-wow PRIVATE DONATE_BEG=1)
endif()
if(XMRTO)
target_compile_definitions(feather-wow PRIVATE XMRTO=1)
endif()
if(BUILD_TOR)
target_compile_definitions(feather PRIVATE BUILD_TOR=1)
if(MORPHTOKEN)
target_compile_definitions(feather PRIVATE HAS_MORPHTOKEN=1)
endif()
if(XMRTO)
target_compile_definitions(feather PRIVATE XMRTO=1)
if(TOR)
target_compile_definitions(feather-wow PRIVATE HAS_TOR=1)
endif()
if(XMRIG)
target_compile_definitions(feather-wow PRIVATE HAS_XMRIG=1)
endif()
if(HAVE_SYS_PRCTL_H)
target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1)
target_compile_definitions(feather-wow PRIVATE HAVE_SYS_PRCTL_H=1)
endif()
if(STATIC)
target_compile_definitions(feather PRIVATE STATIC=1)
target_compile_definitions(feather-wow PRIVATE STATIC=1)
endif()
if(STATIC)
target_compile_definitions(feather-wow PRIVATE STATIC=1)
endif()
if("$ENV{DRONE}" STREQUAL "true")
target_compile_definitions(feather PRIVATE DRONE=1)
target_compile_definitions(feather-wow PRIVATE DRONE=1)
endif()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(feather PRIVATE QT_NO_DEBUG=1)
target_compile_definitions(feather-wow PRIVATE QT_NO_DEBUG=1)
endif()
target_compile_definitions(feather
target_compile_definitions(feather-wow
PUBLIC
${Qt5Core_DEFINITIONS}
${Qt5Widgets_DEFINITIONS}
@ -162,12 +177,12 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
if(UNIX AND NOT APPLE)
# 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.
target_link_libraries(feather -Wl,--whole-archive monero-seed::monero-seed -Wl,--no-whole-archive)
target_link_libraries(feather-wow -Wl,--whole-archive monero-seed::monero-seed -Wl,--no-whole-archive)
else()
target_link_libraries(feather monero-seed::monero-seed)
target_link_libraries(feather-wow monero-seed::monero-seed)
endif()
target_link_libraries(feather
target_link_libraries(feather-wow
wallet_merged
${LMDB_LIBRARY}
epee
@ -194,31 +209,39 @@ target_link_libraries(feather
${QRENCODE_LIBRARY}
)
if(NOT APPLE)
if(APPLE)
target_link_libraries(feather
KDMacTouchBar
)
target_include_directories(feather
PUBLIC ../contrib/KDMacTouchBar)
endif()
if(NOT APPLE)
target_link_libraries(feather-wow
Qt5::QSvgIconPlugin
Qt5::QSvgPlugin
)
endif()
if(STATIC)
target_link_libraries(feather
target_link_libraries(feather-wow
Qt5::QSvgIconPlugin
Qt5::QSvgPlugin)
if(UNIX AND NOT APPLE)
target_link_libraries(feather
target_link_libraries(feather-wow
Qt5::QXcbIntegrationPlugin)
endif()
endif()
if(X11_FOUND)
target_link_libraries(feather ${X11_LIBRARIES})
target_link_libraries(feather-wow ${X11_LIBRARIES})
endif()
if(APPLE)
include(Deploy)
endif()
install(TARGETS feather
install(TARGETS feather-wow
DESTINATION ${CMAKE_INSTALL_PREFIX}
)

@ -0,0 +1,226 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include "MorphTokenWidget.h"
#include "ui_MorphTokenWidget.h"
#include "mainwindow.h"
#include "qrcode/QrCode.h"
#include "dialog/qrcodedialog.h"
#include <QMessageBox>
MorphTokenWidget::MorphTokenWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MorphTokenWidget)
{
ui->setupUi(this);
m_ctx = MainWindow::getContext();
m_network = new UtilsNetworking(this->m_ctx->network);
m_api = new MorphTokenApi(this, m_network);
connect(ui->btnCreateTrade, &QPushButton::clicked, this, &MorphTokenWidget::createTrade);
connect(ui->btn_lookupTrade, &QPushButton::clicked, this, &MorphTokenWidget::lookupTrade);
connect(ui->btn_getRates, &QPushButton::clicked, this, &MorphTokenWidget::getRates);
connect(m_api, &MorphTokenApi::ApiResponse, this, &MorphTokenWidget::onApiResponse);
connect(ui->combo_From, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
this->displayRate();
ui->label_refundAddress->setText(QString("Refund address (%1):").arg(ui->combo_From->currentText()));
});
connect(ui->combo_To, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
this->displayRate();
ui->label_destinationAddress->setText(QString("Destination address (%1):").arg(ui->combo_To->currentText()));
});
connect(ui->check_autorefresh, &QCheckBox::toggled, [this](bool toggled){
m_countdown = 30;
toggled ? m_countdownTimer.start(1000) : m_countdownTimer.stop();
ui->check_autorefresh->setText("Autorefresh");
});
connect(&m_countdownTimer, &QTimer::timeout, this, &MorphTokenWidget::onCountdown);
connect(ui->line_Id, &QLineEdit::textChanged, [this](const QString &text){
ui->btn_lookupTrade->setEnabled(!text.isEmpty());
ui->check_autorefresh->setEnabled(!text.isEmpty());
});
// Default to BTC -> XMR
ui->combo_From->setCurrentIndex(1);
ui->combo_To->setCurrentIndex(0);
ui->label_rate->setVisible(false);
m_ratesTimer.setSingleShot(true);
connect(&m_ratesTimer, &QTimer::timeout, [this]{
ui->label_rate->setVisible(false);
});
ui->qrCode->setVisible(false);
ui->label_depositAddress->setVisible(false);
connect(ui->qrCode, &ClickableLabel::clicked, this, &MorphTokenWidget::showQrCodeDialog);
ui->tabWidget->setTabVisible(2, false);
}
void MorphTokenWidget::createTrade() {
QString inputAsset = ui->combo_From->currentText();
QString outputAsset = ui->combo_To->currentText();
QString refundAddress = ui->line_refundAddress->text();
QString destinationAddress = ui->line_destinationAddress->text();
m_api->createTrade(inputAsset, outputAsset, refundAddress, destinationAddress);
}
void MorphTokenWidget::lookupTrade() {
QString morphId = ui->line_Id->text();
if (!morphId.isEmpty())
m_api->getTrade(morphId);
}
void MorphTokenWidget::getRates() {
m_api->getRates();
}
void MorphTokenWidget::onApiResponse(const MorphTokenApi::MorphTokenResponse &resp) {
if (!resp.ok) {
ui->check_autorefresh->setChecked(false);
QMessageBox::warning(this, "MorphToken error", QString("Request failed:\n\n%1").arg(resp.message));
return;
}
ui->debugInfo->setPlainText(QJsonDocument(resp.obj).toJson(QJsonDocument::Indented));
bool shouldShowQr = (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE);
ui->qrCode->setVisible(shouldShowQr);
ui->label_depositAddress->setVisible(shouldShowQr);
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE) {
ui->tabWidget->setCurrentIndex(1);
ui->line_Id->setText(resp.obj.value("id").toString());
auto obj = resp.obj;
auto input = obj["input"].toObject();
auto output = obj["output"].toArray()[0].toObject();
QString state = obj.value("state").toString();
QString statusText;
ui->trade->setTitle(QString("Trade (%1)").arg(state));
statusText += QString("Morph ID: %1\n\n").arg(obj["id"].toString());
if (state == "PENDING") {
statusText += QString("Waiting for a deposit, send %1 to %2\n").arg(input["asset"].toString(),
input["deposit_address"].toString());
statusText += QString("Rate: 1 %1 -> %2 %3\n\n").arg(input["asset"].toString(),
output["seen_rate"].toString(),
output["asset"].toString());
statusText += "Limits:\n";
statusText += QString(" Minimum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["min"].toDouble()),
input["asset"].toString());
statusText += QString(" Maximum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["max"].toDouble()),
input["asset"].toString());
statusText += QString("\nSend a single deposit. If the amount is outside the limits, a refund will happen.");
m_depositAddress = input["deposit_address"].toString();
const QrCode qrc(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
int width = ui->qrCode->width();
if (qrc.isValid()) {
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
}
} else if (state == "PROCESSING" || state == "TRADING" || state == "CONFIRMING") {
if (state == "CONFIRMING") {
statusText += QString("Waiting for confirmations\n");
} else if (state == "TRADING") {
statusText += QString("Your transaction has been received and is confirmed. MorphToken is now executing your trade.\n"
"Usually this step takes no longer than a minute, "
"but in rare cases it can take a couple hours.\n"
"Wait a bit before contacting support.\n");
}
statusText += QString("Converting %1 to %2\n").arg(input["asset"].toString(), output["asset"].toString());
statusText += QString("Sending to %1\n").arg(output["address"].toString());
statusText += QString("Stuck? Contact support at contact@morphtoken.com");
} else if (state == "COMPLETE") {
if (output["txid"].toString().isEmpty()) {
statusText += QString("MorphToken is sending your transaction.\n");
statusText += QString("MorphToken will send %1 %2 to %2").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
output["asset"].toString(),
output["address"].toString());
} else {
statusText += QString("Sent %1 %2 to %3\ntxid: {}").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
output["asset"].toString(),
output["address"].toString(),
output["txid"].toString());
}
} else if (state == "PROCESSING_REFUND" || state == "COMPLETE_WITH_REFUND") {
statusText += QString("MorphToken will refund %1 %2\nReason: %3\n").arg(obj["final_amount"].toString(),
obj["asset"].toString(),
obj["reason"].toString());
if (obj.contains("txid")) {
statusText += QString("txid: %1").arg(obj["txid"].toString());
}
} else if (state == "COMPLETE_WITHOUT_REFUND") {
statusText += "Deposit amount below network fee, too small to refund.";
}
ui->label_status->setText(statusText);
} else if (resp.endpoint == MorphTokenApi::Endpoint::GET_RATES) {
m_rates = resp.obj.value("data").toObject();
this->displayRate();
ui->label_rate->setVisible(true);
m_ratesTimer.start(120 * 1000);
}
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE) {
QMessageBox::information(this, "MorphToken", "Trade created!\n\nMake sure to save your Morph ID. You may need it in case something goes wrong.");
}
}
void MorphTokenWidget::onCountdown() {
if (m_countdown > 0) {
m_countdown -= 1;
} else {
this->lookupTrade();
m_countdown = 30;
}
ui->check_autorefresh->setText(QString("Autorefresh (%1)").arg(m_countdown));
}
void MorphTokenWidget::displayRate() {
QString inputAsset = ui->combo_From->currentText();
QString outputAsset = ui->combo_To->currentText();
QString outputRate = m_rates.value(inputAsset).toObject().value(outputAsset).toString("1");
QString rateStr = QString("1 %1 -> %2 %3").arg(inputAsset, outputRate, outputAsset);
ui->label_rate->setText(rateStr);
}
void MorphTokenWidget::showQrCodeDialog() {
QrCode qr(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
auto *dialog = new QrCodeDialog(this, qr, "Deposit address");
dialog->exec();
dialog->deleteLater();
}
QString MorphTokenWidget::formatAmount(const QString &asset, double amount) {
double displayAmount;
double div;
if (asset == "ETH")
div = 1e18;
else if (asset == "XMR")
div = 1e12;
else
div = 1e8;
displayAmount = amount / div;
return QString::number(displayAmount, 'f', 8);
}
MorphTokenWidget::~MorphTokenWidget() {
delete ui;
}

@ -0,0 +1,47 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_MORPHTOKENWIDGET_H
#define FEATHER_MORPHTOKENWIDGET_H
#include <QWidget>
#include "appcontext.h"
#include "utils/MorphTokenApi.h"
namespace Ui {
class MorphTokenWidget;
}
class MorphTokenWidget : public QWidget
{
Q_OBJECT
public:
explicit MorphTokenWidget(QWidget *parent = nullptr);
~MorphTokenWidget() override;
private:
void createTrade();
void lookupTrade();
void getRates();
void onApiResponse(const MorphTokenApi::MorphTokenResponse &resp);
void onCountdown();
void displayRate();
void showQrCodeDialog();
QString formatAmount(const QString &asset, double amount);
Ui::MorphTokenWidget *ui;
AppContext *m_ctx;
MorphTokenApi *m_api;
UtilsNetworking *m_network;
QTimer m_countdownTimer;
int m_countdown = 30;
QJsonObject m_rates;
QTimer m_ratesTimer;
QString m_depositAddress;
};
#endif //FEATHER_MORPHTOKENWIDGET_H

@ -0,0 +1,423 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MorphTokenWidget</class>
<widget class="QWidget" name="MorphTokenWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1036</width>
<height>614</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tabCreateTrade">
<attribute name="title">
<string>Create trade</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>From:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combo_From">
<item>
<property name="text">
<string>XMR</string>
</property>
</item>
<item>
<property name="text">
<string>BTC</string>
</property>
</item>
<item>
<property name="text">
<string>ETH</string>
</property>
</item>
<item>
<property name="text">
<string>BCH</string>
</property>
</item>
<item>
<property name="text">
<string>LTC</string>
</property>
</item>
<item>
<property name="text">
<string>DASH</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>To:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="combo_To">
<item>
<property name="text">
<string>XMR</string>
</property>
</item>
<item>
<property name="text">
<string>BTC</string>
</property>
</item>
<item>
<property name="text">
<string>ETH</string>
</property>
</item>
<item>
<property name="text">
<string>BCH</string>
</property>
</item>
<item>
<property name="text">
<string>LTC</string>
</property>
</item>
<item>
<property name="text">
<string>DASH</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_rate">
<property name="text">
<string>rate</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_refundAddress">
<property name="text">
<string>Refund address (XMR):</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="line_refundAddress"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_destinationAddress">
<property name="text">
<string>Destination address (XMR):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="line_destinationAddress"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Powered by MorphToken.com</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_getRates">
<property name="text">
<string>Get Rates</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCreateTrade">
<property name="text">
<string>Create Trade</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabLookupTrade">
<attribute name="title">
<string>Lookup trade</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Morph ID or MorphToken deposit address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_Id"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="check_autorefresh">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Autorefresh</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_lookupTrade">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Lookup trade</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="trade">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Trade</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_status">
<property name="text">
<string>No trade loaded.</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="ClickableLabel" name="qrCode">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>qrcode</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_depositAddress">
<property name="text">
<string>Deposit address</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabDebug">
<attribute name="title">
<string>Debug</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QPlainTextEdit" name="debugInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ClickableLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

@ -9,12 +9,14 @@
#include <QDesktopWidget>
#include "appcontext.h"
#include "globals.h"
#include "utils/tails.h"
#include "utils/whonix.h"
#include "utils/utils.h"
#include "utils/prices.h"
#include "utils/networktype.h"
#include "utils/wsclient.h"
#include "utils/config.h"
// libwalletqt
#include "libwalletqt/WalletManager.h"
@ -28,7 +30,6 @@
#include "model/SubaddressModel.h"
#include "utils/keysfiles.h"
#include "utils/networktype.h"
#include "utils/config.h"
Prices *AppContext::prices = nullptr;
@ -60,11 +61,11 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
QString appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
qDebug() << "Not an appimage, using currentPath()";
return QDir::currentPath() + "/.feather";
return QDir::currentPath() + "/.feather-wow";
}
QFileInfo appImageDir(appImagePath);
return appImageDir.absoluteDir().path() + "/.feather";
return appImageDir.absoluteDir().path() + "/.feather-wow";
}();
@ -79,10 +80,10 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->homeDir = QDir::homePath();
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC)
this->defaultWalletDir = QString("%1/Monero/wallets").arg(this->configRoot);
this->defaultWalletDirRoot = QString("%1/Monero").arg(this->configRoot);
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->configRoot);
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->configRoot);
#elif defined(Q_OS_WIN)
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Monero";
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Wownero";
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
#endif
@ -90,7 +91,7 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
if (!QDir().mkpath(defaultWalletDir))
qCritical() << "Unable to create dir: " << defaultWalletDir;
this->configDirectory = QString("%1/.config/feather/").arg(this->configRoot);
this->configDirectory = QString("%1/.config/feather-wow/").arg(this->configRoot);
#if defined(Q_OS_UNIX)
if(!this->configDirectory.endsWith('/'))
this->configDirectory = QString("%1/").arg(this->configDirectory);
@ -100,19 +101,18 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// Config
createConfigDirectory(this->configDirectory);
if(this->cmdargs->isSet("stagenet"))
this->networkType = NetworkType::STAGENET;
else if(this->cmdargs->isSet("testnet"))
this->networkType = NetworkType::TESTNET;
else
this->networkType = NetworkType::MAINNET;
// if(this->cmdargs->isSet("stagenet"))
// this->networkType = NetworkType::STAGENET;
// else if(this->cmdargs->isSet("testnet"))
// this->networkType = NetworkType::TESTNET;
// else
this->networkType = NetworkType::MAINNET;
// auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt();
// AppContext::nodeSource = static_cast<NodeSource>(nodeSourceUInt);
this->nodes = new Nodes(this, this->networkClearnet);
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
connect(this, &AppContext::walletClosing, this->nodes, &Nodes::onWalletClosing);
// Tor & socks proxy
this->ws = new WSClient(this, m_wsUrl);
@ -147,10 +147,16 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
AppContext::prices = new Prices();
// xmr.to
#if defined(XMRTO)
#ifdef XMRTO
this->XMRTo = new XmrTo(this);
#endif
// XMRig
#ifdef HAS_XMRIG
this->XMRig = new XmRig(this->configDirectory, this);
this->XMRig->prepare();
#endif
this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDirectory);
Monero::Utils::onStartup();
@ -173,7 +179,7 @@ void AppContext::initTor() {
this->tor = new Tor(this, this);
this->tor->start();
if (!(isTails || isWhonix)) {
if (!(isWhonix)) {
auto networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy);
if (m_wsUrl.host().endsWith(".onion"))
@ -188,7 +194,7 @@ void AppContext::initWS() {
void AppContext::onCancelTransaction(PendingTransaction *tx, const QString &address) {
// tx cancelled by user
double amount = tx->amount() / AppContext::cdiv;
double amount = tx->amount() / globals::cdiv;
emit createTransactionCancelled(address, amount);
this->currentWallet->disposeTransaction(tx);
}
@ -227,8 +233,8 @@ void AppContext::onCreateTransaction(const QString &address, const double amount
return;
}
auto balance = this->currentWallet->balance() / AppContext::cdiv;
auto unlocked_balance = this->currentWallet->unlockedBalance() / AppContext::cdiv;
auto balance = this->currentWallet->balance() / globals::cdiv;
auto unlocked_balance = this->currentWallet->unlockedBalance() / globals::cdiv;
if(!all && amount > unlocked_balance) {
emit createTransactionError("Not enough money to spend");
return;
@ -237,7 +243,7 @@ void AppContext::onCreateTransaction(const QString &address, const double amount
return;
}
auto amount_num = static_cast<quint64>(amount * AppContext::cdiv);
auto amount_num = static_cast<quint64>(amount * globals::cdiv);
qDebug() << "creating tx";
if(all || amount == balance)
this->currentWallet->createTransactionAllAsync(address, "", this->tx_mixin, this->tx_priority);
@ -253,7 +259,6 @@ void AppContext::onCreateTransactionError(const QString &msg) {
}
void AppContext::walletClose(bool emitClosedSignal) {
this->nodes->stopTimer();
if(this->currentWallet == nullptr) return;
emit walletClosing();
//ctx->currentWallet->store(); @TODO: uncomment to store on wallet close
@ -276,6 +281,10 @@ void AppContext::onOpenWallet(const QString &path, const QString &password){
return;
}
if (password.isEmpty()) {
this->walletPassword = "";
}
config()->set(Config::firstRun, false);
this->walletPath = path;
@ -310,13 +319,14 @@ void AppContext::onWalletOpened(Wallet *wallet) {
emit walletOpenedError(errMsg);
} else {
this->walletClose(false);
emit walletOpenPasswordNeeded(this->walletPassword.isEmpty());
emit walletOpenPasswordNeeded(!this->walletPassword.isEmpty(), wallet->path());
}
return;
}
this->currentWallet = wallet;
this->walletPath = this->currentWallet->path() + ".keys";
this->walletViewOnly = this->currentWallet->viewOnly();
config()->set(Config::walletPath, this->walletPath);
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
@ -330,9 +340,12 @@ void AppContext::onWalletOpened(Wallet *wallet) {
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
connect(this->currentWallet, &Wallet::connectionStatusChanged, this, &AppContext::onConnectionStatusChanged);
this->nodes->connectToNode();
emit walletOpened();
connect(this->currentWallet, &Wallet::connectionStatusChanged, [this]{
this->nodes->autoConnect();
});
this->nodes->connectToNode();
this->updateBalance();
#ifdef DONATE_BEG
@ -341,6 +354,18 @@ void AppContext::onWalletOpened(Wallet *wallet) {
// force trigger preferredFiat signal for history model
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
this->setWindowTitle();
}
void AppContext::setWindowTitle(bool mining) {
QFileInfo fileInfo(this->walletPath);
auto title = QString("Feather - [%1]").arg(fileInfo.fileName());
if(this->walletViewOnly)
title += " [view-only]";
if(mining)
title += " [mining]";
emit setTitle(title);
}
void AppContext::onWSMessage(const QJsonObject &msg) {
@ -380,7 +405,11 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
else if(cmd == "nodes") {
this->onWSNodes(msg.value("data").toArray());
}
#if defined(HAS_XMRIG)
else if(cmd == "xmrig") {
this->XMRigDownloads(msg.value("data").toObject());
}
#endif
else if(cmd == "crypto_rates") {
QJsonArray crypto_rates = msg.value("data").toArray();
AppContext::prices->cryptoPricesReceived(crypto_rates);
@ -401,7 +430,7 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
this->onWSReddit(reddit_data);
}
else if(cmd == "ccs") {
else if(cmd == "wfs") {
auto ccs_data = msg.value("data").toArray();
this->onWSCCS(ccs_data);
}
@ -434,7 +463,8 @@ void AppContext::onWSNodes(const QJsonArray &nodes) {
auto node = new FeatherNode(
obj.value("address").toString(),
(unsigned int)obj.value("height").toInt(),
obj.value("height").toInt(),
obj.value("target_height").toInt(),
obj.value("online").toBool());
QSharedPointer<FeatherNode> r = QSharedPointer<FeatherNode>(node);
l.append(r);
@ -494,31 +524,17 @@ void AppContext::onWSCCS(const QJsonArray &ccs_data) {
}
void AppContext::createConfigDirectory(const QString &dir) {
if(!Utils::dirExists(dir)) {
qDebug() << QString("Creating directory: %1").arg(dir);
if(!QDir().mkpath(dir))
throw std::runtime_error("Could not create directory " + dir.toStdString());
}
QString config_dir_tor = QString("%1%2").arg(dir).arg("tor");
if(!Utils::dirExists(config_dir_tor)) {
qDebug() << QString("Creating directory: %1").arg(config_dir_tor);
if (!QDir().mkpath(config_dir_tor))
throw std::runtime_error("Could not create directory " + config_dir_tor.toStdString());
}
QString config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
if(!Utils::dirExists(config_dir_tordata)) {
qDebug() << QString("Creating directory: %1").arg(config_dir_tordata);
if (!QDir().mkpath(config_dir_tordata))
throw std::runtime_error("Could not create directory " + config_dir_tordata.toStdString());
}
QString config_dir_torsocks = QString("%1%2").arg(dir).arg("torsocks");
if(!Utils::dirExists(config_dir_torsocks)) {
qDebug() << QString("Creating directory: %1").arg(config_dir_torsocks);
if (!QDir().mkpath(config_dir_torsocks))
throw std::runtime_error("Could not create directory " + config_dir_torsocks.toStdString());
QString config_dir_xmrig = QString("%1%2").arg(dir).arg("xmrig");
QStringList createDirs({dir, config_dir_tor, config_dir_tordata, config_dir_xmrig});
for(const auto &d: createDirs) {
if(!Utils::dirExists(d)) {
qDebug() << QString("Creating directory: %1").arg(d);
if (!QDir().mkpath(d))
throw std::runtime_error("Could not create directory " + d.toStdString());
}
}
}
@ -541,6 +557,43 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
return;
}
this->createWalletFinish(password);
}
void AppContext::createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight) {
if(Utils::fileExists(path)) {
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
qCritical() << err;
emit walletCreatedError(err);
return;
}
if(!this->walletManager->addressValid(address, this->networkType)) {
auto err = QString("Failed to create wallet. Invalid address provided.").arg(path);
qCritical() << err;
emit walletCreatedError(err);
return;
}
if(!this->walletManager->keyValid(viewkey, address, true, this->networkType)) {
auto err = QString("Failed to create wallet. Invalid viewkey provided.").arg(path);
qCritical() << err;
emit walletCreatedError(err);
return;
}
if(!spendkey.isEmpty() && !this->walletManager->keyValid(spendkey, address, false, this->networkType)) {
auto err = QString("Failed to create wallet. Invalid spendkey provided.").arg(path);
qCritical() << err;
emit walletCreatedError(err);
return;
}
this->currentWallet = this->walletManager->createWalletFromKeys(path, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
this->createWalletFinish(password);
}
void AppContext::createWalletFinish(const QString &password) {
this->currentWallet->setPassword(password);
this->currentWallet->store();
this->walletPassword = password;
@ -548,9 +601,9 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
}
void AppContext::initRestoreHeights() {
restoreHeights[NetworkType::TESTNET] = new RestoreHeightLookup(NetworkType::TESTNET);
restoreHeights[NetworkType::STAGENET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_stagenet.txt", NetworkType::STAGENET);
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
restoreHeights[NetworkType::TESTNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_wownero_mainnet.txt", NetworkType::TESTNET);
restoreHeights[NetworkType::STAGENET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_wownero_mainnet.txt", NetworkType::STAGENET);
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_wownero_mainnet.txt", NetworkType::MAINNET);
}
void AppContext::onSetRestoreHeight(unsigned int height){
@ -562,7 +615,7 @@ void AppContext::onSetRestoreHeight(unsigned int height){
}
this->currentWallet->setWalletCreationHeight(height);
this->currentWallet->setPassword(this->walletPassword); // trigger .keys write
this->currentWallet->setPassword(this->currentWallet->getPassword()); // trigger .keys write
// nuke wallet cache
const auto fn = this->currentWallet->path();
@ -614,8 +667,9 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) {
}
void AppContext::donateBeg() {
if(this->networkType != NetworkType::Type::MAINNET)
return;
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)
@ -643,23 +697,23 @@ AppContext::~AppContext() {
// ############################################## LIBWALLET QT #########################################################
void AppContext::onMoneySpent(const QString &txId, quint64 amount) {
auto amount_num = amount / AppContext::cdiv;
auto amount_num = amount / globals::cdiv;
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
}
void AppContext::onMoneyReceived(const QString &txId, quint64 amount) {
// Incoming tx included in a block.
auto amount_num = amount / AppContext::cdiv;
auto amount_num = amount / globals::cdiv;
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
}
void AppContext::onUnconfirmedMoneyReceived(const QString &txId, quint64 amount) {
// Incoming transaction in pool
auto amount_num = amount / AppContext::cdiv;
auto amount_num = amount / globals::cdiv;
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
if(this->currentWallet->synchronized()) {
auto notify = QString("%1 XMR (pending)").arg(amount_num);
auto notify = QString("%1 WOW (pending)").arg(amount_num);
Utils::desktopNotify("Payment received", notify, 5000);
}
}
@ -673,13 +727,15 @@ void AppContext::onWalletUpdate() {
this->storeWallet();
}
void AppContext::onWalletRefreshed() {
void AppContext::onWalletRefreshed(bool success) {
if (!this->refreshed) {
refreshModels();
this->refreshed = true;
this->storeWallet();
}
qDebug() << "Wallet refresh status: " << success;
this->currentWallet->refreshHeightAsync();
}
@ -696,7 +752,7 @@ void AppContext::onWalletNewBlock(quint64 blockheight, quint64 targetHeight) {
void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) {
qDebug() << Q_FUNC_INFO << walletHeight << daemonHeight << targetHeight;
if (!this->currentWallet->connected())
if (this->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected)
return;
if (daemonHeight < targetHeight) {
@ -742,12 +798,12 @@ void AppContext::storeWallet() {
void AppContext::updateBalance() {
if(!this->currentWallet)
throw std::runtime_error("this should not happen, ever");
return;
AppContext::balance = this->currentWallet->balance() / AppContext::cdiv;
AppContext::balance = this->currentWallet->balance() / globals::cdiv;
auto balance_str = QString::number(balance, 'f');
double unlocked = this->currentWallet->unlockedBalance() / AppContext::cdiv;
double unlocked = this->currentWallet->unlockedBalance() / globals::cdiv;
auto unlocked_str = QString::number(unlocked, 'f');
emit balanceUpdated(balance, unlocked, balance_str, unlocked_str);

@ -14,6 +14,7 @@
#include "utils/networking.h"
#include "utils/tor.h"
#include "utils/xmrto.h"
#include "utils/xmrig.h"
#include "utils/wsclient.h"
#include "utils/txfiathistory.h"
#include "widgets/RedditPost.h"
@ -38,13 +39,13 @@ public:
bool isTails = false;
bool isWhonix = false;
bool isDebug = false;
const QString featherDonationAddress = "47ntfT2Z5384zku39pTM6hGcnLnvpRYW2Azm87GiAAH2bcTidtq278TL6HmwyL8yjMeERqGEBs3cqC8vvHPJd1cWQrGC65f";
const int featherDonationAmount = 50; // euro
const QString featherDonationAddress = "WW2xGxtet29WxM6RBaxkTaBZoXC4mHGYoiHM5rPgViwNjnTLR452skNTsBMFp9JgMbdGucFQWF3PG95Hau9MnFjp2rmKgrcC7";
const int featherDonationAmount = 25; // euro
bool featherDonationSending = false;
QCommandLineParser *cmdargs;
QString coinName = "monero";
QString coinName = "wownero";
bool isTorSocks = false;
QString homeDir;
QString accountName;
@ -56,6 +57,7 @@ public:
QString walletPath;
QString walletPassword = "";
bool walletViewOnly = false;
NetworkType::Type networkType;
QString applicationPath;
@ -67,7 +69,8 @@ public:
const unsigned int kdfRounds = 1;
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
quint32 tx_mixin = static_cast<const quint32 &>(10);
static constexpr const double cdiv = 1e12;
static constexpr const double cdiv = 1e11;
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
QNetworkAccessManager *network;
QNetworkAccessManager *networkClearnet;
@ -76,6 +79,7 @@ public:
Tor *tor;
WSClient *ws;
XmrTo *XMRTo;
XmRig *XMRig;
Nodes *nodes;
static Prices *prices;
static WalletKeysFilesModel *wallets;
@ -89,6 +93,8 @@ public:
WalletManager *walletManager;
Wallet *currentWallet = nullptr;
void createWallet(FeatherSeed seed, const QString &path, const QString &password);
void createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight);
void createWalletFinish(const QString &password);
void syncStatusUpdated(quint64 height, quint64 target);
void updateBalance();
void initTor();
@ -98,6 +104,7 @@ public:
void walletClose(bool emitClosedSignal = true);
void storeWallet();
void refreshModels();
void setWindowTitle(bool mining = false);
public slots:
void onOpenWallet(const QString& path, const QString &password);
@ -149,6 +156,7 @@ signals:
void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
void nodeSourceChanged(NodeSource nodeSource);
void XMRigDownloads(const QJsonObject &data);
void setCustomNodes(QList<FeatherNode> nodes);
void ccsEmpty();
void openAliasResolveError(const QString &msg);
@ -160,13 +168,14 @@ signals:
void initiateTransaction();
void endTransaction();
void walletClosing();
void setTitle(const QString &title); // set window title
private:
void sorry();
const unsigned int m_donationBoundary = 15;
UtilsNetworking *m_utilsNetworkingNodes;
QTimer *m_storeTimer = new QTimer(this);
QUrl m_wsUrl = QUrl(QStringLiteral("ws://dtg2clrd6iand4mwp2x6nhbqd3nqbxlbiw65f6vkwmmutxy2sijsnjyd.onion/ws"));
QUrl m_wsUrl = QUrl(QStringLiteral("ws://dtg2clrd6iand4mwp2x6nhbqd3nqbxlbiw65f6vkwmmutxy2sijsnjyd.onion/wow/ws"));
};
#endif //FEATHER_APPCONTEXT_H

@ -3,7 +3,7 @@
<file>assets/about.txt</file>
<file>assets/ack.txt</file>
<file>assets/contributors.txt</file>
<file>assets/feather.desktop</file>
<file>assets/feather-wow.desktop</file>
<file>assets/images/appicons/32x32.png</file>
<file>assets/images/appicons/48x48.png</file>
<file>assets/images/appicons/64x64.png</file>
@ -23,6 +23,7 @@
<file>assets/images/coldcard.png</file>
<file>assets/images/coldcard_unpaired.png</file>
<file>assets/images/confirmed.png</file>
<file>assets/images/confirmed_icon.png</file>
<file>assets/images/confirmed.svg</file>
<file>assets/images/connect.svg</file>
<file>assets/images/copy.png</file>
@ -30,10 +31,13 @@
<file>assets/images/edit.png</file>
<file>assets/images/exchange.png</file>
<file>assets/images/expired.png</file>
<file>assets/images/expired_icon.png</file>
<file>assets/images/eye1.png</file>
<file>assets/images/eye_blind.png</file>
<file>assets/images/feather.png</file>
<file>assets/images/file.png</file>
<file>assets/images/ghost.png</file>
<file>assets/images/ghost_icon.png</file>
<file>assets/images/history.png</file>
<file>assets/images/info.png</file>
<file>assets/images/key.png</file>
@ -41,12 +45,34 @@
<file>assets/images/ledger_unpaired.png</file>
<file>assets/images/lightning.png</file>
<file>assets/images/lock.png</file>
<file>assets/images/lock_icon.png</file>
<file>assets/images/lock.svg</file>
<file>assets/images/microphone.png</file>
<file>assets/images/mining.png</file>
<file>assets/images/network.png</file>
<file>assets/images/offline_tx.png</file>
<file>assets/images/person.svg</file>
<file>assets/images/photos/1.png</file>
<file>assets/images/photos/illiterate_illuminati.png</file>
<file>assets/images/photos/wow1.png</file>
<file>assets/images/photos/wow2.png</file>
<file>assets/images/photos/wow3.png</file>
<file>assets/images/photos/wow4.png</file>
<file>assets/images/photos/wow5.png</file>
<file>assets/images/photos/wow6.png</file>
<file>assets/images/photos/wow7.png</file>
<file>assets/images/photos/wow8.png</file>
<file>assets/images/photos/wow9.png</file>
<file>assets/images/photos/wow10.png</file>
<file>assets/images/photos/wow11.png</file>
<file>assets/images/photos/wow12.png</file>
<file>assets/images/photos/wow13.png</file>
<file>assets/images/photos/wow14.png</file>
<file>assets/images/photos/wow15.png</file>
<file>assets/images/photos/wow16.png</file>
<file>assets/images/photos/wow17.png</file>
<file>assets/images/photos/wow18.png</file>
<file>assets/images/photos/wow19.png</file>
<file>assets/images/preferences.png</file>
<file>assets/images/preferences.svg</file>
<file>assets/images/qrcode.png</file>
@ -91,10 +117,11 @@
<file>assets/images/warning.png</file>
<file>assets/images/xmrto_big.png</file>
<file>assets/images/xmrto.png</file>
<file>assets/images/xmrig.ico</file>
<file>assets/images/xmrig.svg</file>
<file>assets/images/zoom.png</file>
<file>assets/mnemonic_25_english.txt</file>
<file>assets/restore_heights_monero_mainnet.txt</file>
<file>assets/restore_heights_monero_stagenet.txt</file>
<file>assets/restore_heights_wownero_mainnet.txt</file>
<file>assets/user_agents.txt</file>
</qresource>
</RCC>

@ -6,5 +6,4 @@ The wallet UI is heavily inspired by Electrum. We would like to recognize Thomas
Huge thanks to nioc, fluffypony, wowario, thrmo for help during development.
Some more shoutouts for people for hosting nodes and/or having good ideas:
dnale0r, dEBRUYNE, binaryFate, lza_menace, jwinterm, kico, wowario
rottensox for testing :-)

@ -0,0 +1,14 @@
[Desktop Entry]
Comment=Lightweight Wownero Wallet
Exec=feather-wow
GenericName[en_US]=Wownero Wallet
GenericName=Wownero Wallet
Icon=feather-wow
Name[en_US]=Feather-WOW
Name=Feather-WOW
Categories=Finance;Network;
StartupNotify=false
StartupWMClass=feather
Terminal=false
Type=Application
MimeType=x-scheme-handler/wownero;

@ -1,14 +0,0 @@
[Desktop Entry]
Comment=Lightweight Monero Wallet
Exec=feather
GenericName[en_US]=Monero Wallet
GenericName=Monero Wallet
Icon=feather
Name[en_US]=Feather
Name=Feather
Categories=Finance;Network;
StartupNotify=false
StartupWMClass=feather
Terminal=false
Type=Application
MimeType=x-scheme-handler/monero;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 KiB

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 496.06 496.06"><title>XMRig</title><defs><style>.a{fill:none;}.b{clip-path:url(#a);}.c{fill:#ec671a;}.d{fill:#575756;}.e{fill:#fff;}</style><clipPath id="a" transform="translate(-106.3 -106.3)"><circle class="a" cx="354.33" cy="354.33" r="248.03"/></clipPath></defs><title>Xr_icon</title><g class="b"><rect class="c" width="496.06" height="496.06"/><polygon class="d" points="496.06 496.06 0 496.06 0 387.21 124.02 387.55 189.12 291.44 254.23 387.55 496.06 387.55 496.06 496.06"/><path class="e" d="M481.44,441.14c0-13.18,0-27,0-37.32,0-54.11,22.15-77.57,55.82-52.71-0.39.07,27.9-43.41,27.9-43.41-35.68-24.09-67.09-10.17-86.81,19.45V295.42H428.74V441.14H391.12l-61-91.46,89.91-134.87H351.85L296,298.52l-55.81-83.71H172l89.91,134.87-61,91.46H106.3v52.37l59.65,0.16-0.12.18H234l62-93,62,93H602.36V441.14H481.44Z" transform="translate(-106.3 -106.3)"/></g></svg>

Before

Width:  |  Height:  |  Size: 955 B

After

Width:  |  Height:  |  Size: 955 B

File diff suppressed because it is too large Load Diff

@ -1,444 +0,0 @@
1518932025:1
1519057016:1500
1519148374:3000
1519251680:4500
1519344568:6000
1519443436:7500
1519538388:9000
1519630287:10500
1519706564:12000
1519797372:13500
1519887275:15000
1519977816:16500
1520068127:18000
1520163067:19500
1520267595:21000
1520378423:22500
1520470528:24000
1520547672:25500
1520637599:27000
1520727714:28500
1520817129:30000
1520907017:31500
1521042482:33000
1521203259:34500
1521379791:36000
1521565539:37500
1521768004:39000
1521952047:40500
1522127660:42000
1522284923:43500
1522510139:45000
1522676022:46500
1522871613:48000
1522969561:49500
1523145200:51000
1523316302:52500
1523550884:54000
1523758049:55500
1523917524:57000
1524106084:58500
1524290437:60000
1524464139:61500
1524728732:63000
1524905041:64500
1525093135:66000
1525239917:67500
1525443579:69000
1525663214:70500
1525839621:72000
1525989826:73500
1526182919:75000
1526437405:76500
1526649137:78000
1526812889:79500
1526991726:81000
1527184073:82500
1527351889:84000
1527567839:85500
1527759754:87000
1527916443:88500
1528110008:90000
1528285005:91500
1528486806:93000
1528666327:94500
1528872096:96000
1529015390:97500
1529205809:99000
1529384415:100500
1529595764:102000
1529770640:103500
1529950955:105000
1530115141:106500
1530306273:108000
1530491510:109500
1530677550:111000
1530823854:112500
1531032372:114000
1531175619:115500
1531371373:117000
1531541897:118500
1531706629:120000
1531888223:121500
1532104373:123000
1532314129:124500
1532503060:126000
1532663651:127500
1532894128:129000
1533095950:130500
1533302631:132000
1533464469:133500
1533670723:135000
1533833911:136500
1534030841:138000
1534203160:139500
1534397055:141000
1534577048:142500
1534752236:144000
1534931462:145500
1535119263:147000
1535300364:148500
1535471540:150000
1535640429:151500
1535836082:153000
1536059510:154500
1536209710:156000
1536366875:157500
1536560444:159000
1536749043:160500
1536926345:162000
1537105693:163500
1537296613:165000
1537501175:166500
1537667740:168000
1537844801:169500
1538000228:171000
1538199137:172500
1538418406:174000
1538612473:175500
1538979696:177000
1539147054:178500
1539303989:180000
1539488395:181500
1539652212:183000
1539819281:184500
1539971301:186000
1540146343:187500
1540330288:189000
1540505588:190500
1540688044:192000
1540869374:193500
1541049379:195000
1541223210:196500
1541393586:198000
1541577202:199500
1541762094:201000
1541909862:202500
1542199063:204000
1542399873:205500
1542579333:207000
1542763985:208500
1542947442:210000
1543124804:211500
1543323933:213000
1543489307:214500
1543672244:216000
1543837576:217500
1544042420:219000
1544222096:220500
1544402628:222000
1544546843:223500
1544730464:225000
1545134663:226500
1545301874:228000
1545488939:229500
1545664263:231000
1545834607:232500
1546014630:234000
1546192930:235500
1546373750:237000
1546556089:238500
1546738764:240000
1546917228:241500
1547091360:243000
1547272679:244500
1547463907:246000
1547636722:247500
1547817060:249000
1548001021:250500
1548185375:252000
1548380319:253500
1548555267:255000
1548741069:256500
1548926544:258000
1549140801:259500
1549297081:261000
1549478379:262500
1549651888:264000
1549860339:265500
1550024455:267000
1550217332:268500
1551312389:270000
1551527936:271500
1551665288:273000
1551836443:274500
1552026339:276000
1552218441:277500
1552443760:279000
1552680291:280500
1552854334:282000
1553030527:283500
1553211359:285000
1553385763:286500
1553588198:288000
1553760642:289500
1553937158:291000
1554121990:292500
1554302691:294000
1554479953:295500
1554666762:297000
1554850956:298500
1555057081:300000
1555228611:301500
1555400592:303000
1555615005:304500
1555789135:306000
1555995570:307500
1556183867:309000
1556362195:310500
1556541232:312000
1556728140:313500
1556903453:315000
1557065264:316500
1557273415:318000
1557443047:319500
1557618850:321000
1557798876:322500
1557976530:324000
1558172356:325500
1558356032:327000
1558534222:328500
1558719934:330000
1558892036:331500
1559075695:333000
1559253052:334500
1559462339:336000
1559647394:337500
1559827424:339000
1560002042:340500
1560189126:342000
1560343483:343500
1560548477:345000
1560709613:346500
1560888640:348000
1561072261:349500
1561249189:351000
1561433494:352500
1561609875:354000
1561789136:355500
1561971530:357000
1562146235:358500
1562283612:360000
1562536678:361500
1562727970:363000
1562896064:364500
1563081360:366000
1563222147:367500
1563471753:369000
1563648133:370500
1563844968:372000
1564019071:373500
1564194349:375000
1564378444:376500
1564557884:378000
1564733144:379500
1564931656:381000
1565088150:382500
1565272766:384000
1565457895:385500
1565632566:387000
1565824568:388500
1565992971:390000
1566155025:391500
1566345274:393000
1566534162:394500
1566736618:396000
1566937517:397500
1567128136:399000
1567296600:400500
1567482630:402000
1567671945:403500
1567828904:405000
1568061755:406500
1568241437:408000
1568413206:409500
1568589427:411000
1568777072:412500
1568950797:414000
1569140033:415500
1569321139:417000
1569497726:418500
1569685524:420000
1569858754:421500
1570025222:423000
1570195891:424500
1570412169:426000
1570568122:427500
1570772965:429000
1570968490:430500
1571107236:432000
1571359933:433500
1571520970:435000
1571737619:436500
1571890574:438000
1572104152:439500
1572299391:441000
1572483040:442500
1572663772:444000
1572839626:445500
1573049256:447000
1573229282:448500
1573407580:450000
1573589178:451500
1573735333:453000
1573840717:454500
1574096272:456000
1574280161:457500
1574455892:459000
1574604632:460500
1574809987:462000
1574975801:463500
1575140569:465000
1575422392:466500
1575583824:468000
1575763253:469500
1575944350:471000
1576125074:472500
1576324269:474000
1576508865:475500
1576669612:477000
1576891906:478500
1577078563:480000
1577265105:481500
1577431370:483000
1577744409:484500
1577874268:486000
1578038130:487500
1578231375:489000
1578439644:490500
1578625982:492000
1578808598:493500
1578985283:495000
1579175621:496500
1579347774:498000
1579517894:499500
1579687667:501000
1579868646:502500
1580046838:504000
1580240961:505500
1580428207:507000
1580572904:508500
1580823996:510000
1580994064:511500
1581183924:513000
1581351893:514500
1581521085:516000
1581702405:517500
1581943619:519000
1582098842:520500
1582299537:522000
1582480013:523500
1582659742:525000
1582839127:526500
1583020057:528000
1583266489:529500
1583429819:531000
1583615548:532500
1583785190:534000
1584011766:535500
1584192961:537000
1584342104:538500
1584521327:540000
1584706894:541500
1584883565:543000
1585062315:544500
1585244138:546000
1585427591:547500
1585600165:549000
1585784601:550500
1585961031:552000
1586147565:553500
1586322716:555000
1586513730:556500
1586698639:558000
1586878453:559500
1587062131:561000
1587234968:562500
1587391529:564000
1587591247:565500
1587752262:567000
1587969335:568500
1588148503:570000
1588405392:571500
1588593609:573000
1588778703:574500
1588949467:576000
1589172941:577500
1589358899:579000
1589536247:580500
1589730950:582000
1589882572:583500
1590062931:585000
1590237283:586500
1590430792:588000
1590599140:589500
1590807968:591000
1590987339:592500
1591166304:594000
1591348974:595500
1591542069:597000
1591723208:598500
1591908870:600000
1592060567:601500
1592259549:603000
1592465269:604500
1592647205:606000
1592828242:607500
1593006687:609000
1593201658:610500
1593492855:612000
1593636856:613500
1593811224:615000
1594004429:616500
1594162422:618000
1594348191:619500
1594523256:621000
1594685351:622500
1594890677:624000
1595073577:625500
1595269373:627000
1595477524:628500
1595668392:630000
1595867552:631500
1596043173:633000
1596209876:634500
1596419711:636000
1596600935:637500
1596770710:639000
1596979435:640500
1597172685:642000
1597347250:643500
1597508807:645000
1597698614:646500
1597871525:648000
1598058524:649500
1598368966:651000
1598552832:652500
1598715002:654000
1598902853:655500
1599078705:657000
1599253008:658500
1599430407:660000
1599636188:661500
1599809533:663000
1600001474:664500

@ -0,0 +1,170 @@
1522624244:1
1522919763:1500
1523409727:3000
1523960364:4500
1524369547:6000
1524784400:7500
1525233663:9000
1525687037:10500
1526135584:12000
1526578718:13500
1527063859:15000
1527518523:16500
1527977555:18000
1528436212:19500
1528893646:21000
1529347707:22500
1529812899:24000
1530272289:25500
1530735091:27000
1531195321:28500
1531660804:30000
1532117133:31500
1532568099:33000
1533015693:34500
1533470854:36000
1533923432:37500
1534374443:39000
1534823621:40500
1535290349:42000
1535735446:43500
1536201310:45000
1536655339:46500
1537109771:48000
1537565159:49500
1538015344:51000
1538470517:52500
1538927269:54000
1539379855:55500
1539826869:57000
1540311781:58500
1540788117:60000
1541297876:61500
1541794302:63000
1542407858:64500
1543204218:66000
1543955252:67500
1544703247:69000
1545372024:70500
1546002226:72000
1546636335:73500
1547342810:75000
1548268561:76500
1548921502:78000
1549481994:79500
1550247894:81000
1550743647:82500
1551184199:84000
1551626083:85500
1552086272:87000
1552546202:88500
1552994646:90000
1553451645:91500
1553898976:93000
1554352300:94500
1554801655:96000
1555254551:97500
1555700170:99000
1556151520:100500
1556602014:102000
1557057709:103500
1557508808:105000
1557961787:106500
1558413175:108000
1558860703:109500
1559310651:111000
1559759587:112500
1560207825:114000
1560635185:115500
1561076902:117000
1561514633:118500
1561983210:120000
1562435722:121500
1562903313:123000
1563367656:124500
1563794760:126000
1564249987:127500
1564698967:129000
1565154467:130500
1565606876:132000
1566053072:133500
1566496455:135000
1566940483:136500
1567391114:138000
1567840593:139500
1568290979:141000
1568739494:142500
1569196321:144000
1569643702:145500
1570095074:147000
1570533207:148500
1570991907:150000
1571447301:151500
1571895478:153000
1572343106:154500
1572791579:156000
1573241814:157500
1573692641:159000
1574137867:160500
1574600312:162000
1575051207:163500
1575500955:165000
1575953057:166500
1576401859:168000
1576850419:169500
1577301553:171000
1577753392:172500
1578199780:174000
1578655276:175500
1579104342:177000
1579549362:178500
1580004525:180000
1580451365:181500
1580900062:183000
1581350016:184500
1581795609:186000
1582251322:187500
1582703447:189000
1583152160:190500
1583601824:192000
1584051198:193500
1584499159:195000
1584949547:196500
1585404000:198000
1585843662:199500
1586292852:201000
1586738236:202500
1587201986:204000
1587663936:205500
1588119101:207000
1588586510:208500
1589057972:210000
1589520509:211500
1589974638:213000
1590425544:214500
1590876865:216000
1591326399:217500
1591759684:219000
1592226165:220500
1592681388:222000
1593141896:223500
1593595859:225000
1594048319:226500
1594489197:228000
1594955184:229500
1595408732:231000
1595854685:232500
1596303095:234000
1596732814:235500
1597198383:237000
1597650820:238500
1598099448:240000
1598553007:241500
1599004645:243000
1599459515:244500
1599914532:246000
1600357718:247500
1600811447:249000
1601257433:250500
1601710572:252000
1602154921:253500

@ -1,6 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>tor/tor</file>
<file>tor/libevent-2.1.7.dylib</file>
</qresource>
</RCC>

@ -1,5 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>tor/tor</file>
<file>assets/exec/xmrig</file>
</qresource>
</RCC>

@ -1,5 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>tor/tor.exe</file>
<file>assets/exec/tor</file>
</qresource>
</RCC>

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
<file>assets/exec/tor</file>
<file>assets/exec/libevent-2.1.7.dylib</file>
</qresource>
</RCC>

@ -123,7 +123,7 @@ void CalcWidget::initComboBox() {
ui->comboCalcFrom->addItems(marketsKeys);
ui->comboCalcFrom->insertSeparator(marketsKeys.count());
ui->comboCalcFrom->addItems(ratesKeys);
ui->comboCalcFrom->setCurrentIndex(marketsKeys.indexOf("XMR"));
ui->comboCalcFrom->setCurrentIndex(marketsKeys.indexOf("WOW"));
ui->comboCalcTo->addItems(marketsKeys);
ui->comboCalcTo->insertSeparator(marketsKeys.count());

@ -58,7 +58,7 @@ void CLI::onWalletOpenedError(const QString &err) {
return this->finishedError(err);
}
void CLI::onWalletOpenPasswordRequired(bool invalidPassword) {
void CLI::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
if(mode == CLIMode::CLIModeExportContacts ||
mode == CLIMode::CLIModeExportTxHistory)
return this->finishedError("invalid password");

@ -26,7 +26,7 @@ public slots:
//libwalletqt
void onWalletOpened();
void onWalletOpenedError(const QString& err);
void onWalletOpenPasswordRequired(bool invalidPassword);
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
private:
AppContext *ctx;

@ -7,6 +7,7 @@
#include "utils/utils.h"
#include "dialog/outputinfodialog.h"
#include "dialog/outputsweepdialog.h"
#include "mainwindow.h"
#include <QClipboard>
#include <QDebug>
@ -21,6 +22,7 @@ CoinsWidget::CoinsWidget(QWidget *parent)
, m_copyMenu(new QMenu("Copy",this))
{
ui->setupUi(this);
m_ctx = MainWindow::getContext();
// header context menu
ui->coins->header()->setContextMenuPolicy(Qt::CustomContextMenu);
@ -40,14 +42,14 @@ CoinsWidget::CoinsWidget(QWidget *parent)
// context menu
ui->coins->setContextMenuPolicy(Qt::CustomContextMenu);
m_thawOutputAction = new QAction("Thaw output");
m_freezeOutputAction = new QAction("Freeze output");
m_thawOutputAction = new QAction("Thaw output", this);
m_freezeOutputAction = new QAction("Freeze output", this);
m_freezeAllSelectedAction = new QAction("Freeze selected");
m_thawAllSelectedAction = new QAction("Thaw selected");
m_freezeAllSelectedAction = new QAction("Freeze selected", this);
m_thawAllSelectedAction = new QAction("Thaw selected", this);
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details");
m_sweepOutputAction = new QAction("Sweep output");
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details", this);
m_sweepOutputAction = new QAction("Sweep output", this);
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
connect(m_viewOutputAction, &QAction::triggered, this, &CoinsWidget::viewOutput);
@ -63,13 +65,19 @@ CoinsWidget::CoinsWidget(QWidget *parent)
void CoinsWidget::setModel(CoinsModel * model, Coins * coins) {
m_coins = coins;
m_model = model;
m_proxyModel = new CoinsProxyModel;
m_proxyModel = new CoinsProxyModel(this);
m_proxyModel->setSourceModel(m_model);
ui->coins->setModel(m_proxyModel);
ui->coins->setColumnHidden(CoinsModel::Spent, true);
ui->coins->setColumnHidden(CoinsModel::SpentHeight, true);
ui->coins->setColumnHidden(CoinsModel::Frozen, true);
if (!m_ctx->currentWallet->viewOnly()) {
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, true);
} else {
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, false);
}
ui->coins->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->coins->header()->setSectionResizeMode(CoinsModel::AddressLabel, QHeaderView::Stretch);
ui->coins->header()->setSortIndicator(CoinsModel::BlockHeight, Qt::DescendingOrder);
@ -103,10 +111,15 @@ void CoinsWidget::showContextMenu(const QPoint &point) {
if (!isSpent) {
isFrozen ? menu->addAction(m_thawOutputAction) : menu->addAction(m_freezeOutputAction);
}
if (!isFrozen && isUnlocked) {
menu->addAction(m_sweepOutputAction);
if (isFrozen || !isUnlocked) {
m_sweepOutputAction->setDisabled(true);
} else {
m_sweepOutputAction->setEnabled(true);
}
}
menu->addAction(m_viewOutputAction);
}
@ -194,6 +207,7 @@ void CoinsWidget::onSweepOutput() {
qCritical() << "key image: " << keyImage;
emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
dialog->deleteLater();
}
void CoinsWidget::copy(copyField field) {

@ -67,6 +67,7 @@ private:
Coins *m_coins;
CoinsModel * m_model;
CoinsProxyModel * m_proxyModel;
AppContext *m_ctx;
void showContextMenu(const QPoint & point);
void copy(copyField field);

@ -68,9 +68,7 @@ HelpLabel::HelpLabel(QWidget *parent) : QLabel(parent)
void HelpLabel::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event)
QMessageBox msgBox(QApplication::activeWindow());
msgBox.setText(this->help_text);
msgBox.exec();
QMessageBox::information(this, "Help", this->help_text);
}
void HelpLabel::enterEvent(QEvent *event)

@ -12,8 +12,8 @@ AboutDialog::AboutDialog(QWidget *parent)
{
ui->setupUi(this);
this->setWindowIcon(QIcon("://assets/images/appicons/64x64.png"));
// cute fox (c) Diego "rehrar" Salazar :-D
QPixmap p(":assets/images/cutexmrfox.png");
QPixmap p(":assets/images/photos/illiterate_illuminati.png");
ui->aboutImage->setPixmap(p.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation));
auto about = Utils::fileOpenQRC(":assets/about.txt");
auto about_text = Utils::barrayToString(about);

@ -43,7 +43,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>a free, open-source Monero wallet</string>
<string>a free, open-source Wownero wallet</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@ -53,7 +53,7 @@
<item>
<widget class="QPlainTextEdit" name="copyrightText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>

@ -0,0 +1,62 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include "broadcasttxdialog.h"
#include "ui_broadcasttxdialog.h"
#include <QMessageBox>
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx)
: QDialog(parent)
, m_ctx(ctx)
, ui(new Ui::BroadcastTxDialog)
{
ui->setupUi(this);
m_network = new UtilsNetworking(m_ctx->network, this);
auto node = ctx->nodes->connection();
m_rpc = new DaemonRpc(this, m_network, node.full);
connect(ui->btn_Broadcast, &QPushButton::clicked, this, &BroadcastTxDialog::broadcastTx);
connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject);
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
this->adjustSize();
}
void BroadcastTxDialog::broadcastTx() {
QString tx = ui->transaction->toPlainText();
QString node = [this]{
QString node;
if (ui->radio_useCustom->isChecked())
node = ui->customNode->text();
else
node = m_ctx->nodes->connection().full;
if (!node.startsWith("http://"))
node = QString("http://%1").arg(node);
return node;
}();
m_rpc->setDaemonAddress(node);
m_rpc->sendRawTransaction(tx);
}
void BroadcastTxDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) {
if (!resp.ok) {
QMessageBox::warning(this, "Transaction broadcast", resp.status);
return;
}
if (resp.endpoint == DaemonRpc::Endpoint::SEND_RAW_TRANSACTION) {
QMessageBox::information(this, "Transaction broadcast", "Transaction submitted successfully.\n\n"
"If the transaction belongs to this wallet it may take several minutes before it shows up in the history tab.");
}
}
BroadcastTxDialog::~BroadcastTxDialog() {
delete ui;
}

@ -0,0 +1,35 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_BROADCASTTXDIALOG_H
#define FEATHER_BROADCASTTXDIALOG_H
#include <QDialog>
#include "appcontext.h"
#include "utils/daemonrpc.h"
namespace Ui {
class BroadcastTxDialog;
}
class BroadcastTxDialog : public QDialog
{
Q_OBJECT
public:
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx);
~BroadcastTxDialog() override;
private slots:
void broadcastTx();
void onApiResponse(const DaemonRpc::DaemonResponse &resp);
private:
Ui::BroadcastTxDialog *ui;
UtilsNetworking *m_network;
AppContext *m_ctx;
DaemonRpc *m_rpc;
};
#endif //FEATHER_BROADCASTTXDIALOG_H

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BroadcastTxDialog</class>
<widget class="QDialog" name="BroadcastTxDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>797</width>
<height>575</height>
</rect>
</property>
<property name="windowTitle">
<string>Broadcast transaction</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Transaction:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="transaction">
<property name="minimumSize">
<size>
<width>500</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>Paste hex encoded signed tx string here</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Node</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="radio_useDefault">
<property name="text">
<string>Use currently connected node</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radio_useCustom">
<property name="text">
<string>Use custom node</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="customNode"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>All transactions are broadcast over Tor</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_Broadcast">
<property name="text">
<string>Broadcast</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_Close">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -43,7 +43,11 @@ DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
ui->label_seedType->setText(ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
ui->label_viewOnly->setText(ctx->currentWallet->viewOnly() ? "True" : "False");
ui->label_OS->setText(QSysInfo::prettyProductName());
QString os = QSysInfo::prettyProductName();
if (ctx->isTails) {
os = QString("Tails %1").arg(TailsOS::version());
}
ui->label_OS->setText(os);
ui->label_timestamp->setText(QString::number(QDateTime::currentSecsSinceEpoch()));
connect(ui->btn_Copy, &QPushButton::clicked, this, &DebugInfoDialog::copyToClipboad);
@ -69,7 +73,7 @@ QString DebugInfoDialog::statusToString(Wallet::ConnectionStatus status) {
void DebugInfoDialog::copyToClipboad() {
QString text = "";
text += QString("Feather version: %1\n").arg(ui->label_featherVersion->text());
text += QString("Monero version: %1\n").arg(ui->label_moneroVersion->text());
text += QString("Wownero version: %1\n").arg(ui->label_moneroVersion->text());
text += QString("Wallet height: %1\n").arg(ui->label_walletHeight->text());
text += QString("Daemon height: %1\n").arg(ui->label_daemonHeight->text());

@ -23,6 +23,10 @@ public:
private:
QString statusToString(Wallet::ConnectionStatus status);
void copyToClipboad();
void updateInfo();
QTimer m_updateTimer;
AppContext *m_ctx;
Ui::DebugInfoDialog *ui;
};

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>693</width>
<height>580</height>
<height>612</height>
</rect>
</property>
<property name="windowTitle">
@ -36,7 +36,7 @@
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Monero version:</string>
<string>Wownero version:</string>
</property>
</widget>
</item>
@ -91,14 +91,14 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Restore height:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QLabel" name="label_restoreHeight">
<property name="text">
<string>TextLabel</string>
@ -108,14 +108,14 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Synchronized:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QLabel" name="label_synchronized">
<property name="text">
<string>TextLabel</string>
@ -125,21 +125,21 @@
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Remote node:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<widget class="QLabel" name="label_remoteNode">
<property name="text">
<string>TextLabel</string>
@ -149,14 +149,14 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Wallet status:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QLabel" name="label_walletStatus">
<property name="text">
<string>TextLabel</string>
@ -166,14 +166,14 @@
</property>
</widget>
</item>
<item row="10" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Tor status:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QLabel" name="label_torStatus">
<property name="text">
<string>TextLabel</string>
@ -183,14 +183,14 @@
</property>
</widget>
</item>
<item row="11" column="0">
<item row="12" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Websocket status:</string>
</property>
</widget>
</item>
<item row="11" column="1">
<item row="12" column="1">
<widget class="QLabel" name="label_websocketStatus">
<property name="text">
<string>TextLabel</string>
@ -200,21 +200,21 @@
</property>
</widget>
</item>
<item row="12" column="1">
<item row="13" column="1">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="13" column="0">
<item row="14" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Network type:</string>
</property>
</widget>
</item>
<item row="13" column="1">
<item row="14" column="1">
<widget class="QLabel" name="label_netType">
<property name="text">
<string>TextLabel</string>
@ -224,14 +224,14 @@
</property>
</widget>
</item>
<item row="14" column="0">
<item row="15" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Seed type:</string>
</property>
</widget>
</item>
<item row="14" column="1">
<item row="15" column="1">
<widget class="QLabel" name="label_seedType">
<property name="text">
<string>TextLabel</string>
@ -241,14 +241,14 @@
</property>
</widget>
</item>
<item row="15" column="0">
<item row="16" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>View only:</string>
</property>
</widget>
</item>
<item row="15" column="1">
<item row="16" column="1">
<widget class="QLabel" name="label_viewOnly">
<property name="text">
<string>TextLabel</string>
@ -258,14 +258,14 @@
</property>
</widget>
</item>
<item row="18" column="0">
<item row="19" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Timestamp:</string>
</property>
</widget>
</item>
<item row="18" column="1">
<item row="19" column="1">
<widget class="QLabel" name="label_timestamp">
<property name="text">
<string>TextLabel</string>
@ -275,27 +275,47 @@
</property>
</widget>
</item>
<item row="17" column="0">
<item row="18" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Operating system:</string>
</property>
</widget>
</item>
<item row="17" column="1">
<item row="18" column="1">
<widget class="QLabel" name="label_OS">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="16" column="1">
<item row="17" column="1">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Target height:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_targetHeight">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>

@ -28,7 +28,7 @@ OutputInfoDialog::OutputInfoDialog(CoinsInfo *cInfo, QWidget *parent)
QString status = cInfo->spent() ? "spent" : (cInfo->frozen() ? "frozen" : "unspent");
ui->label_status->setText(status);
ui->label_amount->setText(QString("%1 XMR").arg(cInfo->displayAmount()));
ui->label_amount->setText(QString("%1 WOW").arg(cInfo->displayAmount()));
ui->label_creationHeight->setText(QString::number(cInfo->blockHeight()));
ui->label_globalIndex->setText(QString::number(cInfo->globalOutputIndex()));
ui->label_internalIndex->setText(QString::number(cInfo->internalOutputIndex()));

@ -5,17 +5,39 @@
#include "ui_passwordchangedialog.h"
#include <QPushButton>
#include <QMessageBox>
PasswordChangeDialog::PasswordChangeDialog(QWidget *parent)
PasswordChangeDialog::PasswordChangeDialog(QWidget *parent, Wallet *wallet)
: QDialog(parent)
, ui(new Ui::PasswordChangeDialog)
, m_wallet(wallet)
{
ui->setupUi(this);
ui->icon->setPixmap(QPixmap(":/assets/images/lock.png").scaledToWidth(32, Qt::SmoothTransformation));
bool noPassword = wallet->getPassword().isEmpty();
QString warning_str = noPassword ? "Your wallet is not password protected. Use this dialog to add a password to your wallet." :
"Your wallet is password protected and encrypted. Use this dialog to change your password.";
ui->label_warning->setText(warning_str);
QPixmap pixmap = noPassword ? QPixmap(":/assets/images/unlock.png") : QPixmap(":/assets/images/lock.png");
ui->icon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
if (noPassword) {
ui->label_currentPassword->hide();
ui->lineEdit_currentPassword->hide();
}
connect(ui->lineEdit_newPassword, &QLineEdit::textChanged, this, &PasswordChangeDialog::passwordsMatch);
connect(ui->lineEdit_confirmPassword, &QLineEdit::textChanged, this, &PasswordChangeDialog::passwordsMatch);
connect(ui->btn_Cancel, &QPushButton::clicked, [this]{
this->reject();
});
connect(ui->btn_OK, &QPushButton::clicked, this, &PasswordChangeDialog::setPassword);
ui->label_match->setVisible(false);
this->adjustSize();
}
@ -24,15 +46,28 @@ PasswordChangeDialog::~PasswordChangeDialog()
delete ui;
}
QString PasswordChangeDialog::getCurrentPassword() {
return ui->lineEdit_currentPassword->text();
void PasswordChangeDialog::passwordsMatch() {
bool match = ui->lineEdit_newPassword->text() == ui->lineEdit_confirmPassword->text();
ui->btn_OK->setEnabled(match);
ui->label_match->setHidden(match);
}
QString PasswordChangeDialog::getNewPassword() {
return ui->lineEdit_newPassword->text();
}
void PasswordChangeDialog::setPassword() {
QString currentPassword = ui->lineEdit_currentPassword->text();
QString newPassword = ui->lineEdit_newPassword->text();
void PasswordChangeDialog::passwordsMatch() {
bool match = ui->lineEdit_newPassword->text() == ui->lineEdit_confirmPassword->text();
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(match);
if (currentPassword != m_wallet->getPassword()) {
QMessageBox::warning(this, "Error", "Incorrect password");
ui->lineEdit_currentPassword->setText("");
ui->lineEdit_currentPassword->setFocus();
return;
}
if (m_wallet->setPassword(newPassword)) {
QMessageBox::information(this, "Information", "Password changed successfully");
this->accept();
}
else {
QMessageBox::warning(this, "Error", QString("Error: %1").arg(m_wallet->errorString()));
}
}

@ -5,6 +5,7 @@
#define FEATHER_PASSWORDCHANGEDIALOG_H
#include <QDialog>
#include "libwalletqt/Wallet.h"
namespace Ui {
class PasswordChangeDialog;
@ -15,16 +16,15 @@ class PasswordChangeDialog : public QDialog
Q_OBJECT
public:
explicit PasswordChangeDialog(QWidget *parent = nullptr);
explicit PasswordChangeDialog(QWidget *parent, Wallet *wallet);
~PasswordChangeDialog() override;
QString getCurrentPassword();
QString getNewPassword();
private:
Ui::PasswordChangeDialog *ui;
Wallet *m_wallet;
void passwordsMatch();
void setPassword();
};
#endif //FEATHER_PASSWORDCHANGEDIALOG_H

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>237</height>
<width>556</width>
<height>309</height>
</rect>
</property>
<property name="sizePolicy">
@ -30,7 +30,7 @@
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="label_warning">
<property name="text">
<string>Your wallet is password protected and encrypted. Use this dialog to change your password.</string>
</property>
@ -41,6 +41,22 @@
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
@ -65,7 +81,7 @@
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="label_currentPassword">
<property name="text">
<string>Current Password:</string>
</property>
@ -88,14 +104,45 @@
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_match">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Passwords do not match</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_Cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_OK">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -105,38 +152,5 @@
<tabstop>lineEdit_confirmPassword</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PasswordChangeDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PasswordChangeDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

@ -0,0 +1,26 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include "passworddialog.h"
#include "ui_passworddialog.h"
PasswordDialog::PasswordDialog(QWidget *parent, const QString &walletName, bool incorrectPassword)
: QDialog(parent)
, ui(new Ui::PasswordDialog)
{
ui->setupUi(this);
ui->label_wallet->setText(QString("Please enter password for wallet: %1").arg(walletName));
ui->label_incorrectPassword->setVisible(incorrectPassword);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this]{
password = ui->line_password->text();
});
this->adjustSize();
}
PasswordDialog::~PasswordDialog()
{
delete ui;
}

@ -0,0 +1,27 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_PASSWORDDIALOG_H
#define FEATHER_PASSWORDDIALOG_H
#include <QDialog>
namespace Ui {
class PasswordDialog;
}
class PasswordDialog : public QDialog
{
Q_OBJECT
public:
explicit PasswordDialog(QWidget *parent, const QString &walletName, bool incorrectPassword);
~PasswordDialog() override;
QString password = "";
private:
Ui::PasswordDialog *ui;
};
#endif //FEATHER_PASSWORDDIALOG_H

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PasswordDialog</class>
<widget class="QDialog" name="PasswordDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>832</width>
<height>158</height>
</rect>
</property>
<property name="windowTitle">
<string>Password required</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_wallet">
<property name="text">
<string>Please enter password for wallet: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_incorrectPassword">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#a40000;&quot;&gt;Incorrect password, try again.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="line_password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PasswordDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PasswordDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

@ -9,15 +9,14 @@
#include <QFileDialog>
#include <QMessageBox>
QrCodeDialog::QrCodeDialog(QWidget *parent, const QString &text, const QString &title)
QrCodeDialog::QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title)
: QDialog(parent)
, ui(new Ui::QrCodeDialog)
{
ui->setupUi(this);
this->setWindowTitle(title);
m_qrc = new QrCode(text, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
m_pixmap = m_qrc->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
ui->QrCode->setPixmap(m_pixmap);
connect(ui->btn_CopyImage, &QPushButton::clicked, this, &QrCodeDialog::copyImage);
@ -32,7 +31,11 @@ QrCodeDialog::QrCodeDialog(QWidget *parent, const QString &text, const QString &
QrCodeDialog::~QrCodeDialog()
{
delete ui;
delete m_qrc;
}
void QrCodeDialog::setQrCode(const QrCode &qrCode) {
m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
ui->QrCode->setPixmap(m_pixmap);
}
void QrCodeDialog::copyImage() {

@ -16,15 +16,15 @@ class QrCodeDialog : public QDialog
Q_OBJECT
public:
explicit QrCodeDialog(QWidget *parent, const QString &text, const QString &title = "Qr Code");
explicit QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title = "Qr Code");
~QrCodeDialog() override;
void setQrCode(const QrCode &qrCode);
private:
void copyImage();
void saveImage();
Ui::QrCodeDialog *ui;
QrCode *m_qrc;
QPixmap m_pixmap;
};

@ -4,27 +4,51 @@
#include "ui_seeddialog.h"
#include "seeddialog.h"
SeedDialog::SeedDialog(const QString &seed, QWidget *parent)
SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
: QDialog(parent)
, ui(new Ui::SeedDialog)
{
ui->setupUi(this);
ui->label_seedIcon->setPixmap(QPixmap(":/assets/images/seed.png").scaledToWidth(64, Qt::SmoothTransformation));
ui->label_restoreHeight->setText(QString::number(wallet->getWalletCreationHeight()));
QString seed_14_words = wallet->getCacheAttribute("feather.seed");
QString seed_25_words = wallet->getSeed();
if (seed_14_words.isEmpty()) {
ui->check_toggleSeedType->hide();
this->setSeed(seed_25_words);
} else {
this->setSeed(seed_14_words);
ui->widgetRestoreHeight->setVisible(false);
}
connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed_14_words](bool toggled){
this->setSeed(toggled ? seed_25_words : seed_14_words);
ui->widgetRestoreHeight->setVisible(toggled);
});
ui->label_restoreHeightHelp->setHelpText("Should you restore your wallet in the future, "
"specifying this block number will recover your wallet quicker.");
this->adjustSize();
}
void SeedDialog::setSeed(const QString &seed) {
ui->seed->setPlainText(seed);
int words = seed.split(" ").size();
ui->label_warning->setText(QString("<p>Please save these %1 words on paper (order is important). "
"This seed will allow you to recover your wallet in case "
"of computer failure."
"</p>"
"<b>WARNING:</b>"
"<ul>"
"<li>Never disclose your seed.</li>"
"<li>Never type it on a website</li>"
"<li>Do not store it electronically</li>"
"</ul>").arg(words));
this->adjustSize();
"This seed will allow you to recover your wallet in case "
"of computer failure."
"</p>"
"<b>WARNING:</b>"
"<ul>"
"<li>Never disclose your seed.</li>"
"<li>Never type it on a website</li>"
"<li>Do not store it electronically</li>"
"</ul>").arg(words));
}
SeedDialog::~SeedDialog()

@ -5,6 +5,7 @@
#define FEATHER_SEEDDIALOG_H
#include <QDialog>
#include "libwalletqt/Wallet.h"
namespace Ui {
class SeedDialog;
@ -15,10 +16,12 @@ class SeedDialog : public QDialog
Q_OBJECT
public:
explicit SeedDialog(const QString& seed, QWidget *parent = nullptr);
explicit SeedDialog(Wallet *wallet, QWidget *parent = nullptr);
~SeedDialog() override;
private:
void setSeed(const QString &seed);
Ui::SeedDialog *ui;
};

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>542</width>
<height>244</height>
<width>590</width>
<height>346</height>
</rect>
</property>
<property name="windowTitle">
@ -34,29 +34,94 @@
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="seed">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>125</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPlainTextEdit" name="seed">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>125</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>300</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widgetRestoreHeight" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="HelpLabel" name="label_restoreHeightHelp">
<property name="text">
<string>Restore height:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_restoreHeight">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="check_toggleSeedType">
<property name="text">
<string>Show 25 word seed</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_warning">
<property name="text">
@ -79,6 +144,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>HelpLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>

@ -6,18 +6,29 @@
#include "libwalletqt/CoinsInfo.h"
#include "libwalletqt/WalletManager.h"
#include <QDebug>
#include "utils.h"
TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txInfo, QWidget *parent)
#include <QMessageBox>
TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent)
: QDialog(parent)
, ui(new Ui::TransactionInfoDialog)
, m_coins(coins)
, m_wallet(wallet)
, m_txInfo(txInfo)
{
ui->setupUi(this);
ui->txid->setText(QString(txInfo->hash()));
ui->txid->setCursorPosition(0);
m_txProofWidget = new TxProofWidget(this, wallet, txInfo);
ui->label_txid->setText(QString(txInfo->hash()));
if (txInfo->direction() == TransactionInfo::Direction_In) {
ui->txKey->hide();
} else {
QString txKey = m_wallet->getTxKey(txInfo->hash());
txKey = txKey.isEmpty() ? "unknown" : txKey;
ui->label_txKey->setText(txKey);
}
ui->label_status->setText(QString("Status: %1 confirmations").arg(txInfo->confirmations()));
ui->label_date->setText(QString("Date: %1").arg(txInfo->timestamp().toString("yyyy-MM-dd HH:mm")));
@ -30,7 +41,7 @@ TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txIn
ui->label_fee->setText(QString("Fee: %1").arg(txInfo->isCoinbase() ? WalletManager::displayAmount(0) : fee));
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(txInfo->unlockTime()));
qDebug() << m_coins->coins_from_txid(txInfo->hash());
qDebug() << m_wallet->coins()->coins_from_txid(txInfo->hash());
QString destinations = txInfo->destinations_formatted();
if (destinations.isEmpty()) {
@ -41,6 +52,8 @@ TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txIn
ui->destinations->setText(destinations);
}
ui->txProofWidget->addWidget(m_txProofWidget);
this->adjustSize();
}

@ -8,6 +8,8 @@
#include <QtSvg/QSvgWidget>
#include "libwalletqt/Coins.h"
#include "libwalletqt/TransactionInfo.h"
#include "libwalletqt/Wallet.h"
#include "widgets/txproofwidget.h"
namespace Ui {
class TransactionInfoDialog;
@ -18,14 +20,15 @@ class TransactionInfoDialog : public QDialog
Q_OBJECT
public:
explicit TransactionInfoDialog(Coins *coins, TransactionInfo *txInfo, QWidget *parent = nullptr);
explicit TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr);
~TransactionInfoDialog() override;
private:
Ui::TransactionInfoDialog *ui;
TransactionInfo *m_txInfo;
Coins *m_coins;
Wallet *m_wallet;
TxProofWidget *m_txProofWidget;
};
#endif //FEATHER_TRANSACTIONINFODIALOG_H

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>547</width>
<height>332</height>
<width>829</width>
<height>570</height>
</rect>
</property>
<property name="windowTitle">
@ -15,32 +15,41 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label">
<property name="text">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Transaction ID:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_txid">
<property name="text">
<string>txid</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txid">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>525</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>txid</string>
</property>
<property name="readOnly">
<bool>true</bool>
<widget class="QGroupBox" name="txKey">
<property name="title">
<string>Transaction key:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_txKey">
<property name="text">
<string>txKey</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
@ -149,6 +158,16 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="txProofWidget"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

@ -0,0 +1,186 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include "txconfadvdialog.h"
#include "ui_txconfadvdialog.h"
#include "libwalletqt/WalletManager.h"
#include "qrcode/QrCode.h"
#include "dialog/qrcodedialog.h"
#include "utils/utils.h"
#include "libwalletqt/PendingTransactionInfo.h"
#include "libwalletqt/Transfer.h"
#include "libwalletqt/Input.h"
#include "model/ModelUtils.h"
#include <QFileDialog>
#include <QMessageBox>
TxConfAdvDialog::TxConfAdvDialog(AppContext *ctx, const QString &description, QWidget *parent)
: QDialog(parent)
, ui(new Ui::TxConfAdvDialog)
, m_ctx(ctx)
, m_exportUnsignedMenu(new QMenu(this))
, m_exportSignedMenu(new QMenu(this))
{
ui->setupUi(this);
m_exportUnsignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::unsignedCopy);
m_exportUnsignedMenu->addAction("Show as QR code", this, &TxConfAdvDialog::unsignedQrCode);
m_exportUnsignedMenu->addAction("Save to file", this, &TxConfAdvDialog::unsignedSaveFile);
ui->btn_exportUnsigned->setMenu(m_exportUnsignedMenu);
m_exportSignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::signedCopy);
m_exportSignedMenu->addAction("Save to file", this, &TxConfAdvDialog::signedSaveFile);
ui->btn_exportSigned->setMenu(m_exportSignedMenu);
if (m_ctx->currentWallet->viewOnly()) {
ui->btn_exportSigned->hide();
ui->btn_send->hide();
}
ui->label_description->setText(QString("Description: %1").arg(description));
connect(ui->btn_sign, &QPushButton::clicked, this, &TxConfAdvDialog::signTransaction);
connect(ui->btn_send, &QPushButton::clicked, this, &TxConfAdvDialog::broadcastTransaction);
connect(ui->btn_close, &QPushButton::clicked, this, &TxConfAdvDialog::closeDialog);
ui->inputs->setFont(ModelUtils::getMonospaceFont());
ui->outputs->setFont(ModelUtils::getMonospaceFont());
this->adjustSize();
}
void TxConfAdvDialog::setTransaction(PendingTransaction *tx) {
ui->btn_sign->hide();
m_tx = tx;
m_tx->refresh();
PendingTransactionInfo *ptx = m_tx->transaction(0);
ui->txid->setText(tx->txid().first());
ui->amount->setText(WalletManager::displayAmount(tx->amount()));
ui->fee->setText(WalletManager::displayAmount(ptx->fee()));
ui->total->setText(WalletManager::displayAmount(tx->amount() + ptx->fee()));
auto size_str = [this]{
if (m_ctx->currentWallet->viewOnly()) {
return QString("Size: %1 bytes (unsigned)").arg(QString::number(m_tx->unsignedTxToBin().size()));
} else {
auto size = m_tx->signedTxToHex(0).size() / 2;
return QString("Size: %1 bytes (%2 bytes unsigned)").arg(QString::number(size), QString::number(m_tx->unsignedTxToBin().size()));
}
}();
ui->label_size->setText(size_str);
this->setupConstructionData(ptx);
}
void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
m_utx = utx;
m_utx->refresh();
ui->btn_exportUnsigned->hide();
ui->btn_exportSigned->hide();
ui->btn_sign->show();
ui->btn_send->hide();
ui->txid->setText("n/a");
ui->label_size->setText("Size: n/a");
ui->amount->setText(WalletManager::displayAmount(utx->amount(0)));
ui->fee->setText(WalletManager::displayAmount(utx->fee(0)));
ui->total->setText(WalletManager::displayAmount(utx->amount(0) + utx->fee(0)));
ConstructionInfo *ci = m_utx->constructionInfo(0);
this->setupConstructionData(ci);
}
void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
QString inputs_str;
auto inputs = ci->inputs();
for (const auto& i: inputs) {
inputs_str += QString("%1 %2\n").arg(i->pubKey(), WalletManager::displayAmount(i->amount()));
}
ui->inputs->setText(inputs_str);
ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(inputs.size())));
QString outputs_str;
auto outputs = ci->outputs();
for (const auto& o: outputs) {
outputs_str += QString("%1 %2\n").arg(o->address(), WalletManager::displayAmount(o->amount()));
}
ui->outputs->setText(outputs_str);
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(outputs.size())));
ui->label_ringSize->setText(QString("Ring size: %1").arg(QString::number(ci->minMixinCount() + 1)));
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(QString::number(ci->unlockTime())));
}
void TxConfAdvDialog::signTransaction() {
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
QString fn = QFileDialog::getSaveFileName(this, "Save signed transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
if(fn.isEmpty()) return;
m_utx->sign(fn) ? QMessageBox::information(this, "Sign transaction", "Transaction saved successfully")
: QMessageBox::warning(this, "Sign transaction", "Failes to save transaction to file.");
}
void TxConfAdvDialog::unsignedSaveFile() {
QString defaultName = QString("%1_unsigned_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*unsigned_monero_tx)");
if(fn.isEmpty()) return;
m_tx->saveToFile(fn) ? QMessageBox::information(this, "Transaction saved to file", "Transaction saved successfully")
: QMessageBox::warning(this, "Save transaction", "Failed to save transaction to file.");
}
void TxConfAdvDialog::signedSaveFile() {
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
if(fn.isEmpty()) return;
m_tx->saveToFile(fn) ? QMessageBox::information(this, "Transaction saved to file", "Transaction saved successfully")
: QMessageBox::warning(this, "Save transaction", "Failed to save transaction to file.");
}
void TxConfAdvDialog::unsignedQrCode() {
if (m_tx->unsignedTxToBin().size() > 2953) {
QMessageBox::warning(this, "Unable to show QR code", "Transaction size exceeds the maximum size for QR codes (2953 bytes).");
return;
}
QrCode qr(m_tx->unsignedTxToBin(), QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::LOW);
auto *dialog = new QrCodeDialog(this, qr, "Unsigned Transaction");
dialog->exec();
dialog->deleteLater();
}
void TxConfAdvDialog::unsignedCopy() {
Utils::copyToClipboard(m_tx->unsignedTxToBase64());
}
void TxConfAdvDialog::signedCopy() {
Utils::copyToClipboard(m_tx->signedTxToHex(0));
}
void TxConfAdvDialog::signedQrCode() {
}
void TxConfAdvDialog::broadcastTransaction() {
if (m_tx == nullptr) return;
m_ctx->currentWallet->commitTransactionAsync(m_tx);
QDialog::accept();
}
void TxConfAdvDialog::closeDialog() {
if (m_tx != nullptr)
m_ctx->currentWallet->disposeTransaction(m_tx);
if (m_utx != nullptr)
m_ctx->currentWallet->disposeTransaction(m_utx);
QDialog::reject();
}
TxConfAdvDialog::~TxConfAdvDialog() {
delete ui;
}

@ -0,0 +1,54 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_TXCONFADVDIALOG_H
#define FEATHER_TXCONFADVDIALOG_H
#include <QDialog>
#include <QStandardItemModel>
#include <QAbstractButton>
#include <QMenu>
#include "libwalletqt/PendingTransaction.h"
#include "appcontext.h"
namespace Ui {
class TxConfAdvDialog;
}
class TxConfAdvDialog : public QDialog
{
Q_OBJECT
public:
explicit TxConfAdvDialog(AppContext *ctx, const QString &description, QWidget *parent = nullptr);
~TxConfAdvDialog() override;
void setTransaction(PendingTransaction *tx);
void setUnsignedTransaction(UnsignedTransaction *utx);
private:
void setupConstructionData(ConstructionInfo *ci);
void signTransaction();
void broadcastTransaction();
void closeDialog();
void unsignedCopy();
void unsignedQrCode();
void unsignedSaveFile();
void signedCopy();
void signedQrCode();
void signedSaveFile();
Ui::TxConfAdvDialog *ui;
AppContext *m_ctx;
PendingTransaction *m_tx = nullptr;
UnsignedTransaction *m_utx = nullptr;
QMenu *m_exportUnsignedMenu;
QMenu *m_exportSignedMenu;
};
#endif //FEATHER_TXCONFADVDIALOG_H

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

Loading…
Cancel
Save