Compare commits

..

1 Commits

Author SHA1 Message Date
dsc 6745f49e8f Commit cd1cd5cb75
4 years ago

@ -14,9 +14,11 @@ 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
- TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make -j6 release-static
- make -j8 release-static
environment:
OPENSSL_ROOT_DIR: /usr/local/openssl/
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
@ -28,7 +30,7 @@ steps:
- name: files_linux_release
path: /files
commands:
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
@ -42,7 +44,7 @@ volumes:
path: /var/drone/ccache_linux_release/
- name: files_linux_release
host:
path: /build/feather_files/files/linux-release/
path: /mnt/storage1/feather_files/files/linux-release/
---
@ -54,7 +56,7 @@ steps:
- name: build
image: feather:appimage
commands:
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export BRANCH="$DRONE_SOURCE_BRANCH"
- bash ./contrib/build-appimage.sh
- name: deploy
@ -73,7 +75,7 @@ steps:
volumes:
- name: files_linux_appimage
host:
path: /build/feather_files/files/linux-release-appimage/
path: /mnt/storage1/feather_files/files/linux-release-appimage/
---
@ -92,9 +94,11 @@ 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" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make -j6 windows-mxe-release
- PATH=/mxe/usr/bin/:$PATH make -j8 windows-mxe-release
environment:
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
- name: deploy
@ -105,7 +109,7 @@ steps:
- name: files_win_release
path: /files
commands:
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
@ -118,7 +122,7 @@ volumes:
path: /var/drone/ccache_win_release/
- name: files_win_release
host:
path: /build/feather_files/files/windows-mxe-release/
path: /mnt/storage1/feather_files/files/windows-mxe-release/
---
@ -143,7 +147,7 @@ steps:
- name: files_mac_release
path: /files
commands:
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
- export FN="feather-`git rev-parse --short HEAD`.zip"
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
- mkdir -p "$TARGET_DIR"
- echo "writing to $TARGET_DIR/$FN"
@ -153,9 +157,9 @@ steps:
volumes:
- name: files_mac_release
host:
path: /build/feather_files/files/mac-release/
path: /mnt/storage1/feather_files/files/mac-release/
---
kind: signature
hmac: 527d334190a8a824b3b781a05ae4c7d87f4fa2bc37ebc53a96db91f925fa4a52
hmac: 91e773a27d27f29ea62f5df500664b733df641b7c31202c8e5558174fd046fba
...

3
.gitmodules vendored

