Browse Source

Beta-6

hw_ledger
tobtoht 9 months ago
parent
commit
d4cfa05157
Signed by: tobtoht GPG Key ID: 1CADD27F41F45C3C
  1. 1
      .gitignore
  2. 6
      BUILDING.md
  3. 11
      CMakeLists.txt
  4. 2
      HACKING.md
  5. 6
      Makefile
  6. 11
      PKGBUILD
  7. 60
      cmake/FindHIDAPI.cmake
  8. 2
      monero
  9. 17
      src/CMakeLists.txt
  10. 121
      src/api/LocalMoneroApi.cpp
  11. 53
      src/api/LocalMoneroApi.h
  12. 350
      src/appcontext.cpp
  13. 50
      src/appcontext.h
  14. 15
      src/assets.qrc
  15. 52
      src/assets/gpg_keys/featherwallet.asc
  16. 5
      src/assets/images/info2.svg
  17. 1
      src/assets/images/localMonero_buy.svg
  18. 1
      src/assets/images/localMonero_buy_white.svg
  19. BIN
      src/assets/images/localMonero_logo.png
  20. 1
      src/assets/images/localMonero_register.svg
  21. 1
      src/assets/images/localMonero_search.svg
  22. 1
      src/assets/images/localMonero_sell.svg
  23. 1
      src/assets/images/localMonero_sell_white.svg
  24. BIN
      src/assets/images/securityLevelSafer.png
  25. 13
      src/assets/images/securityLevelSafer.svg
  26. BIN
      src/assets/images/securityLevelSaferWhite.png
  27. BIN
      src/assets/images/securityLevelSafest.png
  28. 13
      src/assets/images/securityLevelSafest.svg
  29. BIN
      src/assets/images/securityLevelSafestWhite.png
  30. BIN
      src/assets/images/securityLevelStandard.png
  31. 13
      src/assets/images/securityLevelStandard.svg
  32. BIN
      src/assets/images/securityLevelStandardWhite.png
  33. 21
      src/calcwidget.cpp
  34. 5
      src/calcwidget.h
  35. 8
      src/calcwindow.cpp
  36. 31
      src/coinswidget.cpp
  37. 9
      src/coinswidget.h
  38. 7
      src/contactswidget.cpp
  39. 19
      src/dialog/InfoDialog.cpp
  40. 26
      src/dialog/InfoDialog.h
  41. 71
      src/dialog/InfoDialog.ui
  42. 51
      src/dialog/LocalMoneroInfoDialog.cpp
  43. 35
      src/dialog/LocalMoneroInfoDialog.h
  44. 195
      src/dialog/LocalMoneroInfoDialog.ui
  45. 3
      src/dialog/TxProofDialog.cpp
  46. 223
      src/dialog/UpdateDialog.cpp
  47. 51
      src/dialog/UpdateDialog.h
  48. 87
      src/dialog/UpdateDialog.ui
  49. 7
      src/dialog/broadcasttxdialog.cpp
  50. 42
      src/dialog/debuginfodialog.cpp
  51. 76
      src/dialog/debuginfodialog.ui
  52. 15
      src/dialog/keysdialog.cpp
  53. 2
      src/dialog/restoredialog.cpp
  54. 24
      src/dialog/splashdialog.cpp
  55. 25
      src/dialog/splashdialog.h
  56. 60
      src/dialog/splashdialog.ui
  57. 130
      src/dialog/torinfodialog.cpp
  58. 15
      src/dialog/torinfodialog.h
  59. 340
      src/dialog/torinfodialog.ui
  60. 1
      src/dialog/txconfadvdialog.cpp
  61. 3
      src/dialog/txconfdialog.cpp
  62. 7
      src/dialog/tximportdialog.cpp
  63. 3
      src/globals.h
  64. 16
      src/historywidget.cpp
  65. 26
      src/libwalletqt/Subaddress.cpp
  66. 13
      src/libwalletqt/Subaddress.h
  67. 8
      src/libwalletqt/TransactionHistory.cpp
  68. 23
      src/libwalletqt/Wallet.cpp
  69. 15
      src/libwalletqt/Wallet.h
  70. 9
      src/libwalletqt/WalletListenerImpl.cpp
  71. 2
      src/libwalletqt/WalletListenerImpl.h
  72. 45
      src/libwalletqt/WalletManager.cpp
  73. 14
      src/libwalletqt/WalletManager.h
  74. 1148
      src/mainwindow.cpp
  75. 113
      src/mainwindow.h
  76. 35
      src/mainwindow.ui
  77. 3
      src/model/AddressBookModel.cpp
  78. 9
      src/model/CoinsModel.cpp
  79. 2
      src/model/CoinsModel.h
  80. 1
      src/model/HistoryView.cpp
  81. 1
      src/model/HistoryView.h
  82. 172
      src/model/LocalMoneroModel.cpp
  83. 47
      src/model/LocalMoneroModel.h
  84. 7
      src/model/NodeModel.cpp
  85. 1
      src/model/SubaddressModel.cpp
  86. 31
      src/model/TransactionHistoryModel.cpp
  87. 9
      src/model/TransactionHistoryModel.h
  88. 31
      src/receivewidget.cpp
  89. 3
      src/receivewidget.h
  90. 25
      src/sendwidget.cpp
  91. 5
      src/sendwidget.h
  92. 32
      src/settings.cpp
  93. 7
      src/settings.h
  94. 145
      src/settings.ui
  95. 52
      src/utils/AppData.cpp
  96. 41
      src/utils/AppData.h
  97. 85
      src/utils/AsyncTask.h
  98. 1
      src/utils/ColorScheme.cpp
  99. 22
      src/utils/FeatherSeed.h
  100. 32
      src/utils/Icons.cpp

1
.gitignore

