Compare commits
218 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e2ae5c88f | ||
|
|
2021d61d91 | ||
|
|
46d0b6b0ff | ||
|
|
8ae8019f5a | ||
|
|
1a6ea3302c | ||
|
|
4de4e82ee6 | ||
|
|
5da0d5768a | ||
|
|
58586ce2dc | ||
|
|
1cc9e8af49 | ||
|
|
37a5bdc331 | ||
|
|
cc4815a3db | ||
|
|
46eba838f5 | ||
|
|
b31cc36de2 | ||
|
|
76dd4fee2f | ||
|
|
55a4b67880 | ||
|
|
97fb3d4982 | ||
|
|
28f0645d9e | ||
|
|
85dff323c8 | ||
|
|
0468e0e43a | ||
|
|
2cc3ebd315 | ||
|
|
377a1a9680 | ||
|
|
877f822de0 | ||
|
|
0feef2268d | ||
|
|
002fe8eefa | ||
|
|
1dba06a7ff | ||
|
|
838b0179e5 | ||
|
|
b12ad939ad | ||
|
|
74fbf77f62 | ||
|
|
211ef24d18 | ||
|
|
a2e0ea4faf | ||
|
|
6ab104ead7 | ||
|
|
62748b6121 | ||
|
|
8bbf2bfcbb | ||
|
|
de81af0e8d | ||
|
|
346913f3db | ||
|
|
fd8983a7ff | ||
|
|
4ef5ca5721 | ||
|
|
119deb4e82 | ||
|
|
ea92f3f272 | ||
|
|
9c383bcc24 | ||
|
|
4fba8e654d | ||
|
|
14383a1922 | ||
|
|
1610b93975 | ||
|
|
8ece450f18 | ||
|
|
ced654707d | ||
|
|
da63ab2e05 | ||
|
|
ddd95f73b4 | ||
|
|
08e386f63f | ||
|
|
bf324ec2d9 | ||
|
|
e9afaa9cc8 | ||
|
|
78e3b947d1 | ||
|
|
05b4a3dd6b | ||
|
|
c2f706a28a | ||
|
|
4df925e8a8 | ||
|
|
adf88c63ec | ||
|
|
c2545e226b | ||
|
|
5c7c4dde14 | ||
|
|
2bbb6adb4f | ||
|
|
493105b457 | ||
|
|
2bf0dd840f | ||
|
|
0f67580e8f | ||
|
|
599033815a | ||
|
|
de49ddcf5b | ||
|
|
d752117ed3 | ||
|
|
fa9285a108 | ||
|
|
9194522b56 | ||
|
|
11a16952c5 | ||
|
|
a959919b8a | ||
|
|
6f88ad0a13 | ||
|
|
cb169f11d4 | ||
|
|
98b81fa1ee | ||
|
|
a6fadd2140 | ||
|
|
09bf4a1e71 | ||
|
|
f44fcb4459 | ||
|
|
c5d598f401 | ||
|
|
9476a9880c | ||
|
|
487f2ceecb | ||
|
|
d0950499da | ||
|
|
56df20ba27 | ||
|
|
41b827b65d | ||
|
|
0b6ceab25b | ||
|
|
2566f445b2 | ||
|
|
9199f95af5 | ||
|
|
f2b4e1ea3e | ||
|
|
621c11925b | ||
|
|
a4cc91cca2 | ||
|
|
db4123ccb5 | ||
|
|
98f8f194cd | ||
|
|
f8a3a26e0d | ||
|
|
f7792b72bf | ||
|
|
d160828cda | ||
|
|
e85b51e1c2 | ||
|
|
6b926b3199 | ||
|
|
008a38aae2 | ||
|
|
70e3c2d3ad | ||
|
|
817b015335 | ||
|
|
bddb9b0050 | ||
|
|
78f7b05ecb | ||
|
|
999bc21d6b | ||
|
|
901b4b0f77 | ||
|
|
69dc0b3570 | ||
|
|
0bebf412fd | ||
|
|
ce1c5aebf7 | ||
|
|
7eef4f3364 | ||
|
|
ecadd44a16 | ||
|
|
4bd7d43588 | ||
|
|
50c8f30d0b | ||
|
|
e2203a871c | ||
|
|
3e159e0ed6 | ||
|
|
d922c91071 | ||
|
|
fa35410851 | ||
|
|
23b74a3412 | ||
|
|
6e24930026 | ||
|
|
b8b96ee719 | ||
|
|
22450cb68d | ||
|
|
a4b907a668 | ||
|
|
843536c7a1 | ||
|
|
31ae9b3947 | ||
|
|
c72729fa5d | ||
|
|
a07299f64d | ||
|
|
d55a001875 | ||
|
|
917d6d4243 | ||
|
|
05e629c0eb | ||
|
|
9c901b33f0 | ||
|
|
a0ff56fbef | ||
|
|
fc8c1114b2 | ||
|
|
93e5ef883c | ||
|
|
10a36f96f6 | ||
|
|
80cd2e08a4 | ||
|
|
97755e1b34 | ||
|
|
8a8a3847d7 | ||
|
|
00a95c11a0 | ||
|
|
8f12e97b79 | ||
|
|
9961305867 | ||
|
|
9e83942920 | ||
|
|
e390b43bdc | ||
|
|
cc8ddde01b | ||
|
|
98b2727857 | ||
|
|
4d2186fb75 | ||
|
|
419a1975eb | ||
|
|
9846bff226 | ||
|
|
a37c3756b7 | ||
|
|
c844e3d179 | ||
|
|
61d8b5efb6 | ||
|
|
ecf5c501d6 | ||
|
|
db639b96a3 | ||
|
|
e65956e910 | ||
|
|
09ce233f4b | ||
|
|
34893e169e | ||
|
|
dd171c8b49 | ||
|
|
985406e70f | ||
|
|
8bf457c864 | ||
|
|
96dab92f33 | ||
|
|
d348324f50 | ||
|
|
4f5903e7fd | ||
|
|
bdadd98622 | ||
|
|
bdad630d51 | ||
|
|
987bf920c7 | ||
|
|
f8e1b9bdb6 | ||
|
|
06fe68b56c | ||
|
|
b811d6a84f | ||
|
|
f91e1791fe | ||
|
|
49ade0d689 | ||
|
|
c970588c55 | ||
|
|
4aab43a127 | ||
|
|
c0aface962 | ||
|
|
bd7bd43504 | ||
|
|
b44d359f9e | ||
|
|
2f109d3333 | ||
|
|
7cc225039c | ||
|
|
cef9be3d02 | ||
|
|
1c31f2e481 | ||
|
|
1173dd2826 | ||
|
|
6239f949fe | ||
|
|
4ec78b35f8 | ||
|
|
87b1518023 | ||
|
|
02c55e3fd1 | ||
|
|
96762ebf09 | ||
|
|
702d3b8ec1 | ||
|
|
a3069e4a58 | ||
|
|
bd9a2d7bbb | ||
|
|
1ef9a5c2d7 | ||
|
|
3f48e3ef1c | ||
|
|
d9f7482ae8 | ||
|
|
facec65dfc | ||
|
|
dc331f0e64 | ||
|
|
67eb486e63 | ||
|
|
edb0358916 | ||
|
|
569ba16df8 | ||
|
|
d48438aeef | ||
|
|
92b3f5a2ee | ||
|
|
f430d49304 | ||
|
|
76c3b3cd1d | ||
|
|
35aee155d7 | ||
|
|
d93af5a469 | ||
|
|
051282931a | ||
|
|
d3780abe33 | ||
|
|
f327d20deb | ||
|
|
dcc16a44be | ||
|
|
36940f07c9 | ||
|
|
b99b333b71 | ||
|
|
5fa310c961 | ||
|
|
e36b166edd | ||
|
|
27525b37e0 | ||
|
|
d3d78416d7 | ||
|
|
f30570d54b | ||
|
|
20579049fa | ||
|
|
544cff7dc1 | ||
|
|
165817ec02 | ||
|
|
95c07e1a62 | ||
|
|
2f8f7c2054 | ||
|
|
99ad8ef1ba | ||
|
|
18a76299f2 | ||
|
|
57c205206c | ||
|
|
afb88ff511 | ||
|
|
75746a8153 | ||
|
|
66d29a4d40 | ||
|
|
6dd7445938 |
107
.github/qt_helper.py
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
import defusedxml.ElementTree
|
||||
import hashlib
|
||||
import mmap
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
MAX_TRIES = 32
|
||||
|
||||
def fetch_links_to_archives(os, target, major, minor, patch, toolchain):
|
||||
MAX_XML_SIZE = 1024 * 1024 * 1024
|
||||
MIRROR = 'download.qt.io'
|
||||
base_url = f'https://{MIRROR}/online/qtsdkrepository/{os}/{target}/qt{major}_{major}{minor}{patch}'
|
||||
url = f'{base_url}/Updates.xml'
|
||||
for _ in range(MAX_TRIES):
|
||||
try:
|
||||
resp = urllib.request.urlopen(url).read(MAX_XML_SIZE)
|
||||
update_xml = defusedxml.ElementTree.fromstring(resp)
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except BaseException as e:
|
||||
print('error', e, flush=True)
|
||||
else:
|
||||
return
|
||||
for pkg in update_xml.findall('./PackageUpdate'):
|
||||
name = pkg.find('.//Name')
|
||||
if name == None:
|
||||
continue
|
||||
if name.text != f'qt.qt{major}.{major}{minor}{patch}.{toolchain}':
|
||||
continue
|
||||
version = pkg.find('.//Version')
|
||||
if version == None:
|
||||
continue
|
||||
archives = pkg.find('.//DownloadableArchives')
|
||||
if archives == None or archives.text == None:
|
||||
continue
|
||||
for archive in archives.text.split(', '):
|
||||
url = f'{base_url}/{name.text}/{version.text}{archive}'
|
||||
file_name = pathlib.Path(urllib.parse.urlparse(url).path).name
|
||||
yield {'name': file_name, 'url': url}
|
||||
|
||||
def download(links):
|
||||
metalink = ET.Element('metalink', xmlns = "urn:ietf:params:xml:ns:metalink")
|
||||
for link in links:
|
||||
file = ET.SubElement(metalink, 'file', name = link['name'])
|
||||
ET.SubElement(file, 'url').text = link['url']
|
||||
data = ET.tostring(metalink, encoding='UTF-8', xml_declaration=True)
|
||||
for _ in range(MAX_TRIES):
|
||||
with subprocess.Popen([
|
||||
'aria2c',
|
||||
'--connect-timeout=8',
|
||||
'--console-log-level=warn',
|
||||
'--continue',
|
||||
'--follow-metalink=mem',
|
||||
'--max-concurrent-downloads=100',
|
||||
'--max-connection-per-server=16',
|
||||
'--max-file-not-found=100',
|
||||
'--max-tries=100',
|
||||
'--min-split-size=1MB',
|
||||
'--retry-wait=1',
|
||||
'--split=100',
|
||||
'--summary-interval=0',
|
||||
'--timeout=8',
|
||||
'--user-agent=',
|
||||
'--metalink-file=-',
|
||||
], stdin=subprocess.PIPE) as aria:
|
||||
aria.communicate(data)
|
||||
if aria.wait() == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def calc_hash_sum(files):
|
||||
obj = hashlib.new('sha256')
|
||||
for path in files:
|
||||
with open(path, 'rb') as f:
|
||||
with mmap.mmap(f.fileno(), 0, mmap.MAP_SHARED, mmap.PROT_READ) as m:
|
||||
file_hash = hashlib.new('sha256', m).digest()
|
||||
obj.update(file_hash)
|
||||
return obj.digest().hex()
|
||||
|
||||
def extract_archives(files, out='.', targets=[]):
|
||||
for path in files:
|
||||
if subprocess.Popen(['7z', 'x', '-bd', '-y', '-aoa', f'-o{out}', path] + targets,
|
||||
stdout=subprocess.DEVNULL,
|
||||
).wait() != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def main():
|
||||
os, target, version, toolchain, expect = sys.argv[1:]
|
||||
major, minor, patch = version.split('.')
|
||||
links = [*fetch_links_to_archives(os, target, major, minor, patch, toolchain)]
|
||||
print(*[l['url'].encode() for l in links], sep='\n', flush=True)
|
||||
assert download(links)
|
||||
result = calc_hash_sum([l['name'] for l in links])
|
||||
print('result', result, 'expect', expect, flush=True)
|
||||
assert result == expect
|
||||
assert extract_archives([l['name'] for l in links], '.', ['{}.{}.{}'.format(major, minor, patch)])
|
||||
[pathlib.Path(l['name']).unlink() for l in links]
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
22
.github/workflows/build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi zmq libpgm libsodium miniupnpc ldns expat libunwind-headers protobuf qt5 pkg-config
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm libsodium miniupnpc ldns expat libunwind-headers protobuf qt5 pkg-config
|
||||
- name: build
|
||||
run: DEV_MODE=ON make release -j3
|
||||
- name: test qml
|
||||
@@ -52,9 +52,12 @@ jobs:
|
||||
- uses: eine/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5 mingw-w64-x86_64-libgcrypt
|
||||
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb mingw-w64-x86_64-unbound git mingw-w64-x86_64-qt5 mingw-w64-x86_64-libgcrypt
|
||||
- name: build
|
||||
run: DEV_MODE=ON make release-win64 -j2
|
||||
- name: deploy
|
||||
run: make deploy
|
||||
working-directory: build/release
|
||||
- name: test qml
|
||||
run: build/release/bin/monero-wallet-gui --test-qml
|
||||
|
||||
@@ -65,14 +68,11 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf pkg-config python3 p7zip
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi openssl zmq libpgm miniupnpc ldns expat libunwind-headers protobuf pkg-config python3 p7zip aria2
|
||||
- name: install dependencies
|
||||
run: pip3 install requests semantic_version lxml py7zr
|
||||
run: pip3 install defusedxml
|
||||
- name: download qt
|
||||
run: |
|
||||
curl -O https://raw.githubusercontent.com/engnr/qt-downloader/master/qt-downloader
|
||||
chmod +x qt-downloader
|
||||
./qt-downloader macos desktop 5.15.2 clang_64
|
||||
run: python3 monero-gui/.github/qt_helper.py mac_x64 desktop 5.15.2 clang_64 c384008156fe63cc183bade0316828c598ff3e5074397c0c9ccc588d6cdc5aca
|
||||
working-directory: ../
|
||||
- name: build
|
||||
run: CMAKE_PREFIX_PATH=/Users/runner/work/monero-gui/5.15.2/clang_64 make release -j3
|
||||
@@ -95,7 +95,8 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.10
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.11
|
||||
if: "!startsWith(github.ref, 'refs/tags/v')"
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: docker-linux-static-{hash}
|
||||
@@ -124,7 +125,8 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.10
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.11
|
||||
if: "!startsWith(github.ref, 'refs/tags/v')"
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: docker-windows-static-{hash}
|
||||
|
||||
@@ -4,28 +4,23 @@ project(monero-gui)
|
||||
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
|
||||
|
||||
set(VERSION_MAJOR "17")
|
||||
set(VERSION_MINOR "2")
|
||||
set(VERSION_REVISION "3")
|
||||
set(VERSION_MINOR "3")
|
||||
set(VERSION_REVISION "2")
|
||||
set(VERSION "0.${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||
|
||||
option(STATIC "Link libraries statically, requires static Qt")
|
||||
|
||||
option(USE_DEVICE_TREZOR "Trezor support compilation" ON)
|
||||
option(WITH_SCANNER "Enable webcam QR scanner" OFF)
|
||||
option(WITH_DESKTOP_ENTRY "Ask to install desktop entry on first startup" ON)
|
||||
option(DEV_MODE "Checkout latest monero master on build" OFF)
|
||||
|
||||
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckLinkerFlag)
|
||||
include(FindCcache)
|
||||
|
||||
if(DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
endif()
|
||||
|
||||
set(BUILD_GUI_DEPS ON)
|
||||
set(ARCH "x86-64" CACHE STRING "Target architecture")
|
||||
set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries")
|
||||
|
||||
if(NOT MANUAL_SUBMODULES)
|
||||
@@ -71,85 +66,31 @@ endif()
|
||||
|
||||
if(STATIC)
|
||||
message(STATUS "Initiating static build")
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
add_definitions(-DMONERO_GUI_STATIC)
|
||||
endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# force version update
|
||||
function (monero_gui_add_library_with_deps)
|
||||
cmake_parse_arguments(MONERO_ADD_LIBRARY "" "NAME" "DEPENDS;SOURCES" ${ARGN})
|
||||
source_group("${MONERO_ADD_LIBRARY_NAME}" FILES ${MONERO_ADD_LIBRARY_SOURCES})
|
||||
|
||||
# Define a ("virtual") object library and an actual library that links those
|
||||
# objects together. The virtual libraries can be arbitrarily combined to link
|
||||
# any subset of objects into one library archive. This is used for releasing
|
||||
# libwallet, which combines multiple components.
|
||||
set(objlib obj_${MONERO_ADD_LIBRARY_NAME})
|
||||
add_library(${objlib} OBJECT ${MONERO_ADD_LIBRARY_SOURCES})
|
||||
add_library("${MONERO_ADD_LIBRARY_NAME}" $<TARGET_OBJECTS:${objlib}>)
|
||||
if (MONERO_ADD_LIBRARY_DEPENDS)
|
||||
add_dependencies(${objlib} ${MONERO_ADD_LIBRARY_DEPENDS})
|
||||
endif()
|
||||
set_property(TARGET "${MONERO_ADD_LIBRARY_NAME}" PROPERTY FOLDER "libs")
|
||||
target_compile_definitions(${objlib}
|
||||
PRIVATE $<TARGET_PROPERTY:${MONERO_ADD_LIBRARY_NAME},INTERFACE_COMPILE_DEFINITIONS>)
|
||||
endfunction ()
|
||||
|
||||
function (monero_gui_add_library name)
|
||||
monero_gui_add_library_with_deps(NAME "${name}" SOURCES ${ARGN})
|
||||
endfunction()
|
||||
|
||||
include_directories(${EASYLOGGING_INCLUDE})
|
||||
link_directories(${EASYLOGGING_LIBRARY_DIRS})
|
||||
|
||||
|
||||
include(VersionGui)
|
||||
monero_gui_add_library(gui_version SOURCES version.js DEPENDS genversiongui)
|
||||
|
||||
message(STATUS "${CMAKE_MODULE_PATH}")
|
||||
|
||||
# OpenSSL
|
||||
if(APPLE AND NOT OPENSSL_ROOT_DIR)
|
||||
execute_process(COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
|
||||
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
|
||||
message(STATUS "OpenSSL: libraries at ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}")
|
||||
|
||||
if(WITH_SCANNER)
|
||||
add_definitions(-DWITH_SCANNER)
|
||||
endif()
|
||||
|
||||
if(WITH_DESKTOP_ENTRY)
|
||||
add_definitions(-DWITH_DESKTOP_ENTRY)
|
||||
endif()
|
||||
|
||||
# Sodium
|
||||
find_library(SODIUM_LIBRARY sodium)
|
||||
message(STATUS "libsodium: libraries at ${SODIUM_LIBRARY}")
|
||||
|
||||
# Boost
|
||||
if(DEBUG)
|
||||
set(Boost_DEBUG ON)
|
||||
endif()
|
||||
if(APPLE AND NOT BOOST_ROOT)
|
||||
execute_process(COMMAND brew --prefix boost OUTPUT_VARIABLE BOOST_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(Boost_THREADAPI win32)
|
||||
endif()
|
||||
find_package(Boost 1.58 REQUIRED COMPONENTS
|
||||
system
|
||||
filesystem
|
||||
thread
|
||||
date_time
|
||||
chrono
|
||||
regex
|
||||
serialization
|
||||
program_options
|
||||
locale)
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
set(CMAKE_SKIP_RPATH ON)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES_PREV ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
@@ -167,19 +108,6 @@ if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
|
||||
message(STATUS "MSYS location: ${msys2_install_path}")
|
||||
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
|
||||
# This is necessary because otherwise CMake will make Boost libraries -lfoo
|
||||
# rather than a full path. Unfortunately, this makes the shared libraries get
|
||||
# linked due to a bug in CMake which misses putting -static flags around the
|
||||
# -lfoo arguments.
|
||||
set(DEFLIB ${msys2_install_path}/mingw${ARCH_WIDTH}/lib)
|
||||
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_DIRECTORIES ${DEFLIB})
|
||||
list(REMOVE_ITEM CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES ${DEFLIB})
|
||||
endif()
|
||||
|
||||
set(QT5_LIBRARIES
|
||||
Qt5Core
|
||||
Qt5Quick
|
||||
@@ -392,10 +320,6 @@ if(ANDROID)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
message(STATUS "Using Boost include dir at ${Boost_INCLUDE_DIRS}")
|
||||
message(STATUS "Using Boost libraries at ${Boost_LIBRARIES}")
|
||||
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
|
||||
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
|
||||
@@ -423,14 +347,11 @@ endif()
|
||||
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
|
||||
|
||||
if(APPLE)
|
||||
include_directories(SYSTEM /usr/include/malloc)
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
if (APPLE AND NOT IOS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -std=c++11")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
@@ -438,8 +359,6 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
# warnings
|
||||
add_c_flag_if_supported(-Werror C_SECURITY_FLAGS)
|
||||
add_cxx_flag_if_supported(-Werror CXX_SECURITY_FLAGS)
|
||||
add_c_flag_if_supported(-Wformat C_SECURITY_FLAGS)
|
||||
add_cxx_flag_if_supported(-Wformat CXX_SECURITY_FLAGS)
|
||||
add_c_flag_if_supported(-Wformat-security C_SECURITY_FLAGS)
|
||||
|
||||
11
DEPLOY.md
@@ -8,11 +8,16 @@ Use macOS 10.12 - 10.13 for better backwards compability.
|
||||
|
||||
3. `git clone --recursive -b v0.X.Y.Z --depth 1 https://github.com/monero-project/monero-gui`
|
||||
|
||||
4. `CMAKE_PREFIX_PATH=~/Qt5.12.8/5.12.8/clang_64 make release`
|
||||
4. Compile `monero-wallet-gui.app`
|
||||
|
||||
5. `cd build/release && make deploy`
|
||||
```
|
||||
mkdir build && cd build
|
||||
cmake -D CMAKE_BUILD_TYPE=Release -D ARCH=default -D CMAKE_PREFIX_PATH=~/Qt5.12.8/5.12.8/clang_64 ..
|
||||
make
|
||||
make deploy
|
||||
```
|
||||
|
||||
6. Replace the `monerod` binary inside `monero-wallet-gui.app/Contents/MacOS/` with one built using deterministic builds / gitian.
|
||||
5. Replace the `monerod` binary inside `monero-wallet-gui.app/Contents/MacOS/` with one built using deterministic builds / gitian.
|
||||
|
||||
## Codesigning and notarizing
|
||||
|
||||
|
||||
@@ -40,9 +40,10 @@ RUN cp -r ${WORKDIR}/platforms ${WORKDIR}/platform-tools ${ANDROID_SDK_ROOT}
|
||||
ENV HOST_PATH=${PATH}
|
||||
ENV PATH=${TOOLCHAIN_DIR}/aarch64-linux-android/bin:${TOOLCHAIN_DIR}/bin:${PATH}
|
||||
|
||||
ARG ZLIB_VERSION=1.2.11
|
||||
ARG ZLIB_HASH=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ARG ZLIB_VERSION=1.2.12
|
||||
ARG ZLIB_HASH=91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9
|
||||
RUN wget -q https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& echo "${ZLIB_HASH} zlib-${ZLIB_VERSION}.tar.gz" | sha256sum -c \
|
||||
&& tar -xzf zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& rm zlib-${ZLIB_VERSION}.tar.gz \
|
||||
&& cd zlib-${ZLIB_VERSION} \
|
||||
@@ -117,6 +118,7 @@ RUN wget -q https://downloads.sourceforge.net/project/boost/boost/${BOOST_VERSIO
|
||||
ARG OPENSSL_VERSION=1.1.1g
|
||||
ARG OPENSSL_HASH=ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46
|
||||
RUN wget -q https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \
|
||||
&& tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& rm openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& cd openssl-${OPENSSL_VERSION} \
|
||||
@@ -129,6 +131,30 @@ RUN wget -q https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \
|
||||
&& make -j${THREADS} install \
|
||||
&& rm -rf $(pwd)
|
||||
|
||||
ARG EXPAT_VERSION=2.4.1
|
||||
ARG EXPAT_HASH=2f9b6a580b94577b150a7d5617ad4643a4301a6616ff459307df3e225bcfbf40
|
||||
RUN wget https://github.com/libexpat/libexpat/releases/download/R_2_4_1/expat-${EXPAT_VERSION}.tar.bz2 && \
|
||||
echo "${EXPAT_HASH} expat-${EXPAT_VERSION}.tar.bz2" | sha256sum -c && \
|
||||
tar -xf expat-${EXPAT_VERSION}.tar.bz2 && \
|
||||
rm expat-${EXPAT_VERSION}.tar.bz2 && \
|
||||
cd expat-${EXPAT_VERSION} && \
|
||||
CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --enable-static --disable-shared --prefix=${PREFIX} --host=aarch64-linux-android && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
ARG UNBOUND_VERSION=1.13.2
|
||||
ARG UNBOUND_HASH=0a13b547f3b92a026b5ebd0423f54c991e5718037fd9f72445817f6a040e1a83
|
||||
RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-${UNBOUND_VERSION}.tar.gz && \
|
||||
echo "${UNBOUND_HASH} unbound-${UNBOUND_VERSION}.tar.gz" | sha256sum -c && \
|
||||
tar -xzf unbound-${UNBOUND_VERSION}.tar.gz && \
|
||||
rm unbound-${UNBOUND_VERSION}.tar.gz && \
|
||||
cd unbound-${UNBOUND_VERSION} && \
|
||||
CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --disable-shared --enable-static --without-pyunbound --with-libexpat=${PREFIX} --with-ssl=${PREFIX} --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only --host=aarch64-linux-android --with-pic --prefix=${PREFIX} && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
ARG ZMQ_VERSION=v4.3.3
|
||||
ARG ZMQ_HASH=04f5bbedee58c538934374dc45182d8fc5926fa3
|
||||
RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} --depth 1 \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
ARG THREADS=1
|
||||
ARG QT_VERSION=5.15.2
|
||||
ARG QT_VERSION=v5.15.3-lts-lgpl
|
||||
|
||||
ENV CFLAGS="-fPIC"
|
||||
ENV CPPFLAGS="-fPIC"
|
||||
@@ -169,7 +169,27 @@ RUN wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz && \
|
||||
tar -xzf openssl-1.1.1g.tar.gz && \
|
||||
rm openssl-1.1.1g.tar.gz && \
|
||||
cd openssl-1.1.1g && \
|
||||
./config no-asm no-shared no-zlib-dynamic --openssldir=/usr && \
|
||||
./config no-asm no-shared no-zlib-dynamic --prefix=/usr --openssldir=/usr && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN wget https://github.com/libexpat/libexpat/releases/download/R_2_4_1/expat-2.4.1.tar.bz2 && \
|
||||
echo "2f9b6a580b94577b150a7d5617ad4643a4301a6616ff459307df3e225bcfbf40 expat-2.4.1.tar.bz2" | sha256sum -c && \
|
||||
tar -xf expat-2.4.1.tar.bz2 && \
|
||||
rm expat-2.4.1.tar.bz2 && \
|
||||
cd expat-2.4.1 && \
|
||||
./configure --enable-static --disable-shared --prefix=/usr && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.13.2.tar.gz && \
|
||||
echo "0a13b547f3b92a026b5ebd0423f54c991e5718037fd9f72445817f6a040e1a83 unbound-1.13.2.tar.gz" | sha256sum -c && \
|
||||
tar -xzf unbound-1.13.2.tar.gz && \
|
||||
rm unbound-1.13.2.tar.gz && \
|
||||
cd unbound-1.13.2 && \
|
||||
./configure --disable-shared --enable-static --without-pyunbound --with-libexpat=/usr --with-ssl=/usr --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only --with-pic && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -222,9 +242,9 @@ RUN git clone -b v1.0.23 --depth 1 https://github.com/libusb/libusb && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
|
||||
RUN git clone -b hidapi-0.11.0 --depth 1 https://github.com/libusb/hidapi && \
|
||||
cd hidapi && \
|
||||
git reset --hard 7da5cc91fc0d2dbe4df4f08cd31f6ca1a262418f && \
|
||||
git reset --hard 0ec60c03cbe87cdbfb9fb199c7536fdcbc0a94b8 && \
|
||||
./bootstrap && \
|
||||
./configure --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
ARG THREADS=1
|
||||
ARG QT_VERSION=5.15.2
|
||||
ARG QT_VERSION=v5.15.3-lts-lgpl
|
||||
ENV SOURCE_DATE_EPOCH=1397818193
|
||||
|
||||
RUN apt update && \
|
||||
@@ -12,9 +12,9 @@ RUN apt update && \
|
||||
RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \
|
||||
update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix)
|
||||
|
||||
RUN git clone -b v0.17.0.0 --depth 1 https://github.com/monero-project/monero && \
|
||||
RUN git clone -b v0.17.3.0 --depth 1 https://github.com/monero-project/monero && \
|
||||
cd monero && \
|
||||
git reset --hard d27d4526fe89b7cdeb4b296280c4a6cf7efe21f8 && \
|
||||
git reset --hard ab18fea3500841fc312630d49ed6840b3aedb34d && \
|
||||
cp -a contrib/depends / && \
|
||||
cd .. && \
|
||||
rm -rf monero
|
||||
|
||||
@@ -360,7 +360,8 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Account") + translationManager.emptyString
|
||||
symbol: qsTr("T") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "T" + translationManager.emptyString
|
||||
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = accountButton
|
||||
@@ -381,7 +382,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Send") + translationManager.emptyString
|
||||
symbol: qsTr("S") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "S" + translationManager.emptyString
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = transferButton
|
||||
@@ -403,7 +404,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Address book") + translationManager.emptyString
|
||||
symbol: qsTr("B") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "B" + translationManager.emptyString
|
||||
under: transferButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
@@ -425,7 +426,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Receive") + translationManager.emptyString
|
||||
symbol: qsTr("R") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "R" + translationManager.emptyString
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = receiveButton
|
||||
@@ -447,7 +448,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Transactions") + translationManager.emptyString
|
||||
symbol: qsTr("H") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "H" + translationManager.emptyString
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = historyButton
|
||||
@@ -469,7 +470,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Advanced") + translationManager.emptyString
|
||||
symbol: qsTr("D") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "D" + translationManager.emptyString
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = advancedButton
|
||||
@@ -490,7 +491,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Settings") + translationManager.emptyString
|
||||
symbol: qsTr("E") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "E" + translationManager.emptyString
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = settingsButton
|
||||
|
||||
15
Makefile
@@ -29,7 +29,7 @@ else
|
||||
endif
|
||||
|
||||
default:
|
||||
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
mkdir -p build && cd build && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
debug:
|
||||
mkdir -p build && cd build && cmake -D DEV_MODE=$(or ${DEV_MODE},ON) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D CMAKE_BUILD_TYPE=Debug .. && $(MAKE) VERBOSE=1
|
||||
|
||||
@@ -38,19 +38,20 @@ depends:
|
||||
cd build/$(target)/release && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
|
||||
|
||||
devmode:
|
||||
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},ON) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
mkdir -p build && cd build && cmake -D DEV_MODE=$(or ${DEV_MODE},ON) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
clean:
|
||||
mkdir -p build && cd build && rm -rf *
|
||||
scanner:
|
||||
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},ON) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D WITH_SCANNER=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
mkdir -p build && cd build && cmake -D DEV_MODE=$(or ${DEV_MODE},ON) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D WITH_SCANNER=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
|
||||
release:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
|
||||
release-linux-armv8:
|
||||
mkdir -p $(builddir)/release
|
||||
cd $(builddir)/release
|
||||
cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="armv8-a" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="linux-armv8" $(topdir) && $(MAKE)
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="armv8-a" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="linux-armv8" $(topdir) && $(MAKE)
|
||||
|
||||
release-linux-ppc64le:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D ARCH="ppc64le" -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
|
||||
release-static:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},OFF) -DMANUAL_SUBMODULES=${MANUAL_SUBMODULES} -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
|
||||
@@ -66,7 +66,7 @@ Rectangle {
|
||||
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
||||
signal sweepUnmixableClicked()
|
||||
signal generatePaymentIdInvoked()
|
||||
signal getProofClicked(string txid, string address, string message);
|
||||
signal getProofClicked(string txid, string address, string message, string amount);
|
||||
signal checkProofClicked(string txid, string address, string message, string signature);
|
||||
|
||||
Rectangle {
|
||||
|
||||
30
README.md
@@ -75,9 +75,10 @@ See [LICENSE](LICENSE).
|
||||
|
||||
Do you speak a second language and would like to help translate the Monero GUI? Check out Weblate, our localization platform, at [translate.getmonero.org](https://translate.getmonero.org/). Choose the language and suggest a translation for a string or review an existing one. The Localization Workgroup made [a guide with step-by-step instructions](https://github.com/monero-ecosystem/monero-translations/blob/master/weblate.md) for Weblate.
|
||||
|
||||
If you need help/support or any info you can contact the localization workgroup on the IRC channel #monero-translations (relayed on matrix/riot and MatterMost) or by email at translate[at]getmonero[dot]org. For more info about the Localization workgroup: [github.com/monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations)
|
||||
If you need help/support or any info you can contact the localization workgroup on the IRC channel #monero-translations (relayed on [Matrix](https://matrix.to/#/!BKMbQLMDzHKzmtrBfg:matrix.org?via=monero.social&via=matrix.org&via=libera.chat)) or by email at translate[at]getmonero[dot]org. For more info about the Localization workgroup: [github.com/monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations)
|
||||
|
||||
Status of the translations:
|
||||
|
||||
<a href="https://translate.getmonero.org/engage/monero/?utm_source=widget">
|
||||
<img src="https://translate.getmonero.org/widgets/monero/-/gui-wallet/horizontal-auto.svg" alt="Translation status" />
|
||||
</a>
|
||||
@@ -88,6 +89,7 @@ Packages are available for
|
||||
* Arch Linux: [monero-gui](https://www.archlinux.org/packages/community/x86_64/monero-gui/)
|
||||
* Debian: See the [whonix/monero-gui repository](https://gitlab.com/whonix/monero-gui#how-to-install-monero-using-apt-get)
|
||||
* Void Linux: `xbps-install -S monero-gui`
|
||||
* Flatpak: [Monero GUI](https://flathub.org/apps/details/org.getmonero.Monero)
|
||||
* GuixSD: `guix package -i monero-gui`
|
||||
* macOS (homebrew): `brew install --cask monero-wallet`
|
||||
|
||||
@@ -97,6 +99,8 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
*Note*: Qt 5.9.7 is the minimum version required to build the GUI.
|
||||
|
||||
*Note*: Official GUI releases use monero-wallet-gui from this process alongside the supporting binaries (monerod, etc) from the [CLI deterministic builds](https://github.com/monero-project/monero/blob/master/contrib/gitian/README.md).
|
||||
|
||||
### Building Reproducible Windows static binaries with Docker (any OS)
|
||||
|
||||
1. Install Docker [https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)
|
||||
@@ -205,7 +209,7 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
- For Debian distributions (Debian, Ubuntu, Mint, Tails...)
|
||||
|
||||
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev`
|
||||
`sudo apt install build-essential cmake miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev`
|
||||
|
||||
- For Gentoo
|
||||
|
||||
@@ -228,6 +232,9 @@ The following instructions will fetch Qt from your distribution's repositories i
|
||||
`sudo apt install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtqml-models2 qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-platform qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev`
|
||||
|
||||
- For Gentoo
|
||||
|
||||
|
||||
The *qml* USE flag must be enabled.
|
||||
|
||||
`sudo emerge dev-qt/qtcore:5 dev-qt/qtdeclarative:5 dev-qt/qtquickcontrols:5 dev-qt/qtquickcontrols2:5 dev-qt/qtgraphicaleffects:5`
|
||||
|
||||
@@ -237,9 +244,7 @@ The following instructions will fetch Qt from your distribution's repositories i
|
||||
|
||||
`sudo apt install qtmultimedia5-dev qml-module-qtmultimedia`
|
||||
|
||||
- For Gentoo
|
||||
|
||||
The *qml* USE flag must be enabled.
|
||||
- For Gentoo
|
||||
|
||||
`emerge dev-qt/qtmultimedia:5`
|
||||
|
||||
@@ -253,11 +258,20 @@ The following instructions will fetch Qt from your distribution's repositories i
|
||||
|
||||
4. Build
|
||||
|
||||
If on x86-64:
|
||||
|
||||
```
|
||||
make release -j4
|
||||
```
|
||||
|
||||
If on ppc64le:
|
||||
|
||||
```
|
||||
make release-linux-ppc64le -j4
|
||||
```
|
||||
|
||||
\* `4` - number of CPU threads to use
|
||||
\* Add `CMAKE_PREFIX_PATH` enviroment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/5.9.7/gcc_64 make release -j4`
|
||||
\* Add `CMAKE_PREFIX_PATH` environment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/5.9.7/gcc_64 make release -j4`
|
||||
|
||||
The executable can be found in the build/release/bin folder.
|
||||
|
||||
@@ -269,7 +283,7 @@ The executable can be found in the build/release/bin folder.
|
||||
|
||||
3. Install [monero](https://github.com/monero-project/monero) dependencies:
|
||||
|
||||
`brew install boost hidapi zmq libpgm libsodium miniupnpc ldns expat libunwind-headers protobuf libgcrypt`
|
||||
`brew install cmake pkg-config openssl boost unbound hidapi zmq libpgm libsodium miniupnpc ldns expat libunwind-headers protobuf libgcrypt`
|
||||
|
||||
4. Install Qt:
|
||||
|
||||
@@ -288,7 +302,7 @@ The executable can be found in the build/release/bin folder.
|
||||
make release -j4
|
||||
```
|
||||
\* `4` - number of CPU threads to use
|
||||
\* Add `CMAKE_PREFIX_PATH` enviroment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/5.9.7/clang_64 make release -j4`
|
||||
\* Add `CMAKE_PREFIX_PATH` environment variable to set a custom Qt install directory, e.g. `CMAKE_PREFIX_PATH=$HOME/Qt/5.9.7/clang_64 make release -j4`
|
||||
|
||||
The executable can be found in the `build/release/bin` folder.
|
||||
|
||||
|
||||
@@ -74,19 +74,19 @@ if(APPLE OR (WIN32 AND NOT STATIC))
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
list(APPEND WIN_DEPLOY_DLLS
|
||||
libicudtd68.dll
|
||||
libicuind68.dll
|
||||
libicuiod68.dll
|
||||
libicutud68.dll
|
||||
libicuucd68.dll
|
||||
libicudtd69.dll
|
||||
libicuind69.dll
|
||||
libicuiod69.dll
|
||||
libicutud69.dll
|
||||
libicuucd69.dll
|
||||
)
|
||||
else() # assume release
|
||||
list(APPEND WIN_DEPLOY_DLLS
|
||||
libicudt68.dll
|
||||
libicuin68.dll
|
||||
libicuio68.dll
|
||||
libicutu68.dll
|
||||
libicuuc68.dll
|
||||
libicudt69.dll
|
||||
libicuin69.dll
|
||||
libicuio69.dll
|
||||
libicutu69.dll
|
||||
libicuuc69.dll
|
||||
)
|
||||
endif()
|
||||
list(TRANSFORM WIN_DEPLOY_DLLS PREPEND "$ENV{MSYSTEM_PREFIX}/bin/")
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
# Copyright (c) 2014-2020, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
# - Try to find readline include dirs and libraries
|
||||
#
|
||||
# Automatically finds ccache build accelerator, if it's found in PATH.
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# project(monero)
|
||||
# include(FindCcache) # Include AFTER the project() macro to be able to reach the CMAKE_CXX_COMPILER variable
|
||||
#
|
||||
# Properties modified by this module:
|
||||
#
|
||||
# GLOBAL PROPERTY RULE_LAUNCH_COMPILE set to ccache, when ccache found
|
||||
# GLOBAL PROPERTY RULE_LAUNCH_LINK set to ccache, when ccache found
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if (CCACHE_FOUND)
|
||||
set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp")
|
||||
file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }")
|
||||
execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET)
|
||||
if (${RET} EQUAL 0)
|
||||
message("found usable ccache: ${CCACHE_FOUND}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}")
|
||||
else()
|
||||
message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}")
|
||||
endif()
|
||||
else()
|
||||
message("ccache NOT found!")
|
||||
endif()
|
||||
@@ -1,67 +0,0 @@
|
||||
# Copyright (c) 2014-2019, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
function (git_get_version_tag git directory result_var)
|
||||
execute_process(COMMAND "${git}" rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${directory}
|
||||
OUTPUT_VARIABLE COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT COMMIT)
|
||||
message(WARNING "${directory}: cannot determine current commit. Make sure that you are building from a Git working tree")
|
||||
set(${result_var} "unknown" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND "${git}" describe --tags --exact-match
|
||||
WORKING_DIRECTORY ${directory}
|
||||
OUTPUT_VARIABLE TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(TAG)
|
||||
message(STATUS "${directory}: building tagged release ${TAG}-${COMMIT}")
|
||||
set(${result_var} "${TAG}-${COMMIT}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND "${git}" describe --tags --long
|
||||
WORKING_DIRECTORY ${directory}
|
||||
OUTPUT_VARIABLE MOST_RECENT_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(MOST_RECENT_TAG)
|
||||
message(STATUS "${directory}: ahead of or behind a tagged release, building ${MOST_RECENT_TAG}")
|
||||
set(${result_var} "${MOST_RECENT_TAG}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "${directory}: building ${COMMIT} commit")
|
||||
set(${result_var} "${COMMIT}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -26,7 +26,8 @@
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
function (write_static_version_header VERSION_TAG_GUI)
|
||||
function (write_static_version_header tag)
|
||||
set(VERSION_TAG_GUI "${tag}" CACHE STRING "The tag portion of the Monero GUI software version" FORCE)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/version.js.in" "${CMAKE_CURRENT_SOURCE_DIR}/version.js")
|
||||
endfunction ()
|
||||
|
||||
@@ -36,11 +37,8 @@ if ("$Format:$" STREQUAL "")
|
||||
write_static_version_header("release")
|
||||
elseif (GIT_FOUND OR Git_FOUND)
|
||||
message(STATUS "Found Git: ${GIT_EXECUTABLE}")
|
||||
|
||||
include(GitGetVersionTag)
|
||||
git_get_version_tag(${GIT_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR} VERSION_TAG_GUI)
|
||||
STRING(REGEX REPLACE "^v([0-9])" "\\1" VERSION_TAG_GUI ${VERSION_TAG_GUI})
|
||||
write_static_version_header(${VERSION_TAG_GUI})
|
||||
get_version_tag_from_git("${GIT_EXECUTABLE}")
|
||||
write_static_version_header(${VERSIONTAG})
|
||||
else()
|
||||
message(STATUS "WARNING: Git was not found!")
|
||||
write_static_version_header("unknown")
|
||||
|
||||
@@ -37,27 +37,30 @@ RowLayout {
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
spacing: 4
|
||||
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: false
|
||||
spacing: 12
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
|
||||
StandardButton {
|
||||
id: button1
|
||||
small: true
|
||||
primary: false
|
||||
visible: button1.text
|
||||
}
|
||||
|
||||
|
||||
StandardButton {
|
||||
id: button2
|
||||
small: true
|
||||
primary: false
|
||||
visible: button2.text
|
||||
}
|
||||
|
||||
|
||||
StandardButton {
|
||||
id: button3
|
||||
small: true
|
||||
primary: false
|
||||
visible: button3.text
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,21 @@ import FontAwesome 1.0
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
MouseArea {
|
||||
signal cut()
|
||||
signal copy()
|
||||
signal paste()
|
||||
signal remove()
|
||||
signal selectAll()
|
||||
|
||||
id: root
|
||||
acceptedButtons: Qt.RightButton
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.RightButton)
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
root.parent.persistentSelection = true;
|
||||
contextMenu.open()
|
||||
root.parent.cursorVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
@@ -22,19 +29,50 @@ MouseArea {
|
||||
border.color: MoneroComponents.Style.buttonBackgroundColorDisabledHover
|
||||
border.width: 1
|
||||
radius: 2
|
||||
color: MoneroComponents.Style.buttonBackgroundColorDisabled
|
||||
color: MoneroComponents.Style.blackTheme ? MoneroComponents.Style.buttonBackgroundColorDisabled : "#E5E5E5"
|
||||
}
|
||||
|
||||
padding: 1
|
||||
width: 100
|
||||
width: 110
|
||||
x: root.mouseX
|
||||
y: root.mouseY
|
||||
|
||||
onClosed: {
|
||||
if (!root.parent.activeFocus) {
|
||||
root.parent.cursorVisible = false;
|
||||
}
|
||||
root.parent.persistentSelection = false;
|
||||
root.parent.forceActiveFocus()
|
||||
}
|
||||
|
||||
MoneroComponents.ContextMenuItem {
|
||||
enabled: root.parent.selectedText != "" && !root.parent.readOnly
|
||||
onTriggered: root.cut()
|
||||
text: qsTr("Cut") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.ContextMenuItem {
|
||||
enabled: root.parent.selectedText != ""
|
||||
onTriggered: root.copy()
|
||||
text: qsTr("Copy") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.ContextMenuItem {
|
||||
enabled: root.parent.canPaste === true
|
||||
glyphIcon: FontAwesome.paste
|
||||
onTriggered: root.paste()
|
||||
text: qsTr("Paste") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.ContextMenuItem {
|
||||
enabled: root.parent.selectedText != "" && !root.parent.readOnly
|
||||
onTriggered: root.remove()
|
||||
text: qsTr("Delete") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.ContextMenuItem {
|
||||
enabled: root.parent.text != ""
|
||||
onTriggered: root.selectAll()
|
||||
text: qsTr("Select All") + translationManager.emptyString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,21 +13,31 @@ MenuItem {
|
||||
|
||||
background: Rectangle {
|
||||
color: MoneroComponents.Style.buttonBackgroundColorDisabledHover
|
||||
opacity: mouse.containsMouse ? 1 : 0
|
||||
opacity: 0
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: menuItem.triggered()
|
||||
visible: menuItem.enabled
|
||||
onEntered: {
|
||||
parent.opacity = 1;
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0;
|
||||
}
|
||||
onClicked: {
|
||||
if (menuItem.enabled) {
|
||||
menuItem.triggered();
|
||||
parent.opacity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.leftMargin: 20
|
||||
anchors.rightMargin: 10
|
||||
opacity: menuItem.enabled ? 1 : 0.4
|
||||
spacing: 8
|
||||
@@ -42,7 +52,7 @@ MenuItem {
|
||||
}
|
||||
|
||||
Text {
|
||||
color: MoneroComponents.Style.buttonTextColor
|
||||
color: MoneroComponents.Style.blackTheme ? MoneroComponents.Style.buttonTextColor : MoneroComponents.Style.defaultFontColor
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 14
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -64,7 +64,7 @@ Item {
|
||||
Rectangle{
|
||||
id: rect
|
||||
anchors.fill: parent
|
||||
color: MoneroComponents.Style.buttonInlineBackgroundColor
|
||||
color: buttonArea.containsMouse ? MoneroComponents.Style.buttonInlineBackgroundColorHover : MoneroComponents.Style.buttonInlineBackgroundColor
|
||||
radius: 4
|
||||
|
||||
|
||||
@@ -101,13 +101,9 @@ Item {
|
||||
}
|
||||
onEntered: {
|
||||
tooltip.text ? tooltip.tooltipPopup.open() : ""
|
||||
rect.color = buttonColor ? buttonColor : "#707070";
|
||||
rect.opacity = 0.8;
|
||||
}
|
||||
onExited: {
|
||||
tooltip.text ? tooltip.tooltipPopup.close() : ""
|
||||
rect.opacity = 1.0;
|
||||
rect.color = buttonColor ? buttonColor : "#808080";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,16 @@ TextField {
|
||||
|
||||
MoneroComponents.ContextMenu {
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onCut: textField.cut();
|
||||
onCopy: textField.copy();
|
||||
onPaste: {
|
||||
textField.clear();
|
||||
var previoustextFieldLength = textField.length
|
||||
var previousCursorPosition = textField.cursorPosition;
|
||||
textField.paste();
|
||||
textField.forceActiveFocus()
|
||||
textField.cursorPosition = previousCursorPosition + (textField.length - previoustextFieldLength);
|
||||
}
|
||||
onRemove: textField.remove(selectionStart, selectionEnd);
|
||||
onSelectAll: textField.selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,16 @@ TextArea {
|
||||
|
||||
MoneroComponents.ContextMenu {
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onCut: textArea.cut();
|
||||
onCopy: textArea.copy();
|
||||
onPaste: {
|
||||
textArea.clear();
|
||||
var previoustextFieldLength = textArea.length
|
||||
var previousCursorPosition = textArea.cursorPosition;
|
||||
textArea.paste();
|
||||
textArea.forceActiveFocus()
|
||||
textArea.cursorPosition = previousCursorPosition + (textArea.length - previoustextFieldLength);
|
||||
}
|
||||
onRemove: textArea.remove(selectionStart, selectionEnd);
|
||||
onSelectAll: textArea.selectAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import "../components/effects/" as MoneroEffects
|
||||
Label {
|
||||
id: item
|
||||
fontSize: 18
|
||||
tooltipIconVisible: true
|
||||
|
||||
Rectangle {
|
||||
anchors.top: item.bottom
|
||||
|
||||
@@ -218,7 +218,7 @@ ColumnLayout {
|
||||
Layout.preferredHeight: inputHeight
|
||||
|
||||
leftPadding: item.inputPaddingLeft
|
||||
rightPadding: (inlineButtons.width > 0 ? inlineButtons.width + inlineButtons.spacing : 0) + inputPaddingRight
|
||||
rightPadding: (inlineButtons.width > 0 ? inlineButtons.width + inlineButtons.spacing : 0) + inputPaddingRight + (password || passwordLinked ? 45 : 0)
|
||||
topPadding: item.inputPaddingTop
|
||||
bottomPadding: item.inputPaddingBottom
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ import "." as MoneroComponents
|
||||
|
||||
Rectangle {
|
||||
default property list<MoneroComponents.NavbarItem> items
|
||||
property alias currentIndex: repeater.currentIndex
|
||||
property alias previousIndex: repeater.previousIndex
|
||||
|
||||
color: "transparent"
|
||||
height: grid.height
|
||||
@@ -100,7 +102,10 @@ Rectangle {
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: items.length
|
||||
property int currentIndex: 0
|
||||
property int previousIndex: 0
|
||||
|
||||
RowLayout {
|
||||
spacing: 0
|
||||
@@ -149,7 +154,11 @@ Rectangle {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: items[index].selected()
|
||||
onClicked: {
|
||||
repeater.previousIndex = repeater.currentIndex;
|
||||
repeater.currentIndex = index;
|
||||
items[index].selected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ Rectangle {
|
||||
case Wallet.ConnectionStatus_Connected:
|
||||
if (!appWindow.daemonSynced)
|
||||
return qsTr("Synchronizing");
|
||||
if (persistentSettings.useRemoteNode && persistentSettings.allowRemoteNodeMining && appWindow.isMining)
|
||||
return qsTr("Remote node") + " + " + qsTr("Mining");
|
||||
if (persistentSettings.useRemoteNode)
|
||||
return qsTr("Remote node");
|
||||
return appWindow.isMining ? qsTr("Connected") + " + " + qsTr("Mining"): qsTr("Connected");
|
||||
|
||||
@@ -108,7 +108,11 @@ Item {
|
||||
leftPanel.enabled = true
|
||||
middlePanel.enabled = true
|
||||
wizard.enabled = true
|
||||
titleBar.state = "default"
|
||||
if (rootItem.state == "wizard") {
|
||||
titleBar.state = "essentials"
|
||||
} else {
|
||||
titleBar.state = "default"
|
||||
}
|
||||
|
||||
root.visible = false;
|
||||
appWindow.hideBalanceForced = false;
|
||||
|
||||
@@ -42,6 +42,13 @@ MoneroComponents.Dialog {
|
||||
|
||||
onActiveFocusChanged: activeFocus && remoteNodeAddress.forceActiveFocus()
|
||||
|
||||
function onOk() {
|
||||
root.success = true;
|
||||
root.close();
|
||||
}
|
||||
|
||||
function onCancel() { root.close(); }
|
||||
|
||||
function add(callbackOnSuccess) {
|
||||
root.editMode = false;
|
||||
root.callbackOnSuccess = callbackOnSuccess;
|
||||
@@ -89,6 +96,11 @@ MoneroComponents.Dialog {
|
||||
|
||||
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
||||
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
||||
|
||||
Keys.enabled: root.visible
|
||||
Keys.onEnterPressed: root.onOk()
|
||||
Keys.onReturnPressed: root.onOk()
|
||||
Keys.onEscapePressed: root.onCancel()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -116,6 +128,11 @@ MoneroComponents.Dialog {
|
||||
placeholderFontSize: 15
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
|
||||
Keys.enabled: root.visible
|
||||
Keys.onEnterPressed: root.onOk()
|
||||
Keys.onReturnPressed: root.onOk()
|
||||
Keys.onEscapePressed: root.onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 80
|
||||
color: "transparent"
|
||||
property var trusted: remoteNodesModel.get(index).trusted
|
||||
property var trusted: remoteNodesModel.get(index) ? remoteNodesModel.get(index).trusted : false
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: addressText
|
||||
@@ -107,8 +107,9 @@ ColumnLayout {
|
||||
MoneroComponents.Label {
|
||||
id: trustedDaemonCheckMark
|
||||
anchors.left: addressText.right
|
||||
anchors.leftMargin: 6
|
||||
anchors.leftMargin: 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 2
|
||||
z: itemMouseArea.z + 1
|
||||
fontSize: 16
|
||||
fontFamily: FontAwesome.fontFamilySolid
|
||||
@@ -144,6 +145,9 @@ ColumnLayout {
|
||||
tooltipLeft: true
|
||||
onClicked: remoteNodeDialog.edit(remoteNodesModel.get(index), function (remoteNode) {
|
||||
remoteNodesModel.set(index, remoteNode)
|
||||
if (index === remoteNodesModel.selected) {
|
||||
remoteNodesModel.applyRemoteNode(index)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ import "../components" as MoneroComponents
|
||||
ColumnLayout {
|
||||
id: settingsListItem
|
||||
property alias iconText: iconLabel.text
|
||||
property alias symbol: symbolText.text
|
||||
property alias description: area.text
|
||||
property alias title: header.text
|
||||
property bool isLast: false
|
||||
property bool enabled: true
|
||||
signal clicked()
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -37,6 +39,7 @@ ColumnLayout {
|
||||
width: parent.width
|
||||
height: header.height + area.contentHeight
|
||||
color: "transparent";
|
||||
opacity: settingsListItem.enabled ? 1 : 0.25
|
||||
anchors.left: parent.left
|
||||
anchors.bottomMargin: 4
|
||||
anchors.topMargin: 4
|
||||
@@ -102,6 +105,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
visible: settingsListItem.enabled
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
@@ -111,5 +115,17 @@ ColumnLayout {
|
||||
settingsListItem.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: symbolText
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 44
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 12
|
||||
font.bold: true
|
||||
color: MoneroComponents.Style.menuButtonTextColor
|
||||
visible: appWindow.ctrlPressed
|
||||
themeTransition: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,16 +39,12 @@ Item {
|
||||
property bool primary: true
|
||||
property string rightIcon: ""
|
||||
property string rightIconInactive: ""
|
||||
property color textColor: !button.enabled
|
||||
? MoneroComponents.Style.buttonTextColorDisabled
|
||||
: primary
|
||||
? MoneroComponents.Style.buttonTextColor
|
||||
: MoneroComponents.Style.buttonSecondaryTextColor;
|
||||
property color textColor: primary ? MoneroComponents.Style.buttonTextColor : MoneroComponents.Style.buttonSecondaryTextColor;
|
||||
property bool small: false
|
||||
property alias text: label.text
|
||||
property alias fontBold: label.font.bold
|
||||
property int fontSize: {
|
||||
if(small) return 14;
|
||||
if(small) return 13.5;
|
||||
else return 16;
|
||||
}
|
||||
property alias label: label
|
||||
@@ -72,6 +68,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
radius: 3
|
||||
border.width: parent.focus && parent.enabled ? 1 : 0
|
||||
opacity: 1
|
||||
|
||||
state: button.enabled ? "active" : "disabled"
|
||||
Component.onCompleted: state = state
|
||||
@@ -102,7 +99,14 @@ Item {
|
||||
when: !button.enabled
|
||||
PropertyChanges {
|
||||
target: buttonRect
|
||||
color: MoneroComponents.Style.buttonBackgroundColorDisabled
|
||||
opacity: 0.5
|
||||
color: primary
|
||||
? MoneroComponents.Style.buttonBackgroundColor
|
||||
: MoneroComponents.Style.buttonSecondaryBackgroundColor
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -122,7 +126,7 @@ Item {
|
||||
MoneroComponents.TextPlain {
|
||||
id: label
|
||||
font.family: MoneroComponents.Style.fontBold.name
|
||||
font.bold: true
|
||||
font.bold: button.primary ? true : false
|
||||
font.pixelSize: button.fontSize
|
||||
color: !buttonArea.pressed ? button.textColor : "transparent"
|
||||
visible: text !== ""
|
||||
@@ -145,6 +149,7 @@ Item {
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
width: button.small ? 16 : 20
|
||||
height: button.small ? 16 : 20
|
||||
opacity: buttonRect.opacity
|
||||
source: {
|
||||
if (fontAwesomeIcon) return "";
|
||||
if(button.rightIconInactive !== "" && !button.enabled) {
|
||||
|
||||
@@ -191,7 +191,7 @@ ColumnLayout {
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 0
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.bold: true
|
||||
font.bold: false
|
||||
font.pixelSize: fontItemSize
|
||||
color: itemArea.containsMouse || index === columnid.currentIndex || itemArea.containsMouse ? "#FA6800" : "#FFFFFF"
|
||||
text: qsTr(column1) + translationManager.emptyString
|
||||
|
||||
@@ -42,11 +42,12 @@ QtObject {
|
||||
property string buttonBackgroundColorDisabled: blackTheme ? _b_buttonBackgroundColorDisabled : _w_buttonBackgroundColorDisabled
|
||||
property string buttonBackgroundColorDisabledHover: blackTheme ? _b_buttonBackgroundColorDisabledHover : _w_buttonBackgroundColorDisabledHover
|
||||
property string buttonInlineBackgroundColor: blackTheme ? _b_buttonInlineBackgroundColor : _w_buttonInlineBackgroundColor
|
||||
property string buttonInlineBackgroundColorHover: blackTheme ? _b_buttonInlineBackgroundColorHover : _w_buttonInlineBackgroundColorHover
|
||||
property string buttonTextColor: blackTheme ? _b_buttonTextColor : _w_buttonTextColor
|
||||
property string buttonTextColorDisabled: blackTheme ? _b_buttonTextColorDisabled : _w_buttonTextColorDisabled
|
||||
property string buttonSecondaryBackgroundColor: "#d9d9d9"
|
||||
property string buttonSecondaryBackgroundColorHover: "#a6a6a6"
|
||||
property string buttonSecondaryTextColor: "#4d4d4d"
|
||||
property string buttonSecondaryBackgroundColor: blackTheme ? _b_buttonSecondaryBackgroundColor : _w_buttonSecondaryBackgroundColor
|
||||
property string buttonSecondaryBackgroundColorHover: blackTheme ? _b_buttonSecondaryBackgroundColorHover : _w_buttonSecondaryBackgroundColorHover
|
||||
property string buttonSecondaryTextColor: blackTheme ? _b_buttonSecondaryTextColor : _w_buttonSecondaryTextColor
|
||||
property string dividerColor: blackTheme ? _b_dividerColor : _w_dividerColor
|
||||
property real dividerOpacity: blackTheme ? _b_dividerOpacity : _w_dividerOpacity
|
||||
|
||||
@@ -102,8 +103,12 @@ QtObject {
|
||||
property string _b_buttonBackgroundColorDisabled: "#707070"
|
||||
property string _b_buttonBackgroundColorDisabledHover: "#808080"
|
||||
property string _b_buttonInlineBackgroundColor: "#707070"
|
||||
property string _b_buttonInlineBackgroundColorHover: "#808080"
|
||||
property string _b_buttonTextColor: "white"
|
||||
property string _b_buttonTextColorDisabled: "black"
|
||||
property string _b_buttonSecondaryBackgroundColor: "#707070"
|
||||
property string _b_buttonSecondaryBackgroundColorHover: "#808080"
|
||||
property string _b_buttonSecondaryTextColor: "white"
|
||||
property string _b_dividerColor: "white"
|
||||
property real _b_dividerOpacity: 0.20
|
||||
|
||||
@@ -158,9 +163,13 @@ QtObject {
|
||||
property string _w_buttonBackgroundColorHover: "#E65E00"
|
||||
property string _w_buttonBackgroundColorDisabled: "#bbbbbb"
|
||||
property string _w_buttonBackgroundColorDisabledHover: "#D1D1D1"
|
||||
property string _w_buttonInlineBackgroundColor: "#bbbbbb"
|
||||
property string _w_buttonInlineBackgroundColor: "#d9d9d9"
|
||||
property string _w_buttonInlineBackgroundColorHover: "#C8C8C8"
|
||||
property string _w_buttonTextColor: "white"
|
||||
property string _w_buttonTextColorDisabled: "black"
|
||||
property string _w_buttonSecondaryBackgroundColor: "#d9d9d9"
|
||||
property string _w_buttonSecondaryBackgroundColorHover: "#C8C8C8"
|
||||
property string _w_buttonSecondaryTextColor: "#4d4d4d"
|
||||
property string _w_dividerColor: "black"
|
||||
property real _w_dividerOpacity: 0.20
|
||||
|
||||
@@ -185,7 +194,7 @@ QtObject {
|
||||
property string _w_menuButtonImageRightColorActive: "#FA6800"
|
||||
property string _w_menuButtonImageRightColor: "#808080"
|
||||
property string _w_menuButtonImageDotArrowSource: "qrc:///images/arrow-right-medium-white.png"
|
||||
property string _w_inlineButtonTextColor: "black"
|
||||
property string _w_inlineButtonTextColor: "#4d4d4d"
|
||||
property string _w_inlineButtonBorderColor: "transparent"
|
||||
property string _w_appWindowBackgroundColor: "black"
|
||||
property string _w_appWindowBorderColor: "#dedede"
|
||||
|
||||
@@ -57,17 +57,20 @@ Rectangle {
|
||||
signal minimizeClicked
|
||||
signal languageClicked
|
||||
signal closeWalletClicked
|
||||
signal lockWalletClicked
|
||||
|
||||
state: "default"
|
||||
states: [
|
||||
State {
|
||||
name: "default";
|
||||
PropertyChanges { target: btnCloseWallet; visible: true}
|
||||
PropertyChanges { target: btnLockWallet; visible: true}
|
||||
PropertyChanges { target: btnLanguageToggle; visible: true}
|
||||
}, State {
|
||||
// show only theme switcher and window controls
|
||||
name: "essentials";
|
||||
PropertyChanges { target: btnCloseWallet; visible: false}
|
||||
PropertyChanges { target: btnLockWallet; visible: false}
|
||||
PropertyChanges { target: btnLanguageToggle; visible: false}
|
||||
}
|
||||
]
|
||||
@@ -91,6 +94,46 @@ Rectangle {
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
|
||||
// lock wallet
|
||||
Rectangle {
|
||||
id: btnLockWallet
|
||||
color: "transparent"
|
||||
Layout.preferredWidth: parent.height
|
||||
Layout.preferredHeight: parent.height
|
||||
|
||||
Text {
|
||||
text: FontAwesome.lock
|
||||
font.family: FontAwesome.fontFamilySolid
|
||||
font.pixelSize: 16
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.styleName: "Solid"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
opacity: 0.75
|
||||
}
|
||||
|
||||
MoneroComponents.Tooltip {
|
||||
id: btnLockWalletTooltip
|
||||
anchors.fill: parent
|
||||
text: qsTr("Lock this wallet") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: {
|
||||
parent.color = MoneroComponents.Style.titleBarButtonHoverColor
|
||||
btnLockWalletTooltip.tooltipPopup.open()
|
||||
}
|
||||
onExited: {
|
||||
parent.color = "transparent"
|
||||
btnLockWalletTooltip.tooltipPopup.close()
|
||||
}
|
||||
onClicked: root.lockWalletClicked(leftPanel.visible)
|
||||
}
|
||||
}
|
||||
|
||||
// collapse sidebar
|
||||
Rectangle {
|
||||
id: btnCloseWallet
|
||||
|
||||
@@ -48,7 +48,7 @@ Rectangle {
|
||||
Text {
|
||||
id: icon
|
||||
visible: tooltipIconVisible
|
||||
color: MoneroComponents.Style.orange
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.family: FontAwesome.fontFamily
|
||||
font.pixelSize: 10
|
||||
font.styleName: "Regular"
|
||||
@@ -95,11 +95,12 @@ Rectangle {
|
||||
delay: 200
|
||||
|
||||
RowLayout {
|
||||
Layout.maximumWidth: 350
|
||||
Layout.maximumWidth: 370
|
||||
|
||||
Text {
|
||||
id: tooltip
|
||||
width: contentWidth
|
||||
Layout.maximumWidth: 370
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 12
|
||||
|
||||
@@ -338,8 +338,9 @@ Rectangle {
|
||||
spacing: 16
|
||||
|
||||
Text {
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.pixelSize: 15
|
||||
property bool maliciousTxFee: parseFloat(root.transactionFee) > 0.01
|
||||
color: maliciousTxFee ? "red" : MoneroComponents.Style.defaultFontColor
|
||||
font.pixelSize: maliciousTxFee ? 20 : 15
|
||||
text: {
|
||||
if (currentWallet) {
|
||||
if (!root.transactionFee) {
|
||||
@@ -349,7 +350,7 @@ Rectangle {
|
||||
return qsTr("Calculating fee") + "..." + translationManager.emptyString;
|
||||
}
|
||||
} else {
|
||||
return root.transactionFee + " XMR"
|
||||
return root.transactionFee + " XMR" + (maliciousTxFee ? " (HIGH FEE)" : "")
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
|
||||
@@ -35,6 +35,7 @@ Object {
|
||||
property string arrowLeft : "\uf060"
|
||||
property string arrowRight : "\uf061"
|
||||
property string cashRegister: "\uf788"
|
||||
property string checkCircle: "\uf058"
|
||||
property string clipboard : "\uf0ea"
|
||||
property string clockO : "\uf017"
|
||||
property string cloud : "\uf0c2"
|
||||
@@ -52,6 +53,7 @@ Object {
|
||||
property string info : "\uf129"
|
||||
property string key : "\uf084"
|
||||
property string language : "\uf1ab"
|
||||
property string lock : "\uf023"
|
||||
property string minus : "\uf068"
|
||||
property string minusCircle : "\uf056"
|
||||
property string moonO : "\uf186"
|
||||
|
||||
BIN
images/open-wallet-from-file-mainnet.png
Normal file
|
After Width: | Height: | Size: 640 B |
BIN
images/open-wallet-from-file-mainnet@2x.png
Normal file
|
After Width: | Height: | Size: 937 B |
BIN
images/open-wallet-from-file-stagenet.png
Normal file
|
After Width: | Height: | Size: 569 B |
BIN
images/open-wallet-from-file-stagenet@2x.png
Normal file
|
After Width: | Height: | Size: 717 B |
BIN
images/open-wallet-from-file-testnet.png
Normal file
|
After Width: | Height: | Size: 608 B |
BIN
images/open-wallet-from-file-testnet@2x.png
Normal file
|
After Width: | Height: | Size: 779 B |
BIN
images/open-wallet-from-file-trezor.png
Normal file
|
After Width: | Height: | Size: 969 B |
BIN
images/open-wallet-from-file-trezor@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
images/open-wallet-from-file-view-only.png
Normal file
|
After Width: | Height: | Size: 958 B |
BIN
images/open-wallet-from-file-view-only@2x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 742 B After Width: | Height: | Size: 424 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 678 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -61,6 +61,10 @@ function checkSignature(signature) {
|
||||
if ((signature.length - 12) % 88 != 0)
|
||||
return false;
|
||||
return check256(signature, signature.length);
|
||||
} else if (signature.indexOf("ReserveProofV") === 0) {
|
||||
if ((signature.length - 14) % 447 != 0)
|
||||
return false;
|
||||
return check256(signature, signature.length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -73,30 +77,3 @@ function isValidOpenAliasAddress(address) {
|
||||
// we can get an awful lot of valid domains, including non ASCII chars... accept anything
|
||||
return true
|
||||
}
|
||||
|
||||
function makeQRCodeString(addr, amount, txDescription, recipientName) {
|
||||
var XMR_URI_SCHEME = "monero:"
|
||||
var XMR_AMOUNT = "tx_amount"
|
||||
var XMR_RECIPIENT_NAME = "recipient_name"
|
||||
var XMR_TX_DESCRIPTION = "tx_description"
|
||||
var qrCodeString =""
|
||||
qrCodeString += (XMR_URI_SCHEME + addr)
|
||||
if (amount !== undefined && amount !== ""){
|
||||
qrCodeString += ("?" + XMR_AMOUNT + "=" + amount)
|
||||
}
|
||||
if (txDescription !== undefined && txDescription !== ""){
|
||||
if (amount == ""){
|
||||
qrCodeString += ("?" + XMR_TX_DESCRIPTION + "=" + encodeURI(txDescription))
|
||||
} else {
|
||||
qrCodeString += ("&" + XMR_TX_DESCRIPTION + "=" + encodeURI(txDescription))
|
||||
}
|
||||
}
|
||||
if (recipientName !== undefined && recipientName !== ""){
|
||||
if (amount == "" && txDescription == ""){
|
||||
qrCodeString += ("?" + XMR_RECIPIENT_NAME + "=" + encodeURI(recipientName))
|
||||
} else {
|
||||
qrCodeString += ("&" + XMR_RECIPIENT_NAME + "=" + encodeURI(recipientName))
|
||||
}
|
||||
}
|
||||
return qrCodeString
|
||||
}
|
||||
|
||||
BIN
lang/flags/el.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
lang/flags/is.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
lang/flags/vi.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
@@ -61,4 +61,9 @@ Lojban
|
||||
<!-- <language display_name="کورمانجی" locale="kmr_KMR" wallet_language="English" flag="/lang/flags/ku.png" qs="none"/> -->
|
||||
<!-- <language display_name="বাংলা" locale="bn_BN" wallet_language="English" flag="/lang/flags/bd.png" qs="none"/> -->
|
||||
<!-- <language display_name="Gaeilge" locale="ga_GA" wallet_language="English" flag="/lang/flags/irl.png" qs="none"/> -->
|
||||
<language display_name="Afrikaans" locale="af_AF" wallet_language="English" flag="/lang/flags/za.png" qs="none"/>
|
||||
<language display_name="Ελληνικά" locale="el_GR" wallet_language="English" flag="/lang/flags/el.png" qs="none"/>
|
||||
<language display_name="தமிழ்" locale="ta_IN" wallet_language="English" flag="/lang/flags/in.png" qs="none"/>
|
||||
<language display_name="Tiếng Việt" locale="vi_VN" wallet_language="English" flag="/lang/flags/vi.png" qs="none"/>
|
||||
<language display_name="Íslenska" locale="is_IS" wallet_language="English" flag="/lang/flags/is.png" qs="none"/>
|
||||
</languages>
|
||||
|
||||
120
main.qml
@@ -42,6 +42,7 @@ import moneroComponents.WalletManager 1.0
|
||||
import moneroComponents.PendingTransaction 1.0
|
||||
import moneroComponents.NetworkType 1.0
|
||||
import moneroComponents.Settings 1.0
|
||||
import moneroComponents.P2PoolManager 1.0
|
||||
|
||||
import "components"
|
||||
import "components" as MoneroComponents
|
||||
@@ -131,6 +132,17 @@ ApplicationWindow {
|
||||
leftPanel.selectItem(page)
|
||||
}
|
||||
|
||||
function lock() {
|
||||
passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
|
||||
passwordDialog.onAcceptedCallback = function() {
|
||||
if(walletPassword === passwordDialog.password)
|
||||
passwordDialog.close();
|
||||
else
|
||||
passwordDialog.showError(qsTr("Wrong password") + translationManager.emptyString);
|
||||
}
|
||||
passwordDialog.open(usefulName(persistentSettings.wallet_path));
|
||||
}
|
||||
|
||||
function sequencePressed(obj, seq) {
|
||||
if(seq === undefined || !leftPanel.enabled)
|
||||
return
|
||||
@@ -139,6 +151,8 @@ ApplicationWindow {
|
||||
return
|
||||
}
|
||||
|
||||
// lock wallet on demand
|
||||
if(seq === "Ctrl+L" && !passwordDialog.visible) lock()
|
||||
if(seq === "Ctrl+S") middlePanel.state = "Transfer"
|
||||
else if(seq === "Ctrl+R") middlePanel.state = "Receive"
|
||||
else if(seq === "Ctrl+H") middlePanel.state = "History"
|
||||
@@ -259,9 +273,6 @@ ApplicationWindow {
|
||||
walletPassword,
|
||||
persistentSettings.nettype,
|
||||
persistentSettings.kdfRounds);
|
||||
|
||||
// Hide titlebar based on persistentSettings.customDecorations
|
||||
titleBar.visible = persistentSettings.customDecorations;
|
||||
}
|
||||
|
||||
function closeWallet(callback) {
|
||||
@@ -721,6 +732,7 @@ ApplicationWindow {
|
||||
if (splash) {
|
||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."));
|
||||
}
|
||||
p2poolManager.exit()
|
||||
daemonManager.stopAsync(persistentSettings.nettype, function(result) {
|
||||
daemonStartStopInProgress = 0;
|
||||
if (splash) {
|
||||
@@ -733,14 +745,18 @@ ApplicationWindow {
|
||||
function onDaemonStarted(){
|
||||
console.log("daemon started");
|
||||
daemonStartStopInProgress = 0;
|
||||
currentWallet.connected(true);
|
||||
// resume refresh
|
||||
currentWallet.startRefresh();
|
||||
if (currentWallet) {
|
||||
currentWallet.connected(true);
|
||||
// resume refresh
|
||||
currentWallet.startRefresh();
|
||||
}
|
||||
// resume simplemode connection timer
|
||||
appWindow.disconnectedEpoch = Utils.epoch();
|
||||
}
|
||||
function onDaemonStopped(){
|
||||
currentWallet.connected(true);
|
||||
if (currentWallet) {
|
||||
currentWallet.connected(true);
|
||||
}
|
||||
}
|
||||
|
||||
function onDaemonStartFailure(error) {
|
||||
@@ -984,24 +1000,28 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
// called on "getProof"
|
||||
function handleGetProof(txid, address, message) {
|
||||
console.log("Getting payment proof: ")
|
||||
console.log("\ttxid: ", txid,
|
||||
", address: ", address,
|
||||
", message: ", message);
|
||||
|
||||
function spendProofFallback(txid, result){
|
||||
if (!result || result.indexOf("error|") === 0) {
|
||||
currentWallet.getSpendProofAsync(txid, message, txProofComputed);
|
||||
} else {
|
||||
txProofComputed(txid, result);
|
||||
function handleGetProof(txid, address, message, amount) {
|
||||
if (amount.length > 0) {
|
||||
var result = currentWallet.getReserveProof(false, currentWallet.currentSubaddressAccount, walletManager.amountFromString(amount), message)
|
||||
txProofComputed(null, result)
|
||||
} else {
|
||||
console.log("Getting payment proof: ")
|
||||
console.log("\ttxid: ", txid,
|
||||
", address: ", address,
|
||||
", message: ", message);
|
||||
function spendProofFallback(txid, result){
|
||||
if (!result || result.indexOf("error|") === 0) {
|
||||
currentWallet.getSpendProofAsync(txid, message, txProofComputed);
|
||||
} else {
|
||||
txProofComputed(txid, result);
|
||||
}
|
||||
}
|
||||
if (address.length > 0)
|
||||
currentWallet.getTxProofAsync(txid, address, message, spendProofFallback);
|
||||
else
|
||||
spendProofFallback(txid, null);
|
||||
}
|
||||
|
||||
if (address.length > 0)
|
||||
currentWallet.getTxProofAsync(txid, address, message, spendProofFallback);
|
||||
else
|
||||
spendProofFallback(txid, null);
|
||||
informationPopup.open()
|
||||
}
|
||||
|
||||
function txProofComputed(txid, result){
|
||||
@@ -1024,12 +1044,18 @@ ApplicationWindow {
|
||||
", signature: ", signature);
|
||||
|
||||
var result;
|
||||
if (address.length > 0)
|
||||
var isReserveProof = signature.indexOf("ReserveProofV") === 0;
|
||||
if (address.length > 0 && !isReserveProof) {
|
||||
result = currentWallet.checkTxProof(txid, address, message, signature);
|
||||
else
|
||||
}
|
||||
else if (isReserveProof) {
|
||||
result = currentWallet.checkReserveProof(address, message, signature);
|
||||
}
|
||||
else {
|
||||
result = currentWallet.checkSpendProof(txid, message, signature);
|
||||
}
|
||||
var results = result.split("|");
|
||||
if (address.length > 0 && results.length == 5 && results[0] === "true") {
|
||||
if (address.length > 0 && results.length == 5 && results[0] === "true" && !isReserveProof) {
|
||||
var good = results[1] === "true";
|
||||
var received = results[2];
|
||||
var in_pool = results[3] === "true";
|
||||
@@ -1057,6 +1083,12 @@ ApplicationWindow {
|
||||
informationPopup.title = qsTr("Payment proof check") + translationManager.emptyString;
|
||||
informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical;
|
||||
informationPopup.text = good ? qsTr("Good signature") : qsTr("Bad signature");
|
||||
}
|
||||
else if (isReserveProof && results[0] === "true") {
|
||||
var good = results[1] === "true";
|
||||
informationPopup.title = qsTr("Reserve proof check") + translationManager.emptyString;
|
||||
informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical;
|
||||
informationPopup.text = good ? qsTr("Good signature on %1 total and %2 spent.").arg(results[2]).arg(results[3]) : qsTr("Bad signature");
|
||||
}
|
||||
else {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
@@ -1124,13 +1156,10 @@ ApplicationWindow {
|
||||
Timer {
|
||||
id: fiatPriceTimer
|
||||
interval: 1000 * 60;
|
||||
running: persistentSettings.fiatPriceEnabled;
|
||||
running: persistentSettings.fiatPriceEnabled && currentWallet !== undefined
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if(persistentSettings.fiatPriceEnabled)
|
||||
appWindow.fiatApiRefresh();
|
||||
}
|
||||
triggeredOnStart: false
|
||||
onTriggered: appWindow.fiatApiRefresh()
|
||||
triggeredOnStart: true
|
||||
}
|
||||
|
||||
function fiatApiParseTicker(url, resp, currency){
|
||||
@@ -1269,14 +1298,6 @@ ApplicationWindow {
|
||||
leftPanel.balanceFiatString = bFiat;
|
||||
}
|
||||
|
||||
function fiatTimerStart(){
|
||||
fiatPriceTimer.start();
|
||||
}
|
||||
|
||||
function fiatTimerStop(){
|
||||
fiatPriceTimer.stop();
|
||||
}
|
||||
|
||||
function fiatApiError(msg){
|
||||
console.log("fiatPriceError: " + msg);
|
||||
}
|
||||
@@ -1332,12 +1353,8 @@ ApplicationWindow {
|
||||
openWallet("wizard");
|
||||
}
|
||||
|
||||
if(persistentSettings.fiatPriceEnabled){
|
||||
appWindow.fiatApiRefresh();
|
||||
appWindow.fiatTimerStart();
|
||||
}
|
||||
|
||||
if (persistentSettings.askDesktopShortcut && !persistentSettings.portable) {
|
||||
const desktopEntryEnabled = (typeof builtWithDesktopEntry != "undefined") && builtWithDesktopEntry;
|
||||
if (persistentSettings.askDesktopShortcut && !persistentSettings.portable && desktopEntryEnabled) {
|
||||
persistentSettings.askDesktopShortcut = false;
|
||||
|
||||
if (isTails) {
|
||||
@@ -1374,6 +1391,8 @@ ApplicationWindow {
|
||||
property string account_name
|
||||
property string wallet_path
|
||||
property bool allow_background_mining : false
|
||||
property bool allow_p2pool_mining : false
|
||||
property bool allowRemoteNodeMining : false
|
||||
property bool miningIgnoreBattery : true
|
||||
property var nettype: NetworkType.MAINNET
|
||||
property int restore_height : 0
|
||||
@@ -1382,6 +1401,7 @@ ApplicationWindow {
|
||||
property bool is_recovering_from_device : false
|
||||
property bool customDecorations : true
|
||||
property string daemonFlags
|
||||
property string p2poolFlags
|
||||
property int logLevel: 0
|
||||
property string logCategories: ""
|
||||
property string daemonUsername: "" // TODO: drop after v0.17.2.0 release
|
||||
@@ -1691,6 +1711,9 @@ ApplicationWindow {
|
||||
informationPopup.open();
|
||||
}
|
||||
onRejectedNewPassword: {}
|
||||
Keys.enabled: !passwordDialog.visible && informationPopup.visible
|
||||
Keys.onEnterPressed: informationPopup.close()
|
||||
Keys.onReturnPressed: informationPopup.close()
|
||||
}
|
||||
|
||||
DevicePassphraseDialog {
|
||||
@@ -1898,10 +1921,11 @@ ApplicationWindow {
|
||||
TitleBar {
|
||||
id: titleBar
|
||||
visible: persistentSettings.customDecorations && middlePanel.state !== "Merchant"
|
||||
walletName: persistentSettings.displayWalletNameInTitleBar ? appWindow.walletName : ""
|
||||
walletName: persistentSettings.displayWalletNameInTitleBar && rootItem.state != "wizard" ? appWindow.walletName : ""
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
onCloseClicked: appWindow.close();
|
||||
onLockWalletClicked: appWindow.lock();
|
||||
onLanguageClicked: appWindow.toggleLanguageView();
|
||||
onCloseWalletClicked: appWindow.showWizard();
|
||||
onMaximizeClicked: appWindow.visibility = appWindow.visibility !== Window.Maximized ? Window.Maximized : Window.Windowed
|
||||
@@ -2142,7 +2166,7 @@ ApplicationWindow {
|
||||
console.log("close accepted");
|
||||
// Close wallet non async on exit
|
||||
daemonManager.exit();
|
||||
|
||||
p2poolManager.exit();
|
||||
closeWallet(Qt.quit);
|
||||
}
|
||||
|
||||
|
||||
2
monero
@@ -164,11 +164,48 @@ Rectangle {
|
||||
id: addressRow
|
||||
spacing: 0
|
||||
|
||||
MoneroComponents.LabelSubheader {
|
||||
Layout.fillWidth: true
|
||||
fontSize: 24
|
||||
textFormat: Text.RichText
|
||||
text: qsTr("Accounts") + translationManager.emptyString
|
||||
RowLayout {
|
||||
spacing: 0
|
||||
|
||||
MoneroComponents.LabelSubheader {
|
||||
Layout.fillWidth: true
|
||||
fontSize: 24
|
||||
textFormat: Text.RichText
|
||||
text: qsTr("Accounts") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: createNewAccountButton
|
||||
visible: !selectAndSend
|
||||
small: true
|
||||
text: qsTr("Create new account") + translationManager.emptyString
|
||||
fontSize: 13
|
||||
onClicked: {
|
||||
inputDialog.labelText = qsTr("Set the label of the new account:") + translationManager.emptyString
|
||||
inputDialog.onAcceptedCallback = function() {
|
||||
appWindow.currentWallet.subaddressAccount.addRow(inputDialog.inputText)
|
||||
appWindow.currentWallet.switchSubaddressAccount(appWindow.currentWallet.numSubaddressAccounts() - 1)
|
||||
appWindow.onWalletUpdate();
|
||||
}
|
||||
inputDialog.onRejectedCallback = null;
|
||||
inputDialog.open()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: createNewAccountButton.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.left: createNewAccountButton.left
|
||||
anchors.right: createNewAccountButton.right
|
||||
height: 2
|
||||
color: MoneroComponents.Style.appWindowBorderColor
|
||||
|
||||
MoneroEffects.ColorTransition {
|
||||
targetObj: parent
|
||||
blackColor: MoneroComponents.Style._b_appWindowBorderColor
|
||||
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -349,30 +386,6 @@ Rectangle {
|
||||
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: addNewAccountCheckbox
|
||||
visible: !selectAndSend
|
||||
border: false
|
||||
uncheckedIcon: FontAwesome.plusCircle
|
||||
toggleOnClick: false
|
||||
fontAwesomeIcons: true
|
||||
fontSize: 16
|
||||
iconOnTheLeft: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
text: qsTr("Create new account") + translationManager.emptyString;
|
||||
onClicked: {
|
||||
inputDialog.labelText = qsTr("Set the label of the new account:") + translationManager.emptyString
|
||||
inputDialog.onAcceptedCallback = function() {
|
||||
appWindow.currentWallet.subaddressAccount.addRow(inputDialog.inputText)
|
||||
appWindow.currentWallet.switchSubaddressAccount(appWindow.currentWallet.numSubaddressAccounts() - 1)
|
||||
appWindow.onWalletUpdate();
|
||||
}
|
||||
inputDialog.onRejectedCallback = null;
|
||||
inputDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,8 @@ Rectangle {
|
||||
MoneroComponents.StandardButton {
|
||||
id: addFirstEntryButton
|
||||
Layout.topMargin: 20
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
small: true
|
||||
text: qsTr("Add an address") + translationManager.emptyString
|
||||
onClicked: {
|
||||
root.showAddAddress();
|
||||
@@ -115,6 +117,18 @@ Rectangle {
|
||||
text: qsTr("Address book") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: addAddressButton
|
||||
Layout.bottomMargin: 8
|
||||
Layout.alignment: Qt.AlignRight
|
||||
small: true
|
||||
text: qsTr("Add address") + translationManager.emptyString
|
||||
fontSize: 13
|
||||
onClicked: {
|
||||
root.showAddAddress();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: addressBookListRow
|
||||
property int addressBookListItemHeight: 50
|
||||
@@ -235,7 +249,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.IconButton {
|
||||
id: renameButton
|
||||
id: editEntryButton
|
||||
image: "qrc:///images/edit.svg"
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
opacity: isOpenGL ? 0.5 : 1
|
||||
@@ -244,7 +258,7 @@ Rectangle {
|
||||
fontAwesomeFallbackOpacity: 0.5
|
||||
Layout.preferredWidth: 23
|
||||
Layout.preferredHeight: 21
|
||||
tooltip: qsTr("Edit address label") + translationManager.emptyString
|
||||
tooltip: qsTr("Edit entry") + translationManager.emptyString
|
||||
|
||||
onClicked: {
|
||||
addressBookListView.currentIndex = index;
|
||||
@@ -287,23 +301,6 @@ Rectangle {
|
||||
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: addNewEntryCheckbox
|
||||
border: false
|
||||
uncheckedIcon: FontAwesome.plusCircle
|
||||
toggleOnClick: false
|
||||
fontAwesomeIcons: true
|
||||
fontSize: 16
|
||||
iconOnTheLeft: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
text: qsTr("Add address") + translationManager.emptyString;
|
||||
onClicked: {
|
||||
root.showAddAddress();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ColumnLayout {
|
||||
id: addContactLayout
|
||||
@@ -313,11 +310,12 @@ Rectangle {
|
||||
MoneroComponents.Label {
|
||||
fontSize: 32
|
||||
wrapMode: Text.WordWrap
|
||||
text: (root.editEntry ? qsTr("Edit an address") : qsTr("Add an address")) + translationManager.emptyString
|
||||
text: (root.editEntry ? qsTr("Edit entry") : qsTr("Add an address")) + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.LineEditMulti {
|
||||
id: addressLine
|
||||
visible: !root.editEntry
|
||||
Layout.topMargin: 20
|
||||
KeyNavigation.backtab: deleteButton.visible ? deleteButton: cancelButton
|
||||
KeyNavigation.tab: resolveButton.visible ? resolveButton : descriptionLine
|
||||
@@ -325,16 +323,16 @@ Rectangle {
|
||||
.arg(qsTr("Address")) + translationManager.emptyString
|
||||
placeholderText: {
|
||||
if(persistentSettings.nettype == NetworkType.MAINNET){
|
||||
return "4.. / 8.. / OpenAlias";
|
||||
return "4.. / 8.. / monero:.. / OpenAlias";
|
||||
} else if (persistentSettings.nettype == NetworkType.STAGENET){
|
||||
return "5.. / 7..";
|
||||
return "5.. / 7.. / monero:..";
|
||||
} else if(persistentSettings.nettype == NetworkType.TESTNET){
|
||||
return "9.. / B..";
|
||||
return "9.. / B.. / monero:..";
|
||||
}
|
||||
}
|
||||
wrapMode: Text.WrapAnywhere
|
||||
addressValidation: true
|
||||
pasteButton: true
|
||||
pasteButton: false
|
||||
onTextChanged: {
|
||||
const parsed = walletManager.parse_uri_to_object(addressLine.text);
|
||||
if (!parsed.error) {
|
||||
@@ -436,39 +434,13 @@ Rectangle {
|
||||
}
|
||||
RowLayout {
|
||||
Layout.topMargin: 20
|
||||
MoneroComponents.StandardButton {
|
||||
id: addButton
|
||||
KeyNavigation.backtab: descriptionLine
|
||||
KeyNavigation.tab: cancelButton
|
||||
text: (root.editEntry ? qsTr("Save") : qsTr("Add")) + translationManager.emptyString
|
||||
enabled: root.checkInformation(addressLine.text, appWindow.persistentSettings.nettype)
|
||||
onClicked: {
|
||||
console.log("Add")
|
||||
if (!currentWallet.addressBook.addRow(addressLine.text.trim(),"", descriptionLine.text)) {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
// TODO: check currentWallet.addressBook.errorString() instead.
|
||||
if(currentWallet.addressBook.errorCode() === AddressBook.Invalid_Address)
|
||||
informationPopup.text = qsTr("Invalid address") + translationManager.emptyString
|
||||
else if(currentWallet.addressBook.errorCode() === AddressBook.Invalid_Payment_Id)
|
||||
informationPopup.text = currentWallet.addressBook.errorString()
|
||||
else
|
||||
informationPopup.text = qsTr("Can't create entry") + translationManager.emptyString
|
||||
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
} else {
|
||||
if (root.editEntry) {
|
||||
currentWallet.addressBook.deleteRow(addressBookListView.currentIndex);
|
||||
}
|
||||
root.showAddressBook();
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: cancelButton
|
||||
KeyNavigation.backtab: addButton
|
||||
KeyNavigation.tab: deleteButton.visible ? deleteButton : addressLine
|
||||
small: true
|
||||
text: qsTr("Cancel") + translationManager.emptyString
|
||||
primary: false
|
||||
onClicked: root.showAddressBook();
|
||||
@@ -478,6 +450,7 @@ Rectangle {
|
||||
id: deleteButton
|
||||
KeyNavigation.backtab: cancelButton
|
||||
KeyNavigation.tab: addressLine
|
||||
small: true
|
||||
visible: root.editEntry
|
||||
text: qsTr("Delete") + translationManager.emptyString
|
||||
primary: false
|
||||
@@ -486,6 +459,38 @@ Rectangle {
|
||||
root.showAddressBook();
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: addButton
|
||||
KeyNavigation.backtab: descriptionLine
|
||||
KeyNavigation.tab: cancelButton
|
||||
small: true
|
||||
text: (root.editEntry ? qsTr("Save") : qsTr("Add")) + translationManager.emptyString
|
||||
enabled: root.checkInformation(addressLine.text, appWindow.persistentSettings.nettype)
|
||||
onClicked: {
|
||||
if (!root.editEntry) {
|
||||
if (currentWallet.addressBook.addRow(addressLine.text.trim(),"", descriptionLine.text)) {
|
||||
console.log("Entry added")
|
||||
} else {
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
// TODO: check currentWallet.addressBook.errorString() instead.
|
||||
if (currentWallet.addressBook.errorCode() === AddressBook.Invalid_Address)
|
||||
informationPopup.text = qsTr("Invalid address") + translationManager.emptyString
|
||||
else if (currentWallet.addressBook.errorCode() === AddressBook.Invalid_Payment_Id)
|
||||
informationPopup.text = currentWallet.addressBook.errorString()
|
||||
else
|
||||
informationPopup.text = qsTr("Can't create entry") + translationManager.emptyString
|
||||
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
}
|
||||
} else {
|
||||
currentWallet.addressBook.setDescription(addressBookListView.currentIndex, descriptionLine.text);
|
||||
console.log("Description edited")
|
||||
}
|
||||
root.showAddressBook()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ ColumnLayout {
|
||||
property alias state: stateView.state
|
||||
|
||||
MoneroComponents.Navbar {
|
||||
id: navbarId
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: height
|
||||
Layout.bottomMargin: height
|
||||
@@ -128,7 +129,7 @@ ColumnLayout {
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: 0 - target.width
|
||||
from: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * - target.width
|
||||
to: 0
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
@@ -137,7 +138,7 @@ ColumnLayout {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: target.width
|
||||
to: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * target.width
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
@@ -1013,6 +1013,7 @@ Rectangle {
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
visible: isout
|
||||
enabled: currentWallet ? !currentWallet.isHwBacked() : false
|
||||
anchors.left: btnDetails.right
|
||||
anchors.leftMargin: 10
|
||||
text: FontAwesome.productHunt
|
||||
|
||||
456
pages/Mining.qml
@@ -26,17 +26,21 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQml.Models 2.2
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
import "../components" as MoneroComponents
|
||||
import moneroComponents.Wallet 1.0
|
||||
import moneroComponents.P2PoolManager 1.0
|
||||
import moneroComponents.DaemonManager 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: "transparent"
|
||||
property alias miningHeight: mainLayout.height
|
||||
property double currentHashRate: 0
|
||||
property int threads: idealThreadCount / 2
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
@@ -51,13 +55,14 @@ Rectangle {
|
||||
MoneroComponents.Label {
|
||||
id: soloTitleLabel
|
||||
fontSize: 24
|
||||
text: qsTr("Solo mining") + translationManager.emptyString
|
||||
text: qsTr("Mining") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.WarningBox {
|
||||
Layout.bottomMargin: 8
|
||||
id: localDaemonWarning
|
||||
text: qsTr("Mining is only available on local daemons.") + translationManager.emptyString
|
||||
visible: persistentSettings.useRemoteNode
|
||||
visible: persistentSettings.useRemoteNode && !persistentSettings.allowRemoteNodeMining
|
||||
}
|
||||
|
||||
MoneroComponents.WarningBox {
|
||||
@@ -68,7 +73,7 @@ Rectangle {
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: soloMainLabel
|
||||
text: qsTr("Mining with your computer helps strengthen the Monero network. The more that people mine, the harder it is for the network to be attacked, and every little bit helps.\n\nMining also gives you a small chance to earn some Monero. Your computer will create hashes looking for block solutions. If you find a block, you will get the associated reward. Good luck!") + translationManager.emptyString
|
||||
text: qsTr("Mining with your computer helps strengthen the Monero network. The more people mine, the harder it is for the network to be attacked, and every little bit helps.\n\nMining also gives you a small chance to earn some Monero. Your computer will create hashes looking for block solutions. If you find a block, you will get the associated reward. Good luck!") + "\n\n" + qsTr("P2Pool mining is a decentralized way to pool mine that pays out more frequently compared to solo mining, while also supporting the network.") + translationManager.emptyString
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
@@ -89,6 +94,43 @@ Rectangle {
|
||||
columnSpacing: 20
|
||||
rowSpacing: 16
|
||||
|
||||
ListModel {
|
||||
id: miningModeModel
|
||||
|
||||
ListElement { column1: qsTr("Solo") ; column2: ""; priority: 0}
|
||||
ListElement { column1: "P2Pool" ; column2: ""; priority: 1}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: miningModeLabel
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
text: qsTr("Mining mode") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 5
|
||||
spacing: 10
|
||||
|
||||
MoneroComponents.StandardDropdown {
|
||||
Layout.maximumWidth: 200
|
||||
id: miningModeDropdown
|
||||
visible: true
|
||||
currentIndex: 0
|
||||
dataModel: miningModeModel
|
||||
onChanged: {
|
||||
persistentSettings.allow_p2pool_mining = miningModeDropdown.currentIndex === 1;
|
||||
walletManager.stopMining();
|
||||
p2poolManager.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
@@ -106,30 +148,56 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: soloMinerThreadsLine
|
||||
Layout.minimumWidth: 200
|
||||
text: "1"
|
||||
validator: IntValidator { bottom: 1; top: idealThreadCount }
|
||||
}
|
||||
RowLayout {
|
||||
MoneroComponents.StandardButton {
|
||||
id: removeThreadButton
|
||||
small: true
|
||||
primary: false
|
||||
text: "−"
|
||||
enabled: threads > 1
|
||||
onClicked: threads--
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: numAvailableThreadsText
|
||||
text: qsTr("Max # of CPU threads available for mining: ") + idealThreadCount + translationManager.emptyString
|
||||
wrapMode: Text.WordWrap
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 14
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.bottomMargin: 1
|
||||
Layout.minimumWidth: 45
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
text: threads
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 16
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
scrollGestureEnabled: false
|
||||
onWheel: {
|
||||
if (wheel.angleDelta.y > 0 && threads < idealThreadCount) {
|
||||
return threads++
|
||||
} else if (wheel.angleDelta.y < 0 && threads > 1) {
|
||||
return threads--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: addThreadButton
|
||||
small: true
|
||||
primary: false
|
||||
text: "+"
|
||||
enabled: threads < idealThreadCount
|
||||
onClicked: threads++
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
MoneroComponents.StandardButton {
|
||||
id: autoRecommendedThreadsButton
|
||||
small: true
|
||||
text: qsTr("Use recommended # of threads") + translationManager.emptyString
|
||||
primary: false
|
||||
text: qsTr("Use half (recommended)") + translationManager.emptyString
|
||||
enabled: startSoloMinerButton.enabled
|
||||
onClicked: {
|
||||
soloMinerThreadsLine.text = Math.floor(idealThreadCount / 2);
|
||||
threads = idealThreadCount / 2
|
||||
appWindow.showStatusMessage(qsTr("Set to use recommended # of threads"),3)
|
||||
}
|
||||
}
|
||||
@@ -137,25 +205,16 @@ Rectangle {
|
||||
MoneroComponents.StandardButton {
|
||||
id: autoSetMaxThreadsButton
|
||||
small: true
|
||||
text: qsTr("Use all threads") + translationManager.emptyString
|
||||
primary: false
|
||||
text: qsTr("Use all threads") + " (" + idealThreadCount + ")" + translationManager.emptyString
|
||||
enabled: startSoloMinerButton.enabled
|
||||
onClicked: {
|
||||
soloMinerThreadsLine.text = idealThreadCount
|
||||
threads = idealThreadCount
|
||||
appWindow.showStatusMessage(qsTr("Set to use all threads") + translationManager.emptyString,3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
MoneroComponents.CheckBox {
|
||||
id: backgroundMining
|
||||
enabled: startSoloMinerButton.enabled
|
||||
checked: persistentSettings.allow_background_mining
|
||||
onClicked: {persistentSettings.allow_background_mining = checked}
|
||||
text: qsTr("Background mining (experimental)") + translationManager.emptyString
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
// Disable this option until stable
|
||||
visible: false
|
||||
@@ -169,6 +228,37 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Layout.minimumWidth: 140
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: optionsLabel
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
visible: !persistentSettings.allow_p2pool_mining
|
||||
text: qsTr("Options") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
wrapMode: Text.Wrap
|
||||
Layout.preferredWidth: manageSoloMinerLabel.textWidth
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
|
||||
RowLayout {
|
||||
MoneroComponents.CheckBox {
|
||||
id: backgroundMining
|
||||
visible: !persistentSettings.allow_p2pool_mining
|
||||
enabled: startSoloMinerButton.enabled && !persistentSettings.allow_p2pool_mining
|
||||
checked: persistentSettings.allow_background_mining
|
||||
onClicked: persistentSettings.allow_background_mining = checked
|
||||
text: qsTr("Background mining (experimental)") + translationManager.emptyString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
@@ -191,18 +281,55 @@ Rectangle {
|
||||
visible: true
|
||||
id: startSoloMinerButton
|
||||
small: true
|
||||
primary: !stopSoloMinerButton.enabled
|
||||
text: qsTr("Start mining") + translationManager.emptyString
|
||||
onClicked: {
|
||||
var success = walletManager.startMining(appWindow.currentWallet.address(0, 0), soloMinerThreadsLine.text, persistentSettings.allow_background_mining, persistentSettings.miningIgnoreBattery)
|
||||
if (success) {
|
||||
update()
|
||||
} else {
|
||||
errorPopup.title = qsTr("Error starting mining") + translationManager.emptyString;
|
||||
errorPopup.text = qsTr("Couldn't start mining.<br>") + translationManager.emptyString
|
||||
if (persistentSettings.useRemoteNode)
|
||||
errorPopup.text += qsTr("Mining is only available on local daemons. Run a local daemon to be able to mine.<br>") + translationManager.emptyString
|
||||
errorPopup.icon = StandardIcon.Critical
|
||||
errorPopup.open()
|
||||
var daemonReady = appWindow.daemonSynced && appWindow.daemonRunning && !persistentSettings.useRemoteNode
|
||||
if (persistentSettings.allowRemoteNodeMining) {
|
||||
daemonReady = persistentSettings.useRemoteNode && appWindow.daemonSynced
|
||||
}
|
||||
if (daemonReady) {
|
||||
var success;
|
||||
if (persistentSettings.allow_p2pool_mining) {
|
||||
if (p2poolManager.isInstalled()) {
|
||||
if (persistentSettings.allowRemoteNodeMining) {
|
||||
startP2Pool()
|
||||
}
|
||||
else {
|
||||
daemonManager.stopAsync(persistentSettings.nettype, startP2PoolLocal)
|
||||
}
|
||||
}
|
||||
else {
|
||||
confirmationDialog.title = qsTr("P2Pool installation") + translationManager.emptyString;
|
||||
confirmationDialog.text = qsTr("P2Pool will be installed at %1. Proceed?").arg(applicationDirectory) + translationManager.emptyString;
|
||||
confirmationDialog.icon = StandardIcon.Question;
|
||||
confirmationDialog.cancelText = qsTr("No") + translationManager.emptyString;
|
||||
confirmationDialog.okText = qsTr("Yes") + translationManager.emptyString;
|
||||
confirmationDialog.onAcceptedCallback = function() {
|
||||
p2poolManager.download();
|
||||
statusMessageText.text = "Downloading P2Pool...";
|
||||
statusMessage.visible = true
|
||||
startSoloMinerButton.enabled = false;
|
||||
stopSoloMinerButton.enabled = false;
|
||||
}
|
||||
confirmationDialog.open();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = walletManager.startMining(appWindow.currentWallet.address(0, 0), threads, persistentSettings.allow_background_mining, persistentSettings.miningIgnoreBattery)
|
||||
if (success)
|
||||
{
|
||||
update()
|
||||
}
|
||||
else
|
||||
{
|
||||
miningError(qsTr("Couldn't start mining.<br>") + translationManager.emptyString)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
miningError(qsTr("Couldn't start mining.<br>") + translationManager.emptyString)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,9 +338,11 @@ Rectangle {
|
||||
visible: true
|
||||
id: stopSoloMinerButton
|
||||
small: true
|
||||
primary: stopSoloMinerButton.enabled
|
||||
text: qsTr("Stop mining") + translationManager.emptyString
|
||||
onClicked: {
|
||||
walletManager.stopMining()
|
||||
p2poolManager.exit()
|
||||
update()
|
||||
}
|
||||
}
|
||||
@@ -246,23 +375,161 @@ Rectangle {
|
||||
inputPaddingLeft: 0
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: chainModel
|
||||
|
||||
ListElement { column1: qsTr("Mini") ; column2: ""; priority: 0}
|
||||
ListElement { column1: qsTr("Main") ; column2: ""; priority: 1}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: chainLabel
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
visible: persistentSettings.allow_p2pool_mining
|
||||
text: qsTr("Chain") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
|
||||
MoneroComponents.Tooltip {
|
||||
id: chainsHelpTooltip
|
||||
text: qsTr("Use the mini chain if you have a low hashrate.") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: chainsTooltipArea
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
enabled: persistentSettings.allow_p2pool_mining
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
chainsHelpTooltip.tooltipPopup.open();
|
||||
}
|
||||
onExited: {
|
||||
chainsHelpTooltip.tooltipPopup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 5
|
||||
spacing: 10
|
||||
|
||||
MoneroComponents.StandardDropdown {
|
||||
Layout.maximumWidth: 200
|
||||
id: chainDropdown
|
||||
visible: persistentSettings.allow_p2pool_mining
|
||||
currentIndex: 0
|
||||
dataModel: chainModel
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: flagsLabel
|
||||
visible: persistentSettings.allow_p2pool_mining
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
text: qsTr("Flags") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
}
|
||||
|
||||
MoneroComponents.Tooltip {
|
||||
id: flagsHelpTooltip
|
||||
text: "
|
||||
Usage:<br>
|
||||
--wallet Wallet address to mine to. Subaddresses and integrated addresses are not supported!<br>
|
||||
--host IP address of your Monero node, default is 127.0.0.1<br>
|
||||
--rpc-port monerod RPC API port number, default is 18081<br>
|
||||
--zmq-port monerod ZMQ pub port number, default is 18083 (same port as in monerod\'s \"--zmq-pub\" command line parameter)<br>
|
||||
--stratum Comma-separated list of IP:port for stratum server to listen on<br>
|
||||
--p2p Comma-separated list of IP:port for p2p server to listen on<br>
|
||||
--addpeers Comma-separated list of IP:port of other p2pool nodes to connect to<br>
|
||||
--light-mode Don't allocate RandomX dataset, saves 2GB of RAM<br>
|
||||
--loglevel Verbosity of the log, integer number between 0 and 6<br>
|
||||
--config Name of the p2pool config file<br>
|
||||
--data-api Path to the p2pool JSON data (use it in tandem with an external web-server)<br>
|
||||
--local-api Enable /local/ path in api path for Stratum Server and built-in miner statistics<br>
|
||||
--stratum-api An alias for --local-api<br>
|
||||
--no-cache Disable p2pool.cache<br>
|
||||
--no-color Disable colors in console output<br>
|
||||
--no-randomx Disable internal RandomX hasher: p2pool will use RPC calls to monerod to check PoW hashes<br>
|
||||
--out-peers N Maximum number of outgoing connections for p2p server (any value between 10 and 1000)<br>
|
||||
--in-peers N Maximum number of incoming connections for p2p server (any value between 10 and 1000)<br>
|
||||
--start-mining N Start built-in miner using N threads (any value between 1 and 64)<br>
|
||||
--help Show this help message
|
||||
"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: flagsTooltipArea
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
enabled: persistentSettings.allow_p2pool_mining
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
flagsHelpTooltip.tooltipPopup.open();
|
||||
}
|
||||
onExited: {
|
||||
flagsHelpTooltip.tooltipPopup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
MoneroComponents.LineEditMulti {
|
||||
id: p2poolFlags
|
||||
Layout.minimumWidth: 100
|
||||
Layout.bottomMargin: 20
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
visible: persistentSettings.allow_p2pool_mining
|
||||
wrapMode: Text.WrapAnywhere
|
||||
labelText: qsTr("P2Pool startup flags") + translationManager.emptyString
|
||||
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
||||
placeholderFontSize: 15
|
||||
text: persistentSettings.p2poolFlags
|
||||
addressValidation: false
|
||||
onEditingFinished: {
|
||||
persistentSettings.allowRemoteNodeMining = p2poolFlags.text.includes("--host");
|
||||
persistentSettings.p2poolFlags = p2poolFlags.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatusText() {
|
||||
function updateStatusText(p2poolHashrate) {
|
||||
if (appWindow.isMining) {
|
||||
var userHashRate = walletManager.miningHashRate();
|
||||
if (userHashRate === 0) {
|
||||
statusText.text = qsTr("Mining temporarily suspended.") + translationManager.emptyString;
|
||||
if (persistentSettings.allow_p2pool_mining) {
|
||||
if (p2poolHashrate === 0) {
|
||||
statusText.text = qsTr("Starting P2Pool") + translationManager.emptyString;
|
||||
}
|
||||
else {
|
||||
statusText.text = qsTr("Mining with P2Pool, at %1 H/s").arg(p2poolHashrate) + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var blockTime = 120;
|
||||
var blocksPerDay = 86400 / blockTime;
|
||||
var globalHashRate = walletManager.networkDifficulty() / blockTime;
|
||||
var probabilityFindNextBlock = userHashRate / globalHashRate;
|
||||
var probabilityFindBlockDay = 1 - Math.pow(1 - probabilityFindNextBlock, blocksPerDay);
|
||||
var chanceFindBlockDay = Math.round(1 / probabilityFindBlockDay);
|
||||
statusText.text = qsTr("Mining at %1 H/s. It gives you a 1 in %2 daily chance of finding a block.").arg(userHashRate).arg(chanceFindBlockDay) + translationManager.emptyString;
|
||||
var userHashRate = walletManager.miningHashRate();
|
||||
if (userHashRate === 0) {
|
||||
statusText.text = qsTr("Mining temporarily suspended.") + translationManager.emptyString;
|
||||
}
|
||||
else {
|
||||
var blockTime = 120;
|
||||
var blocksPerDay = 86400 / blockTime;
|
||||
var globalHashRate = walletManager.networkDifficulty() / blockTime;
|
||||
var probabilityFindNextBlock = userHashRate / globalHashRate;
|
||||
var probabilityFindBlockDay = 1 - Math.pow(1 - probabilityFindNextBlock, blocksPerDay);
|
||||
var chanceFindBlockDay = Math.round(1 / probabilityFindBlockDay);
|
||||
statusText.text = qsTr("Mining at %1 H/s. It gives you a 1 in %2 daily chance of finding a block.").arg(userHashRate).arg(chanceFindBlockDay) + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -270,16 +537,35 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
function onMiningStatus(isMining) {
|
||||
var daemonReady = !persistentSettings.useRemoteNode && appWindow.daemonSynced
|
||||
function onMiningStatus(isMining, hashrate) {
|
||||
var daemonReady = appWindow.daemonSynced
|
||||
if (!persistentSettings.allowRemoteNodeMining) {
|
||||
var daemonReady = !persistentSettings.useRemoteNode && daemonReady
|
||||
}
|
||||
appWindow.isMining = isMining;
|
||||
updateStatusText()
|
||||
updateStatusText(hashrate)
|
||||
startSoloMinerButton.enabled = !appWindow.isMining && daemonReady
|
||||
stopSoloMinerButton.enabled = !startSoloMinerButton.enabled && daemonReady
|
||||
}
|
||||
|
||||
function update() {
|
||||
walletManager.miningStatusAsync();
|
||||
persistentSettings.allow_p2pool_mining = miningModeDropdown.currentIndex === 1;
|
||||
if (persistentSettings.allow_p2pool_mining) {
|
||||
p2poolManager.getStatus();
|
||||
}
|
||||
else {
|
||||
walletManager.miningStatusAsync();
|
||||
}
|
||||
}
|
||||
|
||||
function miningError(message) {
|
||||
p2poolManager.exit()
|
||||
errorPopup.title = qsTr("Error starting mining") + translationManager.emptyString;
|
||||
errorPopup.text = message
|
||||
if (persistentSettings.useRemoteNode && !persistentSettings.allowRemoteNodeMining)
|
||||
errorPopup.text += qsTr("Mining is only available on local daemons. Run a local daemon to be able to mine.<br>") + translationManager.emptyString
|
||||
errorPopup.icon = StandardIcon.Critical
|
||||
errorPopup.open()
|
||||
}
|
||||
|
||||
MoneroComponents.StandardDialog {
|
||||
@@ -289,21 +575,67 @@ Rectangle {
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 2000; running: false; repeat: true
|
||||
interval: 2000
|
||||
running: middlePanel.advancedView.state === "Mining" && middlePanel.state === "Advanced" && currentWallet !== undefined && (!persistentSettings.useRemoteNode || persistentSettings.allowRemoteNodeMining)
|
||||
repeat: true
|
||||
onTriggered: update()
|
||||
}
|
||||
|
||||
function onPageCompleted() {
|
||||
console.log("Mining page loaded");
|
||||
update()
|
||||
timer.running = !persistentSettings.useRemoteNode
|
||||
function startP2PoolLocal() {
|
||||
var noSync = false;
|
||||
var customDaemonArgs = persistentSettings.daemonFlags.toLowerCase();
|
||||
var daemonArgs = "--zmq-pub " + "tcp://127.0.0.1:18083 " + "--disable-dns-checkpoints "
|
||||
if (!customDaemonArgs.includes("--zmq-pub") && !customDaemonArgs.includes("--disable-dns-checkpoints") && !customDaemonArgs.includes("--no-zmq")) {
|
||||
daemonArgs = daemonArgs + customDaemonArgs;
|
||||
}
|
||||
var success = daemonManager.start(daemonArgs, persistentSettings.nettype, persistentSettings.blockchainDataDir, persistentSettings.bootstrapNodeAddress, noSync, persistentSettings.pruneBlockchain)
|
||||
if (success) {
|
||||
startP2Pool()
|
||||
}
|
||||
else {
|
||||
miningError(qsTr("Couldn't start mining.<br>") + translationManager.emptyString)
|
||||
}
|
||||
}
|
||||
|
||||
function onPageClosed() {
|
||||
timer.running = false
|
||||
function startP2Pool() {
|
||||
var address = currentWallet.address(0, 0);
|
||||
var chain = "mini"
|
||||
if (chainDropdown.currentIndex === 1) {
|
||||
chain = "main"
|
||||
}
|
||||
var p2poolArgs = persistentSettings.p2poolFlags;
|
||||
var success = p2poolManager.start(p2poolArgs, address, chain, threads);
|
||||
if (success)
|
||||
{
|
||||
update()
|
||||
}
|
||||
else {
|
||||
miningError(qsTr("Couldn't start mining.<br>") + translationManager.emptyString)
|
||||
}
|
||||
}
|
||||
|
||||
function p2poolDownloadFailed() {
|
||||
statusMessage.visible = false
|
||||
errorPopup.title = qsTr("P2Pool Installation Failed") + translationManager.emptyString;
|
||||
errorPopup.text = "P2Pool installation failed."
|
||||
errorPopup.icon = StandardIcon.Critical
|
||||
errorPopup.open()
|
||||
update()
|
||||
}
|
||||
|
||||
function p2poolDownloadSucceeded() {
|
||||
statusMessage.visible = false
|
||||
informationPopup.title = qsTr("P2Pool Installation Succeeded") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("P2Pool has successfully installed.");
|
||||
informationPopup.icon = StandardIcon.Critical
|
||||
informationPopup.open()
|
||||
update()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
walletManager.miningStatus.connect(onMiningStatus);
|
||||
p2poolManager.p2poolStatus.connect(onMiningStatus);
|
||||
p2poolManager.p2poolDownloadFailure.connect(p2poolDownloadFailed);
|
||||
p2poolManager.p2poolDownloadSuccess.connect(p2poolDownloadSucceeded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +63,11 @@ Rectangle {
|
||||
|
||||
function generateQRCodeString() {
|
||||
if (pageReceive.state == "PaymentRequest") {
|
||||
return TxUtils.makeQRCodeString(appWindow.current_address,
|
||||
(amountToReceiveXMR.text != "" && parseFloat(amountToReceiveXMR.text) != 0 ? amountToReceiveXMR.text : ""),
|
||||
(txDescriptionInput.text != "" ? txDescriptionInput.text : ""),
|
||||
(receiverNameInput.text != "" ? receiverNameInput.text : ""));
|
||||
return walletManager.make_uri(appWindow.current_address,
|
||||
walletManager.amountFromString(amountToReceiveXMR.text),
|
||||
txDescriptionInput.text, receiverNameInput.text);
|
||||
} else {
|
||||
return TxUtils.makeQRCodeString(appWindow.current_address);
|
||||
return walletManager.make_uri(appWindow.current_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,13 +151,16 @@ Rectangle {
|
||||
Menu {
|
||||
id: qrMenu
|
||||
title: "QrCode"
|
||||
currentIndex: menuItem1.hovered ? 0 : menuItem2.hovered ? 1 : -1
|
||||
|
||||
MenuItem {
|
||||
id: menuItem1
|
||||
text: qsTr("Copy to clipboard") + translationManager.emptyString;
|
||||
onTriggered: walletManager.saveQrCodeToClipboard(generateQRCodeString())
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: menuItem2
|
||||
text: qsTr("Save as Image") + translationManager.emptyString;
|
||||
onTriggered: qrFileDialog.open()
|
||||
}
|
||||
@@ -344,6 +346,7 @@ Rectangle {
|
||||
id: txDescriptionInput
|
||||
Layout.preferredWidth: 165
|
||||
Layout.maximumWidth: 165
|
||||
maximumLength: 800
|
||||
topPadding: 7
|
||||
leftPadding: 7
|
||||
font.pixelSize: 14
|
||||
@@ -391,6 +394,7 @@ Rectangle {
|
||||
selectByMouse: true
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
placeholderText: qsTr("Visible to the sender") + translationManager.emptyString
|
||||
maximumLength: 100
|
||||
|
||||
background: Rectangle {
|
||||
color: MoneroComponents.Style.blackTheme ? "transparent" : "white"
|
||||
|
||||
@@ -112,11 +112,8 @@ Rectangle {
|
||||
MoneroComponents.LabelSubheader {
|
||||
Layout.fillWidth: true
|
||||
textFormat: Text.RichText
|
||||
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" +
|
||||
qsTr("Outputs marked as spent") + " <a href='#'>" + qsTr("Help") + "</a>" + translationManager.emptyString
|
||||
onLinkActivated: {
|
||||
sharedRingDBDialog.title = qsTr("Outputs marked as spent") + translationManager.emptyString;
|
||||
sharedRingDBDialog.text = qsTr(
|
||||
text: qsTr("Outputs marked as spent") + translationManager.emptyString
|
||||
tooltip: qsTr(
|
||||
"In order to obscure which inputs in a Monero transaction are being spent, a third party should not be able " +
|
||||
"to tell which inputs in a ring are already known to be spent. Being able to do so would weaken the protection " +
|
||||
"afforded by ring signatures. If all but one of the inputs are known to be already spent, then the input being " +
|
||||
@@ -128,9 +125,6 @@ Rectangle {
|
||||
"Alternatively, you can scan the blockchain (and the blockchain of key-reusing Monero clones) yourself " +
|
||||
"using the monero-blockchain-mark-spent-outputs tool to create a list of known spent outputs.<br>"
|
||||
) + translationManager.emptyString
|
||||
sharedRingDBDialog.icon = StandardIcon.Information
|
||||
sharedRingDBDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
@@ -249,11 +243,8 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 24
|
||||
textFormat: Text.RichText
|
||||
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" +
|
||||
qsTr("Rings") + " <a href='#'>" + qsTr("Help") + "</a>" + translationManager.emptyString
|
||||
onLinkActivated: {
|
||||
sharedRingDBDialog.title = qsTr("Rings") + translationManager.emptyString;
|
||||
sharedRingDBDialog.text = qsTr(
|
||||
text: qsTr("Rings") + translationManager.emptyString
|
||||
tooltip: qsTr(
|
||||
"In order to avoid nullifying the protection afforded by Monero's ring signatures, an output should not " +
|
||||
"be spent with different rings on different blockchains. While this is normally not a concern, it can become one " +
|
||||
"when a key-reusing Monero clone allows you to spend existing outputs. In this case, you need to ensure this " +
|
||||
@@ -266,9 +257,6 @@ Rectangle {
|
||||
"If you do not use a key-reusing Monero clone without these safety features, then you do not need to do anything " +
|
||||
"as it is all automated.<br>"
|
||||
) + translationManager.emptyString
|
||||
sharedRingDBDialog.icon = StandardIcon.Information
|
||||
sharedRingDBDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
|
||||
@@ -394,11 +394,11 @@ Rectangle {
|
||||
wrapMode: Text.WrapAnywhere
|
||||
placeholderText: {
|
||||
if(persistentSettings.nettype == NetworkType.MAINNET){
|
||||
return "4.. / 8.. / OpenAlias";
|
||||
return "4.. / 8.. / monero:.. / OpenAlias";
|
||||
} else if (persistentSettings.nettype == NetworkType.STAGENET){
|
||||
return "5.. / 7..";
|
||||
return "5.. / 7.. / monero:..";
|
||||
} else if(persistentSettings.nettype == NetworkType.TESTNET){
|
||||
return "9.. / B..";
|
||||
return "9.. / B.. / monero:..";
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
@@ -855,7 +855,6 @@ Rectangle {
|
||||
StandardButton {
|
||||
id: sendButton
|
||||
rightIcon: "qrc:///images/rightArrow.png"
|
||||
rightIconInactive: "qrc:///images/rightArrowInactive.png"
|
||||
Layout.topMargin: 4
|
||||
text: qsTr("Send") + translationManager.emptyString
|
||||
enabled: !sendButtonWarningBox.visible && !warningContent && !recipientModel.hasEmptyAddress() && !paymentIdWarningBox.visible
|
||||
@@ -898,6 +897,32 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
AdvancedOptionsItem {
|
||||
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||
title: qsTr("Outputs") + translationManager.emptyString
|
||||
button1.text: qsTr("Export") + translationManager.emptyString
|
||||
button1.enabled: appWindow.viewOnly
|
||||
button1.onClicked: {
|
||||
console.log("Transfer: export outputs clicked")
|
||||
exportOutputsDialog.open();
|
||||
}
|
||||
button2.text: qsTr("Import") + translationManager.emptyString
|
||||
button2.enabled: !appWindow.viewOnly
|
||||
button2.onClicked: {
|
||||
console.log("Transfer: import outputs clicked")
|
||||
importOutputsDialog.open();
|
||||
}
|
||||
tooltip: {
|
||||
var header = qsTr("Required for cold wallets to sign their corresponding key images") + translationManager.emptyString;
|
||||
return "<style type='text/css'>.header{ font-size: 13px; } p{line-height:20px; margin-top:0px; margin-bottom:0px; " +
|
||||
";} p.orange{color:#ff9323;}</style>" +
|
||||
"<div class='header'>" + header + "</div>" +
|
||||
"<p>" + qsTr("1. Using view-only wallet, export the outputs into a file") + "</p>" +
|
||||
"<p>" + qsTr("2. Using cold wallet, import the outputs file") + "</p>" +
|
||||
translationManager.emptyString
|
||||
}
|
||||
}
|
||||
|
||||
AdvancedOptionsItem {
|
||||
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||
title: qsTr("Key images") + translationManager.emptyString
|
||||
@@ -1005,24 +1030,7 @@ Rectangle {
|
||||
// deleting transaction object, we don't want memleaks
|
||||
transaction.destroy();
|
||||
} else {
|
||||
confirmationDialog.text = qsTr("\nNumber of transactions: ") + transaction.txCount
|
||||
for (var i = 0; i < transaction.txCount; ++i) {
|
||||
confirmationDialog.text += qsTr("\nTransaction #%1").arg(i+1)
|
||||
+qsTr("\nRecipient: ") + transaction.recipientAddress[i]
|
||||
+ (transaction.paymentId[i] == "" ? "" : qsTr("\n\payment ID: ") + transaction.paymentId[i])
|
||||
+ qsTr("\nAmount: ") + walletManager.displayAmount(transaction.amount(i))
|
||||
+ qsTr("\nFee: ") + walletManager.displayAmount(transaction.fee(i))
|
||||
+ qsTr("\nRingsize: ") + (transaction.mixin(i)+1)
|
||||
|
||||
// TODO: add descriptions to unsigned_tx_set?
|
||||
// + (transactionDescription === "" ? "" : (qsTr("\n\nDescription: ") + transactionDescription))
|
||||
+ translationManager.emptyString
|
||||
if (i > 0) {
|
||||
confirmationDialog.text += "\n\n"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
confirmationDialog.text = qsTr("\nConfirmation message:\n ") + transaction.confirmationMessage
|
||||
console.log(transaction.confirmationMessage);
|
||||
|
||||
// Show confirmation dialog
|
||||
@@ -1072,6 +1080,41 @@ Rectangle {
|
||||
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: exportOutputsDialog
|
||||
selectMultiple: false
|
||||
selectExisting: false
|
||||
onAccepted: {
|
||||
console.log(walletManager.urlToLocalPath(exportOutputsDialog.fileUrl))
|
||||
if (currentWallet.exportOutputs(walletManager.urlToLocalPath(exportOutputsDialog.fileUrl), true)) {
|
||||
appWindow.showStatusMessage(qsTr("Outputs successfully exported to file") + translationManager.emptyString, 3);
|
||||
} else {
|
||||
appWindow.showStatusMessage(currentWallet.errorString, 5);
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled");
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: importOutputsDialog
|
||||
selectMultiple: false
|
||||
selectExisting: true
|
||||
title: qsTr("Please choose a file") + translationManager.emptyString
|
||||
onAccepted: {
|
||||
console.log(walletManager.urlToLocalPath(importOutputsDialog.fileUrl))
|
||||
if (currentWallet.importOutputs(walletManager.urlToLocalPath(importOutputsDialog.fileUrl))) {
|
||||
appWindow.showStatusMessage(qsTr("Outputs successfully imported to wallet") + translationManager.emptyString, 3);
|
||||
} else {
|
||||
appWindow.showStatusMessage(currentWallet.errorString, 5);
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled");
|
||||
}
|
||||
}
|
||||
|
||||
//ExportKeyImagesDialog
|
||||
FileDialog {
|
||||
id: exportKeyImagesDialog
|
||||
@@ -1079,7 +1122,11 @@ Rectangle {
|
||||
selectExisting: false
|
||||
onAccepted: {
|
||||
console.log(walletManager.urlToLocalPath(exportKeyImagesDialog.fileUrl))
|
||||
currentWallet.exportKeyImages(walletManager.urlToLocalPath(exportKeyImagesDialog.fileUrl));
|
||||
if (currentWallet.exportKeyImages(walletManager.urlToLocalPath(exportKeyImagesDialog.fileUrl), true)) {
|
||||
appWindow.showStatusMessage(qsTr("Key images successfully exported to file") + translationManager.emptyString, 3);
|
||||
} else {
|
||||
appWindow.showStatusMessage(currentWallet.errorString, 5);
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled");
|
||||
@@ -1094,7 +1141,11 @@ Rectangle {
|
||||
title: qsTr("Please choose a file") + translationManager.emptyString
|
||||
onAccepted: {
|
||||
console.log(walletManager.urlToLocalPath(importKeyImagesDialog.fileUrl))
|
||||
currentWallet.importKeyImages(walletManager.urlToLocalPath(importKeyImagesDialog.fileUrl));
|
||||
if (currentWallet.importKeyImages(walletManager.urlToLocalPath(importKeyImagesDialog.fileUrl))) {
|
||||
appWindow.showStatusMessage(qsTr("Key images successfully imported to wallet") + translationManager.emptyString, 3);
|
||||
} else {
|
||||
appWindow.showStatusMessage(currentWallet.errorString, 5);
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled");
|
||||
|
||||
@@ -60,13 +60,13 @@ Rectangle {
|
||||
MoneroComponents.Label {
|
||||
id: soloTitleLabel
|
||||
fontSize: 24
|
||||
text: qsTr("Prove Transaction") + translationManager.emptyString
|
||||
text: qsTr("Prove Transaction") + " / " + qsTr("Reserve") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Generate a proof of your incoming/outgoing payment by supplying the transaction ID, the recipient address and an optional message. \n" +
|
||||
"For the case of outgoing payments, you can get a 'Spend Proof' that proves the authorship of a transaction. In this case, you don't need to specify the recipient address.") + translationManager.emptyString
|
||||
"For the case of outgoing payments, you can get a 'Spend Proof' that proves the authorship of a transaction. In this case, you don't need to specify the recipient address.") + qsTr("\nFor reserve proofs you don't need to specify tx id or address.") + translationManager.emptyString
|
||||
wrapMode: Text.Wrap
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 14
|
||||
@@ -83,6 +83,7 @@ Rectangle {
|
||||
placeholderText: qsTr("Paste tx ID") + translationManager.emptyString
|
||||
readOnly: false
|
||||
copyButton: true
|
||||
enabled: getReserveProofAmtLine.text.length === 0
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
@@ -95,6 +96,38 @@ Rectangle {
|
||||
placeholderText: qsTr("Recipient's wallet address") + translationManager.emptyString;
|
||||
readOnly: false
|
||||
copyButton: true
|
||||
enabled: getReserveProofAmtLine.text.length === 0
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: getReserveProofAmtLine
|
||||
Layout.fillWidth: true
|
||||
labelFontSize: 14
|
||||
labelText: qsTr("Amount") + translationManager.emptyString
|
||||
fontSize: 16
|
||||
placeholderFontSize: 16
|
||||
placeholderText: qsTr("Paste amount of XMR (reserve proof only)") + translationManager.emptyString
|
||||
readOnly: false
|
||||
copyButton: true
|
||||
enabled: getProofAddressLine.text.length === 0 && getProofTxIdLine.text.length === 0
|
||||
onTextChanged: {
|
||||
text = text.trim().replace(",", ".");
|
||||
const match = text.match(/^0+(\d.*)/);
|
||||
if (match) {
|
||||
const cursorPosition = cursorPosition;
|
||||
text = match[1];
|
||||
cursorPosition = Math.max(cursorPosition, 1) - 1;
|
||||
} else if(text.indexOf('.') === 0) {
|
||||
text = '0' + text;
|
||||
if (text.length > 2) {
|
||||
cursorPosition = 1;
|
||||
}
|
||||
}
|
||||
error = walletManager.amountFromString(text) > appWindow.getUnlockedBalance();
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
@@ -113,10 +146,10 @@ Rectangle {
|
||||
Layout.topMargin: 16
|
||||
small: true
|
||||
text: qsTr("Generate") + translationManager.emptyString
|
||||
enabled: TxUtils.checkTxID(getProofTxIdLine.text) && (getProofAddressLine.text.length == 0 || TxUtils.checkAddress(getProofAddressLine.text, appWindow.persistentSettings.nettype))
|
||||
enabled: TxUtils.checkTxID(getProofTxIdLine.text) && (getProofAddressLine.text.length == 0 || TxUtils.checkAddress(getProofAddressLine.text, appWindow.persistentSettings.nettype)) || getReserveProofAmtLine.text.length != 0 && walletManager.amountFromString(getReserveProofAmtLine.text) < appWindow.getUnlockedBalance() && walletManager.amountFromString(getReserveProofAmtLine.text) > 0
|
||||
onClicked: {
|
||||
console.log("getProof: Generate clicked: txid " + getProofTxIdLine.text + ", address " + getProofAddressLine.text + ", message: " + getProofMessageLine.text);
|
||||
middlePanel.getProofClicked(getProofTxIdLine.text, getProofAddressLine.text, getProofMessageLine.text)
|
||||
middlePanel.getProofClicked(getProofTxIdLine.text, getProofAddressLine.text, getProofMessageLine.text, getReserveProofAmtLine.text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,12 +166,12 @@ Rectangle {
|
||||
MoneroComponents.Label {
|
||||
id: soloTitleLabel2
|
||||
fontSize: 24
|
||||
text: qsTr("Check Transaction") + translationManager.emptyString
|
||||
text: qsTr("Check Transaction") + " / " + qsTr("Reserve") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
text: qsTr("Verify that funds were paid to an address by supplying the transaction ID, the recipient address, the message used for signing and the signature.\n" +
|
||||
"For the case with Spend Proof, you don't need to specify the recipient address.") + translationManager.emptyString
|
||||
"For the case with Spend Proof, you don't need to specify the recipient address.") + "\n" + qsTr("Transaction is not needed for reserve proof.") + translationManager.emptyString
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
@@ -189,7 +222,7 @@ Rectangle {
|
||||
labelFontSize: 14
|
||||
labelText: qsTr("Signature") + translationManager.emptyString
|
||||
placeholderFontSize: 16
|
||||
placeholderText: qsTr("Paste tx proof") + translationManager.emptyString;
|
||||
placeholderText: qsTr("Paste tx proof") + " / " + qsTr("reserve proof") + translationManager.emptyString;
|
||||
readOnly: false
|
||||
copyButton: true
|
||||
}
|
||||
@@ -198,7 +231,7 @@ Rectangle {
|
||||
Layout.topMargin: 16
|
||||
small: true
|
||||
text: qsTr("Check") + translationManager.emptyString
|
||||
enabled: TxUtils.checkTxID(checkProofTxIdLine.text) && TxUtils.checkSignature(checkProofSignatureLine.text) && ((checkProofSignatureLine.text.indexOf("SpendProofV") === 0 && checkProofAddressLine.text.length == 0) || (checkProofSignatureLine.text.indexOf("SpendProofV") !== 0 && TxUtils.checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.nettype)))
|
||||
enabled: (TxUtils.checkTxID(checkProofTxIdLine.text) && TxUtils.checkSignature(checkProofSignatureLine.text) && ((checkProofSignatureLine.text.indexOf("SpendProofV") === 0 && checkProofAddressLine.text.length == 0) || (checkProofSignatureLine.text.indexOf("SpendProofV") !== 0 && TxUtils.checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.nettype)))) || (TxUtils.checkSignature(checkProofSignatureLine.text) && checkProofSignatureLine.text.indexOf("ReserveProofV") === 0 && TxUtils.checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.nettype))
|
||||
onClicked: {
|
||||
console.log("checkProof: Check clicked: txid " + checkProofTxIdLine.text + ", address " + checkProofAddressLine.text + ", message " + checkProofMessageLine.text + ", signature " + checkProofSignatureLine.text);
|
||||
middlePanel.checkProofClicked(checkProofTxIdLine.text, checkProofAddressLine.text, checkProofMessageLine.text, checkProofSignatureLine.text)
|
||||
|
||||
@@ -211,7 +211,7 @@ Item {
|
||||
|
||||
smooth: false
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "image://qrcode/" + TxUtils.makeQRCodeString(appWindow.current_address, amountToReceive.text)
|
||||
source: "image://qrcode/" + walletManager.make_uri(appWindow.current_address, walletManager.amountFromString(amountToReceive.text))
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -400,7 +400,7 @@ Item {
|
||||
font.pixelSize: 12
|
||||
font.bold: true
|
||||
color: _color
|
||||
text: TxUtils.makeQRCodeString(appWindow.current_address, amountToReceive.text)
|
||||
text: walletManager.make_uri(appWindow.current_address, walletManager.amountFromString(amountToReceive.text))
|
||||
themeTransition: false
|
||||
|
||||
MouseArea {
|
||||
@@ -685,7 +685,7 @@ Item {
|
||||
selectExisting: false
|
||||
nameFilters: ["Image (*.png)"]
|
||||
onAccepted: {
|
||||
if(!walletManager.saveQrCode(TxUtils.makeQRCodeString(appWindow.current_address, amountToReceive.text), walletManager.urlToLocalPath(fileUrl))) {
|
||||
if (!walletManager.saveQrCode(walletManager.make_uri(appWindow.current_address, walletManager.amountFromString(amountToReceive.text)), walletManager.urlToLocalPath(fileUrl))) {
|
||||
console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(fileUrl) )
|
||||
receivePageDialog.title = qsTr("Save QrCode") + translationManager.emptyString;
|
||||
receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString;
|
||||
|
||||
@@ -49,6 +49,7 @@ ColumnLayout {
|
||||
property alias settingsStateViewState: settingsStateView.state
|
||||
|
||||
MoneroComponents.Navbar {
|
||||
id: navbarId
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: height
|
||||
Layout.bottomMargin: height
|
||||
@@ -145,7 +146,7 @@ ColumnLayout {
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: 0 - target.width
|
||||
from: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * - target.width
|
||||
to: 0
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
@@ -154,7 +155,7 @@ ColumnLayout {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: target.width
|
||||
to: (navbarId.currentIndex < navbarId.previousIndex ? 1 : -1) * target.width
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
@@ -409,7 +409,7 @@ Rectangle {
|
||||
small: true
|
||||
text: qsTr("Donate to Monero") + translationManager.emptyString
|
||||
onClicked: {
|
||||
middlePanel.sendTo("888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", "", "Donation to Monero Core Team");
|
||||
middlePanel.sendTo("888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", "", qsTr("Donation to Monero Core Team") + translationManager.emptyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ Rectangle {
|
||||
if (!checked) {
|
||||
console.log("Disabled price conversion");
|
||||
persistentSettings.fiatPriceEnabled = false;
|
||||
appWindow.fiatTimerStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,8 +231,6 @@ Rectangle {
|
||||
onClicked: {
|
||||
console.log("Enabled price conversion");
|
||||
persistentSettings.fiatPriceEnabled = true;
|
||||
appWindow.fiatApiRefresh();
|
||||
appWindow.fiatTimerStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,14 @@ Rectangle {
|
||||
anchors.topMargin: 0
|
||||
spacing: 0
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
iconText: FontAwesome.lock
|
||||
description: qsTr("Locks the wallet on demand.") + translationManager.emptyString
|
||||
title: qsTr("Lock this wallet") + translationManager.emptyString
|
||||
symbol: (isMac ? "⌃" : qsTr("Ctrl+")) + "L" + translationManager.emptyString
|
||||
onClicked: appWindow.lock();
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
iconText: FontAwesome.signOutAlt
|
||||
description: qsTr("Logs out of this wallet.") + translationManager.emptyString
|
||||
@@ -91,6 +99,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
enabled: leftPanel.progressBar.fillLevel == 100
|
||||
iconText: FontAwesome.repeat
|
||||
description: qsTr("Use this feature if you think the shown balance is not accurate.") + translationManager.emptyString
|
||||
title: qsTr("Rescan wallet balance") + translationManager.emptyString
|
||||
@@ -100,7 +109,11 @@ Rectangle {
|
||||
if (!currentWallet.rescanSpent()) {
|
||||
console.error("Error: ", currentWallet.errorString);
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||
informationPopup.text = qsTr("Error: ") + currentWallet.errorString
|
||||
if (currentWallet.errorString == "Rescan spent can only be used with a trusted daemon") {
|
||||
informationPopup.text = qsTr("Error: ") + qsTr("Rescan spent can only be used with a trusted remote node. If you trust the current node you are connected to (%1), you can mark it as trusted in Settings > Node page.").arg(remoteNodesModel.currentRemoteNode().address) + translationManager.emptyString;
|
||||
} else {
|
||||
informationPopup.text = qsTr("Error: ") + currentWallet.errorString;
|
||||
}
|
||||
informationPopup.icon = StandardIcon.Critical
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
|
||||
15
qml.qrc
@@ -97,6 +97,9 @@
|
||||
<file>lang/flags/gb.png</file>
|
||||
<file>lang/flags/us.png</file>
|
||||
<file>lang/flags/nb_NO.png</file>
|
||||
<file>lang/flags/el.png</file>
|
||||
<file>lang/flags/vi.png</file>
|
||||
<file>lang/flags/is.png</file>
|
||||
<file>pages/Receive.qml</file>
|
||||
<file>pages/TxKey.qml</file>
|
||||
<file>pages/SharedRingDB.qml</file>
|
||||
@@ -159,8 +162,6 @@
|
||||
<file>js/TxUtils.js</file>
|
||||
<file>images/warning.png</file>
|
||||
<file>images/warning@2x.png</file>
|
||||
<file>images/rightArrowInactive.png</file>
|
||||
<file>images/rightArrowInactive@2x.png</file>
|
||||
<file>js/Windows.js</file>
|
||||
<file>js/Utils.js</file>
|
||||
<file>components/RadioButton.qml</file>
|
||||
@@ -215,6 +216,16 @@
|
||||
<file>images/restore-wallet-from-hardware.png</file>
|
||||
<file>images/open-wallet-from-file@2x.png</file>
|
||||
<file>images/open-wallet-from-file.png</file>
|
||||
<file>images/open-wallet-from-file-mainnet@2x.png</file>
|
||||
<file>images/open-wallet-from-file-mainnet.png</file>
|
||||
<file>images/open-wallet-from-file-stagenet@2x.png</file>
|
||||
<file>images/open-wallet-from-file-stagenet.png</file>
|
||||
<file>images/open-wallet-from-file-testnet@2x.png</file>
|
||||
<file>images/open-wallet-from-file-testnet.png</file>
|
||||
<file>images/open-wallet-from-file-view-only@2x.png</file>
|
||||
<file>images/open-wallet-from-file-view-only.png</file>
|
||||
<file>images/open-wallet-from-file-trezor@2x.png</file>
|
||||
<file>images/open-wallet-from-file-trezor.png</file>
|
||||
<file>images/restore-wallet@2x.png</file>
|
||||
<file>images/restore-wallet.png</file>
|
||||
<file>images/create-wallet@2x.png</file>
|
||||
|
||||
@@ -36,6 +36,8 @@ file(GLOB SOURCE_FILES
|
||||
"libwalletqt/UnsignedTransaction.h"
|
||||
"daemon/*.h"
|
||||
"daemon/*.cpp"
|
||||
"p2pool/*.h"
|
||||
"p2pool/*.cpp"
|
||||
"model/*.h"
|
||||
"model/*.cpp"
|
||||
"qt/*.h"
|
||||
@@ -97,13 +99,12 @@ target_include_directories(monero-wallet-gui PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/monero/external/qrcodegen
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/daemon
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/p2pool
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libwalletqt
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/model
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/QR-Code-scanner
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zxcvbn-c
|
||||
${X11_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_compile_definitions(monero-wallet-gui
|
||||
@@ -124,13 +125,13 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
|
||||
target_link_libraries(monero-wallet-gui
|
||||
epee
|
||||
common
|
||||
net
|
||||
wallet_api
|
||||
qrcodegen
|
||||
easylogging
|
||||
${Boost_LIBRARIES}
|
||||
${QT5_LIBRARIES}
|
||||
${EXTRA_LIBRARIES}
|
||||
${ICU_LIBRARIES}
|
||||
openpgp
|
||||
qrdecoder
|
||||
translations
|
||||
|
||||
@@ -132,3 +132,19 @@ QString AddressBook::getDescription(const QString &address) const
|
||||
}
|
||||
return QString::fromStdString(m_rows.value(*it)->getDescription());
|
||||
}
|
||||
|
||||
void AddressBook::setDescription(int index, const QString &description)
|
||||
{
|
||||
bool result;
|
||||
|
||||
{
|
||||
QWriteLocker locker(&m_lock);
|
||||
|
||||
result = m_addressBookImpl->setDescription(index, description.toStdString());
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
getAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
Q_INVOKABLE QString errorString() const;
|
||||
Q_INVOKABLE int errorCode() const;
|
||||
Q_INVOKABLE QString getDescription(const QString &address) const;
|
||||
Q_INVOKABLE void setDescription(int index, const QString &label);
|
||||
|
||||
enum ErrorCode {
|
||||
Status_Ok,
|
||||
|
||||
@@ -170,7 +170,7 @@ QString TransactionHistory::writeCSV(quint32 accountIndex, QString out)
|
||||
|
||||
// write header
|
||||
QTextStream output(&data);
|
||||
output << "blockHeight,epoch,date,direction,amount,atomicAmount,fee,txid,label,subaddrAccount,paymentId\n";
|
||||
output << "blockHeight,epoch,date,direction,amount,atomicAmount,fee,txid,label,subaddrAccount,paymentId,description\n";
|
||||
|
||||
QReadLocker locker(&m_lock);
|
||||
for (const auto &tx : m_pimpl->getAll()) {
|
||||
@@ -198,6 +198,8 @@ QString TransactionHistory::writeCSV(quint32 accountIndex, QString out)
|
||||
}
|
||||
QString label = info.label();
|
||||
label.remove(QChar('"')); // reserved
|
||||
QString description = info.description();
|
||||
description.remove(QChar('"')); // reserved
|
||||
quint64 blockHeight = info.blockHeight();
|
||||
QDateTime timeStamp = info.timestamp();
|
||||
QString date = info.date() + " " + info.time();
|
||||
@@ -209,11 +211,11 @@ QString TransactionHistory::writeCSV(quint32 accountIndex, QString out)
|
||||
}
|
||||
|
||||
// format and write
|
||||
QString line = QString("%1,%2,%3,%4,%5,%6,%7,%8,\"%9\",%10,%11\n")
|
||||
QString line = QString("%1,%2,%3,%4,%5,%6,%7,%8,\"%9\",%10,%11,\"%12\"\n")
|
||||
.arg(QString::number(blockHeight), QString::number(epoch), date)
|
||||
.arg(direction, displayAmount, QString::number(atomicAmount))
|
||||
.arg(info.fee(), info.hash(), label, QString::number(subaddrAccount))
|
||||
.arg(paymentId);
|
||||
.arg(paymentId, description);
|
||||
output << line;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@ bool TransactionInfo::isFailed() const
|
||||
return m_failed;
|
||||
}
|
||||
|
||||
bool TransactionInfo::isCoinbase() const
|
||||
{
|
||||
return m_coinbase;
|
||||
}
|
||||
|
||||
double TransactionInfo::amount() const
|
||||
{
|
||||
@@ -126,6 +130,11 @@ QString TransactionInfo::paymentId() const
|
||||
return m_paymentId;
|
||||
}
|
||||
|
||||
QString TransactionInfo::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
QString TransactionInfo::destinations_formatted() const
|
||||
{
|
||||
QString destinations;
|
||||
@@ -144,10 +153,12 @@ TransactionInfo::TransactionInfo(const Monero::TransactionInfo *pimpl, QObject *
|
||||
, m_confirmations(pimpl->confirmations())
|
||||
, m_direction(static_cast<Direction>(pimpl->direction()))
|
||||
, m_failed(pimpl->isFailed())
|
||||
, m_coinbase(pimpl->isCoinbase())
|
||||
, m_fee(pimpl->fee())
|
||||
, m_hash(QString::fromStdString(pimpl->hash()))
|
||||
, m_label(QString::fromStdString(pimpl->label()))
|
||||
, m_paymentId(QString::fromStdString(pimpl->paymentId()))
|
||||
, m_description(QString::fromStdString(pimpl->description()))
|
||||
, m_pending(pimpl->isPending())
|
||||
, m_subaddrAccount(pimpl->subaddrAccount())
|
||||
, m_timestamp(QDateTime::fromTime_t(pimpl->timestamp()))
|
||||
|
||||
@@ -42,6 +42,7 @@ class TransactionInfo : public QObject
|
||||
Q_PROPERTY(Direction direction READ direction)
|
||||
Q_PROPERTY(bool isPending READ isPending)
|
||||
Q_PROPERTY(bool isFailed READ isFailed)
|
||||
Q_PROPERTY(bool isCoinbase READ isCoinbase)
|
||||
Q_PROPERTY(double amount READ amount)
|
||||
Q_PROPERTY(quint64 atomicAmount READ atomicAmount)
|
||||
Q_PROPERTY(QString displayAmount READ displayAmount)
|
||||
@@ -57,6 +58,7 @@ class TransactionInfo : public QObject
|
||||
Q_PROPERTY(QString date READ date)
|
||||
Q_PROPERTY(QString time READ time)
|
||||
Q_PROPERTY(QString paymentId READ paymentId)
|
||||
Q_PROPERTY(QString description READ description)
|
||||
Q_PROPERTY(QString destinations_formatted READ destinations_formatted)
|
||||
|
||||
public:
|
||||
@@ -71,6 +73,7 @@ public:
|
||||
Direction direction() const;
|
||||
bool isPending() const;
|
||||
bool isFailed() const;
|
||||
bool isCoinbase() const;
|
||||
double amount() const;
|
||||
quint64 atomicAmount() const;
|
||||
QString displayAmount() const;
|
||||
@@ -87,6 +90,7 @@ public:
|
||||
QString date() const;
|
||||
QString time() const;
|
||||
QString paymentId() const;
|
||||
QString description() const;
|
||||
//! only applicable for output transactions
|
||||
//! used in tx details popup
|
||||
QString destinations_formatted() const;
|
||||
@@ -104,7 +108,9 @@ private:
|
||||
QString m_hash;
|
||||
QString m_label;
|
||||
QString m_paymentId;
|
||||
QString m_description;
|
||||
bool m_pending;
|
||||
bool m_coinbase;
|
||||
quint32 m_subaddrAccount;
|
||||
QSet<quint32> m_subaddrIndex;
|
||||
QDateTime m_timestamp;
|
||||
|
||||
@@ -489,9 +489,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)
|
||||
@@ -499,6 +499,14 @@ bool Wallet::importKeyImages(const QString& path)
|
||||
return m_walletImpl->importKeyImages(path.toStdString());
|
||||
}
|
||||
|
||||
bool Wallet::exportOutputs(const QString& path, bool all) {
|
||||
return m_walletImpl->exportOutputs(path.toStdString(), all);
|
||||
}
|
||||
|
||||
bool Wallet::importOutputs(const QString& path) {
|
||||
return m_walletImpl->importOutputs(path.toStdString());
|
||||
}
|
||||
|
||||
bool Wallet::refresh(bool historyAndSubaddresses /* = true */)
|
||||
{
|
||||
refreshingSet(true);
|
||||
@@ -836,6 +844,25 @@ Q_INVOKABLE QString Wallet::checkSpendProof(const QString &txid, const QString &
|
||||
return QString::fromStdString(result);
|
||||
}
|
||||
|
||||
Q_INVOKABLE QString Wallet::getReserveProof(bool all, quint32 account_index, quint64 amount, const QString &message) const
|
||||
{
|
||||
qDebug("Generating reserve proof");
|
||||
std::string result = m_walletImpl->getReserveProof(all, account_index, amount, message.toStdString());
|
||||
if (result.empty())
|
||||
result = "error|" + m_walletImpl->errorString();
|
||||
return QString::fromStdString(result);
|
||||
}
|
||||
|
||||
Q_INVOKABLE QString Wallet::checkReserveProof(const QString &address, const QString &message, const QString &signature) const
|
||||
{
|
||||
bool good;
|
||||
uint64_t total;
|
||||
uint64_t spent;
|
||||
bool success = m_walletImpl->checkReserveProof(address.toStdString(), message.toStdString(), signature.toStdString(), good, total, spent);
|
||||
std::string result = std::string(success ? "true" : "false") + "|" + std::string(good ? "true" : "false") + "|" + QString::number(total).toStdString() + "|" + QString::number(spent).toStdString();
|
||||
return QString::fromStdString(result);
|
||||
}
|
||||
|
||||
QString Wallet::signMessage(const QString &message, bool filename) const
|
||||
{
|
||||
if (filename) {
|
||||
@@ -925,6 +952,12 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id
|
||||
return res;
|
||||
}
|
||||
|
||||
QString Wallet::make_uri(const QString &address, const quint64 &amount, const QString &tx_description, const QString &recipient_name) const
|
||||
{
|
||||
std::string error;
|
||||
return QString::fromStdString(m_walletImpl->make_uri(address.toStdString(), "", amount, tx_description.toStdString(), recipient_name.toStdString(), error));
|
||||
}
|
||||
|
||||
bool Wallet::rescanSpent()
|
||||
{
|
||||
QMutexLocker locker(&m_asyncMutex);
|
||||
|
||||
@@ -205,9 +205,13 @@ 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);
|
||||
|
||||
//! refreshes the wallet
|
||||
Q_INVOKABLE bool refresh(bool historyAndSubaddresses = true);
|
||||
|
||||
@@ -296,6 +300,8 @@ public:
|
||||
|
||||
//! Parse URI
|
||||
Q_INVOKABLE bool 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);
|
||||
//! Make URI
|
||||
Q_INVOKABLE QString make_uri(const QString &address, const quint64 &amount = 0, const QString &tx_description = "", const QString &recipient_name = "") const;
|
||||
|
||||
//! Namespace your cacheAttribute keys to avoid collisions
|
||||
Q_INVOKABLE bool setCacheAttribute(const QString &key, const QString &val);
|
||||
@@ -312,6 +318,8 @@ public:
|
||||
Q_INVOKABLE QString getSpendProof(const QString &txid, const QString &message) const;
|
||||
Q_INVOKABLE void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback);
|
||||
Q_INVOKABLE QString checkSpendProof(const QString &txid, const QString &message, const QString &signature) const;
|
||||
Q_INVOKABLE QString getReserveProof(bool all, quint32 account_index, quint64 amount, const QString &message) const;
|
||||
Q_INVOKABLE QString checkReserveProof(const QString &address, const QString &message, const QString &signature) const;
|
||||
// Rescan spent outputs
|
||||
Q_INVOKABLE bool rescanSpent();
|
||||
|
||||
|
||||
@@ -441,6 +441,14 @@ QVariantMap WalletManager::parse_uri_to_object(const QString &uri) const
|
||||
return result;
|
||||
}
|
||||
|
||||
QString WalletManager::make_uri(const QString &address, const quint64 &amount, const QString &tx_description, const QString &recipient_name) const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (m_currentWallet)
|
||||
return m_currentWallet->make_uri(address, amount, tx_description, recipient_name);
|
||||
return "";
|
||||
}
|
||||
|
||||
void WalletManager::setLogLevel(int logLevel)
|
||||
{
|
||||
Monero::WalletManagerFactory::setLogLevel(logLevel);
|
||||
|
||||
@@ -179,6 +179,7 @@ public:
|
||||
Q_INVOKABLE QString resolveOpenAlias(const QString &address) const;
|
||||
Q_INVOKABLE bool 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) const;
|
||||
Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const;
|
||||
Q_INVOKABLE QString make_uri(const QString &address, const quint64 &amount = 0, const QString &tx_description = "", const QString &recipient_name = "") const;
|
||||
Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const;
|
||||
Q_INVOKABLE void saveQrCodeToClipboard(const QString &) const;
|
||||
Q_INVOKABLE void checkUpdatesAsync(
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
// IOS exclusions
|
||||
#ifndef Q_OS_IOS
|
||||
#include "daemon/DaemonManager.h"
|
||||
#include "p2pool/P2PoolManager.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -401,6 +402,8 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw
|
||||
#ifndef Q_OS_IOS
|
||||
qmlRegisterUncreatableType<DaemonManager>("moneroComponents.DaemonManager", 1, 0, "DaemonManager",
|
||||
"DaemonManager can't be instantiated directly");
|
||||
qmlRegisterUncreatableType<P2PoolManager>("moneroComponents.P2PoolManager", 1, 0, "P2PoolManager",
|
||||
"P2PoolManager can't be instantiated directly");
|
||||
#endif
|
||||
qmlRegisterUncreatableType<AddressBookModel>("moneroComponents.AddressBookModel", 1, 0, "AddressBookModel",
|
||||
"AddressBookModel can't be instantiated directly");
|
||||
@@ -462,7 +465,9 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw
|
||||
// Exclude daemon manager from IOS
|
||||
#ifndef Q_OS_IOS
|
||||
DaemonManager daemonManager;
|
||||
P2PoolManager p2poolManager;
|
||||
engine.rootContext()->setContextProperty("daemonManager", &daemonManager);
|
||||
engine.rootContext()->setContextProperty("p2poolManager", &p2poolManager);
|
||||
#endif
|
||||
|
||||
engine.rootContext()->setContextProperty("isWindows", isWindows);
|
||||
@@ -503,6 +508,12 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw
|
||||
#endif
|
||||
engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner);
|
||||
|
||||
bool builtWithDesktopEntry = false;
|
||||
#ifdef WITH_DESKTOP_ENTRY
|
||||
builtWithDesktopEntry = true;
|
||||
#endif
|
||||
engine.rootContext()->setContextProperty("builtWithDesktopEntry", builtWithDesktopEntry);
|
||||
|
||||
engine.rootContext()->setContextProperty("moneroVersion", MONERO_VERSION_FULL);
|
||||
|
||||
// Load main window (context properties needs to be defined obove this line)
|
||||
|
||||
238
src/p2pool/P2PoolManager.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright (c) 2014-2022, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "P2PoolManager.h"
|
||||
#include "net/http_client.h"
|
||||
#include "common/util.h"
|
||||
#include "qt/utils.h"
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QMutexLocker>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QApplication>
|
||||
#include <QProcess>
|
||||
#include <QMap>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
void P2PoolManager::download() {
|
||||
m_scheduler.run([this] {
|
||||
QUrl url;
|
||||
QString fileName;
|
||||
QString validHash;
|
||||
#ifdef Q_OS_WIN
|
||||
url = "https://github.com/SChernykh/p2pool/releases/download/v1.9/p2pool-v1.9-windows-x64.zip";
|
||||
fileName = "p2pool-v1.9-windows-x64.zip";
|
||||
validHash = "2587903dc04a4879dca2b6f5c5b584e869928e716274a7660e24b219c9f18839";
|
||||
#elif defined(Q_OS_LINUX)
|
||||
url = "https://github.com/SChernykh/p2pool/releases/download/v1.9/p2pool-v1.9-linux-x64.tar.gz";
|
||||
fileName = "p2pool-v1.9-linux-x64.tar.gz";
|
||||
validHash = "0cd85d933ac4a76708d326698d9db3155bb29d0be82984c735fabd9e9a351b8e";
|
||||
#elif defined(Q_OS_MACOS)
|
||||
url = "https://github.com/SChernykh/p2pool/releases/download/v1.9/p2pool-v1.9-macos-x64.tar.gz";
|
||||
fileName = "p2pool-v1.9-macos-x64.tar.gz";
|
||||
validHash = "47fbdd69d719da80597dd5487f109b61e30b540499cced7b93de1ee01344351e";
|
||||
#endif
|
||||
QFile file(fileName);
|
||||
epee::net_utils::http::http_simple_client http_client;
|
||||
const epee::net_utils::http::http_response_info* response = NULL;
|
||||
std::string userAgent = randomUserAgent().toStdString();
|
||||
std::chrono::milliseconds timeout = std::chrono::seconds(10);
|
||||
http_client.set_server(url.host().toStdString(), "443", {});
|
||||
bool success = http_client.invoke_get(url.path().toStdString(), timeout, {}, std::addressof(response), {{"User-Agent", userAgent}});
|
||||
if (response->m_response_code == 302) {
|
||||
epee::net_utils::http::fields_list fields = response->m_header_info.m_etc_fields;
|
||||
for (std::pair<std::string, std::string> i : fields) {
|
||||
if (i.first == "Location") {
|
||||
url = QString::fromStdString(i.second);
|
||||
http_client.set_server(url.host().toStdString(), "443", {});
|
||||
std::string query = url.query(QUrl::FullyEncoded).toStdString();
|
||||
std::string path = url.path().toStdString() + "?" + query;
|
||||
http_client.wipe_response();
|
||||
success = http_client.invoke_get(path, timeout, {}, std::addressof(response), {{"User-Agent", userAgent}});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
emit p2poolDownloadFailure();
|
||||
}
|
||||
else {
|
||||
std::string stringData = response->m_body;
|
||||
QByteArray data(stringData.c_str(), stringData.length());
|
||||
QByteArray hashData = QCryptographicHash::hash(data, QCryptographicHash::Sha256);
|
||||
QString hash = hashData.toHex();
|
||||
if (hash != validHash) {
|
||||
emit p2poolDownloadFailure();
|
||||
}
|
||||
else {
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(data);
|
||||
file.close();
|
||||
QProcess::execute("tar", {"-xzf", fileName, "--strip=1", "-C", QApplication::applicationDirPath()});
|
||||
QFile::remove(fileName);
|
||||
if (isInstalled()) {
|
||||
emit p2poolDownloadSuccess();
|
||||
}
|
||||
else {
|
||||
emit p2poolDownloadFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
bool P2PoolManager::isInstalled() {
|
||||
if (!QFileInfo(m_p2pool).isFile())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void P2PoolManager::getStatus() {
|
||||
QString statsPath = QApplication::applicationDirPath() + "/stats/local/miner";
|
||||
bool status = true;
|
||||
if (!QFileInfo(statsPath).isFile() || !started)
|
||||
{
|
||||
status = started;
|
||||
emit p2poolStatus(status, 0);
|
||||
return;
|
||||
}
|
||||
QFile statsFile(statsPath);
|
||||
statsFile.open(QIODevice::ReadOnly);
|
||||
QTextStream statsOut(&statsFile);
|
||||
QByteArray data;
|
||||
statsOut >> data;
|
||||
statsFile.close();
|
||||
QJsonDocument json = QJsonDocument::fromJson(data);
|
||||
QJsonObject jsonObj = json.object();
|
||||
int hashrate = jsonObj.value("current_hashrate").toInt();
|
||||
emit p2poolStatus(status, hashrate);
|
||||
return;
|
||||
}
|
||||
|
||||
bool P2PoolManager::start(const QString &flags, const QString &address, const QString &chain, const QString &threads)
|
||||
{
|
||||
// prepare command line arguments and pass to p2pool
|
||||
QStringList arguments;
|
||||
|
||||
// Custom startup flags for p2pool
|
||||
foreach (const QString &str, flags.split(" ")) {
|
||||
qDebug() << QString(" [%1] ").arg(str);
|
||||
if (!str.isEmpty())
|
||||
arguments << str;
|
||||
}
|
||||
|
||||
if (!arguments.contains("--local-api")) {
|
||||
arguments << "--local-api";
|
||||
}
|
||||
|
||||
if (!arguments.contains("--data-api")) {
|
||||
QDir dir;
|
||||
QString dirName = QApplication::applicationDirPath() + "/stats/";
|
||||
QDir statsDir(dirName);
|
||||
if (dir.exists(dirName)) {
|
||||
statsDir.removeRecursively();
|
||||
}
|
||||
dir.mkdir(dirName);
|
||||
arguments << "--data-api" << dirName;
|
||||
}
|
||||
|
||||
if (!arguments.contains("--start-mining")) {
|
||||
arguments << "--start-mining" << threads;
|
||||
}
|
||||
|
||||
if (chain == "mini") {
|
||||
arguments << "--mini";
|
||||
}
|
||||
|
||||
if (!arguments.contains("--wallet")) {
|
||||
arguments << "--wallet" << address;
|
||||
}
|
||||
|
||||
qDebug() << "starting p2pool " + m_p2pool;
|
||||
qDebug() << "With command line arguments " << arguments;
|
||||
|
||||
QMutexLocker locker(&m_p2poolMutex);
|
||||
|
||||
m_p2poold.reset(new QProcess());
|
||||
|
||||
// Set program parameters
|
||||
m_p2poold->setProgram(m_p2pool);
|
||||
m_p2poold->setArguments(arguments);
|
||||
m_p2poold->setWorkingDirectory(QApplication::applicationDirPath());
|
||||
|
||||
// Start p2pool
|
||||
started = m_p2poold->startDetached();
|
||||
|
||||
if (!started) {
|
||||
qDebug() << "P2Pool start error: " + m_p2poold->errorString();
|
||||
emit p2poolStartFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void P2PoolManager::exit()
|
||||
{
|
||||
qDebug("P2PoolManager: exit()");
|
||||
#ifdef Q_OS_WIN
|
||||
QProcess::execute("taskkill", {"/F", "/IM", "p2pool.exe"});
|
||||
#else
|
||||
QProcess::execute("pkill", {"p2pool"});
|
||||
#endif
|
||||
started = false;
|
||||
QString dirName = QApplication::applicationDirPath() + "/stats/";
|
||||
QDir dir(dirName);
|
||||
dir.removeRecursively();
|
||||
}
|
||||
|
||||
P2PoolManager::P2PoolManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_scheduler(this)
|
||||
{
|
||||
started = false;
|
||||
// Platform dependent path to p2pool
|
||||
#ifdef Q_OS_WIN
|
||||
m_p2pool = QApplication::applicationDirPath() + "/p2pool.exe";
|
||||
#elif defined(Q_OS_UNIX)
|
||||
m_p2pool = QApplication::applicationDirPath() + "/p2pool";
|
||||
#endif
|
||||
if (m_p2pool.length() == 0) {
|
||||
qCritical() << "no p2pool binary defined for current platform";
|
||||
}
|
||||
}
|
||||
|
||||
P2PoolManager::~P2PoolManager() {
|
||||
m_scheduler.shutdownWaitForFinished();
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
// Copyright (c) 2014-2022, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
@@ -26,27 +26,47 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <libusb.h>
|
||||
#ifndef P2POOLMANAGER_H
|
||||
#define P2POOLMANAGER_H
|
||||
|
||||
#define UNUSED(expr) (void)(expr)
|
||||
#include <memory>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
libusb_device **devs;
|
||||
libusb_context *ctx = NULL;
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <QProcess>
|
||||
#include "NetworkType.h"
|
||||
#include "qt/FutureScheduler.h"
|
||||
|
||||
int r = libusb_init(&ctx); UNUSED(r);
|
||||
ssize_t cnt = libusb_get_device_list(ctx, &devs); UNUSED(cnt);
|
||||
class P2PoolManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
struct libusb_device_descriptor desc;
|
||||
r = libusb_get_device_descriptor(devs[0], &desc); UNUSED(r);
|
||||
uint8_t bus_id = libusb_get_bus_number(devs[0]); UNUSED(bus_id);
|
||||
uint8_t addr = libusb_get_device_address(devs[0]); UNUSED(addr);
|
||||
public:
|
||||
explicit P2PoolManager(QObject *parent = 0);
|
||||
~P2PoolManager();
|
||||
|
||||
uint8_t tmp_path[16];
|
||||
r = libusb_get_port_numbers(devs[0], tmp_path, sizeof(tmp_path));
|
||||
UNUSED(r);
|
||||
UNUSED(tmp_path);
|
||||
Q_INVOKABLE bool start(const QString &flags, const QString &address, const QString &chain, const QString &threads);
|
||||
Q_INVOKABLE void exit();
|
||||
Q_INVOKABLE bool isInstalled();
|
||||
Q_INVOKABLE void getStatus();
|
||||
Q_INVOKABLE void download();
|
||||
private:
|
||||
|
||||
libusb_free_device_list(devs, 1);
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
bool running(NetworkType::Type nettype) const;
|
||||
signals:
|
||||
void p2poolStartFailure() const;
|
||||
void p2poolStatus(bool isMining, int hashrate) const;
|
||||
void p2poolDownloadFailure() const;
|
||||
void p2poolDownloadSuccess() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QProcess> m_p2poold;
|
||||
QMutex m_p2poolMutex;
|
||||
QString m_p2pool;
|
||||
bool started = false;
|
||||
|
||||
mutable FutureScheduler m_scheduler;
|
||||
};
|
||||
|
||||
#endif // P2POOLMANAGER_H
|
||||
@@ -53,9 +53,6 @@ private:
|
||||
return qMakePair(false, QFuture<T>());
|
||||
}
|
||||
|
||||
QFutureWatcher<void> schedule(std::function<void()> function);
|
||||
QFutureWatcher<QJSValueList> schedule(std::function<QJSValueList() noexcept> function, const QJSValue &callback);
|
||||
|
||||
private:
|
||||
size_t Alive;
|
||||
QWaitCondition Condition;
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#if defined(_WIN32) && !defined(MONERO_GUI_STATIC)
|
||||
#include <Winsock2.h>
|
||||
#endif
|
||||
|
||||
#include "updater.h"
|
||||
|
||||
#include <common/util.h>
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
var GUI_VERSION = "@VERSION_TAG_GUI@";
|
||||
var GUI_VERSION_NUMBER = "@VERSION@"
|
||||
var GUI_VERSION_TAG = "@VERSION_TAG_GUI@";
|
||||
var GUI_VERSION = GUI_VERSION_NUMBER + "-" + GUI_VERSION_TAG
|
||||
|
||||
@@ -516,7 +516,7 @@ typedef struct
|
||||
uint8_t LeetCnv[sizeof L33TCnv / LEET_NORM_MAP_SIZE + 1];
|
||||
/* uint8_t LeetChr[3]; */
|
||||
uint8_t First;
|
||||
uint8_t PossChars[48];
|
||||
uint8_t PossChars[49];
|
||||
} DictWork_t;
|
||||
|
||||
/**********************************************************************************
|
||||
|
||||