@ -7,6 +7,3 @@
[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" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" 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 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 'TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" 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 '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` and will **not** have Tor embedded.
The resulting Mac OS application can be found `build/bin/feather.app`.

@ -11,9 +11,7 @@ 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(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(BUILD_TOR "Build Tor" OFF)
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)
@ -30,7 +28,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
set(MONERO_HEAD "a6bf6dbb8f1637ccffee1a30d2285324a3ce4d66")
set(MONERO_HEAD "a1404e92cb439ba0f120e7c4a579ed0b9a0372a4")
set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64")
set(BUILD_64 ON)
@ -160,7 +158,6 @@ 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()
@ -176,48 +173,47 @@ if(UNIX AND NOT APPLE)
endif()
endif()
if("$ENV{DRONE}" STREQUAL "true")
message(STATUS "We are inside a static compile with Drone CI")
endif()
# Tor/torsocks
set(TOR_TAG "tor-0.4.3.5")
set(TOR_DIR "${CMAKE_SOURCE_DIR}/contrib/tor")
# 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()
# 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()
if(BUILD_TOR AND APPLE)
execute_process(COMMAND bash -c "touch ${CMAKE_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
ENDIF()
message(STATUS "Embedding Tor binary at ${TOR}")
else()
message(STATUS "Skipping Tor inclusion because -DTOR=Off")
if(UNIX AND NOT APPLE)
set(TOR_LIB "libtorsocks.so")
elseif(APPLE)
set(TOR_LIB "libtorsocks.dylib")
endif()
# 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}")
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/")
endif()
message(STATUS "Embedding XMRig binary at ${XMRIG}")
else()
message(STATUS "Skipping XMRig inclusion because -DXMRIG=Off")
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()
endif()
if(MINGW)
@ -365,8 +361,4 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
if(APPLE)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/KDMacTouchBar")
endif()
add_subdirectory(src)

@ -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 qtdeclarative -skip qtmultimedia \
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
-nomake examples -nomake tests -nomake tools
@ -305,9 +305,3 @@ 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 qtmultimedia
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
# plugins
RUN cd mxe && make -j$THREADS MXE_PLUGIN_DIRS='/mxe/plugins/apps/' MXE_TARGETS='x86_64-w64-mingw32.static' tor
@ -72,10 +72,3 @@ 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,8 +42,7 @@ 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
- `-DTOR=/path/to/tor` - Embed a Tor executable inside Feather
- `-DXMRIG=/path/to/xmrig` - Embed a XMRig executable inside Feather
- `-DBUILD_TOR=OFF` - disable embedded Tor
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
And:

@ -31,8 +31,6 @@ CMAKEFLAGS = \
-DBUILD_64=On \
-DBUILD_TESTS=Off \
-DXMRTO=ON \
-DXMRIG=Off \
-DTOR=Off \
-DCMAKE_CXX_STANDARD=11 \
-DCMAKE_VERBOSE_MAKEFILE=On \
-DINSTALL_VENDORED_LIBUNBOUND=Off \
@ -42,33 +40,29 @@ CMAKEFLAGS = \
$(CMAKEFLAGS_EXTRA)
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
release-static: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
release-static: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
release-static: CMAKEFLAGS += -DBUILD_TOR=On
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 += -DTOR=$(or ${TOR},OFF)
windows-mxe-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
windows-mxe-release: CMAKEFLAGS += -DBUILD_TOR=On
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 += -DTOR=$(or ${TOR},OFF)
windows-mxe-debug: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TOR=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)

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

@ -1 +1 @@
Subproject commit a6bf6dbb8f1637ccffee1a30d2285324a3ce4d66
Subproject commit a1404e92cb439ba0f120e7c4a579ed0b9a0372a4

@ -42,16 +42,14 @@ file(GLOB SOURCE_FILES
"dialog/*.cpp"
)
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")
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")
endif()
set(EXECUTABLE_FLAG)
@ -76,8 +74,7 @@ endif()
add_executable(feather ${EXECUTABLE_FLAG} main.cpp
${SOURCE_FILES}
${RESOURCES}
${ASSETS_TOR}
${ASSETS_XMRIG}
${ASSETS_OS}
)
# mac os bundle
@ -125,16 +122,12 @@ if(DONATE_BEG)
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
endif()
if(XMRTO)
target_compile_definitions(feather PRIVATE XMRTO=1)
endif()
if(TOR)
target_compile_definitions(feather PRIVATE HAS_TOR=1)
if(BUILD_TOR)
target_compile_definitions(feather PRIVATE BUILD_TOR=1)
endif()
if(XMRIG)
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
if(XMRTO)
target_compile_definitions(feather PRIVATE XMRTO=1)
endif()
if(HAVE_SYS_PRCTL_H)
@ -201,14 +194,6 @@ target_link_libraries(feather
${QRENCODE_LIBRARY}
)
if(APPLE)
target_link_libraries(feather
KDMacTouchBar
)
target_include_directories(feather
PUBLIC ../contrib/KDMacTouchBar)
endif()
if(NOT APPLE)
target_link_libraries(feather
Qt5::QSvgIconPlugin

@ -9,14 +9,12 @@
#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"
@ -30,6 +28,7 @@
#include "model/SubaddressModel.h"
#include "utils/keysfiles.h"
#include "utils/networktype.h"
#include "utils/config.h"
Prices *AppContext::prices = nullptr;
@ -148,17 +147,10 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
AppContext::prices = new Prices();
// xmr.to
#ifdef XMRTO
#if defined(XMRTO)
this->XMRTo = new XmrTo(this);
#endif
// XMRig
#ifdef HAS_XMRIG
this->XMRig = new XmRig(this->configDirectory, this);
if(!this->isTails)
this->XMRig->prepare();
#endif
this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDirectory);
Monero::Utils::onStartup();
@ -196,7 +188,7 @@ void AppContext::initWS() {
void AppContext::onCancelTransaction(PendingTransaction *tx, const QString &address) {
// tx cancelled by user
double amount = tx->amount() / globals::cdiv;
double amount = tx->amount() / AppContext::cdiv;
emit createTransactionCancelled(address, amount);
this->currentWallet->disposeTransaction(tx);
}
@ -235,8 +227,8 @@ void AppContext::onCreateTransaction(const QString &address, const double amount
return;
}
auto balance = this->currentWallet->balance() / globals::cdiv;
auto unlocked_balance = this->currentWallet->unlockedBalance() / globals::cdiv;
auto balance = this->currentWallet->balance() / AppContext::cdiv;
auto unlocked_balance = this->currentWallet->unlockedBalance() / AppContext::cdiv;
if(!all && amount > unlocked_balance) {
emit createTransactionError("Not enough money to spend");
return;
@ -245,7 +237,7 @@ void AppContext::onCreateTransaction(const QString &address, const double amount
return;
}
auto amount_num = static_cast<quint64>(amount * globals::cdiv);
auto amount_num = static_cast<quint64>(amount * AppContext::cdiv);
qDebug() << "creating tx";
if(all || amount == balance)
this->currentWallet->createTransactionAllAsync(address, "", this->tx_mixin, this->tx_priority);
@ -318,14 +310,13 @@ void AppContext::onWalletOpened(Wallet *wallet) {
emit walletOpenedError(errMsg);
} else {
this->walletClose(false);
emit walletOpenPasswordNeeded(this->walletPassword.isEmpty(), wallet->path());
emit walletOpenPasswordNeeded(this->walletPassword.isEmpty());
}
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);
@ -339,9 +330,9 @@ void AppContext::onWalletOpened(Wallet *wallet) {
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
connect(this->currentWallet, &Wallet::connectionStatusChanged, this, &AppContext::onConnectionStatusChanged);
emit walletOpened();
this->nodes->connectToNode();
emit walletOpened();
this->updateBalance();
#ifdef DONATE_BEG
@ -350,18 +341,6 @@ 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) {
@ -401,11 +380,7 @@ 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);
@ -519,17 +494,31 @@ 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");
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());
}
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());
}
}
@ -552,43 +541,6 @@ 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;
@ -662,9 +614,8 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) {
}
void AppContext::donateBeg() {
if(this->currentWallet == nullptr) return;
if(this->networkType != NetworkType::Type::MAINNET) return;
if(this->currentWallet->viewOnly()) return;
if(this->networkType != NetworkType::Type::MAINNET)
return;
auto donationCounter = config()->get(Config::donateBeg).toInt();
if(donationCounter == -1)
@ -692,19 +643,19 @@ AppContext::~AppContext() {
// ############################################## LIBWALLET QT #########################################################
void AppContext::onMoneySpent(const QString &txId, quint64 amount) {
auto amount_num = amount / globals::cdiv;
auto amount_num = amount / AppContext::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 / globals::cdiv;
auto amount_num = amount / AppContext::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 / globals::cdiv;
auto amount_num = amount / AppContext::cdiv;
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
if(this->currentWallet->synchronized()) {
@ -791,12 +742,12 @@ void AppContext::storeWallet() {
void AppContext::updateBalance() {
if(!this->currentWallet)
return;
throw std::runtime_error("this should not happen, ever");
AppContext::balance = this->currentWallet->balance() / globals::cdiv;
AppContext::balance = this->currentWallet->balance() / AppContext::cdiv;
auto balance_str = QString::number(balance, 'f');
double unlocked = this->currentWallet->unlockedBalance() / globals::cdiv;
double unlocked = this->currentWallet->unlockedBalance() / AppContext::cdiv;
auto unlocked_str = QString::number(unlocked, 'f');
emit balanceUpdated(balance, unlocked, balance_str, unlocked_str);

@ -14,7 +14,6 @@
#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"
@ -57,7 +56,6 @@ public:
QString walletPath;
QString walletPassword = "";
bool walletViewOnly = false;
NetworkType::Type networkType;
QString applicationPath;
@ -69,7 +67,7 @@ public:
const unsigned int kdfRounds = 1;
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
quint32 tx_mixin = static_cast<const quint32 &>(10);
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
static constexpr const double cdiv = 1e12;
QNetworkAccessManager *network;
QNetworkAccessManager *networkClearnet;
@ -78,7 +76,6 @@ public:
Tor *tor;
WSClient *ws;
XmrTo *XMRTo;
XmRig *XMRig;
Nodes *nodes;
static Prices *prices;
static WalletKeysFilesModel *wallets;
@ -92,8 +89,6 @@ 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();
@ -103,7 +98,6 @@ 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);
@ -146,7 +140,7 @@ signals:
void walletCreatedError(const QString &msg);
void walletCreated(Wallet *wallet);
void walletOpenedError(QString msg);
void walletOpenPasswordNeeded(bool invalidPassword, QString path);
void walletOpenPasswordNeeded(bool invalidPassword);
void transactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
void createTransactionError(QString message);
void createTransactionCancelled(QString address, double amount);
@ -155,7 +149,6 @@ 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);
@ -167,7 +160,6 @@ signals:
void initiateTransaction();
void endTransaction();
void walletClosing();
void setTitle(const QString &title); // set window title
private:
void sorry();

@ -23,7 +23,6 @@
<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>
@ -31,13 +30,10 @@
<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>
@ -45,7 +41,6 @@
<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/network.png</file>
@ -96,8 +91,6 @@
<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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

@ -1 +0,0 @@
<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

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

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

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

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

@ -58,7 +58,7 @@ void CLI::onWalletOpenedError(const QString &err) {
return this->finishedError(err);
}
void CLI::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
void CLI::onWalletOpenPasswordRequired(bool invalidPassword) {
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, const QString &path);
void onWalletOpenPasswordRequired(bool invalidPassword);
private:
AppContext *ctx;

@ -7,7 +7,6 @@
#include "utils/utils.h"
#include "dialog/outputinfodialog.h"
#include "dialog/outputsweepdialog.h"
#include "mainwindow.h"
#include <QClipboard>
#include <QDebug>
@ -22,7 +21,6 @@ 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);
@ -42,14 +40,14 @@ CoinsWidget::CoinsWidget(QWidget *parent)
// context menu
ui->coins->setContextMenuPolicy(Qt::CustomContextMenu);
m_thawOutputAction = new QAction("Thaw output", this);
m_freezeOutputAction = new QAction("Freeze output", this);
m_thawOutputAction = new QAction("Thaw output");
m_freezeOutputAction = new QAction("Freeze output");
m_freezeAllSelectedAction = new QAction("Freeze selected", this);
m_thawAllSelectedAction = new QAction("Thaw selected", this);
m_freezeAllSelectedAction = new QAction("Freeze selected");
m_thawAllSelectedAction = new QAction("Thaw selected");
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details", this);
m_sweepOutputAction = new QAction("Sweep output", this);
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details");
m_sweepOutputAction = new QAction("Sweep output");
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
connect(m_viewOutputAction, &QAction::triggered, this, &CoinsWidget::viewOutput);
@ -65,19 +63,13 @@ CoinsWidget::CoinsWidget(QWidget *parent)
void CoinsWidget::setModel(CoinsModel * model, Coins * coins) {
m_coins = coins;
m_model = model;
m_proxyModel = new CoinsProxyModel(this);
m_proxyModel = new CoinsProxyModel;
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);
@ -111,15 +103,10 @@ 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);
}
@ -207,7 +194,6 @@ void CoinsWidget::onSweepOutput() {
qCritical() << "key image: " << keyImage;
emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
dialog->deleteLater();
}
void CoinsWidget::copy(copyField field) {

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

@ -53,7 +53,7 @@
<item>
<widget class="QPlainTextEdit" name="copyrightText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>

@ -1,62 +0,0 @@
// 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;
}

@ -1,35 +0,0 @@
// 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

@ -1,111 +0,0 @@
<?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,11 +43,7 @@ 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");
QString os = QSysInfo::prettyProductName();
if (ctx->isTails) {
os = QString("Tails %1").arg(TailsOS::version());
}
ui->label_OS->setText(os);
ui->label_OS->setText(QSysInfo::prettyProductName());
ui->label_timestamp->setText(QString::number(QDateTime::currentSecsSinceEpoch()));
connect(ui->btn_Copy, &QPushButton::clicked, this, &DebugInfoDialog::copyToClipboad);

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

@ -16,15 +16,15 @@ class QrCodeDialog : public QDialog
Q_OBJECT
public:
explicit QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title = "Qr Code");
explicit QrCodeDialog(QWidget *parent, const QString &text, 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;
};

@ -8,23 +8,16 @@
#include "libwalletqt/WalletManager.h"
#include <QDebug>
TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent)
TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txInfo, QWidget *parent)
: QDialog(parent)
, ui(new Ui::TransactionInfoDialog)
, m_wallet(wallet)
, m_coins(coins)
, m_txInfo(txInfo)
{
ui->setupUi(this);
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->txid->setText(QString(txInfo->hash()));
ui->txid->setCursorPosition(0);
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")));
@ -37,7 +30,7 @@ TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *tx
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_wallet->coins()->coins_from_txid(txInfo->hash());
qDebug() << m_coins->coins_from_txid(txInfo->hash());
QString destinations = txInfo->destinations_formatted();
if (destinations.isEmpty()) {

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

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>547</width>
<height>436</height>
<height>332</height>
</rect>
</property>
<property name="windowTitle">
@ -15,41 +15,32 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<widget class="QLabel" name="label">
<property name="text">
<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="QGroupBox" name="txKey">
<property name="title">
<string>Transaction key:</string>
<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>
</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>

@ -1,186 +0,0 @@
// 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;
}

@ -1,54 +0,0 @@
// 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

@ -1,279 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TxConfAdvDialog</class>
<widget class="QDialog" name="TxConfAdvDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>542</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>800</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Transaction</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Transaction ID:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txid">
<property name="text">
<string>txid</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_amount">
<property name="text">
<string>Amount: </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="amount">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_fee">
<property name="text">
<string>Fee: </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="fee">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Total:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="total">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_description">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_size">
<property name="text">
<string>Size: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_unlockTime">
<property name="text">
<string>Unlock time: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_ringSize">
<property name="text">
<string>Ringsize:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_inputs">
<property name="text">
<string>Inputs</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="inputs">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_outputs">
<property name="text">
<string>Outputs</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="outputs">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="btn_exportUnsigned">
<property name="text">
<string>Export unsigned</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btn_exportSigned">
<property name="text">
<string>Export signed</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</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_sign">
<property name="text">
<string>Sign</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_send">
<property name="text">
<string>Send</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>

@ -6,16 +6,12 @@
#include "appcontext.h"
#include "utils/config.h"
#include "model/ModelUtils.h"
#include "libwalletqt/WalletManager.h"
#include "txconfadvdialog.h"
#include "globals.h"
#include <QMessageBox>
TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent)
TxConfDialog::TxConfDialog(PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent)
: QDialog(parent)
, ui(new Ui::TxConfDialog)
, m_ctx(ctx)
, m_tx(tx)
, m_address(address)
, m_description(description)
@ -32,31 +28,44 @@ TxConfDialog::TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QStrin
};
QString amount = WalletManager::displayAmount(tx->amount());
QString amount_fiat = convert(tx->amount() / globals::cdiv);
QString amount_fiat = convert(tx->amount() / AppContext::cdiv);
ui->label_amount->setText(QString("%1 (%2 %3)").arg(amount, amount_fiat, preferredCur));
QString fee = WalletManager::displayAmount(tx->fee());
QString fee_fiat = convert(tx->fee() / globals::cdiv);
QString fee_fiat = convert(tx->fee() / AppContext::cdiv);
ui->label_fee->setText(QString("%1 (%2 %3)").arg(fee, fee_fiat, preferredCur));
QString total = WalletManager::displayAmount(tx->amount() + tx->fee());
QString total_fiat = convert((tx->amount() + tx->fee()) / globals::cdiv);
QString total_fiat = convert((tx->amount() + tx->fee()) / AppContext::cdiv);
ui->label_total->setText(QString("%1 (%2 %3)").arg(total, total_fiat, preferredCur));
ui->label_address->setText(ModelUtils::displayAddress(address, 2));
ui->label_address->setFont(ModelUtils::getMonospaceFont());
ui->label_address->setToolTip(address);
ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Send");
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::showAdvanced);
this->adjustSize();
}
void TxConfDialog::setShowAdvanced() {
this->showAdvanced = true;
QDialog::reject();
void TxConfDialog::showAdvanced() {
const auto amount = m_tx->amount() / AppContext::cdiv;
const auto fee = m_tx->fee() / AppContext::cdiv;
QString body = QString("Address: %2\n").arg(m_address.left(60));
body += m_address.mid(60) + "\n";
if(!m_description.isEmpty())
body = QString("%1Description: %2\n").arg(body, m_description);
body = QString("%1Amount: %2 XMR\n").arg(body, QString::number(amount));
body = QString("%1Fee: %2 XMR\n").arg(body, QString::number(fee));
body = QString("%1Ringsize: %2").arg(body, QString::number(m_mixin + 1));
auto subaddrIndices = m_tx->subaddrIndices();
for (int i = 0; i < subaddrIndices.count(); ++i){
body = QString("%1\nSpending address index: %2").arg(body, QString::number(subaddrIndices.at(i).toInt()));
}
QMessageBox::information(this, "Transaction information", body);
}
TxConfDialog::~TxConfDialog() {

@ -7,7 +7,6 @@
#include <QDialog>
#include "libwalletqt/PendingTransaction.h"
#include "libwalletqt/WalletManager.h"
#include "appcontext.h"
namespace Ui {
class TxConfDialog;
@ -18,18 +17,13 @@ class TxConfDialog : public QDialog
Q_OBJECT
public:
explicit TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent = nullptr);
explicit TxConfDialog(PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent = nullptr);
~TxConfDialog() override;
bool showAdvanced = false;
private:
void setShowAdvanced();
void saveToFile();
void copyToClipboard();
void showAdvanced();
Ui::TxConfDialog *ui;
AppContext *m_ctx;
PendingTransaction *m_tx;
QString m_address;
QString m_description;

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>844</width>
<width>655</width>
<height>248</height>
</rect>
</property>

@ -1,59 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include "viewonlydialog.h"
#include "ui_viewonlydialog.h"
ViewOnlyDialog::ViewOnlyDialog(AppContext *ctx, QWidget *parent)
: QDialog(parent)
, ui(new Ui::ViewOnlyDialog), m_ctx(ctx)
{
ui->setupUi(this);
ui->label_restoreHeight->setText(QString::number(ctx->currentWallet->getWalletCreationHeight()));
ui->label_primaryAddress->setText(ctx->currentWallet->address(0, 0));
ui->label_secretViewKey->setText(ctx->currentWallet->getSecretViewKey());
connect(ui->btn_Copy, &QPushButton::clicked, this, &ViewOnlyDialog::copyToClipboad);
connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet);
ui->btn_Save->setEnabled(!m_ctx->currentWallet->viewOnly());
this->adjustSize();
}
void ViewOnlyDialog::onWriteViewOnlyWallet(){
QString fn = QFileDialog::getSaveFileName(this, "Save .keys wallet file", QDir::homePath(), "Monero wallet (*.keys)");
if(fn.isEmpty()) return;
if(!fn.endsWith(".keys")) fn += ".keys";
QString passwd;
QInputDialog passwordDialog(this);
passwordDialog.setInputMode(QInputDialog::TextInput);
passwordDialog.setTextEchoMode(QLineEdit::Password);
passwordDialog.setWindowTitle("View-Only wallet password");
passwordDialog.setLabelText("Protect this view-only wallet with a password?");
passwordDialog.resize(300, 100);
if((bool)passwordDialog.exec())
passwd = passwordDialog.textValue();
m_ctx->currentWallet->createViewOnly(fn, passwd);
QMessageBox::information(this, "Information", "View-only wallet successfully written to disk.");
}
void ViewOnlyDialog::copyToClipboad() {
QString text = "";
text += QString("Address: %1\n").arg(ui->label_primaryAddress->text());
text += QString("Secret view key: %1\n").arg(ui->label_secretViewKey->text());
text += QString("Restore height: %1\n").arg(ui->label_restoreHeight->text());
Utils::copyToClipboard(text);
}
ViewOnlyDialog::~ViewOnlyDialog()
{
delete ui;
}

@ -1,32 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_VIEWONLYDIALOG_H
#define FEATHER_VIEWONLYDIALOG_H
#include <QDialog>
#include "appcontext.h"
namespace Ui {
class ViewOnlyDialog;
}
class ViewOnlyDialog : public QDialog
{
Q_OBJECT
public:
explicit ViewOnlyDialog(AppContext *ctx, QWidget *parent = nullptr);
~ViewOnlyDialog() override;
private slots:
void onWriteViewOnlyWallet();
private:
Ui::ViewOnlyDialog *ui;
AppContext *m_ctx = nullptr;
void copyToClipboad();
};
#endif //FEATHER_KEYSDIALOG_H

@ -1,139 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ViewOnlyDialog</class>
<widget class="QDialog" name="ViewOnlyDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>659</width>
<height>254</height>
</rect>
</property>
<property name="windowTitle">
<string>View-Only</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Restore height</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<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>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Primary address</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_primaryAddress">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Secret view key</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_secretViewKey">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btn_Copy">
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_Save">
<property name="text">
<string>Create view-only wallet</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ViewOnlyDialog</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>ViewOnlyDialog</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>

@ -1,14 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_GLOBALS_H
#define FEATHER_GLOBALS_H
#include <QtGlobal>
namespace globals
{
const qreal cdiv = 1e12;
}
#endif //FEATHER_GLOBALS_H

@ -45,11 +45,11 @@ HistoryWidget::HistoryWidget(QWidget *parent)
}
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
void HistoryWidget::setModel(Coins *coins, TransactionHistoryProxyModel *model, TransactionHistory *txHistory)
{
m_coins = coins;
m_model = model;
m_wallet = wallet;
m_txHistory = m_wallet->history();
m_txHistory = txHistory;
ui->history->setModel(m_model);
ui->history->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
@ -66,7 +66,7 @@ void HistoryWidget::showTxDetails() {
});
if (i != nullptr) {
auto * dialog = new TransactionInfoDialog(m_wallet, i, this);
auto * dialog = new TransactionInfoDialog(m_coins, i, this);
dialog->exec();
}
}

@ -7,7 +7,6 @@
#include "model/TransactionHistoryModel.h"
#include "model/TransactionHistoryProxyModel.h"
#include "libwalletqt/Coins.h"
#include "libwalletqt/Wallet.h"
#include <QWidget>
#include <QMenu>
@ -22,7 +21,7 @@ Q_OBJECT
public:
explicit HistoryWidget(QWidget *parent = nullptr);
void setModel(TransactionHistoryProxyModel *model, Wallet *wallet);
void setModel(Coins * coins, TransactionHistoryProxyModel * model, TransactionHistory * txHistory);
~HistoryWidget() override;
public slots:
@ -52,7 +51,7 @@ private:
QMenu *m_copyMenu;
TransactionHistory *m_txHistory;
TransactionHistoryProxyModel *m_model;
Wallet *m_wallet = nullptr;
Coins *m_coins;
};
#endif //FEATHER_HISTORYWIDGET_H

@ -1,53 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2020, The Monero Project.
#include "ConstructionInfo.h"
#include "Input.h"
#include "Transfer.h"
quint64 ConstructionInfo::unlockTime() const {
return m_unlockTime;
}
QSet<quint32> ConstructionInfo::subaddressIndices() const {
return m_subaddressIndices;
}
QVector<QString> ConstructionInfo::subaddresses() const {
return m_subaddresses;
}
quint64 ConstructionInfo::minMixinCount() const {
return m_minMixinCount;
}
QList<Input *> ConstructionInfo::inputs() const {
return m_inputs;
}
QList<Transfer *> ConstructionInfo::outputs() const {
return m_outputs;
}
ConstructionInfo::ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl, QObject *parent)
: QObject(parent)
, m_unlockTime(pimpl->unlockTime())
, m_minMixinCount(pimpl->minMixinCount())
{
for (auto const &i : pimpl->inputs())
{
Input *input = new Input(i.amount, QString::fromStdString(i.pubkey), this);
m_inputs.append(input);
}
for (auto const &o : pimpl->outputs())
{
Transfer *output = new Transfer(o.amount, QString::fromStdString(o.address), this);
m_outputs.append(output);
}
for (uint32_t i : pimpl->subaddressIndices())
{
m_subaddressIndices.insert(i);
}
}

@ -1,46 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2020, The Monero Project.
#ifndef FEATHER_CONSTRUCTIONINFO_H
#define FEATHER_CONSTRUCTIONINFO_H
#include <wallet/api/wallet2_api.h>
#include <QObject>
#include <QSet>
class Input;
class Transfer;
class ConstructionInfo : public QObject
{
Q_OBJECT
Q_PROPERTY(quint64 unlockTime READ unlockTime)
Q_PROPERTY(QSet<quint32> subaddressIndices READ subaddressIndices)
Q_PROPERTY(QVector<QString> subaddresses READ subaddresses)
Q_PROPERTY(quint64 minMixinCount READ minMixinCount)
Q_PROPERTY(QList<Input*> inputs READ inputs)
Q_PROPERTY(QList<Transfer*> outputs READ outputs)
public:
quint64 unlockTime() const;
QSet<quint32> subaddressIndices() const;
QVector<QString> subaddresses() const;
quint64 minMixinCount() const;
QList<Input*> inputs() const;
QList<Transfer*> outputs() const;
private:
explicit ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl, QObject *parent = nullptr);
friend class PendingTransactionInfo;
friend class UnsignedTransaction;
quint64 m_unlockTime;
QSet<quint32> m_subaddressIndices;
QVector<QString> m_subaddresses;
quint64 m_minMixinCount;
mutable QList<Input*> m_inputs;
mutable QList<Transfer*> m_outputs;
};
#endif //FEATHER_CONSTRUCTIONINFO_H

@ -1,29 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2020, The Monero Project.
#ifndef FEATHER_INPUT_H
#define FEATHER_INPUT_H
#include <wallet/api/wallet2_api.h>
#include <QObject>
#include <utility>
class Input : public QObject
{
Q_OBJECT
Q_PROPERTY(quint64 amount READ amount)
Q_PROPERTY(QString pubKey READ pubKey)
private:
explicit Input(uint64_t _amount, QString _address, QObject *parent = nullptr): QObject(parent), m_amount(_amount), m_pubkey(std::move(_address)) {};
friend class ConstructionInfo;
quint64 m_amount;
QString m_pubkey;
public:
quint64 amount() const { return m_amount; }
QString pubKey() const { return m_pubkey; }
};
#endif //FEATHER_INPUT_H

@ -16,12 +16,10 @@ QString PendingTransaction::errorString() const
bool PendingTransaction::commit()
{
return m_pimpl->commit();
}
bool PendingTransaction::saveToFile(const QString &fileName)
{
return m_pimpl->commit(fileName.toStdString());
// Save transaction to file if fileName is set.
if(!m_fileName.isEmpty())
return m_pimpl->commit(m_fileName.toStdString());
return m_pimpl->commit(m_fileName.toStdString());
}
quint64 PendingTransaction::amount() const
@ -39,6 +37,7 @@ quint64 PendingTransaction::fee() const
return m_pimpl->fee();
}
QStringList PendingTransaction::txid() const
{
QStringList list;
@ -64,33 +63,9 @@ QList<QVariant> PendingTransaction::subaddrIndices() const
return result;
}
QByteArray PendingTransaction::unsignedTxToBin() const {
return QByteArray::fromStdString(m_pimpl->unsignedTxToBin());
}
QString PendingTransaction::unsignedTxToBase64() const
void PendingTransaction::setFilename(const QString &fileName)
{
return QString::fromStdString(m_pimpl->unsignedTxToBase64());
}
QString PendingTransaction::signedTxToHex(int index) const
{
return QString::fromStdString(m_pimpl->signedTxToHex(index));
}
PendingTransactionInfo * PendingTransaction::transaction(int index) const {
return m_pending_tx_info[index];
}
void PendingTransaction::refresh()
{
qDeleteAll(m_pending_tx_info);
m_pending_tx_info.clear();
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) {
m_pending_tx_info.append(new PendingTransactionInfo(i, this));
}
m_fileName = fileName;
}
PendingTransaction::PendingTransaction(Monero::PendingTransaction *pt, QObject *parent)

@ -9,8 +9,6 @@
#include <QVariant>
#include <wallet/api/wallet2_api.h>
#include "PendingTransactionInfo.h"
//namespace Monero {
//class PendingTransaction;
@ -32,7 +30,7 @@ public:
enum Status {
Status_Ok = Monero::PendingTransaction::Status_Ok,
Status_Error = Monero::PendingTransaction::Status_Error,
Status_Critical = Monero::PendingTransaction::Status_Critical
Status_Critical = Monero::PendingTransaction::Status_Critical
};
Q_ENUM(Status)
@ -47,27 +45,21 @@ public:
Status status() const;
QString errorString() const;
Q_INVOKABLE bool commit();
bool saveToFile(const QString &fileName);
quint64 amount() const;
quint64 dust() const;
quint64 fee() const;
QStringList txid() const;
quint64 txCount() const;
QList<QVariant> subaddrIndices() const;
QByteArray unsignedTxToBin() const;
QString unsignedTxToBase64() const;
QString signedTxToHex(int index) const;
void refresh();
PendingTransactionInfo * transaction(int index) const;
Q_INVOKABLE void setFilename(const QString &fileName);
private:
explicit PendingTransaction(Monero::PendingTransaction * pt, QObject *parent = nullptr);
explicit PendingTransaction(Monero::PendingTransaction * pt, QObject *parent = 0);
private:
friend class Wallet;
Monero::PendingTransaction * m_pimpl;
mutable QList<PendingTransactionInfo*> m_pending_tx_info;
QString m_fileName;
};
#endif // PENDINGTRANSACTION_H

@ -1,32 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2020, The Monero Project.
#include "PendingTransactionInfo.h"
#include "Input.h"
#include "Transfer.h"
quint64 PendingTransactionInfo::fee() const {
return m_fee;
}
quint64 PendingTransactionInfo::dust() const {
return m_dust;
}
bool PendingTransactionInfo::dustAddedToFee() const {
return m_dustAddedToFee;
}
QString PendingTransactionInfo::txKey() const {
return m_txKey;
}
PendingTransactionInfo::PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl, QObject *parent)
: ConstructionInfo(pimpl->constructionData(), parent)
, m_fee(pimpl->fee())
, m_dust(pimpl->dust())
, m_dustAddedToFee(pimpl->dustAddedToFee())
, m_txKey(QString::fromStdString(pimpl->txKey()))
{
}

@ -1,46 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2020, The Monero Project.
#ifndef FEATHER_PENDINGTRANSACTIONINFO_H
#define FEATHER_PENDINGTRANSACTIONINFO_H
#include <wallet/api/wallet2_api.h>
#include "ConstructionInfo.h"
#include <QObject>
#include <QSet>
class Input;
class Transfer;
class PendingTransactionInfo : public ConstructionInfo
{
Q_OBJECT
Q_PROPERTY(quint64 fee READ fee)
Q_PROPERTY(quint64 dust READ dust)
Q_PROPERTY(bool dustAddedToFee READ dustAddedToFee)
Q_PROPERTY(QString txKey READ txKey)
Q_PROPERTY(quint64 unlockTime READ unlockTime)
Q_PROPERTY(QSet<quint32> subaddressIndices READ subaddressIndices)
Q_PROPERTY(QVector<QString> subaddresses READ subaddresses)
Q_PROPERTY(quint64 minMixinCount READ minMixinCount)
Q_PROPERTY(QList<Input*> inputs READ inputs)
Q_PROPERTY(QList<Transfer*> outputs READ outputs)
public:
quint64 fee() const;
quint64 dust() const;
bool dustAddedToFee() const;
QString txKey() const;
private:
explicit PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl, QObject *parent = nullptr);
friend class PendingTransaction;
quint64 m_fee;
quint64 m_dust;
bool m_dustAddedToFee;
QString m_txKey;
};
#endif //FEATHER_PENDINGTRANSACTIONINFO_H

@ -17,7 +17,6 @@ private:
explicit Transfer(uint64_t _amount, QString _address, QObject *parent = 0): QObject(parent), m_amount(_amount), m_address(std::move(_address)) {};
private:
friend class TransactionInfo;
friend class ConstructionInfo;
quint64 m_amount;
QString m_address;
public:

@ -83,21 +83,6 @@ void UnsignedTransaction::setFilename(const QString &fileName)
m_fileName = fileName;
}
ConstructionInfo * UnsignedTransaction::constructionInfo(int index) const {
return m_construction_info[index];
}
void UnsignedTransaction::refresh()
{
qDeleteAll(m_construction_info);
m_construction_info.clear();
m_pimpl->refresh();
for (const auto i : m_pimpl->getAll()) {
m_construction_info.append(new ConstructionInfo(i, this));
}
}
UnsignedTransaction::UnsignedTransaction(Monero::UnsignedTransaction *pt, Monero::Wallet *walletImpl, QObject *parent)
: QObject(parent), m_pimpl(pt), m_walletImpl(walletImpl)
{

@ -7,13 +7,14 @@
#include <QObject>
#include <wallet/api/wallet2_api.h>
#include "libwalletqt/PendingTransactionInfo.h"
class UnsignedTransaction : public QObject
{
Q_OBJECT
Q_PROPERTY(Status status READ status)
Q_PROPERTY(QString errorString READ errorString)
// Q_PROPERTY(QList<qulonglong> amount READ amount)
// Q_PROPERTY(QList<qulonglong> fee READ fee)
Q_PROPERTY(quint64 txCount READ txCount)
Q_PROPERTY(QString confirmationMessage READ confirmationMessage)
Q_PROPERTY(QStringList recipientAddress READ recipientAddress)
@ -40,19 +41,15 @@ public:
quint64 minMixinCount() const;
Q_INVOKABLE bool sign(const QString &fileName) const;
Q_INVOKABLE void setFilename(const QString &fileName);
void refresh();
ConstructionInfo * constructionInfo(int index) const;
private:
explicit UnsignedTransaction(Monero::UnsignedTransaction * pt, Monero::Wallet *walletImpl, QObject *parent = nullptr);
explicit UnsignedTransaction(Monero::UnsignedTransaction * pt, Monero::Wallet *walletImpl, QObject *parent = 0);
~UnsignedTransaction();
private:
friend class Wallet;
Monero::UnsignedTransaction * m_pimpl;
QString m_fileName;
Monero::Wallet * m_walletImpl;
mutable QList<ConstructionInfo*> m_construction_info;
};
#endif // UNSIGNEDTRANSACTION_H

@ -2,11 +2,6 @@
// Copyright (c) 2014-2020, The Monero Project.
#include "Wallet.h"
#include <chrono>
#include <stdexcept>
#include <thread>
#include "PendingTransaction.h"
#include "UnsignedTransaction.h"
#include "TransactionHistory.h"
@ -32,8 +27,6 @@
#include <QVector>
#include <QMutexLocker>
#include "utils/ScopeGuard.h"
namespace {
static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 5;
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
@ -43,7 +36,7 @@ namespace {
}
Wallet::Wallet(QObject * parent)
: Wallet(nullptr, parent)
: Wallet(nullptr, parent)
{
}
@ -107,20 +100,6 @@ bool Wallet::disconnected() const
return m_disconnected;
}
bool Wallet::refreshing() const
{
return m_refreshing;
}
void Wallet::refreshingSet(bool value)
{
if (m_refreshing.exchange(value) != value)
{
emit refreshingChanged();
}
}
void Wallet::setConnectionStatus(ConnectionStatus value)
{
if (m_connectionStatus == value)
@ -132,7 +111,7 @@ void Wallet::setConnectionStatus(ConnectionStatus value)
emit connectionStatusChanged(m_connectionStatus);
bool disconnected = m_connectionStatus == Wallet::ConnectionStatus_Connecting ||
m_connectionStatus == Wallet::ConnectionStatus_Disconnected;
m_connectionStatus == Wallet::ConnectionStatus_Disconnected;
if (m_disconnected != disconnected)
{
@ -141,29 +120,6 @@ void Wallet::setConnectionStatus(ConnectionStatus value)
}
}
QString Wallet::getProxyAddress() const
{
QMutexLocker locker(&m_proxyMutex);
return m_proxyAddress;
}
void Wallet::setProxyAddress(QString address)
{
m_scheduler.run([this, address] {
{
QMutexLocker locker(&m_proxyMutex);
if (!m_walletImpl->setProxy(address.toStdString()))
{
qCritical() << "failed to set proxy" << address;
}
m_proxyAddress = address;
}
emit proxyAddressChanged();
});
}
bool Wallet::synchronized() const
{
return m_walletImpl->synchronized();
@ -189,18 +145,20 @@ QString Wallet::path() const
return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->path()));
}
//void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */)
//void Wallet::storeAsync(const QVariant &callback, const QString &path /* = "" */)
//{
// const auto future = m_scheduler.run(
// [this, path] {
// QMutexLocker locker(&m_asyncMutex);
//
// return QJSValueList({m_walletImpl->store(path.toStdString())});
// },
// callback);
// [this, path] {
// QMutexLocker locker(&m_storeMutex);
// return QVariantList({m_walletImpl->store(path.toStdString())});
// },
// callback);
// if (!future.first)
// {
// QJSValue(callback).call(QJSValueList({false}));
// QVariant(callback).call(QVariantList({false}));
// }
//}
@ -209,7 +167,7 @@ void Wallet::store(const QString &path)
m_walletImpl->store(path.toStdString());
}
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight, const QString& proxyAddress)
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
{
qDebug() << "init non async";
if (isRecovering){
@ -223,20 +181,7 @@ bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 uppe
if (isRecovering || isRecoveringFromDevice) {
m_walletImpl->setRefreshFromBlockHeight(restoreHeight);
}
{
QMutexLocker locker(&m_proxyMutex);
if (!m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), false, false, proxyAddress.toStdString()))
{
return false;
}
m_proxyAddress = proxyAddress;
}
emit proxyAddressChanged();
m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString());
setTrustedDaemon(trustedDaemon);
return true;
}
@ -248,24 +193,17 @@ void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemon
m_daemonPassword = daemonPassword;
}
void Wallet::initAsync(
const QString &daemonAddress,
bool trustedDaemon /* = false */,
quint64 upperTransactionLimit /* = 0 */,
bool isRecovering /* = false */,
bool isRecoveringFromDevice /* = false */,
quint64 restoreHeight /* = 0 */,
const QString &proxyAddress /* = "" */)
void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
{
qDebug() << "initAsync: " + daemonAddress;
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress] {
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress);
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight] {
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight);
if (success)
{
emit walletCreationHeightChanged();
qDebug() << "init async finished - starting refresh";
connected(true);
startRefresh();
m_walletImpl->startRefresh();
}
});
if (future.first)
@ -434,7 +372,7 @@ quint64 Wallet::daemonBlockChainHeight() const
// cache daemon blockchain height for some time (60 seconds by default)
if (m_daemonBlockChainHeight == 0
|| m_daemonBlockChainHeightTime.elapsed() / 1000 > m_daemonBlockChainHeightTtl) {
|| m_daemonBlockChainHeightTime.elapsed() / 1000 > m_daemonBlockChainHeightTtl) {
m_daemonBlockChainHeight = m_walletImpl->daemonBlockChainHeight();
m_daemonBlockChainHeightTime.restart();
}
@ -444,7 +382,7 @@ quint64 Wallet::daemonBlockChainHeight() const
quint64 Wallet::daemonBlockChainTargetHeight() const
{
if (m_daemonBlockChainTargetHeight <= 1
|| m_daemonBlockChainTargetHeightTime.elapsed() / 1000 > m_daemonBlockChainTargetHeightTtl) {
|| m_daemonBlockChainTargetHeightTime.elapsed() / 1000 > m_daemonBlockChainTargetHeightTtl) {
m_daemonBlockChainTargetHeight = m_walletImpl->daemonBlockChainTargetHeight();
// Target height is set to 0 if daemon is synced.
@ -458,9 +396,9 @@ quint64 Wallet::daemonBlockChainTargetHeight() const
return m_daemonBlockChainTargetHeight;
}
bool Wallet::exportKeyImages(const QString& path, bool all)
bool Wallet::exportKeyImages(const QString& path)
{
return m_walletImpl->exportKeyImages(path.toStdString(), all);
return m_walletImpl->exportKeyImages(path.toStdString());
}
bool Wallet::importKeyImages(const QString& path)
@ -468,22 +406,42 @@ bool Wallet::importKeyImages(const QString& path)
return m_walletImpl->importKeyImages(path.toStdString());
}
bool Wallet::exportOutputs(const QString& path, bool all) {
return m_walletImpl->exportOutputs(path.toStdString(), all);
bool Wallet::refresh()
{
bool result = m_walletImpl->refresh();
m_history->refresh(currentSubaddressAccount());
m_subaddress->refresh(currentSubaddressAccount());
m_coins->refresh(currentSubaddressAccount());
m_subaddressAccount->getAll();
if (result)
emit updated();
return result;
}
bool Wallet::importOutputs(const QString& path) {
return m_walletImpl->importOutputs(path.toStdString());
void Wallet::refreshAsync()
{
qDebug() << "refresh async";
m_walletImpl->refreshAsync();
}
void Wallet::startRefresh()
void Wallet::setAutoRefreshInterval(int seconds)
{
m_refreshEnabled = true;
m_walletImpl->setAutoRefreshInterval(seconds);
}
void Wallet::pauseRefresh()
int Wallet::autoRefreshInterval() const
{
m_refreshEnabled = false;
return m_walletImpl->autoRefreshInterval();
}
void Wallet::startRefresh() const
{
m_walletImpl->startRefresh();
}
void Wallet::pauseRefresh() const
{
m_walletImpl->pauseRefresh();
}
PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id,
@ -492,15 +450,15 @@ PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QSt
{
std::set<uint32_t> subaddr_indices;
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count,
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count,
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
PendingTransaction * result = new PendingTransaction(ptImpl,0);
return result;
}
void Wallet::createTransactionAsync(const QString &dst_addr, const QString &payment_id,
quint64 amount, quint32 mixin_count,
PendingTransaction::Priority priority)
quint64 amount, quint32 mixin_count,
PendingTransaction::Priority priority)
{
m_scheduler.run([this, dst_addr, payment_id, amount, mixin_count, priority] {
PendingTransaction *tx = createTransaction(dst_addr, payment_id, amount, mixin_count, priority);
@ -513,15 +471,15 @@ PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const
{
std::set<uint32_t> subaddr_indices;
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
dst_addr.toStdString(), payment_id.toStdString(), Monero::optional<uint64_t>(), mixin_count,
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
dst_addr.toStdString(), payment_id.toStdString(), Monero::optional<uint64_t>(), mixin_count,
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
PendingTransaction * result = new PendingTransaction(ptImpl, this);
return result;
}
void Wallet::createTransactionAllAsync(const QString &dst_addr, const QString &payment_id,
quint32 mixin_count,
PendingTransaction::Priority priority)
quint32 mixin_count,
PendingTransaction::Priority priority)
{
m_scheduler.run([this, dst_addr, payment_id, mixin_count, priority] {
PendingTransaction *tx = createTransactionAll(dst_addr, payment_id, mixin_count, priority);
@ -543,7 +501,7 @@ void Wallet::createTransactionSingleAsync(const QString &key_image, const QStrin
{
m_scheduler.run([this, key_image, dst_addr, outputs, priority] {
PendingTransaction *tx = createTransactionSingle(key_image, dst_addr, outputs, priority);
emit transactionCreated(tx, dst_addr, "", 10); // todo: return true mixincount
emit transactionCreated(tx, dst_addr, "", 0); // todo: return true mixincount
});
}
@ -570,21 +528,6 @@ UnsignedTransaction * Wallet::loadTxFile(const QString &fileName)
return result;
}
UnsignedTransaction * Wallet::loadTxFromBase64Str(const QString &unsigned_tx)
{
Monero::UnsignedTransaction * ptImpl = m_walletImpl->loadUnsignedTxFromBase64Str(unsigned_tx.toStdString());
UnsignedTransaction * result = new UnsignedTransaction(ptImpl, m_walletImpl, this);
return result;
}
PendingTransaction * Wallet::loadSignedTxFile(const QString &fileName)
{
qDebug() << "Tying to load " << fileName;
Monero::PendingTransaction * ptImpl = m_walletImpl->loadSignedTx(fileName.toStdString());
PendingTransaction * result = new PendingTransaction(ptImpl, this);
return result;
}
bool Wallet::submitTxFile(const QString &fileName) const
{
qDebug() << "Trying to submit " << fileName;
@ -594,29 +537,6 @@ bool Wallet::submitTxFile(const QString &fileName) const
return m_walletImpl->importKeyImages(fileName.toStdString() + "_keyImages");
}
bool Wallet::refresh(bool historyAndSubaddresses /* = true */)
{
refreshingSet(true);
const auto cleanup = sg::make_scope_guard([this]() noexcept {
refreshingSet(false);
});
{
QMutexLocker locker(&m_asyncMutex);
bool result = m_walletImpl->refresh();
if (historyAndSubaddresses)
{
m_history->refresh(currentSubaddressAccount());
m_subaddress->refresh(currentSubaddressAccount());
m_subaddressAccount->getAll();
}
if (result)
emit updated();
return result;
}
}
void Wallet::commitTransactionAsync(PendingTransaction *t)
{
m_scheduler.run([this, t] {
@ -636,6 +556,7 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
delete t;
}
// @ TODO: QJSValue could probably be QVARIANT
//void Wallet::estimateTransactionFeeAsync(const QString &destination,
// quint64 amount,
// PendingTransaction::Priority priority,
@ -643,8 +564,8 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
//{
// m_scheduler.run([this, destination, amount, priority] {
// const uint64_t fee = m_walletImpl->estimateTransactionFee(
// {std::make_pair(destination.toStdString(), amount)},
// static_cast<Monero::PendingTransaction::Priority>(priority));
// {std::make_pair(destination.toStdString(), amount)},
// static_cast<Monero::PendingTransaction::Priority>(priority));
// return QJSValueList({QString::fromStdString(Monero::Wallet::displayAmount(fee))});
// }, callback);
//}
@ -762,17 +683,17 @@ bool Wallet::setCacheAttribute(const QString &key, const QString &val)
bool Wallet::setUserNote(const QString &txid, const QString &note)
{
return m_walletImpl->setUserNote(txid.toStdString(), note.toStdString());
return m_walletImpl->setUserNote(txid.toStdString(), note.toStdString());
}
QString Wallet::getUserNote(const QString &txid) const
{
return QString::fromStdString(m_walletImpl->getUserNote(txid.toStdString()));
return QString::fromStdString(m_walletImpl->getUserNote(txid.toStdString()));
}
QString Wallet::getTxKey(const QString &txid) const
{
return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString()));
return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString()));
}
//void Wallet::getTxKeyAsync(const QString &txid, const QJSValue &callback)
@ -910,6 +831,7 @@ bool Wallet::verifySignedMessage(const QString &message, const QString &address,
return m_walletImpl->verifySignedMessage(message.toStdString(), address.toStdString(), signature.toStdString());
}
}
bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error)
{
std::string s_address, s_payment_id, s_tx_description, s_recipient_name, s_error;
@ -930,8 +852,6 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id
bool Wallet::rescanSpent()
{
QMutexLocker locker(&m_asyncMutex);
return m_walletImpl->rescanSpent();
}
@ -1008,8 +928,8 @@ QString Wallet::getRing(const QString &key_image)
{
if (!ring.isEmpty())
ring = ring + " ";
QString s;
s.setNum(out);
QString s;
s.setNum(out);
ring = ring + s;
}
return ring;
@ -1029,8 +949,8 @@ QString Wallet::getRings(const QString &txid)
for (uint64_t out: cring.second)
{
ring = ring + " ";
QString s;
s.setNum(out);
QString s;
s.setNum(out);
ring = ring + s;
}
}
@ -1044,9 +964,9 @@ bool Wallet::setRing(const QString &key_image, const QString &ring, bool relativ
foreach(QString str, strOuts)
{
uint64_t out;
bool ok;
out = str.toULong(&ok);
if (ok)
bool ok;
out = str.toULong(&ok);
if (ok)
cring.push_back(out);
}
return m_walletImpl->setRing(key_image.toStdString(), cring, relative);
@ -1081,28 +1001,26 @@ void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device
}
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
: QObject(parent)
, m_walletImpl(w)
, m_history(nullptr)
, m_historyModel(nullptr)
, m_addressBook(nullptr)
, m_addressBookModel(nullptr)
, m_daemonBlockChainHeight(0)
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
, m_daemonBlockChainTargetHeight(0)
, m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS)
, m_connectionStatus(Wallet::ConnectionStatus_Disconnected)
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
, m_disconnected(true)
, m_currentSubaddressAccount(0)
, m_subaddress(nullptr)
, m_subaddressModel(nullptr)
, m_subaddressAccount(nullptr)
, m_subaddressAccountModel(nullptr)
, m_coinsModel(nullptr)
, m_refreshEnabled(false)
, m_refreshing(false)
, m_scheduler(this)
: QObject(parent)
, m_walletImpl(w)
, m_history(nullptr)
, m_historyModel(nullptr)
, m_addressBook(nullptr)
, m_addressBookModel(nullptr)
, m_daemonBlockChainHeight(0)
, m_daemonBlockChainHeightTtl(DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS)
, m_daemonBlockChainTargetHeight(0)
, m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS)
, m_connectionStatus(Wallet::ConnectionStatus_Disconnected)
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
, m_disconnected(true)
, m_currentSubaddressAccount(0)
, m_subaddress(nullptr)
, m_subaddressModel(nullptr)
, m_subaddressAccount(nullptr)
, m_subaddressAccountModel(nullptr)
, m_coinsModel(nullptr)
, m_scheduler(this)
{
m_history = new TransactionHistory(m_walletImpl->history(), this);
m_addressBook = new AddressBook(m_walletImpl->addressBook(), this);
@ -1120,16 +1038,11 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
m_connectionStatusRunning = false;
m_daemonUsername = "";
m_daemonPassword = "";
startRefreshThread();
}
Wallet::~Wallet()
{
qDebug("~Wallet: Closing wallet");
pauseRefresh();
m_walletImpl->stop();
qDebug() << "~Wallet: Closing wallet";
m_scheduler.shutdownWaitForFinished();
@ -1149,43 +1062,13 @@ Wallet::~Wallet()
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
if(status() == Status_Critical)
qDebug("Not storing wallet cache");
// Don't store on wallet close for now
// else if( m_walletImpl->store(""))
// qDebug("Wallet cache stored successfully");
// else
// qDebug("Error storing wallet cache");
else if( m_walletImpl->store(""))
qDebug("Wallet cache stored successfully");
else
qDebug("Error storing wallet cache");
delete m_walletImpl;
m_walletImpl = NULL;
delete m_walletListener;
m_walletListener = NULL;
qDebug("m_walletImpl deleted");
}
void Wallet::startRefreshThread()
{
const auto future = m_scheduler.run([this] {
constexpr const std::chrono::seconds refreshInterval{10};
constexpr const std::chrono::milliseconds intervalResolution{100};
auto last = std::chrono::steady_clock::now();
while (!m_scheduler.stopping())
{
if (m_refreshEnabled)
{
const auto now = std::chrono::steady_clock::now();
const auto elapsed = now - last;
if (elapsed >= refreshInterval)
{
refresh(false);
last = std::chrono::steady_clock::now();
}
}
std::this_thread::sleep_for(intervalResolution);
}
});
if (!future.first)
{
throw std::runtime_error("failed to start auto refresh thread");
}
}

@ -8,6 +8,7 @@
#include <QObject>
#include <QMutex>
#include <QList>
//#include <QJSValue>
#include <QtConcurrent/QtConcurrent>
#include "wallet/api/wallet2_api.h" // we need to have an access to the Monero::Wallet::Status enum here;
@ -19,13 +20,14 @@
#include "WalletListenerImpl.h"
namespace Monero {
struct Wallet; // forward declaration
struct Wallet; // forward declaration
}
class TransactionHistory;
class TransactionHistoryModel;
class TransactionHistoryProxyModel;
class TransactionHistorySortFilterModel;
class AddressBook;
class AddressBookModel;
class Subaddress;
@ -48,14 +50,13 @@ struct TxProofResult {
class Wallet : public QObject, public PassprasePrompter
{
Q_OBJECT
Q_OBJECT
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged)
Q_PROPERTY(QString seed READ getSeed)
Q_PROPERTY(QString seedLanguage READ getSeedLanguage)
Q_PROPERTY(Status status READ status)
Q_PROPERTY(NetworkType::Type nettype READ nettype)
// Q_PROPERTY(ConnectionStatus connected READ connected)
Q_PROPERTY(ConnectionStatus connected READ connected)
Q_PROPERTY(quint32 currentSubaddressAccount READ currentSubaddressAccount NOTIFY currentSubaddressAccountChanged)
Q_PROPERTY(bool synchronized READ synchronized)
Q_PROPERTY(QString errorString READ errorString)
@ -75,7 +76,6 @@ Q_OBJECT
Q_PROPERTY(QString secretSpendKey READ getSecretSpendKey)
Q_PROPERTY(QString publicSpendKey READ getPublicSpendKey)
Q_PROPERTY(QString daemonLogPath READ getDaemonLogPath CONSTANT)
Q_PROPERTY(QString proxyAddress READ getProxyAddress WRITE setProxyAddress NOTIFY proxyAddressChanged)
Q_PROPERTY(quint64 walletCreationHeight READ getWalletCreationHeight WRITE setWalletCreationHeight NOTIFY walletCreationHeightChanged)
public:
@ -120,7 +120,6 @@ public:
//! returns true if wallet was ever synchronized
bool synchronized() const;
//! returns last operation's error message
QString errorString() const;
@ -139,14 +138,7 @@ public:
// Q_INVOKABLE void storeAsync(const QJSValue &callback, const QString &path = "");
//! initializes wallet asynchronously
Q_INVOKABLE void initAsync(
const QString &daemonAddress,
bool trustedDaemon = false,
quint64 upperTransactionLimit = 0,
bool isRecovering = false,
bool isRecoveringFromDevice = false,
quint64 restoreHeight = 0,
const QString &proxyAddress = "");
Q_INVOKABLE void initAsync(const QString &daemonAddress, bool trustedDaemon = false, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0);
// Set daemon rpc user/pass
Q_INVOKABLE void setDaemonLogin(const QString &daemonUsername = "", const QString &daemonPassword = "");
@ -192,19 +184,24 @@ public:
Q_INVOKABLE void refreshHeightAsync();
//! export/import key images
Q_INVOKABLE bool exportKeyImages(const QString& path, bool all = false);
Q_INVOKABLE bool exportKeyImages(const QString& path);
Q_INVOKABLE bool importKeyImages(const QString& path);
//! export/import outputs
Q_INVOKABLE bool exportOutputs(const QString& path, bool all = false);
Q_INVOKABLE bool importOutputs(const QString& path);
//! refreshes the wallet
Q_INVOKABLE bool refresh(bool historyAndSubaddresses = false);
Q_INVOKABLE bool refresh();
//! refreshes the wallet asynchronously
Q_INVOKABLE void refreshAsync();
//! setup auto-refresh interval in seconds
Q_INVOKABLE void setAutoRefreshInterval(int seconds);
//! return auto-refresh interval in seconds
Q_INVOKABLE int autoRefreshInterval() const;
// pause/resume refresh
Q_INVOKABLE void startRefresh();
Q_INVOKABLE void pauseRefresh();
Q_INVOKABLE void startRefresh() const;
Q_INVOKABLE void pauseRefresh() const;
//! returns current wallet's block height
//! (can be less than daemon's blockchain height when wallet sync in progress)
@ -228,7 +225,7 @@ public:
//! creates transaction with all outputs
Q_INVOKABLE PendingTransaction * createTransactionAll(const QString &dst_addr, const QString &payment_id,
quint32 mixin_count, PendingTransaction::Priority priority);
quint32 mixin_count, PendingTransaction::Priority priority);
//! creates async transaction with all outputs
Q_INVOKABLE void createTransactionAllAsync(const QString &dst_addr, const QString &payment_id,
@ -251,12 +248,6 @@ public:
//! Sign a transfer from file
Q_INVOKABLE UnsignedTransaction * loadTxFile(const QString &fileName);
//! Load an unsigned transaction from a base64 encoded string
Q_INVOKABLE UnsignedTransaction * loadTxFromBase64Str(const QString &unsigned_tx);
//! Load a signed transaction from file
Q_INVOKABLE PendingTransaction * loadSignedTxFile(const QString &fileName);
//! Submit a transfer from file
Q_INVOKABLE bool submitTxFile(const QString &fileName) const;
@ -411,8 +402,6 @@ signals:
void connectionStatusChanged(int status) const;
void currentSubaddressAccountChanged() const;
void disconnectedChanged() const;
void proxyAddressChanged() const;
void refreshingChanged() const;
private:
Wallet(QObject * parent = nullptr);
@ -420,23 +409,10 @@ private:
~Wallet();
//! initializes wallet
bool init(
const QString &daemonAddress,
bool trustedDaemon,
quint64 upperTransactionLimit,
bool isRecovering,
bool isRecoveringFromDevice,
quint64 restoreHeight,
const QString& proxyAddress);
bool init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight);
bool disconnected() const;
bool refreshing() const;
void refreshingSet(bool value);
void setConnectionStatus(ConnectionStatus value);
QString getProxyAddress() const;
void setProxyAddress(QString address);
void startRefreshThread();
private:
friend class WalletManager;
@ -469,17 +445,13 @@ private:
mutable SubaddressAccountModel * m_subaddressAccountModel;
Coins * m_coins;
mutable CoinsModel * m_coinsModel;
QMutex m_asyncMutex;
QMutex m_connectionStatusMutex;
bool m_connectionStatusRunning;
QString m_daemonUsername;
QString m_daemonPassword;
QString m_proxyAddress;
mutable QMutex m_proxyMutex;
std::atomic<bool> m_refreshEnabled;
std::atomic<bool> m_refreshing;
WalletListenerImpl *m_walletListener;
FutureScheduler m_scheduler;
QMutex m_storeMutex;
};

@ -22,14 +22,12 @@ int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(assets);
#if defined(Q_OS_MAC) && defined(HAS_TOR)
Q_INIT_RESOURCE(assets_tor_macos);
#elif defined(HAS_TOR)
Q_INIT_RESOURCE(assets_tor);
#endif
#if defined(HAS_XMRIG)
Q_INIT_RESOURCE(assets_mining);
#if defined(Q_OS_MAC) && defined(BUILD_TOR)
Q_INIT_RESOURCE(assets_macos_tor);
#elif defined(Q_OS_LINUX) && defined(BUILD_TOR)
Q_INIT_RESOURCE(assets_linux_tor);
#elif defined(Q_OS_WIN) && defined(BUILD_TOR)
Q_INIT_RESOURCE(assets_windows_tor);
#endif
QStringList argv_;

@ -13,15 +13,11 @@
#include "widgets/ccswidget.h"
#include "widgets/redditwidget.h"
#include "dialog/txconfdialog.h"
#include "dialog/txconfadvdialog.h"
#include "dialog/debuginfodialog.h"
#include "dialog/walletinfodialog.h"
#include "dialog/torinfodialog.h"
#include "dialog/viewonlydialog.h"
#include "dialog/broadcasttxdialog.h"
#include "utils/utils.h"
#include "utils/config.h"
#include "utils/daemonrpc.h"
#include "components.h"
#include "calcwindow.h"
#include "ui_mainwindow.h"
@ -118,9 +114,10 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_ctx->XMRTo, &XmrTo::openURL, this, [=](const QString &url){ Utils::externalLinkWarning(url); });
ui->xmrToWidget->setHistoryModel(m_ctx->XMRTo->tableModel);
#else
ui->tabWidget->setTabVisible(Tabs::XMR_TO, false);
ui->tabWidget->setTabVisible(5, false);
#endif
#if defined(Q_OS_LINUX)
// system tray
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/images/appicons/64x64.png"));
@ -168,24 +165,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_ctx->nodes, &Nodes::nodeExhausted, this, &MainWindow::showNodeExhaustedMessage);
connect(m_ctx->nodes, &Nodes::WSNodeExhausted, this, &MainWindow::showWSNodeExhaustedMessage);
// XMRig
#ifdef HAS_XMRIG
m_xmrig = new XMRigWidget(m_ctx, this);
ui->xmrRigLayout->addWidget(m_xmrig);
connect(m_ctx->XMRig, &XmRig::output, m_xmrig, &XMRigWidget::onProcessOutput);
connect(m_ctx->XMRig, &XmRig::error, m_xmrig, &XMRigWidget::onProcessError);
connect(m_ctx->XMRig, &XmRig::hashrate, m_xmrig, &XMRigWidget::onHashrate);
connect(m_ctx, &AppContext::walletClosed, m_xmrig, &XMRigWidget::onWalletClosed);
connect(m_ctx, &AppContext::walletOpened, m_xmrig, &XMRigWidget::onWalletOpened);
connect(m_ctx, &AppContext::XMRigDownloads, m_xmrig, &XMRigWidget::onDownloads);
connect(m_xmrig, &XMRigWidget::miningStarted, [=]{ m_ctx->setWindowTitle(true); });
connect(m_xmrig, &XMRigWidget::miningEnded, [=]{ m_ctx->setWindowTitle(false); });
#else
ui->tabWidget->setTabVisible(Tabs::XMRIG, false);
#endif
// CCS/Reddit widget
m_ccsWidget = new CCSWidget(this);
m_redditWidget = new RedditWidget(this);
@ -276,7 +255,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, tickerWidget, &TickerWidget::init);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_balanceWidget, &TickerWidget::init);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, m_ctx, &AppContext::onPreferredFiatCurrencyChanged);
connect(m_windowSettings, &Settings::preferredFiatCurrencyChanged, ui->sendWidget, QOverload<>::of(&SendWidget::onPreferredFiatCurrencyChanged));
// CCS/Reddit widget
connect(m_windowSettings, &Settings::homeWidgetChanged, this, &MainWindow::homeWidgetChanged);
@ -297,7 +275,7 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
});
connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) {
ui->historyWidget->setSearchText(text);
ui->tabWidget->setCurrentIndex(Tabs::HISTORY);
ui->tabWidget->setCurrentIndex(1); //history
});
// History
@ -345,20 +323,8 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
connect(ui->coinsWidget, &CoinsWidget::sweepOutput, m_ctx, &AppContext::onSweepOutput);
connect(m_ctx, &AppContext::walletClosing, [=]{
ui->tabWidget->setCurrentIndex(Tabs::HOME);
ui->tabWidget->setCurrentIndex(0);
});
// window title
connect(m_ctx, &AppContext::setTitle, this, &QMainWindow::setWindowTitle);
// init touchbar
#ifdef Q_OS_MAC
m_touchbar = new KDMacTouchBar(this);
m_touchbarActionWelcome = new QAction(QIcon(":/assets/images/feather.png"), "Welcome to Feather!");
m_touchbarWalletItems = {ui->actionSettings, ui->actionCalculator, ui->actionKeys, ui->actionDonate_to_Feather};
m_touchbarWizardItems = {m_touchbarActionWelcome};
#endif
// setup some UI
this->initMain();
this->initWidgets();
@ -390,7 +356,6 @@ void MainWindow::initMain() {
this->show();
m_wizard = this->createWizard(WalletWizard::Page_Menu);
m_wizard->show();
this->touchbarShowWizard();
}
void MainWindow::initMenu() {
@ -400,26 +365,16 @@ void MainWindow::initMenu() {
connect(ui->actionShow_Coins, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["Coins"] = new ToggleTab(ui->tabCoins, "Coins", "Coins", ui->actionShow_Coins, Config::showTabCoins);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_Coins, "Coins");
connect(ui->actionShow_calc, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
#if defined(XMRTO)
connect(ui->actionShow_xmr_to, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["XMRto"] = new ToggleTab(ui->tabXmrTo, "XMRto", "XMR.to", ui->actionShow_xmr_to, Config::showTabXMRto);
m_tabShowHideMapper["XMRto"] = new ToggleTab(ui->tabXmrTo, "XMRto", "xmr.to", ui->actionShow_xmr_to, Config::showTabXMRto);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_xmr_to, "XMRto");
#else
ui->actionShow_xmr_to->setVisible(false);
#endif
#if defined(HAS_XMRIG)
connect(ui->actionShow_XMRig, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["XMRig"] = new ToggleTab(ui->tabXmrRig, "XMRig", "XMRig", ui->actionShow_XMRig, Config::showTabXMRig);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_XMRig, "XMRig");
#else
ui->actionShow_XMRig->setVisible(false);
#endif
connect(ui->actionShow_calc, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
m_tabShowHideMapper["Calc"] = new ToggleTab(ui->tabCalc, "Calc", "Calc", ui->actionShow_calc, Config::showTabCalc);
m_tabShowHideSignalMapper->setMapping(ui->actionShow_calc, "Calc");
for (const auto &key: m_tabShowHideMapper.keys()) {
const auto toggleTab = m_tabShowHideMapper.value(key);
@ -434,7 +389,6 @@ void MainWindow::initMenu() {
connect(ui->actionSeed, &QAction::triggered, this, &MainWindow::showSeedDialog);
connect(ui->actionPassword, &QAction::triggered, this, &MainWindow::showPasswordDialog);
connect(ui->actionKeys, &QAction::triggered, this, &MainWindow::showKeysDialog);
connect(ui->actionViewOnly, &QAction::triggered, this, &MainWindow::showViewOnlyDialog);
connect(ui->actionStore_wallet, &QAction::triggered, [&]{
m_ctx->currentWallet->store();
});
@ -444,10 +398,6 @@ void MainWindow::initMenu() {
connect(ui->actionUpdate_balance, &QAction::triggered, [&]{
m_ctx->updateBalance();
});
connect(ui->actionExportKeyImages, &QAction::triggered, this, &MainWindow::exportKeyImages);
connect(ui->actionImportKeyImages, &QAction::triggered, this, &MainWindow::importKeyImages);
connect(ui->actionExportOutputs, &QAction::triggered, this, &MainWindow::exportOutputs);
connect(ui->actionImportOutputs, &QAction::triggered, this, &MainWindow::importOutputs);
// set restore height
connect(ui->actionChange_restore_height, &QAction::triggered, this, &MainWindow::showRestoreHeightDialog);
@ -462,8 +412,7 @@ void MainWindow::initMenu() {
connect(ui->actionExport_CSV, &QAction::triggered, [=]{
if(m_ctx->currentWallet == nullptr) return;
QString fn = QFileDialog::getSaveFileName(this, "Save CSV file", QDir::homePath(), "CSV (*.csv)");
if(fn.isEmpty()) return;
if(!fn.endsWith(".csv")) fn += ".csv";
if(!fn.startsWith(".csv")) fn += ".csv";
m_ctx->currentWallet->history()->writeCSV(fn);
Utils::showMessageBox("CSV export", QString("Transaction history exported to %1").arg(fn), false);
});
@ -486,15 +435,9 @@ void MainWindow::initMenu() {
Utils::showMessageBox("Address book exported", QString("Address book exported to %1").arg(fn), false);
});
connect(ui->actionImportContactsCSV, &QAction::triggered, this, &MainWindow::importContacts);
// Tools
connect(ui->actionSignVerify, &QAction::triggered, this, &MainWindow::menuSignVerifyClicked);
connect(ui->actionVerifyTxProof, &QAction::triggered, this, &MainWindow::menuVerifyTxProof);
connect(ui->actionLoadUnsignedTxFromFile, &QAction::triggered, this, &MainWindow::loadUnsignedTx);
connect(ui->actionLoadUnsignedTxFromClipboard, &QAction::triggered, this, &MainWindow::loadUnsignedTxFromClipboard);
connect(ui->actionLoadSignedTxFromFile, &QAction::triggered, this, &MainWindow::loadSignedTx);
connect(ui->actionLoadSignedTxFromText, &QAction::triggered, this, &MainWindow::loadSignedTxFromText);
// About screen
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::menuAboutClicked);
@ -534,11 +477,7 @@ WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
void MainWindow::showWizard(WalletWizard::Page startPage) {
this->setEnabled(false);
if (m_wizard == nullptr)
m_wizard = this->createWizard(startPage);
m_wizard->setStartId(startPage);
m_wizard->restart();
m_wizard->setEnabled(true);
m_wizard = this->createWizard(startPage);
m_wizard->show();
}
@ -554,32 +493,17 @@ void MainWindow::onWalletClosed(WalletWizard::Page page) {
this->showWizard(page);
}
void MainWindow::touchbarShowWizard() {
#ifdef Q_OS_MAC
m_touchbar->clear();
for(auto* action: m_touchbarWizardItems) m_touchbar->addAction(action);
#endif
}
void MainWindow::touchbarShowWallet() {
#ifdef Q_OS_MAC
m_touchbar->clear();
for(auto* action: m_touchbarWalletItems) m_touchbar->addAction(action);
#endif
}
void MainWindow::onWalletCreatedError(const QString &err) {
Utils::showMessageBox("Wallet creation error", err, true);
this->showWizard(WalletWizard::Page_CreateWallet);
}
void MainWindow::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
QFileInfo fileInfo(path);
void MainWindow::onWalletOpenPasswordRequired(bool invalidPassword) {
QInputDialog passwordDialog(this);
passwordDialog.setInputMode(QInputDialog::TextInput);
passwordDialog.setTextEchoMode(QLineEdit::Password);
passwordDialog.setWindowTitle("Password required");
passwordDialog.setLabelText(QString("Please enter %1 wallet password.").arg(fileInfo.fileName()));
passwordDialog.setLabelText("Please enter wallet password.");
passwordDialog.resize(300, 100);
if(!(bool)passwordDialog.exec())
return this->showWizard(WalletWizard::Page_OpenWallet);
@ -594,7 +518,6 @@ void MainWindow::onWalletOpenedError(const QString &err) {
QMessageBox::warning(this, "Wallet open error", err);
this->setWindowTitle("Feather");
this->showWizard(WalletWizard::Page_OpenWallet);
this->touchbarShowWizard();
}
void MainWindow::onWalletCreated(Wallet *wallet) {
@ -607,6 +530,8 @@ void MainWindow::onWalletOpened() {
qDebug() << Q_FUNC_INFO;
if(m_wizard != nullptr) {
m_wizard->hide();
m_wizard->disconnect();
m_wizard->deleteLater();
}
this->raise();
@ -618,6 +543,10 @@ void MainWindow::onWalletOpened() {
else
m_statusLabelStatus->setText("Wallet opened - Searching for node");
// window title as wallet name
QFileInfo fileInfo(m_ctx->walletPath);
this->setWindowTitle(QString("Feather - [%1]").arg(fileInfo.fileName()));
connect(m_ctx->currentWallet, &Wallet::connectionStatusChanged, this, &MainWindow::onConnectionStatusChanged);
// receive page
@ -634,7 +563,7 @@ void MainWindow::onWalletOpened() {
// history page
m_ctx->currentWallet->history()->refresh(m_ctx->currentWallet->currentSubaddressAccount());
ui->historyWidget->setModel(m_ctx->currentWallet->historyModel(), m_ctx->currentWallet);
ui->historyWidget->setModel(m_ctx->currentWallet->coins(), m_ctx->currentWallet->historyModel(), m_ctx->currentWallet->history());
connect(m_ctx->currentWallet->history(), &TransactionHistory::txNoteChanged, [this]{
m_ctx->storeWallet();
});
@ -654,24 +583,14 @@ void MainWindow::onWalletOpened() {
connect(m_ctx->currentWallet->coins(), &Coins::coinThawed, [this]{
m_ctx->storeWallet();
});
this->touchbarShowWallet();
}
void MainWindow::onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str) {
qDebug() << Q_FUNC_INFO;
bool hide = config()->get(Config::hideBalance).toBool();
auto label_str = QString("Balance: %1 XMR").arg(unlocked_str);
if(balance > unlocked)
label_str += QString(" (+%1 XMR unconfirmed)").arg(QString::number(balance - unlocked, 'f'));
if (hide) {
label_str = "Balance: HIDDEN";
}
m_statusLabelBalance->setText(label_str);
m_balanceWidget->setHidden(hide);
}
void MainWindow::onSynchronized() {
@ -749,26 +668,15 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QStrin
} else {
const auto &description = m_ctx->tmpTxDescription;
auto *dialog = new TxConfDialog(m_ctx, tx, address, description, mixin, this);
auto *dialog = new TxConfDialog(tx, address, description, mixin, this);
switch (dialog->exec()) {
case QDialog::Rejected:
{
if (!dialog->showAdvanced)
m_ctx->onCancelTransaction(tx, address);
m_ctx->onCancelTransaction(tx, address);
break;
}
case QDialog::Accepted:
m_ctx->currentWallet->commitTransactionAsync(tx);
break;
}
if (dialog->showAdvanced) {
auto *dialog_adv = new TxConfAdvDialog(m_ctx, description, this);
dialog_adv->setTransaction(tx);
dialog_adv->exec();
dialog_adv->deleteLater();
}
dialog->deleteLater();
}
}
@ -942,12 +850,6 @@ void MainWindow::showKeysDialog() {
dialog->deleteLater();
}
void MainWindow::showViewOnlyDialog() {
auto *dialog = new ViewOnlyDialog(m_ctx, this);
dialog->exec();
dialog->deleteLater();
}
void MainWindow::menuTorClicked() {
auto *dialog = new TorInfoDialog(m_ctx, this);
@ -962,8 +864,6 @@ void MainWindow::menuNewRestoreClicked() {
}
void MainWindow::menuQuitClicked() {
cleanupBeforeClose();
QCoreApplication::quit();
}
@ -1037,7 +937,11 @@ void MainWindow::homeWidgetChanged(const QString &widgetName) {
}
void MainWindow::closeEvent(QCloseEvent *event) {
cleanupBeforeClose();
m_ctx->tor->stop();
this->saveGeo();
if(m_wizard != nullptr)
m_wizard->close();
QWidget::closeEvent(event);
}
@ -1048,17 +952,17 @@ void MainWindow::donateButtonClicked() {
donation = 1.337;
ui->sendWidget->fill(m_ctx->featherDonationAddress, "Donation to the Feather development team", donation);
ui->tabWidget->setCurrentIndex(Tabs::SEND);
ui->tabWidget->setCurrentIndex(2);
}
void MainWindow::showHistoryTab() {
this->raise();
ui->tabWidget->setCurrentIndex(Tabs::HISTORY);
ui->tabWidget->setCurrentIndex(1);
}
void MainWindow::showSendTab() {
this->raise();
ui->tabWidget->setCurrentIndex(Tabs::SEND);
ui->tabWidget->setCurrentIndex(2);
}
void MainWindow::showCalcWindow() {
@ -1067,7 +971,7 @@ void MainWindow::showCalcWindow() {
void MainWindow::showSendScreen(const CCSEntry &entry) {
ui->sendWidget->fill(entry);
ui->tabWidget->setCurrentIndex(Tabs::SEND);
ui->tabWidget->setCurrentIndex(2);
}
void MainWindow::onViewOnBlockExplorer(const QString &txid) {
@ -1085,29 +989,6 @@ void MainWindow::onAddContact(const QString &address, const QString &name) {
}
}
void MainWindow::importContacts() {
const QString targetFile = QFileDialog::getOpenFileName(this, "Import CSV file", QDir::homePath(), "CSV Files (*.csv)");
if(targetFile.isEmpty()) return;
auto *model = m_ctx->currentWallet->addressBookModel();
QMapIterator<QString, QString> i(model->readCSV(targetFile));
int inserts = 0;
while (i.hasNext()) {
i.next();
bool addressValid = WalletManager::addressValid(i.value(), m_ctx->currentWallet->nettype());
if(addressValid) {
m_ctx->currentWallet->addressBook()->addRow(i.value(), "", i.key());
inserts++;
}
}
if(inserts > 0) {
m_ctx->storeWallet();
}
QMessageBox::information(this, "Contacts imported", QString("Total contacts imported: %1").arg(inserts));
}
MainWindow *MainWindow::getInstance() {
return pMainWindow;
}
@ -1179,123 +1060,6 @@ void MainWindow::showWSNodeExhaustedMessage() {
QMessageBox::warning(this, "Could not connect to a node", msg);
}
void MainWindow::exportKeyImages() {
QString fn = QFileDialog::getSaveFileName(this, "Save key images to file", QDir::homePath(), "Key Images (*_keyImages)");
if (fn.isEmpty()) return;
if (!fn.endsWith("_keyImages")) fn += "_keyImages";
m_ctx->currentWallet->exportKeyImages(fn, true);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Key image export", QString("Failed to export key images.\nReason: %1").arg(err));
} else {
QMessageBox::information(this, "Key image export", "Successfully exported key images.");
}
}
void MainWindow::importKeyImages() {
QString fn = QFileDialog::getOpenFileName(this, "Import key image file", QDir::homePath(), "Key Images (*_keyImages)");
if (fn.isEmpty()) return;
m_ctx->currentWallet->importKeyImages(fn);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Key image import", QString("Failed to import key images.\n\n%1").arg(err));
} else {
QMessageBox::information(this, "Key image import", "Successfully imported key images");
m_ctx->refreshModels();
}
}
void MainWindow::exportOutputs() {
QString fn = QFileDialog::getSaveFileName(this, "Save outputs to file", QDir::homePath(), "Outputs (*_outputs)");
if (fn.isEmpty()) return;
if (!fn.endsWith("_outputs")) fn += "_outputs";
m_ctx->currentWallet->exportOutputs(fn, true);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Outputs export", QString("Failed to export outputs.\nReason: %1").arg(err));
} else {
QMessageBox::information(this, "Outputs export", "Successfully exported outputs.");
}
}
void MainWindow::importOutputs() {
QString fn = QFileDialog::getOpenFileName(this, "Import outputs file", QDir::homePath(), "Outputs (*_outputs)");
if (fn.isEmpty()) return;
m_ctx->currentWallet->importOutputs(fn);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Outputs import", QString("Failed to import outputs.\n\n%1").arg(err));
} else {
QMessageBox::information(this, "Outputs import", "Successfully imported outputs");
m_ctx->refreshModels();
}
}
void MainWindow::cleanupBeforeClose() {
m_ctx->walletManager->closeWallet();
m_ctx->tor->stop();
this->saveGeo();
}
void MainWindow::loadUnsignedTx() {
QString fn = QFileDialog::getOpenFileName(this, "Select transaction to load", QDir::homePath(), "Transaction (*unsigned_monero_tx)");
if (fn.isEmpty()) return;
UnsignedTransaction *tx = m_ctx->currentWallet->loadTxFile(fn);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Load transaction from file", QString("Failed to load transaction.\n\n%1").arg(err));
return;
}
this->createUnsignedTxDialog(tx);
}
void MainWindow::loadUnsignedTxFromClipboard() {
QString unsigned_tx = Utils::copyFromClipboard();
if (unsigned_tx.isEmpty()) {
QMessageBox::warning(this, "Load unsigned transaction from clipboard", "Clipboard is empty");
return;
}
UnsignedTransaction *tx = m_ctx->currentWallet->loadTxFromBase64Str(unsigned_tx);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Load unsigned transaction from clipboard", QString("Failed to load transaction.\n\n%1").arg(err));
return;
}
this->createUnsignedTxDialog(tx);
}
void MainWindow::loadSignedTx() {
QString fn = QFileDialog::getOpenFileName(this, "Select transaction to load", QDir::homePath(), "Transaction (*signed_monero_tx)");
if (fn.isEmpty()) return;
PendingTransaction *tx = m_ctx->currentWallet->loadSignedTxFile(fn);
auto err = m_ctx->currentWallet->errorString();
if (!err.isEmpty()) {
QMessageBox::warning(this, "Load signed transaction from file", err);
return;
}
auto *dialog = new TxConfAdvDialog(m_ctx, "", this);
dialog->setTransaction(tx);
dialog->exec();
dialog->deleteLater();
}
void MainWindow::loadSignedTxFromText() {
auto dialog = new BroadcastTxDialog(this, m_ctx);
dialog->exec();
dialog->deleteLater();
}
void MainWindow::createUnsignedTxDialog(UnsignedTransaction *tx) {
auto *dialog = new TxConfAdvDialog(m_ctx, "", this);
dialog->setUnsignedTransaction(tx);
dialog->exec();
dialog->deleteLater();
}
MainWindow::~MainWindow() {
delete ui;
}

@ -4,10 +4,6 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#ifdef Q_OS_MAC
#include "src/kdmactouchbar.h"
#endif
#include <QMainWindow>
#include <QSystemTrayIcon>
#include <QScreen>
@ -23,7 +19,6 @@
#include "widgets/ccswidget.h"
#include "widgets/redditwidget.h"
#include "widgets/tickerwidget.h"
#include "widgets/xmrigwidget.h"
#include "utils/networking.h"
#include "appcontext.h"
#include "utils/config.h"
@ -69,17 +64,6 @@ public:
qreal screenDpiPhysical;
qreal screenRatio;
enum Tabs {
HOME = 0,
HISTORY,
SEND,
RECEIVE,
COINS,
CALC,
XMR_TO,
XMRIG
};
public slots:
void initWidgets();
void initMenu();
@ -95,7 +79,6 @@ public slots:
void showConnectionStatusDialog();
void showPasswordDialog();
void showKeysDialog();
void showViewOnlyDialog();
void donateButtonClicked();
void showCalcWindow();
void showSendTab();
@ -111,22 +94,11 @@ public slots:
void onWalletCreated(Wallet *wallet);
void menuWalletCloseClicked();
void menuWalletOpenClicked();
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
void onWalletOpenPasswordRequired(bool invalidPassword);
void onViewOnBlockExplorer(const QString &txid);
void onAddContact(const QString &address, const QString &name);
void importContacts();
void showRestoreHeightDialog();
// offline tx signing
void exportKeyImages();
void importKeyImages();
void exportOutputs();
void importOutputs();
void loadUnsignedTx();
void loadUnsignedTxFromClipboard();
void loadSignedTx();
void loadSignedTxFromText();
// libwalletqt
void onBalanceUpdated(double balance, double unlocked, const QString &balance_str, const QString &unlocked_str);
void onSynchronized();
@ -147,7 +119,6 @@ private:
static MainWindow * pMainWindow;
void closeEvent(QCloseEvent *event) override;
void cleanupBeforeClose();
void create_status_bar();
void initMain();
void loadSkins();
@ -157,9 +128,6 @@ private:
void showDebugInfo();
void showNodeExhaustedMessage();
void showWSNodeExhaustedMessage();
void createUnsignedTxDialog(UnsignedTransaction *tx);
void touchbarShowWizard();
void touchbarShowWallet();
WalletWizard *createWizard(WalletWizard::Page startPage);
@ -169,7 +137,6 @@ private:
SignVerifyDialog *m_windowSignVerify = nullptr;
RestoreDialog *m_restoreDialog = nullptr;
AboutDialog *m_aboutDialog = nullptr;
XMRigWidget *m_xmrig = nullptr;
bool m_windowSpawned = false;
@ -198,12 +165,7 @@ private:
SubaddressProxyModel *subaddressProxyModel;
TransactionHistoryModel *txHistModel;
CoinsModel *coinsModel;
#ifdef Q_OS_MAC
QAction *m_touchbarActionWelcome;
KDMacTouchBar *m_touchbar;
QList<QAction *> m_touchbarWalletItems;
QList<QAction *> m_touchbarWizardItems;
#endif
QSignalMapper *m_tabShowHideSignalMapper;
QMap<QString, ToggleTab*> m_tabShowHideMapper;
WalletWizard *m_wizard = nullptr;

@ -224,27 +224,13 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tabCalc">
<attribute name="icon">
<iconset resource="assets.qrc">
<normaloff>:/assets/images/coldcard.png</normaloff>:/assets/images/coldcard.png</iconset>
</attribute>
<attribute name="title">
<string>Calc</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="CalcWidget" name="conversionWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabXmrTo">
<attribute name="icon">
<iconset resource="assets.qrc">
<normaloff>:/assets/images/xmrto.png</normaloff>:/assets/images/xmrto.png</iconset>
</attribute>
<attribute name="title">
<string>XMR.to</string>
<string>xmr.to</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
@ -280,17 +266,17 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tabXmrRig">
<widget class="QWidget" name="tabCalc">
<attribute name="icon">
<iconset resource="assets.qrc">
<normaloff>:/assets/images/xmrig.ico</normaloff>:/assets/images/xmrig.ico</iconset>
<normaloff>:/assets/images/coldcard.png</normaloff>:/assets/images/coldcard.png</iconset>
</attribute>
<attribute name="title">
<string>XMRig</string>
<string>Calc</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QGridLayout" name="xmrRigLayout"/>
<widget class="CalcWidget" name="conversionWidget" native="true"/>
</item>
</layout>
</widget>
@ -333,33 +319,15 @@
<string>Contacts</string>
</property>
<addaction name="actionExportContactsCSV"/>
<addaction name="actionImportContactsCSV"/>
</widget>
<widget class="QMenu" name="menuAdvanced">
<property name="title">
<string>Advanced</string>
</property>
<widget class="QMenu" name="menuExport">
<property name="title">
<string>Export</string>
</property>
<addaction name="actionExportOutputs"/>
<addaction name="actionExportKeyImages"/>
</widget>
<widget class="QMenu" name="menuImport">
<property name="title">
<string>Import</string>
</property>
<addaction name="actionImportOutputs"/>
<addaction name="actionImportKeyImages"/>
</widget>
<addaction name="actionStore_wallet"/>
<addaction name="actionUpdate_balance"/>
<addaction name="actionRefresh_tabs"/>
<addaction name="separator"/>
<addaction name="actionChange_restore_height"/>
<addaction name="menuExport"/>
<addaction name="menuImport"/>
</widget>
<addaction name="actionInformation"/>
<addaction name="menuAdvanced"/>
@ -367,7 +335,6 @@
<addaction name="actionPassword"/>
<addaction name="actionSeed"/>
<addaction name="actionKeys"/>
<addaction name="actionViewOnly"/>
<addaction name="separator"/>
<addaction name="menuHistory"/>
<addaction name="menuContacts"/>
@ -376,26 +343,9 @@
<property name="title">
<string>Tools</string>
</property>
<widget class="QMenu" name="menuLoad_transaction">
<property name="title">
<string>Load unsigned transaction</string>
</property>
<addaction name="actionLoadUnsignedTxFromFile"/>
<addaction name="actionLoadUnsignedTxFromClipboard"/>
</widget>
<widget class="QMenu" name="menuLoad_signed_transaction">
<property name="title">
<string>Broadcast transaction</string>
</property>
<addaction name="actionLoadSignedTxFromFile"/>
<addaction name="actionLoadSignedTxFromText"/>
</widget>
<addaction name="actionSignVerify"/>
<addaction name="actionVerifyTxProof"/>
<addaction name="separator"/>
<addaction name="menuLoad_transaction"/>
<addaction name="menuLoad_signed_transaction"/>
<addaction name="separator"/>
<addaction name="actionCalculator"/>
<addaction name="actionCreateDesktopEntry"/>
</widget>
@ -416,9 +366,8 @@
<string>View</string>
</property>
<addaction name="actionShow_Coins"/>
<addaction name="actionShow_calc"/>
<addaction name="actionShow_xmr_to"/>
<addaction name="actionShow_XMRig"/>
<addaction name="actionShow_calc"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuWallet"/>
@ -506,7 +455,7 @@
</action>
<action name="actionShow_xmr_to">
<property name="text">
<string>Show XMR.to</string>
<string>Show Xmr.To</string>
</property>
</action>
<action name="actionShow_calc">
@ -539,11 +488,6 @@
<string>Refresh models</string>
</property>
</action>
<action name="actionImportContactsCSV">
<property name="text">
<string>Import CSV</string>
</property>
</action>
<action name="actionExportContactsCSV">
<property name="text">
<string>Export CSV</string>
@ -574,96 +518,6 @@
<string>Debug info</string>
</property>
</action>
<action name="actionCreate_view_only_details">
<property name="text">
<string>Details</string>
</property>
</action>
<action name="actionCreate_view_only_wallet_file">
<property name="text">
<string>Export wallet file</string>
</property>
</action>
<action name="actionViewOnly">
<property name="text">
<string>View-Only</string>
</property>
</action>
<action name="actionExport_key_images">
<property name="text">
<string>Export key images</string>
</property>
</action>
<action name="actionImport_key_images">
<property name="text">
<string>Import key images</string>
</property>
</action>
<action name="actionExport_outputs">
<property name="text">
<string>Export outputs</string>
</property>
</action>
<action name="actionImport_outputs">
<property name="text">
<string>Import outputs</string>
</property>
</action>
<action name="actionExportKeyImages">
<property name="text">
<string>Key Images</string>
</property>
</action>
<action name="actionExportOutputs">
<property name="text">
<string>Outputs</string>
</property>
</action>
<action name="actionImportKeyImages">
<property name="text">
<string>Key images</string>
</property>
</action>
<action name="actionImportOutputs">
<property name="text">
<string>Outputs</string>
</property>
</action>
<action name="actionShow_XMRig">
<property name="text">
<string>Show XMRig</string>
</property>
</action>
<action name="actionImportTransaction">
<property name="text">
<string>Transaction</string>
</property>
</action>
<action name="actionSubmitTransaction">
<property name="text">
<string>Submit transaction file</string>
</property>
</action>
<action name="actionLoadUnsignedTxFromFile">
<property name="text">
<string>From file</string>
</property>
</action>
<action name="actionLoadSignedTxFromFile">
<property name="text">
<string>From file</string>
</property>
</action>
<action name="actionLoadUnsignedTxFromClipboard">
<property name="text">
<string>From clipboard</string>
</property>
</action>
<action name="actionLoadSignedTxFromText">
<property name="text">
<string>From text</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

@ -174,26 +174,3 @@ bool AddressBookModel::writeCSV(const QString &path) {
csv = QString("address,description\n%1").arg(csv);
return Utils::fileWrite(path, csv);
}
QMap<QString, QString> AddressBookModel::readCSV(const QString &path) {
if(!Utils::fileExists(path)) {
return QMap<QString, QString>();
}
QString csv = Utils::barrayToString(Utils::fileOpen(path));
QTextStream stream(&csv);
QMap<QString, QString> map;
while(!stream.atEnd()) {
QStringList line = stream.readLine().split(",");
if(line.length() != 2) {
continue;
}
QString address = line.at(0);
QString description = line.at(1);
description = description.replace("\"", "");
if(!description.isEmpty() && !address.isEmpty()) {
map[description] = address;
}
}
return map;
}

@ -36,7 +36,6 @@ public:
bool isShowFullAddresses() const;
void setShowFullAddresses(bool show);
bool writeCSV(const QString &path);
QMap<QString, QString> readCSV(const QString &path);
public slots:
void startReset();

@ -6,7 +6,6 @@
#include "Coins.h"
#include <wallet/api/wallet2_api.h>
#include "ModelUtils.h"
#include "globals.h"
#include <QDebug>
#include <QHash>
@ -20,9 +19,6 @@ CoinsModel::CoinsModel(QObject *parent, Coins *coins)
{
connect(m_coins, &Coins::refreshStarted, this, &CoinsModel::startReset);
connect(m_coins, &Coins::refreshFinished, this, &CoinsModel::endReset);
m_eye = QIcon(":/assets/images/eye1.png");
m_eyeBlind = QIcon(":/assets/images/eye_blind.png");
}
void CoinsModel::startReset(){
@ -86,19 +82,6 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
result = Qt::AlignRight;
}
}
else if (role == Qt::DecorationRole) {
switch (index.column()) {
case KeyImageKnown:
{
if (cInfo.keyImageKnown()) {
result = QVariant(m_eye);
}
else {
result = QVariant(m_eyeBlind);
}
}
}
}
else if (role == Qt::FontRole) {
switch(index.column()) {
case PubKey:
@ -108,16 +91,6 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
}
}
else if (role == Qt::ToolTipRole) {
switch(index.column()) {
case KeyImageKnown:
{
if (cInfo.keyImageKnown()) {
result = "Key image known";
} else {
result = "Key image unknown. Outgoing transactions that include this output will not be detected.";
}
}
}
if (cInfo.frozen()) {
result = "Output is frozen.";
}
@ -169,8 +142,6 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
{
switch (column)
{
case KeyImageKnown:
return "";
case PubKey:
return cInfo.pubKey().mid(0,8);
case OutputPoint:
@ -191,14 +162,10 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
case SpentHeight:
return cInfo.spentHeight();
case Amount:
{
if (role == Qt::UserRole) {
return cInfo.amount() / globals::cdiv;
}
return QString::number(cInfo.amount() / globals::cdiv, 'f', 12);
}
return QString::number(cInfo.amount() / 1e12, 'f', 12);
case Frozen:
return cInfo.frozen();
default:
{
qCritical() << "Unimplemented role";

@ -9,7 +9,6 @@
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
#include <QDebug>
#include <QIcon>
class Coins;
class CoinsInfo;
@ -21,8 +20,7 @@ Q_OBJECT
public:
enum ModelColumn
{
KeyImageKnown = 0,
PubKey,
PubKey = 0,
OutputPoint,
Address,
AddressLabel,
@ -51,8 +49,6 @@ private:
QVariant parseTransactionInfo(const CoinsInfo &cInfo, int column, int role) const;
Coins *m_coins;
QIcon m_eye;
QIcon m_eyeBlind;
};
#endif //FEATHER_COINSMODEL_H

@ -7,7 +7,6 @@
CoinsProxyModel::CoinsProxyModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
setSortRole(Qt::UserRole);
}
bool CoinsProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const

@ -8,8 +8,8 @@
NodeModel::NodeModel(unsigned int nodeSource, QObject *parent)
: QAbstractTableModel(parent)
, m_nodeSource(nodeSource)
, m_offline(QIcon(":/assets/images/expired_icon.png"))
, m_online(QIcon(":/assets/images/confirmed_icon.png"))
, m_offline(QIcon(":/assets/images/expired.png"))
, m_online(QIcon(":/assets/images/confirmed.png"))
{
}

@ -4,7 +4,6 @@
#include "TransactionHistoryModel.h"
#include "TransactionHistory.h"
#include "TransactionInfo.h"
#include "globals.h"
#include <QDateTime>
#include <QDebug>
@ -152,7 +151,7 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionInfo &tI
}
case Column::Amount:
{
QString amount = QString::number(tInfo.atomicAmount() / globals::cdiv, 'f', 4);
QString amount = QString::number(tInfo.atomicAmount() / 1e12, 'f', 4);
amount = (tInfo.direction() == TransactionInfo::Direction_Out && tInfo.amount() > 0) ? "-" + amount : "+" + amount;
return amount;
}

@ -141,8 +141,7 @@ void ReceiveWidget::setQrCode(const QString &address){
void ReceiveWidget::showQrCodeDialog() {
QModelIndex index = ui->addresses->currentIndex();
QString address = index.model()->data(index.siblingAtColumn(SubaddressModel::Address), Qt::UserRole).toString();
QrCode qr(address, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
auto *dialog = new QrCodeDialog(this, qr, "Address");
auto *dialog = new QrCodeDialog(this, address, "Address");
dialog->exec();
dialog->deleteLater();
}

@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include <QMessageBox>
#include "sendwidget.h"
#include "widgets/ccswidget.h"
@ -26,8 +27,8 @@ SendWidget::SendWidget(QWidget *parent) :
connect(ui->lineAmount, &QLineEdit::textEdited, this, &SendWidget::amountEdited);
connect(ui->lineAddress, &QLineEdit::textEdited, this, &SendWidget::addressEdited);
connect(ui->btn_openAlias, &QPushButton::clicked, this, &SendWidget::aliasClicked);
ui->label_conversionAmount->setText("");
ui->label_conversionAmount->hide();
ui->label_xmrAmount->setText("");
ui->label_xmrAmount->hide();
ui->btn_openAlias->hide();
}
@ -74,7 +75,7 @@ void SendWidget::fillAddress(const QString &address) {
void SendWidget::sendClicked() {
double amount = 0.0;
QString currency = ui->comboCurrencySelection->currentText();
QString recipient = ui->lineAddress->text().simplified().remove(' ');
QString recipient = ui->lineAddress->text(); // @TODO: regex
QString description = ui->lineDescription->text();
if(recipient.isEmpty()) {
QMessageBox::warning(this, "Malformed recipient", "The recipient address was not correct");
@ -119,26 +120,18 @@ void SendWidget::btnMaxClicked() {
void SendWidget::updateConversionLabel() {
auto amount = this->amount();
if(amount == -1) return;
ui->label_conversionAmount->setText("");
ui->label_xmrAmount->setText("");
if(amount <= 0) {
ui->label_conversionAmount->hide();
ui->label_xmrAmount->hide();
return;
}
QString conversionAmountStr = [this]{
QString currency = ui->comboCurrencySelection->currentText();
if (currency != "XMR") {
return QString("~%1 XMR").arg(QString::number(this->conversionAmount(), 'f'));
} else {
auto preferredFiatCurrency = config()->get(Config::preferredFiatCurrency).toString();
double conversionAmount = AppContext::prices->convert("XMR", preferredFiatCurrency, this->amount());
return QString("~%1 %2").arg(QString::number(conversionAmount, 'f', 2), preferredFiatCurrency);
};
}();
ui->label_conversionAmount->setText(conversionAmountStr);
ui->label_conversionAmount->show();
QString currency = ui->comboCurrencySelection->currentText();
if (currency != "XMR") {
QString xmr_str = QString("%1 XMR").arg(QString::number(this->conversionAmount()));
ui->label_xmrAmount->setText(xmr_str);
ui->label_xmrAmount->show();
}
}
double SendWidget::conversionAmount() {
@ -170,7 +163,7 @@ void SendWidget::clearFields() {
ui->lineAddress->clear();
ui->lineAmount->clear();
ui->lineDescription->clear();
ui->label_conversionAmount->clear();
ui->label_xmrAmount->clear();
}
void SendWidget::onWalletClosed() {
@ -185,10 +178,6 @@ void SendWidget::onEndTransaction() {
ui->btnSend->setEnabled(true);
}
void SendWidget::onPreferredFiatCurrencyChanged() {
this->updateConversionLabel();
}
SendWidget::~SendWidget() {
delete ui;
}

@ -38,7 +38,6 @@ public slots:
void onOpenAliasResolveError(const QString &err);
void onOpenAliasResolved(const QString &address, const QString &openAlias);
void onWalletClosed();
void onPreferredFiatCurrencyChanged();
void onInitiateTransaction();
void onEndTransaction();

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>175</height>
<width>500</width>
<height>148</height>
</rect>
</property>
<property name="windowTitle">
@ -126,11 +126,6 @@
<string>GBP</string>
</property>
</item>
<item>
<property name="text">
<string>AUD</string>
</property>
</item>
<item>
<property name="text">
<string>JPY</string>
@ -138,12 +133,12 @@
</item>
<item>
<property name="text">
<string>NZD</string>
<string>RMB</string>
</property>
</item>
<item>
<property name="text">
<string>RMB</string>
<string>AUD</string>
</property>
</item>
</widget>
@ -158,12 +153,12 @@
</widget>
</item>
<item>
<widget class="QLabel" name="label_conversionAmount">
<widget class="QLabel" name="label_xmrAmount">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>conversionAmount</string>
<string>xmrAmount</string>
</property>
</widget>
</item>

@ -22,11 +22,6 @@ Settings::Settings(QWidget *parent) :
connect(ui->btnCopyToClipboard, &QPushButton::clicked, this, &Settings::copyToClipboard);
connect(ui->checkBox_checkForAppUpdates, &QCheckBox::clicked, this, &Settings::checkboxExternalLinkWarn);
connect(ui->checkBox_externalLink, &QCheckBox::clicked, this, &Settings::checkboxExternalLinkWarn);
connect(ui->checkBox_hideBalance, &QCheckBox::toggled, [this](bool toggled){
config()->set(Config::hideBalance, toggled);
m_ctx->updateBalance();
});
connect(ui->closeButton, &QDialogButtonBox::accepted, this, &Settings::close);
// nodes
@ -37,7 +32,6 @@ Settings::Settings(QWidget *parent) :
// setup checkboxes
ui->checkBox_externalLink->setChecked(config()->get(Config::warnOnExternalLink).toBool());
ui->checkBox_checkForAppUpdates->setChecked(config()->get(Config::checkForAppUpdates).toBool());
ui->checkBox_hideBalance->setChecked(config()->get(Config::hideBalance).toBool());
// setup comboboxes
auto settingsHomeWidget = config()->get(Config::homeWidget).toString();

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1019</width>
<height>396</height>
<width>754</width>
<height>278</height>
</rect>
</property>
<property name="windowTitle">
@ -93,11 +93,6 @@
<string>MXN</string>
</property>
</item>
<item>
<property name="text">
<string>NZD</string>
</property>
</item>
<item>
<property name="text">
<string>SEK</string>
@ -175,11 +170,6 @@
<string>moneroblocks.info</string>
</property>
</item>
<item>
<property name="text">
<string>blockchair.com</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
@ -196,13 +186,6 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkBox_hideBalance">
<property name="text">
<string>Hide balance</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_node">

@ -57,13 +57,13 @@ QWidget
outline: 0;
}
QWidget::item:hover
QWidget:item:hover
{
background-color: #3daee9;
color: #eff0f1;
}
QWidget::item:selected
QWidget:item:selected
{
background-color: #3daee9;
}
@ -525,7 +525,7 @@ QTextEdit
QPlainTextEdit
{
background-color: #232629;
background-color: #232629;;
color: #eff0f1;
border-radius: 0.2ex;
border: 0.1ex solid #76797c;

@ -54,13 +54,13 @@ QWidget
outline: 0;
}
QWidget::item:hover
QWidget:item:hover
{
background-color: #33A4DF;
color: #31363B;
}
QWidget::item:selected
QWidget:item:selected
{
background-color: #33A4DF;
}
@ -567,7 +567,7 @@ QMainWindow::separator:hover
color: white;
padding-left: 0.4ex;
border: 0.1ex solid #BAB9B8;
spacing: 0.2ex;
spacing: 0.2x;
}
QMenu::separator
@ -1659,14 +1659,12 @@ QPushButton:flat {
border: none;
}
QScrollBar::handle:vertical,
QScrollBar::handle:horizontal
QScrollBar::handle:vertical
{
background-color: #BAB9B8;
}
QScrollBar:vertical,
QScrollBar:horizontal
QScrollBar:vertical
{
background-color: #eff0f1;
border: 0.1ex transparent #eff0f1;

@ -34,20 +34,16 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::walletDirectory,{QS("walletDirectory"), ""}},
{Config::autoOpenWalletPath,{QS("autoOpenWalletPath"), ""}},
{Config::walletPath,{QS("walletPath"), ""}},
{Config::xmrigPath,{QS("xmrigPath"), ""}},
{Config::xmrigPool,{QS("xmrigPool"), "pool.xmr.pt:9000"}},
{Config::nodes,{QS("nodes"), "{}"}},
{Config::websocketEnabled,{QS("websocketEnabled"), true}},
{Config::nodeSource,{QS("nodeSource"), 0}},
{Config::useOnionNodes,{QS("useOnionNodes"), false}},
{Config::showTabCoins,{QS("showTabCoins"), false}},
{Config::showTabXMRto,{QS("showTabXMRto"), true}},
{Config::showTabXMRig,{QS("showTabXMRig"), false}},
{Config::showTabCalc,{QS("showTabCalc"), true}},
{Config::geometry, {QS("geometry"), {}}},
{Config::windowState, {QS("windowState"), {}}},
{Config::firstRun,{QS("firstRun"), false}},
{Config::hideBalance, {QS("hideBalance"), false}}
};

@ -32,8 +32,6 @@ public:
blockExplorer,
walletDirectory,
walletPath,
xmrigPath,
xmrigPool,
nodes,
websocketEnabled,
nodeSource,
@ -41,11 +39,9 @@ public:
showTabCoins,
showTabXMRto,
showTabCalc,
showTabXMRig,
geometry,
windowState,
firstRun,
hideBalance
firstRun
};
~Config() override;

@ -1,89 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include "daemonrpc.h"
#include <utility>
DaemonRpc::DaemonRpc(QObject *parent, UtilsNetworking *network, QString daemonAddress)
: QObject(parent)
, m_network(network)
, m_daemonAddress(std::move(daemonAddress))
{
}
void DaemonRpc::sendRawTransaction(const QString &tx_as_hex, bool do_not_relay, bool do_sanity_checks) {
QJsonObject req;
req["tx_as_hex"] = tx_as_hex;
req["do_not_relay"] = do_not_relay;
req["do_sanity_checks"] = do_sanity_checks;
QString url = QString("%1/send_raw_transaction").arg(m_daemonAddress);
QNetworkReply *reply = m_network->postJson(url, req);
connect(reply, &QNetworkReply::finished, std::bind(&DaemonRpc::onResponse, this, reply, Endpoint::SEND_RAW_TRANSACTION));
}
void DaemonRpc::onResponse(QNetworkReply *reply, Endpoint endpoint) {
const auto ok = reply->error() == QNetworkReply::NoError;
const auto err = reply->errorString();
QByteArray data = reply->readAll();
QJsonObject obj;
if (!data.isEmpty() && Utils::validateJSON(data)) {
auto doc = QJsonDocument::fromJson(data);
obj = doc.object();
}
else if (!ok) {
emit ApiResponse(DaemonResponse(false, endpoint, err));
return;
}
else {
emit ApiResponse(DaemonResponse(false, endpoint, "Invalid response from daemon"));
return;
}
if (obj.value("status").toString() != "OK") {
QString failedMsg;
switch (endpoint) {
case SEND_RAW_TRANSACTION:
failedMsg = this->onSendRawTransactionFailed(obj);
break;
default:
failedMsg = obj.value("status").toString();
}
emit ApiResponse(DaemonResponse(false, endpoint, failedMsg, obj));
return;
}
reply->deleteLater();
emit ApiResponse(DaemonResponse(true, endpoint, "", obj));
}
QString DaemonRpc::onSendRawTransactionFailed(const QJsonObject &obj) {
QString message = [&obj]{
if (obj.value("double_spend").toBool())
return "Transaction is a double spend";
if (obj.value("fee_too_low").toBool())
return "Fee is too low";
if (obj.value("invalid_input").toBool())
return "Output is invalid";
if (obj.value("low_mixin").toBool())
return "Mixin count is too low";
if (obj.value("overspend").toBool())
return "Transaction uses more money than available";
if (obj.value("too_big").toBool())
return "Transaction size is too big";
return "Daemon returned an unknown error";
}();
return QString("Transaction failed: %1").arg(message);
}
void DaemonRpc::setDaemonAddress(const QString &daemonAddress) {
m_daemonAddress = daemonAddress;
}
void DaemonRpc::setNetwork(UtilsNetworking *network) {
m_network = network;
}

@ -1,49 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_DAEMON_RPC_H
#define FEATHER_DAEMON_RPC_H
#include <QObject>
#include "utils/networking.h"
class DaemonRpc : public QObject {
Q_OBJECT
public:
enum Endpoint {
SEND_RAW_TRANSACTION = 0
};
struct DaemonResponse {
explicit DaemonResponse(bool ok, Endpoint endpoint, QString status, QJsonObject obj = {})
: ok(ok), endpoint(endpoint), status(std::move(status)), obj(std::move(obj)) {};
bool ok;
DaemonRpc::Endpoint endpoint;
QString status;
QJsonObject obj;
};
explicit DaemonRpc(QObject *parent, UtilsNetworking *network, QString daemonAddress);
void sendRawTransaction(const QString &tx_as_hex, bool do_not_relay = false, bool do_sanity_checks = true);
void setDaemonAddress(const QString &daemonAddress);
void setNetwork(UtilsNetworking *network);
signals:
void ApiResponse(DaemonResponse resp);
private slots:
void onResponse(QNetworkReply *reply, Endpoint endpoint);
QString onSendRawTransactionFailed(const QJsonObject &obj);
private:
UtilsNetworking *m_network;
QString m_daemonAddress;
};
#endif //FEATHER_DAEMON_RPC_H

@ -25,9 +25,6 @@ struct FeatherNode {
FeatherNode(QString _address, unsigned int height, bool online) : height(height), online(online){
// wonky ipv4/host parsing, should be fine(tm)(c).
if(_address.isEmpty()) return;
if(_address.contains("https://")) {
this->isHttps = true;
}
_address = _address.replace("https://", "");
_address = _address.replace("http://", "");
if(_address.contains("@")){ // authentication, user/pass
@ -59,7 +56,6 @@ struct FeatherNode {
bool tor = false;
bool isConnecting = false;
bool isActive = false;
bool isHttps = false;
QString generateFull() {
QString auth;
@ -69,7 +65,7 @@ struct FeatherNode {
}
QString as_url() {
return QString("%1://%2/get_info").arg(this->isHttps ? "https": "http",this->full);
return QString("http://%1/get_info").arg(this->full);
}
bool operator == (const FeatherNode &other) const {

@ -28,7 +28,6 @@ Prices::Prices(QObject *parent) : QObject(parent) {
fiat["CNY"] = "¥";
fiat["CZK"] = "";
fiat["AUD"] = "$";
fiat["NZD"] = "$";
}
void Prices::cryptoPricesReceived(const QJsonArray &data) {

@ -3,17 +3,9 @@
#include "scheduler.h"
#include <mutex>
#include <QThreadPool>
FutureScheduler::FutureScheduler(QObject *parent)
: QObject(parent), Alive(0), Stopping(false)
{
static std::once_flag once;
std::call_once(once, []() {
QThreadPool::globalInstance()->setMaxThreadCount(4);
});
}
FutureScheduler::~FutureScheduler()
@ -76,11 +68,6 @@ QPair<bool, QFuture<void>> FutureScheduler::run(std::function<void()> function)
// });
//}
bool FutureScheduler::stopping() const noexcept
{
return Stopping;
}
bool FutureScheduler::add() noexcept
{
QMutexLocker locker(&Mutex);

@ -26,7 +26,6 @@ public:
QPair<bool, QFuture<void>> run(std::function<void()> function) noexcept;
// QPair<bool, QFuture<QJSValueList>> run(std::function<QJSValueList()> function, const QJSValue &callback);
bool stopping() const noexcept;
private:
bool add() noexcept;
@ -63,7 +62,7 @@ private:
size_t Alive;
QWaitCondition Condition;
QMutex Mutex;
std::atomic<bool> Stopping;
bool Stopping;
};
#endif // FUTURE_SCHEDULER_H

@ -84,16 +84,16 @@ struct FeatherSeed {
time_t time = 0;
unsigned int restoreHeight = 0;
RestoreHeightLookup *lookup = nullptr;
QString language;
QString language = "English";
std::string coinName;
explicit FeatherSeed(RestoreHeightLookup *lookup, const std::string &coinName = "monero", const QString &language = "English") : lookup(lookup), coinName(coinName), language(language) {}
explicit FeatherSeed(RestoreHeightLookup *lookup, const std::string &coinName = "monero") : lookup(lookup), coinName(coinName) {}
static FeatherSeed fromSeed(RestoreHeightLookup *lookup,
const std::string &coinName,
const QString &seedLanguage,
const std::string &mnemonicSeed) {
auto rtn = FeatherSeed(lookup, coinName, seedLanguage);
auto rtn = FeatherSeed(lookup, coinName);
rtn.coinName = coinName;
rtn.lookup = lookup;
rtn.mnemonicSeed = QString::fromStdString(mnemonicSeed);
@ -108,8 +108,8 @@ struct FeatherSeed {
return rtn;
}
static FeatherSeed generate(RestoreHeightLookup *lookup, const std::string &coinName, const QString &language) {
auto rtn = FeatherSeed(lookup, coinName, language);
static FeatherSeed generate(RestoreHeightLookup *lookup, const std::string &coinName) {
auto rtn = FeatherSeed(lookup, coinName);
time_t _time = std::time(nullptr);
monero_seed seed(_time, coinName);
@ -130,7 +130,7 @@ struct FeatherSeed {
if(this->lookup == nullptr) return wallet;
if(this->mnemonicSeed.split(" ").count() == 14) {
if(this->spendKey.isEmpty()) {
auto _seed = FeatherSeed::fromSeed(this->lookup, this->coinName, this->language, this->mnemonicSeed.toStdString());
auto _seed = FeatherSeed::fromSeed(this->lookup, this->coinName, this->mnemonicSeed.toStdString());
_seed.setRestoreHeight();
this->time = _seed.time;
this->restoreHeight = _seed.restoreHeight;

@ -38,20 +38,6 @@ bool TailsOS::detectDotPersistence()
return QDir(tailsPathData + "dotfiles").exists();
}
QString TailsOS::version()
{
if (!Utils::fileExists("/etc/os-release"))
return "";
QByteArray data = Utils::fileOpen("/etc/os-release");
QRegExp re(R"(TAILS_VERSION_ID="(\d+.\d+))");
int pos = re.indexIn(data);
if (pos >= 0) {
return re.cap(1);
}
return "";
}
void TailsOS::showDataPersistenceDisabledWarning()
{
QMessageBox msgBox;

@ -13,7 +13,6 @@ public:
static bool detect();
static bool detectDataPersistence();
static bool detectDotPersistence();
static QString version();
static void showDataPersistenceDisabledWarning();
static void askPersistence();

@ -43,15 +43,9 @@ Tor::Tor(AppContext *ctx, QObject *parent)
return;
}
#ifndef HAS_TOR
qCritical() << "Feather built without embedded Tor. Assuming --use-local-tor";
this->localTor = true;
return;
#endif
bool unpacked = this->unpackBins();
if (!unpacked) {
qCritical() << "Error unpacking embedded Tor. Assuming --use-local-tor";
qCritical() << "Feather built without embedded Tor. Assuming --use-local-tor";
this->localTor = true;
return;
}
@ -181,9 +175,11 @@ void Tor::handleProcessError(QProcess::ProcessError error) {
bool Tor::unpackBins() {
QString torFile;
// On MacOS write libevent to disk
#if defined(Q_OS_MAC)
QString libEvent = ":/assets/exec/libevent-2.1.7.dylib";
// @TODO: refactor for Mac OS - should compile Tor statically.
#if defined(Q_OS_MAC) && defined(DRONE)
// Tor on Mac requires libevent.dylib, borrowed the executable from
// the official Tor Browser release for now.
QString libEvent = ":/tor/libevent-2.1.7.dylib";
if (Utils::fileExists(libEvent)) {
QFile e(libEvent);
QFileInfo eventInfo(e);
@ -194,19 +190,18 @@ bool Tor::unpackBins() {
}
#endif
torFile = ":/assets/exec/tor";
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
torFile = ":/tor/tor";
#elif defined(Q_OS_WIN)
torFile = ":/tor/tor.exe";
#endif
if (!Utils::fileExists(torFile))
return false;
// write to disk
QFile f(torFile);
QFileInfo fileInfo(f);
this->torPath = QDir(this->torDir).filePath(fileInfo.fileName());
#if defined(Q_OS_WIN)
if(!this->torPath.endsWith(".exe"))
this->torPath += ".exe";
#endif
qDebug() << "Writing Tor executable to " << this->torPath;
qDebug() << this->torPath;
f.copy(torPath);
f.close();
@ -214,6 +209,7 @@ bool Tor::unpackBins() {
QFile torBin(this->torPath);
torBin.setPermissions(QFile::ExeGroup | QFile::ExeOther | QFile::ExeOther | QFile::ExeUser);
#endif
return true;
}

@ -68,7 +68,7 @@ QByteArray Utils::fileOpenQRC(const QString &path) {
bool Utils::fileWrite(const QString &path, const QString &data) {
QFile file(path);
if(file.open(QIODevice::WriteOnly)){
QTextStream out(&file); out << data << Qt::endl;
QTextStream out(&file); out << data << endl;
file.close();
return true;
}
@ -334,15 +334,15 @@ int Utils::showMessageBox(const QString &windowTitle, const QString &body, bool
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
if(warning) {
QPixmap iconWarning = QPixmap(":/assets/images/ghost_icon.png");
QPixmap iconWarning = QPixmap(":/assets/images/ghost.png")
.scaled(QSize(48,48), Qt::KeepAspectRatio, Qt::SmoothTransformation);
QPixmap iconInfo = QPixmap(":/assets/images/info.png")
.scaled(QSize(48,48), Qt::KeepAspectRatio, Qt::SmoothTransformation);
if(warning)
msgBox.setIconPixmap(iconWarning);
}
else {
QPixmap iconInfo = QPixmap(":/assets/images/info.png")
.scaled(QSize(48,48), Qt::KeepAspectRatio, Qt::SmoothTransformation);
else
msgBox.setIconPixmap(iconInfo);
}
return msgBox.exec();
}
@ -362,15 +362,6 @@ void Utils::copyToClipboard(const QString &string){
#endif
}
QString Utils::copyFromClipboard() {
QClipboard * clipboard = QApplication::clipboard();
if (!clipboard) {
qWarning() << "Unable to access clipboard";
return "";
}
return clipboard->text();
}
QString Utils::blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid) {
if (blockExplorer == "exploremonero.com") {
if (nettype == NetworkType::MAINNET) {
@ -382,11 +373,6 @@ QString Utils::blockExplorerLink(const QString &blockExplorer, NetworkType::Type
return QString("https://moneroblocks.info/tx/%1").arg(txid);
}
}
else if (blockExplorer == "blockchair.com") {
if (nettype == NetworkType::MAINNET) {
return QString("https://blockchair.com/monero/transaction/%1").arg(txid);
}
}
switch (nettype) {
case NetworkType::MAINNET:

@ -85,7 +85,6 @@ public:
static QStandardItem *qStandardItem(const QString &text);
static QStandardItem *qStandardItem(const QString &text, QFont &font);
static void copyToClipboard(const QString &string);
static QString copyFromClipboard();
static QString blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid);
static QString getUnixAccountName();
static QString xdgDesktopEntry();

@ -1,138 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include <QtCore>
#include <QScreen>
#include <QDesktopWidget>
#include <QProcess>
#include <QDesktopServices>
#include "utils/utils.h"
#include "utils/xmrig.h"
#include "appcontext.h"
XmRig::XmRig(const QString &configDir, QObject *parent) : QObject(parent) {
this->rigDir = QDir(configDir).filePath("xmrig");
}
void XmRig::prepare() {
// unpack and set process signals
if(!this->unpackBins()) {
qCritical() << "failed to write XMRig to config directory";
return;
}
m_process.setProcessChannelMode(QProcess::MergedChannels);
connect(&m_process, &QProcess::readyReadStandardOutput, this, &XmRig::handleProcessOutput);
connect(&m_process, &QProcess::errorOccurred, this, &XmRig::handleProcessError);
connect(&m_process, &QProcess::stateChanged, this, &XmRig::stateChanged);
}
void XmRig::stop() {
if(m_process.state() == QProcess::Running) {
#if defined(Q_OS_WIN)
m_process.kill(); // https://doc.qt.io/qt-5/qprocess.html#terminate
#else
m_process.terminate();
#endif
}
}
void XmRig::start(const QString &path,
unsigned int threads,
const QString &address,
const QString &username,
const QString &password,
bool tor, bool tls) {
auto state = m_process.state();
if (state == QProcess::ProcessState::Running || state == QProcess::ProcessState::Starting) {
emit error("Can't start XMRig, already running or starting");
return;
}
if(path.isEmpty()) {
emit error("XmRig->Start path parameter missing.");
return;
}
if(!Utils::fileExists(path)) {
emit error(QString("Path to XMRig binary invalid; file does not exist: %1").arg(path));
return;
}
QStringList arguments;
arguments << "-o" << address;
arguments << "-a" << "rx/0";
arguments << "-u" << username;
if(!password.isEmpty())
arguments << "-p" << password;
arguments << "--no-color";
arguments << "-t" << QString::number(threads);
if(tor)
arguments << "-x" << QString("%1:%2").arg(Tor::torHost).arg(Tor::torPort);
if(tls)
arguments << "--tls";
arguments << "--donate-level" << "1";
QString cmd = QString("%1 %2").arg(path, arguments.join(" "));
emit output(cmd.toUtf8());
m_process.start(path, arguments);
}
void XmRig::stateChanged(QProcess::ProcessState state) {
if(state == QProcess::ProcessState::Running)
emit output("XMRig started");
else if (state == QProcess::ProcessState::NotRunning)
emit output("XMRig stopped");
}
void XmRig::handleProcessOutput() {
QByteArray _output = m_process.readAllStandardOutput();
if(_output.contains("miner") && _output.contains("speed")) {
// detect hashrate
auto str = Utils::barrayToString(_output);
auto spl = str.mid(str.indexOf("speed")).split(" ");
auto rate = spl.at(2) + "H/s";
qDebug() << "mining hashrate: " << rate;
emit hashrate(rate);
}
emit output(_output);
}
void XmRig::handleProcessError(QProcess::ProcessError err) {
if (err == QProcess::ProcessError::Crashed)
emit error("XMRig crashed or killed");
else if (err == QProcess::ProcessError::FailedToStart) {
auto path = config()->get(Config::xmrigPath).toString();
emit error(QString("XMRig binary failed to start: %1").arg(path));
}
}
bool XmRig::unpackBins() {
QString rigFile;
rigFile = ":/assets/exec/xmrig";
if (!Utils::fileExists(rigFile))
return false;
// write to disk
QFile f(rigFile);
QFileInfo fileInfo(f);
this->rigPath = QDir(this->rigDir).filePath(fileInfo.fileName());
#if defined(Q_OS_WIN)
if(!this->rigPath.endsWith(".exe"))
this->rigPath += ".exe";
#endif
qDebug() << "Writing XMRig executable to " << this->rigPath;
f.copy(rigPath);
f.close();
#if defined(Q_OS_UNIX)
QFile rigBin(this->rigPath);
rigBin.setPermissions(QFile::ExeGroup | QFile::ExeOther | QFile::ExeOther | QFile::ExeUser);
#endif
return true;
}

@ -1,47 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_XMRIG_H
#define FEATHER_XMRIG_H
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <QtCore>
#include <QRegExp>
#include <QtNetwork>
#include <QApplication>
#include <QMainWindow>
#include "utils/childproc.h"
class XmRig : public QObject
{
Q_OBJECT
public:
explicit XmRig(const QString &configDir, QObject *parent = nullptr);
void prepare();
void start(const QString &path, unsigned int threads, const QString &address, const QString &username, const QString &password, bool tor = false, bool tls = true);
void stop();
bool unpackBins();
QString rigDir;
QString rigPath;
signals:
void error(const QString &msg);
void output(const QByteArray &data);
void hashrate(const QString &rate);
private slots:
void stateChanged(QProcess::ProcessState);
void handleProcessOutput();
void handleProcessError(QProcess::ProcessError error);
private:
ChildProcess m_process;
};
#endif //FEATHER_XMRIG_H

@ -41,6 +41,7 @@ void XmrToApi::getOrderStatus(const QString &uuid) {
void XmrToApi::onResponse(QNetworkReply *reply, Endpoint endpoint) {
const auto ok = reply->error() == QNetworkReply::NoError;
const auto err = reply->errorString();
reply->deleteLater();
QByteArray data = reply->readAll();
QJsonObject obj;
@ -63,7 +64,6 @@ void XmrToApi::onResponse(QNetworkReply *reply, Endpoint endpoint) {
return;
}
reply->deleteLater();
emit ApiResponse(XmrToResponse(true, endpoint, "", obj));
}

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

Loading…
Cancel
Save