@ -14,3 +14,4 @@ src/tor/*
!src/tor/.gitkeep
src/config-feather.h
src/assets/exec/*
feather.AppDir/*

6
BUILDING.md

@ -22,7 +22,7 @@ git clone --branch master --recursive https://git.featherwallet.org/feather/feat
cd feather
```
Replace `master` with the desired version tag (e.g. `beta-4`) to build the release binary.
Replace `master` with the desired version tag (e.g. `beta-6`) to build the release binary.
#### 2. Base image
@ -37,7 +37,7 @@ Building the base image takes a while. You only need to build the base image onc
##### Standalone binary
```bash
docker run --rm -it -v $PWD:/feather -w /feather feather:linux sh -c 'make release-static -j4'
docker run --rm -it -v $PWD:/feather -w /feather feather:linux sh -c 'CHECK_UPDATES=On make release-static -j4'
```
If you're re-running a build make sure to `rm -rf build/` first.
@ -75,7 +75,7 @@ Building the base image takes a while. You only need to build the base image onc
#### 3. Build
```bash
docker run --rm -it -v $PWD:/feather -w /feather feather:win sh -c 'make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j4'
docker run --rm -it -v $PWD:/feather -w /feather feather:win sh -c 'CHECK_UPDATES=On make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j4'
```
If you're re-running a build make sure to `rm -rf build/` first.

11
CMakeLists.txt

@ -7,11 +7,13 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
set(VERSION_MAJOR "0")
set(VERSION_MINOR "1")
set(VERSION_REVISION "0")
set(VERSION "beta-5")
set(VERSION "beta-6")
option(FETCH_DEPS "Download dependencies if they are not found" ON)
option(LOCALMONERO "Include LocalMonero module" ON)
option(XMRIG "Include XMRig module" ON)
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
option(CHECK_UPDATES "Enable checking for application updates" OFF)
option(STATIC "Link libraries statically, requires static Qt")
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
@ -29,7 +31,7 @@ if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
set(MONERO_HEAD "41327974116dedccc2f9709d8ad3a8a1f591faed")
set(MONERO_HEAD "626f1b4ba1c1a490ca97fe265230aea84eac0ed2")
set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64")
set(BUILD_64 ON)
@ -139,6 +141,11 @@ if(NOT monero-seed_FOUND)
endif()
endif()
# libzip
find_package(zlib CONFIG)
find_path(LIBZIP_INCLUDE_DIRS zip.h)
find_library(LIBZIP_LIBRARIES zip)
# Boost
if(DEBUG)
set(Boost_DEBUG ON)

2
HACKING.md

@ -41,9 +41,11 @@ via the `CMAKE_PREFIX_PATH` definition. For me this is:
There are some Monero/Feather related options/definitions that you may pass:
- `-DLOCALMONERO=OFF` - disable LocalMonero feature
- `-DXMRIG=OFF` - disable XMRig feature
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
- `-DCHECK_UPDATES=ON` - enable checking for updates, only for standalone binaries
And:

6
Makefile

@ -30,7 +30,9 @@ CMAKEFLAGS = \
-DARCH=x86_64 \
-DBUILD_64=On \
-DBUILD_TESTS=Off \
-DLOCALMONERO=On \
-DXMRIG=On \
-DCHECK_UPDATES=Off \
-DTOR_BIN=Off \
-DCMAKE_CXX_STANDARD=11 \
-DCMAKE_VERBOSE_MAKEFILE=On \
@ -42,6 +44,7 @@ CMAKEFLAGS = \
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
release-static: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
release-static: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF)
release-static:
@ -50,10 +53,11 @@ release-static:
depends:
mkdir -p build/$(target)/release
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DTOR_BIN=$(or ${TOR_BIN},OFF) -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
mac-release: CMAKEFLAGS += -DSTATIC=Off
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
mac-release: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
mac-release:

11
PKGBUILD

@ -2,24 +2,19 @@
# Contributor: wowario <wowario[at]protonmail[dot]com>
pkgname='monero-feather-git'
pkgver=0.1.0.925ef5683
pkgver=0.5.0.d1bb4c143f
pkgrel=1
pkgdesc='a free Monero desktop wallet'
license=('BSD')
arch=('x86_64')
url="https://featherwallet.org"
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'pcsclite' 'hidapi' 'protobuf' 'miniupnpc' 'libgcrypt' 'qrencode' 'libsodium' 'libpgm' 'expat' 'qt5-base' 'qt5-websockets' 'tor')
depends=('boost-libs' 'libunwind' 'openssl' 'readline' 'zeromq' 'pcsclite' 'hidapi' 'protobuf' 'libusb' 'libudev.so' 'miniupnpc' 'libgcrypt' 'qrencode' 'libsodium' 'libpgm' 'expat' 'qt5-base' 'qt5-websockets' 'qt5-svg' 'tor' 'libzip')
makedepends=('git' 'cmake' 'boost')
source=("${pkgname}"::"git+https://git.featherwallet.org/feather/feather")
sha256sums=('SKIP')
pkgver() {
cd "${srcdir}/${pkgname}"
printf "%s.%s" "$(git describe --tags --abbrev=0)" "$(git rev-parse --short=9 HEAD)"
}
build() {
cd "${srcdir}/${pkgname}"
git submodule update --init --recursive
@ -32,4 +27,6 @@ build() {
package_monero-feather-git() {
install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
install -Dm755 "${srcdir}/${pkgname}/build/bin/feather" "${pkgdir}/usr/bin/feather"
install -Dm644 "${srcdir}/${pkgname}/src/assets/feather.desktop" "${pkgdir}/usr/share/applications/feather.desktop"
install -Dm644 "${srcdir}/${pkgname}/src/assets/images/feather.png" "${pkgdir}/usr/share/pixmaps/feather.png"
}

60
cmake/FindHIDAPI.cmake

@ -0,0 +1,60 @@
# - try to find HIDAPI library
# from http://www.signal11.us/oss/hidapi/
#
# Cache Variables: (probably not for direct use in your scripts)
# HIDAPI_INCLUDE_DIR
# HIDAPI_LIBRARY
#
# Non-cache variables you might use in your CMakeLists.txt:
# HIDAPI_FOUND
# HIDAPI_INCLUDE_DIRS
# HIDAPI_LIBRARIES
#
# Requires these CMake modules:
# FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
find_library(HIDAPI_LIBRARY
NAMES hidapi hidapi-libusb)
find_path(HIDAPI_INCLUDE_DIR
NAMES hidapi.h
PATH_SUFFIXES
hidapi)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(HIDAPI
DEFAULT_MSG
HIDAPI_LIBRARY
HIDAPI_INCLUDE_DIR)
if(HIDAPI_FOUND)
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}")
if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux"))
find_library(LIBUSB-1.0_LIBRARY usb-1.0)
find_library(LIBUDEV_LIBRARY udev)
if(LIBUSB-1.0_LIBRARY)
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUSB-1.0_LIBRARY}")
if(LIBUDEV_LIBRARY)
set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUDEV_LIBRARY}")
else()
message(WARNING "libudev library not found, binaries may fail to link.")
endif()
else()
message(WARNING "libusb-1.0 library not found, binaries may fail to link.")
endif()
endif()
set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}")
endif()
mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY)

2
monero

@ -1 +1 @@
Subproject commit 41327974116dedccc2f9709d8ad3a8a1f591faed
Subproject commit 626f1b4ba1c1a490ca97fe265230aea84eac0ed2

17
src/CMakeLists.txt

@ -18,6 +18,8 @@ qt5_add_resources(RESOURCES assets.qrc)
file(GLOB SOURCE_FILES
"*.h"
"*.cpp"
"api/*.h"
"api/*.cpp"
"utils/*.h"
"utils/*.cpp"
"libwalletqt/*.h"
@ -116,12 +118,22 @@ target_include_directories(feather PUBLIC
${Qt5Svg_INCLUDE_DIRS}
${Qt5Xml_INCLUDE_DIRS}
${Qt5WebSockets_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
${LIBZIP_INCLUDE_DIRS}
)
if(DONATE_BEG)
target_compile_definitions(feather PRIVATE DONATE_BEG=1)
endif()
if (CHECK_UPDATES)
target_compile_definitions(feather PRIVATE CHECK_UPDATES=1)
endif()
if(LOCALMONERO)
target_compile_definitions(feather PRIVATE HAS_LOCALMONERO=1)
endif()
if(TOR_BIN)
target_compile_definitions(feather PRIVATE HAS_TOR_BIN=1)
endif()
@ -130,6 +142,9 @@ if(XMRIG)
target_compile_definitions(feather PRIVATE HAS_XMRIG=1)
endif()
# TODO: PLACEHOLDER
target_compile_definitions(feather PRIVATE HAS_WEBSOCKET=1)
if(HAVE_SYS_PRCTL_H)
target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1)
endif()
@ -191,6 +206,8 @@ target_link_libraries(feather
openpgp
Threads::Threads
${QRENCODE_LIBRARY}
${ZLIB_LIBRARIES}
${LIBZIP_LIBRARIES}
)
if(APPLE)

121
src/api/LocalMoneroApi.cpp

@ -0,0 +1,121 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#include "LocalMoneroApi.h"
LocalMoneroApi::LocalMoneroApi(QObject *parent, UtilsNetworking *network, const QString &baseUrl)
: QObject(parent)
, m_network(network)
, m_baseUrl(baseUrl)
{
}
void LocalMoneroApi::countryCodes() {
QString url = QString("%1/countrycodes").arg(m_baseUrl);
QNetworkReply *reply = m_network->getJson(url);
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::COUNTRY_CODES));
}
void LocalMoneroApi::currencies() {
QString url = QString("%1/currencies").arg(m_baseUrl);
QNetworkReply *reply = m_network->getJson(url);
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::CURRENCIES));
}
void LocalMoneroApi::paymentMethods(const QString &countryCode) {
QString url;
if (countryCode.isEmpty()) {
url = QString("%1/payment_methods").arg(m_baseUrl);
} else {
url = QString("%1/payment_methods/%2").arg(m_baseUrl, countryCode);
}
QNetworkReply *reply = m_network->getJson(url);
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::PAYMENT_METHODS));
}
void LocalMoneroApi::buyMoneroOnline(const QString &currencyCode, const QString &countryCode,
const QString &paymentMethod, const QString &amount, int page)
{
QString url = this->getBuySellUrl(true, currencyCode, countryCode, paymentMethod, amount, page);
QNetworkReply *reply = m_network->getJson(url);
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::BUY_MONERO_ONLINE));
}
void LocalMoneroApi::sellMoneroOnline(const QString &currencyCode, const QString &countryCode,
const QString &paymentMethod, const QString &amount, int page)
{
QString url = this->getBuySellUrl(false, currencyCode, countryCode, paymentMethod, amount, page);
QNetworkReply *reply = m_network->getJson(url);
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::SELL_MONERO_ONLINE));
}
void LocalMoneroApi::accountInfo(const QString &username) {
QString url = QString("%1/account_info/%2").arg(m_baseUrl, username);
QNetworkReply *reply = m_network->getJson(url);
connect(reply, &QNetworkReply::finished, std::bind(&LocalMoneroApi::onResponse, this, reply, Endpoint::ACCOUNT_INFO));
}
void LocalMoneroApi::onResponse(QNetworkReply *reply, LocalMoneroApi::Endpoint endpoint) {
const bool ok = reply->error() == QNetworkReply::NoError;
const QString err = reply->errorString();
QByteArray data = reply->readAll();
qDebug() << "Response";
qDebug() << data;
for (const auto header : reply->rawHeaderList()) {
qDebug() << header << ": " << reply->rawHeader(header);
}
qDebug() << reply->rawHeaderPairs();
qDebug() << "Request";
for (const auto header : reply->request().rawHeaderList()) {
qDebug() << "header: " << header << ": " << reply->request().rawHeader(header);
}
qDebug() << reply->request().url();
reply->deleteLater();
QJsonObject obj;
if (!data.isEmpty() && Utils::validateJSON(data)) {
auto doc = QJsonDocument::fromJson(data);
obj = doc.object();
}
else if (!ok) {
emit ApiResponse(LocalMoneroResponse{false, endpoint, err, {}});
return;
}
else {
emit ApiResponse(LocalMoneroResponse{false, endpoint, "Invalid response from LocalMonero", {}});
return;
}
if (obj.contains("error")) {
QString errorStr = QJsonDocument(obj["error"].toObject()).toJson(QJsonDocument::Compact);
emit ApiResponse(LocalMoneroResponse{false, endpoint, errorStr, obj});
return;
}
emit ApiResponse(LocalMoneroResponse{true, endpoint, "", obj});
}
QString LocalMoneroApi::getBuySellUrl(bool buy, const QString &currencyCode, const QString &countryCode,
const QString &paymentMethod, const QString &amount, int page)
{
QString url = QString("%1/%2-monero-online/%3").arg(m_baseUrl, buy ? "buy" : "sell", currencyCode);
if (!countryCode.isEmpty() && paymentMethod.isEmpty())
url += QString("/%1").arg(countryCode);
else if (countryCode.isEmpty() && !paymentMethod.isEmpty())
url += QString("/%1").arg(paymentMethod);
else if (!countryCode.isEmpty() && !paymentMethod.isEmpty())
url += QString("/%1/%2").arg(countryCode, paymentMethod);
QUrlQuery query;
if (!amount.isEmpty())
query.addQueryItem("amount", amount);
if (page > 0)
query.addQueryItem("page", QString::number(page));
url += "?" + query.toString();
return url;
}

53
src/api/LocalMoneroApi.h

@ -0,0 +1,53 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#ifndef FEATHER_LOCALMONEROAPI_H
#define FEATHER_LOCALMONEROAPI_H
#include <QObject>
#include "utils/networking.h"
class LocalMoneroApi : public QObject {
Q_OBJECT
public:
enum Endpoint {
COUNTRY_CODES,
CURRENCIES,
PAYMENT_METHODS,
BUY_MONERO_ONLINE,
SELL_MONERO_ONLINE,
ACCOUNT_INFO
};
struct LocalMoneroResponse {
bool ok;
Endpoint endpoint;
QString message;
QJsonObject obj;
};
explicit LocalMoneroApi(QObject *parent, UtilsNetworking *network, const QString &baseUrl = "https://agoradesk.com/api/v1");
void countryCodes();
void currencies();
void paymentMethods(const QString &countryCode = "");
void buyMoneroOnline(const QString &currencyCode, const QString &countryCode="", const QString &paymentMethod="", const QString &amount = "", int page = 0);
void sellMoneroOnline(const QString &currencyCode, const QString &countryCode="", const QString &paymentMethod="", const QString &amount = "", int page = 0);
void accountInfo(const QString &username);
signals:
void ApiResponse(LocalMoneroResponse resp);
private slots:
void onResponse(QNetworkReply *reply, Endpoint endpoint);
private:
QString getBuySellUrl(bool buy, const QString &currencyCode, const QString &countryCode="", const QString &paymentMethod="", const QString &amount = "", int page = 0);
QString m_baseUrl;
UtilsNetworking *m_network;
};
#endif //FEATHER_LOCALMONEROAPI_H

350
src/appcontext.cpp

@ -13,20 +13,16 @@
#include "libwalletqt/Coins.h"
#include "model/TransactionHistoryModel.h"
#include "model/SubaddressModel.h"
#include "utils/NetworkManager.h"
#include "utils/WebsocketClient.h"
#include "utils/WebsocketNotifier.h"
Prices *AppContext::prices = nullptr;
WalletKeysFilesModel *AppContext::wallets = nullptr;
TxFiatHistory *AppContext::txFiatHistory = nullptr;
double AppContext::balance = 0;
QMap<QString, QString> AppContext::txCache;
AppContext::AppContext(QCommandLineParser *cmdargs) {
this->network = new QNetworkAccessManager();
this->networkClearnet = new QNetworkAccessManager();
this->cmdargs = cmdargs;
this->isTorSocks = Utils::isTorsocks();
this->isTails = TailsOS::detect();
this->isWhonix = WhonixOS::detect();
@ -45,21 +41,20 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// ----------------- Network Type -----------------
if (this->cmdargs->isSet("stagenet"))
if (this->cmdargs->isSet("stagenet")) {
this->networkType = NetworkType::STAGENET;
else if (this->cmdargs->isSet("testnet"))
config()->set(Config::networkType, NetworkType::STAGENET);
}
else if (this->cmdargs->isSet("testnet")) {
this->networkType = NetworkType::TESTNET;
else
config()->set(Config::networkType, NetworkType::TESTNET);
}
else {
this->networkType = NetworkType::MAINNET;
config()->set(Config::networkType, NetworkType::MAINNET);
}
this->nodes = new Nodes(this, this->networkClearnet);
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
// Tor & socks proxy
this->ws = new WSClient(this, globals::websocketUrl);
connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
this->nodes = new Nodes(this, this);
// Store the wallet every 2 minutes
m_storeTimer.start(2 * 60 * 1000);
@ -67,31 +62,6 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->storeWallet();
});
// restore height lookup
this->initRestoreHeights();
// price history lookup
auto genesis_timestamp = this->restoreHeights[NetworkType::Type::MAINNET]->data.firstKey();
AppContext::txFiatHistory = new TxFiatHistory(genesis_timestamp, configDir);
connect(this->ws, &WSClient::connectionEstablished, AppContext::txFiatHistory, &TxFiatHistory::onUpdateDatabase);
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYear, [=](int year){
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1}})").arg(year).toUtf8();
this->ws->sendMsg(data);
});
connect(AppContext::txFiatHistory, &TxFiatHistory::requestYearMonth, [=](int year, int month) {
QByteArray data = QString(R"({"cmd": "txFiatHistory", "data": {"year": %1, "month": %2}})").arg(year).arg(month).toUtf8();
this->ws->sendMsg(data);
});
// fiat/crypto lookup
AppContext::prices = new Prices();
// XMRig
#ifdef HAS_XMRIG
this->XMRig = new XmRig(configDir, this);
this->XMRig->prepare();
#endif
this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDir);
Monero::Utils::onStartup();
@ -108,23 +78,32 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// libwallet connects
connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened);
connect(this->walletManager, &WalletManager::walletCreated, this, &AppContext::onWalletCreated);
connect(this->walletManager, &WalletManager::deviceButtonRequest, this, &AppContext::onDeviceButtonRequest);
connect(this->walletManager, &WalletManager::deviceError, this, &AppContext::onDeviceError);
// TODO: move me
connect(websocketNotifier(), &WebsocketNotifier::NodesReceived, this->nodes, &Nodes::onWSNodesReceived);
}
void AppContext::initTor() {
this->tor = new Tor(this, this);
this->tor->start();
if (!(isWhonix) && !(isTorSocks)) {
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy);
if (globals::websocketUrl.host().endsWith(".onion")) {
this->ws->webSocket.setProxy(*networkProxy);
}
}
if (this->cmdargs->isSet("tor-host"))
config()->set(Config::socks5Host, this->cmdargs->value("tor-host"));
if (this->cmdargs->isSet("tor-port"))
config()->set(Config::socks5Port, this->cmdargs->value("tor-port"));
if (this->cmdargs->isSet("use-local-tor"))
config()->set(Config::useLocalTor, true);
torManager()->init();
torManager()->start();
connect(torManager(), &TorManager::connectionStateChanged, &websocketNotifier()->websocketClient, &WebsocketClient::onToggleConnect);
this->onTorSettingsChanged();
}
void AppContext::initWS() {
this->ws->start();
websocketNotifier()->websocketClient.start();
}
void AppContext::onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address) {
@ -249,6 +228,17 @@ void AppContext::onOpenWallet(const QString &path, const QString &password){
this->walletManager->openWalletAsync(path, password, this->networkType, 1);
}
void AppContext::onWalletCreated(Wallet * wallet) {
// Currently only called when a wallet is created from device.
auto state = wallet->status();
if (state != Wallet::Status_Ok) {
emit walletCreatedError(wallet->errorString());
return;
}
this->onWalletOpened(wallet);
}
void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
if(this->currentWallet) {
auto *model = this->currentWallet->transactionHistoryModel();
@ -276,8 +266,7 @@ void AppContext::commitTransaction(PendingTransaction *tx) {
}
void AppContext::onMultiBroadcast(PendingTransaction *tx) {
UtilsNetworking *net = new UtilsNetworking(this->network, this);
DaemonRpc *rpc = new DaemonRpc(this, net, "");
DaemonRpc rpc{this, getNetworkTor(), ""};
int count = tx->txCount();
for (int i = 0; i < count; i++) {
@ -286,40 +275,81 @@ void AppContext::onMultiBroadcast(PendingTransaction *tx) {
for (const auto& node: this->nodes->websocketNodes()) {
if (!node.online) continue;
QString address = node.as_url();
QString address = node.toURL();
qDebug() << QString("Relaying %1 to: %2").arg(tx->txid()[i], address);
rpc->setDaemonAddress(address);
rpc->sendRawTransaction(txData);
rpc.setDaemonAddress(address);
rpc.sendRawTransaction(txData);
}
}
}
void AppContext::onDeviceButtonRequest(quint64 code) {
emit deviceButtonRequest(code);
}
void AppContext::onDeviceError(const QString &message) {
qCritical() << "Device error: " << message;
emit deviceError(message);
}
void AppContext::onTorSettingsChanged() {
if (WhonixOS::detect() || Utils::isTorsocks()) {
return;
}
// use local tor -> bundled tor
QString host = config()->get(Config::socks5Host).toString();
quint16 port = config()->get(Config::socks5Port).toString().toUShort();
if (!torManager()->isLocalTor()) {
host = torManager()->featherTorHost;
port = torManager()->featherTorPort;
}
QNetworkProxy proxy{QNetworkProxy::Socks5Proxy, host, port};
getNetworkTor()->setProxy(proxy);
websocketNotifier()->websocketClient.webSocket.setProxy(proxy);
this->nodes->connectToNode();
auto privacyLevel = config()->get(Config::torPrivacyLevel).toInt();
qDebug() << "Changed privacyLevel to " << privacyLevel;
}
void AppContext::onInitialNetworkConfigured() {
this->initTor();
this->initWS();
}
void AppContext::onWalletOpened(Wallet *wallet) {
auto state = wallet->status();
if (state != Wallet::Status_Ok) {
auto errMsg = wallet->errorString();
if(errMsg == QString("basic_string::_M_replace_aux") || errMsg == QString("std::bad_alloc")) {
if (state == Wallet::Status_BadPassword) {
this->closeWallet(false);
// Don't show incorrect password when we try with empty password for the first time
bool showIncorrectPassword = m_openWalletTriedOnce;
m_openWalletTriedOnce = true;
emit walletOpenPasswordNeeded(showIncorrectPassword, wallet->path());
}
else if (errMsg == QString("basic_string::_M_replace_aux") || errMsg == QString("std::bad_alloc")) {
qCritical() << errMsg;
this->walletManager->clearWalletCache(this->walletPath);
errMsg = QString("%1\n\nAttempted to clean wallet cache. Please restart Feather.").arg(errMsg);
this->closeWallet(false);
emit walletOpenedError(errMsg);
} else if(errMsg.contains("wallet cannot be opened as")) {
this->closeWallet(false);
emit walletOpenedError(errMsg);
} else if(errMsg.contains("is opened by another wallet program")) {
this->closeWallet(false);
emit walletOpenedError(errMsg);
} else {
this->closeWallet(false);
emit walletOpenPasswordNeeded(!this->walletPassword.isEmpty(), wallet->path());
emit walletOpenedError(errMsg);
}
return;
}
m_openWalletTriedOnce = false;
this->refreshed = false;
this->currentWallet = wallet;
this->walletPath = this->currentWallet->path() + ".keys";
this->walletPassword = this->currentWallet->getPassword();
config()->set(Config::walletPath, this->walletPath);
connect(this->currentWallet, &Wallet::moneySpent, this, &AppContext::onMoneySpent);
@ -331,6 +361,7 @@ void AppContext::onWalletOpened(Wallet *wallet) {
connect(this->currentWallet, &Wallet::transactionCommitted, this, &AppContext::onTransactionCommitted);
connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed);
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
connect(this->currentWallet, &Wallet::deviceError, this, &AppContext::onDeviceError);
emit walletOpened();
@ -346,142 +377,6 @@ void AppContext::onWalletOpened(Wallet *wallet) {
// force trigger preferredFiat signal for history model
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
this->setWindowTitle();
}
void AppContext::setWindowTitle(bool mining) {
QFileInfo fileInfo(this->walletPath);
auto title = QString("Feather - [%1]").arg(fileInfo.fileName());
if(this->currentWallet && this->currentWallet->viewOnly())
title += " [view-only]";
if(mining)
title += " [mining]";
emit setTitle(title);
}
void AppContext::onWSMessage(const QJsonObject &msg) {
QString cmd = msg.value("cmd").toString();
if (cmd == "blockheights") {
QJsonObject data = msg.value("data").toObject();
int mainnet = data.value("mainnet").toInt();
int stagenet = data.value("stagenet").toInt();
this->heights[NetworkType::MAINNET] = mainnet;
this->heights[NetworkType::STAGENET] = stagenet;
}
else if(cmd == "nodes") {
this->onWSNodes(msg.value("data").toArray());
}
#if defined(HAS_XMRIG)
else if(cmd == "xmrig") {
this->XMRigDownloads(msg.value("data").toObject());
}
#endif
else if(cmd == "crypto_rates") {
QJsonArray crypto_rates = msg.value("data").toArray();
AppContext::prices->cryptoPricesReceived(crypto_rates);
}
else if(cmd == "fiat_rates") {
QJsonObject fiat_rates = msg.value("data").toObject();
AppContext::prices->fiatPricesReceived(fiat_rates);
}
else if(cmd == "reddit") {
QJsonArray reddit_data = msg.value("data").toArray();
this->onWSReddit(reddit_data);
}
else if(cmd == "ccs") {
auto ccs_data = msg.value("data").toArray();
this->onWSCCS(ccs_data);
}
else if(cmd == "txFiatHistory") {
auto txFiatHistory_data = msg.value("data").toObject();
AppContext::txFiatHistory->onWSData(txFiatHistory_data);
}
}
void AppContext::onWSNodes(const QJsonArray &nodes) {
QList<QSharedPointer<FeatherNode>> l;
for (auto &&entry: nodes) {
auto obj = entry.toObject();
auto nettype = obj.value("nettype");
auto type = obj.value("type");
// filter remote node network types
if(nettype == "mainnet" && this->networkType != NetworkType::MAINNET)
continue;
if(nettype == "stagenet" && this->networkType != NetworkType::STAGENET)
continue;
if(nettype == "testnet" && this->networkType != NetworkType::TESTNET)
continue;
if(type == "clearnet" && (this->isTails || this->isWhonix || this->isTorSocks))
continue;
if(type == "tor" && (!(this->isTails || this->isWhonix || this->isTorSocks)))
continue;
auto node = new FeatherNode(
obj.value("address").toString(),
obj.value("height").toInt(),
obj.value("target_height").toInt(),
obj.value("online").toBool());
QSharedPointer<FeatherNode> r = QSharedPointer<FeatherNode>(node);
l.append(r);
}
this->nodes->onWSNodesReceived(l);
}
void AppContext::onWSReddit(const QJsonArray& reddit_data) {
QList<QSharedPointer<RedditPost>> l;
for (auto &&entry: reddit_data) {
auto obj = entry.toObject();
auto redditPost = new RedditPost(
obj.value("title").toString(),
obj.value("author").toString(),
obj.value("permalink").toString(),
obj.value("comments").toInt());
QSharedPointer<RedditPost> r = QSharedPointer<RedditPost>(redditPost);
l.append(r);
}
emit redditUpdated(l);
}
void AppContext::onWSCCS(const QJsonArray &ccs_data) {
QList<QSharedPointer<CCSEntry>> l;
QStringList fonts = {"state", "address", "author", "date",
"title", "target_amount", "raised_amount",
"percentage_funded", "contributions"};
for (auto &&entry: ccs_data) {
auto obj = entry.toObject();
auto c = QSharedPointer<CCSEntry>(new CCSEntry());
if (obj.value("state").toString() != "FUNDING-REQUIRED")
continue;
c->state = obj.value("state").toString();
c->address = obj.value("address").toString();
c->author = obj.value("author").toString();
c->date = obj.value("date").toString();
c->title = obj.value("title").toString();
c->url = obj.value("url").toString();
c->target_amount = obj.value("target_amount").toDouble();
c->raised_amount = obj.value("raised_amount").toDouble();
c->percentage_funded = obj.value("percentage_funded").toDouble();
c->contributions = obj.value("contributions").toInt();
l.append(c);
}
emit ccsUpdated(l);
}
void AppContext::createConfigDirectory(const QString &dir) {
@ -528,7 +423,18 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
return;
}
this->createWalletFinish(password);
this->onWalletOpened(wallet);
}
void AppContext::createWalletFromDevice(const QString &path, const QString &password, int restoreHeight) {
if(Utils::fileExists(path)) {
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
qCritical() << err;
emit walletCreatedError(err);
return;
}
this->walletManager->createWalletFromDeviceAsync(path, password, this->networkType, "Ledger", restoreHeight);
}
void AppContext::createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic) {
@ -539,7 +445,7 @@ void AppContext::createWalletFromKeys(const QString &path, const QString &passwo
return;
}
if(!this->walletManager->addressValid(address, this->networkType)) {
if(!WalletManager::addressValid(address, this->networkType)) {
auto err = QString("Failed to create wallet. Invalid address provided.").arg(path);
qCritical() << err;
emit walletCreatedError(err);
@ -560,21 +466,8 @@ void AppContext::createWalletFromKeys(const QString &path, const QString &passwo
return;
}
this->currentWallet = this->walletManager->createWalletFromKeys(path, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
this->createWalletFinish(password);
}
void AppContext::createWalletFinish(const QString &password) {
this->currentWallet->setPassword(password);
this->currentWallet->store();
this->walletPassword = password;
emit walletCreated(this->currentWallet);
}
void AppContext::initRestoreHeights() {
restoreHeights[NetworkType::TESTNET] = new RestoreHeightLookup(NetworkType::TESTNET);
restoreHeights[NetworkType::STAGENET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_stagenet.txt", NetworkType::STAGENET);
restoreHeights[NetworkType::MAINNET] = RestoreHeightLookup::fromFile(":/assets/restore_heights_monero_mainnet.txt", NetworkType::MAINNET);
Wallet *wallet = this->walletManager->createWalletFromKeys(path, password, this->seedLanguage, this->networkType, address, viewkey, spendkey, restoreHeight);
this->walletManager->walletOpened(wallet);
}
void AppContext::onSetRestoreHeight(quint64 height){
@ -643,7 +536,7 @@ void AppContext::donateBeg() {
if (this->currentWallet->viewOnly()) return;
auto donationCounter = config()->get(Config::donateBeg).toInt();
if(donationCounter == -1)
if (donationCounter == -1)
return; // previously donated
donationCounter += 1;
@ -688,7 +581,13 @@ void AppContext::onWalletUpdate() {
this->updateBalance();
}
void AppContext::onWalletRefreshed(bool success) {
void AppContext::onWalletRefreshed(bool success, const QString &message) {
if (!success) {
// Something went wrong during refresh, in some cases we need to notify the user
qCritical() << "Exception during refresh: " << message; // Can't use ->errorString() here, other SLOT might snipe it first
return;
}
if (!this->refreshed) {
refreshModels();
this->refreshed = true;
@ -778,11 +677,10 @@ void AppContext::updateBalance() {
if (!this->currentWallet)
return;
quint64 balance_u = this->currentWallet->balance();
AppContext::balance = balance_u / globals::cdiv;
double spendable = this->currentWallet->unlockedBalance();
quint64 balance = this->currentWallet->balance();
quint64 spendable = this->currentWallet->unlockedBalance();
emit balanceUpdated(balance_u, spendable);
emit balanceUpdated(balance, spendable);
}
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {

50
src/appcontext.h

@ -5,16 +5,13 @@
#define FEATHER_APPCONTEXT_H
#include <QObject>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QTimer>
#include "utils/tails.h"
#include "utils/whonix.h"
#include "utils/prices.h"
#include "utils/networking.h"
#include "utils/tor.h"
#include "utils/xmrig.h"
#include "utils/TorManager.h"
#include "utils/wsclient.h"
#include "utils/txfiathistory.h"
#include "utils/FeatherSeed.h"
@ -28,9 +25,6 @@
#include "utils/keysfiles.h"
#include "PendingTransaction.h"
#define SUBADDRESS_LOOKAHEAD_MINOR 200
class AppContext : public QObject
{
Q_OBJECT
@ -43,7 +37,6 @@ public:
bool isTails = false;
bool isWhonix = false;
bool isTorSocks = false;
bool donationSending = false;
@ -54,25 +47,13 @@ public:
QString walletPassword = "";
NetworkType::Type networkType;
QMap<NetworkType::Type, int> heights;
QMap<NetworkType::Type, RestoreHeightLookup*> restoreHeights;
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
QNetworkAccessManager *network;
QNetworkAccessManager *networkClearnet;
QNetworkProxy *networkProxy{};
Nodes *nodes; // TODO: move this to mainwindow (?)
Tor *tor{};
WSClient *ws;
XmRig *XMRig;
Nodes *nodes;
DaemonRpc *daemonRpc;
static Prices *prices;
static WalletKeysFilesModel *wallets;
static double balance;
static QMap<QString, QString> txCache;
static TxFiatHistory *txFiatHistory;
static void createConfigDirectory(const QString &dir);
@ -81,17 +62,15 @@ public:
WalletManager *walletManager;
Wallet *currentWallet = nullptr;
void createWallet(FeatherSeed seed, const QString &path, const QString &password, const QString &seedOffset = "");
void createWalletFromDevice(const QString &path, const QString &password, int restoreHeight);
void createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, bool deterministic = false);
void createWalletFinish(const QString &password);
void commitTransaction(PendingTransaction *tx);
void syncStatusUpdated(quint64 height, quint64 target);
void updateBalance();
void initTor();
void initRestoreHeights();
void initWS();
void donateBeg();
void refreshModels();
void setWindowTitle(bool mining = false);
// Closes the currently opened wallet
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
@ -99,6 +78,7 @@ public:
public slots:
void onOpenWallet(const QString& path, const QString &password);
void onWalletCreated(Wallet * wallet);
void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
@ -109,18 +89,17 @@ public slots:
void onPreferredFiatCurrencyChanged(const QString &symbol);
void onAmountPrecisionChanged(int precision);
void onMultiBroadcast(PendingTransaction *tx);
void onDeviceButtonRequest(quint64 code);
void onTorSettingsChanged();
void onInitialNetworkConfigured();
void onDeviceError(const QString &message);
private slots:
void onWSNodes(const QJsonArray &nodes);
void onWSMessage(const QJsonObject& msg);
void onWSCCS(const QJsonArray &ccs_data);
void onWSReddit(const QJsonArray& reddit_data);
void onMoneySpent(const QString &txId, quint64 amount);
void onMoneyReceived(const QString &txId, quint64 amount);
void onUnconfirmedMoneyReceived(const QString &txId, quint64 amount);
void onWalletUpdate();
void onWalletRefreshed(bool success);
void onWalletRefreshed(bool success, const QString &message);
void onWalletOpened(Wallet *wallet);
void onWalletNewBlock(quint64 blockheight, quint64 targetHeight);
void onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight);
@ -148,12 +127,6 @@ signals:
void createTransactionError(QString message);
void createTransactionCancelled(const QVector<QString> &address, double amount);
void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
void redditUpdated(QList<QSharedPointer<RedditPost>> &posts);
void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
void nodeSourceChanged(NodeSource nodeSource);
void XMRigDownloads(const QJsonObject &data);
void setCustomNodes(QList<FeatherNode> nodes);
void openAliasResolveError(const QString &msg);
void openAliasResolved(const QString &address, const QString &openAlias);
void setRestoreHeightError(const QString &msg);
@ -162,10 +135,13 @@ signals:
void donationNag();
void initiateTransaction();
void endTransaction();
void setTitle(const QString &title); // set window title
void deviceButtonRequest(quint64 code);
void updatesAvailable(const QJsonObject &updates);
void deviceError(const QString &message);
private:
QTimer m_storeTimer;
bool m_openWalletTriedOnce = false;
};
#endif //FEATHER_APPCONTEXT_H

15
src/assets.qrc

@ -5,6 +5,7 @@
<file>assets/contributors.txt</file>
<file>assets/feather.desktop</file>
<file>assets/nodes.json</file>
<file>assets/gpg_keys/featherwallet.asc</file>
<file>assets/images/appicons/32x32.png</file>
<file>assets/images/appicons/48x48.png</file>
<file>assets/images/appicons/64x64.png</file>
@ -44,10 +45,18 @@
<file>assets/images/gnome-calc.png</file>
<file>assets/images/history.png</file>
<file>assets/images/info.png</file>
<file>assets/images/info2.svg</file>
<file>assets/images/key.png</file>
<file>assets/images/ledger.png</file>
<file>assets/images/ledger_unpaired.png</file>
<file>assets/images/lightning.png</file>
<file>assets/images/localMonero_search.svg</file>
<file>assets/images/localMonero_buy.svg</file>
<file>assets/images/localMonero_buy_white.svg</file>
<file>assets/images/localMonero_sell.svg</file>
<file>assets/images/localMonero_sell_white.svg</file>
<file>assets/images/localMonero_logo.png</file>
<file>assets/images/localMonero_register.svg</file>
<file>assets/images/lock.png</file>
<file>assets/images/lock_icon.png</file>
<file>assets/images/lock.svg</file>
@ -63,6 +72,12 @@
<file>assets/images/revealer_c.png</file>
<file>assets/images/revealer.png</file>
<file>assets/images/seal.png</file>
<file>assets/images/securityLevelSafer.png</file>
<file>assets/images/securityLevelSafest.png</file>
<file>assets/images/securityLevelStandard.png</file>
<file>assets/images/securityLevelSaferWhite.png</file>
<file>assets/images/securityLevelSafestWhite.png</file>
<file>assets/images/securityLevelStandardWhite.png</file>
<file>assets/images/seed.png</file>
<file>assets/images/speaker.png</file>
<file>assets/images/status_connected_fork.png</file>

52
src/assets/gpg_keys/featherwallet.asc

@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
Ig==
=2rq8
-----END PGP PUBLIC KEY BLOCK-----

5
src/assets/images/info2.svg

@ -0,0 +1,5 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" enable-background="new 0 0 48 48">
<circle fill="#2196F3" cx="24" cy="24" r="21"/>
<rect x="22" y="22" fill="#fff" width="4" height="11"/>
<circle fill="#fff" cx="24" cy="16.5" r="2.5"/>
</svg>

After

Width:  |  Height:  |  Size: 278 B

1
src/assets/images/localMonero_buy.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M10,0V4H8L12,8L16,4H14V0M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>

After

Width:  |  Height:  |  Size: 574 B

1
src/assets/images/localMonero_buy_white.svg

@ -0,0 +1 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M10,0V4H8L12,8L16,4H14V0M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>

After

Width:  |  Height:  |  Size: 589 B

BIN
src/assets/images/localMonero_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

1
src/assets/images/localMonero_register.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root-684" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></svg>

After

Width:  |  Height:  |  Size: 318 B

1
src/assets/images/localMonero_search.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root-6593" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></svg>

After

Width:  |  Height:  |  Size: 412 B

1
src/assets/images/localMonero_sell.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M12,0L8,4H10V8H14V4H16M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>

After

Width:  |  Height:  |  Size: 572 B

1
src/assets/images/localMonero_sell_white.svg

@ -0,0 +1 @@
<svg fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg" class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" style="margin-right: 8px;"><path d="M12,0L8,4H10V8H14V4H16M1,2V4H3L6.6,11.59L5.25,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42C7.29,15 7.17,14.89 7.17,14.75L7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.59 17.3,11.97L21.16,4.96L19.42,4H19.41L18.31,6L15.55,11H8.53L8.4,10.73L6.16,6L5.21,4L4.27,2M7,18A2,2 0 0,0 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20A2,2 0 0,0 7,18M17,18A2,2 0 0,0 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20A2,2 0 0,0 17,18Z"></path></svg>

After

Width:  |  Height:  |  Size: 587 B

BIN
src/assets/images/securityLevelSafer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

13
src/assets/images/securityLevelSafer.svg

@ -0,0 +1,13 @@
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
use:not(:target) {
display: none;
}
</style>
<defs>
<g id="safer_icon" stroke="none" stroke-width="1">
<path fill-rule="nonzero" d="M7.0 2.1658351C7.0 13.931584 7.0 2.1658351 7.0 13.931584C9.8656467 13.057677 12.0 10.241354 12.0 7.2727273C12.0 7.2727273 12.0 4.2437572 12.0 4.2437572C12.0 4.2437572 7.0 2.1658351 7.0 2.1658351C7.0 2.1658351 7.0 2.1658351 7.0 2.1658351M7.0 0.0C7.0 0.0 14.0 2.9090909 14.0 2.9090909C14.0 2.9090909 14.0 7.2727273 14.0 7.2727273C14.0 11.309091 11.013333 15.083636 7.0 16.0C2.9866667 15.083636 0.0 11.309091 0.0 7.2727273C0.0 7.2727273 0.0 2.9090909 0.0 2.9090909C0.0 2.9090909 7.0 0.0 7.0 0.0"/>
</g>
</defs>
<use id="safer" fill="context-fill" fill-opacity="context-fill-opacity" href="#safer_icon" />
</svg>

After

Width:  |  Height:  |  Size: 932 B

BIN
src/assets/images/securityLevelSaferWhite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/images/securityLevelSafest.png

Binary file not shown.