Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d41d526da0 | ||
|
|
33a85e1bd8 | ||
|
|
fde578aac9 | ||
|
|
925ef56830 | ||
|
|
585c850dd5 | ||
|
|
72f33da508 | ||
|
|
480ecfd047 | ||
|
|
1a6024c697 | ||
|
|
a11dd576e4 | ||
|
|
9fed3eb171 | ||
|
|
20156a7440 | ||
|
|
ca5b1df7df | ||
|
|
474ee2100c | ||
|
|
1ccedea1dd | ||
|
|
70dd25c477 | ||
|
|
bce983a6a5 | ||
|
|
eb8d150a9d | ||
|
|
841739cee9 | ||
|
|
d4dbc748c8 | ||
|
|
5ce6c8933d | ||
|
|
f61355c409 | ||
|
|
edad1928ab | ||
|
|
4201167477 | ||
|
|
16414ba2e9 | ||
|
|
1d001322cc | ||
|
|
9d781d1b57 | ||
|
|
3453e5ef34 | ||
|
|
76da957b81 | ||
|
|
35438792a0 | ||
|
|
01966df866 | ||
|
|
e2eb32ce78 | ||
|
|
03f484c2ae | ||
|
|
c44ee73ed0 | ||
|
|
e6e669845c | ||
|
|
5a08bc353e | ||
|
|
c8bc66a287 | ||
|
|
3beb470bbd | ||
|
|
b875ee362e | ||
|
|
4e3a977995 | ||
|
|
eb801ed9d4 | ||
|
|
d9dfef021f | ||
|
|
d2d3978de6 | ||
|
|
02f7249e6e | ||
|
|
a3309cea91 | ||
|
|
137ef6da72 | ||
|
|
4f56147319 | ||
|
|
d58259cefe | ||
|
|
9be79bba48 | ||
|
|
64db32dca9 | ||
|
|
774b4ed15f | ||
|
|
d337653e53 | ||
|
|
b06b7fca2a | ||
|
|
169720002c | ||
|
|
d7135393cd | ||
|
|
1e4a442b16 | ||
|
|
519df3f5a1 | ||
|
|
2a03ca9eda | ||
|
|
ff5dff26bb | ||
|
|
cbd29e6290 | ||
|
|
0f3c005b60 | ||
|
|
39c1d3bbf7 | ||
|
|
b66aceccc8 | ||
|
|
e9a4a828d6 | ||
|
|
bd60e30c3f | ||
|
|
046b2cfc4c | ||
|
|
24133ac390 | ||
|
|
8aecf79647 | ||
|
|
780a35fabe | ||
|
|
0e7c773bfd | ||
|
|
11f8406be3 | ||
|
|
2c99454565 | ||
|
|
cd96abe545 | ||
|
|
d76b688ec4 | ||
|
|
ed47e8e05c | ||
|
|
268b87a803 | ||
|
|
318690681b | ||
|
|
b1e37e714d | ||
|
|
18b514351d | ||
|
|
63c120bdb0 | ||
|
|
96409ecc17 | ||
|
|
c1cba44cba | ||
|
|
7a1ac42b1e | ||
|
|
87fd142ffd | ||
|
|
62ad0d9527 | ||
|
|
4f9e4ac93d | ||
|
|
cb6f2873a9 | ||
|
|
20b741d30d | ||
|
|
7ed2ff15d5 | ||
|
|
da4b2d16ef | ||
|
|
4521223413 | ||
|
|
0dd81c4873 | ||
|
|
df2ff26c0f | ||
|
|
e657e2a6b0 | ||
|
|
34a63897d6 | ||
|
|
6279591d1b | ||
|
|
403b8140b3 | ||
|
|
71ba9f6d4a | ||
|
|
3d5dae1f42 | ||
|
|
78eabc5ae7 | ||
|
|
485fb89941 | ||
|
|
df280cdd91 | ||
|
|
9b96ece2f9 | ||
|
|
a44087b750 | ||
|
|
70cbf59ce8 | ||
|
|
4aeaf2660f | ||
|
|
9b7849c5be | ||
|
|
51a0f86652 | ||
|
|
fa6ee35fb0 | ||
|
|
b1160280fe | ||
|
|
7441d6cb4a | ||
|
|
23000c6e60 | ||
|
|
75edc6e5bb | ||
|
|
06f649cbb7 | ||
|
|
ff78b5a6c2 | ||
|
|
47d965c6cd | ||
|
|
d6c26d4159 | ||
|
|
78516f4546 | ||
|
|
49f072eea4 | ||
|
|
302c258e6b | ||
|
|
70f1846de6 | ||
|
|
ae2815c1c6 | ||
|
|
24993bc0b2 | ||
|
|
216b2e0c5e | ||
|
|
44a2fc30dc | ||
|
|
aee60f33b7 | ||
|
|
5d8700370b | ||
|
|
1ef29207d5 | ||
|
|
497567e61f | ||
|
|
15c51e2e2b | ||
|
|
c5a055eb70 | ||
|
|
1b9cc7c679 | ||
|
|
0f0b033fe4 | ||
|
|
3feda6d6c7 | ||
|
|
103965ef40 | ||
|
|
3ee5e003a7 | ||
|
|
96ac717ede | ||
|
|
d0260ce018 | ||
|
|
61a135e1c2 | ||
|
|
98db512164 | ||
|
|
a325883b62 | ||
|
|
5c7c13d76a | ||
|
|
06cee3f03e | ||
|
|
ec0d88cf1b | ||
|
|
eca37f934c | ||
|
|
18cf749ad9 | ||
|
|
1febbc869f | ||
|
|
ea49097ab3 | ||
|
|
1dcc69db39 | ||
|
|
fc3ac2eb33 | ||
|
|
0289aba113 | ||
|
|
15f076f7d6 | ||
|
|
63e2ba799d | ||
|
|
1ff8a2aaf4 | ||
|
|
980340546a | ||
|
|
ebde5a1cb7 | ||
|
|
ffa8c3e5bf | ||
|
|
5a8641660c | ||
|
|
36641d73a6 | ||
|
|
eac29cd0b6 | ||
|
|
e8a5f59b4c | ||
|
|
7526714b4a | ||
|
|
2ab78022f5 | ||
|
|
363bf0ea38 | ||
|
|
d465639d2a | ||
|
|
0790b30b5d |
@@ -14,11 +14,9 @@ steps:
|
||||
commands:
|
||||
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
|
||||
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
|
||||
- git submodule update --init --depth 50 contrib/tor
|
||||
- git submodule update --init --depth 50 contrib/torsocks
|
||||
- git submodule update --init --depth 120 monero
|
||||
- git submodule update --init --depth 120 --recursive monero
|
||||
- make -j8 release-static
|
||||
- TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make -j6 release-static
|
||||
environment:
|
||||
OPENSSL_ROOT_DIR: /usr/local/openssl/
|
||||
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
|
||||
@@ -30,7 +28,7 @@ steps:
|
||||
- name: files_linux_release
|
||||
path: /files
|
||||
commands:
|
||||
- export FN="feather-`git rev-parse --short HEAD`.zip"
|
||||
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
|
||||
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
|
||||
- mkdir -p "$TARGET_DIR"
|
||||
- echo "writing to $TARGET_DIR/$FN"
|
||||
@@ -44,7 +42,7 @@ volumes:
|
||||
path: /var/drone/ccache_linux_release/
|
||||
- name: files_linux_release
|
||||
host:
|
||||
path: /mnt/storage1/feather_files/files/linux-release/
|
||||
path: /build/feather_files/files/linux-release/
|
||||
|
||||
---
|
||||
|
||||
@@ -56,7 +54,7 @@ steps:
|
||||
- name: build
|
||||
image: feather:appimage
|
||||
commands:
|
||||
- export FN="feather-`git rev-parse --short HEAD`.zip"
|
||||
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
|
||||
- export BRANCH="$DRONE_SOURCE_BRANCH"
|
||||
- bash ./contrib/build-appimage.sh
|
||||
- name: deploy
|
||||
@@ -75,7 +73,7 @@ steps:
|
||||
volumes:
|
||||
- name: files_linux_appimage
|
||||
host:
|
||||
path: /mnt/storage1/feather_files/files/linux-release-appimage/
|
||||
path: /build/feather_files/files/linux-release-appimage/
|
||||
|
||||
---
|
||||
|
||||
@@ -94,11 +92,9 @@ steps:
|
||||
commands:
|
||||
- git config --global url."http://gitea:3000/tor/".insteadOf https://git.torproject.org/
|
||||
- git config --global url."http://gitea:3000/".insteadOf https://github.com/
|
||||
- git submodule update --init --depth 50 contrib/tor
|
||||
- git submodule update --init --depth 50 contrib/torsocks
|
||||
- git submodule update --init --depth 120 monero
|
||||
- git submodule update --init --depth 120 --recursive monero
|
||||
- PATH=/mxe/usr/bin/:$PATH make -j8 windows-mxe-release
|
||||
- PATH="/mxe/usr/bin/:$PATH" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make -j6 windows-mxe-release
|
||||
environment:
|
||||
CMAKEFLAGS_EXTRA: -DFETCH_DEPS=Off
|
||||
- name: deploy
|
||||
@@ -109,7 +105,7 @@ steps:
|
||||
- name: files_win_release
|
||||
path: /files
|
||||
commands:
|
||||
- export FN="feather-`git rev-parse --short HEAD`.zip"
|
||||
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
|
||||
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
|
||||
- mkdir -p "$TARGET_DIR"
|
||||
- echo "writing to $TARGET_DIR/$FN"
|
||||
@@ -122,7 +118,7 @@ volumes:
|
||||
path: /var/drone/ccache_win_release/
|
||||
- name: files_win_release
|
||||
host:
|
||||
path: /mnt/storage1/feather_files/files/windows-mxe-release/
|
||||
path: /build/feather_files/files/windows-mxe-release/
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +143,7 @@ steps:
|
||||
- name: files_mac_release
|
||||
path: /files
|
||||
commands:
|
||||
- export FN="feather-`git rev-parse --short HEAD`.zip"
|
||||
- export FN="feather-`echo $DRONE_COMMIT_AFTER | cut -c 1-7`.zip"
|
||||
- export TARGET_DIR="/files/$DRONE_SOURCE_BRANCH"
|
||||
- mkdir -p "$TARGET_DIR"
|
||||
- echo "writing to $TARGET_DIR/$FN"
|
||||
@@ -157,9 +153,9 @@ steps:
|
||||
volumes:
|
||||
- name: files_mac_release
|
||||
host:
|
||||
path: /mnt/storage1/feather_files/files/mac-release/
|
||||
path: /build/feather_files/files/mac-release/
|
||||
---
|
||||
kind: signature
|
||||
hmac: 91e773a27d27f29ea62f5df500664b733df641b7c31202c8e5558174fd046fba
|
||||
hmac: 527d334190a8a824b3b781a05ae4c7d87f4fa2bc37ebc53a96db91f925fa4a52
|
||||
|
||||
...
|
||||
|
||||
@@ -7,3 +7,6 @@
|
||||
[submodule "contrib/tor"]
|
||||
path = contrib/tor
|
||||
url = https://git.torproject.org/tor.git
|
||||
[submodule "contrib/KDMacTouchBar"]
|
||||
path = contrib/KDMacTouchBar
|
||||
url = https://github.com/KDAB/KDMacTouchBar.git
|
||||
|
||||
@@ -35,7 +35,7 @@ Note: You only need to build the base image once.
|
||||
#### 3. Build
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v /tmp/ccache:/root/.ccache -v /root/feather:/feather -w /feather feather:win /bin/bash -c 'PATH=/mxe/usr/bin/:$PATH make windows-mxe-release -j8'
|
||||
docker run --rm -it -v /tmp/ccache:/root/.ccache -v /root/feather:/feather -w /feather feather:win /bin/bash -c 'PATH="/mxe/usr/bin/:$PATH" TOR="/mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe" XMRIG="/xmrig/xmrig.exe" make windows-mxe-release -j8'
|
||||
```
|
||||
|
||||
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
|
||||
@@ -68,7 +68,7 @@ Note: You only need to build the base image once.
|
||||
#### 3. Build
|
||||
|
||||
```bash
|
||||
docker run --env OPENSSL_ROOT_DIR=/usr/local/openssl/ --rm -it -v /tmp/ccache:/root/.ccache -v PATH_TO_FEATHER:/feather -w /feather feather:linux sh -c 'make release-static -j8'
|
||||
docker run --env OPENSSL_ROOT_DIR=/usr/local/openssl/ --rm -it -v /tmp/ccache:/root/.ccache -v PATH_TO_FEATHER:/feather -w /feather feather:linux sh -c 'TOR="/usr/local/tor/bin/tor" XMRIG="/xmrig/xmrig" make release-static -j8'
|
||||
```
|
||||
|
||||
Replace `PATH_TO_FEATHER` with the absolute path to Feather locally.
|
||||
@@ -98,4 +98,4 @@ Build Feather.
|
||||
CMAKE_PREFIX_PATH=~/Qt5.15.1/5.15.1/clang_64 make mac-release
|
||||
```
|
||||
|
||||
The resulting Mac OS application can be found `build/bin/feather.app`.
|
||||
The resulting Mac OS application can be found `build/bin/feather.app` and will **not** have Tor embedded.
|
||||
@@ -11,7 +11,10 @@ set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||
|
||||
option(FETCH_DEPS "Download dependencies if they are not found" ON)
|
||||
option(XMRTO "Include Xmr.To module" ON)
|
||||
option(BUILD_TOR "Build Tor" OFF)
|
||||
option(MORPHTOKEN "Include MorphToken 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(STATIC "Link libraries statically, requires static Qt")
|
||||
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
|
||||
option(DONATE_BEG "Prompt donation window every once in a while" ON)
|
||||
@@ -28,7 +31,7 @@ if(DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
endif()
|
||||
|
||||
set(MONERO_HEAD "a1404e92cb439ba0f120e7c4a579ed0b9a0372a4")
|
||||
set(MONERO_HEAD "2c41634c13994a8a112a1e3a61e5e0b456791a97")
|
||||
set(BUILD_GUI_DEPS ON)
|
||||
set(ARCH "x86-64")
|
||||
set(BUILD_64 ON)
|
||||
@@ -158,6 +161,7 @@ find_package(Boost 1.58 REQUIRED COMPONENTS
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
# https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446
|
||||
set(CMAKE_SKIP_RPATH ON)
|
||||
endif()
|
||||
|
||||
@@ -173,47 +177,48 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Tor/torsocks
|
||||
set(TOR_TAG "tor-0.4.3.5")
|
||||
set(TOR_DIR "${CMAKE_SOURCE_DIR}/contrib/tor")
|
||||
|
||||
if(BUILD_TOR AND APPLE)
|
||||
execute_process(COMMAND bash -c "touch ${CMAKE_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
|
||||
ENDIF()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(TOR_LIB "libtorsocks.so")
|
||||
elseif(APPLE)
|
||||
set(TOR_LIB "libtorsocks.dylib")
|
||||
if("$ENV{DRONE}" STREQUAL "true")
|
||||
message(STATUS "We are inside a static compile with Drone CI")
|
||||
endif()
|
||||
|
||||
if("$ENV{DRONE}" STREQUAL "true" AND APPLE)
|
||||
message(STATUS "We are inside a static compile with Drone CI")
|
||||
# @TODO: taken from Tor Browser official release for now
|
||||
execute_process(COMMAND bash -c "cp ~/tor/libevent-2.1.7.dylib ${CMAKE_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
|
||||
execute_process(COMMAND bash -c "cp ~/tor/tor ${CMAKE_SOURCE_DIR}/src/tor/tor")
|
||||
elseif("$ENV{DRONE}" STREQUAL "true" AND BUILD_TOR)
|
||||
message(STATUS "We are inside a static compile with Drone CI")
|
||||
if(MINGW)
|
||||
execute_process(COMMAND bash -c "cp /mxe/usr/x86_64-w64-mingw32.static/bin/tor.exe ${CMAKE_SOURCE_DIR}/src/tor/tor.exe")
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
execute_process(COMMAND bash -c "cp /usr/local/tor/bin/tor ${CMAKE_SOURCE_DIR}/src/tor/tor")
|
||||
execute_process(COMMAND bash -c "cp /usr/local/torsocks/lib/torsocks/* ${CMAKE_SOURCE_DIR}/src/tor/")
|
||||
# To build Feather with embedded (and static) 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()
|
||||
|
||||
message(STATUS "Embedding Tor binary at ${TOR}")
|
||||
else()
|
||||
if(BUILD_TOR)
|
||||
if(UNIX OR APPLE)
|
||||
execute_process(COMMAND bash -c "ls -al src/tor/${TOR_LIB} 2>/dev/null" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE TOR_FOUND OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(TOR_FOUND)
|
||||
message(STATUS "${TOR_LIB} found, skipping Tor build")
|
||||
else()
|
||||
message(STATUS "${TOR_LIB} not found, building Tor")
|
||||
execute_process(COMMAND bash -c "bash build_tor.sh ${TOR_TAG} ${CMAKE_SOURCE_DIR} 'ON'" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/contrib)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Skipping Tor build because -DBUILD_TOR=OFF")
|
||||
message(STATUS "Skipping Tor inclusion because -DTOR=Off")
|
||||
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}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Embedding XMRig binary at ${XMRIG}")
|
||||
else()
|
||||
message(STATUS "Skipping XMRig inclusion because -DXMRIG=Off")
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
@@ -361,4 +366,8 @@ 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 qtmultimedia \
|
||||
-skip qtdeclarative \
|
||||
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
|
||||
-nomake examples -nomake tests -nomake tools
|
||||
|
||||
@@ -305,3 +305,9 @@ RUN git clone https://git.wownero.com/feather/monero-seed.git && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
|
||||
make -Cbuild -j$THREADS && \
|
||||
make -Cbuild install
|
||||
|
||||
RUN apt install -y curl && \
|
||||
curl -LO "https://github.com/xmrig/xmrig/releases/download/v6.3.5/xmrig-6.3.5-linux-static-x64.tar.gz" && \
|
||||
echo "24d4f07cf5850f00ab513b228f95769a5a5ed68d35808d98f9959b58d97985a0 xmrig-6.3.5-linux-static-x64.tar.gz" > hashsum.txt && \
|
||||
sha256sum -c hashsum.txt && \
|
||||
tar xvf xmrig-6.3.5-linux-static-x64.tar.gz --one-top-level=/xmrig --strip 1
|
||||
|
||||
@@ -50,7 +50,7 @@ RUN apt install -y \
|
||||
|
||||
RUN git clone -b feather-patch --depth 1 https://git.wownero.com/feather/mxe.git && \
|
||||
cd mxe && \
|
||||
make -j$THREADS MXE_TARGETS='x86_64-w64-mingw32.static' gcc libqrencode pkgconf libgpg_error libgcrypt cmake libsodium lzma readline libzmq boost qtbase qtsvg qtwebsockets qtimageformats
|
||||
make -j$THREADS MXE_TARGETS='x86_64-w64-mingw32.static' gcc libqrencode pkgconf libgpg_error libgcrypt cmake libsodium lzma readline libzmq boost qtbase qtsvg qtwebsockets qtimageformats qtmultimedia
|
||||
|
||||
# plugins
|
||||
RUN cd mxe && make -j$THREADS MXE_PLUGIN_DIRS='/mxe/plugins/apps/' MXE_TARGETS='x86_64-w64-mingw32.static' tor
|
||||
@@ -72,3 +72,10 @@ RUN git clone https://git.wownero.com/feather/monero-seed.git && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
|
||||
make -Cbuild -j$THREADS && \
|
||||
make -Cbuild install
|
||||
|
||||
RUN apt install -y curl && \
|
||||
curl -LO "https://github.com/xmrig/xmrig/releases/download/v6.3.5/xmrig-6.3.5-gcc-win64.zip" && \
|
||||
echo "e45915ada7e6e30f6ab40abf33831056449d5914307d7706bb0ad439b6d64c12 xmrig-6.3.5-gcc-win64.zip" > hashsum.txt && \
|
||||
sha256sum -c hashsum.txt && \
|
||||
unzip -q xmrig-6.3.5-gcc-win64.zip -d /xmrig && \
|
||||
mv /xmrig/xmrig-6.3.5/* /xmrig/
|
||||
|
||||
@@ -42,7 +42,9 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
|
||||
There are some Monero/Feather related options/definitions that you may pass:
|
||||
|
||||
- `-DXMRTO=OFF` - disable Xmr.To feature
|
||||
- `-DBUILD_TOR=OFF` - disable embedded Tor
|
||||
- `-DMORPHTOKEN=OFF` - diable MorphToken feature
|
||||
- `-DTOR=/path/to/tor` - Embed a Tor executable inside Feather
|
||||
- `-DXMRIG=/path/to/xmrig` - Embed a XMRig executable inside Feather
|
||||
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
|
||||
|
||||
And:
|
||||
|
||||
@@ -31,6 +31,9 @@ CMAKEFLAGS = \
|
||||
-DBUILD_64=On \
|
||||
-DBUILD_TESTS=Off \
|
||||
-DXMRTO=ON \
|
||||
-DMORPHTOKEN=ON \
|
||||
-DXMRIG=Off \
|
||||
-DTOR=Off \
|
||||
-DCMAKE_CXX_STANDARD=11 \
|
||||
-DCMAKE_VERBOSE_MAKEFILE=On \
|
||||
-DINSTALL_VENDORED_LIBUNBOUND=Off \
|
||||
@@ -40,29 +43,33 @@ CMAKEFLAGS = \
|
||||
$(CMAKEFLAGS_EXTRA)
|
||||
|
||||
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
|
||||
release-static: CMAKEFLAGS += -DBUILD_TOR=On
|
||||
release-static: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||
release-static: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||
release-static:
|
||||
cmake -Bbuild $(CMAKEFLAGS)
|
||||
$(MAKE) -Cbuild
|
||||
|
||||
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
||||
windows-mxe-release: CMAKEFLAGS += -DBUILD_TOR=On
|
||||
windows-mxe-release: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||
windows-mxe-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||
windows-mxe-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||
windows-mxe-release:
|
||||
cmake -Bbuild $(CMAKEFLAGS)
|
||||
$(MAKE) -Cbuild
|
||||
|
||||
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TAG="win-x64"
|
||||
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TOR=Off
|
||||
windows-mxe-debug: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||
windows-mxe-debug: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||
windows-mxe-debug: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Debug
|
||||
windows-mxe-debug:
|
||||
cmake -Bbuild $(CMAKEFLAGS)
|
||||
$(MAKE) -Cbuild
|
||||
|
||||
mac-release: CMAKEFLAGS += -DSTATIC=Off
|
||||
mac-release: CMAKEFLAGS += -DTOR=$(or ${TOR},OFF)
|
||||
mac-release: CMAKEFLAGS += -DXMRIG=$(or ${XMRIG},OFF)
|
||||
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
|
||||
mac-release: CMAKEFLAGS += -DBUILD_TOR=Off
|
||||
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||
mac-release:
|
||||
cmake -Bbuild $(CMAKEFLAGS)
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
# Maintainer: wowario <wowario[at]protonmail[dot]com>
|
||||
# Contributor: wowario <wowario[at]protonmail[dot]com>
|
||||
|
||||
pkgbase=('monero-feather-git')
|
||||
pkgname=('monero-feather-git')
|
||||
pkgver=v0.1.0.0.cd1cd5cb75
|
||||
pkgname='monero-feather-git'
|
||||
pkgver=0.1.0.925ef5683
|
||||
pkgrel=1
|
||||
pkgdesc="a free Monero desktop wallet"
|
||||
pkgdesc='a free Monero desktop wallet'
|
||||
license=('BSD')
|
||||
arch=('x86_64')
|
||||
url="https://featherwallet.org"
|
||||
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'zeromq' 'pcsclite' 'hidapi' 'protobuf' 'miniupnpc'
|
||||
'libgcrypt' 'qrencode' 'ccache' 'libsodium' 'libpgm' 'expat' 'base-devel' 'qt5-base')
|
||||
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'pcsclite' 'hidapi' 'protobuf' 'miniupnpc' 'libgcrypt' 'qrencode' 'libsodium' 'libpgm' 'expat' 'qt5-base' 'qt5-websockets' 'tor')
|
||||
makedepends=('git' 'cmake' 'boost')
|
||||
provides=('monero-feather-git')
|
||||
|
||||
source=("${pkgname}"::"git+https://git.wownero.com/feather/feather")
|
||||
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
cd "$srcdir/$pkgname"
|
||||
git describe --long --tags | sed 's/\([^-]*-\)g/r\1/;s/-/./g'
|
||||
cd "${srcdir}/${pkgname}"
|
||||
printf "%s.%s" "$(git describe --tags --abbrev=0)" "$(git rev-parse --short=9 HEAD)"
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}"
|
||||
mkdir build && cd build && cmake .. && make release-static -j2
|
||||
git submodule update --init --recursive
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
}
|
||||
|
||||
package_feather-git() {
|
||||
package_monero-feather-git() {
|
||||
install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
install -Dm755 "${srcdir}/${pkgname}/build/bin/feather" "${pkgdir}/usr/bin/feather"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://build.featherwallet.org/feather/feather)
|
||||
|
||||
Feather is a free, open-source Monero client Linux with ports for Mac OS and Windows written in C++ with the Qt framework. It is created and maintained by [dsc](dsc@xmr.pm) and [tobtoht](thotbot@protonmail.com).
|
||||
Feather is a free, open-source Monero client Linux with ports for Mac OS and Windows written in C++ with the Qt framework. It is created and maintained by [dsc](dsc@xmr.pm) and [tobtoht](thotbot@protonmail.com).
|
||||
|
||||
## Development resources
|
||||
* Web: [featherwallet.org](https://featherwallet.org)
|
||||
|
||||
@@ -42,14 +42,16 @@ file(GLOB SOURCE_FILES
|
||||
"dialog/*.cpp"
|
||||
)
|
||||
|
||||
if((APPLE AND BUILD_TOR) OR (APPLE AND "$ENV{DRONE}" STREQUAL "true"))
|
||||
set(ASSETS_OS "assets_macos_tor.qrc")
|
||||
elseif(UNIX AND NOT APPLE AND BUILD_TOR)
|
||||
set(ASSETS_OS "assets_linux_tor.qrc")
|
||||
elseif(MINGW AND BUILD_TOR)
|
||||
set(ASSETS_OS "assets_windows_tor.qrc")
|
||||
else()
|
||||
message(STATUS "Building without embedded Tor")
|
||||
if(TOR)
|
||||
if(APPLE)
|
||||
set(ASSETS_TOR "assets_tor_macos.qrc")
|
||||
else()
|
||||
set(ASSETS_TOR "assets_tor.qrc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(XMRIG)
|
||||
set(ASSETS_XMRIG "assets_mining.qrc")
|
||||
endif()
|
||||
|
||||
set(EXECUTABLE_FLAG)
|
||||
@@ -74,7 +76,8 @@ endif()
|
||||
add_executable(feather ${EXECUTABLE_FLAG} main.cpp
|
||||
${SOURCE_FILES}
|
||||
${RESOURCES}
|
||||
${ASSETS_OS}
|
||||
${ASSETS_TOR}
|
||||
${ASSETS_XMRIG}
|
||||
)
|
||||
|
||||
# mac os bundle
|
||||
@@ -122,14 +125,22 @@ if(DONATE_BEG)
|
||||
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
|
||||
endif()
|
||||
|
||||
if(BUILD_TOR)
|
||||
target_compile_definitions(feather PRIVATE BUILD_TOR=1)
|
||||
endif()
|
||||
|
||||
if(XMRTO)
|
||||
target_compile_definitions(feather PRIVATE XMRTO=1)
|
||||
endif()
|
||||
|
||||
if(MORPHTOKEN)
|
||||
target_compile_definitions(feather PRIVATE HAS_MORPHTOKEN=1)
|
||||
endif()
|
||||
|
||||
if(TOR)
|
||||
target_compile_definitions(feather PRIVATE HAS_TOR=1)
|
||||
endif()
|
||||
|
||||
if(XMRIG)
|
||||
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
|
||||
endif()
|
||||
|
||||
if(HAVE_SYS_PRCTL_H)
|
||||
target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1)
|
||||
endif()
|
||||
@@ -194,6 +205,14 @@ 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
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "MorphTokenWidget.h"
|
||||
#include "ui_MorphTokenWidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "qrcode/QrCode.h"
|
||||
#include "dialog/qrcodedialog.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
MorphTokenWidget::MorphTokenWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::MorphTokenWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
||||
m_network = new UtilsNetworking(this->m_ctx->network);
|
||||
m_api = new MorphTokenApi(this, m_network);
|
||||
|
||||
connect(ui->btnCreateTrade, &QPushButton::clicked, this, &MorphTokenWidget::createTrade);
|
||||
connect(ui->btn_lookupTrade, &QPushButton::clicked, this, &MorphTokenWidget::lookupTrade);
|
||||
connect(ui->btn_getRates, &QPushButton::clicked, this, &MorphTokenWidget::getRates);
|
||||
|
||||
connect(m_api, &MorphTokenApi::ApiResponse, this, &MorphTokenWidget::onApiResponse);
|
||||
|
||||
connect(ui->combo_From, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
|
||||
this->displayRate();
|
||||
ui->label_refundAddress->setText(QString("Refund address (%1):").arg(ui->combo_From->currentText()));
|
||||
});
|
||||
connect(ui->combo_To, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index){
|
||||
this->displayRate();
|
||||
ui->label_destinationAddress->setText(QString("Destination address (%1):").arg(ui->combo_To->currentText()));
|
||||
});
|
||||
|
||||
connect(ui->check_autorefresh, &QCheckBox::toggled, [this](bool toggled){
|
||||
m_countdown = 30;
|
||||
toggled ? m_countdownTimer.start(1000) : m_countdownTimer.stop();
|
||||
ui->check_autorefresh->setText("Autorefresh");
|
||||
});
|
||||
connect(&m_countdownTimer, &QTimer::timeout, this, &MorphTokenWidget::onCountdown);
|
||||
|
||||
connect(ui->line_Id, &QLineEdit::textChanged, [this](const QString &text){
|
||||
ui->btn_lookupTrade->setEnabled(!text.isEmpty());
|
||||
ui->check_autorefresh->setEnabled(!text.isEmpty());
|
||||
});
|
||||
|
||||
// Default to BTC -> XMR
|
||||
ui->combo_From->setCurrentIndex(1);
|
||||
ui->combo_To->setCurrentIndex(0);
|
||||
|
||||
ui->label_rate->setVisible(false);
|
||||
m_ratesTimer.setSingleShot(true);
|
||||
connect(&m_ratesTimer, &QTimer::timeout, [this]{
|
||||
ui->label_rate->setVisible(false);
|
||||
});
|
||||
|
||||
ui->qrCode->setVisible(false);
|
||||
ui->label_depositAddress->setVisible(false);
|
||||
connect(ui->qrCode, &ClickableLabel::clicked, this, &MorphTokenWidget::showQrCodeDialog);
|
||||
|
||||
ui->tabWidget->setTabVisible(2, false);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::createTrade() {
|
||||
QString inputAsset = ui->combo_From->currentText();
|
||||
QString outputAsset = ui->combo_To->currentText();
|
||||
QString refundAddress = ui->line_refundAddress->text();
|
||||
QString destinationAddress = ui->line_destinationAddress->text();
|
||||
|
||||
m_api->createTrade(inputAsset, outputAsset, refundAddress, destinationAddress);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::lookupTrade() {
|
||||
QString morphId = ui->line_Id->text();
|
||||
|
||||
if (!morphId.isEmpty())
|
||||
m_api->getTrade(morphId);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::getRates() {
|
||||
m_api->getRates();
|
||||
}
|
||||
|
||||
void MorphTokenWidget::onApiResponse(const MorphTokenApi::MorphTokenResponse &resp) {
|
||||
if (!resp.ok) {
|
||||
ui->check_autorefresh->setChecked(false);
|
||||
QMessageBox::warning(this, "MorphToken error", QString("Request failed:\n\n%1").arg(resp.message));
|
||||
return;
|
||||
}
|
||||
|
||||
ui->debugInfo->setPlainText(QJsonDocument(resp.obj).toJson(QJsonDocument::Indented));
|
||||
|
||||
bool shouldShowQr = (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE);
|
||||
ui->qrCode->setVisible(shouldShowQr);
|
||||
ui->label_depositAddress->setVisible(shouldShowQr);
|
||||
|
||||
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE || resp.endpoint == MorphTokenApi::Endpoint::GET_TRADE) {
|
||||
ui->tabWidget->setCurrentIndex(1);
|
||||
ui->line_Id->setText(resp.obj.value("id").toString());
|
||||
|
||||
auto obj = resp.obj;
|
||||
auto input = obj["input"].toObject();
|
||||
auto output = obj["output"].toArray()[0].toObject();
|
||||
QString state = obj.value("state").toString();
|
||||
QString statusText;
|
||||
|
||||
ui->trade->setTitle(QString("Trade (%1)").arg(state));
|
||||
|
||||
statusText += QString("Morph ID: %1\n\n").arg(obj["id"].toString());
|
||||
|
||||
if (state == "PENDING") {
|
||||
statusText += QString("Waiting for a deposit, send %1 to %2\n").arg(input["asset"].toString(),
|
||||
input["deposit_address"].toString());
|
||||
statusText += QString("Rate: 1 %1 -> %2 %3\n\n").arg(input["asset"].toString(),
|
||||
output["seen_rate"].toString(),
|
||||
output["asset"].toString());
|
||||
statusText += "Limits:\n";
|
||||
statusText += QString(" Minimum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["min"].toDouble()),
|
||||
input["asset"].toString());
|
||||
statusText += QString(" Maximum amount accepted: %1 %2\n").arg(formatAmount(input["asset"].toString(), input["limits"].toObject()["max"].toDouble()),
|
||||
input["asset"].toString());
|
||||
statusText += QString("\nSend a single deposit. If the amount is outside the limits, a refund will happen.");
|
||||
|
||||
m_depositAddress = input["deposit_address"].toString();
|
||||
|
||||
const QrCode qrc(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::MEDIUM);
|
||||
int width = ui->qrCode->width();
|
||||
if (qrc.isValid()) {
|
||||
ui->qrCode->setPixmap(qrc.toPixmap(1).scaled(width, width, Qt::KeepAspectRatio));
|
||||
}
|
||||
} else if (state == "PROCESSING" || state == "TRADING" || state == "CONFIRMING") {
|
||||
if (state == "CONFIRMING") {
|
||||
statusText += QString("Waiting for confirmations\n");
|
||||
} else if (state == "TRADING") {
|
||||
statusText += QString("Your transaction has been received and is confirmed. MorphToken is now executing your trade.\n"
|
||||
"Usually this step takes no longer than a minute, "
|
||||
"but in rare cases it can take a couple hours.\n"
|
||||
"Wait a bit before contacting support.\n");
|
||||
}
|
||||
statusText += QString("Converting %1 to %2\n").arg(input["asset"].toString(), output["asset"].toString());
|
||||
statusText += QString("Sending to %1\n").arg(output["address"].toString());
|
||||
statusText += QString("Stuck? Contact support at contact@morphtoken.com");
|
||||
} else if (state == "COMPLETE") {
|
||||
if (output["txid"].toString().isEmpty()) {
|
||||
statusText += QString("MorphToken is sending your transaction.\n");
|
||||
statusText += QString("MorphToken will send %1 %2 to %2").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
|
||||
output["asset"].toString(),
|
||||
output["address"].toString());
|
||||
} else {
|
||||
statusText += QString("Sent %1 %2 to %3\ntxid: {}").arg(this->formatAmount(output["asset"].toString(), output["converted_amount"].toDouble() - output["network_fee"].toObject()["fee"].toDouble()),
|
||||
output["asset"].toString(),
|
||||
output["address"].toString(),
|
||||
output["txid"].toString());
|
||||
}
|
||||
} else if (state == "PROCESSING_REFUND" || state == "COMPLETE_WITH_REFUND") {
|
||||
statusText += QString("MorphToken will refund %1 %2\nReason: %3\n").arg(obj["final_amount"].toString(),
|
||||
obj["asset"].toString(),
|
||||
obj["reason"].toString());
|
||||
|
||||
if (obj.contains("txid")) {
|
||||
statusText += QString("txid: %1").arg(obj["txid"].toString());
|
||||
}
|
||||
} else if (state == "COMPLETE_WITHOUT_REFUND") {
|
||||
statusText += "Deposit amount below network fee, too small to refund.";
|
||||
}
|
||||
|
||||
ui->label_status->setText(statusText);
|
||||
} else if (resp.endpoint == MorphTokenApi::Endpoint::GET_RATES) {
|
||||
m_rates = resp.obj.value("data").toObject();
|
||||
this->displayRate();
|
||||
ui->label_rate->setVisible(true);
|
||||
m_ratesTimer.start(120 * 1000);
|
||||
}
|
||||
|
||||
if (resp.endpoint == MorphTokenApi::Endpoint::CREATE_TRADE) {
|
||||
QMessageBox::information(this, "MorphToken", "Trade created!\n\nMake sure to save your Morph ID. You may need it in case something goes wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
void MorphTokenWidget::onCountdown() {
|
||||
if (m_countdown > 0) {
|
||||
m_countdown -= 1;
|
||||
} else {
|
||||
this->lookupTrade();
|
||||
m_countdown = 30;
|
||||
}
|
||||
ui->check_autorefresh->setText(QString("Autorefresh (%1)").arg(m_countdown));
|
||||
}
|
||||
|
||||
void MorphTokenWidget::displayRate() {
|
||||
QString inputAsset = ui->combo_From->currentText();
|
||||
QString outputAsset = ui->combo_To->currentText();
|
||||
QString outputRate = m_rates.value(inputAsset).toObject().value(outputAsset).toString("1");
|
||||
|
||||
QString rateStr = QString("1 %1 -> %2 %3").arg(inputAsset, outputRate, outputAsset);
|
||||
ui->label_rate->setText(rateStr);
|
||||
}
|
||||
|
||||
void MorphTokenWidget::showQrCodeDialog() {
|
||||
QrCode qr(m_depositAddress, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
|
||||
auto *dialog = new QrCodeDialog(this, qr, "Deposit address");
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
QString MorphTokenWidget::formatAmount(const QString &asset, double amount) {
|
||||
double displayAmount;
|
||||
double div;
|
||||
|
||||
if (asset == "ETH")
|
||||
div = 1e18;
|
||||
else if (asset == "XMR")
|
||||
div = 1e12;
|
||||
else
|
||||
div = 1e8;
|
||||
|
||||
displayAmount = amount / div;
|
||||
|
||||
return QString::number(displayAmount, 'f', 8);
|
||||
}
|
||||
|
||||
MorphTokenWidget::~MorphTokenWidget() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_MORPHTOKENWIDGET_H
|
||||
#define FEATHER_MORPHTOKENWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "appcontext.h"
|
||||
#include "utils/MorphTokenApi.h"
|
||||
|
||||
namespace Ui {
|
||||
class MorphTokenWidget;
|
||||
}
|
||||
|
||||
class MorphTokenWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MorphTokenWidget(QWidget *parent = nullptr);
|
||||
~MorphTokenWidget() override;
|
||||
|
||||
private:
|
||||
void createTrade();
|
||||
void lookupTrade();
|
||||
void getRates();
|
||||
void onApiResponse(const MorphTokenApi::MorphTokenResponse &resp);
|
||||
|
||||
void onCountdown();
|
||||
void displayRate();
|
||||
void showQrCodeDialog();
|
||||
|
||||
QString formatAmount(const QString &asset, double amount);
|
||||
|
||||
Ui::MorphTokenWidget *ui;
|
||||
|
||||
AppContext *m_ctx;
|
||||
MorphTokenApi *m_api;
|
||||
UtilsNetworking *m_network;
|
||||
QTimer m_countdownTimer;
|
||||
int m_countdown = 30;
|
||||
QJsonObject m_rates;
|
||||
QTimer m_ratesTimer;
|
||||
QString m_depositAddress;
|
||||
};
|
||||
|
||||
#endif //FEATHER_MORPHTOKENWIDGET_H
|
||||
@@ -0,0 +1,423 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MorphTokenWidget</class>
|
||||
<widget class="QWidget" name="MorphTokenWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1036</width>
|
||||
<height>614</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabCreateTrade">
|
||||
<attribute name="title">
|
||||
<string>Create trade</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>From:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_From">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XMR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ETH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BCH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DASH</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>To:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_To">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XMR</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ETH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BCH</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LTC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DASH</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_rate">
|
||||
<property name="text">
|
||||
<string>rate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_refundAddress">
|
||||
<property name="text">
|
||||
<string>Refund address (XMR):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="line_refundAddress"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_destinationAddress">
|
||||
<property name="text">
|
||||
<string>Destination address (XMR):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="line_destinationAddress"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Powered by MorphToken.com</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_getRates">
|
||||
<property name="text">
|
||||
<string>Get Rates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnCreateTrade">
|
||||
<property name="text">
|
||||
<string>Create Trade</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabLookupTrade">
|
||||
<attribute name="title">
|
||||
<string>Lookup trade</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Morph ID or MorphToken deposit address:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_Id"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="check_autorefresh">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Autorefresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_lookupTrade">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lookup trade</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="trade">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Trade</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_status">
|
||||
<property name="text">
|
||||
<string>No trade loaded.</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="ClickableLabel" name="qrCode">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>qrcode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_depositAddress">
|
||||
<property name="text">
|
||||
<string>Deposit address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabDebug">
|
||||
<attribute name="title">
|
||||
<string>Debug</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="debugInfo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ClickableLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -9,12 +9,14 @@
|
||||
#include <QDesktopWidget>
|
||||
|
||||
#include "appcontext.h"
|
||||
#include "globals.h"
|
||||
#include "utils/tails.h"
|
||||
#include "utils/whonix.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/prices.h"
|
||||
#include "utils/networktype.h"
|
||||
#include "utils/wsclient.h"
|
||||
#include "utils/config.h"
|
||||
|
||||
// libwalletqt
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
@@ -28,7 +30,6 @@
|
||||
#include "model/SubaddressModel.h"
|
||||
#include "utils/keysfiles.h"
|
||||
#include "utils/networktype.h"
|
||||
#include "utils/config.h"
|
||||
|
||||
|
||||
Prices *AppContext::prices = nullptr;
|
||||
@@ -112,7 +113,6 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||
this->nodes = new Nodes(this, this->networkClearnet);
|
||||
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
|
||||
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
|
||||
connect(this, &AppContext::walletClosing, this->nodes, &Nodes::onWalletClosing);
|
||||
|
||||
// Tor & socks proxy
|
||||
this->ws = new WSClient(this, m_wsUrl);
|
||||
@@ -147,10 +147,16 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
|
||||
AppContext::prices = new Prices();
|
||||
|
||||
// xmr.to
|
||||
#if defined(XMRTO)
|
||||
#ifdef XMRTO
|
||||
this->XMRTo = new XmrTo(this);
|
||||
#endif
|
||||
|
||||
// XMRig
|
||||
#ifdef HAS_XMRIG
|
||||
this->XMRig = new XmRig(this->configDirectory, this);
|
||||
this->XMRig->prepare();
|
||||
#endif
|
||||
|
||||
this->walletManager = WalletManager::instance();
|
||||
QString logPath = QString("%1/daemon.log").arg(configDirectory);
|
||||
Monero::Utils::onStartup();
|
||||
@@ -173,7 +179,7 @@ void AppContext::initTor() {
|
||||
this->tor = new Tor(this, this);
|
||||
this->tor->start();
|
||||
|
||||
if (!(isTails || isWhonix)) {
|
||||
if (!(isWhonix)) {
|
||||
auto networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
|
||||
this->network->setProxy(*networkProxy);
|
||||
if (m_wsUrl.host().endsWith(".onion"))
|
||||
@@ -188,7 +194,7 @@ void AppContext::initWS() {
|
||||
|
||||
void AppContext::onCancelTransaction(PendingTransaction *tx, const QString &address) {
|
||||
// tx cancelled by user
|
||||
double amount = tx->amount() / AppContext::cdiv;
|
||||
double amount = tx->amount() / globals::cdiv;
|
||||
emit createTransactionCancelled(address, amount);
|
||||
this->currentWallet->disposeTransaction(tx);
|
||||
}
|
||||
@@ -227,8 +233,8 @@ void AppContext::onCreateTransaction(const QString &address, const double amount
|
||||
return;
|
||||
}
|
||||
|
||||
auto balance = this->currentWallet->balance() / AppContext::cdiv;
|
||||
auto unlocked_balance = this->currentWallet->unlockedBalance() / AppContext::cdiv;
|
||||
auto balance = this->currentWallet->balance() / globals::cdiv;
|
||||
auto unlocked_balance = this->currentWallet->unlockedBalance() / globals::cdiv;
|
||||
if(!all && amount > unlocked_balance) {
|
||||
emit createTransactionError("Not enough money to spend");
|
||||
return;
|
||||
@@ -237,7 +243,7 @@ void AppContext::onCreateTransaction(const QString &address, const double amount
|
||||
return;
|
||||
}
|
||||
|
||||
auto amount_num = static_cast<quint64>(amount * AppContext::cdiv);
|
||||
auto amount_num = static_cast<quint64>(amount * globals::cdiv);
|
||||
qDebug() << "creating tx";
|
||||
if(all || amount == balance)
|
||||
this->currentWallet->createTransactionAllAsync(address, "", this->tx_mixin, this->tx_priority);
|
||||
@@ -253,7 +259,6 @@ void AppContext::onCreateTransactionError(const QString &msg) {
|
||||
}
|
||||
|
||||
void AppContext::walletClose(bool emitClosedSignal) {
|
||||
this->nodes->stopTimer();
|
||||
if(this->currentWallet == nullptr) return;
|
||||
emit walletClosing();
|
||||
//ctx->currentWallet->store(); @TODO: uncomment to store on wallet close
|
||||
@@ -276,6 +281,10 @@ void AppContext::onOpenWallet(const QString &path, const QString &password){
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.isEmpty()) {
|
||||
this->walletPassword = "";
|
||||
}
|
||||
|
||||
config()->set(Config::firstRun, false);
|
||||
|
||||
this->walletPath = path;
|
||||
@@ -310,13 +319,14 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
||||
emit walletOpenedError(errMsg);
|
||||
} else {
|
||||
this->walletClose(false);
|
||||
emit walletOpenPasswordNeeded(this->walletPassword.isEmpty());
|
||||
emit walletOpenPasswordNeeded(!this->walletPassword.isEmpty(), wallet->path());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this->currentWallet = wallet;
|
||||
this->walletPath = this->currentWallet->path() + ".keys";
|
||||
this->walletViewOnly = this->currentWallet->viewOnly();
|
||||
config()->set(Config::walletPath, this->walletPath);
|
||||
|
||||
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
|
||||
@@ -330,9 +340,12 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
||||
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
|
||||
connect(this->currentWallet, &Wallet::connectionStatusChanged, this, &AppContext::onConnectionStatusChanged);
|
||||
|
||||
this->nodes->connectToNode();
|
||||
|
||||
emit walletOpened();
|
||||
|
||||
connect(this->currentWallet, &Wallet::connectionStatusChanged, [this]{
|
||||
this->nodes->autoConnect();
|
||||
});
|
||||
this->nodes->connectToNode();
|
||||
this->updateBalance();
|
||||
|
||||
#ifdef DONATE_BEG
|
||||
@@ -341,6 +354,18 @@ void AppContext::onWalletOpened(Wallet *wallet) {
|
||||
|
||||
// force trigger preferredFiat signal for history model
|
||||
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
|
||||
this->setWindowTitle();
|
||||
}
|
||||
|
||||
void AppContext::setWindowTitle(bool mining) {
|
||||
QFileInfo fileInfo(this->walletPath);
|
||||
auto title = QString("Feather - [%1]").arg(fileInfo.fileName());
|
||||
if(this->walletViewOnly)
|
||||
title += " [view-only]";
|
||||
if(mining)
|
||||
title += " [mining]";
|
||||
|
||||
emit setTitle(title);
|
||||
}
|
||||
|
||||
void AppContext::onWSMessage(const QJsonObject &msg) {
|
||||
@@ -380,7 +405,11 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
|
||||
else if(cmd == "nodes") {
|
||||
this->onWSNodes(msg.value("data").toArray());
|
||||
}
|
||||
|
||||
#if defined(HAS_XMRIG)
|
||||
else if(cmd == "xmrig") {
|
||||
this->XMRigDownloads(msg.value("data").toObject());
|
||||
}
|
||||
#endif
|
||||
else if(cmd == "crypto_rates") {
|
||||
QJsonArray crypto_rates = msg.value("data").toArray();
|
||||
AppContext::prices->cryptoPricesReceived(crypto_rates);
|
||||
@@ -434,7 +463,8 @@ void AppContext::onWSNodes(const QJsonArray &nodes) {
|
||||
|
||||
auto node = new FeatherNode(
|
||||
obj.value("address").toString(),
|
||||
(unsigned int)obj.value("height").toInt(),
|
||||
obj.value("height").toInt(),
|
||||
obj.value("target_height").toInt(),
|
||||
obj.value("online").toBool());
|
||||
QSharedPointer<FeatherNode> r = QSharedPointer<FeatherNode>(node);
|
||||
l.append(r);
|
||||
@@ -494,31 +524,17 @@ void AppContext::onWSCCS(const QJsonArray &ccs_data) {
|
||||
}
|
||||
|
||||
void AppContext::createConfigDirectory(const QString &dir) {
|
||||
if(!Utils::dirExists(dir)) {
|
||||
qDebug() << QString("Creating directory: %1").arg(dir);
|
||||
if(!QDir().mkpath(dir))
|
||||
throw std::runtime_error("Could not create directory " + dir.toStdString());
|
||||
}
|
||||
|
||||
QString config_dir_tor = QString("%1%2").arg(dir).arg("tor");
|
||||
if(!Utils::dirExists(config_dir_tor)) {
|
||||
qDebug() << QString("Creating directory: %1").arg(config_dir_tor);
|
||||
if (!QDir().mkpath(config_dir_tor))
|
||||
throw std::runtime_error("Could not create directory " + config_dir_tor.toStdString());
|
||||
}
|
||||
|
||||
QString config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
|
||||
if(!Utils::dirExists(config_dir_tordata)) {
|
||||
qDebug() << QString("Creating directory: %1").arg(config_dir_tordata);
|
||||
if (!QDir().mkpath(config_dir_tordata))
|
||||
throw std::runtime_error("Could not create directory " + config_dir_tordata.toStdString());
|
||||
}
|
||||
QString config_dir_xmrig = QString("%1%2").arg(dir).arg("xmrig");
|
||||
|
||||
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());
|
||||
QStringList createDirs({dir, config_dir_tor, config_dir_tordata, config_dir_xmrig});
|
||||
for(const auto &d: createDirs) {
|
||||
if(!Utils::dirExists(d)) {
|
||||
qDebug() << QString("Creating directory: %1").arg(d);
|
||||
if (!QDir().mkpath(d))
|
||||
throw std::runtime_error("Could not create directory " + d.toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,6 +557,43 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
|
||||
return;
|
||||
}
|
||||
|
||||
this->createWalletFinish(password);
|
||||
}
|
||||
|
||||
void AppContext::createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight) {
|
||||
if(Utils::fileExists(path)) {
|
||||
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
|
||||
qCritical() << err;
|
||||
emit walletCreatedError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this->walletManager->addressValid(address, this->networkType)) {
|
||||
auto err = QString("Failed to create wallet. Invalid address provided.").arg(path);
|
||||
qCritical() << err;
|
||||
emit walletCreatedError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this->walletManager->keyValid(viewkey, address, true, this->networkType)) {
|
||||
auto err = QString("Failed to create wallet. Invalid viewkey provided.").arg(path);
|
||||
qCritical() << err;
|
||||
emit walletCreatedError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!spendkey.isEmpty() && !this->walletManager->keyValid(spendkey, address, false, this->networkType)) {
|
||||
auto err = QString("Failed to create wallet. Invalid spendkey provided.").arg(path);
|
||||
qCritical() << err;
|
||||
emit walletCreatedError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this->currentWallet = this->walletManager->createWalletFromKeys(path, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
|
||||
this->createWalletFinish(password);
|
||||
}
|
||||
|
||||
void AppContext::createWalletFinish(const QString &password) {
|
||||
this->currentWallet->setPassword(password);
|
||||
this->currentWallet->store();
|
||||
this->walletPassword = password;
|
||||
@@ -562,7 +615,7 @@ void AppContext::onSetRestoreHeight(unsigned int height){
|
||||
}
|
||||
|
||||
this->currentWallet->setWalletCreationHeight(height);
|
||||
this->currentWallet->setPassword(this->walletPassword); // trigger .keys write
|
||||
this->currentWallet->setPassword(this->currentWallet->getPassword()); // trigger .keys write
|
||||
|
||||
// nuke wallet cache
|
||||
const auto fn = this->currentWallet->path();
|
||||
@@ -614,8 +667,9 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) {
|
||||
}
|
||||
|
||||
void AppContext::donateBeg() {
|
||||
if(this->networkType != NetworkType::Type::MAINNET)
|
||||
return;
|
||||
if(this->currentWallet == nullptr) return;
|
||||
if(this->networkType != NetworkType::Type::MAINNET) return;
|
||||
if(this->currentWallet->viewOnly()) return;
|
||||
|
||||
auto donationCounter = config()->get(Config::donateBeg).toInt();
|
||||
if(donationCounter == -1)
|
||||
@@ -643,19 +697,19 @@ AppContext::~AppContext() {
|
||||
// ############################################## LIBWALLET QT #########################################################
|
||||
|
||||
void AppContext::onMoneySpent(const QString &txId, quint64 amount) {
|
||||
auto amount_num = amount / AppContext::cdiv;
|
||||
auto amount_num = amount / globals::cdiv;
|
||||
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
|
||||
}
|
||||
|
||||
void AppContext::onMoneyReceived(const QString &txId, quint64 amount) {
|
||||
// Incoming tx included in a block.
|
||||
auto amount_num = amount / AppContext::cdiv;
|
||||
auto amount_num = amount / globals::cdiv;
|
||||
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
|
||||
}
|
||||
|
||||
void AppContext::onUnconfirmedMoneyReceived(const QString &txId, quint64 amount) {
|
||||
// Incoming transaction in pool
|
||||
auto amount_num = amount / AppContext::cdiv;
|
||||
auto amount_num = amount / globals::cdiv;
|
||||
qDebug() << Q_FUNC_INFO << txId << " " << QString::number(amount_num);
|
||||
|
||||
if(this->currentWallet->synchronized()) {
|
||||
@@ -673,13 +727,15 @@ void AppContext::onWalletUpdate() {
|
||||
this->storeWallet();
|
||||
}
|
||||
|
||||
void AppContext::onWalletRefreshed() {
|
||||
void AppContext::onWalletRefreshed(bool success) {
|
||||
if (!this->refreshed) {
|
||||
refreshModels();
|
||||
this->refreshed = true;
|
||||
this->storeWallet();
|
||||
}
|
||||
|
||||
qDebug() << "Wallet refresh status: " << success;
|
||||
|
||||
this->currentWallet->refreshHeightAsync();
|
||||
}
|
||||
|
||||
@@ -696,7 +752,7 @@ void AppContext::onWalletNewBlock(quint64 blockheight, quint64 targetHeight) {
|
||||
void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) {
|
||||
qDebug() << Q_FUNC_INFO << walletHeight << daemonHeight << targetHeight;
|
||||
|
||||
if (!this->currentWallet->connected())
|
||||
if (this->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Disconnected)
|
||||
return;
|
||||
|
||||
if (daemonHeight < targetHeight) {
|
||||
@@ -742,12 +798,12 @@ void AppContext::storeWallet() {
|
||||
|
||||
void AppContext::updateBalance() {
|
||||
if(!this->currentWallet)
|
||||
throw std::runtime_error("this should not happen, ever");
|
||||
return;
|
||||
|
||||
AppContext::balance = this->currentWallet->balance() / AppContext::cdiv;
|
||||
AppContext::balance = this->currentWallet->balance() / globals::cdiv;
|
||||
auto balance_str = QString::number(balance, 'f');
|
||||
|
||||
double unlocked = this->currentWallet->unlockedBalance() / AppContext::cdiv;
|
||||
double unlocked = this->currentWallet->unlockedBalance() / globals::cdiv;
|
||||
auto unlocked_str = QString::number(unlocked, 'f');
|
||||
|
||||
emit balanceUpdated(balance, unlocked, balance_str, unlocked_str);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "utils/networking.h"
|
||||
#include "utils/tor.h"
|
||||
#include "utils/xmrto.h"
|
||||
#include "utils/xmrig.h"
|
||||
#include "utils/wsclient.h"
|
||||
#include "utils/txfiathistory.h"
|
||||
#include "widgets/RedditPost.h"
|
||||
@@ -56,6 +57,7 @@ public:
|
||||
|
||||
QString walletPath;
|
||||
QString walletPassword = "";
|
||||
bool walletViewOnly = false;
|
||||
NetworkType::Type networkType;
|
||||
|
||||
QString applicationPath;
|
||||
@@ -67,7 +69,7 @@ public:
|
||||
const unsigned int kdfRounds = 1;
|
||||
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
|
||||
quint32 tx_mixin = static_cast<const quint32 &>(10);
|
||||
static constexpr const double cdiv = 1e12;
|
||||
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
|
||||
|
||||
QNetworkAccessManager *network;
|
||||
QNetworkAccessManager *networkClearnet;
|
||||
@@ -76,6 +78,7 @@ public:
|
||||
Tor *tor;
|
||||
WSClient *ws;
|
||||
XmrTo *XMRTo;
|
||||
XmRig *XMRig;
|
||||
Nodes *nodes;
|
||||
static Prices *prices;
|
||||
static WalletKeysFilesModel *wallets;
|
||||
@@ -89,6 +92,8 @@ public:
|
||||
WalletManager *walletManager;
|
||||
Wallet *currentWallet = nullptr;
|
||||
void createWallet(FeatherSeed seed, const QString &path, const QString &password);
|
||||
void createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight);
|
||||
void createWalletFinish(const QString &password);
|
||||
void syncStatusUpdated(quint64 height, quint64 target);
|
||||
void updateBalance();
|
||||
void initTor();
|
||||
@@ -98,6 +103,7 @@ public:
|
||||
void walletClose(bool emitClosedSignal = true);
|
||||
void storeWallet();
|
||||
void refreshModels();
|
||||
void setWindowTitle(bool mining = false);
|
||||
|
||||
public slots:
|
||||
void onOpenWallet(const QString& path, const QString &password);
|
||||
@@ -120,7 +126,7 @@ private slots:
|
||||
void onMoneyReceived(const QString &txId, quint64 amount);
|
||||
void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount);
|
||||
void onWalletUpdate();
|
||||
void onWalletRefreshed();
|
||||
void onWalletRefreshed(bool success);
|
||||
void onWalletOpened(Wallet *wallet);
|
||||
void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
|
||||
void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
|
||||
@@ -140,7 +146,7 @@ signals:
|
||||
void walletCreatedError(const QString &msg);
|
||||
void walletCreated(Wallet *wallet);
|
||||
void walletOpenedError(QString msg);
|
||||
void walletOpenPasswordNeeded(bool invalidPassword);
|
||||
void walletOpenPasswordNeeded(bool invalidPassword, QString path);
|
||||
void transactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid);
|
||||
void createTransactionError(QString message);
|
||||
void createTransactionCancelled(QString address, double amount);
|
||||
@@ -149,6 +155,7 @@ signals:
|
||||
void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
|
||||
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
|
||||
void nodeSourceChanged(NodeSource nodeSource);
|
||||
void XMRigDownloads(const QJsonObject &data);
|
||||
void setCustomNodes(QList<FeatherNode> nodes);
|
||||
void ccsEmpty();
|
||||
void openAliasResolveError(const QString &msg);
|
||||
@@ -160,13 +167,14 @@ signals:
|
||||
void initiateTransaction();
|
||||
void endTransaction();
|
||||
void walletClosing();
|
||||
void setTitle(const QString &title); // set window title
|
||||
|
||||
private:
|
||||
void sorry();
|
||||
const unsigned int m_donationBoundary = 15;
|
||||
UtilsNetworking *m_utilsNetworkingNodes;
|
||||
QTimer *m_storeTimer = new QTimer(this);
|
||||
QUrl m_wsUrl = QUrl(QStringLiteral("ws://dtg2clrd6iand4mwp2x6nhbqd3nqbxlbiw65f6vkwmmutxy2sijsnjyd.onion/ws"));
|
||||
QUrl m_wsUrl = QUrl(QStringLiteral("ws://7e6egbawekbkxzkv4244pqeqgoo4axko2imgjbedwnn6s5yb6b7oliqd.onion/ws"));
|
||||
};
|
||||
|
||||
#endif //FEATHER_APPCONTEXT_H
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<file>assets/images/coldcard.png</file>
|
||||
<file>assets/images/coldcard_unpaired.png</file>
|
||||
<file>assets/images/confirmed.png</file>
|
||||
<file>assets/images/confirmed_icon.png</file>
|
||||
<file>assets/images/confirmed.svg</file>
|
||||
<file>assets/images/connect.svg</file>
|
||||
<file>assets/images/copy.png</file>
|
||||
@@ -30,10 +31,13 @@
|
||||
<file>assets/images/edit.png</file>
|
||||
<file>assets/images/exchange.png</file>
|
||||
<file>assets/images/expired.png</file>
|
||||
<file>assets/images/expired_icon.png</file>
|
||||
<file>assets/images/eye1.png</file>
|
||||
<file>assets/images/eye_blind.png</file>
|
||||
<file>assets/images/feather.png</file>
|
||||
<file>assets/images/file.png</file>
|
||||
<file>assets/images/ghost.png</file>
|
||||
<file>assets/images/ghost_icon.png</file>
|
||||
<file>assets/images/history.png</file>
|
||||
<file>assets/images/info.png</file>
|
||||
<file>assets/images/key.png</file>
|
||||
@@ -41,8 +45,10 @@
|
||||
<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/morphtoken.png</file>
|
||||
<file>assets/images/network.png</file>
|
||||
<file>assets/images/offline_tx.png</file>
|
||||
<file>assets/images/person.svg</file>
|
||||
@@ -91,6 +97,8 @@
|
||||
<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>
|
||||
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 496.06 496.06"><title>XMRig</title><defs><style>.a{fill:none;}.b{clip-path:url(#a);}.c{fill:#ec671a;}.d{fill:#575756;}.e{fill:#fff;}</style><clipPath id="a" transform="translate(-106.3 -106.3)"><circle class="a" cx="354.33" cy="354.33" r="248.03"/></clipPath></defs><title>Xr_icon</title><g class="b"><rect class="c" width="496.06" height="496.06"/><polygon class="d" points="496.06 496.06 0 496.06 0 387.21 124.02 387.55 189.12 291.44 254.23 387.55 496.06 387.55 496.06 496.06"/><path class="e" d="M481.44,441.14c0-13.18,0-27,0-37.32,0-54.11,22.15-77.57,55.82-52.71-0.39.07,27.9-43.41,27.9-43.41-35.68-24.09-67.09-10.17-86.81,19.45V295.42H428.74V441.14H391.12l-61-91.46,89.91-134.87H351.85L296,298.52l-55.81-83.71H172l89.91,134.87-61,91.46H106.3v52.37l59.65,0.16-0.12.18H234l62-93,62,93H602.36V441.14H481.44Z" transform="translate(-106.3 -106.3)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 955 B |
@@ -1,6 +0,0 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>tor/tor</file>
|
||||
<file>tor/libevent-2.1.7.dylib</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>tor/tor.exe</file>
|
||||
<file>assets/exec/xmrig</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>tor/tor</file>
|
||||
<file>assets/exec/tor</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>assets/exec/tor</file>
|
||||
<file>assets/exec/libevent-2.1.7.dylib</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -58,7 +58,7 @@ void CLI::onWalletOpenedError(const QString &err) {
|
||||
return this->finishedError(err);
|
||||
}
|
||||
|
||||
void CLI::onWalletOpenPasswordRequired(bool invalidPassword) {
|
||||
void CLI::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
||||
if(mode == CLIMode::CLIModeExportContacts ||
|
||||
mode == CLIMode::CLIModeExportTxHistory)
|
||||
return this->finishedError("invalid password");
|
||||
|
||||
@@ -26,7 +26,7 @@ public slots:
|
||||
//libwalletqt
|
||||
void onWalletOpened();
|
||||
void onWalletOpenedError(const QString& err);
|
||||
void onWalletOpenPasswordRequired(bool invalidPassword);
|
||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||
|
||||
private:
|
||||
AppContext *ctx;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "utils/utils.h"
|
||||
#include "dialog/outputinfodialog.h"
|
||||
#include "dialog/outputsweepdialog.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
@@ -21,6 +22,7 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
||||
, m_copyMenu(new QMenu("Copy",this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ctx = MainWindow::getContext();
|
||||
|
||||
// header context menu
|
||||
ui->coins->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
@@ -40,14 +42,14 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
||||
// context menu
|
||||
ui->coins->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
m_thawOutputAction = new QAction("Thaw output");
|
||||
m_freezeOutputAction = new QAction("Freeze output");
|
||||
m_thawOutputAction = new QAction("Thaw output", this);
|
||||
m_freezeOutputAction = new QAction("Freeze output", this);
|
||||
|
||||
m_freezeAllSelectedAction = new QAction("Freeze selected");
|
||||
m_thawAllSelectedAction = new QAction("Thaw selected");
|
||||
m_freezeAllSelectedAction = new QAction("Freeze selected", this);
|
||||
m_thawAllSelectedAction = new QAction("Thaw selected", this);
|
||||
|
||||
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details");
|
||||
m_sweepOutputAction = new QAction("Sweep output");
|
||||
m_viewOutputAction = new QAction(QIcon(":/assets/images/info.png"), "Details", this);
|
||||
m_sweepOutputAction = new QAction("Sweep output", this);
|
||||
connect(m_freezeOutputAction, &QAction::triggered, this, &CoinsWidget::freezeOutput);
|
||||
connect(m_thawOutputAction, &QAction::triggered, this, &CoinsWidget::thawOutput);
|
||||
connect(m_viewOutputAction, &QAction::triggered, this, &CoinsWidget::viewOutput);
|
||||
@@ -63,13 +65,19 @@ CoinsWidget::CoinsWidget(QWidget *parent)
|
||||
void CoinsWidget::setModel(CoinsModel * model, Coins * coins) {
|
||||
m_coins = coins;
|
||||
m_model = model;
|
||||
m_proxyModel = new CoinsProxyModel;
|
||||
m_proxyModel = new CoinsProxyModel(this);
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
ui->coins->setModel(m_proxyModel);
|
||||
ui->coins->setColumnHidden(CoinsModel::Spent, true);
|
||||
ui->coins->setColumnHidden(CoinsModel::SpentHeight, true);
|
||||
ui->coins->setColumnHidden(CoinsModel::Frozen, true);
|
||||
|
||||
if (!m_ctx->currentWallet->viewOnly()) {
|
||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, true);
|
||||
} else {
|
||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, false);
|
||||
}
|
||||
|
||||
ui->coins->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
ui->coins->header()->setSectionResizeMode(CoinsModel::AddressLabel, QHeaderView::Stretch);
|
||||
ui->coins->header()->setSortIndicator(CoinsModel::BlockHeight, Qt::DescendingOrder);
|
||||
@@ -103,10 +111,15 @@ void CoinsWidget::showContextMenu(const QPoint &point) {
|
||||
|
||||
if (!isSpent) {
|
||||
isFrozen ? menu->addAction(m_thawOutputAction) : menu->addAction(m_freezeOutputAction);
|
||||
}
|
||||
if (!isFrozen && isUnlocked) {
|
||||
|
||||
menu->addAction(m_sweepOutputAction);
|
||||
if (isFrozen || !isUnlocked) {
|
||||
m_sweepOutputAction->setDisabled(true);
|
||||
} else {
|
||||
m_sweepOutputAction->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
menu->addAction(m_viewOutputAction);
|
||||
}
|
||||
|
||||
@@ -194,6 +207,7 @@ void CoinsWidget::onSweepOutput() {
|
||||
qCritical() << "key image: " << keyImage;
|
||||
|
||||
emit sweepOutput(keyImage, dialog->address(), dialog->churn(), dialog->outputs());
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void CoinsWidget::copy(copyField field) {
|
||||
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
Coins *m_coins;
|
||||
CoinsModel * m_model;
|
||||
CoinsProxyModel * m_proxyModel;
|
||||
AppContext *m_ctx;
|
||||
|
||||
void showContextMenu(const QPoint & point);
|
||||
void copy(copyField field);
|
||||
|
||||
@@ -68,9 +68,7 @@ HelpLabel::HelpLabel(QWidget *parent) : QLabel(parent)
|
||||
void HelpLabel::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
QMessageBox msgBox(QApplication::activeWindow());
|
||||
msgBox.setText(this->help_text);
|
||||
msgBox.exec();
|
||||
QMessageBox::information(this, "Help", this->help_text);
|
||||
}
|
||||
|
||||
void HelpLabel::enterEvent(QEvent *event)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="copyrightText">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "broadcasttxdialog.h"
|
||||
#include "ui_broadcasttxdialog.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx)
|
||||
: QDialog(parent)
|
||||
, m_ctx(ctx)
|
||||
, ui(new Ui::BroadcastTxDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_network = new UtilsNetworking(m_ctx->network, this);
|
||||
|
||||
auto node = ctx->nodes->connection();
|
||||
m_rpc = new DaemonRpc(this, m_network, node.full);
|
||||
|
||||
connect(ui->btn_Broadcast, &QPushButton::clicked, this, &BroadcastTxDialog::broadcastTx);
|
||||
connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject);
|
||||
|
||||
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void BroadcastTxDialog::broadcastTx() {
|
||||
QString tx = ui->transaction->toPlainText();
|
||||
|
||||
QString node = [this]{
|
||||
QString node;
|
||||
if (ui->radio_useCustom->isChecked())
|
||||
node = ui->customNode->text();
|
||||
else
|
||||
node = m_ctx->nodes->connection().full;
|
||||
|
||||
if (!node.startsWith("http://"))
|
||||
node = QString("http://%1").arg(node);
|
||||
return node;
|
||||
}();
|
||||
|
||||
m_rpc->setDaemonAddress(node);
|
||||
m_rpc->sendRawTransaction(tx);
|
||||
}
|
||||
|
||||
void BroadcastTxDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) {
|
||||
if (!resp.ok) {
|
||||
QMessageBox::warning(this, "Transaction broadcast", resp.status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resp.endpoint == DaemonRpc::Endpoint::SEND_RAW_TRANSACTION) {
|
||||
QMessageBox::information(this, "Transaction broadcast", "Transaction submitted successfully.\n\n"
|
||||
"If the transaction belongs to this wallet it may take several minutes before it shows up in the history tab.");
|
||||
}
|
||||
}
|
||||
|
||||
BroadcastTxDialog::~BroadcastTxDialog() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_BROADCASTTXDIALOG_H
|
||||
#define FEATHER_BROADCASTTXDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "appcontext.h"
|
||||
#include "utils/daemonrpc.h"
|
||||
|
||||
namespace Ui {
|
||||
class BroadcastTxDialog;
|
||||
}
|
||||
|
||||
class BroadcastTxDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx);
|
||||
~BroadcastTxDialog() override;
|
||||
|
||||
private slots:
|
||||
void broadcastTx();
|
||||
void onApiResponse(const DaemonRpc::DaemonResponse &resp);
|
||||
|
||||
private:
|
||||
Ui::BroadcastTxDialog *ui;
|
||||
UtilsNetworking *m_network;
|
||||
AppContext *m_ctx;
|
||||
DaemonRpc *m_rpc;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_BROADCASTTXDIALOG_H
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BroadcastTxDialog</class>
|
||||
<widget class="QDialog" name="BroadcastTxDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>797</width>
|
||||
<height>575</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Broadcast transaction</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Transaction:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="transaction">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Paste hex encoded signed tx string here</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Node</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_useDefault">
|
||||
<property name="text">
|
||||
<string>Use currently connected node</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_useCustom">
|
||||
<property name="text">
|
||||
<string>Use custom node</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="customNode"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>All transactions are broadcast over Tor</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Broadcast">
|
||||
<property name="text">
|
||||
<string>Broadcast</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -12,15 +12,26 @@
|
||||
DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::DebugInfoDialog)
|
||||
, m_ctx(ctx)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->btn_Copy, &QPushButton::clicked, this, &DebugInfoDialog::copyToClipboad);
|
||||
|
||||
m_updateTimer.start(5000);
|
||||
connect(&m_updateTimer, &QTimer::timeout, this, &DebugInfoDialog::updateInfo);
|
||||
this->updateInfo();
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void DebugInfoDialog::updateInfo() {
|
||||
QString torStatus;
|
||||
if(ctx->isTorSocks)
|
||||
if(m_ctx->isTorSocks)
|
||||
torStatus = "Torsocks";
|
||||
else if(ctx->tor->localTor)
|
||||
else if(m_ctx->tor->localTor)
|
||||
torStatus = "Local (assumed to be running)";
|
||||
else if(ctx->tor->torConnected)
|
||||
else if(m_ctx->tor->torConnected)
|
||||
torStatus = "Running";
|
||||
else
|
||||
torStatus = "Unknown";
|
||||
@@ -28,27 +39,28 @@ DebugInfoDialog::DebugInfoDialog(AppContext *ctx, QWidget *parent)
|
||||
ui->label_featherVersion->setText(QString("%1-%2").arg(FEATHER_VERSION, FEATHER_BRANCH));
|
||||
ui->label_moneroVersion->setText(QString("%1-%2").arg(MONERO_VERSION, MONERO_BRANCH));
|
||||
|
||||
ui->label_walletHeight->setText(QString::number(ctx->currentWallet->blockChainHeight()));
|
||||
ui->label_daemonHeight->setText(QString::number(ctx->currentWallet->daemonBlockChainHeight()));
|
||||
ui->label_restoreHeight->setText(QString::number(ctx->currentWallet->getWalletCreationHeight()));
|
||||
ui->label_synchronized->setText(ctx->currentWallet->synchronized() ? "True" : "False");
|
||||
ui->label_walletHeight->setText(QString::number(m_ctx->currentWallet->blockChainHeight()));
|
||||
ui->label_daemonHeight->setText(QString::number(m_ctx->currentWallet->daemonBlockChainHeight()));
|
||||
ui->label_targetHeight->setText(QString::number(m_ctx->currentWallet->daemonBlockChainTargetHeight()));
|
||||
ui->label_restoreHeight->setText(QString::number(m_ctx->currentWallet->getWalletCreationHeight()));
|
||||
ui->label_synchronized->setText(m_ctx->currentWallet->synchronized() ? "True" : "False");
|
||||
|
||||
auto node = ctx->nodes->connection();
|
||||
auto node = m_ctx->nodes->connection();
|
||||
ui->label_remoteNode->setText(node.full);
|
||||
ui->label_walletStatus->setText(this->statusToString(ctx->currentWallet->connected()));
|
||||
ui->label_walletStatus->setText(this->statusToString(m_ctx->currentWallet->connectionStatus()));
|
||||
ui->label_torStatus->setText(torStatus);
|
||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(ctx->ws->webSocket.state()));
|
||||
ui->label_websocketStatus->setText(Utils::QtEnumToString(m_ctx->ws->webSocket.state()));
|
||||
|
||||
ui->label_netType->setText(Utils::QtEnumToString(ctx->currentWallet->nettype()));
|
||||
ui->label_seedType->setText(ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
|
||||
ui->label_viewOnly->setText(ctx->currentWallet->viewOnly() ? "True" : "False");
|
||||
ui->label_netType->setText(Utils::QtEnumToString(m_ctx->currentWallet->nettype()));
|
||||
ui->label_seedType->setText(m_ctx->currentWallet->getCacheAttribute("feather.seed").isEmpty() ? "25 word" : "14 word");
|
||||
ui->label_viewOnly->setText(m_ctx->currentWallet->viewOnly() ? "True" : "False");
|
||||
|
||||
ui->label_OS->setText(QSysInfo::prettyProductName());
|
||||
QString os = QSysInfo::prettyProductName();
|
||||
if (m_ctx->isTails) {
|
||||
os = QString("Tails %1").arg(TailsOS::version());
|
||||
}
|
||||
ui->label_OS->setText(os);
|
||||
ui->label_timestamp->setText(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
|
||||
connect(ui->btn_Copy, &QPushButton::clicked, this, &DebugInfoDialog::copyToClipboad);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
QString DebugInfoDialog::statusToString(Wallet::ConnectionStatus status) {
|
||||
@@ -67,26 +79,28 @@ QString DebugInfoDialog::statusToString(Wallet::ConnectionStatus status) {
|
||||
}
|
||||
|
||||
void DebugInfoDialog::copyToClipboad() {
|
||||
// Two spaces at the end of each line are for newlines in Markdown
|
||||
QString text = "";
|
||||
text += QString("Feather version: %1\n").arg(ui->label_featherVersion->text());
|
||||
text += QString("Monero version: %1\n").arg(ui->label_moneroVersion->text());
|
||||
text += QString("Feather version: %1 \n").arg(ui->label_featherVersion->text());
|
||||
text += QString("Monero version: %1 \n").arg(ui->label_moneroVersion->text());
|
||||
|
||||
text += QString("Wallet height: %1\n").arg(ui->label_walletHeight->text());
|
||||
text += QString("Daemon height: %1\n").arg(ui->label_daemonHeight->text());
|
||||
text += QString("Restore height: %1\n").arg(ui->label_restoreHeight->text());
|
||||
text += QString("Synchronized: %1\n").arg(ui->label_synchronized->text());
|
||||
text += QString("Wallet height: %1 \n").arg(ui->label_walletHeight->text());
|
||||
text += QString("Daemon height: %1 \n").arg(ui->label_daemonHeight->text());
|
||||
text += QString("Target height: %1 \n").arg(ui->label_targetHeight->text());
|
||||
text += QString("Restore height: %1 \n").arg(ui->label_restoreHeight->text());
|
||||
text += QString("Synchronized: %1 \n").arg(ui->label_synchronized->text());
|
||||
|
||||
text += QString("Remote node: %1\n").arg(ui->label_remoteNode->text());
|
||||
text += QString("Wallet status: %1\n").arg(ui->label_walletStatus->text());
|
||||
text += QString("Tor status: %1\n").arg(ui->label_torStatus->text());
|
||||
text += QString("Websocket status: %1\n").arg(ui->label_websocketStatus->text());
|
||||
text += QString("Remote node: %1 \n").arg(ui->label_remoteNode->text());
|
||||
text += QString("Wallet status: %1 \n").arg(ui->label_walletStatus->text());
|
||||
text += QString("Tor status: %1 \n").arg(ui->label_torStatus->text());
|
||||
text += QString("Websocket status: %1 \n").arg(ui->label_websocketStatus->text());
|
||||
|
||||
text += QString("Network type: %1\n").arg(ui->label_netType->text());
|
||||
text += QString("Seed type: %1\n").arg(ui->label_seedType->text());
|
||||
text += QString("View only: %1\n").arg(ui->label_viewOnly->text());
|
||||
text += QString("Network type: %1 \n").arg(ui->label_netType->text());
|
||||
text += QString("Seed type: %1 \n").arg(ui->label_seedType->text());
|
||||
text += QString("View only: %1 \n").arg(ui->label_viewOnly->text());
|
||||
|
||||
text += QString("Operating system: %1\n").arg(ui->label_OS->text());
|
||||
text += QString("Timestamp: %1\n").arg(ui->label_timestamp->text());
|
||||
text += QString("Operating system: %1 \n").arg(ui->label_OS->text());
|
||||
text += QString("Timestamp: %1 \n").arg(ui->label_timestamp->text());
|
||||
|
||||
Utils::copyToClipboard(text);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ public:
|
||||
private:
|
||||
QString statusToString(Wallet::ConnectionStatus status);
|
||||
void copyToClipboad();
|
||||
void updateInfo();
|
||||
|
||||
QTimer m_updateTimer;
|
||||
AppContext *m_ctx;
|
||||
|
||||
Ui::DebugInfoDialog *ui;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>693</width>
|
||||
<height>580</height>
|
||||
<height>612</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -91,14 +91,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Restore height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_restoreHeight">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -108,14 +108,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Synchronized:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="label_synchronized">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -125,21 +125,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_27">
|
||||
<property name="text">
|
||||
<string>Remote node:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QLabel" name="label_remoteNode">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -149,14 +149,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>Wallet status:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="QLabel" name="label_walletStatus">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -166,14 +166,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Tor status:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QLabel" name="label_torStatus">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -183,14 +183,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="text">
|
||||
<string>Websocket status:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QLabel" name="label_websocketStatus">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -200,21 +200,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Network type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<item row="14" column="1">
|
||||
<widget class="QLabel" name="label_netType">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -224,14 +224,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0">
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="text">
|
||||
<string>Seed type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1">
|
||||
<item row="15" column="1">
|
||||
<widget class="QLabel" name="label_seedType">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -241,14 +241,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="0">
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>View only:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
<item row="16" column="1">
|
||||
<widget class="QLabel" name="label_viewOnly">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -258,14 +258,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="0">
|
||||
<item row="19" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Timestamp:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<item row="19" column="1">
|
||||
<widget class="QLabel" name="label_timestamp">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
@@ -275,27 +275,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0">
|
||||
<item row="18" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Operating system:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="1">
|
||||
<item row="18" column="1">
|
||||
<widget class="QLabel" name="label_OS">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<item row="17" column="1">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Target height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_targetHeight">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -5,17 +5,39 @@
|
||||
#include "ui_passwordchangedialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
PasswordChangeDialog::PasswordChangeDialog(QWidget *parent)
|
||||
PasswordChangeDialog::PasswordChangeDialog(QWidget *parent, Wallet *wallet)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::PasswordChangeDialog)
|
||||
, m_wallet(wallet)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->icon->setPixmap(QPixmap(":/assets/images/lock.png").scaledToWidth(32, Qt::SmoothTransformation));
|
||||
|
||||
bool noPassword = wallet->getPassword().isEmpty();
|
||||
|
||||
QString warning_str = noPassword ? "Your wallet is not password protected. Use this dialog to add a password to your wallet." :
|
||||
"Your wallet is password protected and encrypted. Use this dialog to change your password.";
|
||||
ui->label_warning->setText(warning_str);
|
||||
|
||||
QPixmap pixmap = noPassword ? QPixmap(":/assets/images/unlock.png") : QPixmap(":/assets/images/lock.png");
|
||||
ui->icon->setPixmap(pixmap.scaledToWidth(32, Qt::SmoothTransformation));
|
||||
|
||||
if (noPassword) {
|
||||
ui->label_currentPassword->hide();
|
||||
ui->lineEdit_currentPassword->hide();
|
||||
}
|
||||
|
||||
connect(ui->lineEdit_newPassword, &QLineEdit::textChanged, this, &PasswordChangeDialog::passwordsMatch);
|
||||
connect(ui->lineEdit_confirmPassword, &QLineEdit::textChanged, this, &PasswordChangeDialog::passwordsMatch);
|
||||
|
||||
connect(ui->btn_Cancel, &QPushButton::clicked, [this]{
|
||||
this->reject();
|
||||
});
|
||||
connect(ui->btn_OK, &QPushButton::clicked, this, &PasswordChangeDialog::setPassword);
|
||||
|
||||
ui->label_match->setVisible(false);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
@@ -24,15 +46,28 @@ PasswordChangeDialog::~PasswordChangeDialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString PasswordChangeDialog::getCurrentPassword() {
|
||||
return ui->lineEdit_currentPassword->text();
|
||||
}
|
||||
|
||||
QString PasswordChangeDialog::getNewPassword() {
|
||||
return ui->lineEdit_newPassword->text();
|
||||
}
|
||||
|
||||
void PasswordChangeDialog::passwordsMatch() {
|
||||
bool match = ui->lineEdit_newPassword->text() == ui->lineEdit_confirmPassword->text();
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(match);
|
||||
ui->btn_OK->setEnabled(match);
|
||||
ui->label_match->setHidden(match);
|
||||
}
|
||||
|
||||
void PasswordChangeDialog::setPassword() {
|
||||
QString currentPassword = ui->lineEdit_currentPassword->text();
|
||||
QString newPassword = ui->lineEdit_newPassword->text();
|
||||
|
||||
if (currentPassword != m_wallet->getPassword()) {
|
||||
QMessageBox::warning(this, "Error", "Incorrect password");
|
||||
ui->lineEdit_currentPassword->setText("");
|
||||
ui->lineEdit_currentPassword->setFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_wallet->setPassword(newPassword)) {
|
||||
QMessageBox::information(this, "Information", "Password changed successfully");
|
||||
this->accept();
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, "Error", QString("Error: %1").arg(m_wallet->errorString()));
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#define FEATHER_PASSWORDCHANGEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "libwalletqt/Wallet.h"
|
||||
|
||||
namespace Ui {
|
||||
class PasswordChangeDialog;
|
||||
@@ -15,16 +16,15 @@ class PasswordChangeDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PasswordChangeDialog(QWidget *parent = nullptr);
|
||||
explicit PasswordChangeDialog(QWidget *parent, Wallet *wallet);
|
||||
~PasswordChangeDialog() override;
|
||||
|
||||
QString getCurrentPassword();
|
||||
QString getNewPassword();
|
||||
|
||||
private:
|
||||
Ui::PasswordChangeDialog *ui;
|
||||
Wallet *m_wallet;
|
||||
|
||||
void passwordsMatch();
|
||||
void setPassword();
|
||||
};
|
||||
|
||||
#endif //FEATHER_PASSWORDCHANGEDIALOG_H
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>237</height>
|
||||
<width>556</width>
|
||||
<height>309</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -30,7 +30,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QLabel" name="label_warning">
|
||||
<property name="text">
|
||||
<string>Your wallet is password protected and encrypted. Use this dialog to change your password.</string>
|
||||
</property>
|
||||
@@ -41,6 +41,22 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
@@ -65,7 +81,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label_currentPassword">
|
||||
<property name="text">
|
||||
<string>Current Password:</string>
|
||||
</property>
|
||||
@@ -88,14 +104,45 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_match">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Passwords do not match</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Cancel">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_OK">
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -105,38 +152,5 @@
|
||||
<tabstop>lineEdit_confirmPassword</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PasswordChangeDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PasswordChangeDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "passworddialog.h"
|
||||
#include "ui_passworddialog.h"
|
||||
|
||||
PasswordDialog::PasswordDialog(QWidget *parent, const QString &walletName, bool incorrectPassword)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::PasswordDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->label_wallet->setText(QString("Please enter password for wallet: %1").arg(walletName));
|
||||
ui->label_incorrectPassword->setVisible(incorrectPassword);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, [this]{
|
||||
password = ui->line_password->text();
|
||||
});
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
PasswordDialog::~PasswordDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_PASSWORDDIALOG_H
|
||||
#define FEATHER_PASSWORDDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class PasswordDialog;
|
||||
}
|
||||
|
||||
class PasswordDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PasswordDialog(QWidget *parent, const QString &walletName, bool incorrectPassword);
|
||||
~PasswordDialog() override;
|
||||
|
||||
QString password = "";
|
||||
|
||||
private:
|
||||
Ui::PasswordDialog *ui;
|
||||
};
|
||||
|
||||
#endif //FEATHER_PASSWORDDIALOG_H
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PasswordDialog</class>
|
||||
<widget class="QDialog" name="PasswordDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>832</width>
|
||||
<height>158</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Password required</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_wallet">
|
||||
<property name="text">
|
||||
<string>Please enter password for wallet: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_incorrectPassword">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:600; color:#a40000;">Incorrect password, try again.</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PasswordDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PasswordDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -9,15 +9,14 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
QrCodeDialog::QrCodeDialog(QWidget *parent, const QString &text, const QString &title)
|
||||
QrCodeDialog::QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::QrCodeDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setWindowTitle(title);
|
||||
|
||||
m_qrc = new QrCode(text, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
|
||||
m_pixmap = m_qrc->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
|
||||
m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
|
||||
ui->QrCode->setPixmap(m_pixmap);
|
||||
|
||||
connect(ui->btn_CopyImage, &QPushButton::clicked, this, &QrCodeDialog::copyImage);
|
||||
@@ -32,7 +31,11 @@ QrCodeDialog::QrCodeDialog(QWidget *parent, const QString &text, const QString &
|
||||
QrCodeDialog::~QrCodeDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete m_qrc;
|
||||
}
|
||||
|
||||
void QrCodeDialog::setQrCode(const QrCode &qrCode) {
|
||||
m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
|
||||
ui->QrCode->setPixmap(m_pixmap);
|
||||
}
|
||||
|
||||
void QrCodeDialog::copyImage() {
|
||||
|
||||
@@ -16,15 +16,15 @@ class QrCodeDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QrCodeDialog(QWidget *parent, const QString &text, const QString &title = "Qr Code");
|
||||
explicit QrCodeDialog(QWidget *parent, const QrCode &qrCode, const QString &title = "Qr Code");
|
||||
~QrCodeDialog() override;
|
||||
void setQrCode(const QrCode &qrCode);
|
||||
|
||||
private:
|
||||
void copyImage();
|
||||
void saveImage();
|
||||
|
||||
Ui::QrCodeDialog *ui;
|
||||
QrCode *m_qrc;
|
||||
QPixmap m_pixmap;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,27 +4,51 @@
|
||||
#include "ui_seeddialog.h"
|
||||
#include "seeddialog.h"
|
||||
|
||||
SeedDialog::SeedDialog(const QString &seed, QWidget *parent)
|
||||
SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::SeedDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->label_seedIcon->setPixmap(QPixmap(":/assets/images/seed.png").scaledToWidth(64, Qt::SmoothTransformation));
|
||||
|
||||
ui->label_restoreHeight->setText(QString::number(wallet->getWalletCreationHeight()));
|
||||
|
||||
QString seed_14_words = wallet->getCacheAttribute("feather.seed");
|
||||
QString seed_25_words = wallet->getSeed();
|
||||
|
||||
if (seed_14_words.isEmpty()) {
|
||||
ui->check_toggleSeedType->hide();
|
||||
this->setSeed(seed_25_words);
|
||||
} else {
|
||||
this->setSeed(seed_14_words);
|
||||
ui->widgetRestoreHeight->setVisible(false);
|
||||
}
|
||||
|
||||
connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed_14_words](bool toggled){
|
||||
this->setSeed(toggled ? seed_25_words : seed_14_words);
|
||||
ui->widgetRestoreHeight->setVisible(toggled);
|
||||
});
|
||||
|
||||
ui->label_restoreHeightHelp->setHelpText("Should you restore your wallet in the future, "
|
||||
"specifying this block number will recover your wallet quicker.");
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void SeedDialog::setSeed(const QString &seed) {
|
||||
ui->seed->setPlainText(seed);
|
||||
|
||||
int words = seed.split(" ").size();
|
||||
ui->label_warning->setText(QString("<p>Please save these %1 words on paper (order is important). "
|
||||
"This seed will allow you to recover your wallet in case "
|
||||
"of computer failure."
|
||||
"</p>"
|
||||
"<b>WARNING:</b>"
|
||||
"<ul>"
|
||||
"<li>Never disclose your seed.</li>"
|
||||
"<li>Never type it on a website</li>"
|
||||
"<li>Do not store it electronically</li>"
|
||||
"</ul>").arg(words));
|
||||
|
||||
this->adjustSize();
|
||||
"This seed will allow you to recover your wallet in case "
|
||||
"of computer failure."
|
||||
"</p>"
|
||||
"<b>WARNING:</b>"
|
||||
"<ul>"
|
||||
"<li>Never disclose your seed.</li>"
|
||||
"<li>Never type it on a website</li>"
|
||||
"<li>Do not store it electronically</li>"
|
||||
"</ul>").arg(words));
|
||||
}
|
||||
|
||||
SeedDialog::~SeedDialog()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#define FEATHER_SEEDDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "libwalletqt/Wallet.h"
|
||||
|
||||
namespace Ui {
|
||||
class SeedDialog;
|
||||
@@ -15,10 +16,12 @@ class SeedDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SeedDialog(const QString& seed, QWidget *parent = nullptr);
|
||||
explicit SeedDialog(Wallet *wallet, QWidget *parent = nullptr);
|
||||
~SeedDialog() override;
|
||||
|
||||
private:
|
||||
void setSeed(const QString &seed);
|
||||
|
||||
Ui::SeedDialog *ui;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>542</width>
|
||||
<height>244</height>
|
||||
<width>590</width>
|
||||
<height>346</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -34,29 +34,94 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="seed">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>125</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="seed">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>125</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widgetRestoreHeight" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="HelpLabel" name="label_restoreHeightHelp">
|
||||
<property name="text">
|
||||
<string>Restore height:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_restoreHeight">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="check_toggleSeedType">
|
||||
<property name="text">
|
||||
<string>Show 25 word seed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_warning">
|
||||
<property name="text">
|
||||
@@ -79,6 +144,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>HelpLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>components.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
@@ -6,18 +6,29 @@
|
||||
|
||||
#include "libwalletqt/CoinsInfo.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include <QDebug>
|
||||
#include "utils.h"
|
||||
|
||||
TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txInfo, QWidget *parent)
|
||||
#include <QMessageBox>
|
||||
|
||||
TransactionInfoDialog::TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::TransactionInfoDialog)
|
||||
, m_coins(coins)
|
||||
, m_wallet(wallet)
|
||||
, m_txInfo(txInfo)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->txid->setText(QString(txInfo->hash()));
|
||||
ui->txid->setCursorPosition(0);
|
||||
m_txProofWidget = new TxProofWidget(this, wallet, txInfo);
|
||||
|
||||
ui->label_txid->setText(QString(txInfo->hash()));
|
||||
|
||||
if (txInfo->direction() == TransactionInfo::Direction_In) {
|
||||
ui->txKey->hide();
|
||||
} else {
|
||||
QString txKey = m_wallet->getTxKey(txInfo->hash());
|
||||
txKey = txKey.isEmpty() ? "unknown" : txKey;
|
||||
ui->label_txKey->setText(txKey);
|
||||
}
|
||||
|
||||
ui->label_status->setText(QString("Status: %1 confirmations").arg(txInfo->confirmations()));
|
||||
ui->label_date->setText(QString("Date: %1").arg(txInfo->timestamp().toString("yyyy-MM-dd HH:mm")));
|
||||
@@ -30,7 +41,7 @@ TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txIn
|
||||
ui->label_fee->setText(QString("Fee: %1").arg(txInfo->isCoinbase() ? WalletManager::displayAmount(0) : fee));
|
||||
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(txInfo->unlockTime()));
|
||||
|
||||
qDebug() << m_coins->coins_from_txid(txInfo->hash());
|
||||
qDebug() << m_wallet->coins()->coins_from_txid(txInfo->hash());
|
||||
|
||||
QString destinations = txInfo->destinations_formatted();
|
||||
if (destinations.isEmpty()) {
|
||||
@@ -41,6 +52,8 @@ TransactionInfoDialog::TransactionInfoDialog(Coins *coins, TransactionInfo *txIn
|
||||
ui->destinations->setText(destinations);
|
||||
}
|
||||
|
||||
ui->txProofWidget->addWidget(m_txProofWidget);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <QtSvg/QSvgWidget>
|
||||
#include "libwalletqt/Coins.h"
|
||||
#include "libwalletqt/TransactionInfo.h"
|
||||
#include "libwalletqt/Wallet.h"
|
||||
#include "widgets/txproofwidget.h"
|
||||
|
||||
namespace Ui {
|
||||
class TransactionInfoDialog;
|
||||
@@ -18,14 +20,15 @@ class TransactionInfoDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TransactionInfoDialog(Coins *coins, TransactionInfo *txInfo, QWidget *parent = nullptr);
|
||||
explicit TransactionInfoDialog(Wallet *wallet, TransactionInfo *txInfo, QWidget *parent = nullptr);
|
||||
~TransactionInfoDialog() override;
|
||||
|
||||
private:
|
||||
Ui::TransactionInfoDialog *ui;
|
||||
|
||||
TransactionInfo *m_txInfo;
|
||||
Coins *m_coins;
|
||||
Wallet *m_wallet;
|
||||
TxProofWidget *m_txProofWidget;
|
||||
};
|
||||
|
||||
#endif //FEATHER_TRANSACTIONINFODIALOG_H
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>547</width>
|
||||
<height>332</height>
|
||||
<width>829</width>
|
||||
<height>570</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -15,32 +15,41 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Transaction ID:</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_txid">
|
||||
<property name="text">
|
||||
<string>txid</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txid">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>525</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>txid</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
<widget class="QGroupBox" name="txKey">
|
||||
<property name="title">
|
||||
<string>Transaction key:</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_txKey">
|
||||
<property name="text">
|
||||
<string>txKey</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -149,6 +158,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="txProofWidget"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "txconfadvdialog.h"
|
||||
#include "ui_txconfadvdialog.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include "qrcode/QrCode.h"
|
||||
#include "dialog/qrcodedialog.h"
|
||||
#include "utils/utils.h"
|
||||
#include "libwalletqt/PendingTransactionInfo.h"
|
||||
#include "libwalletqt/Transfer.h"
|
||||
#include "libwalletqt/Input.h"
|
||||
#include "model/ModelUtils.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
TxConfAdvDialog::TxConfAdvDialog(AppContext *ctx, const QString &description, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::TxConfAdvDialog)
|
||||
, m_ctx(ctx)
|
||||
, m_exportUnsignedMenu(new QMenu(this))
|
||||
, m_exportSignedMenu(new QMenu(this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_exportUnsignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::unsignedCopy);
|
||||
m_exportUnsignedMenu->addAction("Show as QR code", this, &TxConfAdvDialog::unsignedQrCode);
|
||||
m_exportUnsignedMenu->addAction("Save to file", this, &TxConfAdvDialog::unsignedSaveFile);
|
||||
ui->btn_exportUnsigned->setMenu(m_exportUnsignedMenu);
|
||||
|
||||
m_exportSignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::signedCopy);
|
||||
m_exportSignedMenu->addAction("Save to file", this, &TxConfAdvDialog::signedSaveFile);
|
||||
ui->btn_exportSigned->setMenu(m_exportSignedMenu);
|
||||
|
||||
if (m_ctx->currentWallet->viewOnly()) {
|
||||
ui->btn_exportSigned->hide();
|
||||
ui->btn_send->hide();
|
||||
}
|
||||
|
||||
ui->label_description->setText(QString("Description: %1").arg(description));
|
||||
|
||||
connect(ui->btn_sign, &QPushButton::clicked, this, &TxConfAdvDialog::signTransaction);
|
||||
connect(ui->btn_send, &QPushButton::clicked, this, &TxConfAdvDialog::broadcastTransaction);
|
||||
connect(ui->btn_close, &QPushButton::clicked, this, &TxConfAdvDialog::closeDialog);
|
||||
|
||||
ui->inputs->setFont(ModelUtils::getMonospaceFont());
|
||||
ui->outputs->setFont(ModelUtils::getMonospaceFont());
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::setTransaction(PendingTransaction *tx) {
|
||||
ui->btn_sign->hide();
|
||||
|
||||
m_tx = tx;
|
||||
m_tx->refresh();
|
||||
PendingTransactionInfo *ptx = m_tx->transaction(0);
|
||||
|
||||
ui->txid->setText(tx->txid().first());
|
||||
|
||||
ui->amount->setText(WalletManager::displayAmount(tx->amount()));
|
||||
ui->fee->setText(WalletManager::displayAmount(ptx->fee()));
|
||||
ui->total->setText(WalletManager::displayAmount(tx->amount() + ptx->fee()));
|
||||
|
||||
auto size_str = [this]{
|
||||
if (m_ctx->currentWallet->viewOnly()) {
|
||||
return QString("Size: %1 bytes (unsigned)").arg(QString::number(m_tx->unsignedTxToBin().size()));
|
||||
} else {
|
||||
auto size = m_tx->signedTxToHex(0).size() / 2;
|
||||
return QString("Size: %1 bytes (%2 bytes unsigned)").arg(QString::number(size), QString::number(m_tx->unsignedTxToBin().size()));
|
||||
}
|
||||
}();
|
||||
ui->label_size->setText(size_str);
|
||||
|
||||
this->setupConstructionData(ptx);
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
|
||||
m_utx = utx;
|
||||
m_utx->refresh();
|
||||
|
||||
ui->btn_exportUnsigned->hide();
|
||||
ui->btn_exportSigned->hide();
|
||||
ui->btn_sign->show();
|
||||
ui->btn_send->hide();
|
||||
|
||||
ui->txid->setText("n/a");
|
||||
ui->label_size->setText("Size: n/a");
|
||||
|
||||
ui->amount->setText(WalletManager::displayAmount(utx->amount(0)));
|
||||
ui->fee->setText(WalletManager::displayAmount(utx->fee(0)));
|
||||
ui->total->setText(WalletManager::displayAmount(utx->amount(0) + utx->fee(0)));
|
||||
|
||||
ConstructionInfo *ci = m_utx->constructionInfo(0);
|
||||
this->setupConstructionData(ci);
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
|
||||
QString inputs_str;
|
||||
auto inputs = ci->inputs();
|
||||
for (const auto& i: inputs) {
|
||||
inputs_str += QString("%1 %2\n").arg(i->pubKey(), WalletManager::displayAmount(i->amount()));
|
||||
}
|
||||
ui->inputs->setText(inputs_str);
|
||||
ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(inputs.size())));
|
||||
|
||||
QString outputs_str;
|
||||
auto outputs = ci->outputs();
|
||||
for (const auto& o: outputs) {
|
||||
outputs_str += QString("%1 %2\n").arg(o->address(), WalletManager::displayAmount(o->amount()));
|
||||
}
|
||||
ui->outputs->setText(outputs_str);
|
||||
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(outputs.size())));
|
||||
|
||||
ui->label_ringSize->setText(QString("Ring size: %1").arg(QString::number(ci->minMixinCount() + 1)));
|
||||
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(QString::number(ci->unlockTime())));
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signTransaction() {
|
||||
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save signed transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
|
||||
if(fn.isEmpty()) return;
|
||||
|
||||
m_utx->sign(fn) ? QMessageBox::information(this, "Sign transaction", "Transaction saved successfully")
|
||||
: QMessageBox::warning(this, "Sign transaction", "Failes to save transaction to file.");
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::unsignedSaveFile() {
|
||||
QString defaultName = QString("%1_unsigned_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*unsigned_monero_tx)");
|
||||
if(fn.isEmpty()) return;
|
||||
|
||||
m_tx->saveToFile(fn) ? QMessageBox::information(this, "Transaction saved to file", "Transaction saved successfully")
|
||||
: QMessageBox::warning(this, "Save transaction", "Failed to save transaction to file.");
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signedSaveFile() {
|
||||
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
|
||||
if(fn.isEmpty()) return;
|
||||
|
||||
m_tx->saveToFile(fn) ? QMessageBox::information(this, "Transaction saved to file", "Transaction saved successfully")
|
||||
: QMessageBox::warning(this, "Save transaction", "Failed to save transaction to file.");
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::unsignedQrCode() {
|
||||
if (m_tx->unsignedTxToBin().size() > 2953) {
|
||||
QMessageBox::warning(this, "Unable to show QR code", "Transaction size exceeds the maximum size for QR codes (2953 bytes).");
|
||||
return;
|
||||
}
|
||||
|
||||
QrCode qr(m_tx->unsignedTxToBin(), QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::LOW);
|
||||
auto *dialog = new QrCodeDialog(this, qr, "Unsigned Transaction");
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::unsignedCopy() {
|
||||
Utils::copyToClipboard(m_tx->unsignedTxToBase64());
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signedCopy() {
|
||||
Utils::copyToClipboard(m_tx->signedTxToHex(0));
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signedQrCode() {
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::broadcastTransaction() {
|
||||
if (m_tx == nullptr) return;
|
||||
m_ctx->currentWallet->commitTransactionAsync(m_tx);
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::closeDialog() {
|
||||
if (m_tx != nullptr)
|
||||
m_ctx->currentWallet->disposeTransaction(m_tx);
|
||||
if (m_utx != nullptr)
|
||||
m_ctx->currentWallet->disposeTransaction(m_utx);
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
TxConfAdvDialog::~TxConfAdvDialog() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_TXCONFADVDIALOG_H
|
||||
#define FEATHER_TXCONFADVDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QStandardItemModel>
|
||||
#include <QAbstractButton>
|
||||
#include <QMenu>
|
||||
|
||||
#include "libwalletqt/PendingTransaction.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
namespace Ui {
|
||||
class TxConfAdvDialog;
|
||||
}
|
||||
|
||||
class TxConfAdvDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TxConfAdvDialog(AppContext *ctx, const QString &description, QWidget *parent = nullptr);
|
||||
~TxConfAdvDialog() override;
|
||||
|
||||
void setTransaction(PendingTransaction *tx);
|
||||
void setUnsignedTransaction(UnsignedTransaction *utx);
|
||||
|
||||
private:
|
||||
void setupConstructionData(ConstructionInfo *ci);
|
||||
void signTransaction();
|
||||
void broadcastTransaction();
|
||||
void closeDialog();
|
||||
|
||||
void unsignedCopy();
|
||||
void unsignedQrCode();
|
||||
void unsignedSaveFile();
|
||||
|
||||
void signedCopy();
|
||||
void signedQrCode();
|
||||
void signedSaveFile();
|
||||
|
||||
Ui::TxConfAdvDialog *ui;
|
||||
AppContext *m_ctx;
|
||||
PendingTransaction *m_tx = nullptr;
|
||||
UnsignedTransaction *m_utx = nullptr;
|
||||
QMenu *m_exportUnsignedMenu;
|
||||
QMenu *m_exportSignedMenu;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //FEATHER_TXCONFADVDIALOG_H
|
||||
@@ -0,0 +1,279 @@
|
||||
<?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,12 +6,16 @@
|
||||
#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(PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent)
|
||||
TxConfDialog::TxConfDialog(AppContext *ctx, 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)
|
||||
@@ -28,44 +32,31 @@ TxConfDialog::TxConfDialog(PendingTransaction *tx, const QString &address, const
|
||||
};
|
||||
|
||||
QString amount = WalletManager::displayAmount(tx->amount());
|
||||
QString amount_fiat = convert(tx->amount() / AppContext::cdiv);
|
||||
QString amount_fiat = convert(tx->amount() / globals::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() / AppContext::cdiv);
|
||||
QString fee_fiat = convert(tx->fee() / globals::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()) / AppContext::cdiv);
|
||||
QString total_fiat = convert((tx->amount() + tx->fee()) / globals::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);
|
||||
|
||||
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::showAdvanced);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Send");
|
||||
|
||||
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
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);
|
||||
void TxConfDialog::setShowAdvanced() {
|
||||
this->showAdvanced = true;
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
TxConfDialog::~TxConfDialog() {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QDialog>
|
||||
#include "libwalletqt/PendingTransaction.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
namespace Ui {
|
||||
class TxConfDialog;
|
||||
@@ -17,13 +18,18 @@ class TxConfDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TxConfDialog(PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent = nullptr);
|
||||
explicit TxConfDialog(AppContext *ctx, PendingTransaction *tx, const QString &address, const QString &description, int mixin, QWidget *parent = nullptr);
|
||||
~TxConfDialog() override;
|
||||
|
||||
bool showAdvanced = false;
|
||||
|
||||
private:
|
||||
void showAdvanced();
|
||||
void setShowAdvanced();
|
||||
void saveToFile();
|
||||
void copyToClipboard();
|
||||
|
||||
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>655</width>
|
||||
<width>844</width>
|
||||
<height>248</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "tximportdialog.h"
|
||||
#include "ui_tximportdialog.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
TxImportDialog::TxImportDialog(QWidget *parent, AppContext *ctx)
|
||||
: QDialog(parent)
|
||||
, m_ctx(ctx)
|
||||
, m_loadTimer(new QTimer(this))
|
||||
, ui(new Ui::TxImportDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->resp->hide();
|
||||
ui->label_loading->hide();
|
||||
|
||||
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_load, &QPushButton::clicked, this, &TxImportDialog::loadTx);
|
||||
connect(ui->btn_import, &QPushButton::clicked, this, &TxImportDialog::onImport);
|
||||
|
||||
connect(m_rpc, &DaemonRpc::ApiResponse, this, &TxImportDialog::onApiResponse);
|
||||
|
||||
connect(m_loadTimer, &QTimer::timeout, [this]{
|
||||
ui->label_loading->setText(ui->label_loading->text() + ".");
|
||||
});
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void TxImportDialog::loadTx() {
|
||||
QString txid = ui->line_txid->text();
|
||||
QString node = m_ctx->nodes->connection().full;
|
||||
|
||||
if (!node.startsWith("http://"))
|
||||
node = QString("http://%1").arg(node);
|
||||
|
||||
m_rpc->setDaemonAddress(node);
|
||||
m_rpc->getTransactions(QStringList() << txid, false, true);
|
||||
|
||||
ui->label_loading->setText("Loading transaction");
|
||||
ui->label_loading->setHidden(false);
|
||||
m_loadTimer->start(1000);
|
||||
}
|
||||
|
||||
void TxImportDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) {
|
||||
m_loadTimer->stop();
|
||||
ui->label_loading->setHidden(true);
|
||||
if (!resp.ok) {
|
||||
QMessageBox::warning(this, "Import transaction", resp.status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resp.endpoint == DaemonRpc::Endpoint::GET_TRANSACTIONS) {
|
||||
ui->resp->setVisible(true);
|
||||
ui->resp->setPlainText(QJsonDocument(resp.obj).toJson(QJsonDocument::Indented));
|
||||
this->adjustSize();
|
||||
|
||||
if (resp.obj.contains("missed_tx")) {
|
||||
ui->btn_import->setEnabled(false);
|
||||
QMessageBox::warning(this, "Load transaction", "Transaction could not be found. Make sure the txid is correct, or try connecting to a different node.");
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::information(this, "Load transaction", "Transaction loaded successfully.\n\nAfter closing this message box click the Import button to import the transaction into your wallet.");
|
||||
m_transaction = resp.obj;
|
||||
ui->btn_import->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TxImportDialog::onImport() {
|
||||
QJsonObject tx = m_transaction.value("txs").toArray().first().toObject();
|
||||
|
||||
QString txid = tx.value("tx_hash").toString();
|
||||
|
||||
QVector<quint64> output_indices;
|
||||
for (const auto &o: tx.value("output_indices").toArray()) {
|
||||
output_indices.push_back(o.toInt());
|
||||
}
|
||||
|
||||
quint64 height = tx.value("block_height").toInt();
|
||||
quint64 timestamp = tx.value("block_timestamp").toInt();
|
||||
|
||||
bool pool = tx.value("in_pool").toBool();
|
||||
bool double_spend_seen = tx.value("double_spend_seen").toBool();
|
||||
|
||||
if (m_ctx->currentWallet->importTransaction(tx.value("tx_hash").toString(), output_indices, height, timestamp, false, pool, double_spend_seen)) {
|
||||
QMessageBox::information(this, "Import transaction", "Transaction imported successfully.");
|
||||
} else {
|
||||
QMessageBox::warning(this, "Import transaction", "Transaction import failed.");
|
||||
}
|
||||
m_ctx->refreshModels();
|
||||
}
|
||||
|
||||
TxImportDialog::~TxImportDialog() {
|
||||
delete ui;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_TXIMPORTDIALOG_H
|
||||
#define FEATHER_TXIMPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "appcontext.h"
|
||||
#include "utils/daemonrpc.h"
|
||||
|
||||
namespace Ui {
|
||||
class TxImportDialog;
|
||||
}
|
||||
|
||||
class TxImportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TxImportDialog(QWidget *parent, AppContext *ctx);
|
||||
~TxImportDialog() override;
|
||||
|
||||
private slots:
|
||||
void loadTx();
|
||||
void onImport();
|
||||
void onApiResponse(const DaemonRpc::DaemonResponse &resp);
|
||||
|
||||
private:
|
||||
Ui::TxImportDialog *ui;
|
||||
UtilsNetworking *m_network;
|
||||
AppContext *m_ctx;
|
||||
DaemonRpc *m_rpc;
|
||||
QTimer *m_loadTimer;
|
||||
|
||||
QJsonObject m_transaction;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_TXIMPORTDIALOG_H
|
||||
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TxImportDialog</class>
|
||||
<widget class="QDialog" name="TxImportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>700</width>
|
||||
<height>442</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>700</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Import Transaction</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_txid">
|
||||
<property name="placeholderText">
|
||||
<string>Transaction ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="resp">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Debug info..</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_load">
|
||||
<property name="text">
|
||||
<string>Load</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_import">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_loading">
|
||||
<property name="text">
|
||||
<string>Loading transaction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>TxImportDialog</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>TxImportDialog</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>
|
||||
@@ -16,26 +16,28 @@ VerifyProofDialog::VerifyProofDialog(Wallet *wallet, QWidget *parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->btn_verifySpendProof, &QPushButton::clicked, this, &VerifyProofDialog::checkSpendProof);
|
||||
connect(ui->btn_verifyOutProof, &QPushButton::clicked, this, &VerifyProofDialog::checkOutProof);
|
||||
connect(ui->btn_verifyInProof, &QPushButton::clicked, this, &VerifyProofDialog::checkInProof);
|
||||
connect(ui->btn_verify, &QPushButton::clicked, this, &VerifyProofDialog::checkProof);
|
||||
|
||||
connect(ui->btn_spendClear, &QPushButton::clicked, [this](){
|
||||
ui->lineEdit_spendTxID->clear();
|
||||
ui->lineEdit_spendMessage->clear();
|
||||
ui->input_SpendProof->clear();
|
||||
});
|
||||
connect(ui->btn_outClear, &QPushButton::clicked, [this](){
|
||||
ui->lineEdit_outTxID->clear();
|
||||
ui->lineEdit_outAddress->clear();
|
||||
ui->lineEdit_outMessage->clear();
|
||||
ui->input_OutProof->clear();
|
||||
});
|
||||
connect(ui->btn_inClear, &QPushButton::clicked, [this](){
|
||||
ui->lineEdit_inTxID->clear();
|
||||
ui->lineEdit_inAddress->clear();
|
||||
ui->lineEdit_inMessage->clear();
|
||||
ui->input_InProof->clear();
|
||||
connect(ui->btn_clear, &QPushButton::clicked, [this]{
|
||||
switch (ui->tabWidget->currentIndex()) {
|
||||
case 0:
|
||||
ui->lineEdit_spendTxID->clear();
|
||||
ui->lineEdit_spendMessage->clear();
|
||||
ui->input_SpendProof->clear();
|
||||
break;
|
||||
case 1:
|
||||
ui->lineEdit_outTxID->clear();
|
||||
ui->lineEdit_outAddress->clear();
|
||||
ui->lineEdit_outMessage->clear();
|
||||
ui->input_OutProof->clear();
|
||||
break;
|
||||
case 2:
|
||||
ui->lineEdit_inTxID->clear();
|
||||
ui->lineEdit_inAddress->clear();
|
||||
ui->lineEdit_inMessage->clear();
|
||||
ui->input_InProof->clear();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +46,20 @@ VerifyProofDialog::~VerifyProofDialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void VerifyProofDialog::checkProof() {
|
||||
switch (ui->tabWidget->currentIndex()) {
|
||||
case 0:
|
||||
this->checkSpendProof();
|
||||
break;
|
||||
case 1:
|
||||
this->checkOutProof();
|
||||
break;
|
||||
case 2:
|
||||
this->checkInProof();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyProofDialog::checkSpendProof() {
|
||||
auto r = m_wallet->checkSpendProof(ui->lineEdit_spendTxID->text(), ui->lineEdit_spendMessage->text(), ui->input_SpendProof->toPlainText());
|
||||
|
||||
|
||||
@@ -20,12 +20,13 @@ public:
|
||||
~VerifyProofDialog() override;
|
||||
|
||||
private slots:
|
||||
void checkSpendProof();
|
||||
void checkOutProof();
|
||||
void checkInProof();
|
||||
void checkProof();
|
||||
|
||||
private:
|
||||
void checkTxProof(const QString &txId, const QString &address, const QString &message, const QString &signature);
|
||||
void checkSpendProof();
|
||||
void checkOutProof();
|
||||
void checkInProof();
|
||||
|
||||
Ui::VerifyProofDialog *ui;
|
||||
Wallet *m_wallet;
|
||||
|
||||
@@ -39,17 +39,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Transaction ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_spendTxID"/>
|
||||
</item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
@@ -57,10 +47,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_spendTxID"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>SpendProof:</string>
|
||||
<string>Transaction ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -71,6 +64,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>SpendProof:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPlainTextEdit" name="input_SpendProof">
|
||||
<property name="overwriteMode">
|
||||
@@ -96,37 +96,6 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_spendClear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_verifySpendProof">
|
||||
<property name="text">
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="OutProof">
|
||||
@@ -213,37 +182,6 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</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_outClear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_verifyOutProof">
|
||||
<property name="text">
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="InProof">
|
||||
@@ -331,41 +269,44 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_inClear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_verifyInProof">
|
||||
<property name="text">
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_clear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_verify">
|
||||
<property name="text">
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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
|
||||
@@ -0,0 +1,139 @@
|
||||
<?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>
|
||||
@@ -31,7 +31,7 @@ WalletInfoDialog::WalletInfoDialog(AppContext *ctx, QWidget *parent)
|
||||
void WalletInfoDialog::openWalletDir() {
|
||||
QFileInfo file(m_ctx->walletPath);
|
||||
|
||||
QDesktopServices::openUrl(file.absolutePath());
|
||||
QDesktopServices::openUrl(QUrl(QString("file://%1").arg(file.absolutePath()), QUrl::TolerantMode));
|
||||
}
|
||||
|
||||
WalletInfoDialog::~WalletInfoDialog() {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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
|
||||
@@ -25,12 +25,17 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||
m_copyMenu->addAction("Transaction ID", this, [this]{copy(copyField::TxID);});
|
||||
m_copyMenu->addAction("Date", this, [this]{copy(copyField::Date);});
|
||||
m_copyMenu->addAction("Amount", this, [this]{copy(copyField::Amount);});
|
||||
m_copyMenu->addAction("Spend proof", this, &HistoryWidget::getSpendProof);
|
||||
auto spendProof = m_copyMenu->addAction("Spend proof", this, &HistoryWidget::getSpendProof);
|
||||
|
||||
ui->history->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->history, &QTreeView::customContextMenuRequested, [=](const QPoint & point){
|
||||
QModelIndex index = ui->history->indexAt(point);
|
||||
if (index.isValid()) {
|
||||
TransactionInfo::Direction direction;
|
||||
m_txHistory->transaction(m_model->mapToSource(index).row(), [&direction](TransactionInfo &tInfo) {
|
||||
direction = tInfo.direction();
|
||||
});
|
||||
spendProof->setVisible(direction == TransactionInfo::Direction_Out);
|
||||
m_contextMenu->exec(ui->history->viewport()->mapToGlobal(point));
|
||||
}
|
||||
});
|
||||
@@ -45,11 +50,11 @@ HistoryWidget::HistoryWidget(QWidget *parent)
|
||||
|
||||
}
|
||||
|
||||
void HistoryWidget::setModel(Coins *coins, TransactionHistoryProxyModel *model, TransactionHistory *txHistory)
|
||||
void HistoryWidget::setModel(TransactionHistoryProxyModel *model, Wallet *wallet)
|
||||
{
|
||||
m_coins = coins;
|
||||
m_model = model;
|
||||
m_txHistory = txHistory;
|
||||
m_wallet = wallet;
|
||||
m_txHistory = m_wallet->history();
|
||||
ui->history->setModel(m_model);
|
||||
|
||||
ui->history->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
@@ -66,7 +71,7 @@ void HistoryWidget::showTxDetails() {
|
||||
});
|
||||
|
||||
if (i != nullptr) {
|
||||
auto * dialog = new TransactionInfoDialog(m_coins, i, this);
|
||||
auto * dialog = new TransactionInfoDialog(m_wallet, i, this);
|
||||
dialog->exec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
#include "model/TransactionHistoryProxyModel.h"
|
||||
#include "libwalletqt/Coins.h"
|
||||
#include "libwalletqt/Wallet.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMenu>
|
||||
@@ -21,7 +22,7 @@ Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HistoryWidget(QWidget *parent = nullptr);
|
||||
void setModel(Coins * coins, TransactionHistoryProxyModel * model, TransactionHistory * txHistory);
|
||||
void setModel(TransactionHistoryProxyModel *model, Wallet *wallet);
|
||||
~HistoryWidget() override;
|
||||
|
||||
public slots:
|
||||
@@ -51,7 +52,7 @@ private:
|
||||
QMenu *m_copyMenu;
|
||||
TransactionHistory *m_txHistory;
|
||||
TransactionHistoryProxyModel *m_model;
|
||||
Coins *m_coins;
|
||||
Wallet *m_wallet = nullptr;
|
||||
};
|
||||
|
||||
#endif //FEATHER_HISTORYWIDGET_H
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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,10 +16,12 @@ QString PendingTransaction::errorString() const
|
||||
|
||||
bool PendingTransaction::commit()
|
||||
{
|
||||
// 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());
|
||||
return m_pimpl->commit();
|
||||
}
|
||||
|
||||
bool PendingTransaction::saveToFile(const QString &fileName)
|
||||
{
|
||||
return m_pimpl->commit(fileName.toStdString());
|
||||
}
|
||||
|
||||
quint64 PendingTransaction::amount() const
|
||||
@@ -37,7 +39,6 @@ quint64 PendingTransaction::fee() const
|
||||
return m_pimpl->fee();
|
||||
}
|
||||
|
||||
|
||||
QStringList PendingTransaction::txid() const
|
||||
{
|
||||
QStringList list;
|
||||
@@ -63,9 +64,33 @@ QList<QVariant> PendingTransaction::subaddrIndices() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void PendingTransaction::setFilename(const QString &fileName)
|
||||
QByteArray PendingTransaction::unsignedTxToBin() const {
|
||||
return QByteArray::fromStdString(m_pimpl->unsignedTxToBin());
|
||||
}
|
||||
|
||||
QString PendingTransaction::unsignedTxToBase64() const
|
||||
{
|
||||
m_fileName = 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));
|
||||
}
|
||||
}
|
||||
|
||||
PendingTransaction::PendingTransaction(Monero::PendingTransaction *pt, QObject *parent)
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QVariant>
|
||||
|
||||
#include <wallet/api/wallet2_api.h>
|
||||
#include "PendingTransactionInfo.h"
|
||||
|
||||
|
||||
//namespace Monero {
|
||||
//class PendingTransaction;
|
||||
@@ -30,7 +32,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)
|
||||
|
||||
@@ -45,21 +47,27 @@ 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;
|
||||
Q_INVOKABLE void setFilename(const QString &fileName);
|
||||
QByteArray unsignedTxToBin() const;
|
||||
QString unsignedTxToBase64() const;
|
||||
QString signedTxToHex(int index) const;
|
||||
void refresh();
|
||||
|
||||
PendingTransactionInfo * transaction(int index) const;
|
||||
|
||||
private:
|
||||
explicit PendingTransaction(Monero::PendingTransaction * pt, QObject *parent = 0);
|
||||
explicit PendingTransaction(Monero::PendingTransaction * pt, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
friend class Wallet;
|
||||
Monero::PendingTransaction * m_pimpl;
|
||||
QString m_fileName;
|
||||
mutable QList<PendingTransactionInfo*> m_pending_tx_info;
|
||||
};
|
||||
|
||||
#endif // PENDINGTRANSACTION_H
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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()))
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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
|
||||
@@ -28,6 +28,11 @@ bool TransactionInfo::isCoinbase() const
|
||||
return m_coinbase;
|
||||
}
|
||||
|
||||
quint64 TransactionInfo::balanceDelta() const
|
||||
{
|
||||
return m_amount + m_fee;
|
||||
}
|
||||
|
||||
double TransactionInfo::amount() const
|
||||
{
|
||||
// there's no unsigned uint64 for JS, so better use double
|
||||
@@ -44,6 +49,11 @@ QString TransactionInfo::displayAmount() const
|
||||
return WalletManager::displayAmount(m_amount);
|
||||
}
|
||||
|
||||
quint64 TransactionInfo::atomicFee() const
|
||||
{
|
||||
return m_fee;
|
||||
}
|
||||
|
||||
QString TransactionInfo::fee() const
|
||||
{
|
||||
if(m_fee == 0)
|
||||
@@ -127,6 +137,15 @@ QString TransactionInfo::destinations_formatted() const
|
||||
return destinations;
|
||||
}
|
||||
|
||||
QList<QString> TransactionInfo::destinations() const
|
||||
{
|
||||
QList<QString> dests;
|
||||
for (auto const& t: m_transfers) {
|
||||
dests.append(t->address());
|
||||
}
|
||||
return dests;
|
||||
}
|
||||
|
||||
QString TransactionInfo::rings_formatted() const
|
||||
{
|
||||
QString rings;
|
||||
|
||||
@@ -52,10 +52,12 @@ public:
|
||||
bool isPending() const;
|
||||
bool isFailed() const;
|
||||
bool isCoinbase() const;
|
||||
quint64 balanceDelta() const;
|
||||
double amount() const;
|
||||
quint64 atomicAmount() const;
|
||||
QString displayAmount() const;
|
||||
QString fee() const;
|
||||
quint64 atomicFee() const;
|
||||
quint64 blockHeight() const;
|
||||
QString description() const;
|
||||
QSet<quint32> subaddrIndex() const;
|
||||
@@ -72,6 +74,7 @@ public:
|
||||
QString paymentId() const;
|
||||
//! only applicable for output transactions
|
||||
//! used in tx details popup
|
||||
QList<QString> destinations() const;
|
||||
QString destinations_formatted() const;
|
||||
QString rings_formatted() const;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ 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,6 +83,21 @@ 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,14 +7,13 @@
|
||||
#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)
|
||||
@@ -41,15 +40,19 @@ 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 = 0);
|
||||
explicit UnsignedTransaction(Monero::UnsignedTransaction * pt, Monero::Wallet *walletImpl, QObject *parent = nullptr);
|
||||
~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,6 +2,11 @@
|
||||
// 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"
|
||||
@@ -27,6 +32,8 @@
|
||||
#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;
|
||||
@@ -36,10 +43,15 @@ namespace {
|
||||
}
|
||||
|
||||
Wallet::Wallet(QObject * parent)
|
||||
: Wallet(nullptr, parent)
|
||||
: Wallet(nullptr, parent)
|
||||
{
|
||||
}
|
||||
|
||||
Wallet::ConnectionStatus Wallet::connectionStatus() const
|
||||
{
|
||||
return m_connectionStatus;
|
||||
}
|
||||
|
||||
QString Wallet::getSeed() const
|
||||
{
|
||||
return QString::fromStdString(m_walletImpl->seed());
|
||||
@@ -65,41 +77,28 @@ NetworkType::Type Wallet::nettype() const
|
||||
return static_cast<NetworkType::Type>(m_walletImpl->nettype());
|
||||
}
|
||||
|
||||
|
||||
void Wallet::updateConnectionStatusAsync()
|
||||
{
|
||||
m_scheduler.run([this] {
|
||||
if (m_connectionStatus == Wallet::ConnectionStatus_Disconnected)
|
||||
{
|
||||
setConnectionStatus(ConnectionStatus_Connecting);
|
||||
}
|
||||
ConnectionStatus newStatus = static_cast<ConnectionStatus>(m_walletImpl->connected());
|
||||
if (newStatus != m_connectionStatus || !m_initialized) {
|
||||
m_initialized = true;
|
||||
setConnectionStatus(newStatus);
|
||||
}
|
||||
// Release lock
|
||||
m_connectionStatusRunning = false;
|
||||
});
|
||||
}
|
||||
|
||||
Wallet::ConnectionStatus Wallet::connected(bool forceCheck)
|
||||
{
|
||||
// cache connection status
|
||||
if (forceCheck || !m_initialized || (m_connectionStatusTime.elapsed() / 1000 > m_connectionStatusTtl && !m_connectionStatusRunning) || m_connectionStatusTime.elapsed() > 30000) {
|
||||
m_connectionStatusRunning = true;
|
||||
m_connectionStatusTime.restart();
|
||||
updateConnectionStatusAsync();
|
||||
}
|
||||
|
||||
return m_connectionStatus;
|
||||
}
|
||||
|
||||
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::setConnectionTimeout(int timeout) {
|
||||
m_connectionTimeout = timeout;
|
||||
}
|
||||
|
||||
void Wallet::setConnectionStatus(ConnectionStatus value)
|
||||
{
|
||||
if (m_connectionStatus == value)
|
||||
@@ -111,7 +110,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)
|
||||
{
|
||||
@@ -120,6 +119,29 @@ 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();
|
||||
@@ -135,6 +157,11 @@ bool Wallet::setPassword(const QString &password)
|
||||
return m_walletImpl->setPassword(password.toStdString());
|
||||
}
|
||||
|
||||
QString Wallet::getPassword()
|
||||
{
|
||||
return QString::fromStdString(m_walletImpl->getPassword());
|
||||
}
|
||||
|
||||
QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const
|
||||
{
|
||||
return QString::fromStdString(m_walletImpl->address(accountIndex, addressIndex));
|
||||
@@ -145,20 +172,18 @@ QString Wallet::path() const
|
||||
return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->path()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//void Wallet::storeAsync(const QVariant &callback, const QString &path /* = "" */)
|
||||
//void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */)
|
||||
//{
|
||||
// const auto future = m_scheduler.run(
|
||||
// [this, path] {
|
||||
// QMutexLocker locker(&m_storeMutex);
|
||||
|
||||
// return QVariantList({m_walletImpl->store(path.toStdString())});
|
||||
// },
|
||||
// callback);
|
||||
// [this, path] {
|
||||
// QMutexLocker locker(&m_asyncMutex);
|
||||
//
|
||||
// return QJSValueList({m_walletImpl->store(path.toStdString())});
|
||||
// },
|
||||
// callback);
|
||||
// if (!future.first)
|
||||
// {
|
||||
// QVariant(callback).call(QVariantList({false}));
|
||||
// QJSValue(callback).call(QJSValueList({false}));
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -167,7 +192,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)
|
||||
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight, const QString& proxyAddress)
|
||||
{
|
||||
qDebug() << "init non async";
|
||||
if (isRecovering){
|
||||
@@ -181,7 +206,20 @@ bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 uppe
|
||||
if (isRecovering || isRecoveringFromDevice) {
|
||||
m_walletImpl->setRefreshFromBlockHeight(restoreHeight);
|
||||
}
|
||||
m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString());
|
||||
|
||||
{
|
||||
QMutexLocker locker(&m_proxyMutex);
|
||||
|
||||
if (!m_walletImpl->init(daemonAddress.toStdString(), upperTransactionLimit, m_daemonUsername.toStdString(), m_daemonPassword.toStdString(), m_useSSL, false, proxyAddress.toStdString()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_proxyAddress = proxyAddress;
|
||||
}
|
||||
emit proxyAddressChanged();
|
||||
|
||||
setTrustedDaemon(trustedDaemon);
|
||||
setTrustedDaemon(trustedDaemon);
|
||||
return true;
|
||||
}
|
||||
@@ -193,17 +231,24 @@ void Wallet::setDaemonLogin(const QString &daemonUsername, const QString &daemon
|
||||
m_daemonPassword = daemonPassword;
|
||||
}
|
||||
|
||||
void Wallet::initAsync(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
|
||||
void Wallet::initAsync(
|
||||
const QString &daemonAddress,
|
||||
bool trustedDaemon /* = false */,
|
||||
quint64 upperTransactionLimit /* = 0 */,
|
||||
bool isRecovering /* = false */,
|
||||
bool isRecoveringFromDevice /* = false */,
|
||||
quint64 restoreHeight /* = 0 */,
|
||||
const QString &proxyAddress /* = "" */)
|
||||
{
|
||||
qDebug() << "initAsync: " + daemonAddress;
|
||||
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight] {
|
||||
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight);
|
||||
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress] {
|
||||
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress);
|
||||
if (success)
|
||||
{
|
||||
emit walletCreationHeightChanged();
|
||||
qDebug() << "init async finished - starting refresh";
|
||||
connected(true);
|
||||
m_walletImpl->startRefresh();
|
||||
refreshHeightAsync();
|
||||
startRefresh();
|
||||
}
|
||||
});
|
||||
if (future.first)
|
||||
@@ -246,6 +291,11 @@ void Wallet::setTrustedDaemon(bool arg)
|
||||
m_walletImpl->setTrustedDaemon(arg);
|
||||
}
|
||||
|
||||
void Wallet::setUseSSL(bool ssl)
|
||||
{
|
||||
m_useSSL = ssl;
|
||||
}
|
||||
|
||||
bool Wallet::viewOnly() const
|
||||
{
|
||||
return m_walletImpl->watchOnly();
|
||||
@@ -358,6 +408,8 @@ void Wallet::refreshHeightAsync()
|
||||
daemonHeightFuture.second.waitForFinished();
|
||||
targetHeightFuture.second.waitForFinished();
|
||||
|
||||
setConnectionStatus(ConnectionStatus_Connected);
|
||||
|
||||
emit heightRefreshed(walletHeight, daemonHeight, targetHeight);
|
||||
});
|
||||
}
|
||||
@@ -372,7 +424,8 @@ 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();
|
||||
}
|
||||
@@ -382,7 +435,8 @@ 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.
|
||||
@@ -396,9 +450,9 @@ quint64 Wallet::daemonBlockChainTargetHeight() const
|
||||
return m_daemonBlockChainTargetHeight;
|
||||
}
|
||||
|
||||
bool Wallet::exportKeyImages(const QString& path)
|
||||
bool Wallet::exportKeyImages(const QString& path, bool all)
|
||||
{
|
||||
return m_walletImpl->exportKeyImages(path.toStdString());
|
||||
return m_walletImpl->exportKeyImages(path.toStdString(), all);
|
||||
}
|
||||
|
||||
bool Wallet::importKeyImages(const QString& path)
|
||||
@@ -406,42 +460,40 @@ bool Wallet::importKeyImages(const QString& path)
|
||||
return m_walletImpl->importKeyImages(path.toStdString());
|
||||
}
|
||||
|
||||
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::exportOutputs(const QString& path, bool all) {
|
||||
return m_walletImpl->exportOutputs(path.toStdString(), all);
|
||||
}
|
||||
|
||||
void Wallet::refreshAsync()
|
||||
{
|
||||
qDebug() << "refresh async";
|
||||
m_walletImpl->refreshAsync();
|
||||
bool Wallet::importOutputs(const QString& path) {
|
||||
return m_walletImpl->importOutputs(path.toStdString());
|
||||
}
|
||||
|
||||
void Wallet::setAutoRefreshInterval(int seconds)
|
||||
{
|
||||
m_walletImpl->setAutoRefreshInterval(seconds);
|
||||
bool Wallet::importTransaction(const QString& txid, const QVector<quint64>& output_indeces, quint64 height, quint64 timestamp, bool miner_tx, bool pool, bool double_spend_seen) {
|
||||
std::vector<uint64_t> o_indeces;
|
||||
for (const auto &o : output_indeces) {
|
||||
o_indeces.push_back(o);
|
||||
}
|
||||
|
||||
return m_walletImpl->importTransaction(
|
||||
txid.toStdString(),
|
||||
o_indeces,
|
||||
height,
|
||||
17, // todo: get actual block_version
|
||||
timestamp,
|
||||
miner_tx,
|
||||
pool,
|
||||
double_spend_seen);
|
||||
}
|
||||
|
||||
int Wallet::autoRefreshInterval() const
|
||||
void Wallet::startRefresh()
|
||||
{
|
||||
return m_walletImpl->autoRefreshInterval();
|
||||
m_refreshEnabled = true;
|
||||
m_refreshNow = true;
|
||||
}
|
||||
|
||||
void Wallet::startRefresh() const
|
||||
void Wallet::pauseRefresh()
|
||||
{
|
||||
m_walletImpl->startRefresh();
|
||||
}
|
||||
|
||||
void Wallet::pauseRefresh() const
|
||||
{
|
||||
m_walletImpl->pauseRefresh();
|
||||
m_refreshEnabled = false;
|
||||
}
|
||||
|
||||
PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id,
|
||||
@@ -450,15 +502,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);
|
||||
@@ -471,15 +523,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);
|
||||
@@ -501,7 +553,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, "", 0); // todo: return true mixincount
|
||||
emit transactionCreated(tx, dst_addr, "", 10); // todo: return true mixincount
|
||||
});
|
||||
}
|
||||
|
||||
@@ -528,6 +580,21 @@ 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;
|
||||
@@ -537,6 +604,28 @@ 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();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void Wallet::commitTransactionAsync(PendingTransaction *t)
|
||||
{
|
||||
m_scheduler.run([this, t] {
|
||||
@@ -556,7 +645,6 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
|
||||
delete t;
|
||||
}
|
||||
|
||||
// @ TODO: QJSValue could probably be QVARIANT
|
||||
//void Wallet::estimateTransactionFeeAsync(const QString &destination,
|
||||
// quint64 amount,
|
||||
// PendingTransaction::Priority priority,
|
||||
@@ -564,8 +652,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);
|
||||
//}
|
||||
@@ -683,17 +771,17 @@ bool Wallet::setCacheAttribute(const QString &key, const QString &val)
|
||||
|
||||
bool Wallet::setUserNote(const QString &txid, const QString ¬e)
|
||||
{
|
||||
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)
|
||||
@@ -713,12 +801,10 @@ QString Wallet::checkTxKey(const QString &txid, const QString &tx_key, const QSt
|
||||
return QString::fromStdString(result);
|
||||
}
|
||||
|
||||
QString Wallet::getTxProof(const QString &txid, const QString &address, const QString &message) const
|
||||
TxProof Wallet::getTxProof(const QString &txid, const QString &address, const QString &message) const
|
||||
{
|
||||
std::string result = m_walletImpl->getTxProof(txid.toStdString(), address.toStdString(), message.toStdString());
|
||||
if (result.empty())
|
||||
result = "error|" + m_walletImpl->errorString();
|
||||
return QString::fromStdString(result);
|
||||
return TxProof(QString::fromStdString(result), QString::fromStdString(m_walletImpl->errorString()));
|
||||
}
|
||||
|
||||
//void Wallet::getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback)
|
||||
@@ -738,12 +824,10 @@ TxProofResult Wallet::checkTxProof(const QString &txid, const QString &address,
|
||||
return {success, good, received, in_pool, confirmations};
|
||||
}
|
||||
|
||||
Q_INVOKABLE QString Wallet::getSpendProof(const QString &txid, const QString &message) const
|
||||
Q_INVOKABLE TxProof Wallet::getSpendProof(const QString &txid, const QString &message) const
|
||||
{
|
||||
std::string result = m_walletImpl->getSpendProof(txid.toStdString(), message.toStdString());
|
||||
if (result.empty())
|
||||
result = "error|" + m_walletImpl->errorString();
|
||||
return QString::fromStdString(result);
|
||||
return TxProof(QString::fromStdString(result), QString::fromStdString(m_walletImpl->errorString()));
|
||||
}
|
||||
|
||||
//void Wallet::getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback)
|
||||
@@ -831,7 +915,6 @@ 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;
|
||||
@@ -852,6 +935,8 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id
|
||||
|
||||
bool Wallet::rescanSpent()
|
||||
{
|
||||
QMutexLocker locker(&m_asyncMutex);
|
||||
|
||||
return m_walletImpl->rescanSpent();
|
||||
}
|
||||
|
||||
@@ -928,8 +1013,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;
|
||||
@@ -949,8 +1034,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;
|
||||
}
|
||||
}
|
||||
@@ -964,9 +1049,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);
|
||||
@@ -992,6 +1077,14 @@ void Wallet::onWalletPassphraseNeeded(bool on_device)
|
||||
emit this->walletPassphraseNeeded(on_device);
|
||||
}
|
||||
|
||||
quint64 Wallet::getBytesReceived() const {
|
||||
return m_walletImpl->getBytesReceived();
|
||||
}
|
||||
|
||||
quint64 Wallet::getBytesSent() const {
|
||||
return m_walletImpl->getBytesSent();
|
||||
}
|
||||
|
||||
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
if (m_walletListener != nullptr)
|
||||
@@ -1001,26 +1094,30 @@ 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_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_refreshNow(false)
|
||||
, m_refreshEnabled(false)
|
||||
, m_refreshing(false)
|
||||
, m_scheduler(this)
|
||||
, m_useSSL(true)
|
||||
{
|
||||
m_history = new TransactionHistory(m_walletImpl->history(), this);
|
||||
m_addressBook = new AddressBook(m_walletImpl->addressBook(), this);
|
||||
@@ -1038,11 +1135,16 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||
m_connectionStatusRunning = false;
|
||||
m_daemonUsername = "";
|
||||
m_daemonPassword = "";
|
||||
|
||||
startRefreshThread();
|
||||
}
|
||||
|
||||
Wallet::~Wallet()
|
||||
{
|
||||
qDebug() << "~Wallet: Closing wallet";
|
||||
qDebug("~Wallet: Closing wallet");
|
||||
|
||||
pauseRefresh();
|
||||
m_walletImpl->stop();
|
||||
|
||||
m_scheduler.shutdownWaitForFinished();
|
||||
|
||||
@@ -1062,13 +1164,52 @@ Wallet::~Wallet()
|
||||
//Monero::WalletManagerFactory::getWalletManager()->closeWallet(m_walletImpl);
|
||||
if(status() == Status_Critical)
|
||||
qDebug("Not storing wallet cache");
|
||||
else if( m_walletImpl->store(""))
|
||||
qDebug("Wallet cache stored successfully");
|
||||
else
|
||||
qDebug("Error 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");
|
||||
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 || m_refreshNow)
|
||||
{
|
||||
m_refreshNow = false;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void Wallet::onRefreshed(bool success) {
|
||||
if (success) {
|
||||
setConnectionStatus(ConnectionStatus_Connected);
|
||||
} else {
|
||||
setConnectionStatus(ConnectionStatus_Disconnected);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
#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;
|
||||
@@ -20,14 +19,20 @@
|
||||
#include "WalletListenerImpl.h"
|
||||
|
||||
namespace Monero {
|
||||
struct Wallet; // forward declaration
|
||||
struct Wallet; // forward declaration
|
||||
}
|
||||
|
||||
struct TxProof {
|
||||
TxProof(QString proof, QString error = "")
|
||||
: proof(std::move(proof)), error(std::move(error)){}
|
||||
|
||||
QString proof;
|
||||
QString error;
|
||||
};
|
||||
|
||||
class TransactionHistory;
|
||||
class TransactionHistoryModel;
|
||||
class TransactionHistoryProxyModel;
|
||||
class TransactionHistorySortFilterModel;
|
||||
class AddressBook;
|
||||
class AddressBookModel;
|
||||
class Subaddress;
|
||||
@@ -50,13 +55,14 @@ 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 connectionStatus READ connectionStatus)
|
||||
Q_PROPERTY(quint32 currentSubaddressAccount READ currentSubaddressAccount NOTIFY currentSubaddressAccountChanged)
|
||||
Q_PROPERTY(bool synchronized READ synchronized)
|
||||
Q_PROPERTY(QString errorString READ errorString)
|
||||
@@ -76,6 +82,7 @@ class Wallet : public QObject, public PassprasePrompter
|
||||
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:
|
||||
@@ -98,6 +105,9 @@ public:
|
||||
|
||||
Q_ENUM(ConnectionStatus)
|
||||
|
||||
//! return connection status
|
||||
ConnectionStatus connectionStatus() const;
|
||||
|
||||
//! returns mnemonic seed
|
||||
QString getSeed() const;
|
||||
|
||||
@@ -113,19 +123,19 @@ public:
|
||||
//! returns network type of the wallet.
|
||||
NetworkType::Type nettype() const;
|
||||
|
||||
//! returns whether the wallet is connected, and version status
|
||||
Q_INVOKABLE ConnectionStatus connected(bool forceCheck = false);
|
||||
void updateConnectionStatusAsync();
|
||||
|
||||
//! returns true if wallet was ever synchronized
|
||||
bool synchronized() const;
|
||||
|
||||
|
||||
//! returns last operation's error message
|
||||
QString errorString() const;
|
||||
|
||||
//! changes the password using existing parameters (path, seed, seed lang)
|
||||
Q_INVOKABLE bool setPassword(const QString &password);
|
||||
|
||||
//! get current wallet password
|
||||
Q_INVOKABLE QString getPassword();
|
||||
|
||||
//! returns wallet's public address
|
||||
Q_INVOKABLE QString address(quint32 accountIndex, quint32 addressIndex) const;
|
||||
|
||||
@@ -138,7 +148,14 @@ 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);
|
||||
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 = "");
|
||||
|
||||
// Set daemon rpc user/pass
|
||||
Q_INVOKABLE void setDaemonLogin(const QString &daemonUsername = "", const QString &daemonPassword = "");
|
||||
@@ -149,9 +166,15 @@ public:
|
||||
//! connects to daemon
|
||||
Q_INVOKABLE bool connectToDaemon();
|
||||
|
||||
//! indicates id daemon is trusted
|
||||
//! set connect to daemon timeout
|
||||
Q_INVOKABLE void setConnectionTimeout(int timeout);
|
||||
|
||||
//! indicates if daemon is trusted
|
||||
Q_INVOKABLE void setTrustedDaemon(bool arg);
|
||||
|
||||
//! indicates if ssl should be used to connect to daemon
|
||||
Q_INVOKABLE void setUseSSL(bool ssl);
|
||||
|
||||
//! returns balance
|
||||
Q_INVOKABLE quint64 balance() const;
|
||||
Q_INVOKABLE quint64 balance(quint32 accountIndex) const;
|
||||
@@ -184,24 +207,22 @@ public:
|
||||
Q_INVOKABLE void refreshHeightAsync();
|
||||
|
||||
//! export/import key images
|
||||
Q_INVOKABLE bool exportKeyImages(const QString& path);
|
||||
Q_INVOKABLE bool exportKeyImages(const QString& path, bool all = false);
|
||||
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);
|
||||
|
||||
//! import a transaction
|
||||
Q_INVOKABLE bool importTransaction(const QString& txid, const QVector<quint64>& output_indeces, quint64 height, quint64 timestamp, bool miner_tx, bool pool, bool double_spend_seen);
|
||||
|
||||
//! refreshes the wallet
|
||||
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;
|
||||
Q_INVOKABLE bool refresh(bool historyAndSubaddresses = false);
|
||||
|
||||
// pause/resume refresh
|
||||
Q_INVOKABLE void startRefresh() const;
|
||||
Q_INVOKABLE void pauseRefresh() const;
|
||||
Q_INVOKABLE void startRefresh();
|
||||
Q_INVOKABLE void pauseRefresh();
|
||||
|
||||
//! returns current wallet's block height
|
||||
//! (can be less than daemon's blockchain height when wallet sync in progress)
|
||||
@@ -225,7 +246,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,
|
||||
@@ -248,6 +269,12 @@ 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;
|
||||
|
||||
@@ -327,11 +354,11 @@ public:
|
||||
Q_INVOKABLE QString getTxKey(const QString &txid) const;
|
||||
//Q_INVOKABLE void getTxKeyAsync(const QString &txid, const QJSValue &callback);
|
||||
Q_INVOKABLE QString checkTxKey(const QString &txid, const QString &tx_key, const QString &address);
|
||||
Q_INVOKABLE QString getTxProof(const QString &txid, const QString &address, const QString &message) const;
|
||||
Q_INVOKABLE TxProof getTxProof(const QString &txid, const QString &address, const QString &message) const;
|
||||
// Q_INVOKABLE void getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback);
|
||||
//Q_INVOKABLE QString checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature);
|
||||
Q_INVOKABLE TxProofResult checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature);
|
||||
Q_INVOKABLE QString getSpendProof(const QString &txid, const QString &message) const;
|
||||
Q_INVOKABLE TxProof getSpendProof(const QString &txid, const QString &message) const;
|
||||
// Q_INVOKABLE void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback);
|
||||
Q_INVOKABLE QPair<bool, bool> checkSpendProof(const QString &txid, const QString &message, const QString &signature) const;
|
||||
// Rescan spent outputs
|
||||
@@ -372,6 +399,9 @@ public:
|
||||
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
||||
virtual void onWalletPassphraseNeeded(bool on_device) override;
|
||||
|
||||
Q_INVOKABLE quint64 getBytesReceived() const;
|
||||
Q_INVOKABLE quint64 getBytesSent() const;
|
||||
|
||||
// TODO: setListenter() when it implemented in API
|
||||
signals:
|
||||
// emitted on every event happened with wallet
|
||||
@@ -380,7 +410,7 @@ signals:
|
||||
|
||||
// emitted when refresh process finished (could take a long time)
|
||||
// signalling only after we
|
||||
void refreshed();
|
||||
void refreshed(bool success);
|
||||
|
||||
void moneySpent(const QString &txId, quint64 amount);
|
||||
void moneyReceived(const QString &txId, quint64 amount);
|
||||
@@ -402,6 +432,8 @@ signals:
|
||||
void connectionStatusChanged(int status) const;
|
||||
void currentSubaddressAccountChanged() const;
|
||||
void disconnectedChanged() const;
|
||||
void proxyAddressChanged() const;
|
||||
void refreshingChanged() const;
|
||||
|
||||
private:
|
||||
Wallet(QObject * parent = nullptr);
|
||||
@@ -409,10 +441,24 @@ private:
|
||||
~Wallet();
|
||||
|
||||
//! initializes wallet
|
||||
bool init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight);
|
||||
bool init(
|
||||
const QString &daemonAddress,
|
||||
bool trustedDaemon,
|
||||
quint64 upperTransactionLimit,
|
||||
bool isRecovering,
|
||||
bool isRecoveringFromDevice,
|
||||
quint64 restoreHeight,
|
||||
const QString& proxyAddress);
|
||||
|
||||
bool disconnected() const;
|
||||
bool refreshing() const;
|
||||
void refreshingSet(bool value);
|
||||
void onRefreshed(bool success);
|
||||
|
||||
void setConnectionStatus(ConnectionStatus value);
|
||||
QString getProxyAddress() const;
|
||||
void setProxyAddress(QString address);
|
||||
void startRefreshThread();
|
||||
|
||||
private:
|
||||
friend class WalletManager;
|
||||
@@ -445,13 +491,20 @@ 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_refreshNow;
|
||||
std::atomic<bool> m_refreshEnabled;
|
||||
std::atomic<bool> m_refreshing;
|
||||
WalletListenerImpl *m_walletListener;
|
||||
FutureScheduler m_scheduler;
|
||||
QMutex m_storeMutex;
|
||||
int m_connectionTimeout = 30;
|
||||
bool m_useSSL;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -41,10 +41,11 @@ void WalletListenerImpl::updated()
|
||||
}
|
||||
|
||||
// called when wallet refreshed by background thread or explicitly
|
||||
void WalletListenerImpl::refreshed()
|
||||
void WalletListenerImpl::refreshed(bool success)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->refreshed();
|
||||
m_wallet->onRefreshed(success);
|
||||
emit m_wallet->refreshed(success);
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
virtual void updated() override;
|
||||
|
||||
// called when wallet refreshed by background thread or explicitly
|
||||
virtual void refreshed() override;
|
||||
virtual void refreshed(bool success) override;
|
||||
|
||||
virtual void onDeviceButtonRequest(uint64_t code) override;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
||||
virtual void newBlock(uint64_t height) override { (void) height; };
|
||||
virtual void updated() override {};
|
||||
virtual void refreshed() override {};
|
||||
virtual void refreshed(bool success) override {};
|
||||
|
||||
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override
|
||||
{
|
||||
@@ -335,7 +335,7 @@ bool WalletManager::isMining() const
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (m_currentWallet == nullptr || !m_currentWallet->connected())
|
||||
if (m_currentWallet == nullptr || !m_currentWallet->connectionStatus())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE(assets);
|
||||
|
||||
#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);
|
||||
#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);
|
||||
#endif
|
||||
|
||||
QStringList argv_;
|
||||
|
||||
@@ -13,11 +13,17 @@
|
||||
#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 "dialog/tximportdialog.h"
|
||||
#include "dialog/passworddialog.h"
|
||||
#include "utils/utils.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/daemonrpc.h"
|
||||
#include "components.h"
|
||||
#include "calcwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
@@ -99,8 +105,6 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
});
|
||||
connect(ui->actionShow_debug_info, &QAction::triggered, this, &MainWindow::showDebugInfo);
|
||||
connect(ui->actionOfficialWebsite, &QAction::triggered, [=] { Utils::externalLinkWarning("https://featherwallet.org"); });
|
||||
connect(ui->actionAbout_Qt, &QAction::triggered, [=] { QApplication::aboutQt(); });
|
||||
|
||||
|
||||
#if defined(XMRTO)
|
||||
// xmr.to connects/widget
|
||||
@@ -114,9 +118,12 @@ 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(5, false);
|
||||
ui->tabWidget->setTabVisible(Tabs::XMR_TO, false);
|
||||
#endif
|
||||
|
||||
#ifndef HAS_MORPHTOKEN
|
||||
ui->tabWidget->setTabVisible(Tabs::MORPHTOKEN, false);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
// system tray
|
||||
@@ -165,6 +172,24 @@ 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);
|
||||
@@ -255,6 +280,7 @@ 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);
|
||||
@@ -275,13 +301,13 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
});
|
||||
connect(ui->receiveWidget, &ReceiveWidget::showTransactions, [this](const QString &text) {
|
||||
ui->historyWidget->setSearchText(text);
|
||||
ui->tabWidget->setCurrentIndex(1); //history
|
||||
ui->tabWidget->setCurrentIndex(Tabs::HISTORY);
|
||||
});
|
||||
|
||||
// History
|
||||
connect(ui->historyWidget, &HistoryWidget::spendProof, [&](const QString &txid){
|
||||
QString spendProof = m_ctx->currentWallet->getSpendProof(txid, "");
|
||||
Utils::copyToClipboard(spendProof);
|
||||
TxProof txproof = m_ctx->currentWallet->getSpendProof(txid, "");
|
||||
Utils::copyToClipboard(txproof.proof);
|
||||
});
|
||||
connect(ui->historyWidget, &HistoryWidget::viewOnBlockExplorer, this, &MainWindow::onViewOnBlockExplorer);
|
||||
|
||||
@@ -323,12 +349,26 @@ MainWindow::MainWindow(AppContext *ctx, QWidget *parent) :
|
||||
connect(ui->coinsWidget, &CoinsWidget::sweepOutput, m_ctx, &AppContext::onSweepOutput);
|
||||
|
||||
connect(m_ctx, &AppContext::walletClosing, [=]{
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
ui->tabWidget->setCurrentIndex(Tabs::HOME);
|
||||
});
|
||||
|
||||
// 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();
|
||||
this->initMenu();
|
||||
|
||||
connect(&m_updateBytes, &QTimer::timeout, this, &MainWindow::updateNetStats);
|
||||
}
|
||||
|
||||
void MainWindow::initMain() {
|
||||
@@ -356,25 +396,51 @@ void MainWindow::initMain() {
|
||||
this->show();
|
||||
m_wizard = this->createWizard(WalletWizard::Page_Menu);
|
||||
m_wizard->show();
|
||||
this->touchbarShowWizard();
|
||||
}
|
||||
|
||||
void MainWindow::initMenu() {
|
||||
// setup shortcuts
|
||||
ui->actionStore_wallet->setShortcut(QKeySequence("Ctrl+S"));
|
||||
ui->actionRefresh_tabs->setShortcut(QKeySequence("Ctrl+R"));
|
||||
ui->actionClose->setShortcut(QKeySequence("Ctrl+W"));
|
||||
ui->actionShow_debug_info->setShortcut(QKeySequence("Ctrl+D"));
|
||||
ui->actionSettings->setShortcut(QKeySequence("Ctrl+Alt+S"));
|
||||
|
||||
// hide/show tabs
|
||||
m_tabShowHideSignalMapper = new QSignalMapper(this);
|
||||
|
||||
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(HAS_MORPHTOKEN)
|
||||
connect(ui->actionShow_MorphToken, &QAction::triggered, m_tabShowHideSignalMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
m_tabShowHideMapper["MorphToken"] = new ToggleTab(ui->tabMorphToken, "MorphToken", "MorphToken", ui->actionShow_MorphToken, Config::showTabMorphToken);
|
||||
m_tabShowHideSignalMapper->setMapping(ui->actionShow_MorphToken, "MorphToken");
|
||||
#else
|
||||
ui->actionShow_MorphToken->setVisible(false);
|
||||
#endif
|
||||
|
||||
#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
|
||||
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(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
|
||||
|
||||
for (const auto &key: m_tabShowHideMapper.keys()) {
|
||||
const auto toggleTab = m_tabShowHideMapper.value(key);
|
||||
@@ -389,6 +455,7 @@ 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();
|
||||
});
|
||||
@@ -398,6 +465,10 @@ 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);
|
||||
@@ -412,7 +483,8 @@ 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.startsWith(".csv")) fn += ".csv";
|
||||
if(fn.isEmpty()) return;
|
||||
if(!fn.endsWith(".csv")) fn += ".csv";
|
||||
m_ctx->currentWallet->history()->writeCSV(fn);
|
||||
Utils::showMessageBox("CSV export", QString("Transaction history exported to %1").arg(fn), false);
|
||||
});
|
||||
@@ -435,9 +507,16 @@ 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);
|
||||
connect(ui->actionImport_transaction, &QAction::triggered, this, &MainWindow::importTransaction);
|
||||
|
||||
// About screen
|
||||
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::menuAboutClicked);
|
||||
@@ -477,7 +556,11 @@ WalletWizard *MainWindow::createWizard(WalletWizard::Page startPage){
|
||||
|
||||
void MainWindow::showWizard(WalletWizard::Page startPage) {
|
||||
this->setEnabled(false);
|
||||
m_wizard = this->createWizard(startPage);
|
||||
if (m_wizard == nullptr)
|
||||
m_wizard = this->createWizard(startPage);
|
||||
m_wizard->setStartId(startPage);
|
||||
m_wizard->restart();
|
||||
m_wizard->setEnabled(true);
|
||||
m_wizard->show();
|
||||
}
|
||||
|
||||
@@ -493,24 +576,41 @@ 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) {
|
||||
QInputDialog passwordDialog(this);
|
||||
passwordDialog.setInputMode(QInputDialog::TextInput);
|
||||
passwordDialog.setTextEchoMode(QLineEdit::Password);
|
||||
passwordDialog.setWindowTitle("Password required");
|
||||
passwordDialog.setLabelText("Please enter wallet password.");
|
||||
passwordDialog.resize(300, 100);
|
||||
if(!(bool)passwordDialog.exec())
|
||||
return this->showWizard(WalletWizard::Page_OpenWallet);
|
||||
void MainWindow::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
||||
QFileInfo fileInfo(path);
|
||||
|
||||
const auto passwd = passwordDialog.textValue();
|
||||
m_ctx->walletPassword = passwd;
|
||||
auto dialog = new PasswordDialog(this, fileInfo.fileName(), invalidPassword);
|
||||
switch (dialog->exec()) {
|
||||
case QDialog::Rejected:
|
||||
{
|
||||
this->showWizard(WalletWizard::Page_OpenWallet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_ctx->walletPassword = dialog->password;
|
||||
m_ctx->onOpenWallet(m_ctx->walletPath, m_ctx->walletPassword);
|
||||
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void MainWindow::onWalletOpenedError(const QString &err) {
|
||||
@@ -518,6 +618,7 @@ 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) {
|
||||
@@ -530,8 +631,6 @@ void MainWindow::onWalletOpened() {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
if(m_wizard != nullptr) {
|
||||
m_wizard->hide();
|
||||
m_wizard->disconnect();
|
||||
m_wizard->deleteLater();
|
||||
}
|
||||
|
||||
this->raise();
|
||||
@@ -543,10 +642,6 @@ 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
|
||||
@@ -563,7 +658,7 @@ void MainWindow::onWalletOpened() {
|
||||
|
||||
// history page
|
||||
m_ctx->currentWallet->history()->refresh(m_ctx->currentWallet->currentSubaddressAccount());
|
||||
ui->historyWidget->setModel(m_ctx->currentWallet->coins(), m_ctx->currentWallet->historyModel(), m_ctx->currentWallet->history());
|
||||
ui->historyWidget->setModel(m_ctx->currentWallet->historyModel(), m_ctx->currentWallet);
|
||||
connect(m_ctx->currentWallet->history(), &TransactionHistory::txNoteChanged, [this]{
|
||||
m_ctx->storeWallet();
|
||||
});
|
||||
@@ -583,17 +678,31 @@ void MainWindow::onWalletOpened() {
|
||||
connect(m_ctx->currentWallet->coins(), &Coins::coinThawed, [this]{
|
||||
m_ctx->storeWallet();
|
||||
});
|
||||
|
||||
this->touchbarShowWallet();
|
||||
this->updatePasswordIcon();
|
||||
|
||||
m_updateBytes.start(1000);
|
||||
}
|
||||
|
||||
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() {
|
||||
this->updateNetStats();
|
||||
m_statusLabelStatus->setText("Synchronized");
|
||||
this->onConnectionStatusChanged(Wallet::ConnectionStatus_Connected);
|
||||
}
|
||||
@@ -606,11 +715,12 @@ void MainWindow::onBlockchainSync(int height, int target) {
|
||||
void MainWindow::onRefreshSync(int height, int target) {
|
||||
QString heightText = QString("Wallet refresh: %1/%2").arg(height).arg(target);
|
||||
m_statusLabelStatus->setText(heightText);
|
||||
this->updateNetStats();
|
||||
}
|
||||
|
||||
void MainWindow::onConnectionStatusChanged(int status)
|
||||
{
|
||||
qDebug() << "Wallet connection status changed " << status;
|
||||
qDebug() << "Wallet connection status changed " << Utils::QtEnumToString(static_cast<Wallet::ConnectionStatus>(status));
|
||||
|
||||
// Update connection info in status bar.
|
||||
|
||||
@@ -652,7 +762,7 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QStrin
|
||||
auto tx_err = tx->errorString();
|
||||
qCritical() << tx_err;
|
||||
|
||||
if(m_ctx->currentWallet->connected() == Wallet::ConnectionStatus_WrongVersion)
|
||||
if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_WrongVersion)
|
||||
err = QString("%1 Wrong daemon version: %2").arg(err).arg(tx_err);
|
||||
else
|
||||
err = QString("%1 %2").arg(err).arg(tx_err);
|
||||
@@ -668,15 +778,26 @@ void MainWindow::onCreateTransactionSuccess(PendingTransaction *tx, const QStrin
|
||||
} else {
|
||||
const auto &description = m_ctx->tmpTxDescription;
|
||||
|
||||
auto *dialog = new TxConfDialog(tx, address, description, mixin, this);
|
||||
auto *dialog = new TxConfDialog(m_ctx, tx, address, description, mixin, this);
|
||||
switch (dialog->exec()) {
|
||||
case QDialog::Rejected:
|
||||
m_ctx->onCancelTransaction(tx, address);
|
||||
{
|
||||
if (!dialog->showAdvanced)
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,6 +842,10 @@ void MainWindow::create_status_bar() {
|
||||
m_statusLabelStatus->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
this->statusBar()->addWidget(m_statusLabelStatus);
|
||||
|
||||
m_statusLabelNetStats = new QLabel("", this);
|
||||
m_statusLabelNetStats->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
this->statusBar()->addWidget(m_statusLabelNetStats);
|
||||
|
||||
m_statusLabelBalance = new QLabel("Balance: 0.00 XMR", this);
|
||||
m_statusLabelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
this->statusBar()->addPermanentWidget(m_statusLabelBalance);
|
||||
@@ -753,18 +878,13 @@ void MainWindow::showWalletInfoDialog() {
|
||||
}
|
||||
|
||||
void MainWindow::showSeedDialog() {
|
||||
QString seed = m_ctx->currentWallet->getCacheAttribute("feather.seed");
|
||||
if (seed.isEmpty()) {
|
||||
seed = m_ctx->currentWallet->getSeed();
|
||||
}
|
||||
|
||||
auto *dialog = new SeedDialog(seed, this);
|
||||
auto *dialog = new SeedDialog(m_ctx->currentWallet, this);
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void MainWindow::showConnectionStatusDialog() {
|
||||
auto status = m_ctx->currentWallet->connected(true);
|
||||
auto status = m_ctx->currentWallet->connectionStatus();
|
||||
bool synchronized = m_ctx->currentWallet->synchronized();
|
||||
|
||||
QString statusMsg;
|
||||
@@ -791,32 +911,22 @@ void MainWindow::showConnectionStatusDialog() {
|
||||
statusMsg = "Unknown connection status (this should never happen).";
|
||||
}
|
||||
|
||||
statusMsg += QString("\n\nTx: %1, Rx: %2").arg(Utils::formatBytes(m_ctx->currentWallet->getBytesSent()),
|
||||
Utils::formatBytes(m_ctx->currentWallet->getBytesReceived()));
|
||||
|
||||
QMessageBox::information(this, "Connection Status", statusMsg);
|
||||
}
|
||||
|
||||
void MainWindow::showPasswordDialog() {
|
||||
auto *pdialog = new PasswordChangeDialog(this);
|
||||
int ret = pdialog->exec();
|
||||
if (!ret) return;
|
||||
|
||||
QApplication::setActiveWindow(this);
|
||||
|
||||
QString currentPassword = pdialog->getCurrentPassword();
|
||||
QString newPassword = pdialog->getNewPassword();
|
||||
|
||||
if (currentPassword != m_ctx->walletPassword) {
|
||||
QMessageBox::warning(this, "Error", "Incorrect password");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ctx->currentWallet->setPassword(newPassword)) {
|
||||
QMessageBox::information(this, "Information", "Password changed successfully");
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(this, "Error", QString("Error: %1").arg(m_ctx->currentWallet->errorString()));
|
||||
}
|
||||
|
||||
auto *pdialog = new PasswordChangeDialog(this, m_ctx->currentWallet);
|
||||
pdialog->exec();
|
||||
pdialog->deleteLater();
|
||||
this->updatePasswordIcon();
|
||||
}
|
||||
|
||||
void MainWindow::updatePasswordIcon() {
|
||||
QIcon icon = m_ctx->currentWallet->getPassword().isEmpty() ? QIcon(":/assets/images/unlock.svg") : QIcon(":/assets/images/lock.svg");
|
||||
m_statusBtnPassword->setIcon(icon);
|
||||
}
|
||||
|
||||
void MainWindow::showRestoreHeightDialog() {
|
||||
@@ -850,6 +960,12 @@ 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);
|
||||
@@ -864,6 +980,8 @@ void MainWindow::menuNewRestoreClicked() {
|
||||
}
|
||||
|
||||
void MainWindow::menuQuitClicked() {
|
||||
cleanupBeforeClose();
|
||||
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
@@ -900,7 +1018,9 @@ void MainWindow::menuAboutClicked() {
|
||||
}
|
||||
|
||||
void MainWindow::menuSettingsClicked() {
|
||||
m_windowSettings->raise();
|
||||
m_windowSettings->show();
|
||||
m_windowSettings->activateWindow();
|
||||
}
|
||||
|
||||
void MainWindow::menuSignVerifyClicked() {
|
||||
@@ -937,11 +1057,7 @@ void MainWindow::homeWidgetChanged(const QString &widgetName) {
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event) {
|
||||
m_ctx->tor->stop();
|
||||
|
||||
this->saveGeo();
|
||||
if(m_wizard != nullptr)
|
||||
m_wizard->close();
|
||||
cleanupBeforeClose();
|
||||
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
@@ -952,17 +1068,17 @@ void MainWindow::donateButtonClicked() {
|
||||
donation = 1.337;
|
||||
|
||||
ui->sendWidget->fill(m_ctx->featherDonationAddress, "Donation to the Feather development team", donation);
|
||||
ui->tabWidget->setCurrentIndex(2);
|
||||
ui->tabWidget->setCurrentIndex(Tabs::SEND);
|
||||
}
|
||||
|
||||
void MainWindow::showHistoryTab() {
|
||||
this->raise();
|
||||
ui->tabWidget->setCurrentIndex(1);
|
||||
ui->tabWidget->setCurrentIndex(Tabs::HISTORY);
|
||||
}
|
||||
|
||||
void MainWindow::showSendTab() {
|
||||
this->raise();
|
||||
ui->tabWidget->setCurrentIndex(2);
|
||||
ui->tabWidget->setCurrentIndex(Tabs::SEND);
|
||||
}
|
||||
|
||||
void MainWindow::showCalcWindow() {
|
||||
@@ -971,7 +1087,7 @@ void MainWindow::showCalcWindow() {
|
||||
|
||||
void MainWindow::showSendScreen(const CCSEntry &entry) {
|
||||
ui->sendWidget->fill(entry);
|
||||
ui->tabWidget->setCurrentIndex(2);
|
||||
ui->tabWidget->setCurrentIndex(Tabs::SEND);
|
||||
}
|
||||
|
||||
void MainWindow::onViewOnBlockExplorer(const QString &txid) {
|
||||
@@ -989,6 +1105,29 @@ 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;
|
||||
}
|
||||
@@ -1060,6 +1199,150 @@ 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();
|
||||
}
|
||||
|
||||
void MainWindow::importTransaction() {
|
||||
|
||||
auto result = QMessageBox::warning(this, "Warning", "Using this feature may allow a remote node to associate the transaction with your IP address.\n"
|
||||
"\n"
|
||||
"Connect to a trusted node or run Feather over Tor if network level metadata leakage is included in your threat model.",
|
||||
QMessageBox::Ok | QMessageBox::Cancel);
|
||||
if (result == QMessageBox::Ok) {
|
||||
auto *dialog = new TxImportDialog(this, m_ctx);
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updateNetStats() {
|
||||
if (!m_ctx->currentWallet) {
|
||||
m_statusLabelNetStats->setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ctx->currentWallet->connectionStatus() == Wallet::ConnectionStatus_Connected && m_ctx->currentWallet->synchronized()) {
|
||||
m_statusLabelNetStats->setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
m_statusLabelNetStats->setText(QString("(D: %1)").arg(Utils::formatBytes(m_ctx->currentWallet->getBytesReceived())));
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "src/kdmactouchbar.h"
|
||||
#endif
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QScreen>
|
||||
@@ -19,6 +23,7 @@
|
||||
#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"
|
||||
@@ -64,6 +69,18 @@ public:
|
||||
qreal screenDpiPhysical;
|
||||
qreal screenRatio;
|
||||
|
||||
enum Tabs {
|
||||
HOME = 0,
|
||||
HISTORY,
|
||||
SEND,
|
||||
RECEIVE,
|
||||
COINS,
|
||||
CALC,
|
||||
MORPHTOKEN,
|
||||
XMR_TO,
|
||||
XMRIG
|
||||
};
|
||||
|
||||
public slots:
|
||||
void initWidgets();
|
||||
void initMenu();
|
||||
@@ -79,6 +96,7 @@ public slots:
|
||||
void showConnectionStatusDialog();
|
||||
void showPasswordDialog();
|
||||
void showKeysDialog();
|
||||
void showViewOnlyDialog();
|
||||
void donateButtonClicked();
|
||||
void showCalcWindow();
|
||||
void showSendTab();
|
||||
@@ -94,10 +112,22 @@ public slots:
|
||||
void onWalletCreated(Wallet *wallet);
|
||||
void menuWalletCloseClicked();
|
||||
void menuWalletOpenClicked();
|
||||
void onWalletOpenPasswordRequired(bool invalidPassword);
|
||||
void onWalletOpenPasswordRequired(bool invalidPassword, const QString &path);
|
||||
void onViewOnBlockExplorer(const QString &txid);
|
||||
void onAddContact(const QString &address, const QString &name);
|
||||
void importContacts();
|
||||
void showRestoreHeightDialog();
|
||||
void importTransaction();
|
||||
|
||||
// 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);
|
||||
@@ -119,6 +149,7 @@ private:
|
||||
|
||||
static MainWindow * pMainWindow;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void cleanupBeforeClose();
|
||||
void create_status_bar();
|
||||
void initMain();
|
||||
void loadSkins();
|
||||
@@ -128,6 +159,11 @@ private:
|
||||
void showDebugInfo();
|
||||
void showNodeExhaustedMessage();
|
||||
void showWSNodeExhaustedMessage();
|
||||
void createUnsignedTxDialog(UnsignedTransaction *tx);
|
||||
void touchbarShowWizard();
|
||||
void touchbarShowWallet();
|
||||
void updatePasswordIcon();
|
||||
void updateNetStats();
|
||||
|
||||
WalletWizard *createWizard(WalletWizard::Page startPage);
|
||||
|
||||
@@ -137,6 +173,7 @@ private:
|
||||
SignVerifyDialog *m_windowSignVerify = nullptr;
|
||||
RestoreDialog *m_restoreDialog = nullptr;
|
||||
AboutDialog *m_aboutDialog = nullptr;
|
||||
XMRigWidget *m_xmrig = nullptr;
|
||||
|
||||
bool m_windowSpawned = false;
|
||||
|
||||
@@ -156,6 +193,7 @@ private:
|
||||
// lower status bar
|
||||
QLabel *m_statusLabelBalance;
|
||||
QLabel *m_statusLabelStatus;
|
||||
QLabel *m_statusLabelNetStats;
|
||||
StatusBarButton *m_statusBtnConnectionStatusIndicator;
|
||||
StatusBarButton *m_statusBtnPassword;
|
||||
StatusBarButton *m_statusBtnPreferences;
|
||||
@@ -165,13 +203,20 @@ 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;
|
||||
|
||||
QMap<QString, QString> m_skins;
|
||||
|
||||
QTimer m_updateBytes;
|
||||
|
||||
private slots:
|
||||
void menuToggleTabVisible(const QString &key);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>894</width>
|
||||
<width>1156</width>
|
||||
<height>496</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -224,13 +224,41 @@
|
||||
</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="tabMorphToken">
|
||||
<attribute name="icon">
|
||||
<iconset resource="assets.qrc">
|
||||
<normaloff>:/assets/images/morphtoken.png</normaloff>:/assets/images/morphtoken.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>MorphToken</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="MorphTokenWidget" name="morphtokenWidget" 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">
|
||||
@@ -266,17 +294,17 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabCalc">
|
||||
<widget class="QWidget" name="tabXmrRig">
|
||||
<attribute name="icon">
|
||||
<iconset resource="assets.qrc">
|
||||
<normaloff>:/assets/images/coldcard.png</normaloff>:/assets/images/coldcard.png</iconset>
|
||||
<normaloff>:/assets/images/xmrig.ico</normaloff>:/assets/images/xmrig.ico</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Calc</string>
|
||||
<string>XMRig</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="CalcWidget" name="conversionWidget" native="true"/>
|
||||
<layout class="QGridLayout" name="xmrRigLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -290,7 +318,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>894</width>
|
||||
<width>1156</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -319,15 +347,33 @@
|
||||
<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"/>
|
||||
@@ -335,6 +381,7 @@
|
||||
<addaction name="actionPassword"/>
|
||||
<addaction name="actionSeed"/>
|
||||
<addaction name="actionKeys"/>
|
||||
<addaction name="actionViewOnly"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuHistory"/>
|
||||
<addaction name="menuContacts"/>
|
||||
@@ -343,9 +390,27 @@
|
||||
<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="actionImport_transaction"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCalculator"/>
|
||||
<addaction name="actionCreateDesktopEntry"/>
|
||||
</widget>
|
||||
@@ -354,7 +419,6 @@
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="actionAbout_Qt"/>
|
||||
<addaction name="actionOfficialWebsite"/>
|
||||
<addaction name="actionDonate_to_Feather"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -366,8 +430,10 @@
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionShow_Coins"/>
|
||||
<addaction name="actionShow_xmr_to"/>
|
||||
<addaction name="actionShow_calc"/>
|
||||
<addaction name="actionShow_MorphToken"/>
|
||||
<addaction name="actionShow_xmr_to"/>
|
||||
<addaction name="actionShow_XMRig"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuWallet"/>
|
||||
@@ -455,7 +521,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">
|
||||
@@ -488,6 +554,11 @@
|
||||
<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>
|
||||
@@ -503,11 +574,6 @@
|
||||
<string>Keys</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout_Qt">
|
||||
<property name="text">
|
||||
<string>About Qt</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCreateDesktopEntry">
|
||||
<property name="text">
|
||||
<string>Create desktop entry</string>
|
||||
@@ -518,6 +584,106 @@
|
||||
<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>
|
||||
<action name="actionImport_transaction">
|
||||
<property name="text">
|
||||
<string>Import transaction</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShow_MorphToken">
|
||||
<property name="text">
|
||||
<string>Show MorphToken</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
@@ -562,6 +728,12 @@
|
||||
<header>calcwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>MorphTokenWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>MorphTokenWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="assets.qrc"/>
|
||||
|
||||
@@ -174,3 +174,26 @@ 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,6 +36,7 @@ public:
|
||||
bool isShowFullAddresses() const;
|
||||
void setShowFullAddresses(bool show);
|
||||
bool writeCSV(const QString &path);
|
||||
QMap<QString, QString> readCSV(const QString &path);
|
||||
|
||||
public slots:
|
||||
void startReset();
|
||||
|
||||