From 3b6b5b59f547a7e98591e2b32c6d4bb1f635df4b Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Mon, 10 Aug 2020 15:51:11 +0300 Subject: [PATCH 001/146] Source dirs created --- BlockSettleApp/CMakeLists.txt | 1 + BlockSettleApp/main.cpp | 23 ++++++++ CMakeLists.txt | 4 ++ Core/ApiAdapter.cpp | 52 ++++++++++++++++ Core/ApiAdapter.h | 83 ++++++++++++++++++++++++++ Core/CMakeLists.txt | 32 ++++++++++ Core/TerminalMessage.cpp | 108 ++++++++++++++++++++++++++++++++++ Core/TerminalMessage.h | 89 ++++++++++++++++++++++++++++ GUI/CMakeLists.txt | 1 + GUI/QtWidgets/CMakeLists.txt | 0 common | 2 +- 11 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 Core/ApiAdapter.cpp create mode 100644 Core/ApiAdapter.h create mode 100644 Core/CMakeLists.txt create mode 100644 Core/TerminalMessage.cpp create mode 100644 Core/TerminalMessage.h create mode 100644 GUI/CMakeLists.txt create mode 100644 GUI/QtWidgets/CMakeLists.txt diff --git a/BlockSettleApp/CMakeLists.txt b/BlockSettleApp/CMakeLists.txt index 248ccf0c1..cd6f34f2f 100644 --- a/BlockSettleApp/CMakeLists.txt +++ b/BlockSettleApp/CMakeLists.txt @@ -47,6 +47,7 @@ ELSE () ENDIF () TARGET_LINK_LIBRARIES( ${BLOCKSETTLE_APP_NAME} + ${TERMINAL_CORE_NAME} ${BLOCKSETTLE_UI_LIBRARY_NAME} ${BS_NETWORK_LIB_NAME} ${CPP_WALLET_LIB_NAME} diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 3adf7448f..633232d7d 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -30,7 +30,11 @@ #include "BSTerminalSplashScreen.h" #include "EncryptionUtils.h" +#include "ApiAdapter.h" +#include "TerminalMessage.h" + #include "btc/ecc.h" +#include #include "AppNap.h" @@ -292,6 +296,25 @@ int main(int argc, char** argv) startupBIP151CTX(); startupBIP150CTX(4, true); + const auto loggerName = "terminal"; //FIXME: temporary logger + auto logger = spdlog::daily_logger_mt(loggerName, "terminal.log"); + + // [date time.milliseconds] [level](thread id): text + logger->set_pattern("[%D %H:%M:%S.%e] [%l](%t) %s:%#:%!: %v"); + logger->set_level(spdlog::level::debug); + logger->flush_on(spdlog::level::debug); + + bs::message::TerminalInprocBus inprocBus(logger); + + const auto &apiAdapter = std::make_shared(logger); + // apiAdapter->add(guiAdapter); + inprocBus.addAdapter(apiAdapter); + + if (!inprocBus.run()) { + logger->error("No runnable adapter found on main inproc bus"); + // return 1; + } + return GuiApp(argc, argv); } diff --git a/CMakeLists.txt b/CMakeLists.txt index a375b16ab..23130d746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -425,6 +425,8 @@ SET( BS_PROTO_LIB_NAME BsProtoLib ) SET( CELER_PROTO_LIB_NAME CelerProtoLib ) SET( BLOCKSETTLE_APP_NAME blocksettle ) SET( SIGNER_APP_NAME blocksettle_signer ) +SET( TERMINAL_CORE_NAME TerminalCore ) +SET( TERMINAL_GUI_QT_NAME TerminalGUI_Qt ) SET( BLOCKSETTLE_UI_LIBRARY_NAME bsuilib ) SET( BLOCKSETTLE_HW_LIBRARY_NAME HWIntegrations ) SET( CRYPTO_LIB_NAME ArmoryCryptoLib ) @@ -869,6 +871,8 @@ ADD_SUBDIRECTORY( common/Blocksettle_proto ) ADD_SUBDIRECTORY( Celer ) ADD_SUBDIRECTORY( AuthAPI ) +ADD_SUBDIRECTORY(Core) +ADD_SUBDIRECTORY(GUI) ADD_SUBDIRECTORY(BlockSettleUILib) ADD_SUBDIRECTORY(common/BlocksettleNetworkingLib) ADD_SUBDIRECTORY(common/WalletsLib) diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp new file mode 100644 index 000000000..9a7d617b2 --- /dev/null +++ b/Core/ApiAdapter.cpp @@ -0,0 +1,52 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "ApiAdapter.h" +#include + + +/*class ApiBus : public bs::message::Bus +{ +public: + ApiBus(const std::shared_ptr &); + ~ApiBus() override; + + void addAdapter(const std::shared_ptr &) override; + + void SetCommunicationDumpEnabled(bool dumpCommunication) override; + +private: + class ClientAdapter; + class ServerAdapter; + + std::shared_ptr queue_; + const std::vector> adapters_; +};*/ + + +ApiAdapter::ApiAdapter(const std::shared_ptr &logger) + : logger_(logger) +{} + +void ApiAdapter::run() +{ + logger_->debug("[{}]", __func__); +} + +void ApiAdapter::add(const std::shared_ptr &) +{ + +} + +bool ApiAdapter::process(const bs::message::Envelope &) +{ + logger_->debug("[{}]", __func__); + return true; +} diff --git a/Core/ApiAdapter.h b/Core/ApiAdapter.h new file mode 100644 index 000000000..da811540c --- /dev/null +++ b/Core/ApiAdapter.h @@ -0,0 +1,83 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef API_ADAPTER_H +#define API_ADAPTER_H + +#include "Message/Adapter.h" +#include "TerminalMessage.h" + +namespace bs { + namespace message { + class UserAPI : public User + { + public: + bool isFallback() const override + { + return (value() == 0); + } + + bool isBroadcast() const override + { + return (value() < 0); + } + + std::string name() const override + { + if (value() == 0) { + return "Gateway"; + } + else if (value() < 0) { + return "Broadcast"; + } + else { + return "User#" + std::to_string(value()); + } + } + }; + } +} +class ApiBus; + + +class ApiBusAdapter : public bs::message::Adapter +{ + friend class ApiBus; +private: + void setUserId(const bs::message::UserValue); +}; + + +class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner +{ +// friend class GenoaBusGateway; +public: + ApiAdapter(const std::shared_ptr &); + ~ApiAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "API"; } + + void add(const std::shared_ptr &); + void run() override; + +private: + std::shared_ptr user_; + std::shared_ptr logger_; + std::shared_ptr apiBus_; +// std::shared_ptr gwAdapter_; +}; + + +#endif // API_ADAPTER_H diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt new file mode 100644 index 000000000..80e8557c5 --- /dev/null +++ b/Core/CMakeLists.txt @@ -0,0 +1,32 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) + +PROJECT(${TERMINAL_CORE_NAME}) + +FILE(GLOB SOURCES + *.cpp +) +FILE(GLOB HEADERS + *.h +) + +INCLUDE_DIRECTORIES(${COMMON_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${BOTAN_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${WALLET_LIB_INCLUDE_DIR}) + +ADD_LIBRARY(${TERMINAL_CORE_NAME} + ${SOURCES} + ${HEADERS} +) + +TARGET_INCLUDE_DIRECTORIES(${TERMINAL_CORE_NAME} + PUBLIC ${BLOCK_SETTLE_ROOT}/Core + PRIVATE ${BLOCK_SETTLE_ROOT}/common/BlocksettleNetworkingLib +) + +TARGET_LINK_LIBRARIES(${TERMINAL_CORE_NAME} + ${BS_NETWORK_LIB_NAME} + ${CRYPTO_LIB_NAME} + Qt5::Network + Qt5::Core +) +# Qt5::Sql diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp new file mode 100644 index 000000000..31d08c3f7 --- /dev/null +++ b/Core/TerminalMessage.cpp @@ -0,0 +1,108 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "TerminalMessage.h" +#include "Message/Adapter.h" + +using namespace bs::message; + +static const std::map kTerminalUsersMapping = { + { static_cast(TerminalUsers::BROADCAST), "Broadcast" }, + { static_cast(TerminalUsers::Signer), "Signer" }, + { static_cast(TerminalUsers::API), "API" }, + { static_cast(TerminalUsers::Settings), "Settings" }, + { static_cast(TerminalUsers::BsServer), "BsServer" }, + { static_cast(TerminalUsers::Matching), "Matching" }, + { static_cast(TerminalUsers::Assets), "Assets" }, + { static_cast(TerminalUsers::MktData), "MarketData" }, + { static_cast(TerminalUsers::MDHistory), "MDHistory" }, + { static_cast(TerminalUsers::Blockchain), "Armory" }, + { static_cast(TerminalUsers::Wallets), "Wallets" }, + { static_cast(TerminalUsers::Settlement), "Settlement" }, + { static_cast(TerminalUsers::Chat), "Chat" } +}; + +std::string UserTerminal::name() const +{ + const auto itAcc = kTerminalUsersMapping.find(value()); + return (itAcc == kTerminalUsersMapping.end()) + ? User::name() : itAcc->second; +} + +/*namespace bs { + namespace message { + Envelope pbEnvelope(uint64_t id, UsersPB sender, UsersPB receiver + , const TimeStamp &posted, const TimeStamp &execAt, const std::string &message + , bool request) + { + return Envelope{ id, std::make_shared(sender) + , std::make_shared(receiver), posted, execAt, message, request }; + } + + Envelope pbEnvelope(uint64_t id, UsersPB sender, const std::shared_ptr &receiver + , const std::string &message) + { + return Envelope{ id, std::make_shared(sender), receiver + , {}, {}, message, false }; + } + + Envelope pbEnvelope(UsersPB sender, UsersPB receiver, const std::string &message + , bool request, const TimeStamp &execAt) + { + return Envelope{ 0, std::make_shared(sender), std::make_shared(receiver) + , {}, execAt, message, request }; + } + + Envelope pbEnvelope(const std::shared_ptr &sender, UsersPB receiver + , const std::string &message, bool request, const TimeStamp &execAt) + { + return Envelope{ 0, sender, std::make_shared(receiver) + , {}, execAt, message, request }; + } + } //namespace message +} //namespace bs +*/ + +TerminalInprocBus::TerminalInprocBus(const std::shared_ptr &logger) + : logger_(logger) +{ // we can create multiple queues if needed and distribute them on adapters + queue_ = std::make_shared(std::make_shared(logger), logger + , kTerminalUsersMapping); +} + +TerminalInprocBus::~TerminalInprocBus() +{ + shutdown(); +} + +void TerminalInprocBus::addAdapter(const std::shared_ptr &adapter) +{ + queue_->bindAdapter(adapter); + adapter->setQueue(queue_); + auto runner = std::dynamic_pointer_cast(adapter); + if (runner) { + runnableAdapter_ = runner; + } +} + +void TerminalInprocBus::shutdown() +{ + runnableAdapter_.reset(); + queue_->terminate(); +} + +bool TerminalInprocBus::run() +{ + if (!runnableAdapter_) { + return false; + } + runnableAdapter_->run(); + return true; +} diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h new file mode 100644 index 000000000..6e8044583 --- /dev/null +++ b/Core/TerminalMessage.h @@ -0,0 +1,89 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef TERMINAL_MESSAGE_H +#define TERMINAL_MESSAGE_H + +#include "Message/Bus.h" +#include "Message/Envelope.h" + +namespace bs { + + class MainLoopRuner + { + public: + virtual void run() = 0; + }; + + namespace message { + enum class TerminalUsers : UserValue + { + Unknown = 0, + BROADCAST, + Signer, + API, + Settings, + BsServer, // General target/origin of all sorts of BS server messages + Matching, // Alias for Celer or other matching system + Assets, // Alias for Genoa data storage atm + MktData, + MDHistory, // Charts data storage + Blockchain, // General name for Armory connection + Wallets, + Settlement, + Chat, + UsersCount + }; + + class UserTerminal : public User + { + public: + UserTerminal(TerminalUsers value) : User(static_cast(value)) {} + + std::string name() const override; + + bool isBroadcast() const override + { + return (static_cast(value()) == TerminalUsers::BROADCAST); + } + }; + +/* Envelope pbEnvelope(uint64_t id, TerminalUsers sender, TerminalUsers receiver + , const TimeStamp &posted, const TimeStamp &execAt, const std::string &message + , bool request = false); + Envelope pbEnvelope(uint64_t id, UsersPB sender, const std::shared_ptr &receiver + , const std::string &message); + Envelope pbEnvelope(UsersPB sender, UsersPB receiver, const std::string &message + , bool request = false, const TimeStamp &execAt = {}); + Envelope pbEnvelope(const std::shared_ptr &sender, UsersPB receiver + , const std::string &message, bool request = false, const TimeStamp &execAt = {}); +*/ + + class TerminalInprocBus : public Bus + { + public: + TerminalInprocBus(const std::shared_ptr &); + ~TerminalInprocBus() override; + + void addAdapter(const std::shared_ptr &) override; + + void shutdown(); + bool run(); + + private: + std::shared_ptr logger_; + std::shared_ptr queue_; + std::shared_ptr runnableAdapter_; + }; + + } // namespace message +} // namespace bs + +#endif // TERMINAL_MESSAGE_H diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt new file mode 100644 index 000000000..34e819856 --- /dev/null +++ b/GUI/CMakeLists.txt @@ -0,0 +1 @@ +ADD_SUBDIRECTORY( QtWidgets ) diff --git a/GUI/QtWidgets/CMakeLists.txt b/GUI/QtWidgets/CMakeLists.txt new file mode 100644 index 000000000..e69de29bb diff --git a/common b/common index 3388580ff..21e8f9f4f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 3388580ff3a30da391d9ce29e061a2bbb478b8e3 +Subproject commit 21e8f9f4f9b6a90ab35a67d840308161cd4533ef From 9763abe8220657417463cd56b6cdb54656c378e8 Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Mon, 17 Aug 2020 19:28:08 +0300 Subject: [PATCH 002/146] WIP 1 --- .gitignore | 1 + BlockSettleApp/CMakeLists.txt | 1 + BlockSettleApp/main.cpp | 76 +- .../ChatUI/OTCShieldWidgets/OTCShield.h | 2 +- BlockSettleUILib/InfoDialogs/StartupDialog.h | 8 +- BlockSettleUILib/NotificationCenter.cpp | 7 +- BlockSettleUILib/Settings/SignersProvider.cpp | 3 +- BlockSettleUILib/StatusBarView.cpp | 110 ++ BlockSettleUILib/StatusBarView.h | 15 +- Core/ApiAdapter.cpp | 165 ++- Core/ApiAdapter.h | 32 +- Core/AssetsAdapter.cpp | 28 + Core/AssetsAdapter.h | 41 + Core/BsServerAdapter.cpp | 33 + Core/BsServerAdapter.h | 43 + Core/CMakeLists.txt | 2 + Core/ChatAdapter.cpp | 28 + Core/ChatAdapter.h | 41 + Core/MDHistAdapter.cpp | 28 + Core/MDHistAdapter.h | 41 + Core/MatchingAdapter.cpp | 28 + Core/MatchingAdapter.h | 41 + Core/MktDataAdapter.cpp | 28 + Core/MktDataAdapter.h | 41 + Core/SettingsAdapter.cpp | 290 +++++ Core/SettingsAdapter.h | 82 ++ Core/SettlementAdapter.cpp | 28 + Core/SettlementAdapter.h | 41 + Core/SignerAdapter.cpp | 278 +++++ Core/SignerAdapter.h | 66 ++ Core/TerminalMessage.cpp | 61 +- Core/TerminalMessage.h | 30 +- GUI/CMakeLists.txt | 2 +- GUI/QtWidgets/CMakeLists.txt | 72 ++ GUI/QtWidgets/MainWindow.cpp | 1027 +++++++++++++++++ GUI/QtWidgets/MainWindow.h | 160 +++ GUI/QtWidgets/QtGuiAdapter.cpp | 350 ++++++ GUI/QtWidgets/QtGuiAdapter.h | 73 ++ Scripts/RFQBot_LMAX.qml | 31 +- common | 2 +- 40 files changed, 3314 insertions(+), 122 deletions(-) create mode 100644 Core/AssetsAdapter.cpp create mode 100644 Core/AssetsAdapter.h create mode 100644 Core/BsServerAdapter.cpp create mode 100644 Core/BsServerAdapter.h create mode 100644 Core/ChatAdapter.cpp create mode 100644 Core/ChatAdapter.h create mode 100644 Core/MDHistAdapter.cpp create mode 100644 Core/MDHistAdapter.h create mode 100644 Core/MatchingAdapter.cpp create mode 100644 Core/MatchingAdapter.h create mode 100644 Core/MktDataAdapter.cpp create mode 100644 Core/MktDataAdapter.h create mode 100644 Core/SettingsAdapter.cpp create mode 100644 Core/SettingsAdapter.h create mode 100644 Core/SettlementAdapter.cpp create mode 100644 Core/SettlementAdapter.h create mode 100644 Core/SignerAdapter.cpp create mode 100644 Core/SignerAdapter.h create mode 100644 GUI/QtWidgets/MainWindow.cpp create mode 100644 GUI/QtWidgets/MainWindow.h create mode 100644 GUI/QtWidgets/QtGuiAdapter.cpp create mode 100644 GUI/QtWidgets/QtGuiAdapter.h diff --git a/.gitignore b/.gitignore index e65155d73..2cf37b67d 100755 --- a/.gitignore +++ b/.gitignore @@ -126,3 +126,4 @@ build_terminal/ generated_proto/ terminal.*/ BlockSettleHW/ledger/hidapi/* +GUI/QtWidgets/ui/* diff --git a/BlockSettleApp/CMakeLists.txt b/BlockSettleApp/CMakeLists.txt index cd6f34f2f..9581b766c 100644 --- a/BlockSettleApp/CMakeLists.txt +++ b/BlockSettleApp/CMakeLists.txt @@ -48,6 +48,7 @@ ENDIF () TARGET_LINK_LIBRARIES( ${BLOCKSETTLE_APP_NAME} ${TERMINAL_CORE_NAME} + ${TERMINAL_GUI_QT_NAME} ${BLOCKSETTLE_UI_LIBRARY_NAME} ${BS_NETWORK_LIB_NAME} ${CPP_WALLET_LIB_NAME} diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 633232d7d..4b59c7743 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -30,13 +30,22 @@ #include "BSTerminalSplashScreen.h" #include "EncryptionUtils.h" +#include "Adapters/BlockchainAdapter.h" +#include "Adapters/WalletsAdapter.h" #include "ApiAdapter.h" -#include "TerminalMessage.h" +#include "ChatAdapter.h" +#include "MatchingAdapter.h" +#include "MDHistAdapter.h" +#include "MktDataAdapter.h" +#include "QtGuiAdapter.h" +#include "SettingsAdapter.h" +#include "SettlementAdapter.h" +#include "SignerAdapter.h" #include "btc/ecc.h" #include -#include "AppNap.h" +//#include "AppNap.h" #ifdef USE_QWindowsIntegrationPlugin Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) @@ -129,7 +138,7 @@ static void checkStyleSheet(QApplication &app) app.setStyleSheet(QString::fromLatin1(stylesheetFile.readAll())); } -QScreen *getDisplay(QPoint position) +static QScreen *getDisplay(QPoint position) { for (auto currentScreen : QGuiApplication::screens()) { if (currentScreen->availableGeometry().contains(position, false)) { @@ -157,7 +166,7 @@ static int runUnchecked(QApplication *app, const std::shared_ptrexec(); } @@ -191,7 +200,6 @@ static int GuiApp(int &argc, char** argv) QApplication app(argc, argv); #endif - QApplication::setQuitOnLastWindowClosed(false); QFileInfo localStyleSheetFile(QLatin1String("stylesheet.css")); @@ -296,26 +304,50 @@ int main(int argc, char** argv) startupBIP151CTX(); startupBIP150CTX(4, true); - const auto loggerName = "terminal"; //FIXME: temporary logger - auto logger = spdlog::daily_logger_mt(loggerName, "terminal.log"); - - // [date time.milliseconds] [level](thread id): text - logger->set_pattern("[%D %H:%M:%S.%e] [%l](%t) %s:%#:%!: %v"); - logger->set_level(spdlog::level::debug); - logger->flush_on(spdlog::level::debug); - - bs::message::TerminalInprocBus inprocBus(logger); - - const auto &apiAdapter = std::make_shared(logger); - // apiAdapter->add(guiAdapter); - inprocBus.addAdapter(apiAdapter); + QStringList args; + for (int i = 0; i < argc; ++i) { + args << QLatin1String(argv[i]); + } - if (!inprocBus.run()) { - logger->error("No runnable adapter found on main inproc bus"); - // return 1; + try { + const auto &settings = std::make_shared(); + const auto &adSettings = std::make_shared(settings, args); + const auto &logMgr = adSettings->logManager(); + + bs::message::TerminalInprocBus inprocBus(logMgr->logger()); + inprocBus.addAdapter(adSettings); + + const auto &apiAdapter = std::make_shared(logMgr->logger("API")); + const auto &guiAdapter = std::make_shared(logMgr->logger("ui")); + apiAdapter->add(guiAdapter); + inprocBus.addAdapter(apiAdapter); + + inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger() + , bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain))); + inprocBus.addAdapter(std::make_shared(logMgr->logger() + , bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets))); + + inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger())); + + inprocBus.start(); + if (!inprocBus.run(argc, argv)) { + logMgr->logger()->error("No runnable adapter found on main inproc bus"); + return EXIT_FAILURE; + } + } + catch (const std::exception &e) { + std::cerr << "Failed to start BlockSettle Terminal: " << e.what() << std::endl; + BSMessageBox(BSMessageBox::critical, QObject::tr("BlockSettle Terminal") + , QObject::tr("Unhandled exception detected: %1").arg(QLatin1String(e.what()))).exec(); + return EXIT_FAILURE; } - return GuiApp(argc, argv); +// return GuiApp(argc, argv); } #include "main.moc" diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h index cd070e898..72ba8894b 100644 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h +++ b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h @@ -11,7 +11,7 @@ #ifndef __OTC_SHIELD_H__ #define __OTC_SHIELD_H__ -#include "WalletShieldBase.h" +#include "Trading/WalletShieldBase.h" class OTCShield : public WalletShieldBase { diff --git a/BlockSettleUILib/InfoDialogs/StartupDialog.h b/BlockSettleUILib/InfoDialogs/StartupDialog.h index 5edefb037..d1c88bf47 100644 --- a/BlockSettleUILib/InfoDialogs/StartupDialog.h +++ b/BlockSettleUILib/InfoDialogs/StartupDialog.h @@ -36,8 +36,9 @@ class StartupDialog : public QDialog explicit StartupDialog(bool showLicense, QWidget *parent = nullptr); ~StartupDialog() override; - void init(const std::shared_ptr &appSettings); - void applySelectedConnectivity(std::shared_ptr &armoryServersProvider); + [[deprecated]] void init(const std::shared_ptr &appSettings); + [[deprecated]] void applySelectedConnectivity(std::shared_ptr &armoryServersProvider); + NetworkType getSelectedNetworkType() const; private slots: void onBack(); @@ -48,13 +49,12 @@ private slots: void updateStatus(); void adjustPosition(); void setupConnectivityList(); - NetworkType getSelectedNetworkType() const; private: std::unique_ptr ui_; bool showLicense_; - std::shared_ptr appSettings_; + std::shared_ptr appSettings_; //obsolete }; #endif // __STARTUPDIALOG_H__ diff --git a/BlockSettleUILib/NotificationCenter.cpp b/BlockSettleUILib/NotificationCenter.cpp index 0649e338f..c385281dd 100644 --- a/BlockSettleUILib/NotificationCenter.cpp +++ b/BlockSettleUILib/NotificationCenter.cpp @@ -103,7 +103,7 @@ NotificationTabResponder::TabAction NotificationTabResponder::getTabActionFor(bs return { -1, false, false }; } return { mainWinUi_->tabWidget->indexOf(mainWinUi_->widgetRFQReply), (msg[0].toInt() > 0), - !appSettings_->get(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter)}; + appSettings_ ? !appSettings_->get(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter) : true}; case bs::ui::NotifyType::BlockchainTX: return { mainWinUi_->tabWidget->indexOf(mainWinUi_->widgetTransactions), true, true }; @@ -167,7 +167,7 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif switch (nt) { case bs::ui::NotifyType::BlockchainTX: - if ((msg.size() < 2) || !appSettings_->get(ApplicationSettings::notifyOnTX)) { + if ((msg.size() < 2) || appSettings_ ? !appSettings_->get(ApplicationSettings::notifyOnTX) : true) { return; } title = msg[0].toString(); @@ -321,8 +321,7 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif void NotificationTrayIconResponder::messageClicked() { - if (newVersionMessage_) { - qDebug() << "NEW VERSION"; + if (newVersionMessage_ && appSettings_) { const auto url = appSettings_->get(ApplicationSettings::Binaries_Dl_Url); const auto title = tr("New version download"); const auto errDownload = tr("Failed to open download URL"); diff --git a/BlockSettleUILib/Settings/SignersProvider.cpp b/BlockSettleUILib/Settings/SignersProvider.cpp index 764df735f..d2bc0feb3 100644 --- a/BlockSettleUILib/Settings/SignersProvider.cpp +++ b/BlockSettleUILib/Settings/SignersProvider.cpp @@ -259,8 +259,9 @@ void SignersProvider::setupSigner(int index, bool needUpdate) if (index >= 0 && index < signerList.size()) { appSettings_->set(ApplicationSettings::signerIndex, index); - if (needUpdate) + if (needUpdate) { emit dataChanged(); + } } } diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 53822f697..7eea1b70e 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -107,6 +107,69 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory containerStatusLabel_->setPixmap(iconContainerOffline_); } +StatusBarView::StatusBarView(QStatusBar *parent) + : QObject(nullptr) + , statusBar_(parent) + , iconSize_(16, 16) +{ + for (int s : {16, 24, 32}) + { + iconCeler_.addFile(QString(QLatin1String(":/ICON_BS_%1")).arg(s), QSize(s, s), QIcon::Normal); + iconCeler_.addFile(QString(QLatin1String(":/ICON_BS_%1_GRAY")).arg(s), QSize(s, s), QIcon::Disabled); + } + estimateLabel_ = new QLabel(statusBar_); + + iconCelerOffline_ = iconCeler_.pixmap(iconSize_, QIcon::Disabled); + iconCelerConnecting_ = iconCeler_.pixmap(iconSize_, QIcon::Disabled); + iconCelerOnline_ = iconCeler_.pixmap(iconSize_, QIcon::Normal); + iconCelerError_ = iconCeler_.pixmap(iconSize_, QIcon::Disabled); + + QIcon contIconGray(QLatin1String(":/ICON_STATUS_OFFLINE")); + QIcon contIconYellow(QLatin1String(":/ICON_STATUS_CONNECTING")); + QIcon contIconRed(QLatin1String(":/ICON_STATUS_ERROR")); + QIcon contIconGreen(QLatin1String(":/ICON_STATUS_ONLINE")); + + iconContainerOffline_ = contIconGray.pixmap(iconSize_); + iconContainerError_ = contIconRed.pixmap(iconSize_); + iconContainerOnline_ = contIconGreen.pixmap(iconSize_); + + balanceLabel_ = new QLabel(statusBar_); + + progressBar_ = new CircleProgressBar(statusBar_); + progressBar_->setMinimum(0); + progressBar_->setMaximum(100); + progressBar_->hide(); + + celerConnectionIconLabel_ = new QLabel(statusBar_); + connectionStatusLabel_ = new QLabel(statusBar_); + + containerStatusLabel_ = new QLabel(statusBar_); + containerStatusLabel_->setPixmap(iconContainerOffline_); + containerStatusLabel_->setToolTip(tr("Signing container status")); + + statusBar_->addWidget(estimateLabel_); + statusBar_->addWidget(balanceLabel_); + + statusBar_->addPermanentWidget(celerConnectionIconLabel_); + statusBar_->addPermanentWidget(CreateSeparator()); + statusBar_->addPermanentWidget(progressBar_); + statusBar_->addPermanentWidget(connectionStatusLabel_); + statusBar_->addPermanentWidget(CreateSeparator()); + statusBar_->addPermanentWidget(containerStatusLabel_); + + QWidget* w = new QWidget(); + w->setFixedWidth(6); + statusBar_->addPermanentWidget(w); + + statusBar_->setStyleSheet(QLatin1String("QStatusBar::item { border: none; }")); + + SetLoggedOutStatus(); + + onConnectionClosed(); + + containerStatusLabel_->setPixmap(iconContainerOffline_); +} + StatusBarView::~StatusBarView() noexcept { estimateLabel_->deleteLater(); @@ -291,6 +354,53 @@ void StatusBarView::setBalances() balanceLabel_->setText(text); } +void StatusBarView::onBalanceUpdated(const std::string &symbol, double balance) +{ + balances_[symbol] = balance; + const auto &it = std::find(balanceSymbols_.cbegin(), balanceSymbols_.cend(), symbol); + if (it == balanceSymbols_.cend()) { + if (symbol == bs::network::XbtCurrency) { + balanceSymbols_.insert(balanceSymbols_.cbegin(), symbol); + } + else { + balanceSymbols_.push_back(symbol); + } + } + + QString text; + for (const auto ¤cy : balanceSymbols_) { + if (currency == bs::network::XbtCurrency) { + QString xbt; + switch (armoryConnState_) { + case ArmoryState::Ready: + xbt = UiUtils::displayAmount(balances_.at(currency)); + break; + + case ArmoryState::Scanning: + case ArmoryState::Connected: + xbt = tr("Loading..."); + break; + + case ArmoryState::Closing: + case ArmoryState::Offline: + xbt = tr("..."); + break; + + default: + xbt = tr("..."); + } + + text += tr(" XBT: %1 ").arg(xbt); + } + else { + text += tr("| %1: %2 ") + .arg(QString::fromStdString(currency)) + .arg(UiUtils::displayCurrencyAmount(balances_.at(currency))); + } + } + balanceLabel_->setText(text); +} + void StatusBarView::updateConnectionStatusDetails() { switch (armory_->state()) { diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index 673fcc2ac..f25314fde 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -35,10 +35,11 @@ class StatusBarView : public QObject, public ArmoryCallbackTarget { Q_OBJECT public: - StatusBarView(const std::shared_ptr & + [[deprecated]] StatusBarView(const std::shared_ptr & , const std::shared_ptr & , std::shared_ptr assetManager, const std::shared_ptr & , const std::shared_ptr &, QStatusBar *parent); + StatusBarView(QStatusBar *parent); ~StatusBarView() noexcept override; StatusBarView(const StatusBarView&) = delete; @@ -46,7 +47,8 @@ class StatusBarView : public QObject, public ArmoryCallbackTarget StatusBarView(StatusBarView&&) = delete; StatusBarView& operator = (StatusBarView&&) = delete; -private slots: +public slots: + void onBalanceUpdated(const std::string &symbol, double balance); void onPrepareArmoryConnection(NetworkType); void onArmoryStateChanged(ArmoryState); void onArmoryProgress(BDMPhase, float progress, unsigned int secondsRem); @@ -78,16 +80,15 @@ private slots: private: void setupBtcIcon(NetworkType); void SetLoggedinStatus(); - void SetCelerErrorStatus(const QString& message); void SetLoggedOutStatus(); void SetCelerConnectingStatus(); QWidget *CreateSeparator(); - void setBalances(); - void updateConnectionStatusDetails(); + [[deprecated]] void setBalances(); + [[deprecated]] void updateConnectionStatusDetails(); private: void updateProgress(float progress, unsigned secondsRem); - QString getImportingText() const; + [[deprecated]] QString getImportingText() const; private: QStatusBar *statusBar_; @@ -118,6 +119,8 @@ private slots: std::shared_ptr walletsManager_; std::shared_ptr assetManager_; std::unordered_set importingWallets_; + std::vector balanceSymbols_; + std::unordered_map balances_; }; #endif // __STATUS_BAR_VIEW_H__ diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp index 9a7d617b2..bf3d56766 100644 --- a/Core/ApiAdapter.cpp +++ b/Core/ApiAdapter.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * @@ -12,41 +12,168 @@ #include -/*class ApiBus : public bs::message::Bus +class ApiRouter : public bs::message::Router { public: - ApiBus(const std::shared_ptr &); - ~ApiBus() override; + ApiRouter(const std::shared_ptr &logger) + : bs::message::Router(logger) + {} - void addAdapter(const std::shared_ptr &) override; +protected: + bool isDefaultRouted(const bs::message::Envelope &env) const override + { + if (std::dynamic_pointer_cast(env.receiver)) { + return true; + } + return bs::message::Router::isDefaultRouted(env); + } +}; - void SetCommunicationDumpEnabled(bool dumpCommunication) override; + +class ApiBus : public bs::message::Bus +{ +public: + ApiBus(const std::shared_ptr &logger) + { + queue_ = std::make_shared( + std::make_shared(logger), logger); + } + + ~ApiBus() override + { + queue_->terminate(); + } + + void addAdapter(const std::shared_ptr &adapter) override + { + queue_->bindAdapter(adapter); + adapter->setQueue(queue_); + adapters_.push_back(adapter); + } private: - class ClientAdapter; - class ServerAdapter; + std::shared_ptr queue_; + std::vector> adapters_; +}; + + +class ApiBusGateway : public ApiBusAdapter +{ +public: + ApiBusGateway(const std::shared_ptr &logger + , ApiAdapter *parent) + : logger_(logger), parent_(parent) + , user_(std::make_shared(0)) + , userTermBroadcast_(std::make_shared(bs::message::TerminalUsers::BROADCAST)) + {} + + std::set> supportedReceivers() const override + { + return { user_ }; + } + std::string name() const override { return "APIbusGW"; } - std::shared_ptr queue_; - const std::vector> adapters_; -};*/ + bool process(const bs::message::Envelope &env) override + { + if (!env.receiver) { + return true; // ignore unsolicited broadcasts + } + auto envCopy = env; + envCopy.id = 0; + envCopy.sender = parent_->user_; + + if (std::dynamic_pointer_cast(env.receiver)) { + return parent_->pushFill(envCopy); + } + else if (env.receiver->isFallback()) { + if (env.request) { + envCopy.receiver = userTermBroadcast_; + return parent_->pushFill(envCopy); + } + else { + const auto &itId = idMap_.find(env.id); + if (itId == idMap_.end()) { + envCopy.receiver = userTermBroadcast_; + return parent_->pushFill(envCopy); + } + else { + envCopy.id = itId->second.id; + envCopy.receiver = itId->second.requester; + if (parent_->push(envCopy)) { + idMap_.erase(env.id); + } + return true; + } + } + } + return true; + } + + bool pushToApiBus(const bs::message::Envelope &env) + { + auto envCopy = env; + envCopy.id = 0; + envCopy.receiver.reset(); // = std::make_shared(-1); + bool rc = pushFill(envCopy); + if (rc && env.request) { + idMap_[envCopy.id] = { env.id, env.sender }; + } + return rc; + } + + +private: + std::shared_ptr logger_; + ApiAdapter * parent_{ nullptr }; + std::shared_ptr user_; + std::shared_ptr userTermBroadcast_; + + struct RequestData { + uint64_t id; + std::shared_ptr requester; + }; + std::map idMap_; +}; ApiAdapter::ApiAdapter(const std::shared_ptr &logger) : logger_(logger) -{} - -void ApiAdapter::run() + , user_(std::make_shared(bs::message::TerminalUsers::API)) { - logger_->debug("[{}]", __func__); + apiBus_ = std::make_shared(logger); + gwAdapter_ = std::make_shared(logger, this); + apiBus_->addAdapter(gwAdapter_); } -void ApiAdapter::add(const std::shared_ptr &) +void ApiAdapter::run(int &argc, char **argv) { + if (mainLoopAdapter_) { + mainLoopAdapter_->run(argc, argv); + } + else { + while (true) { + std::this_thread::sleep_for(std::chrono::milliseconds{ 10 }); + } + } +} +void ApiAdapter::add(const std::shared_ptr &adapter) +{ + const auto &runner = std::dynamic_pointer_cast(adapter); + if (runner) { + if (mainLoopAdapter_) { + logger_->error("[{}] main loop adapter already set - ignoring {}" + , __func__, adapter->name()); + } + else { + mainLoopAdapter_ = runner; + } + } + adapter->setUserId(++nextApiUser_); + apiBus_->addAdapter(adapter); } -bool ApiAdapter::process(const bs::message::Envelope &) +bool ApiAdapter::process(const bs::message::Envelope &env) { - logger_->debug("[{}]", __func__); - return true; + return gwAdapter_->pushToApiBus(env); } diff --git a/Core/ApiAdapter.h b/Core/ApiAdapter.h index da811540c..1b323da4f 100644 --- a/Core/ApiAdapter.h +++ b/Core/ApiAdapter.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * @@ -19,6 +19,8 @@ namespace bs { class UserAPI : public User { public: + UserAPI(UserValue value) : User(value) {} + bool isFallback() const override { return (value() == 0); @@ -45,19 +47,30 @@ namespace bs { } } class ApiBus; +class ApiBusGateway; class ApiBusAdapter : public bs::message::Adapter { - friend class ApiBus; -private: - void setUserId(const bs::message::UserValue); +public: + std::set> supportedReceivers() const override + { + return { user_ }; + } + + void setUserId(const bs::message::UserValue userVal) + { + user_ = std::make_shared(userVal); + } + +protected: + std::shared_ptr user_; }; class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner { -// friend class GenoaBusGateway; + friend class ApiBusGateway; public: ApiAdapter(const std::shared_ptr &); ~ApiAdapter() override = default; @@ -70,13 +83,16 @@ class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner std::string name() const override { return "API"; } void add(const std::shared_ptr &); - void run() override; + void run(int &argc, char **argv) override; private: std::shared_ptr user_; std::shared_ptr logger_; - std::shared_ptr apiBus_; -// std::shared_ptr gwAdapter_; + std::shared_ptr apiBus_; + std::shared_ptr mainLoopAdapter_; + std::shared_ptr gwAdapter_; + + bs::message::UserValue nextApiUser_{ 0 }; }; diff --git a/Core/AssetsAdapter.cpp b/Core/AssetsAdapter.cpp new file mode 100644 index 000000000..6916d4d57 --- /dev/null +++ b/Core/AssetsAdapter.cpp @@ -0,0 +1,28 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "AssetsAdapter.h" +#include +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +AssetsAdapter::AssetsAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::Assets)) +{} + +bool AssetsAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/AssetsAdapter.h b/Core/AssetsAdapter.h new file mode 100644 index 000000000..451312a47 --- /dev/null +++ b/Core/AssetsAdapter.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef ASSETS_ADAPTER_H +#define ASSETS_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} + +class AssetsAdapter : public bs::message::Adapter +{ +public: + AssetsAdapter(const std::shared_ptr &); + ~AssetsAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Assets"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +#endif // ASSETS_ADAPTER_H diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp new file mode 100644 index 000000000..11706e8ec --- /dev/null +++ b/Core/BsServerAdapter.cpp @@ -0,0 +1,33 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "BsServerAdapter.h" +#include +#include "ConnectionManager.h" +#include "SslCaBundle.h" +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +BsServerAdapter::BsServerAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::BsServer)) +{ + connMgr_ = std::make_shared(logger_); + connMgr_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); +} + +bool BsServerAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h new file mode 100644 index 000000000..4be881ed7 --- /dev/null +++ b/Core/BsServerAdapter.h @@ -0,0 +1,43 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef BS_SERVER_ADAPTER_H +#define BS_SERVER_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} +class ConnectionManager; + +class BsServerAdapter : public bs::message::Adapter +{ +public: + BsServerAdapter(const std::shared_ptr &); + ~BsServerAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "BS Servers"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; + std::shared_ptr connMgr_; +}; + + +#endif // BS_SERVER_ADAPTER_H diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 80e8557c5..9411aa814 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -10,8 +10,10 @@ FILE(GLOB HEADERS ) INCLUDE_DIRECTORIES(${COMMON_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${COMMON_UI_LIB_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${BOTAN_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${WALLET_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${BLOCKSETTLE_UI_INCLUDE_DIR}) # temporary - until some files reside in old structure ADD_LIBRARY(${TERMINAL_CORE_NAME} ${SOURCES} diff --git a/Core/ChatAdapter.cpp b/Core/ChatAdapter.cpp new file mode 100644 index 000000000..4499f1270 --- /dev/null +++ b/Core/ChatAdapter.cpp @@ -0,0 +1,28 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "ChatAdapter.h" +#include +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +ChatAdapter::ChatAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::Chat)) +{} + +bool ChatAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/ChatAdapter.h b/Core/ChatAdapter.h new file mode 100644 index 000000000..17e2da18f --- /dev/null +++ b/Core/ChatAdapter.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef CHAT_ADAPTER_H +#define CHAT_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} + +class ChatAdapter : public bs::message::Adapter +{ +public: + ChatAdapter(const std::shared_ptr &); + ~ChatAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Chat"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +#endif // CHAT_ADAPTER_H diff --git a/Core/MDHistAdapter.cpp b/Core/MDHistAdapter.cpp new file mode 100644 index 000000000..7a8a02c92 --- /dev/null +++ b/Core/MDHistAdapter.cpp @@ -0,0 +1,28 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "MDHistAdapter.h" +#include +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +MDHistAdapter::MDHistAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::MDHistory)) +{} + +bool MDHistAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/MDHistAdapter.h b/Core/MDHistAdapter.h new file mode 100644 index 000000000..0cc94ef82 --- /dev/null +++ b/Core/MDHistAdapter.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef MDHIST_ADAPTER_H +#define MDHIST_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} + +class MDHistAdapter : public bs::message::Adapter +{ +public: + MDHistAdapter(const std::shared_ptr &); + ~MDHistAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "MDHistory"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +#endif // MDHIST_ADAPTER_H diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp new file mode 100644 index 000000000..56457b938 --- /dev/null +++ b/Core/MatchingAdapter.cpp @@ -0,0 +1,28 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "MatchingAdapter.h" +#include +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::Matching)) +{} + +bool MatchingAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h new file mode 100644 index 000000000..c18a47145 --- /dev/null +++ b/Core/MatchingAdapter.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef MATCHING_ADAPTER_H +#define MATCHING_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} + +class MatchingAdapter : public bs::message::Adapter +{ +public: + MatchingAdapter(const std::shared_ptr &); + ~MatchingAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Matching"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +#endif // MATCHING_ADAPTER_H diff --git a/Core/MktDataAdapter.cpp b/Core/MktDataAdapter.cpp new file mode 100644 index 000000000..3a0e032e4 --- /dev/null +++ b/Core/MktDataAdapter.cpp @@ -0,0 +1,28 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "MktDataAdapter.h" +#include +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +MktDataAdapter::MktDataAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::MktData)) +{} + +bool MktDataAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/MktDataAdapter.h b/Core/MktDataAdapter.h new file mode 100644 index 000000000..b357ea653 --- /dev/null +++ b/Core/MktDataAdapter.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef MKT_DATA_ADAPTER_H +#define MKT_DATA_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} + +class MktDataAdapter : public bs::message::Adapter +{ +public: + MktDataAdapter(const std::shared_ptr &); + ~MktDataAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "MktData"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +#endif // MKT_DATA_ADAPTER_H diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp new file mode 100644 index 000000000..ac6e500f6 --- /dev/null +++ b/Core/SettingsAdapter.cpp @@ -0,0 +1,290 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "SettingsAdapter.h" +#include +#include +#include "ApplicationSettings.h" +#include "ArmoryServersProvider.h" +#include "Settings/SignersProvider.h" +#include "LogManager.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; +using namespace bs::message; + + +SettingsAdapter::SettingsAdapter(const std::shared_ptr &settings + , const QStringList &appArgs) + : appSettings_(settings) + , user_(std::make_shared(TerminalUsers::Settings)) + , userBC_(std::make_shared(TerminalUsers::BROADCAST)) +{ + if (!appSettings_->LoadApplicationSettings(appArgs)) { + throw std::runtime_error("failed to load settings"); + } + logMgr_ = std::make_shared(); + logMgr_->add(appSettings_->GetLogsConfig()); + logger_ = logMgr_->logger(); + + if (!appSettings_->get(ApplicationSettings::initialized)) { + appSettings_->SetDefaultSettings(true); + } + appSettings_->selectNetwork(); + logger_->debug("Settings loaded from {}", appSettings_->GetSettingsPath().toStdString()); + + armoryServersProvider_ = std::make_shared(settings); + signersProvider_ = std::make_shared(appSettings_); +} + +bool SettingsAdapter::process(const bs::message::Envelope &env) +{ + if (env.receiver && (env.receiver->value() == TerminalUsers::Settings)) { + SettingsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettingsMessage::kGetRequest: + return processGetRequest(env, msg.get_request()); + case SettingsMessage::kPutRequest: + return processPutRequest(msg.put_request()); + case SettingsMessage::kArmoryServer: + return processArmoryServer(msg.armory_server()); + case SettingsMessage::kSignerRequest: + return processSignerSettings(env); + case SettingsMessage::kSignerSetKey: + return processSignerSetKey(msg.signer_set_key()); + case SettingsMessage::kSignerReset: + return processSignerReset(); + default: + logger_->warn("[SettingsAdapter::process] unknown data case: {}" + , msg.data_case()); + break; + } + } + else if (env.sender->value() == TerminalUsers::Blockchain) { + //TODO: parse Armory messages + } + return true; +} + +bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env + , const SettingsMessage_SettingsRequest &request) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_get_response(); + for (const auto &req : request.requests()) { + auto resp = msgResp->add_responses(); + resp->set_allocated_request(new SettingRequest(req)); + if (req.source() == SettingSource_Local) { + const auto setting = static_cast(req.index()); + switch (req.type()) { + case SettingType_String: + resp->set_s(appSettings_->get(setting)); + break; + case SettingType_Int: + resp->set_i(appSettings_->get(setting)); + break; + case SettingType_UInt: + resp->set_ui(appSettings_->get(setting)); + break; + case SettingType_UInt64: + resp->set_ui64(appSettings_->get(setting)); + break; + case SettingType_Bool: + resp->set_b(appSettings_->get(setting)); + break; + case SettingType_Float: + resp->set_f(appSettings_->get(setting)); + break; + case SettingType_Rect: { + const auto &rect = appSettings_->get(setting); + auto r = resp->mutable_rect(); + r->set_left(rect.left()); + r->set_top(rect.top()); + r->set_width(rect.width()); + r->set_height(rect.height()); + } + break; + case SettingType_Strings: { + const auto &strings = appSettings_->get(setting); + auto ss = resp->mutable_strings(); + for (const auto &string : strings) { + ss->add_strings(string.toStdString()); + } + } + break; + case SettingType_StrMap: { + const auto &map = appSettings_->get(setting); + auto keyVals = resp->mutable_key_vals(); + for (auto it = map.begin(); it != map.end(); it++) { + auto sm = keyVals->add_key_vals(); + sm->set_key(it.key().toStdString()); + sm->set_value(it.value().toString().toStdString()); + } + } + break; + default: + logger_->error("[{}] unknown setting type: {}", __func__, (int)req.type()); + break; + } + } + } + bs::message::Envelope envResp{ env.id, user_, env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(envResp); +} + +bool SettingsAdapter::processPutRequest(const SettingsMessage_SettingsResponse &request) +{ + for (const auto &req : request.responses()) { + if (req.request().source() == SettingSource_Local) { + const auto setting = static_cast(req.request().index()); + switch (req.request().type()) { + case SettingType_String: + appSettings_->set(setting, QString::fromStdString(req.s())); + break; + case SettingType_Int: + appSettings_->set(setting, req.i()); + break; + case SettingType_UInt: + appSettings_->set(setting, req.ui()); + break; + case SettingType_UInt64: + appSettings_->set(setting, req.ui64()); + break; + case SettingType_Bool: + appSettings_->set(setting, req.b()); + break; + case SettingType_Float: + appSettings_->set(setting, req.f()); + break; + case SettingType_Rect: { + QRect rect(req.rect().left(), req.rect().top(), req.rect().width() + , req.rect().height()); + appSettings_->set(setting, rect); + } + break; + case SettingType_Strings: { + QStringList strings; + for (const auto &s : req.strings().strings()) { + strings << QString::fromStdString(s); + } + appSettings_->set(setting, strings); + } + break; + case SettingType_StrMap: { + QVariantMap map; + for (const auto &keyVal : req.key_vals().key_vals()) { + map[QString::fromStdString(keyVal.key())] = QString::fromStdString(keyVal.value()); + } + appSettings_->set(setting, map); + } + break; + default: + logger_->error("[{}] unknown setting type: {}", __func__, (int)req.request().type()); + break; + } + } + } + SettingsMessage msg; + *(msg.mutable_settings_updated()) = request; + bs::message::Envelope env{ 0, user_, userBC_, bs::message::TimeStamp{} + , bs::message::TimeStamp{}, msg.SerializeAsString() }; + return pushFill(env); +} + +bool SettingsAdapter::processArmoryServer(const BlockSettle::Terminal::SettingsMessage_ArmoryServerSet &request) +{ + int selIndex = 0; + for (const auto &server : armoryServersProvider_->servers()) { + if ((server.name == QString::fromStdString(request.server_name())) + && (server.netType == static_cast(request.network_type()))) { + break; + } + selIndex++; + } + if (selIndex >= armoryServersProvider_->servers().size()) { + logger_->error("[{}] failed to find Armory server {}", __func__, request.server_name()); + return true; + } + armoryServersProvider_->setupServer(selIndex); + appSettings_->selectNetwork(); +} + +bool SettingsAdapter::processSignerSettings(const bs::message::Envelope &env) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_signer_response(); + msgResp->set_is_local(signersProvider_->currentSignerIsLocal()); + msgResp->set_network_type(appSettings_->get(ApplicationSettings::netType)); + + const auto &signerHost = signersProvider_->getCurrentSigner(); + msgResp->set_name(signerHost.name.toStdString()); + msgResp->set_host(signerHost.address.toStdString()); + msgResp->set_port(std::to_string(signerHost.port)); + msgResp->set_key(signerHost.key.toStdString()); + msgResp->set_id(signerHost.serverId()); + msgResp->set_remote_keys_dir(signersProvider_->remoteSignerKeysDir()); + msgResp->set_remote_keys_file(signersProvider_->remoteSignerKeysFile()); + + for (const auto &signer : signersProvider_->signers()) { + auto keyVal = msgResp->add_client_keys(); + keyVal->set_key(signer.serverId()); + keyVal->set_value(signer.key.toStdString()); + } + msgResp->set_local_port(appSettings_->get(ApplicationSettings::localSignerPort)); + msgResp->set_home_dir(appSettings_->GetHomeDir().toStdString()); + msgResp->set_auto_sign_spend_limit(appSettings_->get(ApplicationSettings::autoSignSpendLimit)); + + bs::message::Envelope envResp{ env.id, user_, env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(envResp); +} + +bool SettingsAdapter::processSignerSetKey(const SettingsMessage_SignerSetKey &request) +{ + signersProvider_->addKey(request.server_id(), request.new_key()); + return true; +} + +bool SettingsAdapter::processSignerReset() +{ + signersProvider_->setupSigner(0, true); + return true; +} + +/*void BSTerminalMainWindow::networkSettingsReceived(const NetworkSettings &settings, NetworkSettingsClient client) +{ + if (client == NetworkSettingsClient::Login) { + onLoginProceed(settings); + return; + } + + auto env = static_cast( + applicationSettings_->get(ApplicationSettings::envConfiguration)); + + applicationSettings_->set(ApplicationSettings::mdServerHost, QString::fromStdString(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::MdServer, env))); + applicationSettings_->set(ApplicationSettings::mdhsHost, QString::fromStdString(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Mdhs, env))); + applicationSettings_->set(ApplicationSettings::chatServerHost, QString::fromStdString(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Chat, env))); + applicationSettings_->set(ApplicationSettings::mdServerPort, QString::fromStdString(PubKeyLoader::serverHttpsPort())); + applicationSettings_->set(ApplicationSettings::mdhsPort, QString::fromStdString(PubKeyLoader::serverHttpsPort())); + applicationSettings_->set(ApplicationSettings::chatServerPort, QString::fromStdString(PubKeyLoader::serverHttpPort())); + + mdProvider_->SetConnectionSettings(applicationSettings_->get(ApplicationSettings::mdServerHost) + , applicationSettings_->get(ApplicationSettings::mdServerPort)); + + networkSettingsReceived_ = true; + tryInitChatView(); +}*/ + diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h new file mode 100644 index 000000000..c62172ce6 --- /dev/null +++ b/Core/SettingsAdapter.h @@ -0,0 +1,82 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef SETTINGS_ADAPTER_H +#define SETTINGS_ADAPTER_H + +#include +#include "Message/Adapter.h" +#include "TerminalMessage.h" +#include "NetworkSettingsLoader.h" + +namespace spdlog { + class logger; +} +namespace bs { + class LogManager; + class TradeSettings; +} +namespace BlockSettle { + namespace Terminal { + class SettingsMessage_ArmoryServerSet; + class SettingsMessage_SettingsRequest; + class SettingsMessage_SettingsResponse; + class SettingsMessage_SignerSetKey; + } +} +class ApplicationSettings; +class ArmoryServersProvider; +class CCFileManager; +class SignersProvider; + + +class SettingsAdapter : public bs::message::Adapter +{ +public: + SettingsAdapter(const std::shared_ptr & + , const QStringList &appArgs); + ~SettingsAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Settings"; } + + std::shared_ptr logManager() const { return logMgr_; } + +private: + bool processGetRequest(const bs::message::Envelope & + , const BlockSettle::Terminal::SettingsMessage_SettingsRequest &); + bool processPutRequest(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); + bool processArmoryServer(const BlockSettle::Terminal::SettingsMessage_ArmoryServerSet &); + bool processSignerSettings(const bs::message::Envelope &); + bool processSignerSetKey(const BlockSettle::Terminal::SettingsMessage_SignerSetKey &); + bool processSignerReset(); + +// void networkSettingsReceived(const NetworkSettings &settings, NetworkSettingsClient client); + +private: + std::shared_ptr user_, userBC_; + std::shared_ptr logMgr_; + std::shared_ptr logger_; + std::shared_ptr appSettings_; + std::shared_ptr armoryServersProvider_; + std::shared_ptr signersProvider_; + std::shared_ptr ccFileManager_; + std::unique_ptr networkSettingsLoader_; + std::shared_ptr tradeSettings_; + + bool networkSettingsReceived_{ false }; +}; + + +#endif // SETTINGS_ADAPTER_H diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp new file mode 100644 index 000000000..1e6310b70 --- /dev/null +++ b/Core/SettlementAdapter.cpp @@ -0,0 +1,28 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "SettlementAdapter.h" +#include +#include "TerminalMessage.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + + +SettlementAdapter::SettlementAdapter(const std::shared_ptr &logger) + : logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::Settlement)) +{} + +bool SettlementAdapter::process(const bs::message::Envelope &env) +{ + return true; +} diff --git a/Core/SettlementAdapter.h b/Core/SettlementAdapter.h new file mode 100644 index 000000000..aea09cefe --- /dev/null +++ b/Core/SettlementAdapter.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef SETTLEMENT_ADAPTER_H +#define SETTLEMENT_ADAPTER_H + +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} + +class SettlementAdapter : public bs::message::Adapter +{ +public: + SettlementAdapter(const std::shared_ptr &); + ~SettlementAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Settlement"; } + +private: + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +#endif // SETTLEMENT_ADAPTER_H diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp new file mode 100644 index 000000000..d9ae5fa4a --- /dev/null +++ b/Core/SignerAdapter.cpp @@ -0,0 +1,278 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "SignerAdapter.h" +#include +#include "ConnectionManager.h" +#include "TerminalMessage.h" +#include "HeadlessContainer.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; +using namespace bs::message; + + +SignerAdapter::SignerAdapter(const std::shared_ptr &logger) + : QObject(nullptr), logger_(logger) + , user_(std::make_shared(bs::message::TerminalUsers::Signer)) +{} + +bool SignerAdapter::process(const bs::message::Envelope &env) +{ + if (env.sender->value() == TerminalUsers::System) { + AdministrativeMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse administrative msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case AdministrativeMessage::kStart: + case AdministrativeMessage::kRestart: + start(); + break; + default: break; + } + } + else if (env.sender->value() == TerminalUsers::Settings) { + SettingsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettingsMessage::kSignerResponse: + return processSignerSettings(msg.signer_response()); + } + } + else if (env.receiver && (env.receiver->value() == TerminalUsers::Signer)) { + SignerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse own msg #{}", __func__, env.id); + return true; + } + if (env.request) { + return processOwnRequest(env, msg); + } + else { + switch (msg.data_case()) { + case SignerMessage::kNewKeyResponse: + return processNewKeyResponse(msg.new_key_response()); + default: break; + } + } + } + return true; +} + +void SignerAdapter::start() +{ + SettingsMessage msg; + msg.mutable_signer_request(); + bs::message::Envelope env{ 0, user_, UserTerminal::create(TerminalUsers::Settings) + , {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env + , const BlockSettle::Terminal::SignerMessage &request) +{ + return true; +} + +std::shared_ptr SignerAdapter::makeRemoteSigner( + const BlockSettle::Terminal::SettingsMessage_SignerServer &response) +{ + const auto &netType = static_cast(response.network_type()); + const auto &connMgr = std::make_shared(logger_); + const auto &cbOurNewKey = [this](const std::string &oldKey, const std::string &newKey + , const std::string &srvAddrPort + , const std::shared_ptr> &newKeyProm) + { + connFuture_ = newKeyProm; + connKey_ = newKey; + + SignerMessage msg; + auto msgReq = msg.mutable_new_key_request(); + msgReq->set_old_key(oldKey); + msgReq->set_new_key(newKey); + msgReq->set_server_id(srvAddrPort); + bs::message::Envelope env{ 0, user_, nullptr, {}, {} + , msg.SerializeAsString(), true }; + pushFill(env); + }; + + auto remoteSigner = std::make_shared(logger_ + , QString::fromStdString(response.host()), QString::fromStdString(response.port()) + , netType, connMgr, SignContainer::OpMode::Remote, false + , response.remote_keys_dir(), response.remote_keys_file(), cbOurNewKey); + + bs::network::BIP15xPeers peers; + for (const auto &clientKey : response.client_keys()) { + try { + const BinaryData signerKey = BinaryData::CreateFromHex(clientKey.value()); + peers.push_back(bs::network::BIP15xPeer(clientKey.key(), signerKey)); + } catch (const std::exception &e) { + logger_->warn("[{}] invalid signer key: {}", __func__, e.what()); + } + } + remoteSigner->updatePeerKeys(peers); + return remoteSigner; +} + +bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &response) +{ + curServerId_ = response.id(); + if (response.is_local()) { + QLatin1String localSignerHost("127.0.0.1"); + const auto &localSignerPort = QString::fromStdString(response.local_port()); + const auto &netType = static_cast(response.network_type()); + + if (SignerConnectionExists(localSignerHost, localSignerPort)) { + logger_->error("[{}] failed to bind on local port {}", __func__, response.local_port()); + SignerMessage msg; + auto msgError = msg.mutable_error(); + msgError->set_code((int)bs::error::ErrorCode::InternalError); + msgError->set_text("failed to bind local port"); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +/* BSMessageBox mbox(BSMessageBox::Type::question + , tr("Local Signer Connection") + , tr("Continue with Remote connection in Local GUI mode?") + , tr("The Terminal failed to spawn the headless signer as the program is already running. " + "Would you like to continue with remote connection in Local GUI mode?") + , this); + if (mbox.exec() == QDialog::Rejected) { + return nullptr; + } + + // Use locally started signer as remote + signersProvider_->switchToLocalFullGUI(localSignerHost, localSignerPort); + return createRemoteSigner(true);*/ + } + + const auto &connMgr = std::make_shared(logger_); + const bool startLocalSignerProcess = true; + signer_ = std::make_shared(logger_ + , QString::fromStdString(response.home_dir()), netType, localSignerPort + , connMgr, startLocalSignerProcess, "", "", response.auto_sign_spend_limit()); + connectSignals(); + return sendComponentLoading(); + } + else { + signer_ = makeRemoteSigner(response); + connectSignals(); + return sendComponentLoading(); + } + return true; +} + +void SignerAdapter::connectSignals() +{ + if (!signer_) { + return; + } + connect(signer_.get(), &SignContainer::connectionError, this, [this] + (SignContainer::ConnectionError code, QString text) { + SignerMessage msg; + auto msgErr = msg.mutable_error(); + msgErr->set_code((int)code); + msgErr->set_text(text.toStdString()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &SignContainer::disconnected, this, [this] { + SignerMessage msg; + auto msgErr = msg.mutable_error(); + msgErr->set_code((int)SignContainer::ConnectionError::SignerGoesOffline); + msgErr->set_text("disconnected"); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::AuthLeafAdded, this, [this](const std::string &leafId) { + SignerMessage msg; + msg.set_auth_leaf_added(leafId); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::walletsListUpdated, this, [this] { + SignerMessage msg; + msg.mutable_wallets_list_updated(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::walletsStorageDecrypted, this, [this] { + SignerMessage msg; + msg.mutable_wallet_storage_decrypted(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::ready, this, [this] { + SignerMessage msg; + msg.mutable_ready(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::needNewWalletPrompt, this, [this] { + SignerMessage msg; + msg.mutable_need_new_wallet_prompt(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::walletsReadyToSync, this, [this] { + SignerMessage msg; + msg.mutable_wallets_ready_to_sync(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); + + connect(signer_.get(), &WalletSignerContainer::windowVisibilityChanged, this, [this](bool visible) { + SignerMessage msg; + msg.set_window_visible_changed(visible); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }, Qt::QueuedConnection); +} + +bool SignerAdapter::sendComponentLoading() +{ + static const auto &adminUser = UserTerminal::create(TerminalUsers::System); + AdministrativeMessage msg; + msg.set_component_loading(user_->value()); + Envelope env{ 0, adminUser, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} + +bool SignerAdapter::processNewKeyResponse(bool acceptNewKey) +{ + if (!connFuture_) { + logger_->error("[{}] new key comparison wasn't requested", __func__); + return true; + } + connFuture_->setValue(acceptNewKey); + if (acceptNewKey) { + SettingsMessage msg; + auto msgReq = msg.mutable_signer_set_key(); + msgReq->set_server_id(curServerId_); + msgReq->set_new_key(connKey_); + bs::message::Envelope env{ 0, user_, UserTerminal::create(TerminalUsers::Settings) + , {}, {}, msg.SerializeAsString(), true }; + pushFill(env); + } + connFuture_.reset(); + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h new file mode 100644 index 000000000..b2b92be20 --- /dev/null +++ b/Core/SignerAdapter.h @@ -0,0 +1,66 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef SIGNER_ADAPTER_H +#define SIGNER_ADAPTER_H + +#include +#include "FutureValue.h" +#include "Message/Adapter.h" + +namespace spdlog { + class logger; +} +namespace BlockSettle { + namespace Terminal { + class SettingsMessage_SignerServer; + class SignerMessage; + } +} +class WalletSignerContainer; + +class SignerAdapter : public QObject, public bs::message::Adapter +{ + Q_OBJECT +public: + SignerAdapter(const std::shared_ptr &); + ~SignerAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Signer"; } + +private: + void start(); + + bool processOwnRequest(const bs::message::Envelope & + , const BlockSettle::Terminal::SignerMessage &); + bool processSignerSettings(const BlockSettle::Terminal::SettingsMessage_SignerServer &); + bool processNewKeyResponse(bool); + std::shared_ptr makeRemoteSigner( + const BlockSettle::Terminal::SettingsMessage_SignerServer &); + bool sendComponentLoading(); + void connectSignals(); + +private: + std::shared_ptr logger_; + std::shared_ptr user_; + std::shared_ptr signer_; + + std::shared_ptr> connFuture_; + std::string curServerId_; + std::string connKey_; +}; + + +#endif // SIGNER_ADAPTER_H diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index 31d08c3f7..15d5fd196 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * @@ -11,7 +11,10 @@ #include "TerminalMessage.h" #include "Message/Adapter.h" +#include "terminal.pb.h" + using namespace bs::message; +using namespace BlockSettle::Terminal; static const std::map kTerminalUsersMapping = { { static_cast(TerminalUsers::BROADCAST), "Broadcast" }, @@ -36,39 +39,6 @@ std::string UserTerminal::name() const ? User::name() : itAcc->second; } -/*namespace bs { - namespace message { - Envelope pbEnvelope(uint64_t id, UsersPB sender, UsersPB receiver - , const TimeStamp &posted, const TimeStamp &execAt, const std::string &message - , bool request) - { - return Envelope{ id, std::make_shared(sender) - , std::make_shared(receiver), posted, execAt, message, request }; - } - - Envelope pbEnvelope(uint64_t id, UsersPB sender, const std::shared_ptr &receiver - , const std::string &message) - { - return Envelope{ id, std::make_shared(sender), receiver - , {}, {}, message, false }; - } - - Envelope pbEnvelope(UsersPB sender, UsersPB receiver, const std::string &message - , bool request, const TimeStamp &execAt) - { - return Envelope{ 0, std::make_shared(sender), std::make_shared(receiver) - , {}, execAt, message, request }; - } - - Envelope pbEnvelope(const std::shared_ptr &sender, UsersPB receiver - , const std::string &message, bool request, const TimeStamp &execAt) - { - return Envelope{ 0, sender, std::make_shared(receiver) - , {}, execAt, message, request }; - } - } //namespace message -} //namespace bs -*/ TerminalInprocBus::TerminalInprocBus(const std::shared_ptr &logger) : logger_(logger) @@ -86,10 +56,27 @@ void TerminalInprocBus::addAdapter(const std::shared_ptr &adapter) { queue_->bindAdapter(adapter); adapter->setQueue(queue_); - auto runner = std::dynamic_pointer_cast(adapter); + const auto &runner = std::dynamic_pointer_cast(adapter); if (runner) { runnableAdapter_ = runner; } + + static const auto &adminUser = UserTerminal::create(TerminalUsers::System); + for (const auto &user : adapter->supportedReceivers()) { + AdministrativeMessage msg; + msg.set_component_created(user->value()); + bs::message::Envelope env{ 0, adminUser, {}, {}, {}, msg.SerializeAsString() }; + queue_->pushFill(env); + } +} + +void TerminalInprocBus::start() +{ + static const auto &adminUser = UserTerminal::create(TerminalUsers::System); + AdministrativeMessage msg; + msg.mutable_start(); + bs::message::Envelope env{ 0, adminUser, {}, {}, {}, msg.SerializeAsString() }; + queue_->pushFill(env); } void TerminalInprocBus::shutdown() @@ -98,11 +85,11 @@ void TerminalInprocBus::shutdown() queue_->terminate(); } -bool TerminalInprocBus::run() +bool TerminalInprocBus::run(int &argc, char **argv) { if (!runnableAdapter_) { return false; } - runnableAdapter_->run(); + runnableAdapter_->run(argc, argv); return true; } diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index 6e8044583..92a671f0f 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * @@ -19,7 +19,7 @@ namespace bs { class MainLoopRuner { public: - virtual void run() = 0; + virtual void run(int &argc, char **argv) = 0; }; namespace message { @@ -27,6 +27,7 @@ namespace bs { { Unknown = 0, BROADCAST, + System, // Used only as a sender of administrative messages, no adapter registers it Signer, API, Settings, @@ -37,6 +38,7 @@ namespace bs { MDHistory, // Charts data storage Blockchain, // General name for Armory connection Wallets, + OnChainTracker,// Auth & CC tracker combined in one adapter Settlement, Chat, UsersCount @@ -53,18 +55,17 @@ namespace bs { { return (static_cast(value()) == TerminalUsers::BROADCAST); } - }; -/* Envelope pbEnvelope(uint64_t id, TerminalUsers sender, TerminalUsers receiver - , const TimeStamp &posted, const TimeStamp &execAt, const std::string &message - , bool request = false); - Envelope pbEnvelope(uint64_t id, UsersPB sender, const std::shared_ptr &receiver - , const std::string &message); - Envelope pbEnvelope(UsersPB sender, UsersPB receiver, const std::string &message - , bool request = false, const TimeStamp &execAt = {}); - Envelope pbEnvelope(const std::shared_ptr &sender, UsersPB receiver - , const std::string &message, bool request = false, const TimeStamp &execAt = {}); -*/ + bool isFallback() const override + { + return (static_cast(value()) == TerminalUsers::System); + } + + static std::shared_ptr create(TerminalUsers value) + { + return std::make_shared(value); + } + }; class TerminalInprocBus : public Bus { @@ -75,7 +76,8 @@ namespace bs { void addAdapter(const std::shared_ptr &) override; void shutdown(); - bool run(); + bool run(int &argc, char **argv); + void start(); private: std::shared_ptr logger_; diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 34e819856..55f62efd2 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -1 +1 @@ -ADD_SUBDIRECTORY( QtWidgets ) +ADD_SUBDIRECTORY(QtWidgets) diff --git a/GUI/QtWidgets/CMakeLists.txt b/GUI/QtWidgets/CMakeLists.txt index e69de29bb..fe324cdbe 100644 --- a/GUI/QtWidgets/CMakeLists.txt +++ b/GUI/QtWidgets/CMakeLists.txt @@ -0,0 +1,72 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) + +PROJECT(${TERMINAL_GUI_QT_NAME}) + +FILE(GLOB SOURCES + *.cpp +) +FILE(GLOB HEADERS + *.h +) + +FILE(GLOB UI_FILES + ${BLOCKSETTLE_UI_INCLUDE_DIR}/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/CustomControls/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/InfoDialogs/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/ManageEncryption/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/Settings/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/Trading/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/ChatUI/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/ChatUI/OTCShieldWidgets/*.ui +) + +INCLUDE_DIRECTORIES(../../BlockSettleUILib) +INCLUDE_DIRECTORIES(BlockSettleUILib) +INCLUDE_DIRECTORIES(${COMMON_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${BOTAN_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${WALLET_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${COMMON_UI_LIB_INCLUDE_DIR} ) +INCLUDE_DIRECTORIES(${BS_COMMON_ENUMS_INCLUDE_DIR} ) +INCLUDE_DIRECTORIES(${BS_COMMUNICATION_INCLUDE_DIR} ) +INCLUDE_DIRECTORIES(${BLOCKSETTLE_UI_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${Qt5Svg_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Widgets_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Core_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${Qt5Network_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Qml_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5DBus_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Charts_INCLUDE_DIRS} ) + +qt5_wrap_ui(GENERATED_UI ${UI_FILES}) + +ADD_LIBRARY(${TERMINAL_GUI_QT_NAME} + ${SOURCES} + ${HEADERS} + ${GENERATED_UI} +) + +TARGET_INCLUDE_DIRECTORIES(${TERMINAL_GUI_QT_NAME} + PUBLIC ${BLOCK_SETTLE_ROOT}/GUI/QtWidgets + PRIVATE ${BLOCK_SETTLE_ROOT}/Core + PRIVATE ${BLOCK_SETTLE_ROOT}/common/BlocksettleNetworkingLib + PRIVATE ${BLOCK_SETTLE_ROOT}/BlockSettleUILib +) + +TARGET_LINK_LIBRARIES(${TERMINAL_GUI_QT_NAME} + ${BLOCKSETTLE_UI_LIBRARY_NAME} + ${BS_NETWORK_LIB_NAME} + ${CRYPTO_LIB_NAME} + Qt5::Network + Qt5::Core + Qt5::Widgets + Qt5::Gui + Qt5::Network + Qt5::PrintSupport + Qt5::Core + Qt5::Svg + Qt5::DBus + ${QT_LIBS} + ${OS_SPECIFIC_LIBS} + ${OPENSSL_LIBS} +) diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp new file mode 100644 index 000000000..e5b0df10d --- /dev/null +++ b/GUI/QtWidgets/MainWindow.cpp @@ -0,0 +1,1027 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "MainWindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_BSTerminalMainWindow.h" +#include "ApiAdapter.h" +#include "BSMessageBox.h" +#include "CreateTransactionDialogAdvanced.h" +#include "CreateTransactionDialogSimple.h" +#include "DialogManager.h" +#include "InfoDialogs/AboutDialog.h" +#include "InfoDialogs/StartupDialog.h" +#include "InfoDialogs/SupportDialog.h" +#include "NotificationCenter.h" +#include "Settings/ConfigDialog.h" +#include "StatusBarView.h" +#include "TabWithShortcut.h" +#include "TerminalMessage.h" +#include "UiUtils.h" + +#include "terminal.pb.h" + +using namespace bs::gui::qt; +using namespace BlockSettle::Terminal; + +MainWindow::MainWindow(const std::shared_ptr &logger + , const std::shared_ptr &queue + , const std::shared_ptr &user) + : QMainWindow(nullptr) + , ui_(new Ui::BSTerminalMainWindow()), logger_(logger) + , queue_(queue), guiUser_(user) + , settingsUser_(std::make_shared(bs::message::TerminalUsers::Settings)) +{ + UiUtils::SetupLocale(); + + ui_->setupUi(this); + + setupShortcuts(); + setupInfoWidget(); + + loginButtonText_ = tr("Login"); + + connect(ui_->actionQuit, &QAction::triggered, qApp, &QCoreApplication::quit); + + setupIcon(); + UiUtils::setupIconFont(this); + notifCenter_ = std::make_shared(logger_, nullptr, ui_.get(), sysTrayIcon_, this); + + statusBarView_ = std::make_shared(ui_->statusbar); + + setupToolbar(); + setupMenu(); + + ui_->widgetTransactions->setEnabled(false); + + initChartsView(); + +// ui_->tabWidget->setCurrentIndex(settings->get(ApplicationSettings::GUI_main_tab)); + + updateAppearance(); +// setWidgetsAuthorized(false); + + updateControlEnabledState(); + + initWidgets(); +} + +void MainWindow::setWidgetsAuthorized(bool authorized) +{ + // Update authorized state for some widgets + ui_->widgetPortfolio->setAuthorized(authorized); + ui_->widgetRFQ->setAuthorized(authorized); + ui_->widgetChart->setAuthorized(authorized); +} + +void MainWindow::onGetGeometry(const QRect &mainGeom) +{ + if (mainGeom.isEmpty()) { + return; + } + auto geom = mainGeom; + setGeometry(geom); // This call is required for screenNumber() method to work properly + +#ifdef Q_OS_WINDOWS + int screenNo = QApplication::desktop()->screenNumber(this); + if (screenNo < 0) { + screenNo = 0; + } + const auto screenGeom = QApplication::desktop()->screenGeometry(screenNo); + if (!screenGeom.contains(geom)) { + const int screenWidth = screenGeom.width() * 0.9; + const int screenHeight = screenGeom.height() * 0.9; + geom.setWidth(std::min(geom.width(), screenWidth)); + geom.setHeight(std::min(geom.height(), screenHeight)); + geom.moveCenter(screenGeom.center()); + } + const auto screen = qApp->screens()[screenNo]; + const float pixelRatio = screen->devicePixelRatio(); + if (pixelRatio > 1.0) { + const float coeff = (float)0.9999; // some coefficient that prevents oversizing of main window on HiRes display on Windows + geom.setWidth(geom.width() * coeff); + geom.setHeight(geom.height() * coeff); + } + setGeometry(geom); +#else + if (QApplication::desktop()->screenNumber(this) == -1) { + auto currentScreenRect = QApplication::desktop()->screenGeometry(QCursor::pos()); + // Do not delete 0.9 multiplier, since in some system window size is applying without system native toolbar + geom.setWidth(std::min(geom.width(), static_cast(currentScreenRect.width() * 0.9))); + geom.setHeight(std::min(geom.height(), static_cast(currentScreenRect.height() * 0.9))); + geom.moveCenter(currentScreenRect.center()); + setGeometry(geom); + } +#endif // not Windows +} + +void MainWindow::showStartupDialog(bool showLicense) +{ + StartupDialog startupDialog(showLicense, this); +// startupDialog.init(applicationSettings_); + int result = startupDialog.exec(); + + if (showLicense && (result == QDialog::Rejected)) { + hide(); + QTimer::singleShot(100, [] { qApp->exit(EXIT_FAILURE); }); + return; + } + + // Need update armory settings if case user selects TestNet + const auto &netType = startupDialog.getSelectedNetworkType(); + ApplicationSettings::EnvConfiguration envConfig = (netType == NetworkType::TestNet) ? + ApplicationSettings::EnvConfiguration::Test : ApplicationSettings::EnvConfiguration::Production; + + SettingsMessage msg; + auto msgArmory = msg.mutable_armory_server(); + msgArmory->set_network_type(static_cast(netType)); + msgArmory->set_server_name(ARMORY_BLOCKSETTLE_NAME); + + bs::message::Envelope env{ 0, guiUser_, settingsUser_, bs::message::TimeStamp{} + , bs::message::TimeStamp{}, msg.SerializeAsString(), true }; + queue_->pushFill(env); + + msg.Clear(); + auto msgReq = msg.mutable_put_request(); + auto msgPut = msgReq->add_responses(); + auto req = msgPut->mutable_request(); + req->set_source(SettingSource_Local); + req->set_index(SetIdx_Environment); + req->set_type(SettingType_Int); + msgPut->set_i(static_cast(envConfig)); + + msgPut = msgReq->add_responses(); + req = msgPut->mutable_request(); + req->set_source(SettingSource_Local); + req->set_index(SetIdx_Initialized); + req->set_type(SettingType_Bool); + msgPut->set_b(true); + + env = { 0, guiUser_, settingsUser_, bs::message::TimeStamp{} + , bs::message::TimeStamp{}, msg.SerializeAsString(), true }; + queue_->pushFill(env); +} + +bool MainWindow::event(QEvent *event) +{ + if (event->type() == QEvent::WindowActivate) { + auto tabChangedSignal = QMetaMethod::fromSignal(&QTabWidget::currentChanged); + int currentIndex = ui_->tabWidget->currentIndex(); + tabChangedSignal.invoke(ui_->tabWidget, Q_ARG(int, currentIndex)); + } + return QMainWindow::event(event); +} + +MainWindow::~MainWindow() +{ + NotificationCenter::destroyInstance(); +} + +void MainWindow::setupToolbar() +{ + action_send_ = new QAction(tr("Send Bitcoin"), this); + connect(action_send_, &QAction::triggered, this, &MainWindow::onSend); + + action_generate_address_ = new QAction(tr("Generate &Address"), this); + connect(action_generate_address_, &QAction::triggered, this, &MainWindow::onGenerateAddress); + + action_login_ = new QAction(tr("Login to BlockSettle"), this); + connect(action_login_, &QAction::triggered, this, &MainWindow::onLoggedIn); + + action_logout_ = new QAction(tr("Logout from BlockSettle"), this); + connect(action_logout_, &QAction::triggered, this, &MainWindow::onLoggedOut); + + setupTopRightWidget(); + + action_logout_->setVisible(false); + + connect(ui_->pushButtonUser, &QPushButton::clicked, this, &MainWindow::onButtonUserClicked); + + QMenu* trayMenu = new QMenu(this); + QAction* trayShowAction = trayMenu->addAction(tr("&Open Terminal")); + connect(trayShowAction, &QAction::triggered, this, &QMainWindow::show); + trayMenu->addSeparator(); + + trayMenu->addAction(action_send_); + trayMenu->addAction(action_generate_address_); + trayMenu->addAction(ui_->actionSettings); + + trayMenu->addSeparator(); + trayMenu->addAction(ui_->actionQuit); + sysTrayIcon_->setContextMenu(trayMenu); +} + +void MainWindow::setupTopRightWidget() +{ + auto toolBar = new QToolBar(this); + toolBar->setObjectName(QLatin1String("mainToolBar")); + toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + ui_->tabWidget->setCornerWidget(toolBar, Qt::TopRightCorner); + + toolBar->addAction(action_send_); + toolBar->addAction(action_generate_address_); + + for (int i = 0; i < toolBar->children().size(); ++i) { + auto *toolButton = qobject_cast(toolBar->children().at(i)); + if (toolButton && (toolButton->defaultAction() == action_send_ + || toolButton->defaultAction() == action_generate_address_)) { + toolButton->setObjectName(QLatin1String("mainToolBarActions")); + } + } + +#if defined(Q_OS_WIN) + ui_->tabWidget->setProperty("onWindows", QVariant(true)); +#elif defined(Q_OS_LINUX) + ui_->tabWidget->setProperty("onLinux", QVariant(true)); +#else + ui_->tabWidget->setProperty("onMacos", QVariant(true)); +#endif + + auto *prevStyle = ui_->tabWidget->style(); + ui_->tabWidget->setStyle(nullptr); + ui_->tabWidget->setStyle(prevStyle); +} + +void MainWindow::setupIcon() +{ + QIcon icon; + QString iconFormatString = QString::fromStdString(":/ICON_BS_%1"); + + for (const int s : {16, 24, 32}) { + icon.addFile(iconFormatString.arg(s), QSize(s, s)); + } + + setWindowIcon(icon); + + sysTrayIcon_ = std::make_shared(icon, this); + sysTrayIcon_->setToolTip(windowTitle()); + sysTrayIcon_->show(); + + connect(sysTrayIcon_.get(), &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::Context) { + // Right click, this is handled by the menu, so we don't do anything here. + return; + } + + setWindowState(windowState() & ~Qt::WindowMinimized); + show(); + raise(); + activateWindow(); + }); + + connect(qApp, &QCoreApplication::aboutToQuit, sysTrayIcon_.get(), &QSystemTrayIcon::hide); + connect(qApp, SIGNAL(lastWindowClosed()), sysTrayIcon_.get(), SLOT(hide())); +} + +void MainWindow::setupInfoWidget() +{ + const bool show = true; // applicationSettings_->get(ApplicationSettings::ShowInfoWidget); + ui_->infoWidget->setVisible(show); + +/* if (!show) { + return; + }*/ + + connect(ui_->introductionBtn, &QPushButton::clicked, this, []() { + QDesktopServices::openUrl(QUrl(QLatin1String(""))); + }); + connect(ui_->setUpBtn, &QPushButton::clicked, this, []() { + QDesktopServices::openUrl(QUrl(QLatin1String("https://youtu.be/bvGNi6sBkTo"))); + }); + connect(ui_->closeBtn, &QPushButton::clicked, this, [this]() { + ui_->infoWidget->setVisible(false); +// applicationSettings_->set(ApplicationSettings::ShowInfoWidget, false); + }); +} + +void MainWindow::updateControlEnabledState() +{ +/* if (action_send_) { + action_send_->setEnabled(!walletsMgr_->hdWallets().empty() + && armory_->isOnline() && signContainer_ && signContainer_->isReady()); + }*/ + // Do not allow login until wallets synced (we need to check if user has primary wallet or not). + // Should be OK for both local and remote signer. +// ui_->pushButtonUser->setEnabled(walletsSynched_ && loginApiKey().empty()); +} + +/*void MainWindow::initPortfolioView() +{ + portfolioModel_ = std::make_shared(walletsMgr_, assetManager_, this); + ui_->widgetPortfolio->init(applicationSettings_, mdProvider_, mdCallbacks_ + , portfolioModel_, signContainer_, armory_, utxoReservationMgr_, logMgr_->logger("ui"), walletsMgr_); +} + +void MainWindow::initWalletsView() +{ + ui_->widgetWallets->init(logMgr_->logger("ui"), walletsMgr_, signContainer_ + , applicationSettings_, connectionManager_, assetManager_, authManager_, armory_); + connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &BSTerminalMainWindow::onInitWalletDialogWasShown); +}*/ + +void MainWindow::initChartsView() +{ +/* ui_->widgetChart->init(applicationSettings_, mdProvider_, mdCallbacks_ + , connectionManager_, logMgr_->logger("ui"));*/ +} + +// Initialize widgets related to transactions. +/*void MainWindow::initTransactionsView() +{ + ui_->widgetExplorer->init(armory_, logMgr_->logger(), walletsMgr_, ccFileManager_, authManager_); + ui_->widgetTransactions->init(walletsMgr_, armory_, utxoReservationMgr_, signContainer_, applicationSettings_ + , logMgr_->logger("ui")); + ui_->widgetTransactions->setEnabled(true); + + ui_->widgetTransactions->SetTransactionsModel(transactionsModel_); + ui_->widgetPortfolio->SetTransactionsModel(transactionsModel_); +}*/ + +void MainWindow::onReactivate() +{ + show(); +} + +void MainWindow::raiseWindow() +{ + if (isMinimized()) { + showNormal(); + } else if (isHidden()) { + show(); + } + raise(); + activateWindow(); + setFocus(); +#ifdef Q_OS_WIN + auto hwnd = reinterpret_cast(winId()); + auto flags = static_cast(SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + auto currentProcessId = ::GetCurrentProcessId(); + auto currentThreadId = ::GetCurrentThreadId(); + auto windowThreadId = ::GetWindowThreadProcessId(hwnd, nullptr); + if (currentThreadId != windowThreadId) { + ::AttachThreadInput(windowThreadId, currentThreadId, TRUE); + } + ::AllowSetForegroundWindow(currentProcessId); + ::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, flags); + ::SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, flags); + ::SetForegroundWindow(hwnd); + ::SetFocus(hwnd); + ::SetActiveWindow(hwnd); + if (currentThreadId != windowThreadId) { + ::AttachThreadInput(windowThreadId, currentThreadId, FALSE); + } +#endif // Q_OS_WIN +} + +void MainWindow::updateAppearance() +{ +/* if (!applicationSettings_->get(ApplicationSettings::closeToTray) && isHidden()) { + setWindowState(windowState() & ~Qt::WindowMinimized); + show(); + raise(); + activateWindow(); + }*/ + + setWindowTitle(tr("BlockSettle Terminal")); + +// const auto bsTitle = tr("BlockSettle Terminal [%1]"); +// switch (applicationSettings_->get(ApplicationSettings::netType)) { +// case NetworkType::TestNet: +// setWindowTitle(bsTitle.arg(tr("TESTNET"))); +// break; + +// case NetworkType::RegTest: +// setWindowTitle(bsTitle.arg(tr("REGTEST"))); +// break; + +// default: +// setWindowTitle(tr("BlockSettle Terminal")); +// break; +// } +} + +void MainWindow::onGenerateAddress() +{ +/* if (walletsMgr_->hdWallets().empty()) { + createWallet(true); + return; + } + + const auto defWallet = walletsMgr_->getDefaultWallet(); + std::string selWalletId = defWallet ? defWallet->walletId() : std::string{}; + + if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { + auto wallets = ui_->widgetWallets->getSelectedWallets(); + if (!wallets.empty()) { + selWalletId = wallets[0]->walletId(); + } else { + wallets = ui_->widgetWallets->getFirstWallets(); + + if (!wallets.empty()) { + selWalletId = wallets[0]->walletId(); + } + } + } + SelectWalletDialog selectWalletDialog(walletsMgr_, selWalletId, this); + selectWalletDialog.exec(); + + if (selectWalletDialog.result() == QDialog::Rejected) { + return; + } + + NewAddressDialog newAddressDialog(selectWalletDialog.getSelectedWallet(), this); + newAddressDialog.exec(); + */ +} + +void MainWindow::onSend() +{ + std::string selectedWalletId; + + if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { + auto wallet = ui_->widgetWallets->getSelectedHdWallet(); + if (!wallet) { +// wallet = walletsMgr_->getPrimaryWallet(); + } + if (wallet) { +// selectedWalletId = wallet->walletId(); + } + } + + + std::shared_ptr dlg; + +/* if ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) + || applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { + dlg = std::make_shared(armory_, walletsMgr_, utxoReservationMgr_ + , signContainer_, true, logMgr_->logger("ui"), applicationSettings_, nullptr, bs::UtxoReservationToken{}, this ); + } else { + dlg = std::make_shared(armory_, walletsMgr_, utxoReservationMgr_, signContainer_ + , logMgr_->logger("ui"), applicationSettings_, this); + }*/ + + if (!selectedWalletId.empty()) { + dlg->SelectWallet(selectedWalletId, UiUtils::WalletsTypes::None); + } + + while(true) { + dlg->exec(); + + if ((dlg->result() != QDialog::Accepted) || !dlg->switchModeRequested()) { + break; + } + + auto nextDialog = dlg->SwithcMode(); + dlg = nextDialog; + } +} + +void MainWindow::setupMenu() +{ + // menu role erquired for OSX only, to place it to first menu item + action_login_->setMenuRole(QAction::ApplicationSpecificRole); + action_logout_->setMenuRole(QAction::ApplicationSpecificRole); + + + ui_->menuFile->insertAction(ui_->actionSettings, action_login_); + ui_->menuFile->insertAction(ui_->actionSettings, action_logout_); + + ui_->menuFile->insertSeparator(action_login_); + ui_->menuFile->insertSeparator(ui_->actionSettings); + +/* AboutDialog *aboutDlg = new AboutDialog(applicationSettings_->get(ApplicationSettings::ChangeLog_Base_Url), this); + auto aboutDlgCb = [aboutDlg] (int tab) { + return [aboutDlg, tab]() { + aboutDlg->setTab(tab); + aboutDlg->show(); + }; + };*/ + + SupportDialog *supportDlg = new SupportDialog(this); + auto supportDlgCb = [supportDlg] (int tab, QString title) { + return [supportDlg, tab, title]() { + supportDlg->setTab(tab); + supportDlg->setWindowTitle(title); + supportDlg->show(); + }; + }; + + connect(ui_->actionCreateNewWallet, &QAction::triggered, this, [ww = ui_->widgetWallets]{ ww->onNewWallet(); }); +// connect(ui_->actionAuthenticationAddresses, &QAction::triggered, this, &MainWindow::openAuthManagerDialog); + connect(ui_->actionSettings, &QAction::triggered, this, [=]() { openConfigDialog(); }); +// connect(ui_->actionAccountInformation, &QAction::triggered, this, &MainWindow::openAccountInfoDialog); +// connect(ui_->actionEnterColorCoinToken, &QAction::triggered, this, &MainWindow::openCCTokenDialog); +/* connect(ui_->actionAbout, &QAction::triggered, aboutDlgCb(0)); + connect(ui_->actionVersion, &QAction::triggered, aboutDlgCb(3));*/ + connect(ui_->actionGuides, &QAction::triggered, supportDlgCb(0, QObject::tr("Guides"))); + connect(ui_->actionVideoTutorials, &QAction::triggered, supportDlgCb(1, QObject::tr("Video Tutorials"))); + connect(ui_->actionContact, &QAction::triggered, supportDlgCb(2, QObject::tr("Support"))); + + onUserLoggedOut(); + +#ifndef Q_OS_MAC + ui_->horizontalFrame->hide(); + ui_->menubar->setCornerWidget(ui_->loginGroupWidget); +#endif + +#ifndef PRODUCTION_BUILD +/* auto envType = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration).toInt()); + bool isProd = envType == ApplicationSettings::EnvConfiguration::Production; + ui_->prodEnvSettings->setEnabled(!isProd); + ui_->testEnvSettings->setEnabled(isProd);*/ + connect(ui_->prodEnvSettings, &QPushButton::clicked, this, [this] { + promptSwitchEnv(true); + }); + connect(ui_->testEnvSettings, &QPushButton::clicked, this, [this] { + promptSwitchEnv(false); + }); +#else + ui_->prodEnvSettings->setVisible(false); + ui_->testEnvSettings->setVisible(false); +#endif // !PRODUCTION_BUILD +} + +void MainWindow::openConfigDialog(bool showInNetworkPage) +{ +/* ConfigDialog configDialog(applicationSettings_, armoryServersProvider_, signersProvider_, signContainer_, this); + connect(&configDialog, &ConfigDialog::reconnectArmory, this, &BSTerminalMainWindow::onArmoryNeedsReconnect); + + if (showInNetworkPage) { + configDialog.popupNetworkSettings(); + } + configDialog.exec();*/ + + updateAppearance(); +} + +void MainWindow::onLoggedIn() +{ +// onNetworkSettingsRequired(NetworkSettingsClient::Login); +} + +/*void MainWindow::onLoginProceed(const NetworkSettings &networkSettings) +{ + auto envType = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration).toInt()); + +#ifdef PRODUCTION_BUILD + if (networkSettings.status == Blocksettle::Communication::GetNetworkSettingsResponse_Status_LIVE_TRADING_COMING_SOON) { + BSMessageBox mbox(BSMessageBox::question, tr("Login to BlockSettle"), tr("Live trading is coming soon...") + , tr("In the meantime, you can try p2p trading in our testnet environment. Would you like to do so now?"), this); + mbox.setCancelButtonText(tr("Cancel")); + mbox.setConfirmButtonText(tr("Yes")); + int rc = mbox.exec(); + if (rc == QDialog::Accepted) { + switchToTestEnv(); + restartTerminal(); + } + return; + } +#endif + + if (walletsSynched_ && !walletsMgr_->getPrimaryWallet()) { + addDeferredDialog([this] { + CreatePrimaryWalletPrompt dlg; + int rc = dlg.exec(); + if (rc == CreatePrimaryWalletPrompt::CreateWallet) { + ui_->widgetWallets->CreateNewWallet(); + } else if (rc == CreatePrimaryWalletPrompt::ImportWallet) { + ui_->widgetWallets->ImportNewWallet(); + } + }); + return; + } + + auto bsClient = createClient(); + + auto logger = logMgr_->logger("proxy"); + LoginWindow loginDialog(logger, bsClient, applicationSettings_, this); + + int rc = loginDialog.exec(); + if (rc != QDialog::Accepted && !loginDialog.result()) { + return; + } + + bool isRegistered = (loginDialog.result()->userType == bs::network::UserType::Market + || loginDialog.result()->userType == bs::network::UserType::Trading + || loginDialog.result()->userType == bs::network::UserType::Dealing); + + if (!isRegistered && envType == ApplicationSettings::EnvConfiguration::Test) { + auto createTestAccountUrl = applicationSettings_->get(ApplicationSettings::GetAccount_UrlTest); + BSMessageBox dlg(BSMessageBox::info, tr("Create Test Account") + , tr("Create a BlockSettle test account") + , tr("

Login requires a test account - create one in minutes on test.blocksettle.com

" + "

Once you have registered, return to login in the Terminal.

" + "Create Test Account Now") + .arg(createTestAccountUrl).arg(BSMessageBox::kUrlColor), this); + dlg.setOkVisible(false); + dlg.setCancelVisible(true); + dlg.enableRichText(); + dlg.exec(); + return; + } + + if (!isRegistered && envType == ApplicationSettings::EnvConfiguration::Production) { + auto createAccountUrl = applicationSettings_->get(ApplicationSettings::GetAccount_UrlProd); + BSMessageBox dlg(BSMessageBox::info, tr("Create Account") + , tr("Create a BlockSettle account") + , tr("

Login requires an account - create one in minutes on blocksettle.com

" + "

Once you have registered, return to login in the Terminal.

" + "Create Account Now") + .arg(createAccountUrl).arg(BSMessageBox::kUrlColor), this); + dlg.setOkVisible(false); + dlg.setCancelVisible(true); + dlg.enableRichText(); + dlg.exec(); + return; + } + + networkSettingsReceived(networkSettings, NetworkSettingsClient::MarketData); + + activateClient(bsClient, *loginDialog.result(), loginDialog.email().toStdString()); +}*/ + +void MainWindow::onLoggedOut() +{ + ui_->widgetWallets->setUsername(QString()); +/* if (chatClientServicePtr_) { + chatClientServicePtr_->LogoutFromServer(); + }*/ + ui_->widgetChart->disconnect(); + +/* if (celerConnection_->IsConnected()) { + celerConnection_->CloseConnection(); + }*/ + +// mdProvider_->UnsubscribeFromMD(); + + setLoginButtonText(loginButtonText_); + + setWidgetsAuthorized(false); + +// bsClient_.reset(); +} + +void MainWindow::onUserLoggedIn() +{ + ui_->actionAccountInformation->setEnabled(true); +/* ui_->actionAuthenticationAddresses->setEnabled(celerConnection_->celerUserType() + != BaseCelerClient::CelerUserType::Market);*/ + ui_->actionOneTimePassword->setEnabled(true); + ui_->actionEnterColorCoinToken->setEnabled(true); + + ui_->actionDeposits->setEnabled(true); + ui_->actionWithdrawalRequest->setEnabled(true); + ui_->actionLinkAdditionalBankAccount->setEnabled(true); + +// ccFileManager_->ConnectToCelerClient(celerConnection_); +// ui_->widgetRFQ->onUserConnected(userType_); +// ui_->widgetRFQReply->onUserConnected(userType_); + +// const auto userId = BinaryData::CreateFromHex(celerConnection_->userId()); +/* const auto &deferredDialog = [this, userId] { + walletsMgr_->setUserId(userId); + promoteToPrimaryIfNeeded(); + }; + addDeferredDialog(deferredDialog);*/ + + setLoginButtonText(currentUserLogin_); +} + +void MainWindow::onUserLoggedOut() +{ + ui_->actionAccountInformation->setEnabled(false); + ui_->actionAuthenticationAddresses->setEnabled(false); + ui_->actionEnterColorCoinToken->setEnabled(false); + ui_->actionOneTimePassword->setEnabled(false); + + ui_->actionDeposits->setEnabled(false); + ui_->actionWithdrawalRequest->setEnabled(false); + ui_->actionLinkAdditionalBankAccount->setEnabled(false); + +/* if (walletsMgr_) { + walletsMgr_->setUserId(BinaryData{}); + } + if (authManager_) { + authManager_->OnDisconnectedFromCeler(); + }*/ + + setLoginButtonText(loginButtonText_); +} + +/*void BSTerminalMainWindow::onCelerConnected() +{ + action_login_->setVisible(false); + action_logout_->setVisible(true); + + onUserLoggedIn(); +} + +void BSTerminalMainWindow::onCelerDisconnected() +{ + action_logout_->setVisible(false); + action_login_->setEnabled(true); + action_login_->setVisible(true); + + onUserLoggedOut(); + celerConnection_->CloseConnection(); +}*/ + +void MainWindow::showRunInBackgroundMessage() +{ + sysTrayIcon_->showMessage(tr("BlockSettle is running") + , tr("BlockSettle Terminal is running in the backgroud. Click the tray icon to open the main window.") + , QIcon(QLatin1String(":/resources/login-logo.png"))); +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ +/* if (applicationSettings_->get(ApplicationSettings::closeToTray)) { + hide(); + event->ignore(); + } + else { + if (chatClientServicePtr_) { + chatClientServicePtr_->LogoutFromServer(); + } + */ + QMainWindow::closeEvent(event); + QApplication::exit(); +// } +} + +void MainWindow::changeEvent(QEvent* e) +{ + switch (e->type()) { + case QEvent::WindowStateChange: + if (this->windowState() & Qt::WindowMinimized) { +/* if (applicationSettings_->get(ApplicationSettings::minimizeToTray)) + { + QTimer::singleShot(0, this, &QMainWindow::hide); + }*/ + } + break; + default: + break; + } + QMainWindow::changeEvent(e); +} + +void MainWindow::setLoginButtonText(const QString &text) +{ + auto *button = ui_->pushButtonUser; + button->setText(text); + button->setProperty("usernameButton", QVariant(text == loginButtonText_)); + button->setProperty("usernameButtonLoggedIn", QVariant(text != loginButtonText_)); + button->style()->unpolish(button); + button->style()->polish(button); + button->update(); + +#ifndef Q_OS_MAC + ui_->menubar->adjustSize(); +#endif +} + +void MainWindow::setupShortcuts() +{ + auto overviewTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+1")), this); + overviewTabShortcut->setContext(Qt::WindowShortcut); + connect(overviewTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(0);}); + + auto tradingTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+2")), this); + tradingTabShortcut->setContext(Qt::WindowShortcut); + connect(tradingTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(1);}); + + auto dealingTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+3")), this); + dealingTabShortcut->setContext(Qt::WindowShortcut); + connect(dealingTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(2);}); + + auto walletsTabShortcutt = new QShortcut(QKeySequence(QStringLiteral("Ctrl+4")), this); + walletsTabShortcutt->setContext(Qt::WindowShortcut); + connect(walletsTabShortcutt, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(3);}); + + auto transactionsTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+5")), this); + transactionsTabShortcut->setContext(Qt::WindowShortcut); + connect(transactionsTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(4);}); + + auto explorerTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+6")), this); + explorerTabShortcut->setContext(Qt::WindowShortcut); + connect(explorerTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(5);}); + + auto chartsTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+7")), this); + chartsTabShortcut->setContext(Qt::WindowShortcut); + connect(chartsTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(6);}); + + auto chatTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+8")), this); + chatTabShortcut->setContext(Qt::WindowShortcut); + connect(chatTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(7);}); + + // TODO: Switch ChatWidget to TabWithShortcut if needed (it will ignore shortcuts right now) + + auto addShotcut = [this](const char *keySequence, TabWithShortcut::ShortcutType type) { + auto shortcut = new QShortcut(QKeySequence(QLatin1String(keySequence)), this); + shortcut->setContext(Qt::WindowShortcut); + connect(shortcut, &QShortcut::activated, [this, type]() { + auto widget = dynamic_cast(ui_->tabWidget->currentWidget()); + if (widget) { + widget->shortcutActivated(type); + } + }); + }; + + addShotcut("Alt+1", TabWithShortcut::ShortcutType::Alt_1); + addShotcut("Alt+2", TabWithShortcut::ShortcutType::Alt_2); + addShotcut("Alt+3", TabWithShortcut::ShortcutType::Alt_3); + addShotcut("Ctrl+S", TabWithShortcut::ShortcutType::Ctrl_S); + addShotcut("Ctrl+P", TabWithShortcut::ShortcutType::Ctrl_P); + addShotcut("Ctrl+Q", TabWithShortcut::ShortcutType::Ctrl_Q); + addShotcut("Alt+S", TabWithShortcut::ShortcutType::Alt_S); + addShotcut("Alt+B", TabWithShortcut::ShortcutType::Alt_B); + addShotcut("Alt+P", TabWithShortcut::ShortcutType::Alt_P); +} + +void MainWindow::onButtonUserClicked() { + if (ui_->pushButtonUser->text() == loginButtonText_) { + onLoggedIn(); + } else { + if (BSMessageBox(BSMessageBox::question, tr("User Logout"), tr("You are about to logout") + , tr("Do you want to continue?")).exec() == QDialog::Accepted) + onLoggedOut(); + } +} + +/*void MainWindow::onTabWidgetCurrentChanged(const int &index) +{ + const int chatIndex = ui_->tabWidget->indexOf(ui_->widgetChat); + const bool isChatTab = index == chatIndex; + //ui_->widgetChat->updateChat(isChatTab); +}*/ + +void MainWindow::onSignerVisibleChanged() +{ + processDeferredDialogs(); +} + +void MainWindow::initWidgets() +{ +// InitWalletsView(); +// InitPortfolioView(); + +// ui_->widgetRFQ->initWidgets(mdProvider_, mdCallbacks_, applicationSettings_); + +#if 0 + const auto aqScriptRunner = new AQScriptRunner(quoteProvider, signContainer_ + , mdCallbacks_, assetManager_, logger); + if (!applicationSettings_->get(ApplicationSettings::ExtConnName).empty() + && !applicationSettings_->get(ApplicationSettings::ExtConnHost).empty() + && !applicationSettings_->get(ApplicationSettings::ExtConnPort).empty() + /*&& !applicationSettings_->get(ApplicationSettings::ExtConnPubKey).empty()*/) { + ExtConnections extConns; +/* bs::network::BIP15xParams params; + params.ephemeralPeers = true; + params.cookie = bs::network::BIP15xCookie::ReadServer; + params.serverPublicKey = BinaryData::CreateFromHex(applicationSettings_->get( + ApplicationSettings::ExtConnPubKey)); + const auto &bip15xTransport = std::make_shared(logger, params); + bip15xTransport->setKeyCb(cbApproveExtConn_);*/ + + logger->debug("Setting up ext connection"); + auto connection = std::make_shared(logger, WsDataConnectionParams{ }); + //TODO: BIP15x will be superceded with SSL with certificate checking on both ends +// auto wsConnection = std::make_unique(logger, WsDataConnectionParams{}); +// auto connection = std::make_shared(logger, std::move(wsConnection), bip15xTransport); + if (connection->openConnection(applicationSettings_->get(ApplicationSettings::ExtConnHost) + , applicationSettings_->get(ApplicationSettings::ExtConnPort) + , aqScriptRunner->getExtConnListener().get())) { + extConns[applicationSettings_->get(ApplicationSettings::ExtConnName)] = connection; + } + aqScriptRunner->setExtConnections(extConns); + } + + autoSignQuoteProvider_ = std::make_shared(logger + , aqScriptRunner, applicationSettings_, signContainer_, celerConnection_); + + const auto rfqScriptRunner = new RFQScriptRunner(mdCallbacks_, logger, nullptr); + autoSignRFQProvider_ = std::make_shared(logger + , rfqScriptRunner, applicationSettings_, signContainer_, celerConnection_); +#endif //0 + + auto dialogManager = std::make_shared(this); + +/* ui_->widgetRFQ->init(logger, celerConnection_, authManager_, quoteProvider + , assetManager_, dialogManager, signContainer_, armory_, autoSignRFQProvider_ + , utxoReservationMgr_, orderListModel_.get()); + ui_->widgetRFQReply->init(logger, celerConnection_, authManager_ + , quoteProvider, mdCallbacks_, assetManager_, applicationSettings_, dialogManager + , signContainer_, armory_, connectionManager_, autoSignQuoteProvider_ + , utxoReservationMgr_, orderListModel_.get()); + + connect(ui_->widgetRFQ, &RFQRequestWidget::requestPrimaryWalletCreation, this + , &BSTerminalMainWindow::onCreatePrimaryWalletRequest); + connect(ui_->widgetRFQReply, &RFQReplyWidget::requestPrimaryWalletCreation, this + , &BSTerminalMainWindow::onCreatePrimaryWalletRequest);*/ + + connect(ui_->tabWidget, &QTabWidget::tabBarClicked, this, + [/*requestRFQ = QPointer(ui_->widgetRFQ) + , replyRFQ = QPointer(ui_->widgetRFQReply) + ,*/ tabWidget = QPointer(ui_->tabWidget)] (int index) + { + if (!tabWidget) { + return; + } +/* if (requestRFQ && requestRFQ == tabWidget->widget(index)) { + requestRFQ->forceCheckCondition(); + } + if (replyRFQ && replyRFQ == tabWidget->widget(index)) { + replyRFQ->forceCheckCondition(); + }*/ + }); +} + +void MainWindow::promptSwitchEnv(bool prod) +{ + BSMessageBox mbox(BSMessageBox::question + , tr("Environment selection") + , tr("Switch Environment") + , tr("Do you wish to change to the %1 environment now?").arg(prod ? tr("Production") : tr("Test")) + , this); + mbox.setConfirmButtonText(tr("Yes")); + int rc = mbox.exec(); + if (rc == QDialog::Accepted) { + if (prod) { + switchToProdEnv(); + } else { + switchToTestEnv(); + } + restartTerminal(); + } +} + +void MainWindow::switchToTestEnv() +{ +/* applicationSettings_->set(ApplicationSettings::envConfiguration + , static_cast(ApplicationSettings::EnvConfiguration::Test)); + armoryServersProvider_->setupServer(armoryServersProvider_->getIndexOfTestNetServer());*/ +} + +void MainWindow::switchToProdEnv() +{ +/* applicationSettings_->set(ApplicationSettings::envConfiguration + , static_cast(ApplicationSettings::EnvConfiguration::Production)); + armoryServersProvider_->setupServer(armoryServersProvider_->getIndexOfMainNetServer());*/ +} + +void MainWindow::restartTerminal() +{ +// lockFile_.unlock(); + QProcess::startDetached(qApp->arguments()[0], qApp->arguments()); + qApp->quit(); +} + +void MainWindow::processDeferredDialogs() +{ + if(deferredDialogRunning_) { + return; + } +/* if (signContainer_ && signContainer_->isLocal() && signContainer_->isWindowVisible()) { + return; + }*/ + + deferredDialogRunning_ = true; + while (!deferredDialogs_.empty()) { + deferredDialogs_.front()(); // run stored lambda + deferredDialogs_.pop(); + } + deferredDialogRunning_ = false; +} + +void MainWindow::addDeferredDialog(const std::function &deferredDialog) +{ + // multi thread scope, it's safe to call this function from different threads + QMetaObject::invokeMethod(this, [this, deferredDialog] { + // single thread scope (main thread), it's safe to push to deferredDialogs_ + // and check deferredDialogRunning_ variable + deferredDialogs_.push(deferredDialog); + processDeferredDialogs(); + }, Qt::QueuedConnection); +} diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h new file mode 100644 index 000000000..83101cb38 --- /dev/null +++ b/GUI/QtWidgets/MainWindow.h @@ -0,0 +1,160 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef __GUI_QT_MAIN_WINDOW_H__ +#define __GUI_QT_MAIN_WINDOW_H__ + +#include +#include +#include + +namespace spdlog { + class logger; +} +namespace Ui { + class BSTerminalMainWindow; +} +namespace bs { + namespace message { + class QueueInterface; + class User; + } +} + +class AboutDialog; +class AuthAddressDialog; +class NotificationCenter; +class QSystemTrayIcon; +class StatusBarView; + +namespace bs { + namespace gui { + namespace qt { + class MainWindow : public QMainWindow + { + Q_OBJECT + + public: + MainWindow(const std::shared_ptr & + , const std::shared_ptr & + , const std::shared_ptr < bs::message::User> &); + ~MainWindow() override; + + void onGetGeometry(const QRect &); + void showStartupDialog(bool showLic); + + public slots: + void onReactivate(); + void raiseWindow(); + +/* private: + enum class AutoLoginState + { + Idle, + Connecting, + Connected, + };*/ + + private slots: + void onSend(); + void onGenerateAddress(); + + // void openAuthManagerDialog(); + void openConfigDialog(bool showInNetworkPage = false); + // void openAccountInfoDialog(); + // void openCCTokenDialog(); + + void onLoggedIn(); + // void onLoginProceed(const NetworkSettings &networkSettings); + void onLoggedOut(); + void onButtonUserClicked(); + +// void onCelerConnected(); +// void onCelerDisconnected(); +// void onCelerConnectionError(int errorCode); + void showRunInBackgroundMessage(); + +// void onBsConnectionDisconnected(); +// void onBsConnectionFailed(); + + void onSignerVisibleChanged(); + + protected: + bool event(QEvent *) override; + void closeEvent(QCloseEvent *) override; + void changeEvent(QEvent *) override; + + private: + void onUserLoggedIn(); + void onUserLoggedOut(); + +// void onAccountTypeChanged(bs::network::UserType userType, bool enabled); + + void setLoginButtonText(const QString& text); + + void setupShortcuts(); + void setupInfoWidget(); + void setupIcon(); + void setupToolbar(); + void setupMenu(); + void setupTopRightWidget(); + + void updateAppearance(); + void updateControlEnabledState(); + void setWidgetsAuthorized(bool); + + void initWidgets(); + void initChartsView(); + + void promptSwitchEnv(bool prod); + void switchToTestEnv(); + void switchToProdEnv(); + + void restartTerminal(); + void addDeferredDialog(const std::function &); + void processDeferredDialogs(); + + private: + std::unique_ptr ui_; + std::shared_ptr logger_; + std::shared_ptr queue_; + std::shared_ptr guiUser_, settingsUser_; + + QAction *action_send_{ nullptr }; + QAction *action_generate_address_{ nullptr }; + QAction *action_login_{ nullptr }; + QAction *action_logout_{ nullptr }; + + std::shared_ptr statusBarView_; + std::shared_ptr sysTrayIcon_; + std::shared_ptr notifCenter_; + // std::shared_ptr transactionsModel_; + // std::shared_ptr portfolioModel_; + // std::shared_ptr orderListModel_; + std::shared_ptr authAddrDlg_; + + // std::shared_ptr walletsWizard_; + + QString currentUserLogin_; + QString loginButtonText_; + QTimer * loginTimer_{}; + + bool initialWalletCreateDialogShown_ = false; + bool deferCCsync_ = false; + + std::queue> deferredDialogs_; + bool deferredDialogRunning_ = false; + // bs::network::UserType userType_{}; + }; + } + } +} // namespaces + +#endif // __GUI_QT_MAIN_WINDOW_H__ diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp new file mode 100644 index 000000000..9818225a1 --- /dev/null +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -0,0 +1,350 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "QtGuiAdapter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "AppNap.h" +#include "BSMessageBox.h" +#include "BSTerminalSplashScreen.h" +#include "MainWindow.h" + +#include "terminal.pb.h" + +using namespace BlockSettle::Terminal; + +Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult); +Q_DECLARE_METATYPE(std::string) + +#if defined (Q_OS_MAC) +class MacOsApp : public QApplication +{ + Q_OBJECT +public: + MacOsApp(int &argc, char **argv) : QApplication(argc, argv) {} + ~MacOsApp() override = default; + +signals: + void reactivateTerminal(); + +protected: + bool event(QEvent* ev) override + { + if (ev->type() == QEvent::ApplicationStateChange) { + auto appStateEvent = static_cast(ev); + + if (appStateEvent->applicationState() == Qt::ApplicationActive) { + if (activationRequired_) { + emit reactivateTerminal(); + } else { + activationRequired_ = true; + } + } else { + activationRequired_ = false; + } + } + + return QApplication::event(ev); + } + +private: + bool activationRequired_ = false; +}; +#endif // Q_OS_MAC + + +static void checkStyleSheet(QApplication &app) +{ + QLatin1String styleSheetFileName = QLatin1String("stylesheet.css"); + + QFileInfo info = QFileInfo(QLatin1String(styleSheetFileName)); + + static QDateTime lastTimestamp = info.lastModified(); + + if (lastTimestamp == info.lastModified()) { + return; + } + + lastTimestamp = info.lastModified(); + + QFile stylesheetFile(styleSheetFileName); + + bool result = stylesheetFile.open(QFile::ReadOnly); + assert(result); + + app.setStyleSheet(QString::fromLatin1(stylesheetFile.readAll())); +} + +static QScreen *getDisplay(QPoint position) +{ + for (auto currentScreen : QGuiApplication::screens()) { + if (currentScreen->availableGeometry().contains(position, false)) { + return currentScreen; + } + } + + return QGuiApplication::primaryScreen(); +} + + +QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) + : QObject(nullptr), logger_(logger) + , userSettings_(std::make_shared(bs::message::TerminalUsers::Settings)) +{} + +QtGuiAdapter::~QtGuiAdapter() +{} + +void QtGuiAdapter::run(int &argc, char **argv) +{ + logger_->debug("[QtGuiAdapter::run]"); + + Q_INIT_RESOURCE(armory); + Q_INIT_RESOURCE(tradinghelp); + Q_INIT_RESOURCE(wallethelp); + + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + +#if defined (Q_OS_MAC) + MacOsApp app(argc, argv); +#else + QApplication app(argc, argv); +#endif + + QApplication::setQuitOnLastWindowClosed(false); + + const QFileInfo localStyleSheetFile(QLatin1String("stylesheet.css")); + QFile stylesheetFile(localStyleSheetFile.exists() + ? localStyleSheetFile.fileName() : QLatin1String(":/STYLESHEET")); + + if (stylesheetFile.open(QFile::ReadOnly)) { + app.setStyleSheet(QString::fromLatin1(stylesheetFile.readAll())); + QPalette p = QApplication::palette(); + p.setColor(QPalette::Disabled, QPalette::Light, QColor(10, 22, 25)); + QApplication::setPalette(p); + } + +#ifndef NDEBUG + // Start monitoring to update stylesheet live when file is changed on the disk + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, &app, [&app] { + checkStyleSheet(app); + }); + timer.start(100); +#endif + + QDirIterator it(QLatin1String(":/resources/Raleway/")); + while (it.hasNext()) { + QFontDatabase::addApplicationFont(it.next()); + } + + QString location = QStandardPaths::writableLocation(QStandardPaths::TempLocation); +#ifndef NDEBUG + QString userName = QDir::home().dirName(); + QString lockFilePath = location + QLatin1String("/blocksettle-") + userName + QLatin1String(".lock"); +#else + QString lockFilePath = location + QLatin1String("/blocksettle.lock"); +#endif + QLockFile lockFile(lockFilePath); + lockFile.setStaleLockTime(0); + + if (!lockFile.tryLock()) { + BSMessageBox box(BSMessageBox::info, app.tr("BlockSettle Terminal") + , app.tr("BlockSettle Terminal is already running") + , app.tr("Stop the other BlockSettle Terminal instance. If no other " \ + "instance is running, delete the lockfile (%1).").arg(lockFilePath)); + box.exec(); + return; + } + + qRegisterMetaType(); + qRegisterMetaType>(); + qRegisterMetaType(); + + QString logoIcon; + logoIcon = QLatin1String(":/SPLASH_LOGO"); + + QPixmap splashLogo(logoIcon); + const int splashScreenWidth = 400; + splashScreen_ = new BSTerminalSplashScreen(splashLogo.scaledToWidth(splashScreenWidth + , Qt::SmoothTransformation)); + splashScreen_->show(); + + mainWindow_ = new bs::gui::qt::MainWindow(logger_, queue_, user_); + updateSplashProgress(); + + requestInitialSettings(); + logger_->debug("[QtGuiAdapter::run] initial setup done"); + +#if defined (Q_OS_MAC) + MacOsApp *macApp = (MacOsApp*)(app); + QObject::connect(macApp, &MacOsApp::reactivateTerminal, mainWindow + , &bs::gui::qt::MainWindow::onReactivate); +#endif + bs::disableAppNap(); + + if (app.exec() != 0) { + throw std::runtime_error("application execution failed"); + } +} + +bool QtGuiAdapter::process(const bs::message::Envelope &env) +{ + if (std::dynamic_pointer_cast(env.sender)) { + switch (env.sender->value()) { + case bs::message::TerminalUsers::System: + return processAdminMessage(env); + case bs::message::TerminalUsers::Settings: + return processSettings(env); + } + } + return true; +} + +bool QtGuiAdapter::processSettings(const bs::message::Envelope &env) +{ + SettingsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettingsMessage::kGetResponse: + return processSettingsGetResponse(msg.get_response()); + default: break; + } + return true; +} + +bool QtGuiAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResponse &response) +{ + for (const auto &setting : response.responses()) { + switch (setting.request().index()) { + case SetIdx_GUI_MainGeom: { + QRect mainGeometry(setting.rect().left(), setting.rect().top() + , setting.rect().width(), setting.rect().height()); + if (splashScreen_) { + const auto ¤tDisplay = getDisplay(mainGeometry.center()); + auto splashGeometry = splashScreen_->geometry(); + splashGeometry.moveCenter(currentDisplay->geometry().center()); + QMetaObject::invokeMethod(splashScreen_, [ss=splashScreen_, splashGeometry] { + ss->setGeometry(splashGeometry); + }); + } + QMetaObject::invokeMethod(splashScreen_, [mw=mainWindow_, mainGeometry] { + mw->onGetGeometry(mainGeometry); + }); + } + break; + + case SetIdx_Initialized: + if (!setting.b()) { +#ifdef _WIN32 + // Read registry value in case it was set with installer. Could be used only on Windows for now. + QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Software\\blocksettle\\blocksettle"), QSettings::NativeFormat); + bool showLicense = !settings.value(QLatin1String("license_accepted"), false).toBool(); +#else + bool showLicense = true; +#endif // _WIN32 + QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, showLicense] { + mw->showStartupDialog(showLicense); + }); + } + break; + default: break; + } + } + return true; +} + +bool QtGuiAdapter::processAdminMessage(const bs::message::Envelope &env) +{ + AdministrativeMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse admin msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case AdministrativeMessage::kComponentCreated: + createdComponents_.insert(msg.component_created()); + break; + case AdministrativeMessage::kComponentLoading: + loadingComponents_.insert(msg.component_loading()); + break; + default: break; + } + updateSplashProgress(); + return true; +} + +void QtGuiAdapter::updateSplashProgress() +{ + if (!splashScreen_ || createdComponents_.empty()) { + return; + } + int percent = 100 * loadingComponents_.size() / createdComponents_.size(); + QMetaObject::invokeMethod(splashScreen_, [this, percent] { + splashScreen_->SetProgress(percent); + }); + if (percent >= 100) { + splashProgressCompleted(); + } +} + +void QtGuiAdapter::splashProgressCompleted() +{ + if (!splashScreen_) { + return; + } + QMetaObject::invokeMethod(splashScreen_, [this] { + mainWindow_->show(); + loadingComponents_.clear(); + QTimer::singleShot(500, [this] { + if (splashScreen_) { + splashScreen_->hide(); + splashScreen_->deleteLater(); + splashScreen_ = nullptr; + } + }); + }); +} + +void QtGuiAdapter::requestInitialSettings() +{ + SettingsMessage msg; + auto msgReq = msg.mutable_get_request(); + auto setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_GUI_MainGeom); + setReq->set_type(SettingType_Rect); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_Initialized); + setReq->set_type(SettingType_Bool); + + bs::message::Envelope env{ 0, user_, userSettings_, bs::message::TimeStamp{} + , bs::message::TimeStamp{}, msg.SerializeAsString(), true }; + pushFill(env); +} + +#include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h new file mode 100644 index 000000000..aaec9b359 --- /dev/null +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -0,0 +1,73 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef QT_GUI_ADAPTER_H +#define QT_GUI_ADAPTER_H + +#include +#include +#include "ApiAdapter.h" +#include "ThreadSafeClasses.h" + +namespace bs { + namespace gui { + namespace qt { + class MainWindow; + } + } +} +namespace BlockSettle { + namespace Terminal { + class SettingsMessage_SettingsResponse; + } +} +class BSTerminalSplashScreen; +class GuiThread; + + +using GuiQueue = ArmoryThreading::TimedQueue; + +class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRuner +{ + Q_OBJECT + friend class GuiThread; +public: + QtGuiAdapter(const std::shared_ptr &); + ~QtGuiAdapter() override; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "QtGUI"; } + + void run(int &argc, char **argv) override; + +private: + bool processSettings(const bs::message::Envelope &env); + bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); + bool processAdminMessage(const bs::message::Envelope &env); + void requestInitialSettings(); + void updateSplashProgress(); + void splashProgressCompleted(); + +private: + std::shared_ptr logger_; + std::shared_ptr userSettings_; + bs::gui::qt::MainWindow * mainWindow_{ nullptr }; + BSTerminalSplashScreen * splashScreen_{ nullptr }; + + std::set createdComponents_; + std::set loadingComponents_; +}; + + +#endif // QT_GUI_ADAPTER_H diff --git a/Scripts/RFQBot_LMAX.qml b/Scripts/RFQBot_LMAX.qml index 815202f98..b3e3c8ef9 100644 --- a/Scripts/RFQBot_LMAX.qml +++ b/Scripts/RFQBot_LMAX.qml @@ -35,6 +35,7 @@ BSQuoteReqReply { property var ccInstruments: [ 'LOL/XBT', + 'ARM/XBT', 'POC/XBT' ] property var xbtInstruments: [ @@ -44,6 +45,17 @@ BSQuoteReqReply { 'XBT/PLN' ] + onStarted: { // serve only fixed CC quotes here + if (!isCC() || (direction() !== 1)) { + return + } + if (security == "LOL/XBT") { + sendPrice(0.0001) + } + else if (security == "ARM/XBT") { + sendPrice(0.001) + } + } onBestPriceChanged: { if (!hedgeAllowed) return @@ -110,10 +122,11 @@ BSQuoteReqReply { var price = 0.0 if (direction() === 1) { - price = priceObj.ask * (1.0 + 3 * prcIncrement) + var mult = isXBT() ? 1.5 : 1.0 + price = priceObj.ask * (1.0 + mult * prcIncrement) } else { - price = priceObj.bid * (1.0 - 3 * prcIncrement) + price = priceObj.bid * (1.0 - prcIncrement) } if (bestPrice) { @@ -221,6 +234,16 @@ BSQuoteReqReply { return amount } + function isXBT() + { + return (xbtInstruments.indexOf(security) != -1) + } + + function isCC() + { + return (ccInstruments.indexOf(security) != -1) + } + function sendHedgeOrder(price) { if (!price) { @@ -228,8 +251,8 @@ BSQuoteReqReply { return } - if (ccInstruments.indexOf(security) != -1) return - if (xbtInstruments.indexOf(security) != -1) { + if (isCC()) return + if (isXBT()) { if (quoteReq.quantity > 1.0) { log('XBT amount exceeds limit: ' + quoteReq.quantity) return diff --git a/common b/common index e7f498533..24c39f71c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e7f49853331fd4daefc9d0dda9040ea66422fdb7 +Subproject commit 24c39f71ce82494c215203f96749642e69da4802 From 0e329e5b5fc2ca526deabc0036020f4450514b4a Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Tue, 25 Aug 2020 11:31:39 +0300 Subject: [PATCH 003/146] WIP 3 --- BlockSettleApp/main.cpp | 16 ++- BlockSettleSigner/HeadlessApp.cpp | 1 + BlockSettleSigner/SignerAdapterListener.cpp | 5 +- BlockSettleSigner/SignerAdapterListener.h | 12 +- Core/BsServerAdapter.cpp | 117 -------------------- Core/BsServerAdapter.h | 8 -- Core/SettingsAdapter.cpp | 14 +-- Core/SignerAdapter.cpp | 56 +++++++++- Core/SignerAdapter.h | 13 ++- Core/TerminalMessage.h | 1 + common | 2 +- 11 files changed, 99 insertions(+), 146 deletions(-) diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 4b59c7743..76ee73e71 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -30,7 +30,9 @@ #include "BSTerminalSplashScreen.h" #include "EncryptionUtils.h" +#include "Adapters/AuthEidAdapter.h" #include "Adapters/BlockchainAdapter.h" +#include "Adapters/OnChainTrackerAdapter.h" #include "Adapters/WalletsAdapter.h" #include "ApiAdapter.h" #include "ChatAdapter.h" @@ -322,11 +324,19 @@ int main(int argc, char** argv) apiAdapter->add(guiAdapter); inprocBus.addAdapter(apiAdapter); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); + const auto &signAdapter = std::make_shared(logMgr->logger()); + inprocBus.addAdapter(signAdapter); + + inprocBus.addAdapter(std::make_shared(logMgr->logger() + , bs::message::UserTerminal::create(bs::message::TerminalUsers::AuthEid))); + inprocBus.addAdapter(std::make_shared(logMgr->logger() + , bs::message::UserTerminal::create(bs::message::TerminalUsers::OnChainTracker))); + inprocBus.addAdapter(std::make_shared(logMgr->logger() + , bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets) + , signAdapter->createClient() + , bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain))); inprocBus.addAdapter(std::make_shared(logMgr->logger() , bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain))); - inprocBus.addAdapter(std::make_shared(logMgr->logger() - , bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets))); inprocBus.addAdapter(std::make_shared(logMgr->logger())); inprocBus.addAdapter(std::make_shared(logMgr->logger())); diff --git a/BlockSettleSigner/HeadlessApp.cpp b/BlockSettleSigner/HeadlessApp.cpp index 2c1a956ca..d15472d19 100644 --- a/BlockSettleSigner/HeadlessApp.cpp +++ b/BlockSettleSigner/HeadlessApp.cpp @@ -36,6 +36,7 @@ #include "bs_signer.pb.h" using namespace bs::error; +using namespace Blocksettle::Communication; HeadlessAppObj::HeadlessAppObj(const std::shared_ptr &logger , const std::shared_ptr ¶ms, const std::shared_ptr &queue) diff --git a/BlockSettleSigner/SignerAdapterListener.cpp b/BlockSettleSigner/SignerAdapterListener.cpp index 56af09bde..c85b4c4fb 100644 --- a/BlockSettleSigner/SignerAdapterListener.cpp +++ b/BlockSettleSigner/SignerAdapterListener.cpp @@ -26,6 +26,9 @@ #include "StringUtils.h" #include "SystemFileUtils.h" +#include "bs_signer.pb.h" +#include "headless.pb.h" + using namespace Blocksettle::Communication; class HeadlessContainerCallbacksImpl : public HeadlessContainerCallbacks @@ -298,7 +301,7 @@ void SignerAdapterListener::sendStatusUpdate() callbacks_->ccNamesReceived(!walletsMgr_->ccLeaves().empty()); } -void SignerAdapterListener::sendControlPasswordStatusUpdate(signer::ControlPasswordStatus status) +void SignerAdapterListener::sendControlPasswordStatusUpdate(const signer::ControlPasswordStatus &status) { signer::UpdateControlPasswordStatus evt; evt.set_controlpasswordstatus(status); diff --git a/BlockSettleSigner/SignerAdapterListener.h b/BlockSettleSigner/SignerAdapterListener.h index f13392dd3..be07f83a2 100644 --- a/BlockSettleSigner/SignerAdapterListener.h +++ b/BlockSettleSigner/SignerAdapterListener.h @@ -17,12 +17,18 @@ #include "ServerConnectionListener.h" #include "BSErrorCode.h" -#include "bs_signer.pb.h" -#include "headless.pb.h" namespace spdlog { class logger; } +namespace Blocksettle { + namespace Communication { + namespace signer { + enum ControlPasswordStatus : int; + enum PacketType : int; + } + } +} namespace bs { namespace core { namespace hd { @@ -54,7 +60,7 @@ class SignerAdapterListener : public ServerConnectionListener // Sent to GUI status update message void sendStatusUpdate(); - void sendControlPasswordStatusUpdate(signer::ControlPasswordStatus status); + void sendControlPasswordStatusUpdate(const Blocksettle::Communication::signer::ControlPasswordStatus &); void resetConnection(); diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 63ed2fa9f..5006a589a 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -28,7 +28,6 @@ using namespace bs::message; BsServerAdapter::BsServerAdapter(const std::shared_ptr &logger) : logger_(logger) , user_(std::make_shared(bs::message::TerminalUsers::BsServer)) - , pubPort_(PubKeyLoader::serverHttpPort()) { connMgr_ = std::make_shared(logger_); connMgr_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); @@ -47,8 +46,6 @@ bool BsServerAdapter::process(const bs::message::Envelope &env) start(); break; case AdministrativeMessage::kRestart: - pubHost_.clear(); - hasNetworkSettings_ = false; start(); break; } @@ -90,8 +87,6 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) return true; } switch (msg.data_case()) { - case BsServerMessage::kNetworkSettingsRequest: - return processNetworkSettings(env); case BsServerMessage::kPubNewKeyResponse: return processPuBKeyResponse(msg.pub_new_key_response()); default: break; @@ -105,7 +100,6 @@ bool BsServerAdapter::processLocalSettings(const SettingsMessage_SettingsRespons switch (static_cast(setting.request().index())) { case ApplicationSettings::envConfiguration: { const auto &env = static_cast(setting.i()); - pubHost_ = PubKeyLoader::serverHostName(PubKeyLoader::KeyType::PublicBridge, env); AdministrativeMessage admMsg; admMsg.set_component_loading(user_->value()); @@ -121,117 +115,6 @@ bool BsServerAdapter::processLocalSettings(const SettingsMessage_SettingsRespons return true; } -bool BsServerAdapter::processNetworkSettings(const Envelope &env) -{ - if (pubHost_.empty()) { - logger_->warn("[{}] no PuB host available", __func__); - return false; - } - if (hasNetworkSettings_) { - sendNetworkSettings(env); - return true; - } - if (cmdNetworkSettings_) { - return false; // pool until settings request is complete - } - - const auto &cbApprove = [this](const std::string& oldKey - , const std::string& newKeyHex, const std::string& srvAddrPort - , const std::shared_ptr> &newKeyProm) - { - futPuBkey_ = newKeyProm; - BsServerMessage msg; - auto msgReq = msg.mutable_pub_new_key_request(); - msgReq->set_old_key(oldKey); - msgReq->set_new_key(newKeyHex); - msgReq->set_server_id(srvAddrPort); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); - }; - bs::network::BIP15xParams params; - params.ephemeralPeers = true; - const auto &bip15xTransport = std::make_shared(logger_, params); - bip15xTransport->setKeyCb(cbApprove); - auto wsConn = std::make_unique(logger_, WsDataConnectionParams{}); - auto connection = std::make_shared(logger_, std::move(wsConn), bip15xTransport); - - Blocksettle::Communication::RequestPacket reqPkt; - reqPkt.set_requesttype(Blocksettle::Communication::GetNetworkSettingsType); - reqPkt.set_requestdata(""); - - cmdNetworkSettings_ = std::make_shared("network_settings", connection, logger_); - cmdNetworkSettings_->SetReplyCallback([this, env](const std::string &data) -> bool - { - if (data.empty()) { - logger_->error("[BsServerAdapter::processNetworkSettings] empty reply from server"); - return false; - } - - cmdNetworkSettings_->resetConnection(); - - Blocksettle::Communication::GetNetworkSettingsResponse response; - if (!response.ParseFromString(data)) { - logger_->error("[BsServerAdapter::processNetworkSettings] invalid " - "reply from BlockSettle server"); - return false; - } - if (!response.has_marketdata()) { - logger_->error("[BsServerAdapter::processNetworkSettings] missing MD" - " connection settings"); - return false; - } - if (!response.has_mdhs()) { - logger_->error("[BsServerAdapter::processNetworkSettings] missing MDHS" - " connection settings"); - return false; - } - if (!response.has_chat()) { - logger_->error("[BsServerAdapter::processNetworkSettings] missing Chat" - " connection settings"); - return false; - } - - networkSettings_.marketData = { response.marketdata().host(), int(response.marketdata().port()) }; - networkSettings_.mdhs = { response.mdhs().host(), int(response.mdhs().port()) }; - networkSettings_.chat = { response.chat().host(), int(response.chat().port()) }; - networkSettings_.proxy = { response.proxy().host(), int(response.proxy().port()) }; - networkSettings_.status = response.status(); - networkSettings_.statusMsg = response.statusmsg(); - networkSettings_.isSet = true; - - cmdNetworkSettings_.reset(); - hasNetworkSettings_ = true; - sendNetworkSettings(env); - return true; - }); -} - -void BsServerAdapter::sendNetworkSettings(const Envelope &env) -{ - if (!hasNetworkSettings_) { - logger_->error("[{}] no network settings available atm", __func__); - } - BsServerMessage msg; - auto msgResp = msg.mutable_network_settings_response(); - auto hostPort = msgResp->mutable_mkt_data(); - hostPort->set_host(networkSettings_.marketData.host); - hostPort->set_port(std::to_string(networkSettings_.marketData.port)); - hostPort = msgResp->mutable_mdhs(); - hostPort->set_host(networkSettings_.mdhs.host); - hostPort->set_port(std::to_string(networkSettings_.mdhs.port)); - hostPort = msgResp->mutable_chat(); - hostPort->set_host(networkSettings_.chat.host); - hostPort->set_port(std::to_string(networkSettings_.chat.port)); - hostPort = msgResp->mutable_proxy(); - hostPort->set_host(networkSettings_.proxy.host); - hostPort->set_port(std::to_string(networkSettings_.proxy.port)); - msgResp->set_status(networkSettings_.status); - msgResp->set_status_message(networkSettings_.statusMsg); - - Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; - pushFill(envResp); -} - bool BsServerAdapter::processPuBKeyResponse(bool allowed) { if (!futPuBkey_) { diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index ae020a3d1..be30b67b7 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -13,7 +13,6 @@ #include "Message/Adapter.h" #include "FutureValue.h" -#include "NetworkSettingsLoader.h" namespace spdlog { class logger; @@ -43,8 +42,6 @@ class BsServerAdapter : public bs::message::Adapter void start(); bool processOwnRequest(const bs::message::Envelope &); bool processLocalSettings(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); - bool processNetworkSettings(const bs::message::Envelope &); - void sendNetworkSettings(const bs::message::Envelope &); bool processPuBKeyResponse(bool); private: @@ -52,11 +49,6 @@ class BsServerAdapter : public bs::message::Adapter std::shared_ptr user_; std::shared_ptr connMgr_; - std::string pubHost_; - const std::string pubPort_; - std::atomic_bool hasNetworkSettings_{ false }; - NetworkSettings networkSettings_; - std::shared_ptr cmdNetworkSettings_; std::shared_ptr> futPuBkey_; }; diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 98d09ff58..a43772cad 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -154,14 +154,8 @@ bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env } nbFetched++; } - else if (req.source() == SettingSource_Remote) { - BsServerMessage msg; - msg.mutable_network_settings_request(); - Envelope envReq{ 0, user_, UserTerminal::create(TerminalUsers::BsServer) - , {}, {}, msg.SerializeAsString(), true }; - pushFill(envReq); - remoteSetReqs_[envReq.id] = env; - break; + else { + logger_->error("[{}] unknown settings source: {}", __func__, req.source()); } } if (nbFetched > 0) { @@ -226,8 +220,8 @@ bool SettingsAdapter::processPutRequest(const SettingsMessage_SettingsResponse & } nbUpdates++; } - else if (req.request().source() == SettingSource_Remote) { - logger_->warn("[{}] remote settings ({}) are read-only", __func__ + else { + logger_->warn("[{}] unknown source for setting ({})", __func__ , req.request().index()); continue; } diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index d9ae5fa4a..ac1983c86 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -13,9 +13,12 @@ #include "ConnectionManager.h" #include "TerminalMessage.h" #include "HeadlessContainer.h" +#include "Adapters/SignerClient.h" +#include "common.pb.h" #include "terminal.pb.h" +using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; @@ -25,6 +28,13 @@ SignerAdapter::SignerAdapter(const std::shared_ptr &logger) , user_(std::make_shared(bs::message::TerminalUsers::Signer)) {} +std::unique_ptr SignerAdapter::createClient() const +{ + auto client = std::make_unique(logger_, user_); + client->setQueue(queue_); + return client; +} + bool SignerAdapter::process(const bs::message::Envelope &env) { if (env.sender->value() == TerminalUsers::System) { @@ -82,8 +92,12 @@ void SignerAdapter::start() } bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env - , const BlockSettle::Terminal::SignerMessage &request) + , const SignerMessage &request) { + switch (request.data_case()) { + case SignerMessage::kStartWalletsSync: + return processStartWalletSync(env); + } return true; } @@ -164,11 +178,13 @@ bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &re , QString::fromStdString(response.home_dir()), netType, localSignerPort , connMgr, startLocalSignerProcess, "", "", response.auto_sign_spend_limit()); connectSignals(); + signer_->Start(); return sendComponentLoading(); } else { signer_ = makeRemoteSigner(response); connectSignals(); + signer_->Start(); return sendComponentLoading(); } return true; @@ -276,3 +292,41 @@ bool SignerAdapter::processNewKeyResponse(bool acceptNewKey) connFuture_.reset(); return true; } + +bool SignerAdapter::processStartWalletSync(const bs::message::Envelope &env) +{ + requests_[env.id] = env.sender; + const auto &cbWallets = [this, msgId=env.id] + (const std::vector &wi) + { + const auto &itReq = requests_.find(msgId); + if (itReq == requests_.end()) { + return; + } + SignerMessage msg; + auto msgResp = msg.mutable_wallets_info(); + for (const auto &entry : wi) { + auto wallet = msgResp->add_wallets(); + wallet->set_format((int)entry.format); + wallet->set_id(entry.id); + wallet->set_name(entry.name); + wallet->set_description(entry.description); + wallet->set_network_type((int)entry.netType); + wallet->set_watch_only(entry.watchOnly); + for (const auto &encType : entry.encryptionTypes) { + wallet->add_encryption_types((int)encType); + } + for (const auto &encKey : entry.encryptionKeys) { + wallet->add_encryption_keys(encKey.toBinStr()); + } + auto keyRank = wallet->mutable_encryption_rank(); + keyRank->set_m(entry.encryptionRank.m); + keyRank->set_n(entry.encryptionRank.n); + } + Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + pushFill(envResp); + requests_.erase(itReq); + }; + signer_->syncWalletInfo(cbWallets); + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index b2b92be20..a5c3d7906 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -19,11 +19,14 @@ namespace spdlog { class logger; } namespace BlockSettle { + namespace Common { + class SignerMessage; + } namespace Terminal { class SettingsMessage_SignerServer; - class SignerMessage; } } +class SignerClient; class WalletSignerContainer; class SignerAdapter : public QObject, public bs::message::Adapter @@ -40,11 +43,13 @@ class SignerAdapter : public QObject, public bs::message::Adapter } std::string name() const override { return "Signer"; } + std::unique_ptr createClient() const; + private: void start(); bool processOwnRequest(const bs::message::Envelope & - , const BlockSettle::Terminal::SignerMessage &); + , const BlockSettle::Common::SignerMessage &); bool processSignerSettings(const BlockSettle::Terminal::SettingsMessage_SignerServer &); bool processNewKeyResponse(bool); std::shared_ptr makeRemoteSigner( @@ -52,6 +57,8 @@ class SignerAdapter : public QObject, public bs::message::Adapter bool sendComponentLoading(); void connectSignals(); + bool processStartWalletSync(const bs::message::Envelope &); + private: std::shared_ptr logger_; std::shared_ptr user_; @@ -60,6 +67,8 @@ class SignerAdapter : public QObject, public bs::message::Adapter std::shared_ptr> connFuture_; std::string curServerId_; std::string connKey_; + + std::map> requests_; }; diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index 92a671f0f..645e50748 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -32,6 +32,7 @@ namespace bs { API, Settings, BsServer, // General target/origin of all sorts of BS server messages + AuthEid, Matching, // Alias for Celer or other matching system Assets, // Alias for Genoa data storage atm MktData, diff --git a/common b/common index f745a8afb..9619d2201 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit f745a8afb06dbec712e2c22a721fce6f8cd6ce12 +Subproject commit 9619d22012be5b77c7f75ef4bb537b325f517825 From 182be37397545d76a079eca432a94bb3d8830f25 Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Fri, 4 Sep 2020 22:58:14 +0300 Subject: [PATCH 004/146] WIP 5 --- BlockSettleSigner/SignerInterfaceListener.cpp | 12 +- BlockSettleUILib/AddressListModel.cpp | 93 ++++--- BlockSettleUILib/AddressListModel.h | 27 +- BlockSettleUILib/BSTerminalMainWindow.cpp | 19 +- .../RootWalletPropertiesDialog.cpp | 97 ++++--- .../RootWalletPropertiesDialog.h | 23 +- BlockSettleUILib/NewAddressDialog.cpp | 20 ++ BlockSettleUILib/NewAddressDialog.h | 4 +- BlockSettleUILib/SelectAddressDialog.cpp | 30 ++- BlockSettleUILib/SelectAddressDialog.h | 16 +- BlockSettleUILib/SelectWalletDialog.cpp | 10 +- BlockSettleUILib/SelectWalletDialog.h | 4 +- BlockSettleUILib/StatusBarView.cpp | 8 +- BlockSettleUILib/StatusBarView.h | 2 +- BlockSettleUILib/WalletsViewModel.cpp | 240 ++++++++++++++---- BlockSettleUILib/WalletsViewModel.h | 30 ++- BlockSettleUILib/WalletsWidget.cpp | 75 +++++- BlockSettleUILib/WalletsWidget.h | 22 +- Core/SignerAdapter.cpp | 15 +- GUI/QtWidgets/MainWindow.cpp | 28 +- GUI/QtWidgets/MainWindow.h | 16 +- GUI/QtWidgets/QtGuiAdapter.cpp | 116 ++++++++- GUI/QtWidgets/QtGuiAdapter.h | 26 +- common | 2 +- 24 files changed, 707 insertions(+), 228 deletions(-) diff --git a/BlockSettleSigner/SignerInterfaceListener.cpp b/BlockSettleSigner/SignerInterfaceListener.cpp index c23828dd6..3d64d877e 100644 --- a/BlockSettleSigner/SignerInterfaceListener.cpp +++ b/BlockSettleSigner/SignerInterfaceListener.cpp @@ -418,15 +418,15 @@ void SignerInterfaceListener::onSyncHDWallet(const std::string &data, bs::signer return; } bs::sync::HDWalletData result; - for (int i = 0; i < response.groups_size(); ++i) { - const auto group = response.groups(i); + for (const auto &group : response.groups()) { std::vector leaves; - for (int j = 0; j < group.leaves_size(); ++j) { - const auto leaf = group.leaves(j); + for (const auto &leaf : group.leaves()) { leaves.push_back({ leaf.id(), bs::hd::Path::fromString(leaf.path()) - , false, BinaryData::fromString(leaf.extra_data()) }); + , std::string{}, std::string{}, false + , BinaryData::fromString(leaf.extra_data()) }); } - result.groups.push_back({ static_cast(group.type()), leaves }); + result.groups.push_back({ static_cast(group.type()) + , std::string{}, std::string{}, leaves }); } itCb->second(result); cbHDWalletData_.erase(itCb); diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index 17b90b2f3..43d8ea2ab 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -41,7 +41,7 @@ QString AddressListModel::AddressRow::getAddress() const bool AddressListModel::AddressRow::operator==(const AddressRow& other) const { - return wallet.get() == other.wallet.get() && + return /*wallet.get() == other.wallet.get() &&*/ address == other.address && bytes == other.bytes && transactionCount == other.transactionCount && @@ -74,50 +74,55 @@ AddressListModel::AddressListModel(const std::shared_ptr &wallet) const + , const bs::sync::WalletInfo &wallet) const { AddressRow row; - row.wallet = wallet; +// row.wallet = wallet; row.address = addr; row.transactionCount = -1; row.balance = 0; - if (wallet->type() == bs::core::wallet::Type::Authentication) { + if (wallet.type == bs::core::wallet::Type::Authentication) { row.comment = tr("Authentication PubKey"); const BinaryData rootId; row.displayedAddress = rootId.empty() ? tr("empty") : QString::fromStdString(BtcUtils::base58_encode(rootId)); row.isExternal = true; } else { - row.comment = QString::fromStdString(wallet->getAddressComment(addr)); +// row.comment = QString::fromStdString(wallet->getAddressComment(addr)); row.displayedAddress = QString::fromStdString(addr.display()); - row.walletName = QString::fromStdString(wallet->shortName()); - row.walletId = QString::fromStdString(wallet->walletId()); - row.isExternal = wallet->isExternalAddress(addr); + row.walletName = QString::fromStdString(wallet.name); + row.walletId = QString::fromStdString(wallet.id); +// row.isExternal = wallet->isExternalAddress(addr); } - row.wltType = wallet->type(); + row.wltType = wallet.type; return row; } void AddressListModel::updateWallets() { - updateData(""); +// updateData(""); } -void AddressListModel::updateData(const std::string &walletId) +/*void AddressListModel::updateData(const std::string &walletId) { bool expected = false; bool desired = true; @@ -140,38 +145,43 @@ void AddressListModel::updateData(const std::string &walletId) } processing_.store(false); -} +}*/ -void AddressListModel::updateWallet(const std::shared_ptr &wallet, std::vector &addresses) +void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) { - if (filterBtcOnly_ && wallet->type() != bs::core::wallet::Type::Bitcoin) { + if (filterBtcOnly_ && wallet.type != bs::core::wallet::Type::Bitcoin) { return; } - if (wallet->type() == bs::core::wallet::Type::Authentication) { + if (wallet.type == bs::core::wallet::Type::Authentication) { const auto addr = bs::Address(); auto row = createRow(addr, wallet); - addresses.emplace_back(std::move(row)); + beginResetModel(); + addressRows_.emplace_back(std::move(row)); + endResetModel(); } else { - if ((wallets_.size() > 1) && (wallet->type() == bs::core::wallet::Type::ColorCoin)) { + if ((wallets_.size() > 1) && (wallet.type == bs::core::wallet::Type::ColorCoin)) { return; // don't populate PM addresses when multiple wallets selected } std::vector addressList; switch (addrType_) { case AddressType::External: - addressList = wallet->getExtAddressList(); + emit needExtAddresses(wallet.id); +// addressList = wallet->getExtAddressList(); break; case AddressType::Internal: - addressList = wallet->getIntAddressList(); + emit needIntAddresses(wallet.id); +// addressList = wallet->getIntAddressList(); break; case AddressType::All: case AddressType::ExtAndNonEmptyInt: default: - addressList = wallet->getUsedAddressList(); +// addressList = wallet->getUsedAddressList(); + emit needUsedAddresses(wallet.id); break; } - addresses.reserve(addresses.size() + addressList.size()); +/* addresses.reserve(addresses.size() + addressList.size()); for (size_t i = 0; i < addressList.size(); i++) { const auto &addr = addressList[i]; @@ -181,8 +191,31 @@ void AddressListModel::updateWallet(const std::shared_ptr &wal row.comment = QString::fromStdString(wallet->getAddressComment(addr)); addresses.emplace_back(std::move(row)); - } + }*/ + } +} + +void AddressListModel::onAddresses(const std::string &walletId + , const std::vector &addrs) +{ + const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() + , [walletId](const bs::sync::WalletInfo &wallet) { + return (wallet.id == walletId); + }); + if (itWallet == wallets_.cend()) { + return; + } + std::vector addresses; + for (size_t i = 0; i < addrs.size(); i++) { + const auto &addr = addrs[i]; + auto row = createRow(addr, *itWallet); + row.addrIndex = i; + addresses.emplace_back(std::move(row)); } + beginResetModel(); + addressRows_ = std::move(addresses); + endResetModel(); + emit needAddrComments(walletId, addrs); } void AddressListModel::updateWalletData() @@ -249,15 +282,15 @@ void AddressListModel::updateWalletData() }; // Get an address's balance & # of TXs from Armory via the wallet. - const auto &wallet = addressRows_[i].wallet; +// const auto &wallet = addressRows_[i].wallet; const auto &address = addressRows_[i].address; - if (!wallet) { +/* if (!wallet) { return; } wallet->onBalanceAvailable([wallet, address, cbTxN, cbBalance] { cbTxN(wallet->getAddrTxN(address)); cbBalance(wallet->getAddrBalance(address)); - }); + });*/ } } @@ -308,7 +341,7 @@ int AddressListModel::rowCount(const QModelIndex& parent) const return 0; } - if ((wallets_.size() == 1) && (wallets_[0]->type() == bs::core::wallet::Type::Authentication)) { + if ((wallets_.size() == 1) && (wallets_[0].type == bs::core::wallet::Type::Authentication)) { return 1; } diff --git a/BlockSettleUILib/AddressListModel.h b/BlockSettleUILib/AddressListModel.h index 9231213ad..d1662bb53 100644 --- a/BlockSettleUILib/AddressListModel.h +++ b/BlockSettleUILib/AddressListModel.h @@ -15,6 +15,7 @@ #include #include #include "CoreWallet.h" +#include "SignerDefs.h" #include "ValidityFlag.h" namespace bs { @@ -33,7 +34,7 @@ class AddressListModel : public QAbstractTableModel public: struct AddressRow { - std::shared_ptr wallet; +// std::shared_ptr wallet; bs::Address address; QByteArray bytes; int transactionCount = 0; @@ -80,10 +81,11 @@ class AddressListModel : public QAbstractTableModel ExtAndNonEmptyInt = 4 }; - typedef std::vector> Wallets; + typedef std::vector Wallets; - AddressListModel(const std::shared_ptr &, QObject* parent + [[deprecated]] AddressListModel(const std::shared_ptr &, QObject* parent , AddressType addrType = AddressType::All); + AddressListModel(QObject* parent, AddressType addrType = AddressType::All); ~AddressListModel() noexcept = default; int rowCount(const QModelIndex & parent) const override; @@ -91,11 +93,20 @@ class AddressListModel : public QAbstractTableModel QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - bool setWallets(const Wallets &, bool force, bool filterBtcOnly); + void setWallets(const Wallets &, bool force, bool filterBtcOnly); + void onAddresses(const std::string &walletId, const std::vector &); + void onAddressComments(const std::map &); + void onAddressBalances(); + +signals: + void needExtAddresses(std::string walletId); + void needIntAddresses(std::string walletId); + void needUsedAddresses(std::string walletId); + void needAddrComments(std::string walletId, const std::vector &); private slots: - void updateWallets(); - void updateData(const std::string &walletId); + void updateWallets(); // deprecated +// void updateData(const std::string &walletId); void removeEmptyIntAddresses(); private: @@ -109,9 +120,9 @@ private slots: ValidityFlag validityFlag_; private: - void updateWallet(const std::shared_ptr &wallet, std::vector &addresses); + void updateWallet(const bs::sync::WalletInfo &wallet); void updateWalletData(); - AddressRow createRow(const bs::Address &, const std::shared_ptr &) const; + AddressRow createRow(const bs::Address &, const bs::sync::WalletInfo &) const; QVariant dataForRow(const AddressListModel::AddressRow &row, int column) const; }; diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index da3a9de79..e546953f5 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -1190,12 +1190,12 @@ void BSTerminalMainWindow::onGenerateAddress() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallets = ui_->widgetWallets->getSelectedWallets(); if (!wallets.empty()) { - selWalletId = wallets[0]->walletId(); + selWalletId = wallets[0].id; } else { wallets = ui_->widgetWallets->getFirstWallets(); if (!wallets.empty()) { - selWalletId = wallets[0]->walletId(); + selWalletId = wallets[0].id; } } } @@ -1206,7 +1206,8 @@ void BSTerminalMainWindow::onGenerateAddress() return; } - NewAddressDialog newAddressDialog(selectWalletDialog.getSelectedWallet(), this); + const auto &wallet = walletsMgr_->getWalletById(selectWalletDialog.getSelectedWallet()); + NewAddressDialog newAddressDialog(wallet, this); newAddressDialog.exec(); } @@ -1216,15 +1217,17 @@ void BSTerminalMainWindow::onSend() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallet = ui_->widgetWallets->getSelectedHdWallet(); - if (!wallet) { - wallet = walletsMgr_->getPrimaryWallet(); + if (wallet.id.empty()) { + const auto &priWallet = walletsMgr_->getPrimaryWallet(); + if (priWallet) { + wallet.id = priWallet->walletId(); + } } - if (wallet) { - selectedWalletId = wallet->walletId(); + if (!wallet.id.empty()) { + selectedWalletId = wallet.id; } } - std::shared_ptr dlg; if ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index ebaf2d29f..e93d4977b 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -32,11 +32,10 @@ class CurrentWalletFilter : public QSortFilterProxyModel { public: - CurrentWalletFilter(const std::shared_ptr &wallet, QObject* parent) + CurrentWalletFilter(const std::string &walletId, QObject* parent) : QSortFilterProxyModel(parent) - , wallet_(wallet) - { - } + , walletId_(walletId) + {} bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override { @@ -47,15 +46,15 @@ class CurrentWalletFilter : public QSortFilterProxyModel auto node = dynamic_cast(sourceModel())->getNode(index); auto wallet = node->hdWallet(); - return (wallet != nullptr) && (node->hdWallet()->walletId() == wallet_->walletId()); + return (node->hdWallet().id == walletId_); } private: - std::shared_ptr wallet_; + std::string walletId_; }; RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptr &logger - , const std::shared_ptr &wallet + , const bs::sync::WalletInfo &wallet , const std::shared_ptr &walletsManager , const std::shared_ptr &armory , const std::shared_ptr &container @@ -68,7 +67,7 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrlabelEncRank->clear(); - walletFilter_ = new CurrentWalletFilter(wallet, this); + walletFilter_ = new CurrentWalletFilter(wallet.id, this); walletFilter_->setSourceModel(walletsModel); ui_->treeViewWallets->setModel(walletFilter_); @@ -112,7 +111,7 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrGetInfo(wallet_->walletId()); + infoReqId_ = signingContainer_->GetInfo(wallet_.id); } ui_->treeViewWallets->expandAll(); @@ -171,7 +170,7 @@ void RootWalletPropertiesDialog::onHDWalletInfo(unsigned int id, const bs::hd::W walletInfo_ = walletInfo; // but wallet name is from bs::hd::Wallet - walletInfo_.setName(QString::fromStdString(wallet_->name())); + walletInfo_.setName(QString::fromStdString(wallet_.name)); ui_->manageEncryptionButton->setEnabled(!walletInfo.isHardwareWallet()); @@ -201,7 +200,7 @@ void RootWalletPropertiesDialog::onWalletSelected() auto node = dynamic_cast(walletFilter_->sourceModel())->getNode(modelIndex); const auto wallet = node->hdWallet(); - if (wallet != nullptr) { + if (!wallet.id.empty()) { updateWalletDetails(wallet); } else { const auto wallets = node->wallets(); @@ -213,19 +212,18 @@ void RootWalletPropertiesDialog::onWalletSelected() } } -void RootWalletPropertiesDialog::updateWalletDetails(const std::shared_ptr& wallet) +void RootWalletPropertiesDialog::updateWalletDetails(const bs::sync::WalletInfo &wi) { - ui_->labelWalletId->setText(QString::fromStdString(wallet->walletId())); - ui_->labelWalletName->setText(QString::fromStdString(wallet->name())); - ui_->labelDescription->setText(QString::fromStdString(wallet->description())); + ui_->labelWalletId->setText(QString::fromStdString(wi.id)); + ui_->labelWalletName->setText(QString::fromStdString(wi.name)); + ui_->labelDescription->setText(QString::fromStdString(wi.description)); ui_->balanceWidget->hide(); - ui_->labelGroupsUsed->setText(tr("%1/%2").arg(QString::number(wallet->getNumGroups())).arg(QString::number(wallet->getNumLeaves()))); +// ui_->labelGroupsUsed->setText(tr("%1/%2").arg(QString::number(wallet->getNumGroups())).arg(QString::number(wallet->getNumLeaves()))); ui_->labelAddressesActive->setText(tr("Loading...")); ui_->labelUTXOs->setText(tr("Loading...")); - unsigned int nbTotalAddresses = 0; auto nbUTXOs = std::make_shared(0); QPointer thisPtr = this; @@ -238,49 +236,49 @@ void RootWalletPropertiesDialog::updateWalletDetails(const std::shared_ptrgetLeaves()) { + if (wi.format == bs::sync::WalletFormat::HD) { + emit needHDWalletDetails(wi.id); + } + else { + ui_->labelAddressesActive->setText(tr("Loading...")); + ui_->labelUTXOs->setText(tr("Loading...")); + emit needWalletBalances(wi.id); + emit needSpendableUTXOs(wi.id); + } +} + +void RootWalletPropertiesDialog::onHDWalletDetails() +{ + unsigned int nbTotalAddresses = 0; +/* for (const auto &leaf : wallet->getLeaves()) { leaf->getSpendableTxOutList(cbUTXOs, UINT64_MAX, true); auto addrCnt = leaf->getActiveAddressCount(); ui_->labelAddressesActive->setText(QString::number(addrCnt)); nbTotalAddresses += leaf->getUsedAddressCount(); - } - + }*/ //TODO: reimplement ui_->labelAddressesUsed->setText(QString::number(nbTotalAddresses)); } -void RootWalletPropertiesDialog::updateWalletDetails(const std::shared_ptr& wallet) +void RootWalletPropertiesDialog::onSpendableUTXOs() +{ +// ui_->labelUTXOs->setText(QString::number(sizeUTXOs)); +} + +void RootWalletPropertiesDialog::onWalletBalances() { - ui_->labelWalletId->setText(QString::fromStdString(wallet->walletId())); - ui_->labelWalletName->setText(QString::fromStdString(wallet->name())); - ui_->labelDescription->setText(QString::fromStdString(wallet->description())); +// ui_->labelAddressesUsed->setText(QString::number(wallet->getUsedAddressCount())); +// ui_->labelAddressesActive->setText(QString::number(activeAddrCnt)); - ui_->labelAddressesUsed->setText(QString::number(wallet->getUsedAddressCount())); +// ui_->labelSpendable->setText(UiUtils::displayAmount(wallet->getSpendableBalance())); +// ui_->labelUnconfirmed->setText(UiUtils::displayAmount(wallet->getUnconfirmedBalance())); +// ui_->labelTotal->setText(UiUtils::displayAmount(wallet->getTotalBalance())); + ui_->balanceWidget->show(); - if (wallet->isBalanceAvailable()) { - ui_->labelAddressesActive->setText(tr("Loading...")); - ui_->labelUTXOs->setText(tr("Loading...")); - wallet->getSpendableTxOutList([this](std::vector utxos) { - QMetaObject::invokeMethod(this, [this, size = utxos.size()] { - ui_->labelUTXOs->setText(QString::number(size)); - }); - }, UINT64_MAX, true); - - auto addrCnt = wallet->getActiveAddressCount(); - QMetaObject::invokeMethod(this, [this, addrCnt] { - ui_->labelAddressesActive->setText(QString::number(addrCnt)); - }); - - ui_->labelSpendable->setText(UiUtils::displayAmount(wallet->getSpendableBalance())); - ui_->labelUnconfirmed->setText(UiUtils::displayAmount(wallet->getUnconfirmedBalance())); - ui_->labelTotal->setText(UiUtils::displayAmount(wallet->getTotalBalance())); - ui_->balanceWidget->show(); - } else { - ui_->labelAddressesActive->setText(tr("N/A")); + /* ui_->labelAddressesActive->setText(tr("N/A")); ui_->labelUTXOs->setText(tr("N/A")); - ui_->balanceWidget->hide(); - } + ui_->balanceWidget->hide();*/ } void RootWalletPropertiesDialog::onRescanBlockchain() @@ -302,7 +300,8 @@ void RootWalletPropertiesDialog::onRescanBlockchain() else { startWalletScan(); }*/ - wallet_->startRescan(); +// wallet_->startRescan(); //TODO: reimplement + emit startRescan(wallet_.id); accept(); } diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index ff7631785..3b5481b58 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -14,6 +14,7 @@ #include #include #include "BinaryData.h" +#include "SignerDefs.h" #include "QWalletInfo.h" namespace Ui { @@ -43,8 +44,8 @@ class RootWalletPropertiesDialog : public QDialog Q_OBJECT public: - RootWalletPropertiesDialog(const std::shared_ptr &logger - , const std::shared_ptr & + [[deprecated]] RootWalletPropertiesDialog(const std::shared_ptr &logger + , const bs::sync::WalletInfo & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -52,8 +53,21 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &, QWidget* parent = nullptr); + RootWalletPropertiesDialog(const std::shared_ptr &logger + , const bs::sync::WalletInfo &, WalletsViewModel *walletsModel + , QWidget* parent = nullptr); ~RootWalletPropertiesDialog() override; + void onHDWalletDetails(); + void onWalletBalances(); + void onSpendableUTXOs(); + +signals: + void startRescan(std::string walletId); + void needHDWalletDetails(std::string walletId); + void needWalletBalances(std::string walletId); + void needSpendableUTXOs(std::string walletId); + private slots: void onDeleteWallet(); void onBackupWallet(); @@ -64,12 +78,11 @@ private slots: void onModelReset(); private: - void updateWalletDetails(const std::shared_ptr &); - void updateWalletDetails(const std::shared_ptr &); + void updateWalletDetails(const bs::sync::WalletInfo &); private: std::unique_ptr ui_; - std::shared_ptr wallet_; + bs::sync::WalletInfo wallet_; std::shared_ptr walletsManager_; bs::hd::WalletInfo walletInfo_; std::shared_ptr signingContainer_; diff --git a/BlockSettleUILib/NewAddressDialog.cpp b/BlockSettleUILib/NewAddressDialog.cpp index 70daf7dd6..97b067dc1 100644 --- a/BlockSettleUILib/NewAddressDialog.cpp +++ b/BlockSettleUILib/NewAddressDialog.cpp @@ -64,6 +64,26 @@ NewAddressDialog::NewAddressDialog(const std::shared_ptr &wall } } +NewAddressDialog::NewAddressDialog(const bs::sync::WalletInfo &wallet + , QWidget* parent) + : QDialog(parent) + , ui_(new Ui::NewAddressDialog()) +{ + ui_->setupUi(this); + ui_->labelWallet->setText(QString::fromStdString(wallet.name)); + + auto copyButton = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); + connect(copyButton, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); + connect(ui_->pushButtonCopyToClipboard, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); + + const auto closeButton = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Close); + if (closeButton) { + connect(closeButton, &QPushButton::clicked, this, &NewAddressDialog::onClose); + } + copyButton->setEnabled(false); + closeButton->setEnabled(false); +} + NewAddressDialog::~NewAddressDialog() = default; void NewAddressDialog::displayAddress() diff --git a/BlockSettleUILib/NewAddressDialog.h b/BlockSettleUILib/NewAddressDialog.h index 81fecdbc8..8d1bb401c 100644 --- a/BlockSettleUILib/NewAddressDialog.h +++ b/BlockSettleUILib/NewAddressDialog.h @@ -14,6 +14,7 @@ #include #include #include "Address.h" +#include "SignerDefs.h" namespace Ui { class NewAddressDialog; @@ -30,8 +31,9 @@ class NewAddressDialog : public QDialog Q_OBJECT public: - NewAddressDialog(const std::shared_ptr& wallet + [[deprecated]] NewAddressDialog(const std::shared_ptr& wallet , QWidget* parent = nullptr); + NewAddressDialog(const bs::sync::WalletInfo &, QWidget* parent = nullptr); ~NewAddressDialog() override; protected: diff --git a/BlockSettleUILib/SelectAddressDialog.cpp b/BlockSettleUILib/SelectAddressDialog.cpp index c96adba88..01b629757 100644 --- a/BlockSettleUILib/SelectAddressDialog.cpp +++ b/BlockSettleUILib/SelectAddressDialog.cpp @@ -38,6 +38,29 @@ SelectAddressDialog::SelectAddressDialog(const std::shared_ptrsetupUi(this); + + model_ = std::make_unique(ui_->treeView, addrType); + model_->setWallets(wallets, false, false); + ui_->treeView->setModel(model_.get()); + + ui_->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + connect(ui_->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SelectAddressDialog::onSelectionChanged); + connect(ui_->treeView, &QTreeView::doubleClicked, this, &SelectAddressDialog::onDoubleClicked); + + connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &SelectAddressDialog::reject); + connect(ui_->pushButtonSelect, &QPushButton::clicked, this, &SelectAddressDialog::accept); + + onSelectionChanged(); +} + SelectAddressDialog::~SelectAddressDialog() = default; void SelectAddressDialog::init() @@ -45,7 +68,12 @@ void SelectAddressDialog::init() ui_->setupUi(this); model_ = std::make_unique(walletsMgr_, ui_->treeView, addrType_); - model_->setWallets(wallets_, false, false); + std::vector walletsInfo; + for (const auto &wallet : wallets_) { + const auto leaf = std::dynamic_pointer_cast(wallet); + walletsInfo.push_back(bs::sync::WalletInfo::fromLeaf(leaf)); + } + model_->setWallets(walletsInfo, false, false); ui_->treeView->setModel(model_.get()); ui_->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); diff --git a/BlockSettleUILib/SelectAddressDialog.h b/BlockSettleUILib/SelectAddressDialog.h index d565733ee..46dc9b877 100644 --- a/BlockSettleUILib/SelectAddressDialog.h +++ b/BlockSettleUILib/SelectAddressDialog.h @@ -35,10 +35,12 @@ class SelectAddressDialog : public QDialog Q_OBJECT public: - SelectAddressDialog(const std::shared_ptr & + [[deprecated]] SelectAddressDialog(const std::shared_ptr & , const std::shared_ptr &, QWidget* parent = nullptr , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); - SelectAddressDialog(const std::shared_ptr &, QWidget* parent = nullptr + [[deprecated]] SelectAddressDialog(const std::shared_ptr &, QWidget* parent = nullptr + , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); + SelectAddressDialog(const AddressListModel::Wallets &, QWidget* parent = nullptr , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); ~SelectAddressDialog() override; @@ -49,15 +51,15 @@ public slots: void onDoubleClicked(const QModelIndex& index); private: - void init(); + [[deprecated]] void init(); bs::Address getAddress(const QModelIndex& index) const; private: std::unique_ptr ui_; - std::vector> wallets_; - std::shared_ptr walletsMgr_; - const AddressListModel::AddressType addrType_; - std::unique_ptr model_; + [[deprecated]] std::vector> wallets_; + [[deprecated]] std::shared_ptr walletsMgr_; + [[deprecated]] const AddressListModel::AddressType addrType_; + std::unique_ptr model_; bs::Address selectedAddr_; }; diff --git a/BlockSettleUILib/SelectWalletDialog.cpp b/BlockSettleUILib/SelectWalletDialog.cpp index 32314bbd9..7d56263b0 100644 --- a/BlockSettleUILib/SelectWalletDialog.cpp +++ b/BlockSettleUILib/SelectWalletDialog.cpp @@ -52,7 +52,7 @@ void SelectWalletDialog::onSelectionChanged() { auto selectedRows = ui_->treeViewWallets->selectionModel()->selectedRows(); if (selectedRows.size() == 1) { - selectedWallet_ = walletsModel_->getWallet(selectedRows[0]); + selectedWallet_ = walletsModel_->getWallet(selectedRows[0]).id; } else { selectedWallet_ = nullptr; @@ -60,20 +60,20 @@ void SelectWalletDialog::onSelectionChanged() walletsModel_->setSelectedWallet(selectedWallet_); auto okButton = ui_->buttonBox->button(QDialogButtonBox::Ok); - okButton->setEnabled(selectedWallet_ != nullptr); + okButton->setEnabled(!selectedWallet_.empty()); } -std::shared_ptr SelectWalletDialog::getSelectedWallet() const +std::string SelectWalletDialog::getSelectedWallet() const { return selectedWallet_; } void SelectWalletDialog::onDoubleClicked(const QModelIndex& index) { - if (!selectedWallet_) { + if (selectedWallet_.empty()) { return; } - selectedWallet_ = walletsModel_->getWallet(index); + selectedWallet_ = walletsModel_->getWallet(index).id; QDialog::accept(); } diff --git a/BlockSettleUILib/SelectWalletDialog.h b/BlockSettleUILib/SelectWalletDialog.h index a470e4123..b9c222c82 100644 --- a/BlockSettleUILib/SelectWalletDialog.h +++ b/BlockSettleUILib/SelectWalletDialog.h @@ -35,7 +35,7 @@ Q_OBJECT SelectWalletDialog(const std::shared_ptr &, const std::string &selWalletId, QWidget* parent = nullptr); ~SelectWalletDialog() override; - std::shared_ptr getSelectedWallet() const; + std::string getSelectedWallet() const; public slots: void onSelectionChanged(); @@ -44,7 +44,7 @@ public slots: private: std::unique_ptr ui_; WalletsViewModel * walletsModel_; - std::shared_ptr selectedWallet_; + std::string selectedWallet_; std::shared_ptr walletsManager_; }; diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index a7eed6ef0..f12ae8bcb 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -97,7 +97,7 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory // connected are not used here because we wait for authenticated signal instead // disconnected are not used here because onContainerError should be always called connect(container.get(), &SignContainer::authenticated, this, &StatusBarView::onContainerAuthorized); - connect(container.get(), &SignContainer::connectionError, this, &StatusBarView::onContainerError); + connect(container.get(), &SignContainer::connectionError, this, &StatusBarView::onSignerStatusChanged); } onArmoryStateChanged(armory_->state(), armory_->topBlock()); @@ -540,7 +540,7 @@ void StatusBarView::onContainerAuthorized() containerStatusLabel_->setPixmap(iconContainerOnline_); } -void StatusBarView::onContainerError(SignContainer::ConnectionError error, const QString &details) +void StatusBarView::onSignerStatusChanged(SignContainer::ConnectionError error, const QString &details) { Q_UNUSED(details); @@ -549,6 +549,10 @@ void StatusBarView::onContainerError(SignContainer::ConnectionError error, const assert(false); break; + case SignContainer::Ready: + containerStatusLabel_->setPixmap(iconContainerOnline_); + break; + case SignContainer::UnknownError: case SignContainer::SocketFailed: case SignContainer::HostNotFound: diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index 8c5a38e7a..04c2f29d1 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -57,7 +57,7 @@ public slots: void onConnectionClosed(); void onConnectionError(int errorCode); void onContainerAuthorized(); - void onContainerError(SignContainer::ConnectionError error, const QString &details); + void onSignerStatusChanged(SignContainer::ConnectionError error, const QString &details); void updateBalances(); void onWalletImportStarted(const std::string &walletId); void onWalletImportFinished(const std::string &walletId); diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index 528d71842..273d944bb 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -21,6 +21,16 @@ #include "Wallets/SyncWalletsManager.h" +void WalletNode::replace(WalletNode *child) +{ + for (auto &c : children_) { + if (child->id() == c->id()) { + c = child; + break; + } + } +} + void WalletNode::clear() { qDeleteAll(children_); @@ -34,8 +44,13 @@ WalletNode *WalletNode::child(int index) const WalletNode *WalletNode::findByWalletId(const std::string &walletId) { - if ((type() == Type::Leaf) && (wallets()[0] != nullptr) && (wallets()[0]->walletId() == walletId)) { - return this; + const auto &tmpWallets = wallets(); + if ((type() == Type::Leaf) && !tmpWallets.empty()) { + for (const auto &wallet : tmpWallets) { + if (wallet.id == walletId) { + return this; + } + } } for (const auto &child : children_) { auto node = child->findByWalletId(walletId); @@ -46,10 +61,11 @@ WalletNode *WalletNode::findByWalletId(const std::string &walletId) return nullptr; } +class WalletGroupNode; class WalletRootNode : public WalletNode { public: - WalletRootNode(WalletsViewModel *vm, const std::shared_ptr &wallet + WalletRootNode(WalletsViewModel *vm, const bs::sync::WalletInfo &wallet , const std::string &name, const std::string &desc, WalletNode::Type type, int row , WalletNode *parent, BTCNumericTypes::balance_type balTotal = 0 , BTCNumericTypes::balance_type balUnconf = 0, BTCNumericTypes::balance_type balSpend = 0 @@ -104,7 +120,7 @@ class WalletRootNode : public WalletNode } std::string id() const override { - return (hdWallet_ ? hdWallet_->walletId() : std::string{}); + return (!hdWallet_.id.empty() ? hdWallet_.id : std::string{}); } void setState(State state) override { @@ -114,15 +130,16 @@ class WalletRootNode : public WalletNode } } - void addGroups(const std::vector> &groups); +// void addGroups(const std::vector> &groups); + WalletGroupNode *addGroup(bs::core::wallet::Type, const std::string &name, const std::string &desc); - std::vector> wallets() const override + std::vector wallets() const override { - std::vector> ret = wallets_; + decltype(wallets_) ret = wallets_; - for (const auto * g : qAsConst(children_)) { + for (const auto *g : qAsConst(children_)) { const auto tmp = g->wallets(); - ret.insert(ret.end(), tmp.cbegin(), tmp.cend()); + ret.insert(ret.cend(), tmp.cbegin(), tmp.cend()); } return ret; @@ -131,14 +148,14 @@ class WalletRootNode : public WalletNode BTCNumericTypes::balance_type getBalanceUnconf() const { return balUnconf_; } BTCNumericTypes::balance_type getBalanceSpend() const { return balSpend_; } size_t getNbUsedAddresses() const { return nbAddr_; } - std::shared_ptr hdWallet() const override { return hdWallet_; } + bs::sync::WalletInfo hdWallet() const override { return hdWallet_; } protected: std::string desc_; std::atomic balTotal_, balUnconf_, balSpend_; size_t nbAddr_; - std::shared_ptr hdWallet_; - std::vector> wallets_; + bs::sync::WalletInfo hdWallet_; + std::vector wallets_; protected: void updateCounters(WalletRootNode *node) { @@ -207,12 +224,16 @@ class WalletRootNode : public WalletNode class WalletLeafNode : public WalletRootNode { public: - WalletLeafNode(WalletsViewModel *vm, const std::shared_ptr &wallet - , const std::shared_ptr &rootWallet, int row, WalletNode *parent) - : WalletRootNode(vm, rootWallet, wallet->shortName(), wallet->description(), Type::Leaf, row, parent - , 0, 0, 0, wallet->getUsedAddressCount()) + WalletLeafNode(WalletsViewModel *vm, const bs::sync::WalletInfo &wallet + , const bs::sync::WalletInfo &rootWallet, int row, WalletNode *parent) + : WalletRootNode(vm, rootWallet, wallet.name, wallet.description, Type::Leaf, row, parent + , 0, 0, 0, 0) , wallet_(wallet) + {} + + void setBalances(const std::shared_ptr &wallet) { + nbAddr_ = wallet->getUsedAddressCount(); wallet->onBalanceAvailable([this, wallet, handle = validityFlag_.handle()]() mutable { ValidityGuard lock(handle); if (!handle.isValid()) { @@ -224,15 +245,19 @@ class WalletLeafNode : public WalletRootNode }); } - std::vector> wallets() const override { return {wallet_}; } + std::vector wallets() const override + { + return { wallet_ }; + } - std::string id() const override { - return wallet_->walletId(); + std::string id() const override + { + return wallet_.id; } QVariant data(int col, int role) const override { if (role == Qt::FontRole) { - if (wallet_ == viewModel_->selectedWallet()) { + if (wallet_.id == viewModel_->selectedWallet()) { QFont font; font.setUnderline(true); return font; @@ -242,14 +267,14 @@ class WalletLeafNode : public WalletRootNode } private: - std::shared_ptr wallet_; + bs::sync::WalletInfo wallet_; ValidityFlag validityFlag_; }; class WalletGroupNode : public WalletRootNode { public: - WalletGroupNode(WalletsViewModel *vm, const std::shared_ptr &hdWallet + WalletGroupNode(WalletsViewModel *vm, const bs::sync::WalletInfo &hdWallet , const std::string &name, const std::string &desc, WalletNode::Type type , int row, WalletNode *parent) : WalletRootNode(vm, hdWallet, name, desc, type, row, parent) {} @@ -260,32 +285,47 @@ class WalletGroupNode : public WalletRootNode } } - std::vector> wallets() const override { return wallets_; } + std::vector wallets() const override { return wallets_; } - void addLeaves(const std::vector> &leaves) { - for (const auto &leaf : leaves) { - if (viewModel_->showRegularWallets() && (leaf->type() != bs::core::wallet::Type::Bitcoin || leaf->purpose() == bs::hd::Purpose::NonSegWit)) { - continue; - } - const auto leafNode = new WalletLeafNode(viewModel_, leaf, hdWallet_, nbChildren(), this); - add(leafNode); - updateCounters(leafNode); - wallets_.push_back(leaf); + void addLeaf(const bs::sync::WalletInfo &leaf, const std::shared_ptr &wallet) { + if (viewModel_->showRegularWallets() && (leaf.type != bs::core::wallet::Type::Bitcoin + || leaf.purpose == bs::hd::Purpose::NonSegWit)) { + return; } + const auto leafNode = new WalletLeafNode(viewModel_, leaf, hdWallet_, nbChildren(), this); + leafNode->setBalances(wallet); + add(leafNode); + updateCounters(leafNode); + wallets_.push_back(leaf); + } + + void addLeaf(const bs::sync::HDWalletData::Leaf &leaf, bs::core::wallet::Type type) { + if (viewModel_->showRegularWallets() && (type != bs::core::wallet::Type::Bitcoin + || leaf.path.get(-2) == bs::hd::Purpose::NonSegWit)) { + return; + } + bs::sync::WalletInfo wi; + wi.format = bs::sync::WalletFormat::Plain; + wi.id = leaf.id; + wi.name = leaf.name; + wi.description = leaf.description; + wi.purpose = static_cast(leaf.path.get(-2)); + const auto leafNode = new WalletLeafNode(viewModel_, wi, hdWallet_, nbChildren(), this); + add(leafNode); + wallets_.push_back(wi); } }; -void WalletRootNode::addGroups(const std::vector> &groups) +WalletGroupNode *WalletRootNode::addGroup(bs::core::wallet::Type type + , const std::string &name, const std::string &desc) { - for (const auto &group : groups) { - if (viewModel_->showRegularWallets() && (group->type() != bs::core::wallet::Type::Bitcoin)) { - continue; - } - const auto groupNode = new WalletGroupNode(viewModel_, hdWallet_, group->name(), group->description() - , getNodeType(group->type()), nbChildren(), this); - add(groupNode); - groupNode->addLeaves(group->getLeaves()); + if (viewModel_->showRegularWallets() && (type != bs::core::wallet::Type::Bitcoin)) { + return nullptr; } + const auto groupNode = new WalletGroupNode(viewModel_, hdWallet_, name, desc + , getNodeType(type), nbChildren(), this); + add(groupNode); + return groupNode; } @@ -315,6 +355,15 @@ WalletsViewModel::WalletsViewModel(const std::shared_ptr(this, WalletNode::Type::Root); +} + WalletNode *WalletsViewModel::getNode(const QModelIndex &index) const { if (!index.isValid()) { @@ -333,7 +382,7 @@ int WalletsViewModel::rowCount(const QModelIndex &parent) const return getNode(parent)->nbChildren(); } -std::vector> WalletsViewModel::getWallets(const QModelIndex &index) const +std::vector WalletsViewModel::getWallets(const QModelIndex &index) const { const auto node = getNode(index); if (node == nullptr) { @@ -342,13 +391,13 @@ std::vector> WalletsViewModel::getWallets(cons return node->wallets(); } -std::shared_ptr WalletsViewModel::getWallet(const QModelIndex &index) const +bs::sync::WalletInfo WalletsViewModel::getWallet(const QModelIndex &index) const { const auto &wallets = getWallets(index); if (wallets.size() == 1) { return wallets[0]; } - return nullptr; + return {}; } QVariant WalletsViewModel::data(const QModelIndex &index, int role) const @@ -545,6 +594,84 @@ void WalletsViewModel::onNewWalletAdded(const std::string &walletId) hdInfoReqIds_[signContainer_->GetInfo(walletId)] = walletId; } +void WalletsViewModel::onHDWallet(const bs::sync::WalletInfo &wi) +{ + const auto &wallet = rootNode_->findByWalletId(wi.id); + int row = wallet ? wallet->row() : rootNode_->nbChildren(); + const auto hdNode = new WalletRootNode(this, wi, wi.name, wi.description + , wi.primary ? WalletNode::Type::WalletPrimary : WalletNode::Type::WalletRegular + , row, rootNode_.get()); + if (wallet) { + rootNode_->replace(hdNode); + emit dataChanged(createIndex(row, 0, static_cast(rootNode_.get())) + , createIndex(row, (int)WalletColumns::ColumnCount - 1, static_cast(rootNode_.get()))); + } + else { + beginInsertRows(createIndex(rootNode_->row(), 0, static_cast(rootNode_.get())) + , rootNode_->nbChildren(), rootNode_->nbChildren()); + rootNode_->add(hdNode); + endInsertRows(); + } + emit needHDWalletDetails(wi.id); +} + +void WalletsViewModel::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) +{ + WalletNode *node{ nullptr }; + for (int i = 0; i < rootNode_->nbChildren(); ++i) { + if (rootNode_->child(i)->id() == hdWallet.id) { + node = rootNode_->child(i); + break; + } + } + const auto &hdNode = dynamic_cast(node); + if (!node || !hdNode) { + return; + } + for (const auto &group : hdWallet.groups) { + if (group.type == bs::hd::CoinType::BlockSettle_Settlement) { + continue; + } + WalletGroupNode *groupNode{ nullptr }; + for (int i = 0; i < node->nbChildren(); ++i) { + const auto &child = node->child(i); + if (child->name() == group.name) { + groupNode = dynamic_cast(child); + break; + } + } + bs::core::wallet::Type groupType{ bs::core::wallet::Type::Unknown }; + switch (group.type) { + case bs::hd::CoinType::Bitcoin_main: + case bs::hd::CoinType::Bitcoin_test: + groupType = bs::core::wallet::Type::Bitcoin; + break; + case bs::hd::CoinType::BlockSettle_Auth: + groupType = bs::core::wallet::Type::Authentication; + break; + case bs::hd::CoinType::BlockSettle_CC: + groupType = bs::core::wallet::Type::ColorCoin; + break; + default: break; + } + if (!groupNode) { + beginInsertRows(createIndex(node->row(), 0, static_cast(node)) + , node->nbChildren(), node->nbChildren()); + groupNode = hdNode->addGroup(groupType, group.name, group.description); + endInsertRows(); + } + for (const auto &leaf : group.leaves) { + const auto &leafNode = groupNode->findByWalletId(leaf.id); + if (!leafNode) { + beginInsertRows(createIndex(groupNode->row(), 0, static_cast(groupNode)) + , groupNode->nbChildren(), groupNode->nbChildren()); + groupNode->addLeaf(leaf, groupType); + endInsertRows(); + } + } + } +} + void WalletsViewModel::LoadWallets(bool keepSelection) { const auto treeView = qobject_cast(QObject::parent()); @@ -559,7 +686,7 @@ void WalletsViewModel::LoadWallets(bool keepSelection) if (node != nullptr) { const auto &wallets = node->wallets(); if (wallets.size() == 1) { - selectedWalletId = wallets[0]->walletId(); + selectedWalletId = wallets[0].id; } } } @@ -571,21 +698,24 @@ void WalletsViewModel::LoadWallets(bool keepSelection) if (!hdWallet) { continue; } - const auto hdNode = new WalletRootNode(this, hdWallet, hdWallet->name(), hdWallet->description() + const auto hdNode = new WalletRootNode(this, bs::sync::WalletInfo::fromWallet(hdWallet) + , hdWallet->name(), hdWallet->description() , getHDWalletType(hdWallet, walletsManager_), rootNode_->nbChildren(), rootNode_.get()); rootNode_->add(hdNode); // filter groups // don't display Settlement - auto groups = hdWallet->getGroups(); - std::vector> filteredGroups; - - std::copy_if(groups.begin(), groups.end(), std::back_inserter(filteredGroups), - [](const std::shared_ptr& item) - { return item->type() != bs::core::wallet::Type::Settlement; } - ); - - hdNode->addGroups(filteredGroups); + for (const auto &group : hdWallet->getGroups()) { + if (group->type() == bs::core::wallet::Type::Settlement) { + continue; + } + auto groupNode = hdNode->addGroup(group->type(), group->name(), group->description()); + if (groupNode) { + for (const auto &leaf : group->getLeaves()) { + groupNode->addLeaf(bs::sync::WalletInfo::fromLeaf(leaf), leaf); + } + } + } if (signContainer_) { if (signContainer_->isOffline()) { hdNode->setState(WalletNode::State::Offline); diff --git a/BlockSettleUILib/WalletsViewModel.h b/BlockSettleUILib/WalletsViewModel.h index aa4b1a48a..ccc17ee46 100644 --- a/BlockSettleUILib/WalletsViewModel.h +++ b/BlockSettleUILib/WalletsViewModel.h @@ -18,14 +18,11 @@ #include #include #include "QWalletInfo.h" +#include "SignerDefs.h" namespace bs { namespace sync { - namespace hd { - class Wallet; - } - class Wallet; class WalletsManager; } } @@ -59,12 +56,13 @@ class WalletNode : viewModel_(vm), parent_(parent), row_(row), type_(type) {} virtual ~WalletNode() { clear(); } - virtual std::vector> wallets() const { return {}; } - virtual std::shared_ptr hdWallet() const { return nullptr; } + virtual std::vector wallets() const { return {}; } + virtual bs::sync::WalletInfo hdWallet() const { return {}; } virtual QVariant data(int, int) const { return QVariant(); } virtual std::string id() const { return {}; } void add(WalletNode *child) { children_.append(child); } + void replace(WalletNode *child); void clear(); int nbChildren() const { return children_.count(); } bool hasChildren() const { return !children_.empty(); } @@ -93,8 +91,9 @@ class WalletsViewModel : public QAbstractItemModel { Q_OBJECT public: - WalletsViewModel(const std::shared_ptr& walletsManager, const std::string &defaultWalletId + [[deprecated]] WalletsViewModel(const std::shared_ptr& walletsManager, const std::string &defaultWalletId , const std::shared_ptr &sc = nullptr, QObject *parent = nullptr, bool showOnlyRegular = false); + WalletsViewModel(const std::string &defaultWalletId, QObject *parent = nullptr, bool showOnlyRegular = false); ~WalletsViewModel() noexcept override = default; WalletsViewModel(const WalletsViewModel&) = delete; @@ -102,16 +101,19 @@ Q_OBJECT WalletsViewModel(WalletsViewModel&&) = delete; WalletsViewModel& operator = (WalletsViewModel&&) = delete; - std::vector> getWallets(const QModelIndex &index) const; - std::shared_ptr getWallet(const QModelIndex &index) const; + std::vector getWallets(const QModelIndex &index) const; + bs::sync::WalletInfo getWallet(const QModelIndex &index) const; WalletNode *getNode(const QModelIndex &) const; - void setSelectedWallet(const std::shared_ptr &selWallet) { selectedWallet_ = selWallet; } + void setSelectedWallet(const std::string &selWallet) { selectedWalletId_ = selWallet; } - std::shared_ptr selectedWallet() const { return selectedWallet_; } + std::string selectedWallet() const { return selectedWalletId_; } bool showRegularWallets() const { return showRegularWallets_; } std::shared_ptr getAuthWallet() const; - void LoadWallets(bool keepSelection = false); + [[deprecated]] void LoadWallets(bool keepSelection = false); + void onHDWallet(const bs::sync::WalletInfo &); + void onHDWalletDetails(const bs::sync::HDWalletData &); + void onWalletBalances(); //TODO: add arg void setBitcoinLeafSelectionMode(bool flag = true) { bitcoinLeafSelectionMode_ = flag; } @@ -125,8 +127,10 @@ Q_OBJECT QModelIndex parent(const QModelIndex &child) const override; bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; + signals: void updateAddresses(); + void needHDWalletDetails(const std::string &walletId); private slots: void onWalletChanged(); @@ -162,7 +166,7 @@ private slots: private: std::shared_ptr walletsManager_; std::shared_ptr signContainer_; - std::shared_ptr selectedWallet_; + std::string selectedWalletId_; std::shared_ptr rootNode_; std::string defaultWalletId_; bool showRegularWallets_; diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 9e39352c3..39f4bac55 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -220,6 +220,30 @@ void WalletsWidget::init(const std::shared_ptr &logger connect(walletsManager_.get(), &bs::sync::WalletsManager::walletsSynchronized, this, &WalletsWidget::onWalletsSynchronized, Qt::QueuedConnection); } +void WalletsWidget::init(const std::shared_ptr &logger) +{ + logger_ = logger; + +// const auto &defWallet = walletsManager_->getDefaultWallet(); +// InitWalletsView(defWallet ? defWallet->walletId() : std::string{}); + +/* auto filter = appSettings_->get(ApplicationSettings::WalletFiltering); + + ui_->pushButtonEmpty->setChecked(filter & AddressSortFilterModel::HideEmpty); + ui_->pushButtonInternal->setChecked(filter & AddressSortFilterModel::HideInternal); + ui_->pushButtonExternal->setChecked(filter & AddressSortFilterModel::HideExternal); + ui_->pushButtonUsed->setChecked(filter & AddressSortFilterModel::HideUsedEmpty); + + updateAddressFilters(filter);*/ + + InitWalletsView({}); + for (auto button : { ui_->pushButtonEmpty, ui_->pushButtonInternal, + ui_->pushButtonExternal, ui_->pushButtonUsed }) { + connect(button, &QPushButton::toggled, this, &WalletsWidget::onFilterSettingsChanged); + } +} + + void WalletsWidget::setUsername(const QString& username) { username_ = username; @@ -227,7 +251,14 @@ void WalletsWidget::setUsername(const QString& username) void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) { - walletsModel_ = new WalletsViewModel(walletsManager_, defaultWalletId, signingContainer_, ui_->treeViewWallets); + if (walletsManager_ && signingContainer_) { + walletsModel_ = new WalletsViewModel(walletsManager_, defaultWalletId, signingContainer_, ui_->treeViewWallets); + } + else { + walletsModel_ = new WalletsViewModel(defaultWalletId, ui_->treeViewWallets); + } + connect(walletsModel_, &WalletsViewModel::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); + ui_->treeViewWallets->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui_->treeViewWallets->setModel(walletsModel_); ui_->treeViewWallets->setFocus(Qt::ActiveWindowFocusReason); @@ -236,7 +267,9 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) ui_->treeViewWallets->setExpandsOnDoubleClick(false); // show the column as per BST-1520 //ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnID)); - walletsModel_->LoadWallets(); + if (walletsManager_ && signingContainer_) { + walletsModel_->LoadWallets(); + } connect(ui_->walletPropertiesButton, &QPushButton::clicked, this, &WalletsWidget::showSelectedWalletProperties); connect(ui_->createWalletButton, &QPushButton::clicked, this, &WalletsWidget::onNewWallet); @@ -251,6 +284,11 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) // No need to connect to wallet manager in AddressListModel explicitly in this case // so just put nullptr pointer in function addressModel_ = new AddressListModel(nullptr, this); + connect(addressModel_, &AddressListModel::needExtAddresses, this, &WalletsWidget::needExtAddresses); + connect(addressModel_, &AddressListModel::needIntAddresses, this, &WalletsWidget::needIntAddresses); + connect(addressModel_, &AddressListModel::needUsedAddresses, this, &WalletsWidget::needUsedAddresses); + connect(addressModel_, &AddressListModel::needAddrComments, this, &WalletsWidget::needAddrComments); + addressSortFilterModel_ = new AddressSortFilterModel(this); addressSortFilterModel_->setSourceModel(addressModel_); addressSortFilterModel_->setSortRole(AddressListModel::SortRole); @@ -264,7 +302,9 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) updateAddresses(); connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WalletsWidget::updateAddresses); connect(walletsModel_, &WalletsViewModel::updateAddresses, this, &WalletsWidget::updateAddresses); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &WalletsWidget::onWalletBalanceChanged, Qt::QueuedConnection); + if (walletsManager_) { + connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &WalletsWidget::onWalletBalanceChanged, Qt::QueuedConnection); + } connect(ui_->treeViewAddresses->model(), &QAbstractItemModel::layoutChanged, this, &WalletsWidget::treeViewAddressesLayoutChanged); connect(ui_->treeViewAddresses->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WalletsWidget::treeViewAddressesSelectionChanged); @@ -283,19 +323,19 @@ WalletNode *WalletsWidget::getSelectedNode() const return nullptr; } -std::vector> WalletsWidget::getSelectedWallets() const +std::vector WalletsWidget::getSelectedWallets() const { const auto node = getSelectedNode(); - return node ? node->wallets() : std::vector>(); + return node ? node->wallets() : std::vector{}; } -std::shared_ptr WalletsWidget::getSelectedHdWallet() const +bs::sync::WalletInfo WalletsWidget::getSelectedHdWallet() const { const auto node = getSelectedNode(); - return node ? node->hdWallet() : nullptr; + return node ? node->hdWallet() : bs::sync::WalletInfo{}; } -std::vector> WalletsWidget::getFirstWallets() const +std::vector WalletsWidget::getFirstWallets() const { if (walletsModel_->rowCount()) { return walletsModel_->getWallets(walletsModel_->index(0, 0)); @@ -304,6 +344,21 @@ std::vector> WalletsWidget::getFirstWallets() } } +void WalletsWidget::onHDWallet(const bs::sync::WalletInfo &wi) +{ + walletsModel_->onHDWallet(wi); +} + +void WalletsWidget::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) +{ + walletsModel_->onHDWalletDetails(hdWallet); +} + +void WalletsWidget::onAddresses(const std::string &walletId, const std::vector &addrs) +{ + addressModel_->onAddresses(walletId, addrs); +} + void WalletsWidget::showSelectedWalletProperties() { auto indexes = ui_->treeViewWallets->selectionModel()->selectedIndexes(); @@ -325,7 +380,7 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) } const auto &hdWallet = node->hdWallet(); - if (hdWallet != nullptr) { + if (!hdWallet.id.empty()) { RootWalletPropertiesDialog(logger_, hdWallet, walletsManager_, armory_, signingContainer_ , walletsModel_, appSettings_, connectionManager_, assetManager_, this).exec(); } @@ -513,7 +568,7 @@ void WalletsWidget::onWalletBalanceChanged(std::string walletId) const auto &selectedWallets = getSelectedWallets(); bool changedSelected = false; for (const auto &wallet : selectedWallets) { - if (wallet->walletId() == walletId) { + if (wallet.id == walletId) { changedSelected = true; break; } diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index fa03be787..9eed6a613 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -16,6 +16,7 @@ #include #include #include "Address.h" +#include "SignerDefs.h" #include "TabWithShortcut.h" #include "BSErrorCode.h" #include "BSErrorCodeStrings.h" @@ -57,7 +58,7 @@ Q_OBJECT WalletsWidget(QWidget* parent = nullptr ); ~WalletsWidget() override; - void init(const std::shared_ptr &logger + [[deprecated]] void init(const std::shared_ptr &logger , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -66,17 +67,23 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr &); + void init(const std::shared_ptr &logger); + void setUsername(const QString& username); WalletNode *getSelectedNode() const; - std::vector> getSelectedWallets() const; - std::vector> getFirstWallets() const; - std::shared_ptr getSelectedHdWallet() const; + std::vector getSelectedWallets() const; + std::vector getFirstWallets() const; + bs::sync::WalletInfo getSelectedHdWallet() const; void CreateNewWallet(); void ImportNewWallet(); void ImportHwWallet(); + void onHDWallet(const bs::sync::WalletInfo &); + void onHDWalletDetails(const bs::sync::HDWalletData &); + void onAddresses(const std::string &walletId, const std::vector &); + void shortcutActivated(ShortcutType s) override; public slots: @@ -96,6 +103,11 @@ public slots: signals: void showContextMenu(QMenu *, QPoint); void newWalletCreationRequest(); + void needHDWalletDetails(const std::string &walletId); + void needExtAddresses(std::string walletId); + void needIntAddresses(std::string walletId); + void needUsedAddresses(std::string walletId); + void needAddrComments(std::string walletId, const std::vector &); private slots: void showWalletProperties(const QModelIndex& index); @@ -141,7 +153,7 @@ private slots: std::shared_ptr curWallet_; unsigned int revokeReqId_ = 0; QString username_; - std::vector> prevSelectedWallets_; + std::vector prevSelectedWallets_; int prevSelectedWalletRow_{-1}; int prevSelectedAddressRow_{-1}; QPoint walletsScrollPos_; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 9913c6040..1f6684454 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -177,8 +177,8 @@ bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &re if (SignerConnectionExists(localSignerHost, localSignerPort)) { logger_->error("[{}] failed to bind on local port {}", __func__, response.local_port()); SignerMessage msg; - auto msgError = msg.mutable_error(); - msgError->set_code((int)bs::error::ErrorCode::InternalError); + auto msgError = msg.mutable_state(); + msgError->set_code((int)SignContainer::SocketFailed); msgError->set_text("failed to bind local port"); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; return pushFill(env); @@ -218,7 +218,7 @@ bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &re void SignerAdapter::connError(SignContainer::ConnectionError errCode, const QString &errMsg) { SignerMessage msg; - auto msgErr = msg.mutable_error(); + auto msgErr = msg.mutable_state(); msgErr->set_code((int)errCode); msgErr->set_text(errMsg.toStdString()); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; @@ -228,9 +228,9 @@ void SignerAdapter::connError(SignContainer::ConnectionError errCode, const QStr void SignerAdapter::connTorn() { SignerMessage msg; - auto msgErr = msg.mutable_error(); - msgErr->set_code((int)SignContainer::ConnectionError::SignerGoesOffline); - msgErr->set_text("disconnected"); + auto msgState = msg.mutable_state(); + msgState->set_code((int)SignContainer::ConnectionError::SignerGoesOffline); + msgState->set_text("disconnected"); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); } @@ -254,7 +254,8 @@ void SignerAdapter::walletsChanged() void SignerAdapter::onReady() { SignerMessage msg; - msg.mutable_ready(); + auto msgState = msg.mutable_state(); + msgState->set_code((int)SignContainer::Ready); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); } diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index ec9464c5a..9508661b1 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -143,12 +143,23 @@ void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) } } -void MainWindow::onSignerStateChanged(int state) +void MainWindow::onSignerStateChanged(int state, const std::string &details) { if (statusBarView_) { + statusBarView_->onSignerStatusChanged(static_cast(state) + , QString::fromStdString(details)); } } +void MainWindow::onHDWallet(const bs::sync::WalletInfo &wi) +{ + ui_->widgetWallets->onHDWallet(wi); +} + +void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) +{ + ui_->widgetWallets->onHDWalletDetails(hdWallet); +} void MainWindow::showStartupDialog(bool showLicense) { @@ -475,11 +486,11 @@ void MainWindow::onSend() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallet = ui_->widgetWallets->getSelectedHdWallet(); - if (!wallet) { + if (wallet.id.empty()) { // wallet = walletsMgr_->getPrimaryWallet(); } - if (wallet) { -// selectedWalletId = wallet->walletId(); + if (!wallet.id.empty()) { + selectedWalletId = wallet.id; } } @@ -898,7 +909,14 @@ void MainWindow::onSignerVisibleChanged() void MainWindow::initWidgets() { -// InitWalletsView(); + ui_->widgetWallets->init(logger_); +// connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &MainWindow::createNewWallet); + connect(ui_->widgetWallets, &WalletsWidget::needHDWalletDetails, this, &MainWindow::needHDWalletDetails); + connect(ui_->widgetWallets, &WalletsWidget::needExtAddresses, this, &MainWindow::needExtAddresses); + connect(ui_->widgetWallets, &WalletsWidget::needIntAddresses, this, &MainWindow::needIntAddresses); + connect(ui_->widgetWallets, &WalletsWidget::needUsedAddresses, this, &MainWindow::needUsedAddresses); + connect(ui_->widgetWallets, &WalletsWidget::needAddrComments, this, &MainWindow::needAddrComments); + // InitPortfolioView(); // ui_->widgetRFQ->initWidgets(mdProvider_, mdCallbacks_, applicationSettings_); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index e851350dc..c59ec32e8 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -14,6 +14,8 @@ #include #include #include +#include "Address.h" +#include "SignerDefs.h" namespace spdlog { class logger; @@ -51,7 +53,10 @@ namespace bs { void showStartupDialog(bool showLic); void onArmoryStateChanged(int state, unsigned int blockNum); - void onSignerStateChanged(int state); + void onSignerStateChanged(int state, const std::string &); + + void onHDWallet(const bs::sync::WalletInfo &); + void onHDWalletDetails(const bs::sync::HDWalletData &); public slots: void onReactivate(); @@ -65,6 +70,15 @@ namespace bs { Connected, };*/ + signals: + void createNewWallet(); + void needHDWalletDetails(const std::string &walletId); + + void needExtAddresses(std::string walletId); + void needIntAddresses(std::string walletId); + void needUsedAddresses(std::string walletId); + void needAddrComments(std::string walletId, const std::vector &); + private slots: void onSend(); void onGenerateAddress(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 7323baf02..af4a9a205 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -22,6 +22,7 @@ #include #include #include +#include "Address.h" #include "AppNap.h" #include "BSMessageBox.h" #include "BSTerminalSplashScreen.h" @@ -34,8 +35,9 @@ using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; -Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult); +Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult) Q_DECLARE_METATYPE(std::string) +Q_DECLARE_METATYPE(std::vector) #if defined (Q_OS_MAC) class MacOsApp : public QApplication @@ -111,6 +113,7 @@ static QScreen *getDisplay(QPoint position) QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) : QObject(nullptr), logger_(logger) , userSettings_(std::make_shared(TerminalUsers::Settings)) + , userWallets_(std::make_shared(TerminalUsers::Wallets)) {} QtGuiAdapter::~QtGuiAdapter() @@ -182,6 +185,7 @@ void QtGuiAdapter::run(int &argc, char **argv) qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType(); + qRegisterMetaType>(); QString logoIcon; logoIcon = QLatin1String(":/SPLASH_LOGO"); @@ -194,6 +198,7 @@ void QtGuiAdapter::run(int &argc, char **argv) splashScreen_->show(); mainWindow_ = new bs::gui::qt::MainWindow(logger_, queue_, user_); + makeMainWinConnections(); updateStates(); requestInitialSettings(); @@ -221,6 +226,8 @@ bool QtGuiAdapter::process(const Envelope &env) return processSettings(env); case TerminalUsers::Blockchain: return processBlockchain(env); + case TerminalUsers::Signer: + return processSigner(env); case TerminalUsers::Wallets: return processWallets(env); case TerminalUsers::AuthEid: @@ -354,6 +361,35 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) return true; } +bool QtGuiAdapter::processSigner(const Envelope &env) +{ + SignerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[QtGuiAdapter::processSigner] failed to parse msg #{}" + , env.id); + if (!env.receiver) { + logger_->debug("[{}] no receiver", __func__); + } + return true; + } + switch (msg.data_case()) { + case SignerMessage::kState: + signerState_ = msg.state().code(); + signerDetails_ = msg.state().text(); + if (mainWindow_) { + QMetaObject::invokeMethod(mainWindow_, [this]{ + mainWindow_->onSignerStateChanged(signerState_, signerDetails_); + }); + } + break; + case SignerMessage::kNeedNewWalletPrompt: + createWallet(true); + break; + default: break; + } + return true; +} + bool QtGuiAdapter::processWallets(const Envelope &env) { WalletsMessage msg; @@ -366,6 +402,18 @@ bool QtGuiAdapter::processWallets(const Envelope &env) loadingComponents_.insert(env.sender->value()); updateSplashProgress(); break; + case WalletsMessage::kWalletLoaded: { + const auto &wi = bs::sync::WalletInfo::fromCommonMsg(msg.wallet_loaded()); + processWalletLoaded(wi); + } + break; + case WalletsMessage::kHdWallet: { + const auto &hdw = bs::sync::HDWalletData::fromCommonMessage(msg.hd_wallet()); + QMetaObject::invokeMethod(mainWindow_, [this, hdw] { + mainWindow_->onHDWalletDetails(hdw); + }); + } + break; default: break; } return true; @@ -414,7 +462,10 @@ void QtGuiAdapter::updateStates() mainWindow_->onArmoryStateChanged(armoryState_, blockNum_); } if (signerState_ >= 0) { - mainWindow_->onSignerStateChanged(signerState_); + mainWindow_->onSignerStateChanged(signerState_, signerDetails_); + } + for (const auto &hdWallet : hdWallets_) { + mainWindow_->onHDWallet(hdWallet.second); } } @@ -423,6 +474,14 @@ void QtGuiAdapter::updateSplashProgress() if (!splashScreen_ || createdComponents_.empty()) { return; } +/* std::string l, c; + for (const auto &lc : loadingComponents_) { + l += std::to_string(lc) + " "; + } + for (const auto &cc : createdComponents_) { + c += std::to_string(cc) + " "; + } + logger_->debug("[{}] {}/{}", __func__, l, c);*/ int percent = 100 * loadingComponents_.size() / createdComponents_.size(); QMetaObject::invokeMethod(splashScreen_, [this, percent] { splashScreen_->SetProgress(percent); @@ -469,4 +528,57 @@ void QtGuiAdapter::requestInitialSettings() pushFill(env); } +void QtGuiAdapter::makeMainWinConnections() +{ + connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); + connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); + connect(mainWindow_, &bs::gui::qt::MainWindow::needIntAddresses, this, &QtGuiAdapter::onNeedIntAddresses); + connect(mainWindow_, &bs::gui::qt::MainWindow::needUsedAddresses, this, &QtGuiAdapter::onNeedUsedAddresses); + connect(mainWindow_, &bs::gui::qt::MainWindow::needAddrComments, this, &QtGuiAdapter::onNeedAddrComments); +} + +void QtGuiAdapter::createWallet(bool primary) +{ + logger_->debug("[{}]", __func__); +} + +void QtGuiAdapter::onNeedHDWalletDetails(const std::string &walletId) +{ + WalletsMessage msg; + msg.set_hd_wallet_get(walletId); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedExtAddresses(std::string walletId) +{ + logger_->debug("[{}] {}", __func__, walletId); +} + +void QtGuiAdapter::onNeedIntAddresses(std::string walletId) +{ + logger_->debug("[{}] {}", __func__, walletId); +} + +void QtGuiAdapter::onNeedUsedAddresses(std::string walletId) +{ + logger_->debug("[{}] {}", __func__, walletId); +} + +void QtGuiAdapter::onNeedAddrComments(std::string walletId, const std::vector &) +{ + logger_->debug("[{}] {}", __func__, walletId); +} + +void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) +{ + hdWallets_[wi.id] = wi; + if (mainWindow_) { + QMetaObject::invokeMethod(mainWindow_, [this, wi] { + mainWindow_->onHDWallet(wi); + }); + } +} + + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 39d317ca9..faca47940 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -13,7 +13,9 @@ #include #include +#include "Address.h" #include "ApiAdapter.h" +#include "SignerDefs.h" #include "ThreadSafeClasses.h" namespace bs { @@ -56,6 +58,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); bool processAdminMessage(const bs::message::Envelope &); bool processBlockchain(const bs::message::Envelope &); + bool processSigner(const bs::message::Envelope &); bool processWallets(const bs::message::Envelope &); bool processAuthEid(const bs::message::Envelope &); bool processOnChainTrack(const bs::message::Envelope &); @@ -65,17 +68,32 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu void splashProgressCompleted(); void updateStates(); + void createWallet(bool primary); + void makeMainWinConnections(); + + void processWalletLoaded(const bs::sync::WalletInfo &); + +private slots: + void onNeedHDWalletDetails(const std::string &walletId); + void onNeedExtAddresses(std::string walletId); + void onNeedIntAddresses(std::string walletId); + void onNeedUsedAddresses(std::string walletId); + void onNeedAddrComments(std::string walletId, const std::vector &); + private: std::shared_ptr logger_; - std::shared_ptr userSettings_; + std::shared_ptr userSettings_, userWallets_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; std::set createdComponents_; std::set loadingComponents_; - int armoryState_{ -1 }; - uint32_t blockNum_{ 0 }; - int signerState_{ -1 }; + int armoryState_{ -1 }; + uint32_t blockNum_{ 0 }; + int signerState_{ -1 }; + std::string signerDetails_; + + std::unordered_map hdWallets_; }; diff --git a/common b/common index 321fe7ca7..d0e1690b4 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 321fe7ca7200304fcb9270a057f1f2543db7ddcb +Subproject commit d0e1690b49e06959211c468f392cc27a2558eb5f From 04c676abae7cbd0855dc8537f99379bdaaaeae2f Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Mon, 7 Sep 2020 09:37:31 +0300 Subject: [PATCH 005/146] Another build fix --- GUI/QtWidgets/MainWindow.cpp | 3 --- common | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 9508661b1..b5acdac67 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -331,9 +331,6 @@ void MainWindow::setupInfoWidget() connect(ui_->introductionBtn, &QPushButton::clicked, this, []() { QDesktopServices::openUrl(QUrl(QLatin1String(""))); }); - connect(ui_->setUpBtn, &QPushButton::clicked, this, []() { - QDesktopServices::openUrl(QUrl(QLatin1String("https://youtu.be/bvGNi6sBkTo"))); - }); connect(ui_->closeBtn, &QPushButton::clicked, this, [this]() { ui_->infoWidget->setVisible(false); // applicationSettings_->set(ApplicationSettings::ShowInfoWidget, false); diff --git a/common b/common index d0e1690b4..309b3e5b4 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit d0e1690b49e06959211c468f392cc27a2558eb5f +Subproject commit 309b3e5b49f33916726abe131086ce33289a2b68 From 737067baaf63d0175b5a1325aa1a34db5dc26855 Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Tue, 8 Sep 2020 21:06:30 +0300 Subject: [PATCH 006/146] Wallet balances --- BlockSettleUILib/AddressListModel.cpp | 99 +++++++++- BlockSettleUILib/AddressListModel.h | 17 +- .../RootWalletPropertiesDialog.cpp | 43 +++++ .../RootWalletPropertiesDialog.h | 6 +- BlockSettleUILib/WalletsViewModel.cpp | 57 ++++-- BlockSettleUILib/WalletsWidget.cpp | 99 ++++++++-- BlockSettleUILib/WalletsWidget.h | 9 +- GUI/QtWidgets/MainWindow.cpp | 38 +++- GUI/QtWidgets/MainWindow.h | 5 + GUI/QtWidgets/QtGuiAdapter.cpp | 171 +++++++++++++++++- GUI/QtWidgets/QtGuiAdapter.h | 9 + common | 2 +- 12 files changed, 497 insertions(+), 58 deletions(-) diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index 087161af3..f52f2b44c 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -160,9 +160,9 @@ void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) if (wallet.type == bs::core::wallet::Type::Authentication) { const auto addr = bs::Address(); auto row = createRow(addr, wallet); - beginResetModel(); + beginInsertRows(QModelIndex(), addressRows_.size(), addressRows_.size()); addressRows_.emplace_back(std::move(row)); - endResetModel(); + endInsertRows(); } else { if ((wallets_.size() > 1) && (wallet.type == bs::core::wallet::Type::ColorCoin)) { return; // don't populate PM addresses when multiple wallets selected @@ -209,11 +209,23 @@ void AddressListModel::onAddresses(const std::string &walletId if ((itWallet == wallets_.cend()) || addrs.empty()) { return; } + const auto &itWalletBal = pooledBalances_.find(walletId); beginInsertRows(QModelIndex(), addressRows_.size() , addressRows_.size() + addrs.size() - 1); for (const auto &addr : addrs) { auto row = createRow(addr, *itWallet); row.addrIndex = addressRows_.size(); + if (itWalletBal != pooledBalances_.end()) { + const auto &itAddrBal = itWalletBal->second.find(addr.id()); + if (itAddrBal == itWalletBal->second.end()) { + row.balance = 0; + row.transactionCount = 0; + } + else { + row.balance = itAddrBal->second.balance; + row.transactionCount = itAddrBal->second.txn; + } + } indexByAddr_[addr.id()] = row.addrIndex; addressRows_.push_back(std::move(row)); } @@ -224,7 +236,67 @@ void AddressListModel::onAddresses(const std::string &walletId void AddressListModel::onAddressComments(const std::string &walletId , const std::map &comments) { - //TODO: implement + const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() + , [walletId](const bs::sync::WalletInfo &wallet) { + return (wallet.id == walletId); + }); + if ((itWallet == wallets_.cend()) || comments.empty()) { + return; + } + for (const auto &comm : comments) { + const auto &itAddr = indexByAddr_.find(comm.first.id()); + if (itAddr == indexByAddr_.end()) { + continue; + } + addressRows_[itAddr->second].comment = QString::fromStdString(comm.second); + emit dataChanged(index(itAddr->second, ColumnComment), index(itAddr->second, ColumnComment)); + } +} + +void AddressListModel::onAddressBalances(const std::string &walletId + , const std::vector &balances) +{ + if (balances.empty()) { + return; + } + const auto &lbdSaveBalToPool = [this, walletId, balances] + { + auto &walletBal = pooledBalances_[walletId]; + for (const auto &bal : balances) { + walletBal[bal.address] = { bal.balTotal, bal.txn }; + } + }; + lbdSaveBalToPool(); + const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() + , [walletId](const bs::sync::WalletInfo &wallet) { + return (wallet.id == walletId); + }); + if (itWallet == wallets_.cend()) { // balances arrived before wallet was set + return; + } + int startRow = INT32_MAX, endRow = 0; + for (const auto &bal : balances) { + const auto &itAddr = indexByAddr_.find(bal.address); + if (itAddr == indexByAddr_.end()) { // wallet was set, but addresses haven't arrived + lbdSaveBalToPool(); + return; + } + startRow = std::min(startRow, itAddr->second); + endRow = std::max(endRow, itAddr->second); + addressRows_[itAddr->second].balance = bal.balTotal; + addressRows_[itAddr->second].transactionCount = bal.txn; + } + for (auto &addrRow : addressRows_) { + if ((addrRow.balance > 0) || (addrRow.transactionCount > 0) + || (addrRow.walletId.toStdString() != walletId)) { + continue; + } + startRow = std::min(startRow, addrRow.addrIndex); + endRow = std::max(endRow, addrRow.addrIndex); + addrRow.balance = 0; + addrRow.transactionCount = 0; + } + emit dataChanged(index(startRow, ColumnTxCount), index(endRow, ColumnBalance)); } void AddressListModel::updateWalletData() @@ -392,6 +464,9 @@ QVariant AddressListModel::dataForRow(const AddressListModel::AddressRow &row, i case AddressListModel::ColumnAddress: return row.getAddress(); case AddressListModel::ColumnBalance: + if (row.balance == UINT64_MAX) { + return {}; + } if (row.wltType == bs::core::wallet::Type::ColorCoin) { if (wallets_.size() == 1) { return UiUtils::displayCCAmount(row.balance); @@ -404,7 +479,12 @@ QVariant AddressListModel::dataForRow(const AddressListModel::AddressRow &row, i return UiUtils::displayAmount(row.balance); } case AddressListModel::ColumnTxCount: - return row.transactionCount; + if (row.transactionCount >= 0) { + return row.transactionCount; + } + else { + return tr("Loading..."); + } case AddressListModel::ColumnComment: return row.getComment(); case AddressListModel::ColumnWallet: @@ -448,6 +528,17 @@ QVariant AddressListModel::data(const QModelIndex& index, int role) const case AddressRole: return row.displayedAddress; + case AddressCommentRole: + return row.comment; + + case WalletTypeRole: + for (const auto &wallet : wallets_) { + if (wallet.id == row.walletId.toStdString()) { + return static_cast(wallet.type); + } + } + return 0; + case Qt::ToolTipRole: if ((index.column() == ColumnComment) && row.isMultiLineComment()) { return row.comment; diff --git a/BlockSettleUILib/AddressListModel.h b/BlockSettleUILib/AddressListModel.h index 797a0bef8..3884a6c8a 100644 --- a/BlockSettleUILib/AddressListModel.h +++ b/BlockSettleUILib/AddressListModel.h @@ -34,7 +34,6 @@ class AddressListModel : public QAbstractTableModel public: struct AddressRow { -// std::shared_ptr wallet; bs::Address address; QByteArray bytes; int transactionCount = 0; @@ -43,7 +42,7 @@ class AddressListModel : public QAbstractTableModel QString displayedAddress; QString walletName; QString walletId; - size_t addrIndex = 0; + int addrIndex = 0; bs::core::wallet::Type wltType = bs::core::wallet::Type::Unknown; bool isExternal; @@ -71,7 +70,9 @@ class AddressListModel : public QAbstractTableModel WalletIdRole, AddrIndexRole, AddressRole, - IsExternalRole + AddressCommentRole, + IsExternalRole, + WalletTypeRole }; enum AddressType { @@ -109,15 +110,21 @@ class AddressListModel : public QAbstractTableModel private slots: void updateWallets(); // deprecated // void updateData(const std::string &walletId); - void removeEmptyIntAddresses(); + void removeEmptyIntAddresses(); // deprecated private: - std::shared_ptr walletsMgr_; + [[deprecated]] std::shared_ptr walletsMgr_; Wallets wallets_; std::vector addressRows_; const AddressType addrType_; std::map indexByAddr_; + struct AddrBalance { + uint64_t balance; + uint32_t txn; + }; + std::unordered_map> pooledBalances_; + std::atomic_bool processing_; bool filterBtcOnly_{false}; ValidityFlag validityFlag_; diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index e93d4977b..1a031f0f0 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -117,6 +117,49 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrtreeViewWallets->expandAll(); } +RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptr &logger + , const bs::sync::WalletInfo &wallet + , WalletsViewModel *walletsModel + , QWidget* parent) + : QDialog(parent) + , ui_(new Ui::WalletPropertiesDialog()) + , wallet_(wallet) + , walletInfo_(wallet) + , logger_(logger) +{ + ui_->setupUi(this); + + ui_->labelEncRank->clear(); + + walletFilter_ = new CurrentWalletFilter(wallet.id, this); + walletFilter_->setSourceModel(walletsModel); + ui_->treeViewWallets->setModel(walletFilter_); + + connect(walletsModel, &WalletsViewModel::modelReset, + this, &RootWalletPropertiesDialog::onModelReset); + + ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnDescription)); + ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnState)); + ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnSpendableBalance)); + ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnUnconfirmedBalance)); + ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnNbAddresses)); + ui_->treeViewWallets->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &RootWalletPropertiesDialog::onWalletSelected); + + connect(ui_->deleteButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onDeleteWallet); + connect(ui_->backupButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onBackupWallet); + connect(ui_->manageEncryptionButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onChangePassword); + connect(ui_->rescanButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onRescanBlockchain); + + updateWalletDetails(wallet_); + + ui_->manageEncryptionButton->setEnabled(false); + + ui_->treeViewWallets->expandAll(); +} + RootWalletPropertiesDialog::~RootWalletPropertiesDialog() = default; void RootWalletPropertiesDialog::onDeleteWallet() diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 3b5481b58..5b9bc62d9 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -64,9 +64,9 @@ Q_OBJECT signals: void startRescan(std::string walletId); - void needHDWalletDetails(std::string walletId); - void needWalletBalances(std::string walletId); - void needSpendableUTXOs(std::string walletId); + void needHDWalletDetails(const std::string &walletId); + void needWalletBalances(const std::string &walletId); + void needSpendableUTXOs(const std::string &walletId); private slots: void onDeleteWallet(); diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index df6226fb8..d1890fab7 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -44,13 +44,8 @@ WalletNode *WalletNode::child(int index) const WalletNode *WalletNode::findByWalletId(const std::string &walletId) { - const auto &tmpWallets = wallets(); - if ((type() == Type::Leaf) && !tmpWallets.empty()) { - for (const auto &wallet : tmpWallets) { - if (wallet.id == walletId) { - return this; - } - } + if (id() == walletId) { + return this; } for (const auto &child : children_) { auto node = child->findByWalletId(walletId); @@ -150,14 +145,6 @@ class WalletRootNode : public WalletNode size_t getNbUsedAddresses() const { return nbAddr_; } bs::sync::WalletInfo hdWallet() const override { return hdWallet_; } -protected: - std::string desc_; - std::atomic balTotal_, balUnconf_, balSpend_; - size_t nbAddr_; - bs::sync::WalletInfo hdWallet_; - std::vector wallets_; - -protected: void updateCounters(WalletRootNode *node) { if (!node) { return; @@ -176,6 +163,14 @@ class WalletRootNode : public WalletNode } } +protected: + std::string desc_; + std::atomic balTotal_, balUnconf_, balSpend_; + size_t nbAddr_; + bs::sync::WalletInfo hdWallet_; + std::vector wallets_; + +protected: QString displayAmountOrLoading(BTCNumericTypes::balance_type balance) const { if (balance < 0) { return QObject::tr("Loading..."); @@ -245,6 +240,15 @@ class WalletLeafNode : public WalletRootNode }); } + void setBalances(size_t nbAddr, BTCNumericTypes::balance_type total + , BTCNumericTypes::balance_type spendable, BTCNumericTypes::balance_type unconfirmed) + { + nbAddr_ = nbAddr; + balTotal_ = total; + balSpend_ = spendable; + balUnconf_ = unconfirmed; + } + std::vector wallets() const override { return { wallet_ }; @@ -607,7 +611,7 @@ void WalletsViewModel::onHDWallet(const bs::sync::WalletInfo &wi) , createIndex(row, (int)WalletColumns::ColumnCount - 1, static_cast(rootNode_.get()))); } else { - beginInsertRows(createIndex(rootNode_->row(), 0, static_cast(rootNode_.get())) + beginInsertRows(QModelIndex() , rootNode_->nbChildren(), rootNode_->nbChildren()); rootNode_->add(hdNode); endInsertRows(); @@ -673,6 +677,27 @@ void WalletsViewModel::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) } } +void WalletsViewModel::onWalletBalances(const bs::sync::WalletBalanceData &wbd) +{ + auto node = rootNode_->findByWalletId(wbd.id); + auto leafNode = dynamic_cast(node); + if (!node || !leafNode) { + return; + } + leafNode->setBalances(wbd.nbAddresses, wbd.balTotal, wbd.balSpendable, wbd.balUnconfirmed); + node = leafNode->parent(); + emit dataChanged(createIndex(leafNode->row(), (int)WalletColumns::ColumnSpendableBalance, static_cast(node)) + , createIndex(leafNode->row(), (int)WalletColumns::ColumnNbAddresses, static_cast(node))); + + auto groupNode = dynamic_cast(node); + if (!groupNode) { + return; + } + groupNode->updateCounters(leafNode); + emit dataChanged(createIndex(groupNode->row(), (int)WalletColumns::ColumnSpendableBalance, static_cast(groupNode->parent())) + , createIndex(leafNode->row(), (int)WalletColumns::ColumnNbAddresses, static_cast(groupNode->parent()))); +} + void WalletsViewModel::LoadWallets(bool keepSelection) { const auto treeView = qobject_cast(QObject::parent()); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index c4126b64f..0cd1de6f0 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -352,6 +352,9 @@ void WalletsWidget::onHDWallet(const bs::sync::WalletInfo &wi) void WalletsWidget::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { + if (rootDlg_) { + //TODO: pass wallet details to currently running root wallet dialog + } walletsModel_->onHDWalletDetails(hdWallet); for (int i = 0; i < walletsModel_->rowCount(); ++i) { @@ -372,10 +375,18 @@ void WalletsWidget::onAddresses(const std::string &walletId, const std::vector &comments) { - logger_->debug("[{}] {} comments", __func__, comments.size()); addressModel_->onAddressComments(walletId, comments); } +void WalletsWidget::onWalletBalance(const bs::sync::WalletBalanceData &wbd) +{ + if (rootDlg_) { + //TODO: pass data to root wallet dialog + } + walletsModel_->onWalletBalances(wbd); + addressModel_->onAddressBalances(wbd.id, wbd.addrBalances); +} + void WalletsWidget::showSelectedWalletProperties() { auto indexes = ui_->treeViewWallets->selectionModel()->selectedIndexes(); @@ -398,8 +409,21 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) const auto &hdWallet = node->hdWallet(); if (!hdWallet.id.empty()) { - RootWalletPropertiesDialog(logger_, hdWallet, walletsManager_, armory_, signingContainer_ - , walletsModel_, appSettings_, connectionManager_, assetManager_, this).exec(); + if (walletsManager_ && armory_ && signingContainer_ && appSettings_ && connectionManager_ && assetManager_) { + RootWalletPropertiesDialog(logger_, hdWallet, walletsManager_, armory_, signingContainer_ + , walletsModel_, appSettings_, connectionManager_, assetManager_, this).exec(); + } + else { + rootDlg_ = new RootWalletPropertiesDialog(logger_, hdWallet, walletsModel_, this); + connect(rootDlg_, &RootWalletPropertiesDialog::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); + connect(rootDlg_, &RootWalletPropertiesDialog::needWalletBalances, this, &WalletsWidget::needWalletBalances); + connect(rootDlg_, &QDialog::finished, [this](int) { + rootDlg_->deleteLater(); + rootDlg_ = nullptr; + }); + rootDlg_->setModal(true); + rootDlg_->show(); + } } } @@ -433,33 +457,61 @@ void WalletsWidget::onAddressContextMenu(const QPoint &p) curAddress_.clear(); return; } - curWallet_ = walletsManager_->getWalletByAddress(curAddress_); + if (walletsManager_) { + curWallet_ = walletsManager_->getWalletByAddress(curAddress_); + + if (!curWallet_) { + logger_->warn("Failed to find wallet for address {}", curAddress_.display()); + return; + } + auto contextMenu = new QMenu(this); + + if ((curWallet_->type() == bs::core::wallet::Type::Bitcoin) || (getSelectedWallets().size() == 1)) { + contextMenu->addAction(actCopyAddr_); + } + contextMenu->addAction(actEditComment_); + + const auto &cbAddrBalance = [this, p, contextMenu](std::vector balances) + { + if (/*(curWallet_ == walletsManager_->getSettlementWallet()) &&*/ walletsManager_->getAuthWallet() + /*&& (curWallet_->getAddrTxN(curAddress_) == 1)*/ && balances[0]) { + contextMenu->addAction(actRevokeSettl_); + } + emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); + }; + + auto balanceVec = curWallet_->getAddrBalance(curAddress_); + if (balanceVec.size() == 0) + emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); + else + cbAddrBalance(balanceVec); + return; + } - if (!curWallet_) { + curWalletId_ = addressModel_->data(addressIndex, AddressListModel::Role::WalletIdRole).toString().toStdString(); + if (curWalletId_.empty()) { logger_->warn("Failed to find wallet for address {}", curAddress_.display()); return; } + curComment_ = addressModel_->data(addressIndex, AddressListModel::Role::AddressCommentRole).toString().toStdString(); auto contextMenu = new QMenu(this); - if ((curWallet_->type() == bs::core::wallet::Type::Bitcoin) || (getSelectedWallets().size() == 1)) { + if ((static_cast(addressModel_->data(addressIndex, AddressListModel::Role::WalletTypeRole).toInt()) + == bs::core::wallet::Type::Bitcoin) || (getSelectedWallets().size() == 1)) { contextMenu->addAction(actCopyAddr_); } contextMenu->addAction(actEditComment_); - const auto &cbAddrBalance = [this, p, contextMenu](std::vector balances) + const auto &cbAddrBalance = [this, p, contextMenu](std::vector balances) { - if (/*(curWallet_ == walletsManager_->getSettlementWallet()) &&*/ walletsManager_->getAuthWallet() - /*&& (curWallet_->getAddrTxN(curAddress_) == 1)*/ && balances[0]) { + if (/*walletsManager_->getAuthWallet() && balances[0]*/false) { //FIXME contextMenu->addAction(actRevokeSettl_); } emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); }; - auto balanceVec = curWallet_->getAddrBalance(curAddress_); - if (balanceVec.size() == 0) - emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); - else - cbAddrBalance(balanceVec); + //TODO: get address balance and add revoke action if needed + contextMenu ->exec(ui_->treeViewAddresses->mapToGlobal(p)); } void WalletsWidget::onShowContextMenu(QMenu *menu, QPoint where) @@ -718,17 +770,28 @@ void WalletsWidget::onCopyAddress() void WalletsWidget::onEditAddrComment() { - if (!curWallet_ || curAddress_.empty()) { + if ((!curWallet_ && curWalletId_.empty()) || curAddress_.empty()) { return; } bool isOk = false; - const auto oldComment = curWallet_->getAddressComment(curAddress_); + std::string oldComment; + if (curWallet_) { + oldComment = curWallet_->getAddressComment(curAddress_); + } + else { + oldComment = curComment_; + } const auto comment = QInputDialog::getText(this, tr("Edit Comment") , tr("Enter new comment for address %1:").arg(QString::fromStdString(curAddress_.display())) , QLineEdit::Normal, QString::fromStdString(oldComment), &isOk); if (isOk) { - if (!curWallet_->setAddressComment(curAddress_, comment.toStdString())) { - BSMessageBox(BSMessageBox::critical, tr("Address Comment"), tr("Failed to save comment")).exec(); + if (curWallet_) { + if (!curWallet_->setAddressComment(curAddress_, comment.toStdString())) { + BSMessageBox(BSMessageBox::critical, tr("Address Comment"), tr("Failed to save comment")).exec(); + } + } + else { + emit setAddrComment(curWalletId_, curAddress_, comment.toStdString()); } } } diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 38879d58c..c5f1fa65a 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -46,6 +46,7 @@ class AuthAddressManager; class ConnectionManager; class QAction; class QMenu; +class RootWalletPropertiesDialog; class SignContainer; class WalletNode; class WalletsViewModel; @@ -104,14 +105,17 @@ public slots: bool filterBtcOnly() const; signals: - void showContextMenu(QMenu *, QPoint); + void showContextMenu(QMenu *, QPoint); // deprecated void newWalletCreationRequest(); void needHDWalletDetails(const std::string &walletId); void needWalletBalances(const std::string &walletId); + void needSpendableUTXOs(const std::string &walletId); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); void needUsedAddresses(const std::string &walletId); void needAddrComments(const std::string &walletId, const std::vector &); + void setAddrComment(const std::string &walletId, const bs::Address & + , const std::string &comment); private slots: void showWalletProperties(const QModelIndex& index); @@ -149,12 +153,15 @@ private slots: WalletsViewModel * walletsModel_; AddressListModel * addressModel_; AddressSortFilterModel * addressSortFilterModel_; + RootWalletPropertiesDialog * rootDlg_{ nullptr }; QAction * actCopyAddr_ = nullptr; QAction * actEditComment_ = nullptr; QAction * actRevokeSettl_ = nullptr; //QAction * actDeleteWallet_ = nullptr; bs::Address curAddress_; std::shared_ptr curWallet_; + std::string curWalletId_; + std::string curComment_; unsigned int revokeReqId_ = 0; QString username_; std::vector prevSelectedWallets_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 9a3b7e925..28d97dc29 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -26,6 +26,7 @@ #include #include "ui_BSTerminalMainWindow.h" #include "ApiAdapter.h" +#include "ApplicationSettings.h" #include "BSMessageBox.h" #include "CreateTransactionDialogAdvanced.h" #include "CreateTransactionDialogSimple.h" @@ -136,6 +137,19 @@ void MainWindow::onGetGeometry(const QRect &mainGeom) #endif // not Windows } +void MainWindow::onSetting(int setting, const QVariant &value) +{ + switch (static_cast(setting)) { + case ApplicationSettings::GUI_main_tab: + ui_->tabWidget->setCurrentIndex(value.toInt()); + break; + case ApplicationSettings::ShowInfoWidget: + ui_->infoWidget->setVisible(value.toBool()); + break; + default: break; + } +} + void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) { if (statusBarView_) { @@ -173,6 +187,11 @@ void MainWindow::onAddressComments(const std::string &walletId ui_->widgetWallets->onAddressComments(walletId, comments); } +void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) +{ + ui_->widgetWallets->onWalletBalance(wbd); +} + void MainWindow::showStartupDialog(bool showLicense) { StartupDialog startupDialog(showLicense, this); @@ -232,6 +251,9 @@ bool MainWindow::event(QEvent *event) MainWindow::~MainWindow() { + emit putSetting(static_cast(ApplicationSettings::GUI_main_geometry), geometry()); + emit putSetting(static_cast(ApplicationSettings::GUI_main_tab), ui_->tabWidget->currentIndex()); + NotificationCenter::destroyInstance(); } @@ -333,19 +355,15 @@ void MainWindow::setupIcon() void MainWindow::setupInfoWidget() { - const bool show = true; // applicationSettings_->get(ApplicationSettings::ShowInfoWidget); - ui_->infoWidget->setVisible(show); - -/* if (!show) { - return; - }*/ - connect(ui_->introductionBtn, &QPushButton::clicked, this, []() { - QDesktopServices::openUrl(QUrl(QLatin1String(""))); + QDesktopServices::openUrl(QUrl(QLatin1String("https://www.youtube.com/watch?v=mUqKq9GKjmI"))); + }); + connect(ui_->tutorialsButton, &QPushButton::clicked, this, []() { + QDesktopServices::openUrl(QUrl(QLatin1String("https://blocksettle.com/tutorials"))); }); connect(ui_->closeBtn, &QPushButton::clicked, this, [this]() { ui_->infoWidget->setVisible(false); -// applicationSettings_->set(ApplicationSettings::ShowInfoWidget, false); + emit putSetting(static_cast(ApplicationSettings::ShowInfoWidget), false); }); } @@ -922,10 +940,12 @@ void MainWindow::initWidgets() // connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &MainWindow::createNewWallet); connect(ui_->widgetWallets, &WalletsWidget::needHDWalletDetails, this, &MainWindow::needHDWalletDetails); connect(ui_->widgetWallets, &WalletsWidget::needWalletBalances, this, &MainWindow::needWalletBalances); + connect(ui_->widgetWallets, &WalletsWidget::needSpendableUTXOs, this, &MainWindow::needSpendableUTXOs); connect(ui_->widgetWallets, &WalletsWidget::needExtAddresses, this, &MainWindow::needExtAddresses); connect(ui_->widgetWallets, &WalletsWidget::needIntAddresses, this, &MainWindow::needIntAddresses); connect(ui_->widgetWallets, &WalletsWidget::needUsedAddresses, this, &MainWindow::needUsedAddresses); connect(ui_->widgetWallets, &WalletsWidget::needAddrComments, this, &MainWindow::needAddrComments); + connect(ui_->widgetWallets, &WalletsWidget::setAddrComment, this, &MainWindow::setAddrComment); // InitPortfolioView(); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 85c605473..c40585dc7 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -49,6 +49,7 @@ namespace bs { , const std::shared_ptr < bs::message::User> &); ~MainWindow() override; + void onSetting(int setting, const QVariant &value); void onGetGeometry(const QRect &); void showStartupDialog(bool showLic); @@ -76,14 +77,18 @@ namespace bs { };*/ signals: + void putSetting(int, const QVariant &); void createNewWallet(); void needHDWalletDetails(const std::string &walletId); void needWalletBalances(const std::string &walletId); + void needSpendableUTXOs(const std::string &walletId); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); void needUsedAddresses(const std::string &walletId); void needAddrComments(const std::string &walletId, const std::vector &); + void setAddrComment(const std::string &walletId, const bs::Address & + , const std::string &comment); private slots: void onSend(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 922ba143f..c9d8efc95 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -290,7 +290,56 @@ bool QtGuiAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResp }); } break; - default: break; + + default: { + int idx = setting.request().index(); + QVariant value; + switch (setting.request().type()) { + case SettingType_String: + value = QString::fromStdString(setting.s()); + break; + case SettingType_Int: + value = setting.i(); + break; + case SettingType_UInt: + value = setting.ui(); + break; + case SettingType_UInt64: + value = setting.ui64(); + break; + case SettingType_Bool: + value = setting.b(); + break; + case SettingType_Float: + value = setting.f(); + break; + case SettingType_Rect: + value = QRect(setting.rect().left(), setting.rect().top() + , setting.rect().width(), setting.rect().height()); + break; + case SettingType_Strings: { + QStringList sl; + for (const auto &s : setting.strings().strings()) { + sl << QString::fromStdString(s); + } + value = sl; + } + break; + case SettingType_StrMap: { + QVariantMap vm; + for (const auto &keyVal : setting.key_vals().key_vals()) { + vm[QString::fromStdString(keyVal.key())] = QString::fromStdString(keyVal.value()); + } + value = vm; + } + break; + default: break; + } + QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, idx, value] { + mw->onSetting(idx, value); + }); + } + break; } } return true; @@ -446,6 +495,8 @@ bool QtGuiAdapter::processWallets(const Envelope &env) } break; + case WalletsMessage::kWalletBalances: + return processWalletBalances(env, msg.wallet_balances()); default: break; } return true; @@ -556,17 +607,95 @@ void QtGuiAdapter::requestInitialSettings() setReq->set_index(SetIdx_Initialized); setReq->set_type(SettingType_Bool); + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_GUI_MainTab); + setReq->set_type(SettingType_Int); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_ShowInfoWidget); + setReq->set_type(SettingType_Bool); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } void QtGuiAdapter::makeMainWinConnections() { + connect(mainWindow_, &bs::gui::qt::MainWindow::putSetting, this, &QtGuiAdapter::onPutSetting); connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); + connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSpendableUTXOs, this, &QtGuiAdapter::onNeedSpendableUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needIntAddresses, this, &QtGuiAdapter::onNeedIntAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needUsedAddresses, this, &QtGuiAdapter::onNeedUsedAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needAddrComments, this, &QtGuiAdapter::onNeedAddrComments); + connect(mainWindow_, &bs::gui::qt::MainWindow::setAddrComment, this, &QtGuiAdapter::onSetAddrComment); +} + +void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) +{ + SettingsMessage msg; + auto msgReq = msg.mutable_put_request(); + auto setResp = msgReq->add_responses(); + auto setReq = setResp->mutable_request(); + setReq->set_source(SettingSource_Local); + setReq->set_index(static_cast(idx)); + switch (value.type()) { + case QVariant::Type::String: + setReq->set_type(SettingType_String); + setResp->set_s(value.toString().toStdString()); + break; + case QVariant::Type::Int: + setReq->set_type(SettingType_Int); + setResp->set_i(value.toInt()); + break; + case QVariant::Type::UInt: + setReq->set_type(SettingType_UInt); + setResp->set_ui(value.toUInt()); + break; + case QVariant::Type::ULongLong: + case QVariant::Type::LongLong: + setReq->set_type(SettingType_UInt64); + setResp->set_ui64(value.toULongLong()); + break; + case QVariant::Type::Double: + setReq->set_type(SettingType_Float); + setResp->set_f(value.toDouble()); + break; + case QVariant::Type::Bool: + setReq->set_type(SettingType_Bool); + setResp->set_b(value.toBool()); + break; + case QVariant::Type::Rect: + setReq->set_type(SettingType_Rect); + { + auto setRect = setResp->mutable_rect(); + setRect->set_left(value.toRect().left()); + setRect->set_top(value.toRect().top()); + setRect->set_height(value.toRect().height()); + setRect->set_width(value.toRect().width()); + } + break; + case QVariant::Type::StringList: + setReq->set_type(SettingType_Strings); + for (const auto &s : value.toStringList()) { + setResp->mutable_strings()->add_strings(s.toStdString()); + } + break; + case QVariant::Type::Map: + setReq->set_type(SettingType_StrMap); + for (const auto &key : value.toMap().keys()) { + auto kvData = setResp->mutable_key_vals()->add_key_vals(); + kvData->set_key(key.toStdString()); + kvData->set_value(value.toMap()[key].toString().toStdString()); + } + break; + } + + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); } void QtGuiAdapter::createWallet(bool primary) @@ -590,6 +719,14 @@ void QtGuiAdapter::onNeedWalletBalances(const std::string &walletId) pushFill(env); } +void QtGuiAdapter::onNeedSpendableUTXOs(const std::string &walletId) +{ + WalletsMessage msg; + msg.set_get_spendable_utxos(walletId); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::onNeedExtAddresses(const std::string &walletId) { WalletsMessage msg; @@ -627,6 +764,19 @@ void QtGuiAdapter::onNeedAddrComments(const std::string &walletId pushFill(env); } +void QtGuiAdapter::onSetAddrComment(const std::string &walletId, const bs::Address &addr + , const std::string &comment) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_set_addr_comments(); + msgReq->set_wallet_id(walletId); + auto msgComm = msgReq->add_comments(); + msgComm->set_address(addr.display()); + msgComm->set_comment(comment); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[wi.id] = wi; @@ -637,5 +787,24 @@ void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) } } +bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env + , const BlockSettle::Common::WalletsMessage_WalletBalances &response) +{ + bs::sync::WalletBalanceData wbd; + wbd.id = response.wallet_id(); + wbd.balTotal = response.total_balance(); + wbd.balSpendable = response.spendable_balance(); + wbd.balUnconfirmed = response.unconfirmed_balance(); + wbd.nbAddresses = response.nb_addresses(); + for (const auto &addrBal : response.address_balances()) { + wbd.addrBalances.push_back({ BinaryData::fromString(addrBal.address()) + , addrBal.txn(), addrBal.total_balance(), addrBal.spendable_balance() + , addrBal.unconfirmed_balance() }); + } + QMetaObject::invokeMethod(mainWindow_, [this, wbd] { + mainWindow_->onWalletBalance(wbd); + }); + return true; +} #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 9da29ca9f..ae4cbdb4b 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -26,6 +26,9 @@ namespace bs { } } namespace BlockSettle { + namespace Common { + class WalletsMessage_WalletBalances; + } namespace Terminal { class SettingsMessage_SettingsResponse; } @@ -72,14 +75,20 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu void makeMainWinConnections(); void processWalletLoaded(const bs::sync::WalletInfo &); + bool processWalletBalances(const bs::message::Envelope & + , const BlockSettle::Common::WalletsMessage_WalletBalances &); private slots: + void onPutSetting(int idx, const QVariant &value); void onNeedHDWalletDetails(const std::string &walletId); void onNeedWalletBalances(const std::string &walletId); + void onNeedSpendableUTXOs(const std::string &walletId); void onNeedExtAddresses(const std::string &walletId); void onNeedIntAddresses(const std::string &walletId); void onNeedUsedAddresses(const std::string &walletId); void onNeedAddrComments(const std::string &walletId, const std::vector &); + void onSetAddrComment(const std::string &walletId, const bs::Address & + , const std::string &comment); private: std::shared_ptr logger_; diff --git a/common b/common index 9f312b096..281e4b961 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 9f312b096ae08949adb799bfc420fb56666d99de +Subproject commit 281e4b961ad648c2f1cd8aa3ba87d7cc9d32379a From afab345ce6ba4f0aeedd428fbf49b2ffb1bf5921 Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Fri, 11 Sep 2020 21:44:29 +0300 Subject: [PATCH 007/146] TX list support --- BlockSettleUILib/TransactionsViewModel.cpp | 112 +++++++++++++++++- BlockSettleUILib/TransactionsViewModel.h | 55 ++++++--- BlockSettleUILib/TransactionsWidget.cpp | 48 +++++++- BlockSettleUILib/TransactionsWidget.h | 6 +- .../TransactionsWidgetInterface.cpp | 5 + .../TransactionsWidgetInterface.h | 3 +- GUI/QtWidgets/MainWindow.cpp | 55 +++++++-- GUI/QtWidgets/MainWindow.h | 13 ++ GUI/QtWidgets/QtGuiAdapter.cpp | 102 +++++++++++++++- GUI/QtWidgets/QtGuiAdapter.h | 9 ++ common | 2 +- 11 files changed, 373 insertions(+), 37 deletions(-) diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index ac624ce5e..d23ebccd8 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -279,6 +279,15 @@ TransactionsViewModel::TransactionsViewModel(const std::shared_ptr &logger + , QObject* parent) + : QAbstractItemModel(parent), logger_(logger) + , allWallets_(true), filterAddress_() +{ + stopped_ = std::make_shared(false); + rootNode_.reset(new TXNode); +} + void TransactionsViewModel::init() { stopped_ = std::make_shared(false); @@ -577,6 +586,21 @@ std::shared_ptr TransactionsViewModel::itemFromTransaction return item; } +std::shared_ptr TransactionsViewModel::createTxItem(const bs::TXEntry &entry) +{ + auto item = std::make_shared(); + item->txEntry = entry; + item->displayDateTime = UiUtils::displayDateTime(entry.txTime); +// item->filterAddress = filterAddress_; + item->walletID = QString::fromStdString(*entry.walletIds.cbegin()); + item->confirmations = entry.nbConf; + if (!item->wallets.empty()) { + item->walletName = QString::fromStdString(item->wallets[0]->name()); + } + item->amountStr = UiUtils::displayAmount(entry.value); + return item; +} + void TransactionsViewModel::onZCReceived(const std::string& requestId, const std::vector& entries) { QMetaObject::invokeMethod(this, [this, entries] { updateTransactionsPage(entries); }); @@ -856,7 +880,7 @@ void TransactionsViewModel::updateBlockHeight(const std::vector(Columns::Amount)) - , index(rootNode_->nbChildren() - 1, static_cast(Columns::Status))); + , index(rootNode_->nbChildren() - 1, static_cast(Columns::Status))); } void TransactionsViewModel::onItemConfirmed(const TransactionPtr item) @@ -1065,6 +1089,92 @@ void TransactionsViewModel::updateTransactionDetails(const TransactionPtr &item TransactionsViewItem::initialize(item, armory_, walletsManager_, cbInited); } +void TransactionsViewModel::onNewBlock(unsigned int curBlock) +{ + if (curBlock_ == curBlock) { + return; + } + const unsigned int diff = curBlock - curBlock_; + curBlock_ = curBlock; + + for (const auto &node : rootNode_->children()) { + node->item()->confirmations += diff; + node->item()->txEntry.nbConf += diff; + } + emit dataChanged(index(0, static_cast(Columns::Status)) + , index(rootNode_->nbChildren() - 1, static_cast(Columns::Status))); +} + +void TransactionsViewModel::onLedgerEntries(const std::string &filter, uint32_t totalPages + , uint32_t curPage, uint32_t curBlock, const std::vector &entries) +{ + if (!curBlock_) { + curBlock_ = curBlock; + } + else if (curBlock_ != curBlock) { + curBlock_ = curBlock; + onNewBlock(curBlock); + } + if (entries.empty()) { + return; + } + + beginInsertRows(QModelIndex(), rootNode_->nbChildren() + , rootNode_->nbChildren() + entries.size() - 1); + for (const auto &entry : entries) { + const auto &item = createTxItem(entry); + itemIndex_[{item->txEntry.txHash, item->walletID.toStdString()}] = rootNode_->nbChildren(); + rootNode_->add(new TXNode(item)); + } + endInsertRows(); + + std::vector txWallet; + txWallet.reserve(entries.size()); + for (const auto &entry : entries) { + const auto &walletId = entry.walletIds.empty() ? std::string{} : *(entry.walletIds.cbegin()); + txWallet.push_back({ entry.txHash, walletId, entry.value }); + } //TODO: can be optimized later to retrieve details only for visible rows + emit needTXDetails(txWallet); +} + +void TransactionsViewModel::onTXDetails(const std::vector &txDet) +{ + for (const auto &tx : txDet) { + const auto &itIndex = itemIndex_.find({tx.txHash, tx.walletId}); + if (itIndex == itemIndex_.end()) { + logger_->warn("[{}] invalid TX: {}", __func__, tx.txHash.toHexStr(true)); + continue; + } + const int row = itIndex->second; + if (row >= rootNode_->nbChildren()) { + logger_->warn("[{}] invalid row: {} of {}", __func__, row, rootNode_->nbChildren()); + continue; + } + const auto &item = rootNode_->children()[row]->item(); + item->walletName = QString::fromStdString(tx.walletName); + item->direction = tx.direction; + item->dirStr = QObject::tr(bs::sync::Transaction::toStringDir(tx.direction)); + item->isValid = tx.isValid ? bs::sync::TxValidity::Valid : bs::sync::TxValidity::Invalid; + item->comment = QString::fromStdString(tx.comment); + item->amountStr = QString::fromStdString(tx.amount); + + item->addressCount = tx.outAddresses.size(); + switch (tx.outAddresses.size()) { + case 0: + item->mainAddress = tr("no addresses"); + break; + case 1: + item->mainAddress = QString::fromStdString(tx.outAddresses[0].display()); + break; + default: + item->mainAddress = tr("%1 output addresses").arg(tx.outAddresses.size()); + break; + } + emit dataChanged(index(row, static_cast(Columns::Wallet)) + , index(row, static_cast(Columns::Comment))); + } +} + void TransactionsViewItem::initialize(const TransactionPtr &item, ArmoryConnection *armory , const std::shared_ptr &walletsMgr diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index 503965f99..bcac281cb 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -22,7 +22,7 @@ #include #include "ArmoryConnection.h" #include "AsyncClient.h" -#include "Wallets/SyncWallet.h" +#include "SignerDefs.h" namespace spdlog { class logger; @@ -45,6 +45,7 @@ struct TransactionsViewItem bool initialized = false; QString mainAddress; int addressCount; + std::vector outAddresses; bs::sync::Transaction::Direction direction = bs::sync::Transaction::Unknown; std::vector> wallets; QString dirStr; @@ -56,7 +57,7 @@ struct TransactionsViewItem BTCNumericTypes::balance_type amount = 0; int32_t txOutIndex{-1}; bool txMultipleOutIndex{false}; - bs::sync::TxValidity isValid = bs::sync::TxValidity::Invalid; + bs::sync::TxValidity isValid = bs::sync::TxValidity::Unknown; bool isCPFP = false; int confirmations = 0; @@ -128,17 +129,18 @@ class TransactionsViewModel : public QAbstractItemModel, public ArmoryCallbackTa { Q_OBJECT public: - TransactionsViewModel(const std::shared_ptr & + [[deprecated]] TransactionsViewModel(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &defWlt , const bs::Address &filterAddress = bs::Address() , QObject* parent = nullptr); - TransactionsViewModel(const std::shared_ptr & + [[deprecated]] TransactionsViewModel(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , QObject* parent = nullptr); + TransactionsViewModel(const std::shared_ptr &, QObject* parent = nullptr); ~TransactionsViewModel() noexcept override; TransactionsViewModel(const TransactionsViewModel&) = delete; @@ -149,7 +151,6 @@ Q_OBJECT void loadAllWallets(bool onNewBlock=false); size_t itemsCount() const { return rootNode_->nbChildren(); } -public: int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; @@ -162,9 +163,16 @@ Q_OBJECT TransactionPtr getItem(const QModelIndex &) const; TransactionPtr getOldestItem() const { return oldestItem_; } TXNode *getNode(const QModelIndex &) const; - bool isTxRevocable(const Tx& tx); + void onLedgerEntries(const std::string &filter, uint32_t totalPages + , uint32_t curPage, uint32_t curBlock, const std::vector &); + void onNewBlock(unsigned int topBlock); + void onTXDetails(const std::vector &); + +signals: + void needTXDetails(const std::vector &); + private slots: void updatePage(); void refresh(); @@ -175,21 +183,22 @@ private slots: void onRefreshTxValidity(); private: - void onNewBlock(unsigned int height, unsigned int branchHgt) override; - void onStateChanged(ArmoryState) override; - void onZCReceived(const std::string& requestId, const std::vector &) override; - void onZCInvalidated(const std::set &ids) override; + [[deprecated]] void onNewBlock(unsigned int height, unsigned int branchHgt) override; + [[deprecated]] void onStateChanged(ArmoryState) override; + [[deprecated]] void onZCReceived(const std::string& requestId, const std::vector &) override; + [[deprecated]] void onZCInvalidated(const std::set &ids) override; - void init(); + [[deprecated]] void init(); void clear(); - void loadLedgerEntries(bool onNewBlock=false); - void ledgerToTxData(const std::map> &rawData + [[deprecated]] void loadLedgerEntries(bool onNewBlock=false); + [[deprecated]] void ledgerToTxData(const std::map> &rawData , bool onNewBlock=false); std::pair updateTransactionsPage(const std::vector &); void updateBlockHeight(const std::vector> &); void updateTransactionDetails(const TransactionPtr &item , const std::function &cb); - std::shared_ptr itemFromTransaction(const bs::TXEntry &); + [[deprecated]] std::shared_ptr itemFromTransaction(const bs::TXEntry &); + std::shared_ptr createTxItem(const bs::TXEntry &); signals: void dataLoaded(int count); @@ -232,6 +241,22 @@ private slots: std::shared_ptr stopped_; std::atomic_bool initialLoadCompleted_{ true }; + struct ItemKey { + BinaryData txHash; + std::string walletId; + + bool operator<(const ItemKey &other) const + { + return ((txHash < other.txHash) || ((txHash == other.txHash) + && (walletId < other.walletId))); + } + bool operator==(const ItemKey &other) const + { + return ((txHash == other.txHash) && (walletId == other.walletId)); + } + }; + std::map itemIndex_; + // If set, amount field will show only related address balance changes // (without fees because fees are related to transaction, not address). // Right now used with AddressDetailDialog only. @@ -240,6 +265,8 @@ private slots: // Tx that could be revoked std::map revocableTxs_; + + uint32_t curBlock_{ 0 }; }; #endif // __TRANSACTIONS_VIEW_MODEL_H__ diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index 136be45ba..5e53bcb08 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -293,6 +293,43 @@ void TransactionsWidget::init(const std::shared_ptr &w scheduleDateFilterCheck(); } +void TransactionsWidget::init(const std::shared_ptr &logger + , const std::shared_ptr &model) +{ + TransactionsWidgetInterface::init(logger); + scheduleDateFilterCheck(); + + model_ = model; + connect(model_.get(), &TransactionsViewModel::dataLoaded, this, &TransactionsWidget::onDataLoaded, Qt::QueuedConnection); + connect(model_.get(), &TransactionsViewModel::initProgress, this, &TransactionsWidget::onProgressInited); + connect(model_.get(), &TransactionsViewModel::updateProgress, this, &TransactionsWidget::onProgressUpdated); + + sortFilterModel_ = new TransactionsSortFilterModel(appSettings_, this); + sortFilterModel_->setSourceModel(model.get()); + sortFilterModel_->setDynamicSortFilter(true); + + connect(sortFilterModel_, &TransactionsSortFilterModel::rowsInserted, this, &TransactionsWidget::updateResultCount); + connect(sortFilterModel_, &TransactionsSortFilterModel::rowsRemoved, this, &TransactionsWidget::updateResultCount); + connect(sortFilterModel_, &TransactionsSortFilterModel::modelReset, this, &TransactionsWidget::updateResultCount); + + auto updateDateTimes = [this]() { + sortFilterModel_->updateDates(ui_->dateEditStart->date(), ui_->dateEditEnd->date()); + }; + connect(ui_->dateEditStart, &QDateTimeEdit::dateTimeChanged, updateDateTimes); + connect(ui_->dateEditEnd, &QDateTimeEdit::dateTimeChanged, updateDateTimes); + + connect(ui_->searchField, &QLineEdit::textChanged, [=](const QString& text) { + sortFilterModel_->updateFilters(sortFilterModel_->walletIds, text, sortFilterModel_->transactionDirection); + }); + + ui_->treeViewTransactions->setSortingEnabled(true); + ui_->treeViewTransactions->setModel(sortFilterModel_); + ui_->treeViewTransactions->hideColumn(static_cast(TransactionsViewModel::Columns::TxHash)); + + ui_->treeViewTransactions->sortByColumn(static_cast(TransactionsViewModel::Columns::Date), Qt::DescendingOrder); + ui_->treeViewTransactions->sortByColumn(static_cast(TransactionsViewModel::Columns::Status), Qt::AscendingOrder); +} + void TransactionsWidget::SetTransactionsModel(const std::shared_ptr& model) { model_ = model; @@ -401,9 +438,12 @@ void TransactionsWidget::walletsChanged() QStringList walletIds; int direction; - const auto varList = appSettings_->get(ApplicationSettings::TransactionFilter).toList(); - walletIds = varList.first().toStringList(); - direction = varList.last().toInt(); + const auto varList = appSettings_ ? appSettings_->get(ApplicationSettings::TransactionFilter).toList() + : QVariantList{}; + if (!varList.empty()) { + walletIds = varList.first().toStringList(); + direction = varList.last().toInt(); + } int currentIndex = -1; int primaryWalletIndex = 0; @@ -462,7 +502,7 @@ void TransactionsWidget::walletsChanged() } else { if (walletIds.contains(c_allWalletsId)) { ui_->walletBox->setCurrentIndex(0); - } else { + } else if (walletsManager_) { const auto primaryWallet = walletsManager_->getPrimaryWallet(); if (primaryWallet) { diff --git a/BlockSettleUILib/TransactionsWidget.h b/BlockSettleUILib/TransactionsWidget.h index d9bba4508..82020a861 100644 --- a/BlockSettleUILib/TransactionsWidget.h +++ b/BlockSettleUILib/TransactionsWidget.h @@ -47,13 +47,15 @@ Q_OBJECT TransactionsWidget(QWidget* parent = nullptr ); ~TransactionsWidget() override; - void init(const std::shared_ptr & + [[deprecated]] void init(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr &); - void SetTransactionsModel(const std::shared_ptr &); + void init(const std::shared_ptr & + , const std::shared_ptr &); + [[deprecated]] void SetTransactionsModel(const std::shared_ptr &); void shortcutActivated(ShortcutType s) override; diff --git a/BlockSettleUILib/TransactionsWidgetInterface.cpp b/BlockSettleUILib/TransactionsWidgetInterface.cpp index 9b13b99d2..22c96250d 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.cpp +++ b/BlockSettleUILib/TransactionsWidgetInterface.cpp @@ -67,6 +67,11 @@ void TransactionsWidgetInterface::init(const std::shared_ptr &logger) +{ + logger_ = logger; +} + void TransactionsWidgetInterface::onRevokeSettlement() { auto txItem = model_->getItem(actionRevoke_->data().toModelIndex()); diff --git a/BlockSettleUILib/TransactionsWidgetInterface.h b/BlockSettleUILib/TransactionsWidgetInterface.h index 18baa44b1..2fc4f9e40 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.h +++ b/BlockSettleUILib/TransactionsWidgetInterface.h @@ -38,12 +38,13 @@ class TransactionsWidgetInterface : public TabWithShortcut { explicit TransactionsWidgetInterface(QWidget *parent = nullptr); ~TransactionsWidgetInterface() noexcept override = default; - void init(const std::shared_ptr & + [[deprecated]] void init(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr &); + void init(const std::shared_ptr &); protected slots: void onRevokeSettlement(); diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 28d97dc29..46d25b340 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -39,6 +39,7 @@ #include "StatusBarView.h" #include "TabWithShortcut.h" #include "TerminalMessage.h" +#include "TransactionsViewModel.h" #include "UiUtils.h" #include "terminal.pb.h" @@ -157,6 +158,22 @@ void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) } } +void MainWindow::onNewBlock(int state, unsigned int blockNum) +{ + if (statusBarView_) { + statusBarView_->onBlockchainStateChanged(state, blockNum); + } + if (txModel_) { + txModel_->onNewBlock(blockNum); + } +} + +void MainWindow::onWalletsReady() +{ + logger_->debug("[{}]", __func__); + emit needLedgerEntries({}); +} + void MainWindow::onSignerStateChanged(int state, const std::string &details) { if (statusBarView_) { @@ -192,6 +209,19 @@ void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) ui_->widgetWallets->onWalletBalance(wbd); } +void MainWindow::onLedgerEntries(const std::string &filter, uint32_t totalPages + , uint32_t curPage, uint32_t curBlock, const std::vector &entries) +{ + if (filter.empty()) { + txModel_->onLedgerEntries(filter, totalPages, curPage, curBlock, entries); + } +} + +void MainWindow::onTXDetails(const std::vector &txDet) +{ + txModel_->onTXDetails(txDet); +} + void MainWindow::showStartupDialog(bool showLicense) { StartupDialog startupDialog(showLicense, this); @@ -251,9 +281,6 @@ bool MainWindow::event(QEvent *event) MainWindow::~MainWindow() { - emit putSetting(static_cast(ApplicationSettings::GUI_main_geometry), geometry()); - emit putSetting(static_cast(ApplicationSettings::GUI_main_tab), ui_->tabWidget->currentIndex()); - NotificationCenter::destroyInstance(); } @@ -399,16 +426,18 @@ void MainWindow::initChartsView() } // Initialize widgets related to transactions. -/*void MainWindow::initTransactionsView() +void MainWindow::initTransactionsView() { - ui_->widgetExplorer->init(armory_, logMgr_->logger(), walletsMgr_, ccFileManager_, authManager_); - ui_->widgetTransactions->init(walletsMgr_, armory_, utxoReservationMgr_, signContainer_, applicationSettings_ - , logMgr_->logger("ui")); + txModel_ = std::make_shared(logger_, this); + connect(txModel_.get(), &TransactionsViewModel::needTXDetails, this + , &MainWindow::needTXDetails); + +// ui_->widgetExplorer->init(armory_, logMgr_->logger(), walletsMgr_, ccFileManager_, authManager_); + ui_->widgetTransactions->init(logger_, txModel_); ui_->widgetTransactions->setEnabled(true); - ui_->widgetTransactions->SetTransactionsModel(transactionsModel_); - ui_->widgetPortfolio->SetTransactionsModel(transactionsModel_); -}*/ + ui_->widgetPortfolio->SetTransactionsModel(txModel_); +} void MainWindow::onReactivate() { @@ -808,6 +837,9 @@ void MainWindow::showRunInBackgroundMessage() void MainWindow::closeEvent(QCloseEvent* event) { + emit putSetting(static_cast(ApplicationSettings::GUI_main_geometry), geometry()); + emit putSetting(static_cast(ApplicationSettings::GUI_main_tab), ui_->tabWidget->currentIndex()); + /* if (applicationSettings_->get(ApplicationSettings::closeToTray)) { hide(); event->ignore(); @@ -818,7 +850,7 @@ void MainWindow::closeEvent(QCloseEvent* event) } */ QMainWindow::closeEvent(event); - QApplication::exit(); + QTimer::singleShot(100, [] { QApplication::exit(); }); // } } @@ -947,6 +979,7 @@ void MainWindow::initWidgets() connect(ui_->widgetWallets, &WalletsWidget::needAddrComments, this, &MainWindow::needAddrComments); connect(ui_->widgetWallets, &WalletsWidget::setAddrComment, this, &MainWindow::setAddrComment); + initTransactionsView(); // InitPortfolioView(); // ui_->widgetRFQ->initWidgets(mdProvider_, mdCallbacks_, applicationSettings_); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index c40585dc7..5720055a4 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -15,6 +15,7 @@ #include #include #include "Address.h" +#include "ArmoryConnection.h" #include "SignerDefs.h" namespace spdlog { @@ -35,6 +36,7 @@ class AuthAddressDialog; class NotificationCenter; class QSystemTrayIcon; class StatusBarView; +class TransactionsViewModel; namespace bs { namespace gui { @@ -54,7 +56,9 @@ namespace bs { void showStartupDialog(bool showLic); void onArmoryStateChanged(int state, unsigned int blockNum); + void onNewBlock(int state, unsigned int blockNum); void onSignerStateChanged(int state, const std::string &); + void onWalletsReady(); void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); @@ -63,6 +67,9 @@ namespace bs { void onAddressComments(const std::string &walletId , const std::map &); void onWalletBalance(const bs::sync::WalletBalanceData &); + void onLedgerEntries(const std::string &filter, uint32_t totalPages + , uint32_t curPage, uint32_t curBlock, const std::vector &); + void onTXDetails(const std::vector &); public slots: void onReactivate(); @@ -90,6 +97,9 @@ namespace bs { void setAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); + void needLedgerEntries(const std::string &filter); + void needTXDetails(const std::vector &); + private slots: void onSend(); void onGenerateAddress(); @@ -139,6 +149,7 @@ namespace bs { void setWidgetsAuthorized(bool); void initWidgets(); + void initTransactionsView(); void initChartsView(); void promptSwitchEnv(bool prod); @@ -168,6 +179,8 @@ namespace bs { // std::shared_ptr orderListModel_; std::shared_ptr authAddrDlg_; + std::shared_ptr txModel_; + // std::shared_ptr walletsWizard_; QString currentUserLogin_; diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index c9d8efc95..a98f4d52e 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -114,6 +114,7 @@ QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) : QObject(nullptr), logger_(logger) , userSettings_(std::make_shared(TerminalUsers::Settings)) , userWallets_(std::make_shared(TerminalUsers::Wallets)) + , userBlockchain_(std::make_shared(TerminalUsers::Blockchain)) {} QtGuiAdapter::~QtGuiAdapter() @@ -401,10 +402,19 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) blockNum_ = msg.new_block().top_block(); if (mainWindow_) { QMetaObject::invokeMethod(mainWindow_, [this]{ - mainWindow_->onArmoryStateChanged(armoryState_, blockNum_); - }); + mainWindow_->onNewBlock(armoryState_, blockNum_); + }); } break; + case ArmoryMessage::kWalletRegistered: + if (msg.wallet_registered().success() && msg.wallet_registered().wallet_id().empty()) { + walletsReady_ = true; + QMetaObject::invokeMethod(mainWindow_, [this] { + mainWindow_->onWalletsReady(); + }); + } + case ArmoryMessage::kLedgerEntries: + return processLedgerEntries(env, msg.ledger_entries()); default: break; } return true; @@ -497,6 +507,8 @@ bool QtGuiAdapter::processWallets(const Envelope &env) case WalletsMessage::kWalletBalances: return processWalletBalances(env, msg.wallet_balances()); + case WalletsMessage::kTxDetailsResponse: + return processTXDetails(msg.tx_details_response()); default: break; } return true; @@ -550,6 +562,9 @@ void QtGuiAdapter::updateStates() for (const auto &hdWallet : hdWallets_) { mainWindow_->onHDWallet(hdWallet.second); } + if (walletsReady_) { + mainWindow_->onWalletsReady(); + } } void QtGuiAdapter::updateSplashProgress() @@ -632,6 +647,8 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needUsedAddresses, this, &QtGuiAdapter::onNeedUsedAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needAddrComments, this, &QtGuiAdapter::onNeedAddrComments); connect(mainWindow_, &bs::gui::qt::MainWindow::setAddrComment, this, &QtGuiAdapter::onSetAddrComment); + connect(mainWindow_, &bs::gui::qt::MainWindow::needLedgerEntries, this, &QtGuiAdapter::onNeedLedgerEntries); + connect(mainWindow_, &bs::gui::qt::MainWindow::needTXDetails, this, &QtGuiAdapter::onNeedTXDetails); } void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) @@ -777,6 +794,28 @@ void QtGuiAdapter::onSetAddrComment(const std::string &walletId, const bs::Addre pushFill(env); } +void QtGuiAdapter::onNeedLedgerEntries(const std::string &filter) +{ + ArmoryMessage msg; + msg.set_get_ledger_entries(filter); + Envelope env{ 0, user_, userBlockchain_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedTXDetails(const const std::vector &txWallet) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_tx_details_request(); + for (const auto &txw : txWallet) { + auto request = msgReq->add_requests(); + request->set_tx_hash(txw.txHash.toBinStr()); + request->set_wallet_id(txw.walletId); + request->set_value(txw.value); + } + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[wi.id] = wi; @@ -788,7 +827,7 @@ void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) } bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env - , const BlockSettle::Common::WalletsMessage_WalletBalances &response) + , const WalletsMessage_WalletBalances &response) { bs::sync::WalletBalanceData wbd; wbd.id = response.wallet_id(); @@ -807,4 +846,61 @@ bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env return true; } +bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &response) +{ + std::vector txDetails; + for (const auto &resp : response.responses()) { + std::vector outAddrs; + for (const auto &addrStr : resp.out_addresses()) { + try { + outAddrs.emplace_back(std::move(bs::Address::fromAddressString(addrStr))); + } + catch (const std::exception &) {} + } + txDetails.push_back({ BinaryData::fromString(resp.tx_hash()), resp.wallet_id() + , resp.wallet_name(), static_cast(resp.wallet_type()) + , static_cast(resp.direction()) + , resp.comment(), resp.valid(), resp.amount(), outAddrs }); + } + QMetaObject::invokeMethod(mainWindow_, [this, txDetails] { + mainWindow_->onTXDetails(txDetails); + }); + return true; +} + +bool QtGuiAdapter::processLedgerEntries(const bs::message::Envelope &env + , const ArmoryMessage_LedgerEntries &response) +{ + std::vector entries; + entries.reserve(response.entries_size()); + for (const auto &entry : response.entries()) { + bs::TXEntry txEntry; + txEntry.txHash = BinaryData::fromString(entry.tx_hash()); + txEntry.value = entry.value(); + txEntry.blockNum = entry.block_num(); + txEntry.txTime = entry.tx_time(); + txEntry.isRBF = entry.rbf(); + txEntry.isChainedZC = entry.chained_zc(); + txEntry.nbConf = entry.nb_conf(); + for (const auto &walletId : entry.wallet_ids()) { + txEntry.walletIds.insert(walletId); + } + for (const auto &addrStr : entry.addresses()) { + try { + const auto &addr = bs::Address::fromAddressString(addrStr); + txEntry.addresses.emplace_back(std::move(addr)); + } + catch (const std::exception &) {} + } + entries.emplace_back(std::move(txEntry)); + } + QMetaObject::invokeMethod(mainWindow_, [this, entries, filter=response.filter() + , totPages=response.total_pages(), curPage=response.cur_page() + , curBlock=response.cur_block()] { + mainWindow_->onLedgerEntries(filter, totPages, curPage, curBlock, entries); + }); + return true; +} + + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index ae4cbdb4b..742e210dd 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -27,6 +27,8 @@ namespace bs { } namespace BlockSettle { namespace Common { + class ArmoryMessage_LedgerEntries; + class WalletsMessage_TXDetailsResponse; class WalletsMessage_WalletBalances; } namespace Terminal { @@ -77,6 +79,9 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu void processWalletLoaded(const bs::sync::WalletInfo &); bool processWalletBalances(const bs::message::Envelope & , const BlockSettle::Common::WalletsMessage_WalletBalances &); + bool processTXDetails(const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); + bool processLedgerEntries(const bs::message::Envelope & + , const BlockSettle::Common::ArmoryMessage_LedgerEntries &); private slots: void onPutSetting(int idx, const QVariant &value); @@ -89,10 +94,13 @@ private slots: void onNeedAddrComments(const std::string &walletId, const std::vector &); void onSetAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); + void onNeedLedgerEntries(const std::string &filter); + void onNeedTXDetails(const std::vector &); private: std::shared_ptr logger_; std::shared_ptr userSettings_, userWallets_; + std::shared_ptr userBlockchain_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; @@ -102,6 +110,7 @@ private slots: uint32_t blockNum_{ 0 }; int signerState_{ -1 }; std::string signerDetails_; + bool walletsReady_{ false }; std::unordered_map hdWallets_; }; diff --git a/common b/common index 281e4b961..00896ce9f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 281e4b961ad648c2f1cd8aa3ba87d7cc9d32379a +Subproject commit 00896ce9f2836e02830d87473ec0d4e440bb102a From f2f3cfe8ca4249acf3e9bad125412fccb0b756b8 Mon Sep 17 00:00:00 2001 From: sergey-chernikov Date: Tue, 15 Sep 2020 00:15:45 +0300 Subject: [PATCH 008/146] TX and address details --- BlockSettleSigner/SignerAdapterContainer.cpp | 2 +- BlockSettleSigner/SignerInterfaceListener.cpp | 2 +- BlockSettleUILib/AddressDetailDialog.cpp | 109 ++++++++++++- BlockSettleUILib/AddressDetailDialog.h | 25 ++- BlockSettleUILib/AddressDetailDialog.ui | 12 +- BlockSettleUILib/AddressListModel.cpp | 76 +++++---- BlockSettleUILib/AddressListModel.h | 11 +- BlockSettleUILib/BSTerminalMainWindow.cpp | 12 +- .../RootWalletPropertiesDialog.cpp | 20 +-- BlockSettleUILib/SelectWalletDialog.cpp | 4 +- BlockSettleUILib/TransactionDetailDialog.cpp | 150 ++++++++++++++++++ BlockSettleUILib/TransactionDetailDialog.h | 11 +- BlockSettleUILib/TransactionsViewModel.cpp | 40 +++-- BlockSettleUILib/TransactionsViewModel.h | 5 + BlockSettleUILib/TransactionsWidget.cpp | 13 +- BlockSettleUILib/WalletsViewModel.cpp | 18 +-- BlockSettleUILib/WalletsWidget.cpp | 81 ++++++++-- BlockSettleUILib/WalletsWidget.h | 10 +- Core/SignerAdapter.cpp | 4 +- GUI/QtWidgets/MainWindow.cpp | 19 ++- GUI/QtWidgets/MainWindow.h | 3 +- GUI/QtWidgets/QtGuiAdapter.cpp | 72 +++++++-- common | 2 +- 23 files changed, 566 insertions(+), 135 deletions(-) diff --git a/BlockSettleSigner/SignerAdapterContainer.cpp b/BlockSettleSigner/SignerAdapterContainer.cpp index faa382a44..220c433d0 100644 --- a/BlockSettleSigner/SignerAdapterContainer.cpp +++ b/BlockSettleSigner/SignerAdapterContainer.cpp @@ -48,7 +48,7 @@ void SignAdapterContainer::syncWalletInfo(const std::function leaves; for (const auto &leaf : group.leaves()) { - leaves.push_back({ leaf.id(), bs::hd::Path::fromString(leaf.path()) + leaves.push_back({ {leaf.id()}, bs::hd::Path::fromString(leaf.path()) , std::string{}, std::string{}, false , BinaryData::fromString(leaf.extra_data()) }); } diff --git a/BlockSettleUILib/AddressDetailDialog.cpp b/BlockSettleUILib/AddressDetailDialog.cpp index 6102199cf..bb3cced2f 100644 --- a/BlockSettleUILib/AddressDetailDialog.cpp +++ b/BlockSettleUILib/AddressDetailDialog.cpp @@ -41,7 +41,7 @@ class IncomingTransactionFilter : public QSortFilterProxyModel } const auto &index = txModel->index(source_row, 0, source_parent); const auto &txItem = txModel->getItem(index); - return (txItem && txItem->isSet() && (txItem->txEntry.value > 0)); + return (txItem && (txItem->txEntry.value > 0)); } }; @@ -58,7 +58,7 @@ class OutgoingTransactionFilter : public QSortFilterProxyModel } const auto &index = txModel->index(source_row, 0, source_parent); const auto &txItem = txModel->getItem(index); - return (txItem && txItem->isSet() && (txItem->txEntry.value < 0)); + return (txItem && (txItem->txEntry.value < 0)); } }; @@ -111,7 +111,7 @@ AddressDetailDialog::AddressDetailDialog(const bs::Address& address auto copyButton = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); connect(copyButton, &QPushButton::clicked, this, &AddressDetailDialog::onCopyClicked); - ui_->labelWallenName->setText(QString::fromStdString(wallet_->name())); + ui_->labelWalletName->setText(QString::fromStdString(wallet_->name())); const auto addressString = QString::fromStdString(address.display()); ui_->labelAddress->setText(addressString); @@ -152,7 +152,7 @@ AddressDetailDialog::AddressDetailDialog(const bs::Address& address } else { QPointer thisPtr = this; - const auto &cbLedgerDelegate = [thisPtr, armory](const std::shared_ptr &delegate) { + const auto &cbLedgerDelegate = [thisPtr](const std::shared_ptr &delegate) { QMetaObject::invokeMethod(qApp, [thisPtr, delegate]{ if (thisPtr) { thisPtr->initModels(delegate); @@ -177,8 +177,103 @@ AddressDetailDialog::AddressDetailDialog(const bs::Address& address this, &AddressDetailDialog::onOutputAddrContextMenu); } +AddressDetailDialog::AddressDetailDialog(const bs::Address& address + , const std::shared_ptr &logger, bs::core::wallet::Type wltType + , uint64_t balance, uint32_t txn, const QString &walletName + , const std::string &addrIndex, const std::string &comment, QWidget* parent) + : QDialog(parent) + , ui_(new Ui::AddressDetailDialog()) + , address_(address) + , logger_(logger) +{ + ui_->setupUi(this); + ui_->labelError->hide(); + + setBalance(balance, wltType); + onAddrTxNReceived(txn); + + auto copyButton = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); + connect(copyButton, &QPushButton::clicked, this, &AddressDetailDialog::onCopyClicked); + + ui_->labelWalletName->setText(walletName); + + const auto addressString = QString::fromStdString(address.display()); + ui_->labelAddress->setText(addressString); + + const auto path = bs::hd::Path::fromString(addrIndex); + QString index; + if (path.length() != 2) { + index = QString::fromStdString(addrIndex); + } else { + const auto lastIndex = QString::number(path.get(-1)); + switch (path.get(-2)) { + case 0: + index = tr("External/%1").arg(lastIndex); + break; + case 1: + index = tr("Internal/%1").arg(lastIndex); + break; + default: + index = tr("Unknown/%1").arg(lastIndex); + } + } + if (index.length() > 64) { + index = index.left(64); + } + ui_->labelAddrIndex->setText(index); + + ui_->labelComment->setText(QString::fromStdString(comment)); + + ui_->inputAddressesWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui_->outputAddressesWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + const QString addrURI = QLatin1String("bitcoin:") + addressString; + ui_->labelQR->setPixmap(UiUtils::getQRCode(addrURI, 128)); + + ui_->inputAddressesWidget->setContextMenuPolicy(Qt::CustomContextMenu); + ui_->outputAddressesWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(ui_->inputAddressesWidget, &QTreeView::customContextMenuRequested, + this, &AddressDetailDialog::onInputAddrContextMenu); + connect(ui_->outputAddressesWidget, &QTreeView::customContextMenuRequested, + this, &AddressDetailDialog::onOutputAddrContextMenu); + + model_ = new TransactionsViewModel(logger_, this); + connect(model_, &TransactionsViewModel::needTXDetails, this, &AddressDetailDialog::needTXDetails); + + IncomingTransactionFilter* incomingFilter = new IncomingTransactionFilter(this); + incomingFilter->setSourceModel(model_); + AddressTransactionFilter* inFilter = new AddressTransactionFilter(this); + inFilter->setSourceModel(incomingFilter); + ui_->inputAddressesWidget->setModel(inFilter); + ui_->inputAddressesWidget->sortByColumn(static_cast(TransactionsViewModel::Columns::Date), Qt::DescendingOrder); + + OutgoingTransactionFilter* outgoingFilter = new OutgoingTransactionFilter(this); + outgoingFilter->setSourceModel(model_); + AddressTransactionFilter* outFilter = new AddressTransactionFilter(this); + outFilter->setSourceModel(outgoingFilter); + ui_->outputAddressesWidget->setModel(outFilter); + ui_->outputAddressesWidget->sortByColumn(static_cast(TransactionsViewModel::Columns::Date), Qt::DescendingOrder); +} + AddressDetailDialog::~AddressDetailDialog() = default; +void AddressDetailDialog::onNewBlock(unsigned int blockNum) +{ + model_->onNewBlock(blockNum); +} + +void AddressDetailDialog::onLedgerEntries(uint32_t curBlock + , const std::vector &entries) +{ + model_->onLedgerEntries({}, 0, 0, curBlock, entries); +} + +void AddressDetailDialog::onTXDetails(const std::vector &details) +{ + model_->onTXDetails(details); +} + void AddressDetailDialog::initModels(const std::shared_ptr &delegate) { TransactionsViewModel* model = new TransactionsViewModel(armory_ @@ -214,6 +309,12 @@ void AddressDetailDialog::onAddrBalanceReceived(const std::vector &bal ? UiUtils::displayCCAmount(balance[0]) : UiUtils::displayAmount(balance[0])); } +void AddressDetailDialog::setBalance(uint64_t balance, bs::core::wallet::Type wltType) +{ + ui_->labelBalance->setText((wltType == bs::core::wallet::Type::ColorCoin) + ? UiUtils::displayCCAmount(balance) : UiUtils::displayAmount(balance)); +} + void AddressDetailDialog::onAddrTxNReceived(uint32_t txn) { ui_->labelTransactions->setText(QString::number(txn)); diff --git a/BlockSettleUILib/AddressDetailDialog.h b/BlockSettleUILib/AddressDetailDialog.h index 3205b3c04..be93662fb 100644 --- a/BlockSettleUILib/AddressDetailDialog.h +++ b/BlockSettleUILib/AddressDetailDialog.h @@ -15,6 +15,8 @@ #include #include "Address.h" #include "AsyncClient.h" +#include "CoreWallet.h" +#include "SignerDefs.h" namespace spdlog { class logger; @@ -29,39 +31,52 @@ namespace bs { } } class ArmoryConnection; -class Tx; +class TransactionsViewModel; class AddressDetailDialog : public QDialog { Q_OBJECT public: - AddressDetailDialog(const bs::Address &address + [[deprecated]] AddressDetailDialog(const bs::Address &address , const std::shared_ptr &wallet , const std::shared_ptr& walletsManager , const std::shared_ptr &armory , const std::shared_ptr &logger , QWidget* parent = nullptr ); + AddressDetailDialog(const bs::Address &, const std::shared_ptr & + , bs::core::wallet::Type, uint64_t balance, uint32_t txn + , const QString &walletName, const std::string &addrIndex + , const std::string &comment, QWidget* parent = nullptr); ~AddressDetailDialog() override; + void onNewBlock(unsigned int blockNum); + void onLedgerEntries(uint32_t curBlock, const std::vector &); + void onTXDetails(const std::vector &); + +signals: + void needTXDetails(const std::vector &); + private slots: void onCopyClicked() const; - void onAddrBalanceReceived(const std::vector &); + void onAddrBalanceReceived(const std::vector &); //deprecated void onAddrTxNReceived(uint32_t); void onInputAddrContextMenu(const QPoint &pos); void onOutputAddrContextMenu(const QPoint &pos); private: + void setBalance(uint64_t, bs::core::wallet::Type); void onError(); - Q_INVOKABLE void initModels(const std::shared_ptr &); + [[deprecated]] void initModels(const std::shared_ptr &); private: std::unique_ptr ui_; + const bs::Address address_; std::shared_ptr logger_; - const bs::Address address_; std::shared_ptr walletsManager_; std::shared_ptr armory_; std::shared_ptr wallet_; + TransactionsViewModel *model_{ nullptr }; }; #endif // __ADDRESS_DETAIL_DIALOG_H__ diff --git a/BlockSettleUILib/AddressDetailDialog.ui b/BlockSettleUILib/AddressDetailDialog.ui index 3096d817a..cb5e6d190 100644 --- a/BlockSettleUILib/AddressDetailDialog.ui +++ b/BlockSettleUILib/AddressDetailDialog.ui @@ -1,14 +1,4 @@ - AddressDetailDialog @@ -220,7 +210,7 @@ - + -- diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index f52f2b44c..bfb5f000a 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -114,7 +114,6 @@ AddressListModel::AddressRow AddressListModel::createRow(const bs::Address &addr // row.comment = QString::fromStdString(wallet->getAddressComment(addr)); row.displayedAddress = QString::fromStdString(addr.display()); row.walletName = QString::fromStdString(wallet.name); - row.walletId = QString::fromStdString(wallet.id); // row.isExternal = wallet->isExternalAddress(addr); } row.wltType = wallet.type; @@ -160,6 +159,7 @@ void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) if (wallet.type == bs::core::wallet::Type::Authentication) { const auto addr = bs::Address(); auto row = createRow(addr, wallet); + row.walletId = QString::fromStdString(*wallet.ids.cbegin()); beginInsertRows(QModelIndex(), addressRows_.size(), addressRows_.size()); addressRows_.emplace_back(std::move(row)); endInsertRows(); @@ -170,18 +170,18 @@ void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) std::vector addressList; switch (addrType_) { case AddressType::External: - emit needExtAddresses(wallet.id); + emit needExtAddresses(*wallet.ids.cbegin()); // addressList = wallet->getExtAddressList(); break; case AddressType::Internal: - emit needIntAddresses(wallet.id); + emit needIntAddresses(*wallet.ids.cbegin()); // addressList = wallet->getIntAddressList(); break; case AddressType::All: case AddressType::ExtAndNonEmptyInt: default: // addressList = wallet->getUsedAddressList(); - emit needUsedAddresses(wallet.id); + emit needUsedAddresses(*wallet.ids.cbegin()); break; } @@ -199,24 +199,31 @@ void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) } } -void AddressListModel::onAddresses(const std::string &walletId - , const std::vector &addrs) +void AddressListModel::onAddresses(const std::vector &addrs) { - const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() - , [walletId](const bs::sync::WalletInfo &wallet) { - return (wallet.id == walletId); - }); - if ((itWallet == wallets_.cend()) || addrs.empty()) { - return; - } - const auto &itWalletBal = pooledBalances_.find(walletId); + std::string mainWalletId; beginInsertRows(QModelIndex(), addressRows_.size() , addressRows_.size() + addrs.size() - 1); for (const auto &addr : addrs) { - auto row = createRow(addr, *itWallet); + const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() + , [walletId = addr.walletId](const bs::sync::WalletInfo &wi){ + for (const auto &id : wi.ids) { + if (id == walletId) { + return true; + } + } + return false; + }); + auto row = createRow(addr.address, *itWallet); + row.index = QString::fromStdString(addr.index); row.addrIndex = addressRows_.size(); + row.walletId = QString::fromStdString(addr.walletId); + if (mainWalletId.empty()) { + mainWalletId = *(*itWallet).ids.cbegin(); + } + const auto &itWalletBal = pooledBalances_.find(mainWalletId); if (itWalletBal != pooledBalances_.end()) { - const auto &itAddrBal = itWalletBal->second.find(addr.id()); + const auto &itAddrBal = itWalletBal->second.find(addr.address.id()); if (itAddrBal == itWalletBal->second.end()) { row.balance = 0; row.transactionCount = 0; @@ -226,23 +233,22 @@ void AddressListModel::onAddresses(const std::string &walletId row.transactionCount = itAddrBal->second.txn; } } - indexByAddr_[addr.id()] = row.addrIndex; + indexByAddr_[addr.address.id()] = row.addrIndex; addressRows_.push_back(std::move(row)); } endInsertRows(); - emit needAddrComments(walletId, addrs); + + std::vector addrsReq; + addrsReq.reserve(addrs.size()); + for (const auto &addr : addrs) { + addrsReq.push_back(addr.address); + } + emit needAddrComments(mainWalletId, addrsReq); } void AddressListModel::onAddressComments(const std::string &walletId , const std::map &comments) { - const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() - , [walletId](const bs::sync::WalletInfo &wallet) { - return (wallet.id == walletId); - }); - if ((itWallet == wallets_.cend()) || comments.empty()) { - return; - } for (const auto &comm : comments) { const auto &itAddr = indexByAddr_.find(comm.first.id()); if (itAddr == indexByAddr_.end()) { @@ -269,7 +275,7 @@ void AddressListModel::onAddressBalances(const std::string &walletId lbdSaveBalToPool(); const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() , [walletId](const bs::sync::WalletInfo &wallet) { - return (wallet.id == walletId); + return (*wallet.ids.cbegin() == walletId); }); if (itWallet == wallets_.cend()) { // balances arrived before wallet was set return; @@ -531,14 +537,28 @@ QVariant AddressListModel::data(const QModelIndex& index, int role) const case AddressCommentRole: return row.comment; + case AddressIndexRole: + return row.index; + case WalletTypeRole: for (const auto &wallet : wallets_) { - if (wallet.id == row.walletId.toStdString()) { - return static_cast(wallet.type); + for (const auto &id : wallet.ids) { + if (id == row.walletId.toStdString()) { + return static_cast(wallet.type); + } } } return 0; + case WalletNameRole: + return row.walletName; + + case TxNRole: + return row.transactionCount; + + case BalanceRole: + return row.balance; + case Qt::ToolTipRole: if ((index.column() == ColumnComment) && row.isMultiLineComment()) { return row.comment; diff --git a/BlockSettleUILib/AddressListModel.h b/BlockSettleUILib/AddressListModel.h index 3884a6c8a..b2369b118 100644 --- a/BlockSettleUILib/AddressListModel.h +++ b/BlockSettleUILib/AddressListModel.h @@ -42,6 +42,7 @@ class AddressListModel : public QAbstractTableModel QString displayedAddress; QString walletName; QString walletId; + QString index; int addrIndex = 0; bs::core::wallet::Type wltType = bs::core::wallet::Type::Unknown; bool isExternal; @@ -72,7 +73,11 @@ class AddressListModel : public QAbstractTableModel AddressRole, AddressCommentRole, IsExternalRole, - WalletTypeRole + AddressIndexRole, + WalletTypeRole, + WalletNameRole, + TxNRole, + BalanceRole }; enum AddressType { @@ -82,7 +87,7 @@ class AddressListModel : public QAbstractTableModel ExtAndNonEmptyInt = 4 }; - typedef std::vector Wallets; + typedef std::vector Wallets; [[deprecated]] AddressListModel(const std::shared_ptr &, QObject* parent , AddressType addrType = AddressType::All); @@ -95,7 +100,7 @@ class AddressListModel : public QAbstractTableModel QVariant headerData(int section, Qt::Orientation orientation, int role) const override; void setWallets(const Wallets &, bool force, bool filterBtcOnly); - void onAddresses(const std::string &walletId, const std::vector &); + void onAddresses(const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); void onAddressBalances(const std::string &walletId diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 36f3e340f..85ef71a97 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -1205,12 +1205,12 @@ void BSTerminalMainWindow::onGenerateAddress() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallets = ui_->widgetWallets->getSelectedWallets(); if (!wallets.empty()) { - selWalletId = wallets[0].id; + selWalletId = wallets[0].ids[0]; } else { wallets = ui_->widgetWallets->getFirstWallets(); if (!wallets.empty()) { - selWalletId = wallets[0].id; + selWalletId = wallets[0].ids[0]; } } } @@ -1232,14 +1232,14 @@ void BSTerminalMainWindow::onSend() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallet = ui_->widgetWallets->getSelectedHdWallet(); - if (wallet.id.empty()) { + if (wallet.ids.empty()) { const auto &priWallet = walletsMgr_->getPrimaryWallet(); if (priWallet) { - wallet.id = priWallet->walletId(); + wallet.ids.push_back(priWallet->walletId()); } } - if (!wallet.id.empty()) { - selectedWalletId = wallet.id; + if (!wallet.ids.empty()) { + selectedWalletId = wallet.ids[0]; } } diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 1a031f0f0..2344b5b40 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -46,7 +46,7 @@ class CurrentWalletFilter : public QSortFilterProxyModel auto node = dynamic_cast(sourceModel())->getNode(index); auto wallet = node->hdWallet(); - return (node->hdWallet().id == walletId_); + return (*node->hdWallet().ids.cbegin() == walletId_); } private: @@ -78,7 +78,7 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrlabelEncRank->clear(); - walletFilter_ = new CurrentWalletFilter(wallet.id, this); + walletFilter_ = new CurrentWalletFilter(*wallet.ids.cbegin(), this); walletFilter_->setSourceModel(walletsModel); ui_->treeViewWallets->setModel(walletFilter_); @@ -111,7 +111,7 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrGetInfo(wallet_.id); + infoReqId_ = signingContainer_->GetInfo(*wallet_.ids.cbegin()); } ui_->treeViewWallets->expandAll(); @@ -131,7 +131,7 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrlabelEncRank->clear(); - walletFilter_ = new CurrentWalletFilter(wallet.id, this); + walletFilter_ = new CurrentWalletFilter(*wallet.ids.cbegin(), this); walletFilter_->setSourceModel(walletsModel); ui_->treeViewWallets->setModel(walletFilter_); @@ -243,7 +243,7 @@ void RootWalletPropertiesDialog::onWalletSelected() auto node = dynamic_cast(walletFilter_->sourceModel())->getNode(modelIndex); const auto wallet = node->hdWallet(); - if (!wallet.id.empty()) { + if (!wallet.ids.empty()) { updateWalletDetails(wallet); } else { const auto wallets = node->wallets(); @@ -257,7 +257,7 @@ void RootWalletPropertiesDialog::onWalletSelected() void RootWalletPropertiesDialog::updateWalletDetails(const bs::sync::WalletInfo &wi) { - ui_->labelWalletId->setText(QString::fromStdString(wi.id)); + ui_->labelWalletId->setText(QString::fromStdString(*wi.ids.cbegin())); ui_->labelWalletName->setText(QString::fromStdString(wi.name)); ui_->labelDescription->setText(QString::fromStdString(wi.description)); @@ -280,13 +280,13 @@ void RootWalletPropertiesDialog::updateWalletDetails(const bs::sync::WalletInfo }; if (wi.format == bs::sync::WalletFormat::HD) { - emit needHDWalletDetails(wi.id); + emit needHDWalletDetails(*wi.ids.cbegin()); } else { ui_->labelAddressesActive->setText(tr("Loading...")); ui_->labelUTXOs->setText(tr("Loading...")); - emit needWalletBalances(wi.id); - emit needSpendableUTXOs(wi.id); + emit needWalletBalances(*wi.ids.cbegin()); + emit needSpendableUTXOs(*wi.ids.cbegin()); } } @@ -344,7 +344,7 @@ void RootWalletPropertiesDialog::onRescanBlockchain() startWalletScan(); }*/ // wallet_->startRescan(); //TODO: reimplement - emit startRescan(wallet_.id); + emit startRescan(*wallet_.ids.cbegin()); accept(); } diff --git a/BlockSettleUILib/SelectWalletDialog.cpp b/BlockSettleUILib/SelectWalletDialog.cpp index 7d56263b0..d55c7e60f 100644 --- a/BlockSettleUILib/SelectWalletDialog.cpp +++ b/BlockSettleUILib/SelectWalletDialog.cpp @@ -52,7 +52,7 @@ void SelectWalletDialog::onSelectionChanged() { auto selectedRows = ui_->treeViewWallets->selectionModel()->selectedRows(); if (selectedRows.size() == 1) { - selectedWallet_ = walletsModel_->getWallet(selectedRows[0]).id; + selectedWallet_ = *walletsModel_->getWallet(selectedRows[0]).ids.cbegin(); } else { selectedWallet_ = nullptr; @@ -74,6 +74,6 @@ void SelectWalletDialog::onDoubleClicked(const QModelIndex& index) return; } - selectedWallet_ = walletsModel_->getWallet(index).id; + selectedWallet_ = *walletsModel_->getWallet(index).ids.cbegin(); QDialog::accept(); } diff --git a/BlockSettleUILib/TransactionDetailDialog.cpp b/BlockSettleUILib/TransactionDetailDialog.cpp index d859782e1..3222f2ee0 100644 --- a/BlockSettleUILib/TransactionDetailDialog.cpp +++ b/BlockSettleUILib/TransactionDetailDialog.cpp @@ -240,6 +240,80 @@ TransactionDetailDialog::TransactionDetailDialog(const TransactionPtr &tvi resize(minimumSize()); } +TransactionDetailDialog::TransactionDetailDialog(const TransactionPtr &txi + , QWidget* parent) + : QDialog(parent) + , ui_(new Ui::TransactionDetailDialog()) +{ + ui_->setupUi(this); + itemSender_ = new QTreeWidgetItem(QStringList(tr("Sender"))); + itemReceiver_ = new QTreeWidgetItem(QStringList(tr("Receiver"))); + + ui_->labelAmount->setText(txi->amountStr); + ui_->labelDirection->setText(tr(bs::sync::Transaction::toString(txi->direction))); + ui_->labelAddress->setText(txi->mainAddress); + + if (txi->confirmations > 0) { + ui_->labelHeight->setText(QString::number(txi->txEntry.blockNum)); + } else { + if (txi->txEntry.isRBF) { + ui_->labelFlag->setText(tr("RBF eligible")); + } else if (txi->isCPFP) { + ui_->labelFlag->setText(tr("CPFP eligible")); + } + } + + ui_->treeAddresses->addTopLevelItem(itemSender_); + ui_->treeAddresses->addTopLevelItem(itemReceiver_); + ui_->labelComment->setText(txi->comment); + + if (txi->tx.isInitialized()) { + ui_->labelSize->setText(QString::number(txi->tx.getTxWeight())); + } + + int64_t value = 0; + for (const auto &addrDet : txi->inputAddresses) { + addInputAddress(addrDet); + value += addrDet.value; + } + if (!txi->changeAddress.address.empty()) { + addChangeAddress(txi->changeAddress); + value -= txi->changeAddress.value; + } + for (const auto &addrDet : txi->outputAddresses) { + addOutputAddress(addrDet); + value -= addrDet.value; + } + + ui_->labelFee->setText(UiUtils::displayAmount(value)); + ui_->labelSb->setText( + QString::number((float)value / (float)txi->tx.getTxWeight())); + + for (int i = 0; i < ui_->treeAddresses->columnCount(); ++i) { + ui_->treeAddresses->resizeColumnToContents(i); + ui_->treeAddresses->setColumnWidth(i, + ui_->treeAddresses->columnWidth(i) + extraTreeWidgetColumnMargin); + } + + ui_->treeAddresses->expandItem(itemSender_); + ui_->treeAddresses->expandItem(itemReceiver_); + adjustSize(); + + ui_->labelConfirmations->setText(QString::number(txi->confirmations)); + + const bool bigEndianHash = true; + ui_->labelHash->setText(QString::fromStdString(txi->txEntry.txHash.toHexStr(bigEndianHash))); + ui_->labelTime->setText(UiUtils::displayDateTime(QDateTime::fromTime_t(txi->txEntry.txTime))); + + ui_->labelWalletName->setText(txi->walletName.isEmpty() ? tr("Unknown") : txi->walletName); + + // allow address column to be copied to clipboard with right click + ui_->treeAddresses->copyToClipboardColumns_.append(2); + + setMinimumHeight(minHeightAtRendering); + resize(minimumSize()); +} + TransactionDetailDialog::~TransactionDetailDialog() = default; QSize TransactionDetailDialog::minimumSize() const @@ -357,6 +431,66 @@ void TransactionDetailDialog::addAddress(TxOut out // can't use const ref due item->addChild(txItem); } +void TransactionDetailDialog::addInputAddress(const bs::sync::AddressDetails &addrDet) +{ + const auto &addressType = tr("Input"); + const auto &displayedAddress = QString::fromStdString(addrDet.address.display()); + const auto &valueStr = QString::fromStdString(addrDet.valueStr); + QStringList items; + items << addressType << valueStr << displayedAddress << QString::fromStdString(addrDet.walletName); + + auto item = new QTreeWidgetItem(items); + item->setData(0, Qt::UserRole, displayedAddress); + item->setData(1, Qt::UserRole, (qulonglong)addrDet.value); + itemSender_->addChild(item); + const auto &txHashStr = QString::fromStdString(fmt::format("{}/{}" + , addrDet.outHash.toHexStr(true), addrDet.outIndex)); + auto txItem = new QTreeWidgetItem(QStringList() << getScriptType(addrDet.type) + << QString::number(addrDet.value) << txHashStr); + txItem->setData(0, Qt::UserRole, txHashStr); + item->addChild(txItem); +} + +void TransactionDetailDialog::addChangeAddress(const bs::sync::AddressDetails &addrDet) +{ + const auto &addressType = tr("Change"); + const auto &displayedAddress = QString::fromStdString(addrDet.address.display()); + const auto &valueStr = QString::fromStdString(addrDet.valueStr); + QStringList items; + items << addressType << valueStr << displayedAddress << QString::fromStdString(addrDet.walletName); + + auto item = new QTreeWidgetItem(items); + item->setData(0, Qt::UserRole, displayedAddress); + item->setData(1, Qt::UserRole, (qulonglong)addrDet.value); + itemSender_->addChild(item); + const auto &txHashStr = QString::fromStdString(fmt::format("{}/{}" + , addrDet.outHash.toHexStr(true), addrDet.outIndex)); + auto txItem = new QTreeWidgetItem(QStringList() << getScriptType(addrDet.type) + << QString::number(addrDet.value) << txHashStr); + txItem->setData(0, Qt::UserRole, txHashStr); + item->addChild(txItem); +} + +void TransactionDetailDialog::addOutputAddress(const bs::sync::AddressDetails &addrDet) +{ + const auto &addressType = tr("Output"); + const auto &displayedAddress = QString::fromStdString(addrDet.address.display()); + const auto &valueStr = QString::fromStdString(addrDet.valueStr); + QStringList items; + items << addressType << valueStr << displayedAddress << QString::fromStdString(addrDet.walletName); + + auto item = new QTreeWidgetItem(items); + item->setData(0, Qt::UserRole, displayedAddress); + item->setData(1, Qt::UserRole, (qulonglong)addrDet.value); + itemReceiver_->addChild(item); + const auto &txHashStr = QString::fromStdString(fmt::format("{}/{}" + , addrDet.outHash.toHexStr(true), addrDet.outIndex)); + auto txItem = new QTreeWidgetItem(QStringList() << getScriptType(addrDet.type) + << QString::number(addrDet.value) << txHashStr); + txItem->setData(0, Qt::UserRole, txHashStr); + item->addChild(txItem); +} + QString TransactionDetailDialog::getScriptType(const TxOut &out) { switch (out.getScriptType()) { @@ -372,3 +506,19 @@ QString TransactionDetailDialog::getScriptType(const TxOut &out) } return tr("unknown"); } + +QString TransactionDetailDialog::getScriptType(TXOUT_SCRIPT_TYPE scrType) +{ + switch (scrType) { + case TXOUT_SCRIPT_STDHASH160: return tr("hash160"); + case TXOUT_SCRIPT_STDPUBKEY65: return tr("pubkey65"); + case TXOUT_SCRIPT_STDPUBKEY33: return tr("pubkey33"); + case TXOUT_SCRIPT_MULTISIG: return tr("multisig"); + case TXOUT_SCRIPT_P2SH: return tr("p2sh"); + case TXOUT_SCRIPT_NONSTANDARD: return tr("non-std"); + case TXOUT_SCRIPT_P2WPKH: return tr("p2wpkh"); + case TXOUT_SCRIPT_P2WSH: return tr("p2wsh"); + case TXOUT_SCRIPT_OPRETURN: return tr("op-return"); + } + return tr("unknown"); +} diff --git a/BlockSettleUILib/TransactionDetailDialog.h b/BlockSettleUILib/TransactionDetailDialog.h index 610a33d99..f3c4957cd 100644 --- a/BlockSettleUILib/TransactionDetailDialog.h +++ b/BlockSettleUILib/TransactionDetailDialog.h @@ -42,8 +42,9 @@ class TransactionDetailDialog : public QDialog Q_OBJECT public: - TransactionDetailDialog(const TransactionPtr &tvi, const std::shared_ptr & + [[deprecated]] TransactionDetailDialog(const TransactionPtr &tvi, const std::shared_ptr & , const std::shared_ptr &, QWidget* parent = nullptr); + TransactionDetailDialog(const TransactionPtr &, QWidget* parent = nullptr); ~TransactionDetailDialog() override; virtual QSize minimumSizeHint() const override; QSize minimumSize() const; @@ -53,10 +54,14 @@ Q_OBJECT private: using WalletsSet = std::set>; - void addAddress(TxOut out, bool isOutput + [[deprecated]] void addAddress(TxOut out, bool isOutput , const BinaryData& txHash, const WalletsSet &inputWallets , const std::vector &allOutputs = {}); - QString getScriptType(const TxOut &); + [[deprecated]] QString getScriptType(const TxOut &); + void addInputAddress(const bs::sync::AddressDetails &); + void addOutputAddress(const bs::sync::AddressDetails &); + void addChangeAddress(const bs::sync::AddressDetails &); + QString getScriptType(TXOUT_SCRIPT_TYPE); private: std::unique_ptr ui_; diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index d23ebccd8..aa1b180aa 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -1105,8 +1105,8 @@ void TransactionsViewModel::onNewBlock(unsigned int curBlock) , index(rootNode_->nbChildren() - 1, static_cast(Columns::Status))); } -void TransactionsViewModel::onLedgerEntries(const std::string &filter, uint32_t totalPages - , uint32_t curPage, uint32_t curBlock, const std::vector &entries) +void TransactionsViewModel::onLedgerEntries(const std::string &, uint32_t + , uint32_t, uint32_t curBlock, const std::vector &entries) { if (!curBlock_) { curBlock_ = curBlock; @@ -1119,14 +1119,30 @@ void TransactionsViewModel::onLedgerEntries(const std::string &filter, uint32_t return; } - beginInsertRows(QModelIndex(), rootNode_->nbChildren() - , rootNode_->nbChildren() + entries.size() - 1); + std::vector newNodes; for (const auto &entry : entries) { const auto &item = createTxItem(entry); - itemIndex_[{item->txEntry.txHash, item->walletID.toStdString()}] = rootNode_->nbChildren(); - rootNode_->add(new TXNode(item)); + const auto &itItem = itemIndex_.find({entry.txHash, item->walletID.toStdString()}); + if (itItem == itemIndex_.end()) { + newNodes.push_back(new TXNode(item)); + } + else { + const int row = itItem->second; + rootNode_->children()[row]->setItem(item); + emit dataChanged(index(row, static_cast(Columns::first)) + , index(row, static_cast(Columns::last))); + } + } + + if (!newNodes.empty()) { + beginInsertRows(QModelIndex(), rootNode_->nbChildren() + , rootNode_->nbChildren() + newNodes.size() - 1); + for (const auto &node : newNodes) { + itemIndex_[{node->item()->txEntry.txHash, node->item()->walletID.toStdString()}] = rootNode_->nbChildren(); + rootNode_->add(node); + } + endInsertRows(); } - endInsertRows(); std::vector txWallet; txWallet.reserve(entries.size()); @@ -1142,7 +1158,6 @@ void TransactionsViewModel::onTXDetails(const std::vectorwarn("[{}] invalid TX: {}", __func__, tx.txHash.toHexStr(true)); continue; } const int row = itIndex->second; @@ -1155,7 +1170,9 @@ void TransactionsViewModel::onTXDetails(const std::vectordirection = tx.direction; item->dirStr = QObject::tr(bs::sync::Transaction::toStringDir(tx.direction)); item->isValid = tx.isValid ? bs::sync::TxValidity::Valid : bs::sync::TxValidity::Invalid; - item->comment = QString::fromStdString(tx.comment); + if (!tx.comment.empty()) { + item->comment = QString::fromStdString(tx.comment); + } item->amountStr = QString::fromStdString(tx.amount); item->addressCount = tx.outAddresses.size(); @@ -1172,6 +1189,11 @@ void TransactionsViewModel::onTXDetails(const std::vector(Columns::Wallet)) , index(row, static_cast(Columns::Comment))); + + item->tx = tx.tx; + item->inputAddresses = tx.inputAddresses; + item->outputAddresses = tx.outputAddresses; + item->changeAddress = tx.changeAddress; } } diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index bcac281cb..62cfb8069 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -42,12 +42,16 @@ struct TransactionsViewItem { bs::TXEntry txEntry; Tx tx; + std::vector prevTXs; bool initialized = false; QString mainAddress; int addressCount; std::vector outAddresses; bs::sync::Transaction::Direction direction = bs::sync::Transaction::Unknown; std::vector> wallets; + std::vector inputAddresses; + std::vector outputAddresses; + bs::sync::AddressDetails changeAddress; QString dirStr; QString walletName; QString walletID; @@ -91,6 +95,7 @@ class TXNode ~TXNode() { clear(); } std::shared_ptr item() const { return item_; } + void setItem(const std::shared_ptr &item) { item_ = item; } size_t nbChildren() const { return children_.size(); } bool hasChildren() const { return !children_.empty(); } TXNode *child(int index) const; diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index 5e53bcb08..6f7c3e4bd 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -536,8 +536,17 @@ void TransactionsWidget::showTransactionDetails(const QModelIndex& index) return; } - TransactionDetailDialog transactionDetailDialog(txItem, walletsManager_, armory_, this); - transactionDetailDialog.exec(); + if (walletsManager_ && armory_) { + TransactionDetailDialog transactionDetailDialog(txItem, walletsManager_, armory_, this); + transactionDetailDialog.exec(); + } + else { + auto txDetailDialog = new TransactionDetailDialog(txItem, this); + connect(txDetailDialog, &QDialog::finished, [txDetailDialog](int) { + txDetailDialog->deleteLater(); + }); + txDetailDialog->show(); + } } void TransactionsWidget::updateResultCount() diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index d1890fab7..1a946205b 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -115,7 +115,7 @@ class WalletRootNode : public WalletNode } std::string id() const override { - return (!hdWallet_.id.empty() ? hdWallet_.id : std::string{}); + return (!hdWallet_.ids.empty() ? hdWallet_.ids[0] : std::string{}); } void setState(State state) override { @@ -256,12 +256,12 @@ class WalletLeafNode : public WalletRootNode std::string id() const override { - return wallet_.id; + return *wallet_.ids.cbegin(); } QVariant data(int col, int role) const override { if (role == Qt::FontRole) { - if (wallet_.id == viewModel_->selectedWallet()) { + if (*wallet_.ids.cbegin() == viewModel_->selectedWallet()) { QFont font; font.setUnderline(true); return font; @@ -310,7 +310,7 @@ class WalletGroupNode : public WalletRootNode } bs::sync::WalletInfo wi; wi.format = bs::sync::WalletFormat::Plain; - wi.id = leaf.id; + wi.ids = leaf.ids; wi.name = leaf.name; wi.description = leaf.description; wi.purpose = static_cast(leaf.path.get(-2)); @@ -600,7 +600,7 @@ void WalletsViewModel::onNewWalletAdded(const std::string &walletId) void WalletsViewModel::onHDWallet(const bs::sync::WalletInfo &wi) { - const auto &wallet = rootNode_->findByWalletId(wi.id); + const auto &wallet = rootNode_->findByWalletId(*wi.ids.cbegin()); int row = wallet ? wallet->row() : rootNode_->nbChildren(); const auto hdNode = new WalletRootNode(this, wi, wi.name, wi.description , wi.primary ? WalletNode::Type::WalletPrimary : WalletNode::Type::WalletRegular @@ -616,7 +616,7 @@ void WalletsViewModel::onHDWallet(const bs::sync::WalletInfo &wi) rootNode_->add(hdNode); endInsertRows(); } - emit needHDWalletDetails(wi.id); + emit needHDWalletDetails(*wi.ids.cbegin()); } void WalletsViewModel::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) @@ -665,14 +665,14 @@ void WalletsViewModel::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) endInsertRows(); } for (const auto &leaf : group.leaves) { - const auto &leafNode = groupNode->findByWalletId(leaf.id); + const auto &leafNode = groupNode->findByWalletId(*leaf.ids.cbegin()); if (!leafNode) { beginInsertRows(createIndex(groupNode->row(), 0, static_cast(groupNode)) , groupNode->nbChildren(), groupNode->nbChildren()); groupNode->addLeaf(leaf, groupType); endInsertRows(); } - emit needWalletBalances(leaf.id); + emit needWalletBalances(*leaf.ids.cbegin()); } } } @@ -712,7 +712,7 @@ void WalletsViewModel::LoadWallets(bool keepSelection) if (node != nullptr) { const auto &wallets = node->wallets(); if (wallets.size() == 1) { - selectedWalletId = wallets[0].id; + selectedWalletId = *wallets[0].ids.cbegin(); } } } diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 0cd1de6f0..39c321894 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -345,6 +345,13 @@ std::vector WalletsWidget::getFirstWallets() const } } +void WalletsWidget::onNewBlock(unsigned int blockNum) +{ + for (const auto &dlg : addrDetDialogs_) { + dlg.second->onNewBlock(blockNum); + } +} + void WalletsWidget::onHDWallet(const bs::sync::WalletInfo &wi) { walletsModel_->onHDWallet(wi); @@ -367,9 +374,25 @@ void WalletsWidget::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) //TODO: keep wallets treeView selection } -void WalletsWidget::onAddresses(const std::string &walletId, const std::vector &addrs) +void WalletsWidget::onAddresses(const std::vector &addrs) { - addressModel_->onAddresses(walletId, addrs); + addressModel_->onAddresses(addrs); +} + +void WalletsWidget::onLedgerEntries(const std::string &filter, uint32_t totalPages + , uint32_t curPage, uint32_t curBlock, const std::vector &entries) +{ + const auto &itDlg = addrDetDialogs_.find(filter); + if (itDlg != addrDetDialogs_.end()) { + itDlg->second->onLedgerEntries(curBlock, entries); + } +} + +void WalletsWidget::onTXDetails(const std::vector &details) +{ + for (const auto &dlg : addrDetDialogs_) { + dlg.second->onTXDetails(details); + } } void WalletsWidget::onAddressComments(const std::string &walletId @@ -408,7 +431,7 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) } const auto &hdWallet = node->hdWallet(); - if (!hdWallet.id.empty()) { + if (!(*hdWallet.ids.cbegin()).empty()) { if (walletsManager_ && armory_ && signingContainer_ && appSettings_ && connectionManager_ && assetManager_) { RootWalletPropertiesDialog(logger_, hdWallet, walletsManager_, armory_, signingContainer_ , walletsModel_, appSettings_, connectionManager_, assetManager_, this).exec(); @@ -430,20 +453,46 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) void WalletsWidget::showAddressProperties(const QModelIndex& index) { auto sourceIndex = addressSortFilterModel_->mapToSource(index); - const auto walletId = addressModel_->data(sourceIndex, AddressListModel::WalletIdRole).toString().toStdString(); - const auto wallet = walletsManager_->getWalletById(walletId); - if (!wallet || (wallet->type() == bs::core::wallet::Type::Authentication)) { - return; - } + const auto &walletId = addressModel_->data(sourceIndex, AddressListModel::WalletIdRole).toString().toStdString(); + if (walletsManager_ && armory_) { + const auto &wallet = walletsManager_->getWalletById(walletId); + if (!wallet || (wallet->type() == bs::core::wallet::Type::Authentication)) { + return; + } - const auto &addresses = wallet->getUsedAddressList(); - const size_t addrIndex = addressModel_->data(sourceIndex, AddressListModel::AddrIndexRole).toUInt(); - const auto address = (addrIndex < addresses.size()) ? addresses[addrIndex] : bs::Address(); + const auto &addresses = wallet->getUsedAddressList(); + const size_t addrIndex = addressModel_->data(sourceIndex, AddressListModel::AddrIndexRole).toUInt(); + const auto address = (addrIndex < addresses.size()) ? addresses[addrIndex] : bs::Address(); - wallet->onBalanceAvailable([this, address, wallet] { - auto dialog = new AddressDetailDialog(address, wallet, walletsManager_, armory_, logger_, this); - QMetaObject::invokeMethod(this, [dialog] { dialog->exec(); }); - }); + wallet->onBalanceAvailable([this, address, wallet] { + auto dialog = new AddressDetailDialog(address, wallet, walletsManager_, armory_, logger_, this); + QMetaObject::invokeMethod(this, [dialog] { dialog->exec(); }); + }); + } + else { + const auto &addrStr = addressModel_->data(sourceIndex, AddressListModel::AddressRole).toString().toStdString(); + const auto &ledgerFilter = walletId + "." + addrStr; + emit needLedgerEntries(ledgerFilter); + const auto wltType = static_cast(addressModel_->data(sourceIndex, AddressListModel::WalletTypeRole).toInt()); + const auto txn = addressModel_->data(sourceIndex, AddressListModel::TxNRole).toInt(); + const uint64_t balance = addressModel_->data(sourceIndex, AddressListModel::BalanceRole).toULongLong(); + const auto &walletName = addressModel_->data(sourceIndex, AddressListModel::WalletNameRole).toString(); + const auto &index = addressModel_->data(sourceIndex, AddressListModel::AddressIndexRole).toString().toStdString(); + const auto &comment = addressModel_->data(sourceIndex, AddressListModel::AddressCommentRole).toString().toStdString(); + try { + const auto &address = bs::Address::fromAddressString(addrStr); + auto dialog = new AddressDetailDialog(address, logger_, wltType, balance + , txn, walletName, index, comment, this); + addrDetDialogs_[ledgerFilter] = dialog; + connect(dialog, &AddressDetailDialog::needTXDetails, this, &WalletsWidget::needTXDetails); + connect(dialog, &QDialog::finished, [dialog, ledgerFilter, this](int) { + dialog->deleteLater(); + addrDetDialogs_.erase(ledgerFilter); + }); + dialog->show(); + } + catch (const std::exception &) {} + } } void WalletsWidget::onAddressContextMenu(const QPoint &p) @@ -637,7 +686,7 @@ void WalletsWidget::onWalletBalanceChanged(std::string walletId) const auto &selectedWallets = getSelectedWallets(); bool changedSelected = false; for (const auto &wallet : selectedWallets) { - if (wallet.id == walletId) { + if (*wallet.ids.cbegin() == walletId) { changedSelected = true; break; } diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index c5f1fa65a..686745036 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -37,6 +37,7 @@ namespace bs { class WalletsManager; } } +class AddressDetailDialog; class AddressListModel; class AddressSortFilterModel; class ApplicationSettings; @@ -81,12 +82,16 @@ Q_OBJECT void ImportNewWallet(); void ImportHwWallet(); + void onNewBlock(unsigned int blockNum); void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); - void onAddresses(const std::string &walletId, const std::vector &); + void onAddresses(const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); void onWalletBalance(const bs::sync::WalletBalanceData &); + void onLedgerEntries(const std::string &filter, uint32_t totalPages + , uint32_t curPage, uint32_t curBlock, const std::vector &); + void onTXDetails(const std::vector &); void shortcutActivated(ShortcutType s) override; @@ -116,6 +121,8 @@ public slots: void needAddrComments(const std::string &walletId, const std::vector &); void setAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); + void needLedgerEntries(const std::string &filter); + void needTXDetails(const std::vector &); private slots: void showWalletProperties(const QModelIndex& index); @@ -169,6 +176,7 @@ private slots: int prevSelectedAddressRow_{-1}; QPoint walletsScrollPos_; QPoint addressesScrollPos_; + std::unordered_map addrDetDialogs_; }; #endif // __WALLETS_WIDGET_H__ diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 1f6684454..c1fe52987 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -328,7 +328,9 @@ bool SignerAdapter::processStartWalletSync(const bs::message::Envelope &env) for (const auto &entry : wi) { auto wallet = msgResp->add_wallets(); wallet->set_format((int)entry.format); - wallet->set_id(entry.id); + for (const auto &id : entry.ids) { + wallet->add_ids(id); + } wallet->set_name(entry.name); wallet->set_description(entry.description); wallet->set_network_type((int)entry.netType); diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 46d25b340..0ec3216bb 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -166,6 +166,7 @@ void MainWindow::onNewBlock(int state, unsigned int blockNum) if (txModel_) { txModel_->onNewBlock(blockNum); } + ui_->widgetWallets->onNewBlock(blockNum); } void MainWindow::onWalletsReady() @@ -192,10 +193,9 @@ void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) ui_->widgetWallets->onHDWalletDetails(hdWallet); } -void MainWindow::onAddresses(const std::string &walletId - , const std::vector &addrs) +void MainWindow::onAddresses(const std::vector &addrs) { - ui_->widgetWallets->onAddresses(walletId, addrs); + ui_->widgetWallets->onAddresses(addrs); } void MainWindow::onAddressComments(const std::string &walletId @@ -215,11 +215,15 @@ void MainWindow::onLedgerEntries(const std::string &filter, uint32_t totalPages if (filter.empty()) { txModel_->onLedgerEntries(filter, totalPages, curPage, curBlock, entries); } + else { + ui_->widgetWallets->onLedgerEntries(filter, totalPages, curPage, curBlock, entries); + } } void MainWindow::onTXDetails(const std::vector &txDet) { txModel_->onTXDetails(txDet); + ui_->widgetWallets->onTXDetails(txDet); } void MainWindow::showStartupDialog(bool showLicense) @@ -542,15 +546,14 @@ void MainWindow::onSend() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallet = ui_->widgetWallets->getSelectedHdWallet(); - if (wallet.id.empty()) { + if (wallet.ids.empty()) { // wallet = walletsMgr_->getPrimaryWallet(); } - if (!wallet.id.empty()) { - selectedWalletId = wallet.id; + if (!wallet.ids.empty()) { + selectedWalletId = *wallet.ids.cbegin(); } } - std::shared_ptr dlg; /* if ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) @@ -978,6 +981,8 @@ void MainWindow::initWidgets() connect(ui_->widgetWallets, &WalletsWidget::needUsedAddresses, this, &MainWindow::needUsedAddresses); connect(ui_->widgetWallets, &WalletsWidget::needAddrComments, this, &MainWindow::needAddrComments); connect(ui_->widgetWallets, &WalletsWidget::setAddrComment, this, &MainWindow::setAddrComment); + connect(ui_->widgetWallets, &WalletsWidget::needLedgerEntries, this, &MainWindow::needLedgerEntries); + connect(ui_->widgetWallets, &WalletsWidget::needTXDetails, this, &MainWindow::needTXDetails); initTransactionsView(); // InitPortfolioView(); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 5720055a4..e52bd5353 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -62,8 +62,7 @@ namespace bs { void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); - void onAddresses(const std::string &walletId - , const std::vector &); + void onAddresses(const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); void onWalletBalance(const bs::sync::WalletBalanceData &); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index a98f4d52e..eed4f8a7d 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -477,16 +477,17 @@ bool QtGuiAdapter::processWallets(const Envelope &env) break; case WalletsMessage::kWalletAddresses: { - std::vector addresses; + std::vector addresses; addresses.reserve(msg.wallet_addresses().addresses_size()); for (const auto &addr : msg.wallet_addresses().addresses()) { try { - addresses.emplace_back(std::move(bs::Address::fromAddressString(addr))); + addresses.push_back({ std::move(bs::Address::fromAddressString(addr.address())) + , addr.index(), addr.wallet_id() }); } catch (const std::exception &) {} } - QMetaObject::invokeMethod(mainWindow_, [this, addresses, walletId=msg.wallet_addresses().wallet_id()] { - mainWindow_->onAddresses(walletId, addresses); + QMetaObject::invokeMethod(mainWindow_, [this, addresses] { + mainWindow_->onAddresses(addresses); }); } break; @@ -775,7 +776,8 @@ void QtGuiAdapter::onNeedAddrComments(const std::string &walletId auto msgReq = msg.mutable_get_addr_comments(); msgReq->set_wallet_id(walletId); for (const auto &addr : addrs) { - msgReq->add_addresses(addr.display()); + auto addrReq = msgReq->add_addresses(); + addrReq->set_address(addr.display()); } Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); @@ -818,7 +820,7 @@ void QtGuiAdapter::onNeedTXDetails(const const std::vector & void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { - hdWallets_[wi.id] = wi; + hdWallets_[*wi.ids.cbegin()] = wi; if (mainWindow_) { QMetaObject::invokeMethod(mainWindow_, [this, wi] { mainWindow_->onHDWallet(wi); @@ -850,17 +852,61 @@ bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &resp { std::vector txDetails; for (const auto &resp : response.responses()) { - std::vector outAddrs; + bs::sync::TXWalletDetails txDet{ BinaryData::fromString(resp.tx_hash()), resp.wallet_id() + , resp.wallet_name(), static_cast(resp.wallet_type()) + , static_cast(resp.direction()) + , resp.comment(), resp.valid(), resp.amount() }; + + const auto &ownTxHash = BinaryData::fromString(resp.tx_hash()); + try { + if (!resp.tx().empty()) { + Tx tx(BinaryData::fromString(resp.tx())); + if (tx.isInitialized()) { + txDet.tx = std::move(tx); + } + } + } catch (const std::exception &e) { + logger_->warn("[QtGuiAdapter::processTXDetails] TX deser error: {}", e.what()); + } for (const auto &addrStr : resp.out_addresses()) { try { - outAddrs.emplace_back(std::move(bs::Address::fromAddressString(addrStr))); + txDet.outAddresses.emplace_back(std::move(bs::Address::fromAddressString(addrStr))); + } catch (const std::exception &e) { + logger_->warn("[QtGuiAdapter::processTXDetails] out deser error: {}", e.what()); } - catch (const std::exception &) {} } - txDetails.push_back({ BinaryData::fromString(resp.tx_hash()), resp.wallet_id() - , resp.wallet_name(), static_cast(resp.wallet_type()) - , static_cast(resp.direction()) - , resp.comment(), resp.valid(), resp.amount(), outAddrs }); + for (const auto &inAddr : resp.input_addresses()) { + try { + txDet.inputAddresses.push_back({ bs::Address::fromAddressString(inAddr.address()) + , inAddr.value(), inAddr.value_string(), inAddr.wallet_name() + , static_cast(inAddr.script_type()) + , BinaryData::fromString(inAddr.out_hash()), inAddr.out_index() }); + } catch (const std::exception &e) { + logger_->warn("[QtGuiAdapter::processTXDetails] input deser error: {}", e.what()); + } + } + for (const auto &outAddr : resp.output_addresses()) { + try { + txDet.outputAddresses.push_back({ bs::Address::fromAddressString(outAddr.address()) + , outAddr.value(), outAddr.value_string(), outAddr.wallet_name() + , static_cast(outAddr.script_type()), ownTxHash + , outAddr.out_index() }); + } catch (const std::exception &e) { // OP_RETURN data for valueStr + txDet.outputAddresses.push_back({ bs::Address{} + , outAddr.value(), outAddr.address(), outAddr.wallet_name() + , static_cast(outAddr.script_type()), ownTxHash + , outAddr.out_index() }); + } + } + try { + txDet.changeAddress = { bs::Address::fromAddressString(resp.change_address().address()) + , resp.change_address().value(), resp.change_address().value_string() + , resp.change_address().wallet_name() + , static_cast(resp.change_address().script_type()) + , ownTxHash, resp.change_address().out_index() }; + } + catch (const std::exception &) {} + txDetails.push_back(txDet); } QMetaObject::invokeMethod(mainWindow_, [this, txDetails] { mainWindow_->onTXDetails(txDetails); diff --git a/common b/common index 00896ce9f..35993048c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 00896ce9f2836e02830d87473ec0d4e440bb102a +Subproject commit 35993048c176b55466351079569a43100699a135 From 71a2e4937b744bc5931ab8ce84bcd03dec56e569 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Sat, 19 Sep 2020 00:07:56 +0300 Subject: [PATCH 009/146] Explorer functionality --- BlockSettleUILib/AddressDetailDialog.h | 2 +- BlockSettleUILib/AddressDetailsWidget.cpp | 137 +++++++++++++++- BlockSettleUILib/AddressDetailsWidget.h | 25 ++- BlockSettleUILib/ExplorerWidget.cpp | 35 +++++ BlockSettleUILib/ExplorerWidget.h | 13 +- BlockSettleUILib/StatusBarView.cpp | 20 +++ BlockSettleUILib/StatusBarView.h | 5 +- BlockSettleUILib/TransactionDetailsWidget.cpp | 148 +++++++++++++++++- BlockSettleUILib/TransactionDetailsWidget.h | 15 +- BlockSettleUILib/TransactionsViewModel.cpp | 2 +- BlockSettleUILib/TransactionsViewModel.h | 2 +- BlockSettleUILib/WalletsWidget.h | 2 +- Core/SettingsAdapter.h | 2 +- GUI/QtWidgets/MainWindow.cpp | 18 ++- GUI/QtWidgets/MainWindow.h | 5 +- GUI/QtWidgets/QtGuiAdapter.cpp | 76 ++++++++- GUI/QtWidgets/QtGuiAdapter.h | 9 +- common | 2 +- 18 files changed, 473 insertions(+), 45 deletions(-) diff --git a/BlockSettleUILib/AddressDetailDialog.h b/BlockSettleUILib/AddressDetailDialog.h index be93662fb..fbb930391 100644 --- a/BlockSettleUILib/AddressDetailDialog.h +++ b/BlockSettleUILib/AddressDetailDialog.h @@ -55,7 +55,7 @@ Q_OBJECT void onTXDetails(const std::vector &); signals: - void needTXDetails(const std::vector &); + void needTXDetails(const std::vector &, const bs::Address &); private slots: void onCopyClicked() const; diff --git a/BlockSettleUILib/AddressDetailsWidget.cpp b/BlockSettleUILib/AddressDetailsWidget.cpp index bb34a012a..8e76ac623 100644 --- a/BlockSettleUILib/AddressDetailsWidget.cpp +++ b/BlockSettleUILib/AddressDetailsWidget.cpp @@ -63,6 +63,11 @@ void AddressDetailsWidget::init(const std::shared_ptr &armory act_->init(armory_.get()); } +void AddressDetailsWidget::init(const std::shared_ptr &logger) +{ + logger_ = logger; +} + void AddressDetailsWidget::setBSAuthAddrs(const std::unordered_set &bsAuthAddrs) { if (bsAuthAddrs.empty()) { @@ -93,13 +98,19 @@ void AddressDetailsWidget::setQueryAddr(const bs::Address &inAddrVal) // Armory can't directly take an address and return all the required data. // Work around this by creating a dummy wallet, adding the explorer address, // registering the wallet, and getting the required data. - const auto walletId = CryptoPRNG::generateRandom(8).toHexStr(); - const auto dummyWallet = std::make_shared(walletId - , "temporary", "Dummy explorer wallet", nullptr, logger_); - dummyWallet->addAddress(inAddrVal, {}, false); - const auto regIds = dummyWallet->registerWallet(armory_, true); - for (const auto ®Id : regIds) { - dummyWallets_[regId] = dummyWallet; + if (armory_) { + const auto walletId = CryptoPRNG::generateRandom(8).toHexStr(); + const auto dummyWallet = std::make_shared(walletId + , "temporary", "Dummy explorer wallet", nullptr, logger_); + dummyWallet->addAddress(inAddrVal, {}, false); + const auto regIds = dummyWallet->registerWallet(armory_, true); + for (const auto& regId : regIds) { + dummyWallets_[regId] = dummyWallet; + } + } + else { + ui_->addressId->setText(QString::fromStdString(currentAddrStr_)); + emit needAddressHistory(inAddrVal); } updateFields(); } @@ -519,3 +530,115 @@ void AddressDetailsWidget::clear() ui_->totalReceived->setText(loading); ui_->totalSent->setText(loading); } + +void AddressDetailsWidget::onNewBlock(unsigned int blockNum) +{ + topBlock_ = blockNum; + //TODO: update conf counts +} + +void AddressDetailsWidget::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) +{ + if (addr != currentAddr_) { + return; // address has probably changed + } + topBlock_ = curBlock; + txEntryHashSet_.clear(); + + // Get the hash and TXEntry object for each relevant Tx hash. + std::vector txDetRequest; + for (const auto& entry : entries) { + const auto& itTX = txMap_.find(entry.txHash); + if (itTX == txMap_.end()) { + txDetRequest.push_back({ entry.txHash, {}, entry.value }); + txEntryHashSet_[entry.txHash] = entry; + } + } + if (txDetRequest.empty()) { + SPDLOG_LOGGER_INFO(logger_, "address {} participates in no TXs", currentAddrStr_); + onTXDetails({}); + } + else { + emit needTXDetails(txDetRequest, currentAddr_); + } +} + +void AddressDetailsWidget::onTXDetails(const std::vector& txDet) +{ + for (const auto& tx : txDet) { + if (txEntryHashSet_.find(tx.txHash) == txEntryHashSet_.end()) { + return; // not our TX details + } + } + CustomTreeWidget* tree = ui_->treeAddressTransactions; + tree->clear(); + + unsigned int totCount = 0; + + // Go through each TXEntry object and calculate all required UI data. + for (const auto& tx : txDet) { + QTreeWidgetItem* item = new QTreeWidgetItem(tree); + // Get fees & fee/byte by looping through the prev Tx set and calculating. + uint64_t fees = 0; + for (const auto& inAddr : tx.inputAddresses) { + fees += inAddr.value; + } + for (const auto& outAddr : tx.outputAddresses) { + fees -= outAddr.value; + } + double feePerByte = (double)fees / (double)tx.tx.getTxWeight(); + + const auto& itEntry = txEntryHashSet_.find(tx.txHash); + if (itEntry == txEntryHashSet_.end()) { + logger_->error("[{}] can't find TXEntry for {}", __func__, tx.txHash.toHexStr(true)); + continue; + } + + // Populate the transaction entries. + item->setText(colDate, + UiUtils::displayDateTime(QDateTime::fromTime_t(itEntry->second.txTime))); + item->setText(colTxId, // Flip Armory's TXID byte order: internal -> RPC + QString::fromStdString(tx.txHash.toHexStr(true))); + item->setData(colConfs, Qt::DisplayRole, itEntry->second.nbConf); + item->setText(colInputsNum, QString::number(tx.tx.getNumTxIn())); + item->setText(colOutputsNum, QString::number(tx.tx.getNumTxOut())); + item->setText(colFees, UiUtils::displayAmount(fees)); + item->setText(colFeePerByte, QString::number(std::nearbyint(feePerByte))); + item->setText(colTxSize, QString::number(tx.tx.getSize())); + + item->setText(colOutputAmt, UiUtils::displayAmount(itEntry->second.value)); + item->setTextAlignment(colOutputAmt, Qt::AlignRight); + + QFont font = item->font(colOutputAmt); + font.setBold(true); + item->setFont(colOutputAmt, font); + + if (!tx.isValid) { + // Mark invalid transactions + item->setTextColor(colOutputAmt, Qt::red); + } + + // Check the total received or sent. + if (itEntry->second.value > 0) { + totalReceived_ += itEntry->second.value; + } + else { + totalSpent_ -= itEntry->second.value; // Negative, so fake that out. + } + totCount++; + + setConfirmationColor(item); + tree->addTopLevelItem(item); + } + + ui_->totalReceived->setText(UiUtils::displayAmount(totalReceived_)); + ui_->totalSent->setText(UiUtils::displayAmount(totalSpent_)); + ui_->balance->setText(UiUtils::displayAmount(totalReceived_ - totalSpent_)); + + emit finished(); + + // Set up the display for total rcv'd/spent. + ui_->transactionCount->setText(QString::number(totCount)); + + tree->resizeColumns(); +} diff --git a/BlockSettleUILib/AddressDetailsWidget.h b/BlockSettleUILib/AddressDetailsWidget.h index 64aa6de49..708b85a1f 100644 --- a/BlockSettleUILib/AddressDetailsWidget.h +++ b/BlockSettleUILib/AddressDetailsWidget.h @@ -14,6 +14,7 @@ #include "Address.h" #include "AuthAddress.h" #include "ArmoryConnection.h" +#include "SignerDefs.h" #include #include @@ -41,14 +42,21 @@ class AddressDetailsWidget : public QWidget explicit AddressDetailsWidget(QWidget *parent = nullptr); ~AddressDetailsWidget() override; - void init(const std::shared_ptr &armory + [[deprecated]] void init(const std::shared_ptr &armory , const std::shared_ptr &inLogger , const std::shared_ptr & , const std::shared_ptr &); + void init(const std::shared_ptr& inLogger); + void setQueryAddr(const bs::Address& inAddrVal); void setBSAuthAddrs(const std::unordered_set &bsAuthAddrs); void clear(); + void onNewBlock(unsigned int blockNum); + void onAddressHistory(const bs::Address&, uint32_t curBlock + , const std::vector&); + void onTXDetails(const std::vector&); + enum AddressTreeColumns { colDate, colTxId, @@ -64,19 +72,21 @@ class AddressDetailsWidget : public QWidget signals: void transactionClicked(QString txId); void finished() const; + void needAddressHistory(const bs::Address&); + void needTXDetails(const std::vector&, const bs::Address &); private slots: void onTxClicked(QTreeWidgetItem *item, int column); - void OnRefresh(std::vector ids, bool online); + void OnRefresh(std::vector ids, bool online); //deprecated void updateFields(); private: void setConfirmationColor(QTreeWidgetItem *item); - void getTxData(const std::shared_ptr &); - void refresh(const std::shared_ptr &); - void loadTransactions(); - void searchForCC(); - void searchForAuth(); + [[deprecated]] void getTxData(const std::shared_ptr &); + [[deprecated]] void refresh(const std::shared_ptr &); + [[deprecated]] void loadTransactions(); + [[deprecated]] void searchForCC(); + [[deprecated]] void searchForAuth(); private: // NB: Right now, the code is slightly inefficient. There are two maps with @@ -122,6 +132,7 @@ private slots: std::map authAddrStates_; std::unordered_set bsAuthAddrs_; bool isAuthAddr_{false}; + uint32_t topBlock_{ 0 }; std::mutex mutex_; diff --git a/BlockSettleUILib/ExplorerWidget.cpp b/BlockSettleUILib/ExplorerWidget.cpp index 655218d08..38d13345f 100644 --- a/BlockSettleUILib/ExplorerWidget.cpp +++ b/BlockSettleUILib/ExplorerWidget.cpp @@ -89,6 +89,20 @@ void ExplorerWidget::init(const std::shared_ptr &armory "Search for a transaction or address.")); } +void ExplorerWidget::init(const std::shared_ptr &logger) +{ + logger_ = logger; + ui_->Transaction->init(logger); + ui_->Address->init(logger); + + ui_->searchBox->setReadOnly(false); + ui_->searchBox->setPlaceholderText(tr("Search for a transaction or address")); + + connect(ui_->Address, &AddressDetailsWidget::needAddressHistory, this, &ExplorerWidget::needAddressHistory); + connect(ui_->Address, &AddressDetailsWidget::needTXDetails, this, &ExplorerWidget::needTXDetails); + connect(ui_->Transaction, &TransactionDetailsWidget::needTXDetails, this, &ExplorerWidget::needTXDetails); +} + void ExplorerWidget::shortcutActivated(ShortcutType) {} @@ -101,6 +115,27 @@ void ExplorerWidget::mousePressEvent(QMouseEvent *event) } } +void ExplorerWidget::onNewBlock(unsigned int blockNum) +{ + ui_->Address->onNewBlock(blockNum); + ui_->Transaction->onNewBlock(blockNum); +} + +void ExplorerWidget::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) +{ + ui_->Address->onAddressHistory(addr, curBlock, entries); +} + +void ExplorerWidget::onTXDetails(const std::vector& txDet) +{ + if (ui_->stackedWidget->currentIndex() == AddressPage) { + ui_->Address->onTXDetails(txDet); + } + else if (ui_->stackedWidget->currentIndex() == TxPage) { + ui_->Transaction->onTXDetails(txDet); + } +} + // The function called when the user uses the search bar (Tx or address). void ExplorerWidget::onSearchStarted(bool saveToHistory) { diff --git a/BlockSettleUILib/ExplorerWidget.h b/BlockSettleUILib/ExplorerWidget.h index 5500fae76..174edd209 100644 --- a/BlockSettleUILib/ExplorerWidget.h +++ b/BlockSettleUILib/ExplorerWidget.h @@ -13,6 +13,7 @@ #include "TabWithShortcut.h" #include "ArmoryConnection.h" +#include "SignerDefs.h" #include #include @@ -36,11 +37,12 @@ Q_OBJECT ExplorerWidget(QWidget *parent = nullptr); ~ExplorerWidget() override; - void init(const std::shared_ptr &armory + [[deprecated]] void init(const std::shared_ptr &armory , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &); + void init(const std::shared_ptr&); void shortcutActivated(ShortcutType s) override; enum Page { @@ -51,6 +53,15 @@ Q_OBJECT void mousePressEvent(QMouseEvent *event) override; + void onNewBlock(unsigned int blockNum); + void onAddressHistory(const bs::Address&, uint32_t curBlock + , const std::vector&); + void onTXDetails(const std::vector&); + +signals: + void needAddressHistory(const bs::Address&); + void needTXDetails(const std::vector&, const bs::Address&); + protected slots: void onSearchStarted(bool saveToHistory); void onExpTimeout(); diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index f12ae8bcb..ca9c25b07 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -194,6 +194,26 @@ void StatusBarView::onBlockchainStateChanged(int state, unsigned int blockNum) onArmoryStateChanged(static_cast(state), blockNum); } +void StatusBarView::onXbtBalance(const bs::sync::WalletBalanceData &wbd) +{ + xbtBalances_[wbd.id] = wbd.balTotal; + displayXbtBalance(); +} + +void StatusBarView::displayXbtBalance() +{ + BTCNumericTypes::balance_type accBalance = 0; + for (const auto& bal : xbtBalances_) { + accBalance += bal.second; + } + const auto xbt = UiUtils::displayAmount(accBalance); + QString text = tr(" XBT: %1 ").arg(xbt); + balanceLabel_->setText(text); + progressBar_->setVisible(false); + estimateLabel_->setVisible(false); + connectionStatusLabel_->show(); +} + void StatusBarView::onStateChanged(ArmoryState state) { QMetaObject::invokeMethod(this, [this, state] { onArmoryStateChanged(state, blockNum_); }); diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index 04c2f29d1..b02d57139 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -58,10 +58,11 @@ public slots: void onConnectionError(int errorCode); void onContainerAuthorized(); void onSignerStatusChanged(SignContainer::ConnectionError error, const QString &details); - void updateBalances(); + void updateBalances(); //deprecated void onWalletImportStarted(const std::string &walletId); void onWalletImportFinished(const std::string &walletId); void onBlockchainStateChanged(int, unsigned int); + void onXbtBalance(const bs::sync::WalletBalanceData&); private: [[deprecated]] void onStateChanged(ArmoryState) override; @@ -85,6 +86,7 @@ public slots: void SetCelerConnectingStatus(); QWidget *CreateSeparator(); [[deprecated]] void setBalances(); + void displayXbtBalance(); void updateConnectionStatusDetails(ArmoryState state, unsigned int blockNum); private: @@ -122,6 +124,7 @@ public slots: std::unordered_set importingWallets_; std::vector balanceSymbols_; std::unordered_map balances_; + std::unordered_map xbtBalances_; int armoryState_{ -1 }; unsigned int blockNum_{ 0 }; }; diff --git a/BlockSettleUILib/TransactionDetailsWidget.cpp b/BlockSettleUILib/TransactionDetailsWidget.cpp index 80c551cad..66d5ec242 100644 --- a/BlockSettleUILib/TransactionDetailsWidget.cpp +++ b/BlockSettleUILib/TransactionDetailsWidget.cpp @@ -70,6 +70,11 @@ void TransactionDetailsWidget::init( act_->init(armoryPtr_.get()); } +void TransactionDetailsWidget::init(const std::shared_ptr &logger) +{ + logger_ = logger; +} + // This function uses getTxByHash() to retrieve info about transaction. The // incoming TXID must be in RPC order, not internal order. void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID @@ -79,6 +84,38 @@ void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID if (firstPass) { clear(); } + + if (!armoryPtr_) { + if (rpcTXID.getSize() == 32) { + curTxHash_ = rpcTXID; + emit needTXDetails({ { curTxHash_, {}, 0 } }, {}); + } + else { + Codec_SignerState::SignerState signerState; + if (signerState.ParseFromString(rpcTXID.toBinStr(true))) { + Signer signer(signerState); + const auto& serTx = signer.serializeUnsignedTx(true); + try { + const Tx tx(serTx); + if (!tx.isInitialized()) { + throw std::runtime_error("Uninited TX"); + } + curTxHash_ = tx.getThisHash(); + emit needTXDetails({ { tx.getThisHash(), {}, 0 } }, {}); + } + catch (const std::exception& e) { + logger_->error("[TransactionDetailsWidget::populateTransactionWidget]" + " {}", e.what()); + } + } + else { + logger_->error("[TransactionDetailsWidget::populateTransactionWidget]" + " failed to decode signer state"); + } + } + return; + } + // get the transaction data from armory const auto txidStr = rpcTXID.getRPCTXID(); const auto cbTX = [this, txidStr](const Tx &tx) { @@ -96,10 +133,6 @@ void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID if (firstPass || !curTx_.isInitialized() || (curTx_.getThisHash() != rpcTXID)) { if (rpcTXID.getSize() == 32) { - if (!armoryPtr_) { - logger_->error("[TransactionDetailsWidget::populateTransactionWidget] Armory is not inited"); - return; - } if (!armoryPtr_->getTxByHash(rpcTXID, cbTX, false)) { if (logger_) { logger_->error("[TransactionDetailsWidget::populateTransactionWidget]" @@ -135,6 +168,105 @@ void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID } } +void TransactionDetailsWidget::onTXDetails(const std::vector &txDet) +{ + if ((txDet.size() > 1) || (!txDet.empty() && (txDet[0].txHash != curTxHash_))) { + logger_->debug("[{}] not our TX details", __func__); + return; // not our data + } + if (!txDet.empty()) { + curTx_ = txDet[0].tx; + } + if (!curTx_.isInitialized()) { + ui_->tranID->setText(tr("Loading...")); + return; + } + ui_->tranID->setText(QString::fromStdString(curTx_.getThisHash().toHexStr(true))); + emit finished(); + + const auto txHeight = curTx_.getTxHeight(); + ui_->nbConf->setVisible(txHeight != UINT32_MAX); + ui_->labelNbConf->setVisible(txHeight != UINT32_MAX); + const uint32_t nbConf = topBlock_ ? topBlock_ + 1 - txHeight : 0; + ui_->nbConf->setText(QString::number(nbConf)); + + if (txDet.empty()) { + return; + } + // Get fees & fee/byte by looping through the prev Tx set and calculating. + uint64_t totIn = 0; + for (const auto& inAddr : txDet[0].inputAddresses) { + totIn += inAddr.value; + } + + uint64_t fees = totIn - curTx_.getSumOfOutputs(); + float feePerByte = (float)fees / (float)curTx_.getTxWeight(); + ui_->tranInput->setText(UiUtils::displayAmount(totIn)); + ui_->tranFees->setText(UiUtils::displayAmount(fees)); + ui_->tranFeePerByte->setText(QString::number(nearbyint(feePerByte))); + ui_->tranNumInputs->setText(QString::number(curTx_.getNumTxIn())); + ui_->tranNumOutputs->setText(QString::number(curTx_.getNumTxOut())); + ui_->tranOutput->setText(UiUtils::displayAmount(curTx_.getSumOfOutputs())); + ui_->tranSize->setText(QString::number(curTx_.getTxWeight())); + + ui_->treeInput->clear(); + ui_->treeOutput->clear(); + + std::map hashCounts; + for (const auto &inAddr : txDet[0].inputAddresses) { + hashCounts[inAddr.outHash]++; + } + + // here's the code to add data to the Input tree. + for (const auto& inAddr : txDet[0].inputAddresses) { + QString addrStr; + const QString walletName = QString::fromStdString(inAddr.walletName); + + // For now, don't display any data if the TxOut is non-std. Displaying a + // hex version of the script is one thing that could be done. This needs + // to be discussed before implementing. Non-std could mean many things. + if (inAddr.type == TXOUT_SCRIPT_NONSTANDARD) { + addrStr = tr(""); + } + else { + addrStr = QString::fromStdString(inAddr.address.display()); + } + + // create a top level item using type, address, amount, wallet values + addItem(ui_->treeInput, addrStr, inAddr.value, walletName, inAddr.outHash + , (hashCounts[inAddr.outHash] > 1) ? inAddr.outIndex : -1); + } + + std::vector outputAddresses = txDet[0].outputAddresses; + if (!txDet[0].changeAddress.address.empty()) { + outputAddresses.push_back(txDet[0].changeAddress); + } + for (const auto &outAddr : outputAddresses) { + QString addrStr; + QString walletName; + + // For now, don't display any data if the TxOut is OP_RETURN or non-std. + // Displaying a hex version of the script is one thing that could be done. + // This needs to be discussed before implementing. OP_RETURN isn't too bad + // (80 bytes max) but non-std could mean just about anything. + if (outAddr.type == TXOUT_SCRIPT_OPRETURN) { + addrStr = tr(""); + } + else if (outAddr.type == TXOUT_SCRIPT_NONSTANDARD) { + addrStr = tr(""); + } + else { + walletName = QString::fromStdString(outAddr.walletName); + addrStr = QString::fromStdString(outAddr.address.display()); + } + + addItem(ui_->treeOutput, addrStr, outAddr.value, walletName, outAddr.outHash); + } + + ui_->treeInput->resizeColumns(); + ui_->treeOutput->resizeColumns(); +} + // Used in callback to process the Tx object returned by Armory. void TransactionDetailsWidget::processTxData(const Tx &tx) { @@ -221,8 +353,13 @@ void TransactionDetailsWidget::setTxGUIValues() loadInputs(); } -void TransactionDetailsWidget::onNewBlock(unsigned int) +void TransactionDetailsWidget::onNewBlock(unsigned int curBlock) { + if (!armoryPtr_) { + topBlock_ = curBlock; + onTXDetails({}); + return; + } if (curTx_.isInitialized()) { populateTransactionWidget(curTx_.getThisHash(), false); } @@ -439,6 +576,7 @@ void TransactionDetailsWidget::clear() { prevTxMap_.clear(); curTx_ = Tx(); + curTxHash_.clear(); ui_->tranID->clear(); ui_->tranNumInputs->clear(); diff --git a/BlockSettleUILib/TransactionDetailsWidget.h b/BlockSettleUILib/TransactionDetailsWidget.h index c77318518..c234e0958 100644 --- a/BlockSettleUILib/TransactionDetailsWidget.h +++ b/BlockSettleUILib/TransactionDetailsWidget.h @@ -14,6 +14,7 @@ #include "ArmoryConnection.h" #include "BinaryData.h" #include "CCFileManager.h" +#include "SignerDefs.h" #include "TxClasses.h" #include @@ -74,10 +75,11 @@ class TransactionDetailsWidget : public QWidget explicit TransactionDetailsWidget(QWidget *parent = nullptr); ~TransactionDetailsWidget() override; - void init(const std::shared_ptr & + [[deprecated]] void init(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &); + void init(const std::shared_ptr&); void populateTransactionWidget(const TxHash &rpcTXID, const bool& firstPass = true); @@ -88,14 +90,19 @@ class TransactionDetailsWidget : public QWidget colWallet }; + void onTXDetails(const std::vector&); + +public slots: + void onNewBlock(unsigned int blockNum); + signals: void addressClicked(QString addressId); void txHashClicked(QString txHash); void finished() const; + void needTXDetails(const std::vector&, const bs::Address&); protected slots: void onAddressClicked(QTreeWidgetItem *item, int column); - void onNewBlock(unsigned int); protected: void loadTreeIn(CustomTreeWidget *tree); @@ -108,7 +115,7 @@ protected slots: void updateCCInputs(); void checkTxForCC(const Tx &, QTreeWidget *); - void processTxData(const Tx &tx); + [[deprecated]] void processTxData(const Tx &tx); void addItem(QTreeWidget *tree, const QString &address, const uint64_t amount , const QString &wallet, const BinaryData &txHash, const int txIndex = -1); @@ -123,6 +130,8 @@ protected slots: std::shared_ptr ccResolver_; Tx curTx_; // The Tx being analyzed in the widget. + BinaryData curTxHash_; + uint32_t topBlock_{ 0 }; // Data captured from Armory callbacks. AsyncClient::TxBatchResult prevTxMap_; // Prev Tx hash / Prev Tx map. diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index aa1b180aa..307865865 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -1150,7 +1150,7 @@ void TransactionsViewModel::onLedgerEntries(const std::string &, uint32_t const auto &walletId = entry.walletIds.empty() ? std::string{} : *(entry.walletIds.cbegin()); txWallet.push_back({ entry.txHash, walletId, entry.value }); } //TODO: can be optimized later to retrieve details only for visible rows - emit needTXDetails(txWallet); + emit needTXDetails(txWallet, {}); } void TransactionsViewModel::onTXDetails(const std::vector &txDet) diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index 62cfb8069..c59c35f2d 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -176,7 +176,7 @@ Q_OBJECT void onTXDetails(const std::vector &); signals: - void needTXDetails(const std::vector &); + void needTXDetails(const std::vector &, const bs::Address &); private slots: void updatePage(); diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 686745036..14d6be527 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -122,7 +122,7 @@ public slots: void setAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); void needLedgerEntries(const std::string &filter); - void needTXDetails(const std::vector &); + void needTXDetails(const std::vector &, const bs::Address &); private slots: void showWalletProperties(const QModelIndex& index); diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index 02dc555ab..90f9088b4 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -21,7 +21,7 @@ namespace spdlog { } namespace bs { class LogManager; - class TradeSettings; + struct TradeSettings; } namespace BlockSettle { namespace Terminal { diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 0ec3216bb..55b2d8ada 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -156,6 +156,7 @@ void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) if (statusBarView_) { statusBarView_->onBlockchainStateChanged(state, blockNum); } + ui_->widgetExplorer->onNewBlock(blockNum); } void MainWindow::onNewBlock(int state, unsigned int blockNum) @@ -167,6 +168,7 @@ void MainWindow::onNewBlock(int state, unsigned int blockNum) txModel_->onNewBlock(blockNum); } ui_->widgetWallets->onNewBlock(blockNum); + ui_->widgetExplorer->onNewBlock(blockNum); } void MainWindow::onWalletsReady() @@ -207,6 +209,7 @@ void MainWindow::onAddressComments(const std::string &walletId void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) { ui_->widgetWallets->onWalletBalance(wbd); + statusBarView_->onXbtBalance(wbd); } void MainWindow::onLedgerEntries(const std::string &filter, uint32_t totalPages @@ -224,6 +227,12 @@ void MainWindow::onTXDetails(const std::vector &txDet { txModel_->onTXDetails(txDet); ui_->widgetWallets->onTXDetails(txDet); + ui_->widgetExplorer->onTXDetails(txDet); +} + +void bs::gui::qt::MainWindow::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) +{ + ui_->widgetExplorer->onAddressHistory(addr, curBlock, entries); } void MainWindow::showStartupDialog(bool showLicense) @@ -436,7 +445,7 @@ void MainWindow::initTransactionsView() connect(txModel_.get(), &TransactionsViewModel::needTXDetails, this , &MainWindow::needTXDetails); -// ui_->widgetExplorer->init(armory_, logMgr_->logger(), walletsMgr_, ccFileManager_, authManager_); + ui_->widgetExplorer->init(logger_); ui_->widgetTransactions->init(logger_, txModel_); ui_->widgetTransactions->setEnabled(true); @@ -569,7 +578,7 @@ void MainWindow::onSend() dlg->SelectWallet(selectedWalletId, UiUtils::WalletsTypes::None); } - while(true) { +/* while(true) { dlg->exec(); if ((dlg->result() != QDialog::Accepted) || !dlg->switchModeRequested()) { @@ -578,7 +587,7 @@ void MainWindow::onSend() auto nextDialog = dlg->SwithcMode(); dlg = nextDialog; - } + }*/ } void MainWindow::setupMenu() @@ -984,6 +993,9 @@ void MainWindow::initWidgets() connect(ui_->widgetWallets, &WalletsWidget::needLedgerEntries, this, &MainWindow::needLedgerEntries); connect(ui_->widgetWallets, &WalletsWidget::needTXDetails, this, &MainWindow::needTXDetails); + connect(ui_->widgetExplorer, &ExplorerWidget::needAddressHistory, this, &MainWindow::needAddressHistory); + connect(ui_->widgetExplorer, &ExplorerWidget::needTXDetails, this, &MainWindow::needTXDetails); + initTransactionsView(); // InitPortfolioView(); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index e52bd5353..0206cc434 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -69,6 +69,8 @@ namespace bs { void onLedgerEntries(const std::string &filter, uint32_t totalPages , uint32_t curPage, uint32_t curBlock, const std::vector &); void onTXDetails(const std::vector &); + void onAddressHistory(const bs::Address&, uint32_t curBlock + , const std::vector&); public slots: void onReactivate(); @@ -97,7 +99,8 @@ namespace bs { , const std::string &comment); void needLedgerEntries(const std::string &filter); - void needTXDetails(const std::vector &); + void needTXDetails(const std::vector&, const bs::Address& addr = {}); + void needAddressHistory(const bs::Address&); private slots: void onSend(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index eed4f8a7d..177877c66 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -414,7 +414,9 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) }); } case ArmoryMessage::kLedgerEntries: - return processLedgerEntries(env, msg.ledger_entries()); + return processLedgerEntries(msg.ledger_entries()); + case ArmoryMessage::kAddressHistory: + return processAddressHist(msg.address_history()); default: break; } return true; @@ -650,6 +652,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::setAddrComment, this, &QtGuiAdapter::onSetAddrComment); connect(mainWindow_, &bs::gui::qt::MainWindow::needLedgerEntries, this, &QtGuiAdapter::onNeedLedgerEntries); connect(mainWindow_, &bs::gui::qt::MainWindow::needTXDetails, this, &QtGuiAdapter::onNeedTXDetails); + connect(mainWindow_, &bs::gui::qt::MainWindow::needAddressHistory, this, &QtGuiAdapter::onNeedAddressHistory); } void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) @@ -804,7 +807,8 @@ void QtGuiAdapter::onNeedLedgerEntries(const std::string &filter) pushFill(env); } -void QtGuiAdapter::onNeedTXDetails(const const std::vector &txWallet) +void QtGuiAdapter::onNeedTXDetails(const const std::vector &txWallet + , const bs::Address &addr) { WalletsMessage msg; auto msgReq = msg.mutable_tx_details_request(); @@ -814,10 +818,27 @@ void QtGuiAdapter::onNeedTXDetails(const const std::vector & request->set_wallet_id(txw.walletId); request->set_value(txw.value); } + if (!addr.empty()) { + msgReq->set_address(addr.display()); + } Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } +void QtGuiAdapter::onNeedAddressHistory(const bs::Address& addr) +{ + logger_->debug("[{}] {}", __func__, addr.display()); + ArmoryMessage msg; + msg.set_get_address_history(addr.display()); + Envelope env{ 0, user_, userBlockchain_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedTxEntries(const std::set& txHashes) +{ + logger_->debug("[{}] {}", __func__, txHashes.size()); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -889,8 +910,8 @@ bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &resp try { txDet.outputAddresses.push_back({ bs::Address::fromAddressString(outAddr.address()) , outAddr.value(), outAddr.value_string(), outAddr.wallet_name() - , static_cast(outAddr.script_type()), ownTxHash - , outAddr.out_index() }); + , static_cast(outAddr.script_type()) + , BinaryData::fromString(outAddr.out_hash()), outAddr.out_index() }); } catch (const std::exception &e) { // OP_RETURN data for valueStr txDet.outputAddresses.push_back({ bs::Address{} , outAddr.value(), outAddr.address(), outAddr.wallet_name() @@ -903,7 +924,8 @@ bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &resp , resp.change_address().value(), resp.change_address().value_string() , resp.change_address().wallet_name() , static_cast(resp.change_address().script_type()) - , ownTxHash, resp.change_address().out_index() }; + , BinaryData::fromString(resp.change_address().out_hash()) + , resp.change_address().out_index() }; } catch (const std::exception &) {} txDetails.push_back(txDet); @@ -914,8 +936,7 @@ bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &resp return true; } -bool QtGuiAdapter::processLedgerEntries(const bs::message::Envelope &env - , const ArmoryMessage_LedgerEntries &response) +bool QtGuiAdapter::processLedgerEntries(const ArmoryMessage_LedgerEntries &response) { std::vector entries; entries.reserve(response.entries_size()); @@ -934,7 +955,7 @@ bool QtGuiAdapter::processLedgerEntries(const bs::message::Envelope &env for (const auto &addrStr : entry.addresses()) { try { const auto &addr = bs::Address::fromAddressString(addrStr); - txEntry.addresses.emplace_back(std::move(addr)); + txEntry.addresses.push_back(addr); } catch (const std::exception &) {} } @@ -949,4 +970,43 @@ bool QtGuiAdapter::processLedgerEntries(const bs::message::Envelope &env } +bool QtGuiAdapter::processAddressHist(const ArmoryMessage_AddressHistory& response) +{ + bs::Address addr; + try { + addr = std::move(bs::Address::fromAddressString(response.address())); + } + catch (const std::exception& e) { + logger_->error("[{}] invalid address: {}", __func__, e.what()); + return true; + } + std::vector entries; + entries.reserve(response.entries_size()); + for (const auto& entry : response.entries()) { + bs::TXEntry txEntry; + txEntry.txHash = BinaryData::fromString(entry.tx_hash()); + txEntry.value = entry.value(); + txEntry.blockNum = entry.block_num(); + txEntry.txTime = entry.tx_time(); + txEntry.isRBF = entry.rbf(); + txEntry.isChainedZC = entry.chained_zc(); + txEntry.nbConf = entry.nb_conf(); + for (const auto& walletId : entry.wallet_ids()) { + txEntry.walletIds.insert(walletId); + } + for (const auto& addrStr : entry.addresses()) { + try { + const auto& addr = bs::Address::fromAddressString(addrStr); + txEntry.addresses.push_back(addr); + } + catch (const std::exception&) {} + } + entries.emplace_back(std::move(txEntry)); + } + QMetaObject::invokeMethod(mainWindow_, [this, entries, addr, curBlock = response.cur_block()] { + mainWindow_->onAddressHistory(addr, curBlock, entries); + }); + return true; +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 742e210dd..c0ed5e6fd 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -28,6 +28,7 @@ namespace bs { namespace BlockSettle { namespace Common { class ArmoryMessage_LedgerEntries; + class ArmoryMessage_AddressHistory; class WalletsMessage_TXDetailsResponse; class WalletsMessage_WalletBalances; } @@ -80,8 +81,8 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processWalletBalances(const bs::message::Envelope & , const BlockSettle::Common::WalletsMessage_WalletBalances &); bool processTXDetails(const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); - bool processLedgerEntries(const bs::message::Envelope & - , const BlockSettle::Common::ArmoryMessage_LedgerEntries &); + bool processLedgerEntries(const BlockSettle::Common::ArmoryMessage_LedgerEntries &); + bool processAddressHist(const BlockSettle::Common::ArmoryMessage_AddressHistory&); private slots: void onPutSetting(int idx, const QVariant &value); @@ -95,7 +96,9 @@ private slots: void onSetAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); void onNeedLedgerEntries(const std::string &filter); - void onNeedTXDetails(const std::vector &); + void onNeedTXDetails(const std::vector &, const bs::Address &); + void onNeedAddressHistory(const bs::Address&); + void onNeedTxEntries(const std::set&); private: std::shared_ptr logger_; diff --git a/common b/common index 967cbfa1c..fc8d7883c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 967cbfa1c515f99cec1c1480116f255b7472bc49 +Subproject commit fc8d7883c78e7cf3502887b7ace41e831b5ed32e From 63808eef586a9710a5d266488575c2959a20216b Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 21 Sep 2020 11:18:52 +0300 Subject: [PATCH 010/146] Updated common --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a24689575..bdf457572 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a24689575b7f6cf4a20c4fef20062ae8310c2e4f +Subproject commit bdf4575726837567218c1d6f0194e6ba9de6234c From d46107917a8aa80bb3344535afe341a16e8d6139 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 23 Sep 2020 11:15:54 +0300 Subject: [PATCH 011/146] WIP 7 --- BlockSettleUILib/CreateTransactionDialog.cpp | 155 +++++++++++++----- BlockSettleUILib/CreateTransactionDialog.h | 20 ++- .../CreateTransactionDialogAdvanced.cpp | 15 +- .../CreateTransactionDialogAdvanced.h | 9 +- .../CreateTransactionDialogSimple.cpp | 8 + .../CreateTransactionDialogSimple.h | 13 +- GUI/QtWidgets/MainWindow.cpp | 59 ++++--- GUI/QtWidgets/MainWindow.h | 12 ++ GUI/QtWidgets/QtGuiAdapter.cpp | 35 +++- GUI/QtWidgets/QtGuiAdapter.h | 8 +- common | 2 +- 11 files changed, 250 insertions(+), 86 deletions(-) diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index 701ef59c9..f0d5b995c 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -43,7 +43,7 @@ #include "XbtAmountValidator.h" // Mirror of cached Armory wait times - NodeRPC::aggregateFeeEstimates() -const std::map feeLevels = { +const std::map kFeeLevels = { { 2, QObject::tr("20 minutes") }, { 4, QObject::tr("40 minutes") }, { 6, QObject::tr("1 hour") }, @@ -75,6 +75,17 @@ CreateTransactionDialog::CreateTransactionDialog(const std::shared_ptr>(); } +CreateTransactionDialog::CreateTransactionDialog(bool loadFeeSuggestions + , uint32_t topBlock, const std::shared_ptr& logger + , QWidget* parent) + : QDialog(parent) + , logger_(logger) + , loadFeeSuggestions_(loadFeeSuggestions) + , topBlock_(topBlock) +{ + qRegisterMetaType>(); +} + CreateTransactionDialog::~CreateTransactionDialog() noexcept = default; void CreateTransactionDialog::init() @@ -127,7 +138,7 @@ void CreateTransactionDialog::init() void CreateTransactionDialog::updateCreateButtonText() { - if (!signContainer_) { + if (!signContainer_ && !walletsManager_) { pushButtonCreate()->setEnabled(false); return; } @@ -140,11 +151,16 @@ void CreateTransactionDialog::updateCreateButtonText() } const auto walletId = UiUtils::getSelectedWalletId(comboBoxWallets()); - auto walletPtr = walletsManager_->getHDWalletById(walletId); - if (walletPtr && !walletPtr->isHardwareWallet() && (signContainer_->isOffline() - || signContainer_->isWalletOffline(walletId))) { - pushButtonCreate()->setText(tr("Export")); - } else { + if (walletsManager_) { + auto walletPtr = walletsManager_->getHDWalletById(walletId); + if (walletPtr && !walletPtr->isHardwareWallet() && (signContainer_->isOffline() + || signContainer_->isWalletOffline(walletId))) { + pushButtonCreate()->setText(tr("Export")); + } else { + selectedWalletChanged(-1); + } + } + else { selectedWalletChanged(-1); } } @@ -194,9 +210,14 @@ int CreateTransactionDialog::SelectWallet(const std::string& walletId, UiUtils:: auto index = UiUtils::selectWalletInCombobox(comboBoxWallets(), walletId , static_cast(type)); if (index < 0) { - const auto rootWallet = walletsManager_->getHDRootForLeaf(walletId); - if (rootWallet) { - index = UiUtils::selectWalletInCombobox(comboBoxWallets(), rootWallet->walletId()); + if (walletsManager_) { + const auto rootWallet = walletsManager_->getHDRootForLeaf(walletId); + if (rootWallet) { + index = UiUtils::selectWalletInCombobox(comboBoxWallets(), rootWallet->walletId()); + } + } + else { + //TODO: select root wallet if needed } } return index; @@ -204,8 +225,38 @@ int CreateTransactionDialog::SelectWallet(const std::string& walletId, UiUtils:: void CreateTransactionDialog::populateWalletsList() { - int index = UiUtils::fillHDWalletsComboBox(comboBoxWallets(), walletsManager_, UiUtils::WalletsTypes::All_AllowHwLegacy); - selectedWalletChanged(index); + if (walletsManager_) { + int index = UiUtils::fillHDWalletsComboBox(comboBoxWallets(), walletsManager_, UiUtils::WalletsTypes::All_AllowHwLegacy); + selectedWalletChanged(index); + } + else { + emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy); + } +} + +void CreateTransactionDialog::onWalletsList(const std::vector&) +{ + //TODO: fill in comboBoxWallets +} + +void CreateTransactionDialog::onFeeLevels(const std::map& feeLevels) +{ + comboBoxFeeSuggestions()->clear(); + + for (const auto& feeVal : feeLevels) { + QString desc; + const auto itLevel = kFeeLevels.find(feeVal.first); + if (itLevel == kFeeLevels.end()) { + desc = tr("%1 minutes").arg(10 * feeVal.first); + } else { + desc = itLevel->second; + } + comboBoxFeeSuggestions()->addItem(tr("%1 blocks (%2): %3 s/b").arg(feeVal.first).arg(desc).arg(feeVal.second) + , feeVal.second); + } + + comboBoxFeeSuggestions()->setEnabled(true); + feeSelectionChanged(0); } void CreateTransactionDialog::populateFeeList() @@ -214,11 +265,21 @@ void CreateTransactionDialog::populateFeeList() comboBoxFeeSuggestions()->setCurrentIndex(0); comboBoxFeeSuggestions()->setEnabled(false); - connect(this, &CreateTransactionDialog::feeLoadingCompleted - , this, &CreateTransactionDialog::onFeeSuggestionsLoaded - , Qt::QueuedConnection); + if (walletsManager_) { + connect(this, &CreateTransactionDialog::feeLoadingCompleted + , this, &CreateTransactionDialog::onFeeSuggestionsLoaded + , Qt::QueuedConnection); - loadFees(); + loadFees(); + } + else { + std::vector feeLevels; + feeLevels.reserve(kFeeLevels.size()); + for (const auto& level : kFeeLevels) { + feeLevels.push_back(level.first); + } + emit needFeeLevels(feeLevels); + } } void CreateTransactionDialog::loadFees() @@ -229,10 +290,10 @@ void CreateTransactionDialog::loadFees() }; auto result = std::make_shared(); - for (const auto &feeLevel : feeLevels) { + for (const auto &feeLevel : kFeeLevels) { result->levels.insert(feeLevel.first); } - for (const auto &feeLevel : feeLevels) { + for (const auto &feeLevel : kFeeLevels) { const auto &cbFee = [this, result, level=feeLevel.first](float fee) { result->levels.erase(level); if (fee < std::numeric_limits::infinity()) { @@ -256,8 +317,8 @@ void CreateTransactionDialog::onFeeSuggestionsLoaded(const std::mapgetHDWalletById(walletId); - if (!rootWallet) { - logger_->error("[{}] wallet with id {} not found", __func__, walletId); - return; - } - if (!rootWallet->isHardwareWallet() && (signContainer_->isWalletOffline(rootWallet->walletId()) - || !rootWallet || signContainer_->isWalletOffline(rootWallet->walletId()))) { - pushButtonCreate()->setText(tr("Export")); - } else { - pushButtonCreate()->setText(tr("Broadcast")); - } - - auto group = rootWallet->getGroup(rootWallet->getXBTGroupType()); - const bool isHardware = rootWallet->isHardwareWallet() || rootWallet->isHardwareOfflineWallet(); - bs::hd::Purpose hwPurpose; - if (isHardware) { - hwPurpose = UiUtils::getSelectedHwPurpose(comboBoxWallets()); - } + if (walletsManager_) { + const auto rootWallet = walletsManager_->getHDWalletById(walletId); + if (!rootWallet) { + logger_->error("[{}] wallet with id {} not found", __func__, walletId); + return; + } + if (!rootWallet->isHardwareWallet() && (signContainer_->isWalletOffline(rootWallet->walletId()) + || !rootWallet || signContainer_->isWalletOffline(rootWallet->walletId()))) { + pushButtonCreate()->setText(tr("Export")); + } else { + pushButtonCreate()->setText(tr("Broadcast")); + } - if (transactionData_->getGroup() != group || isHardware || resetInputs) { + auto group = rootWallet->getGroup(rootWallet->getXBTGroupType()); + const bool isHardware = rootWallet->isHardwareWallet() || rootWallet->isHardwareOfflineWallet(); + bs::hd::Purpose hwPurpose; if (isHardware) { - transactionData_->setWallet(group->getLeaf(hwPurpose), armory_->topBlock() - , resetInputs, cbInputsReset); + hwPurpose = UiUtils::getSelectedHwPurpose(comboBoxWallets()); } - else { - transactionData_->setGroup(group, armory_->topBlock(), false - , resetInputs, cbInputsReset); + + if (transactionData_->getGroup() != group || isHardware || resetInputs) { + if (isHardware) { + transactionData_->setWallet(group->getLeaf(hwPurpose), armory_->topBlock() + , resetInputs, cbInputsReset); + } else { + transactionData_->setGroup(group, armory_->topBlock(), false + , resetInputs, cbInputsReset); + } } } + else { + pushButtonCreate()->setText(tr("Broadcast")); + //TODO: collect selected wallet UTXOs first + transactionData_->setWallets({ walletId }, topBlock_, {}); + } emit walletChanged(); } diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index 5062c124f..64334332b 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -24,6 +24,7 @@ #include "Bip21Types.h" #include "BSErrorCodeStrings.h" #include "CoreWallet.h" +#include "SignerDefs.h" #include "UtxoReservationToken.h" #include "ValidityFlag.h" @@ -58,7 +59,7 @@ class CreateTransactionDialog : public QDialog Q_OBJECT public: - CreateTransactionDialog(const std::shared_ptr & + [[deprecated]] CreateTransactionDialog(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -66,13 +67,25 @@ Q_OBJECT , const std::shared_ptr &applicationSettings , bs::UtxoReservationToken utxoReservation , QWidget* parent); + CreateTransactionDialog(bool loadFeeSuggestions, uint32_t topBlock + , const std::shared_ptr&, QWidget* parent); ~CreateTransactionDialog() noexcept override; int SelectWallet(const std::string& walletId, UiUtils::WalletsTypes type); + virtual void initUI() = 0; virtual bool switchModeRequested() const= 0; virtual std::shared_ptr SwitchMode() = 0; + void onWalletsList(const std::vector&); + void onFeeLevels(const std::map&); + +signals: + void feeLoadingCompleted(const std::map&); //deprecated + void walletChanged(); + void needWalletsList(UiUtils::WalletsTypes); + void needFeeLevels(const std::vector&); + protected: virtual void init(); virtual void clear(); @@ -115,10 +128,6 @@ Q_OBJECT void showError(const QString &text, const QString &detailedText); -signals: - void feeLoadingCompleted(const std::map &); - void walletChanged(); - protected slots: virtual void onFeeSuggestionsLoaded(const std::map &); virtual void feeSelectionChanged(int); @@ -151,6 +160,7 @@ protected slots: std::shared_ptr applicationSettings_; std::shared_ptr utxoReservationManager_; bs::UtxoReservationToken utxoRes_; + uint32_t topBlock_; XbtAmountValidator * xbtValidator_ = nullptr; diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index 2c035d8dc..94e022ea1 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -55,7 +55,7 @@ CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(const std::shar , QWidget* parent) : CreateTransactionDialog(armory, walletManager, utxoReservationManager, container, loadFeeSuggestions, logger, applicationSettings, std::move(utxoReservation), parent) - , ui_(new Ui::CreateTransactionDialogAdvanced) + , ui_(new Ui::CreateTransactionDialogAdvanced) { transactionData_ = txData; selectedChangeAddress_ = bs::Address{}; @@ -64,6 +64,19 @@ CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(const std::shar initUI(); } +CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(bool loadFeeSuggestions + , uint32_t topBlock, const std::shared_ptr& logger + , const std::shared_ptr& txData, bs::UtxoReservationToken utxoReservation + , QWidget* parent) + : CreateTransactionDialog(loadFeeSuggestions, topBlock, logger, parent) + , ui_(new Ui::CreateTransactionDialogAdvanced) +{ + transactionData_ = txData; + selectedChangeAddress_ = bs::Address{}; + + ui_->setupUi(this); +} + CreateTransactionDialogAdvanced::~CreateTransactionDialogAdvanced() = default; std::shared_ptr CreateTransactionDialogAdvanced::CreateForRBF( diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index e4254a0ee..c9c9a1269 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -66,7 +66,7 @@ Q_OBJECT , QWidget* parent = nullptr); public: - CreateTransactionDialogAdvanced(const std::shared_ptr & + [[deprecated]] CreateTransactionDialogAdvanced(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -76,6 +76,11 @@ Q_OBJECT , const std::shared_ptr & , bs::UtxoReservationToken utxoReservation , QWidget* parent = nullptr); + CreateTransactionDialogAdvanced(bool loadFeeSuggestions, uint32_t topBlock + , const std::shared_ptr& + , const std::shared_ptr& + , bs::UtxoReservationToken utxoReservation + , QWidget* parent = nullptr); ~CreateTransactionDialogAdvanced() override; void preSetAddress(const QString& address); @@ -151,7 +156,7 @@ private slots: private: void clear() override; - void initUI(); + void initUI() override; void updateOutputButtonTitle(); diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.cpp b/BlockSettleUILib/CreateTransactionDialogSimple.cpp index 42aac57ae..d0d73565b 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.cpp +++ b/BlockSettleUILib/CreateTransactionDialogSimple.cpp @@ -36,6 +36,14 @@ CreateTransactionDialogSimple::CreateTransactionDialogSimple(const std::shared_p initUI(); } +CreateTransactionDialogSimple::CreateTransactionDialogSimple(uint32_t topBlock + , const std::shared_ptr& logger, QWidget* parent) + : CreateTransactionDialog(true, topBlock, logger, parent) + , ui_(new Ui::CreateTransactionDialogSimple) +{ + ui_->setupUi(this); +} + CreateTransactionDialogSimple::~CreateTransactionDialogSimple() = default; void CreateTransactionDialogSimple::initUI() diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.h b/BlockSettleUILib/CreateTransactionDialogSimple.h index a78e4c121..19b9b21df 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.h +++ b/BlockSettleUILib/CreateTransactionDialogSimple.h @@ -25,7 +25,7 @@ class CreateTransactionDialogSimple : public CreateTransactionDialog Q_OBJECT public: - static std::shared_ptr CreateForPaymentRequest( + [[deprecated]] static std::shared_ptr CreateForPaymentRequest( const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -34,15 +34,21 @@ Q_OBJECT , const std::shared_ptr & , const Bip21::PaymentRequestInfo& paymentInfo , QWidget* parent = nullptr); + static std::shared_ptr CreateForPaymentRequest( + const std::shared_ptr& + , const Bip21::PaymentRequestInfo& paymentInfo + , QWidget* parent = nullptr); public: - CreateTransactionDialogSimple(const std::shared_ptr & + [[deprecated]] CreateTransactionDialogSimple(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &utxoReservationManager , const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr &applicationSettings , QWidget* parent = nullptr); + CreateTransactionDialogSimple(uint32_t topBlock, const std::shared_ptr& + , QWidget* parent = nullptr); ~CreateTransactionDialogSimple() override; bool switchModeRequested() const override; @@ -52,7 +58,6 @@ Q_OBJECT void preSetValue(const double value); void preSetValue(const bs::XBTAmount& value); - protected: QComboBox * comboBoxWallets() const override; QComboBox *comboBoxFeeSuggestions() const override; @@ -88,7 +93,7 @@ private slots: void onImportPressed(); private: - void initUI(); + void initUI() override; std::unique_ptr ui_; unsigned int recipientId_ = 0; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 55b2d8ada..905c5f6b1 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -147,12 +147,15 @@ void MainWindow::onSetting(int setting, const QVariant &value) case ApplicationSettings::ShowInfoWidget: ui_->infoWidget->setVisible(value.toBool()); break; + case ApplicationSettings::AdvancedTxDialogByDefault: + advTxDlgByDefault_ = value.toBool(); default: break; } } void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) { + topBlock_ = blockNum; if (statusBarView_) { statusBarView_->onBlockchainStateChanged(state, blockNum); } @@ -161,6 +164,7 @@ void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) void MainWindow::onNewBlock(int state, unsigned int blockNum) { + topBlock_ = blockNum; if (statusBarView_) { statusBarView_->onBlockchainStateChanged(state, blockNum); } @@ -195,6 +199,13 @@ void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) ui_->widgetWallets->onHDWalletDetails(hdWallet); } +void MainWindow::onWalletsList(const std::vector& wallets) +{ + if (txDlg_) { + txDlg_->onWalletsList(wallets); + } +} + void MainWindow::onAddresses(const std::vector &addrs) { ui_->widgetWallets->onAddresses(addrs); @@ -230,11 +241,18 @@ void MainWindow::onTXDetails(const std::vector &txDet ui_->widgetExplorer->onTXDetails(txDet); } -void bs::gui::qt::MainWindow::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) +void MainWindow::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) { ui_->widgetExplorer->onAddressHistory(addr, curBlock, entries); } +void MainWindow::onFeeLevels(const std::map& feeLevels) +{ + if (txDlg_) { + txDlg_->onFeeLevels(feeLevels); + } +} + void MainWindow::showStartupDialog(bool showLicense) { StartupDialog startupDialog(showLicense, this); @@ -555,39 +573,30 @@ void MainWindow::onSend() if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { auto wallet = ui_->widgetWallets->getSelectedHdWallet(); - if (wallet.ids.empty()) { -// wallet = walletsMgr_->getPrimaryWallet(); - } if (!wallet.ids.empty()) { selectedWalletId = *wallet.ids.cbegin(); } } - std::shared_ptr dlg; - -/* if ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) - || applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { - dlg = std::make_shared(armory_, walletsMgr_, utxoReservationMgr_ - , signContainer_, true, logMgr_->logger("ui"), applicationSettings_, nullptr, bs::UtxoReservationToken{}, this ); + if ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) || advTxDlgByDefault_) { + const bool loadFees = true; + txDlg_ = new CreateTransactionDialogAdvanced(loadFees, topBlock_, logger_ + , nullptr, bs::UtxoReservationToken{}, this ); } else { - dlg = std::make_shared(armory_, walletsMgr_, utxoReservationMgr_, signContainer_ - , logMgr_->logger("ui"), applicationSettings_, this); - }*/ + txDlg_ = new CreateTransactionDialogSimple(topBlock_, logger_, this); + } + connect(txDlg_, &QDialog::finished, [this](int) { + txDlg_->deleteLater(); + txDlg_ = nullptr; + }); + connect(txDlg_, &CreateTransactionDialog::needWalletsList, this, &MainWindow::needWalletsList); + connect(txDlg_, &CreateTransactionDialog::needFeeLevels, this, &MainWindow::needFeeLevels); + txDlg_->initUI(); if (!selectedWalletId.empty()) { - dlg->SelectWallet(selectedWalletId, UiUtils::WalletsTypes::None); + txDlg_->SelectWallet(selectedWalletId, UiUtils::WalletsTypes::None); } - -/* while(true) { - dlg->exec(); - - if ((dlg->result() != QDialog::Accepted) || !dlg->switchModeRequested()) { - break; - } - - auto nextDialog = dlg->SwithcMode(); - dlg = nextDialog; - }*/ + txDlg_->exec(); } void MainWindow::setupMenu() diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 0206cc434..f75c89255 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -17,6 +17,7 @@ #include "Address.h" #include "ArmoryConnection.h" #include "SignerDefs.h" +#include "UiUtils.h" namespace spdlog { class logger; @@ -33,6 +34,7 @@ namespace bs { class AboutDialog; class AuthAddressDialog; +class CreateTransactionDialog; class NotificationCenter; class QSystemTrayIcon; class StatusBarView; @@ -62,6 +64,7 @@ namespace bs { void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); + void onWalletsList(const std::vector&); void onAddresses(const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); @@ -72,6 +75,8 @@ namespace bs { void onAddressHistory(const bs::Address&, uint32_t curBlock , const std::vector&); + void onFeeLevels(const std::map&); + public slots: void onReactivate(); void raiseWindow(); @@ -88,6 +93,7 @@ namespace bs { void putSetting(int, const QVariant &); void createNewWallet(); void needHDWalletDetails(const std::string &walletId); + void needWalletsList(UiUtils::WalletsTypes); void needWalletBalances(const std::string &walletId); void needSpendableUTXOs(const std::string &walletId); @@ -102,6 +108,8 @@ namespace bs { void needTXDetails(const std::vector&, const bs::Address& addr = {}); void needAddressHistory(const bs::Address&); + void needFeeLevels(const std::vector&); + private slots: void onSend(); void onGenerateAddress(); @@ -182,6 +190,7 @@ namespace bs { std::shared_ptr authAddrDlg_; std::shared_ptr txModel_; + CreateTransactionDialog* txDlg_{ nullptr }; // std::shared_ptr walletsWizard_; @@ -191,10 +200,13 @@ namespace bs { bool initialWalletCreateDialogShown_ = false; bool deferCCsync_ = false; + bool advTxDlgByDefault_{ false }; std::queue> deferredDialogs_; bool deferredDialogRunning_ = false; // bs::network::UserType userType_{}; + + uint32_t topBlock_{ 0 }; }; } } diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 177877c66..1bf595b9f 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -417,6 +417,8 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) return processLedgerEntries(msg.ledger_entries()); case ArmoryMessage::kAddressHistory: return processAddressHist(msg.address_history()); + case ArmoryMessage::kFeeLevelsResponse: + return processFeeLevels(msg.fee_levels_response()); default: break; } return true; @@ -635,6 +637,11 @@ void QtGuiAdapter::requestInitialSettings() setReq->set_index(SetIdx_ShowInfoWidget); setReq->set_type(SettingType_Bool); + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_AdvancedTXisDefault); + setReq->set_type(SettingType_Bool); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } @@ -653,6 +660,8 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needLedgerEntries, this, &QtGuiAdapter::onNeedLedgerEntries); connect(mainWindow_, &bs::gui::qt::MainWindow::needTXDetails, this, &QtGuiAdapter::onNeedTXDetails); connect(mainWindow_, &bs::gui::qt::MainWindow::needAddressHistory, this, &QtGuiAdapter::onNeedAddressHistory); + connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletsList, this, &QtGuiAdapter::onNeedWalletsList); + connect(mainWindow_, &bs::gui::qt::MainWindow::needFeeLevels, this, &QtGuiAdapter::onNeedFeeLevels); } void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) @@ -834,9 +843,19 @@ void QtGuiAdapter::onNeedAddressHistory(const bs::Address& addr) pushFill(env); } -void QtGuiAdapter::onNeedTxEntries(const std::set& txHashes) +void QtGuiAdapter::onNeedWalletsList(UiUtils::WalletsTypes) { - logger_->debug("[{}] {}", __func__, txHashes.size()); +} + +void QtGuiAdapter::onNeedFeeLevels(const std::vector& levels) +{ + ArmoryMessage msg; + auto msgReq = msg.mutable_fee_levels_request(); + for (const auto& level : levels) { + msgReq->add_levels(level); + } + Envelope env{ 0, user_, userBlockchain_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); } void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) @@ -1009,4 +1028,16 @@ bool QtGuiAdapter::processAddressHist(const ArmoryMessage_AddressHistory& respon return true; } +bool QtGuiAdapter::processFeeLevels(const ArmoryMessage_FeeLevelsResponse& response) +{ + std::map feeLevels; + for (const auto& pair : response.fee_levels()) { + feeLevels[pair.level()] = pair.fee(); + } + QMetaObject::invokeMethod(mainWindow_, [this, feeLevels]{ + mainWindow_->onFeeLevels(feeLevels); + }); + return true; +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index c0ed5e6fd..27fd8a473 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -17,6 +17,7 @@ #include "ApiAdapter.h" #include "SignerDefs.h" #include "ThreadSafeClasses.h" +#include "UiUtils.h" namespace bs { namespace gui { @@ -27,8 +28,9 @@ namespace bs { } namespace BlockSettle { namespace Common { - class ArmoryMessage_LedgerEntries; class ArmoryMessage_AddressHistory; + class ArmoryMessage_FeeLevelsResponse; + class ArmoryMessage_LedgerEntries; class WalletsMessage_TXDetailsResponse; class WalletsMessage_WalletBalances; } @@ -83,6 +85,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processTXDetails(const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); bool processLedgerEntries(const BlockSettle::Common::ArmoryMessage_LedgerEntries &); bool processAddressHist(const BlockSettle::Common::ArmoryMessage_AddressHistory&); + bool processFeeLevels(const BlockSettle::Common::ArmoryMessage_FeeLevelsResponse&); private slots: void onPutSetting(int idx, const QVariant &value); @@ -98,7 +101,8 @@ private slots: void onNeedLedgerEntries(const std::string &filter); void onNeedTXDetails(const std::vector &, const bs::Address &); void onNeedAddressHistory(const bs::Address&); - void onNeedTxEntries(const std::set&); + void onNeedWalletsList(UiUtils::WalletsTypes); + void onNeedFeeLevels(const std::vector&); private: std::shared_ptr logger_; diff --git a/common b/common index bdf457572..38ac909d9 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit bdf4575726837567218c1d6f0194e6ba9de6234c +Subproject commit 38ac909d9fc01452f53db338f0606ec4a08888f6 From a623c293f60af662e1fe4c2987480cb5e6ca42de Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 29 Sep 2020 17:48:03 +0300 Subject: [PATCH 012/146] Manual create TX implemented --- BlockSettleSigner/SignerAdapterContainer.h | 8 +- BlockSettleUILib/AddressListModel.cpp | 16 +- BlockSettleUILib/CreateTransactionDialog.cpp | 343 ++++++++++++------ BlockSettleUILib/CreateTransactionDialog.h | 29 +- .../CreateTransactionDialogAdvanced.cpp | 122 +++++-- .../CreateTransactionDialogAdvanced.h | 11 +- .../RootWalletPropertiesDialog.cpp | 2 +- .../RootWalletPropertiesDialog.h | 3 +- BlockSettleUILib/SelectAddressDialog.cpp | 33 +- BlockSettleUILib/SelectAddressDialog.h | 15 +- BlockSettleUILib/StatusBarView.cpp | 4 +- BlockSettleUILib/WalletsWidget.cpp | 2 +- BlockSettleUILib/WalletsWidget.h | 3 +- Core/SignerAdapter.cpp | 29 +- Core/SignerAdapter.h | 3 + GUI/QtWidgets/MainWindow.cpp | 48 ++- GUI/QtWidgets/MainWindow.h | 16 +- GUI/QtWidgets/QtGuiAdapter.cpp | 153 ++++++-- GUI/QtWidgets/QtGuiAdapter.h | 20 +- common | 2 +- 20 files changed, 675 insertions(+), 187 deletions(-) diff --git a/BlockSettleSigner/SignerAdapterContainer.h b/BlockSettleSigner/SignerAdapterContainer.h index 170d9fe36..a6923648e 100644 --- a/BlockSettleSigner/SignerAdapterContainer.h +++ b/BlockSettleSigner/SignerAdapterContainer.h @@ -34,9 +34,11 @@ class SignAdapterContainer : public WalletSignerContainer bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & , const SecureBinaryData &password); - bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & - , TXSignMode = TXSignMode::Full, bool = false) override - { return 0; } + [[deprecated]] bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & + , TXSignMode = TXSignMode::Full, bool = false) override { return 0; } + void signTXRequest(const bs::core::wallet::TXSignRequest& + , const std::function& + , TXSignMode mode = TXSignMode::Full, bool keepDuplicatedRecipients = false) override {} void createSettlementWallet(const bs::Address & , const std::function &) override {} diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index bfb5f000a..f3a6159fb 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -275,26 +275,34 @@ void AddressListModel::onAddressBalances(const std::string &walletId lbdSaveBalToPool(); const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() , [walletId](const bs::sync::WalletInfo &wallet) { - return (*wallet.ids.cbegin() == walletId); + const auto& itWltId = std::find_if(wallet.ids.cbegin(), wallet.ids.cend() + , [walletId](const std::string &curId) { + return (curId == walletId); + }); + return (itWltId != wallet.ids.cend()); }); if (itWallet == wallets_.cend()) { // balances arrived before wallet was set return; } int startRow = INT32_MAX, endRow = 0; + unsigned int nbFound = 0; for (const auto &bal : balances) { const auto &itAddr = indexByAddr_.find(bal.address); if (itAddr == indexByAddr_.end()) { // wallet was set, but addresses haven't arrived lbdSaveBalToPool(); - return; + continue; } + nbFound++; startRow = std::min(startRow, itAddr->second); endRow = std::max(endRow, itAddr->second); addressRows_[itAddr->second].balance = bal.balTotal; addressRows_[itAddr->second].transactionCount = bal.txn; } + if (!nbFound) { + return; + } for (auto &addrRow : addressRows_) { - if ((addrRow.balance > 0) || (addrRow.transactionCount > 0) - || (addrRow.walletId.toStdString() != walletId)) { + if ((addrRow.balance > 0) || (addrRow.transactionCount > 0)) { continue; } startRow = std::min(startRow, addrRow.addrIndex); diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index f0d5b995c..109fbd500 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -138,10 +138,6 @@ void CreateTransactionDialog::init() void CreateTransactionDialog::updateCreateButtonText() { - if (!signContainer_ && !walletsManager_) { - pushButtonCreate()->setEnabled(false); - return; - } if (HaveSignedImportedTransaction()) { pushButtonCreate()->setText(tr("Broadcast")); if (signContainer_->isOffline()) { @@ -160,9 +156,6 @@ void CreateTransactionDialog::updateCreateButtonText() selectedWalletChanged(-1); } } - else { - selectedWalletChanged(-1); - } } void CreateTransactionDialog::onSignerAuthenticated() @@ -230,13 +223,61 @@ void CreateTransactionDialog::populateWalletsList() selectedWalletChanged(index); } else { - emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy); + emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy, "CreateTX"); } } -void CreateTransactionDialog::onWalletsList(const std::vector&) +void CreateTransactionDialog::onWalletsList(const std::string &id, const std::vector& hdWallets) { - //TODO: fill in comboBoxWallets + if (id != "CreateTX") { + return; + } + int selected = 0; + auto comboBox = comboBoxWallets(); + comboBox->clear(); + + const auto &addRow = [comboBox] + (const std::string& label, const std::string& walletId, UiUtils::WalletsTypes type) + { + int i = comboBox->count(); + comboBox->addItem(QString::fromStdString(label)); + comboBox->setItemData(i, QString::fromStdString(walletId), UiUtils::WalletIdRole); + comboBox->setItemData(i, QVariant::fromValue(static_cast(type)), UiUtils::WalletType); + }; + + hdWallets_.clear(); + for (const auto& hdWallet : hdWallets) { + hdWallets_[hdWallet.id] = hdWallet; + if (hdWallet.primary) { + selected = comboBox->count(); + } + UiUtils::WalletsTypes type = UiUtils::WalletsTypes::None; + if ((hdWallet.groups.size() == 1) && (hdWallet.groups[0].leaves.size() == 1)) { + const auto& leaf = hdWallet.groups[0].leaves.at(0); + std::string label = hdWallet.name; + const auto purpose = static_cast(leaf.path.get(0) & ~bs::hd::hardFlag); + if (purpose == bs::hd::Purpose::Native) { + label += " Native"; + type = UiUtils::WalletsTypes::HardwareNativeSW; + } else if (purpose == bs::hd::Purpose::Nested) { + label += " Nested"; + type = UiUtils::WalletsTypes::HardwareNestedSW; + } else if (purpose == bs::hd::Purpose::NonSegWit) { + label += " Legacy"; + type = UiUtils::WalletsTypes::HardwareLegacy; + } + addRow(label, hdWallet.id, type); + } + else { + if (hdWallet.offline) { + type = UiUtils::WalletsTypes::WatchOnly; + } else { + type = UiUtils::WalletsTypes::Full; + } + addRow(hdWallet.name, hdWallet.id, type); + } + } + comboBox->setCurrentIndex(selected); } void CreateTransactionDialog::onFeeLevels(const std::map& feeLevels) @@ -376,14 +417,26 @@ void CreateTransactionDialog::selectedWalletChanged(int, bool resetInputs, const } } else { + if (walletId.empty()) { + return; + } pushButtonCreate()->setText(tr("Broadcast")); - //TODO: collect selected wallet UTXOs first - transactionData_->setWallets({ walletId }, topBlock_, {}); + emit needUTXOs("CreateTX", walletId); } emit walletChanged(); } +void CreateTransactionDialog::onUTXOs(const std::string& id + , const std::string& walletId, const std::vector& utxos) +{ + if (id != "CreateTX") { + return; + } + logger_->debug("[{}] {}", __func__, walletId); + transactionData_->setUTXOs({ walletId }, topBlock_, utxos); +} + void CreateTransactionDialog::onTransactionUpdated() { const auto &summary = transactionData_->GetTransactionSummary(); @@ -439,6 +492,61 @@ void CreateTransactionDialog::onMaxPressed() lineEditAmount()->setEnabled(true); } +void CreateTransactionDialog::onSignedTX(const std::string& id, BinaryData signedTX + , bs::error::ErrorCode result) +{ + if (id != "CreateTX") { + return; + } + if (result == bs::error::ErrorCode::TxCancelled) { + stopBroadcasting(); + return; + } + + BinaryData txHash; + try { + if (result != bs::error::ErrorCode::NoError) { + throw std::runtime_error(bs::error::ErrorCodeToString(result).toStdString()); + } + + if (signedTX.empty()) { + throw std::runtime_error("Empty signed TX data received"); + } + const Tx tx(signedTX); + txHash = tx.getThisHash(); + if (tx.isInitialized() && (tx.getTxWeight() >= kTransactionWeightLimit)) { + BSMessageBox mBox(BSMessageBox::question, tr("Oversized Transaction") + , tr("Transaction size limit %1 exceeded: %2. Do you still want to send this transaction?") + .arg(QString::number(kTransactionWeightLimit)).arg(QString::number(tx.getTxWeight())) + , this); + if (mBox.exec() != QDialog::Accepted) { + stopBroadcasting(); + return; + } + } + } catch (const std::exception& e) { + MessageBoxBroadcastError(tr("Invalid signed transaction: %1").arg(QLatin1String(e.what())) + , result, this).exec(); + stopBroadcasting(); + return; + } + + QString detailedText; + if (result == bs::error::ErrorCode::NoError) { + emit needBroadcastZC("CreateTX", signedTX); + if (!textEditComment()->document()->isEmpty()) { + const auto& comment = textEditComment()->document()->toPlainText().toStdString(); + emit needSetTxComment(transactionData_->getWallets().at(0), txHash, comment); + } + accept(); + return; + } else { + detailedText = bs::error::ErrorCodeToString(result); + } + MessageBoxBroadcastError(detailedText, result, this).exec(); + stopBroadcasting(); +} + void CreateTransactionDialog::onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode result) { if (!pendingTXSignId_ || (pendingTXSignId_ != id)) { @@ -453,9 +561,6 @@ void CreateTransactionDialog::onTXSigned(unsigned int id, BinaryData signedTX, b return; } - const auto walletId = UiUtils::getSelectedWalletId(comboBoxWallets()); - const auto wallet = walletsManager_->getWalletById(walletId); - try { if (result != bs::error::ErrorCode::NoError) { throw std::runtime_error(bs::error::ErrorCodeToString(result).toStdString()); @@ -539,79 +644,101 @@ bool CreateTransactionDialog::BroadcastImportedTx() void CreateTransactionDialog::CreateTransaction(const CreateTransactionCb &cb) { - if (!signContainer_) { - BSMessageBox(BSMessageBox::critical, tr("Error") + if (signContainer_ && walletsManager_) { // old code +/* BSMessageBox(BSMessageBox::critical, tr("Error") , tr("Signer is invalid - unable to send transaction"), this).exec(); - return; - } - - getChangeAddress([this, cb = std::move(cb), handle = validityFlag_.handle()](bs::Address changeAddress) { - if (!handle.isValid()) { - return; - } - try { - txReq_ = transactionData_->createTXRequest(checkBoxRBF()->checkState() == Qt::Checked, changeAddress); - if (!changeAddress.empty()) { - auto changeWallet = walletsManager_->getWalletByAddress(changeAddress); - assert(changeWallet); - if (std::find(txReq_.walletIds.begin(), txReq_.walletIds.end(), changeWallet->walletId()) == txReq_.walletIds.end()) { - txReq_.walletIds.push_back(changeWallet->walletId()); - } - } - - // grab supporting transactions for the utxo map. - // required only for HW - std::set hashes; - for (unsigned i=0; igetOutputHash()); + return;*/ + getChangeAddress([this, cb = std::move(cb), handle = validityFlag_.handle()](bs::Address changeAddress) { + if (!handle.isValid()) { + return; } - - auto supportingTxMapCb = [this, handle, cb] - (const AsyncClient::TxBatchResult& result, std::exception_ptr eptr) mutable - { - if (!handle.isValid()) { - return; - } - - if (eptr) { - SPDLOG_LOGGER_ERROR(logger_, "getTXsByHash failed"); - cb(false, "receving supporting transactions failed", "", 0); - return; + try { + txReq_ = transactionData_->createTXRequest(checkBoxRBF()->checkState() == Qt::Checked, changeAddress); + if (!changeAddress.empty()) { + auto changeWallet = walletsManager_->getWalletByAddress(changeAddress); + assert(changeWallet); + if (std::find(txReq_.walletIds.begin(), txReq_.walletIds.end(), changeWallet->walletId()) == txReq_.walletIds.end()) { + txReq_.walletIds.push_back(changeWallet->walletId()); + } } - for (auto& txPair : result) { - txReq_.armorySigner_.addSupportingTx(*txPair.second); + // grab supporting transactions for the utxo map. + // required only for HW + std::set hashes; + for (unsigned i=0; igetOutputHash()); } - const auto cbResolvePublicData = [this, handle, cb] - (bs::error::ErrorCode result, const Codec_SignerState::SignerState &state) + auto supportingTxMapCb = [this, handle, cb] + (const AsyncClient::TxBatchResult& result, std::exception_ptr eptr) mutable { - txReq_.armorySigner_.deserializeState(state); if (!handle.isValid()) { return; } - logger_->debug("[CreateTransactionDialog::CreateTransaction cbResolvePublicData] result={}, state: {}" - , (int)result, state.IsInitialized()); + if (eptr) { + SPDLOG_LOGGER_ERROR(logger_, "getTXsByHash failed"); + cb(false, "receving supporting transactions failed", "", 0); + return; + } + + for (auto& txPair : result) { + txReq_.armorySigner_.addSupportingTx(*txPair.second); + } + + const auto cbResolvePublicData = [this, handle, cb] + (bs::error::ErrorCode result, const Codec_SignerState::SignerState &state) + { + txReq_.armorySigner_.deserializeState(state); + if (!handle.isValid()) { + return; + } - const auto serializedUnsigned = txReq_.armorySigner_.serializeUnsignedTx().toHexStr(); - const auto estimatedSize = txReq_.estimateTxVirtSize(); + logger_->debug("[CreateTransactionDialog::CreateTransaction cbResolvePublicData] result={}, state: {}" + , (int)result, state.IsInitialized()); - cb(true, "", serializedUnsigned, estimatedSize); + const auto serializedUnsigned = txReq_.armorySigner_.serializeUnsignedTx().toHexStr(); + const auto estimatedSize = txReq_.estimateTxVirtSize(); + + cb(true, "", serializedUnsigned, estimatedSize); + }; + + signContainer_->resolvePublicSpenders(txReq_, cbResolvePublicData); }; - signContainer_->resolvePublicSpenders(txReq_, cbResolvePublicData); - }; + if (!armory_->getTXsByHash(hashes, supportingTxMapCb, true)) { + SPDLOG_LOGGER_ERROR(logger_, "getTXsByHash failed"); + cb(false, "receving supporting transactions failed", "", 0); + } + } + catch (const std::exception &e) { + SPDLOG_LOGGER_ERROR(logger_, "exception: {}", e.what()); + cb(false, e.what(), "", 0); + } + }); + return; + } + + getChangeAddress([this, cb](const bs::Address &changeAddress) { + try { + txReq_ = transactionData_->createTXRequest(checkBoxRBF()->checkState() == Qt::Checked, changeAddress); - if (!armory_->getTXsByHash(hashes, supportingTxMapCb, true)) { - SPDLOG_LOGGER_ERROR(logger_, "getTXsByHash failed"); - cb(false, "receving supporting transactions failed", "", 0); + // grab supporting transactions for the utxo map. + // required only for HW + std::set hashes; + for (unsigned i = 0; i < txReq_.armorySigner_.getTxInCount(); i++) { + auto spender = txReq_.armorySigner_.getSpender(i); + hashes.emplace(spender->getOutputHash()); } - } - catch (const std::exception &e) { + //TODO: implement supporting TXs collection + + const auto serializedUnsigned = txReq_.armorySigner_.serializeUnsignedTx().toHexStr(); + const auto estimatedSize = txReq_.estimateTxVirtSize(); + cb(true, "", serializedUnsigned, estimatedSize); + } catch (const std::exception& e) { SPDLOG_LOGGER_ERROR(logger_, "exception: {}", e.what()); - cb(false, e.what(), "", 0); + cb(false, e.what(), {}, 0); } }); } @@ -621,8 +748,6 @@ bool CreateTransactionDialog::createTransactionImpl() QString text; QString detailedText; - const auto hdWallet = walletsManager_->getHDWalletById(UiUtils::getSelectedWalletId(comboBoxWallets())); - try { txReq_.comment = textEditComment()->document()->toPlainText().toStdString(); @@ -676,49 +801,55 @@ bool CreateTransactionDialog::createTransactionImpl() txReq_.txHash = txReq_.txId(); } - if (hdWallet->isOffline() && !hdWallet->isHardwareWallet()) { - QString offlineFilePath; - QString signerOfflineDir = applicationSettings_->get(ApplicationSettings::signerOfflineDir); + if (walletsManager_ && signContainer_) { + const auto hdWallet = walletsManager_->getHDWalletById(UiUtils::getSelectedWalletId(comboBoxWallets())); + if (hdWallet->isOffline() && !hdWallet->isHardwareWallet()) { + QString offlineFilePath; + QString signerOfflineDir = applicationSettings_->get(ApplicationSettings::signerOfflineDir); - const qint64 timestamp = QDateTime::currentDateTime().toSecsSinceEpoch(); - const std::string fileName = fmt::format("{}_{}.bin", hdWallet->walletId(), timestamp); + const qint64 timestamp = QDateTime::currentDateTime().toSecsSinceEpoch(); + const std::string fileName = fmt::format("{}_{}.bin", hdWallet->walletId(), timestamp); - QString defaultFilePath = QDir(signerOfflineDir).filePath(QString::fromStdString(fileName)); - offlineFilePath = QFileDialog::getSaveFileName(this, tr("Save Offline TX as...") - , defaultFilePath, tr("TX files (*.bin);; All files (*)")); + QString defaultFilePath = QDir(signerOfflineDir).filePath(QString::fromStdString(fileName)); + offlineFilePath = QFileDialog::getSaveFileName(this, tr("Save Offline TX as...") + , defaultFilePath, tr("TX files (*.bin);; All files (*)")); - if (offlineFilePath.isEmpty()) { - return true; - } + if (offlineFilePath.isEmpty()) { + return true; + } - QFileInfo exportFileIndo(offlineFilePath); - QString newSignerOfflineDir = exportFileIndo.absoluteDir().path(); - if (signerOfflineDir != newSignerOfflineDir) { - applicationSettings_->set(ApplicationSettings::signerOfflineDir, newSignerOfflineDir); - } - if (exportFileIndo.suffix() != QLatin1String("bin")) { - offlineFilePath += QLatin1String(".bin"); - } + QFileInfo exportFileIndo(offlineFilePath); + QString newSignerOfflineDir = exportFileIndo.absoluteDir().path(); + if (signerOfflineDir != newSignerOfflineDir) { + applicationSettings_->set(ApplicationSettings::signerOfflineDir, newSignerOfflineDir); + } + if (exportFileIndo.suffix() != QLatin1String("bin")) { + offlineFilePath += QLatin1String(".bin"); + } - bs::error::ErrorCode result = bs::core::wallet::ExportTxToFile(txReq_, offlineFilePath); - if (result == bs::error::ErrorCode::NoError) { - BSMessageBox(BSMessageBox::info, tr("Offline Transaction") - , tr("Request was successfully exported") - , tr("Saved to %1").arg(offlineFilePath), this).exec(); - return false; // export was success so we could close the dialog - } - else { - BSMessageBox(BSMessageBox::warning, tr("Offline Transaction") - , tr("Failed to save offline Tx request") - , tr("Filename: %1").arg(offlineFilePath), this).exec(); + bs::error::ErrorCode result = bs::core::wallet::ExportTxToFile(txReq_, offlineFilePath); + if (result == bs::error::ErrorCode::NoError) { + BSMessageBox(BSMessageBox::info, tr("Offline Transaction") + , tr("Request was successfully exported") + , tr("Saved to %1").arg(offlineFilePath), this).exec(); + return false; // export was success so we could close the dialog + } else { + BSMessageBox(BSMessageBox::warning, tr("Offline Transaction") + , tr("Failed to save offline Tx request") + , tr("Filename: %1").arg(offlineFilePath), this).exec(); + } + } else { + startBroadcasting(); + pendingTXSignId_ = signContainer_->signTXRequest(txReq_, SignContainer::TXSignMode::Full, true); + if (!pendingTXSignId_) { + throw std::logic_error("Signer failed to send request"); + } } } else { + //TODO: add implementation for HW wallets startBroadcasting(); - pendingTXSignId_ = signContainer_->signTXRequest(txReq_, SignContainer::TXSignMode::Full, true); - if (!pendingTXSignId_) { - throw std::logic_error("Signer failed to send request"); - } + emit needSignTX("CreateTX", txReq_, true); } return true; } diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index 64334332b..c76a77357 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -24,7 +24,7 @@ #include "Bip21Types.h" #include "BSErrorCodeStrings.h" #include "CoreWallet.h" -#include "SignerDefs.h" +#include "SignContainer.h" #include "UtxoReservationToken.h" #include "ValidityFlag.h" @@ -77,14 +77,33 @@ Q_OBJECT virtual bool switchModeRequested() const= 0; virtual std::shared_ptr SwitchMode() = 0; - void onWalletsList(const std::vector&); + virtual void onAddresses(const std::vector&) {} + virtual void onAddressComments(const std::string& walletId + , const std::map&) {} + virtual void onAddressBalances(const std::string& walletId + , const std::vector&) {} + + virtual void onWalletsList(const std::string &id, const std::vector&); void onFeeLevels(const std::map&); + void onUTXOs(const std::string& id, const std::string& walletId, const std::vector&); + void onSignedTX(const std::string& id, BinaryData signedTX, bs::error::ErrorCode result); signals: void feeLoadingCompleted(const std::map&); //deprecated void walletChanged(); - void needWalletsList(UiUtils::WalletsTypes); + void needWalletsList(UiUtils::WalletsTypes, const std::string &id); void needFeeLevels(const std::vector&); + void needUTXOs(const std::string &id, const std::string& walletId + , bool confOnly=false, bool swOnly=false); + void needExtAddresses(const std::string& walletId); + void needIntAddresses(const std::string& walletId); + void needUsedAddresses(const std::string& walletId); + void needAddrComments(const std::string& walletId, const std::vector&); + void needWalletBalances(const std::string& walletId); + void needSignTX(const std::string& id, const bs::core::wallet::TXSignRequest & + , bool keepDupRecips = false, SignContainer::TXSignMode mode = SignContainer::TXSignMode::Full); + void needBroadcastZC(const std::string &id, const BinaryData &); + void needSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string &comment); protected: virtual void init(); @@ -134,7 +153,7 @@ protected slots: virtual void selectedWalletChanged(int, bool resetInputs = false , const std::function &cbInputsReset = nullptr); virtual void onMaxPressed(); - void onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode result); + void onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode result); //deprecated void updateCreateButtonText(); void onSignerAuthenticated(); @@ -185,6 +204,8 @@ protected slots: ValidityFlag validityFlag_; + std::unordered_map hdWallets_; + private: bs::core::wallet::TXSignRequest txReq_; }; diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index 94e022ea1..083e350ea 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -726,6 +726,13 @@ QLabel* CreateTransactionDialogAdvanced::changeLabel() const return ui_->labelReturnAmount; } +void CreateTransactionDialogAdvanced::onWalletsList(const std::string& id + , const std::vector& wallets) +{ + CreateTransactionDialog::onWalletsList(id, wallets); + ui_->pushButtonSelectInputs->setEnabled(ui_->comboBoxWallets->count() > 0); +} + void CreateTransactionDialogAdvanced::onOutputsClicked(const QModelIndex &index) { if (!index.isValid()) { @@ -1282,7 +1289,8 @@ void CreateTransactionDialogAdvanced::onCreatePressed() return; } - CreateTransaction([this, handle = validityFlag_.handle()](bool result, const std::string &errorMsg, const std::string& unsignedTx, uint64_t virtSize) { + CreateTransaction([this, handle = validityFlag_.handle()] + (bool result, const std::string &errorMsg, const std::string& unsignedTx, uint64_t virtSize) { if (!handle.isValid()) { return; } @@ -1529,35 +1537,107 @@ void CreateTransactionDialogAdvanced::onNewAddressSelectedForChange() showExistingChangeAddress(false); } -void CreateTransactionDialogAdvanced::onExistingAddressSelectedForChange() +void CreateTransactionDialogAdvanced::onAddresses(const std::vector& addrs) { - if (!transactionData_->getWallet()) { - SPDLOG_LOGGER_ERROR(logger_, "wallet now found"); - return; + if (selChangeAddrDlg_) { + selChangeAddrDlg_->onAddresses(addrs); } - const auto hdWallet = walletsManager_->getHDRootForLeaf(transactionData_->getWallet()->walletId()); - std::shared_ptr group; - if (hdWallet) { - group = hdWallet->getGroup(hdWallet->getXBTGroupType()); +} + +void CreateTransactionDialogAdvanced::onAddressComments(const std::string& walletId + , const std::map& addrComments) +{ + if (selChangeAddrDlg_) { + selChangeAddrDlg_->onAddressComments(walletId, addrComments); } +} - SelectAddressDialog *selectAddressDialog = nullptr; - if (group) { - selectAddressDialog = new SelectAddressDialog(group, this, AddressListModel::AddressType::Internal); +void CreateTransactionDialogAdvanced::onAddressBalances(const std::string& walletId + , const std::vector& addrBal) +{ + if (selChangeAddrDlg_) { + selChangeAddrDlg_->onAddressBalances(walletId, addrBal); } - else { - selectAddressDialog = new SelectAddressDialog(walletsManager_, transactionData_->getWallet() - , this, AddressListModel::AddressType::Internal); +} + +void CreateTransactionDialogAdvanced::onExistingAddressSelectedForChange() +{ + if (walletsManager_) { + if (!transactionData_->getWallet()) { + SPDLOG_LOGGER_ERROR(logger_, "wallet not found"); + return; + } + const auto hdWallet = walletsManager_->getHDRootForLeaf(transactionData_->getWallet()->walletId()); + std::shared_ptr group; + if (hdWallet) { + group = hdWallet->getGroup(hdWallet->getXBTGroupType()); + } + + if (group) { + selChangeAddrDlg_ = new SelectAddressDialog(group, this, AddressListModel::AddressType::Internal); + } else { + selChangeAddrDlg_ = new SelectAddressDialog(walletsManager_, transactionData_->getWallet() + , this, AddressListModel::AddressType::Internal); + } + + if (selChangeAddrDlg_->exec() == QDialog::Accepted) { + selectedChangeAddress_ = selChangeAddrDlg_->getSelectedAddress(); + showExistingChangeAddress(true); + } else { + if (!selectedChangeAddress_.isValid()) { + ui_->radioButtonNewAddrNative->setChecked(true); + } + } } + else { + selChangeAddrDlg_ = new SelectAddressDialog(this, AddressListModel::AddressType::Internal); + connect(selChangeAddrDlg_, &SelectAddressDialog::needExtAddresses, this, &CreateTransactionDialog::needExtAddresses); + connect(selChangeAddrDlg_, &SelectAddressDialog::needIntAddresses, this, &CreateTransactionDialog::needIntAddresses); + connect(selChangeAddrDlg_, &SelectAddressDialog::needUsedAddresses, this, &CreateTransactionDialog::needUsedAddresses); + connect(selChangeAddrDlg_, &SelectAddressDialog::needAddrComments, this, &CreateTransactionDialog::needAddrComments); + + std::vector wallets; + std::unordered_set balanceIds; + for (const auto& walletId : transactionData_->getWallets()) { + const auto& itHdWallet = hdWallets_.find(walletId); + if (itHdWallet != hdWallets_.end()) { + for (const auto& group : itHdWallet->second.groups) { + for (const auto& leaf : group.leaves) { + bs::sync::WalletInfo wi; + wi.format = bs::sync::WalletFormat::Plain; + wi.ids = leaf.ids; + if (leaf.ids.size() == 2) { + balanceIds.insert(leaf.ids.at(1)); + } + else { + balanceIds.insert(leaf.ids.at(0)); + } + wi.name = leaf.name; + wi.type = bs::core::wallet::Type::Bitcoin; + wi.primary = itHdWallet->second.primary; + wallets.push_back(wi); + } + } + } + } + selChangeAddrDlg_->setWallets(wallets); + for (const auto& walletId : balanceIds) { + emit needWalletBalances(walletId); + } - if (selectAddressDialog->exec() == QDialog::Accepted) { - selectedChangeAddress_ = selectAddressDialog->getSelectedAddress(); - showExistingChangeAddress(true); - } else { - if (!selectedChangeAddress_.isValid()) { - ui_->radioButtonNewAddrNative->setChecked(true); + if (selChangeAddrDlg_->exec() == QDialog::Accepted) { + selectedChangeAddress_ = selChangeAddrDlg_->getSelectedAddress(); + showExistingChangeAddress(true); + } else { + if (!selectedChangeAddress_.isValid()) { + ui_->radioButtonNewAddrNative->setChecked(true); + } } } + if (selChangeAddrDlg_) { + selChangeAddrDlg_->deleteLater(); + selChangeAddrDlg_ = nullptr; + } } void CreateTransactionDialogAdvanced::SetFixedWallet(const std::string& walletId, const std::function &cbInputsReset) diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index c9c9a1269..ea71fd215 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -26,8 +26,8 @@ namespace bs { class WalletsManager; } } - class QNetworkAccessManager; +class SelectAddressDialog; class CreateTransactionDialogAdvanced : public CreateTransactionDialog { @@ -90,9 +90,15 @@ Q_OBJECT bool switchModeRequested() const override; std::shared_ptr SwitchMode() override; + void onWalletsList(const std::string& id, const std::vector&) override; protected: bool eventFilter(QObject *watched, QEvent *) override; + void onAddresses(const std::vector&) override; + void onAddressComments(const std::string& walletId + , const std::map&) override; + void onAddressBalances(const std::string& walletId + , const std::vector&) override; QComboBox *comboBoxWallets() const override; QComboBox *comboBoxFeeSuggestions() const override; @@ -150,6 +156,7 @@ private slots: void onUpdateChangeWidget(); void onBitPayTxVerified(bool result); void onVerifyBitPayUnsignedTx(const std::string& unsignedTx, uint64_t virtSize); + signals: void VerifyBitPayUnsignedTx(const std::string& unsignedTx, uint64_t virtSize); void BitPayTxVerified(bool result); @@ -233,6 +240,8 @@ private slots: Bip21::PaymentRequestInfo paymentInfo_; std::shared_ptr nam_; + + SelectAddressDialog* selChangeAddrDlg_{ nullptr }; }; #endif // __CREATE_TRANSACTION_DIALOG_ADVANCED_H__ diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 8656588b1..1b6143d0e 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -285,7 +285,7 @@ void RootWalletPropertiesDialog::updateWalletDetails(const bs::sync::WalletInfo ui_->labelAddressesActive->setText(tr("Loading...")); ui_->labelUTXOs->setText(tr("Loading...")); emit needWalletBalances(*wi.ids.cbegin()); - emit needSpendableUTXOs(*wi.ids.cbegin()); + emit needUTXOs("RootWP", *wi.ids.cbegin(), true); } } diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 5b9bc62d9..3de1f6e79 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -66,7 +66,8 @@ Q_OBJECT void startRescan(std::string walletId); void needHDWalletDetails(const std::string &walletId); void needWalletBalances(const std::string &walletId); - void needSpendableUTXOs(const std::string &walletId); + void needUTXOs(const std::string& id, const std::string& walletId + , bool confOnly = false, bool swOnly = false); private slots: void onDeleteWallet(); diff --git a/BlockSettleUILib/SelectAddressDialog.cpp b/BlockSettleUILib/SelectAddressDialog.cpp index 01b629757..e46267861 100644 --- a/BlockSettleUILib/SelectAddressDialog.cpp +++ b/BlockSettleUILib/SelectAddressDialog.cpp @@ -38,8 +38,8 @@ SelectAddressDialog::SelectAddressDialog(const std::shared_ptrsetupUi(this); model_ = std::make_unique(ui_->treeView, addrType); - model_->setWallets(wallets, false, false); + connect(model_.get(), &AddressListModel::needExtAddresses, this, &SelectAddressDialog::needExtAddresses); + connect(model_.get(), &AddressListModel::needIntAddresses, this, &SelectAddressDialog::needIntAddresses); + connect(model_.get(), &AddressListModel::needUsedAddresses, this, &SelectAddressDialog::needUsedAddresses); + connect(model_.get(), &AddressListModel::needAddrComments, this, &SelectAddressDialog::needAddrComments); + ui_->treeView->setModel(model_.get()); ui_->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); @@ -57,11 +61,32 @@ SelectAddressDialog::SelectAddressDialog(const AddressListModel::Wallets &wallet connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &SelectAddressDialog::reject); connect(ui_->pushButtonSelect, &QPushButton::clicked, this, &SelectAddressDialog::accept); +} +SelectAddressDialog::~SelectAddressDialog() = default; + +void SelectAddressDialog::setWallets(const AddressListModel::Wallets& wallets) +{ + model_->setWallets(wallets, false, false); onSelectionChanged(); } -SelectAddressDialog::~SelectAddressDialog() = default; +void SelectAddressDialog::onAddresses(const std::vector& addrs) +{ + model_->onAddresses(addrs); +} + +void SelectAddressDialog::onAddressComments(const std::string& walletId + , const std::map& addrComments) +{ + model_->onAddressComments(walletId, addrComments); +} + +void SelectAddressDialog::onAddressBalances(const std::string& walletId + , const std::vector& addrBal) +{ + model_->onAddressBalances(walletId, addrBal); +} void SelectAddressDialog::init() { diff --git a/BlockSettleUILib/SelectAddressDialog.h b/BlockSettleUILib/SelectAddressDialog.h index 46dc9b877..bd934d0da 100644 --- a/BlockSettleUILib/SelectAddressDialog.h +++ b/BlockSettleUILib/SelectAddressDialog.h @@ -40,11 +40,24 @@ Q_OBJECT , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); [[deprecated]] SelectAddressDialog(const std::shared_ptr &, QWidget* parent = nullptr , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); - SelectAddressDialog(const AddressListModel::Wallets &, QWidget* parent = nullptr + SelectAddressDialog(QWidget* parent , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); ~SelectAddressDialog() override; bs::Address getSelectedAddress() const; + void setWallets(const AddressListModel::Wallets&); + + void onAddresses(const std::vector&); + void onAddressComments(const std::string& walletId + , const std::map&); + void onAddressBalances(const std::string& walletId + , const std::vector&); + +signals: + void needExtAddresses(const std::string& walletId); + void needIntAddresses(const std::string& walletId); + void needUsedAddresses(const std::string& walletId); + void needAddrComments(const std::string& walletId, const std::vector&); public slots: void onSelectionChanged(); diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index ca9c25b07..5ee811eda 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -195,8 +195,8 @@ void StatusBarView::onBlockchainStateChanged(int state, unsigned int blockNum) } void StatusBarView::onXbtBalance(const bs::sync::WalletBalanceData &wbd) -{ - xbtBalances_[wbd.id] = wbd.balTotal; +{ // uppercase eliminates ext-int balance duplication + xbtBalances_[QString::fromStdString(wbd.id).toUpper().toStdString()] = wbd.balTotal; displayXbtBalance(); } diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 1992d136a..34e9c290b 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -28,7 +28,6 @@ #include "AssetManager.h" #include "BSMessageBox.h" #include "NewWalletDialog.h" -#include "SelectAddressDialog.h" #include "SignContainer.h" #include "WalletsViewModel.h" #include "WalletWarningDialog.h" @@ -440,6 +439,7 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) rootDlg_ = new RootWalletPropertiesDialog(logger_, hdWallet, walletsModel_, this); connect(rootDlg_, &RootWalletPropertiesDialog::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); connect(rootDlg_, &RootWalletPropertiesDialog::needWalletBalances, this, &WalletsWidget::needWalletBalances); + connect(rootDlg_, &RootWalletPropertiesDialog::needUTXOs, this, &WalletsWidget::needUTXOs); connect(rootDlg_, &QDialog::finished, [this](int) { rootDlg_->deleteLater(); rootDlg_ = nullptr; diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 14d6be527..3e41afd9d 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -114,7 +114,8 @@ public slots: void newWalletCreationRequest(); void needHDWalletDetails(const std::string &walletId); void needWalletBalances(const std::string &walletId); - void needSpendableUTXOs(const std::string &walletId); + void needUTXOs(const std::string& id, const std::string& walletId + , bool confOnly = false, bool swOnly = false); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); void needUsedAddresses(const std::string &walletId); diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index c1fe52987..ad4bc3ec3 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -10,10 +10,11 @@ */ #include "SignerAdapter.h" #include +#include "Adapters/SignerClient.h" #include "ConnectionManager.h" -#include "TerminalMessage.h" #include "HeadlessContainer.h" -#include "Adapters/SignerClient.h" +#include "ProtobufHeadlessUtils.h" +#include "TerminalMessage.h" #include "common.pb.h" #include "terminal.pb.h" @@ -119,6 +120,8 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processDelHdRoot(request.del_hd_root()); case SignerMessage::kDelHdLeaf: return processDelHdLeaf(request.del_hd_leaf()); + case SignerMessage::kSignTxRequest: + return processSignTx(env, request.sign_tx_request()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -610,3 +613,25 @@ bool SignerAdapter::processDelHdLeaf(const std::string &walletId) { return (signer_->DeleteHDLeaf(walletId) > 0); } + +bool SignerAdapter::processSignTx(const bs::message::Envelope& env + , const SignerMessage_SignTxRequest& request) +{ + const auto& cbSigned = [this, env, id=request.id()] + (BinaryData signedTX, bs::error::ErrorCode result, const std::string& errorReason) + { + SignerMessage msg; + auto msgResp = msg.mutable_sign_tx_response(); + msgResp->set_id(id); + msgResp->set_signed_tx(signedTX.toBinStr()); + msgResp->set_error_code((int)result); + msgResp->set_error_text(errorReason); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(envResp); + }; + const auto& txReq = bs::signer::pbTxRequestToCore(request.tx_request(), logger_); + signer_->signTXRequest(txReq, cbSigned + , static_cast(request.sign_mode()) + , request.keep_dup_recips()); + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 3c40f32f2..ae8020f11 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -23,6 +23,7 @@ namespace BlockSettle { class SignerMessage; class SignerMessage_ExtendAddrChain; class SignerMessage_SetSettlementId; + class SignerMessage_SignTxRequest; class SignerMessage_SyncAddresses; class SignerMessage_SyncAddressComment; class SignerMessage_SyncNewAddresses; @@ -90,6 +91,8 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget bool processGetRootPubKey(const bs::message::Envelope &, const std::string &walletId); bool processDelHdRoot(const std::string &walletId); bool processDelHdLeaf(const std::string &walletId); + bool processSignTx(const bs::message::Envelope& + , const BlockSettle::Common::SignerMessage_SignTxRequest&); private: std::shared_ptr logger_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 905c5f6b1..447eb6a64 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -199,26 +199,39 @@ void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) ui_->widgetWallets->onHDWalletDetails(hdWallet); } -void MainWindow::onWalletsList(const std::vector& wallets) +void MainWindow::onWalletsList(const std::string &id, const std::vector& wallets) { if (txDlg_) { - txDlg_->onWalletsList(wallets); + txDlg_->onWalletsList(id, wallets); } } void MainWindow::onAddresses(const std::vector &addrs) { - ui_->widgetWallets->onAddresses(addrs); + if (txDlg_) { + txDlg_->onAddresses(addrs); + } + else { + ui_->widgetWallets->onAddresses(addrs); + } } void MainWindow::onAddressComments(const std::string &walletId , const std::map &comments) { - ui_->widgetWallets->onAddressComments(walletId, comments); + if (txDlg_) { + txDlg_->onAddressComments(walletId, comments); + } + else { + ui_->widgetWallets->onAddressComments(walletId, comments); + } } void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) { + if (txDlg_) { + txDlg_->onAddressBalances(wbd.id, wbd.addrBalances); + } ui_->widgetWallets->onWalletBalance(wbd); statusBarView_->onXbtBalance(wbd); } @@ -253,6 +266,22 @@ void MainWindow::onFeeLevels(const std::map& feeLevels) } } +void bs::gui::qt::MainWindow::onUTXOs(const std::string& id + , const std::string& walletId, const std::vector& utxos) +{ + if (txDlg_) { + txDlg_->onUTXOs(id, walletId, utxos); + } +} + +void bs::gui::qt::MainWindow::onSignedTX(const std::string& id, BinaryData signedTX + , bs::error::ErrorCode result) +{ + if (txDlg_) { + txDlg_->onSignedTX(id, signedTX, result); + } +} + void MainWindow::showStartupDialog(bool showLicense) { StartupDialog startupDialog(showLicense, this); @@ -591,6 +620,15 @@ void MainWindow::onSend() }); connect(txDlg_, &CreateTransactionDialog::needWalletsList, this, &MainWindow::needWalletsList); connect(txDlg_, &CreateTransactionDialog::needFeeLevels, this, &MainWindow::needFeeLevels); + connect(txDlg_, &CreateTransactionDialog::needUTXOs, this, &MainWindow::needUTXOs); + connect(txDlg_, &CreateTransactionDialog::needExtAddresses, this, &MainWindow::needExtAddresses); + connect(txDlg_, &CreateTransactionDialog::needIntAddresses, this, &MainWindow::needIntAddresses); + connect(txDlg_, &CreateTransactionDialog::needUsedAddresses, this, &MainWindow::needUsedAddresses); + connect(txDlg_, &CreateTransactionDialog::needAddrComments, this, &MainWindow::needAddrComments); + connect(txDlg_, &CreateTransactionDialog::needWalletBalances, this, &MainWindow::needWalletBalances); + connect(txDlg_, &CreateTransactionDialog::needSignTX, this, &MainWindow::needSignTX); + connect(txDlg_, &CreateTransactionDialog::needBroadcastZC, this, &MainWindow::needBroadcastZC); + connect(txDlg_, &CreateTransactionDialog::needSetTxComment, this, &MainWindow::needSetTxComment); txDlg_->initUI(); if (!selectedWalletId.empty()) { @@ -993,7 +1031,7 @@ void MainWindow::initWidgets() // connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &MainWindow::createNewWallet); connect(ui_->widgetWallets, &WalletsWidget::needHDWalletDetails, this, &MainWindow::needHDWalletDetails); connect(ui_->widgetWallets, &WalletsWidget::needWalletBalances, this, &MainWindow::needWalletBalances); - connect(ui_->widgetWallets, &WalletsWidget::needSpendableUTXOs, this, &MainWindow::needSpendableUTXOs); + connect(ui_->widgetWallets, &WalletsWidget::needUTXOs, this, &MainWindow::needUTXOs); connect(ui_->widgetWallets, &WalletsWidget::needExtAddresses, this, &MainWindow::needExtAddresses); connect(ui_->widgetWallets, &WalletsWidget::needIntAddresses, this, &MainWindow::needIntAddresses); connect(ui_->widgetWallets, &WalletsWidget::needUsedAddresses, this, &MainWindow::needUsedAddresses); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index f75c89255..f58b58dd0 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -16,7 +16,7 @@ #include #include "Address.h" #include "ArmoryConnection.h" -#include "SignerDefs.h" +#include "SignContainer.h" #include "UiUtils.h" namespace spdlog { @@ -64,7 +64,7 @@ namespace bs { void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); - void onWalletsList(const std::vector&); + void onWalletsList(const std::string &id, const std::vector&); void onAddresses(const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); @@ -76,6 +76,8 @@ namespace bs { , const std::vector&); void onFeeLevels(const std::map&); + void onUTXOs(const std::string& id, const std::string& walletId, const std::vector&); + void onSignedTX(const std::string &id, BinaryData signedTX, bs::error::ErrorCode result); public slots: void onReactivate(); @@ -93,9 +95,8 @@ namespace bs { void putSetting(int, const QVariant &); void createNewWallet(); void needHDWalletDetails(const std::string &walletId); - void needWalletsList(UiUtils::WalletsTypes); + void needWalletsList(UiUtils::WalletsTypes, const std::string &id); void needWalletBalances(const std::string &walletId); - void needSpendableUTXOs(const std::string &walletId); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); @@ -109,6 +110,13 @@ namespace bs { void needAddressHistory(const bs::Address&); void needFeeLevels(const std::vector&); + void needUTXOs(const std::string& id, const std::string& walletId + , bool confOnly = false, bool swOnly = false); + + void needSignTX(const std::string& id, const bs::core::wallet::TXSignRequest& + , bool keepDupRecips = false, SignContainer::TXSignMode mode = SignContainer::TXSignMode::Full); + void needBroadcastZC(const std::string& id, const BinaryData&); + void needSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string& comment); private slots: void onSend(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 1bf595b9f..5c97f32b8 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -27,6 +27,7 @@ #include "BSMessageBox.h" #include "BSTerminalSplashScreen.h" #include "MainWindow.h" +#include "ProtobufHeadlessUtils.h" #include "common.pb.h" #include "terminal.pb.h" @@ -115,6 +116,7 @@ QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) , userSettings_(std::make_shared(TerminalUsers::Settings)) , userWallets_(std::make_shared(TerminalUsers::Wallets)) , userBlockchain_(std::make_shared(TerminalUsers::Blockchain)) + , userSigner_(std::make_shared(TerminalUsers::Signer)) {} QtGuiAdapter::~QtGuiAdapter() @@ -448,6 +450,8 @@ bool QtGuiAdapter::processSigner(const Envelope &env) case SignerMessage::kNeedNewWalletPrompt: createWallet(true); break; + case SignerMessage::kSignTxResponse: + return processSignTX(msg.sign_tx_response()); default: break; } return true; @@ -514,6 +518,10 @@ bool QtGuiAdapter::processWallets(const Envelope &env) return processWalletBalances(env, msg.wallet_balances()); case WalletsMessage::kTxDetailsResponse: return processTXDetails(msg.tx_details_response()); + case WalletsMessage::kWalletsListResponse: + return processWalletsList(msg.wallets_list_response()); + case WalletsMessage::kUtxos: + return processUTXOs(msg.utxos()); default: break; } return true; @@ -651,7 +659,6 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::putSetting, this, &QtGuiAdapter::onPutSetting); connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); - connect(mainWindow_, &bs::gui::qt::MainWindow::needSpendableUTXOs, this, &QtGuiAdapter::onNeedSpendableUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needIntAddresses, this, &QtGuiAdapter::onNeedIntAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needUsedAddresses, this, &QtGuiAdapter::onNeedUsedAddresses); @@ -662,6 +669,10 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needAddressHistory, this, &QtGuiAdapter::onNeedAddressHistory); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletsList, this, &QtGuiAdapter::onNeedWalletsList); connect(mainWindow_, &bs::gui::qt::MainWindow::needFeeLevels, this, &QtGuiAdapter::onNeedFeeLevels); + connect(mainWindow_, &bs::gui::qt::MainWindow::needUTXOs, this, &QtGuiAdapter::onNeedUTXOs); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSignTX, this, &QtGuiAdapter::onNeedSignTX); + connect(mainWindow_, &bs::gui::qt::MainWindow::needBroadcastZC, this, &QtGuiAdapter::onNeedBroadcastZC); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSetTxComment, this, &QtGuiAdapter::onNeedSetTxComment); } void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) @@ -743,22 +754,16 @@ void QtGuiAdapter::onNeedHDWalletDetails(const std::string &walletId) void QtGuiAdapter::onNeedWalletBalances(const std::string &walletId) { + logger_->debug("[{}] {}", __func__, walletId); WalletsMessage msg; msg.set_get_wallet_balances(walletId); Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } -void QtGuiAdapter::onNeedSpendableUTXOs(const std::string &walletId) -{ - WalletsMessage msg; - msg.set_get_spendable_utxos(walletId); - Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); -} - void QtGuiAdapter::onNeedExtAddresses(const std::string &walletId) { + logger_->debug("[{}] {}", __func__, walletId); WalletsMessage msg; msg.set_get_ext_addresses(walletId); Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; @@ -767,14 +772,16 @@ void QtGuiAdapter::onNeedExtAddresses(const std::string &walletId) void QtGuiAdapter::onNeedIntAddresses(const std::string &walletId) { + logger_->debug("[{}] {}", __func__, walletId); WalletsMessage msg; - msg.set_get_ext_addresses(walletId); + msg.set_get_int_addresses(walletId); Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } void QtGuiAdapter::onNeedUsedAddresses(const std::string &walletId) { + logger_->debug("[{}] {}", __func__, walletId); WalletsMessage msg; msg.set_get_used_addresses(walletId); Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; @@ -843,8 +850,31 @@ void QtGuiAdapter::onNeedAddressHistory(const bs::Address& addr) pushFill(env); } -void QtGuiAdapter::onNeedWalletsList(UiUtils::WalletsTypes) +void QtGuiAdapter::onNeedWalletsList(UiUtils::WalletsTypes wt, const std::string &id) { + WalletsMessage msg; + auto msgReq = msg.mutable_wallets_list_request(); + msgReq->set_id(id); + if (wt & UiUtils::WalletsTypes::WatchOnly) { + msgReq->set_watch_only(true); + } + if (wt & UiUtils::WalletsTypes::Full) { + msgReq->set_full(true); + } + if (wt & UiUtils::WalletsTypes::HardwareLegacy) { + msgReq->set_legacy(true); + msgReq->set_hardware(true); + } + if (wt & UiUtils::WalletsTypes::HardwareNativeSW) { + msgReq->set_native_sw(true); + msgReq->set_hardware(true); + } + if (wt & UiUtils::WalletsTypes::HardwareNestedSW) { + msgReq->set_nested_sw(true); + msgReq->set_hardware(true); + } + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); } void QtGuiAdapter::onNeedFeeLevels(const std::vector& levels) @@ -858,6 +888,57 @@ void QtGuiAdapter::onNeedFeeLevels(const std::vector& levels) pushFill(env); } +void QtGuiAdapter::onNeedUTXOs(const std::string& id, const std::string& walletId, bool confOnly, bool swOnly) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_get_utxos(); + msgReq->set_id(id); + msgReq->set_wallet_id(walletId); + msgReq->set_confirmed_only(confOnly); + msgReq->set_segwit_only(swOnly); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedSignTX(const std::string& id + , const bs::core::wallet::TXSignRequest& txReq, bool keepDupRecips + , SignContainer::TXSignMode mode) +{ + logger_->debug("[{}] {}", __func__, id); + SignerMessage msg; + auto msgReq = msg.mutable_sign_tx_request(); + msgReq->set_id(id); + *msgReq->mutable_tx_request() = bs::signer::coreTxRequestToPb(txReq, keepDupRecips); + msgReq->set_sign_mode((int)mode); + msgReq->set_keep_dup_recips(keepDupRecips); + Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedBroadcastZC(const std::string& id, const BinaryData& tx) +{ + ArmoryMessage msg; + auto msgReq = msg.mutable_tx_push(); + msgReq->set_push_id(id); + auto msgTx = msgReq->add_txs_to_push(); + msgTx->set_tx(tx.toBinStr()); + //not adding TX hashes atm + Envelope env{ 0, user_, userBlockchain_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string& comment) +{ + logger_->debug("[{}] {}: {}", __func__, txHash.toHexStr(true), comment); + WalletsMessage msg; + auto msgReq = msg.mutable_set_tx_comment(); + msgReq->set_wallet_id(walletId); + msgReq->set_tx_hash(txHash.toBinStr()); + msgReq->set_comment(comment); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -882,10 +963,9 @@ bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env , addrBal.txn(), addrBal.total_balance(), addrBal.spendable_balance() , addrBal.unconfirmed_balance() }); } - QMetaObject::invokeMethod(mainWindow_, [this, wbd] { + return QMetaObject::invokeMethod(mainWindow_, [this, wbd] { mainWindow_->onWalletBalance(wbd); }); - return true; } bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &response) @@ -949,10 +1029,9 @@ bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &resp catch (const std::exception &) {} txDetails.push_back(txDet); } - QMetaObject::invokeMethod(mainWindow_, [this, txDetails] { + return QMetaObject::invokeMethod(mainWindow_, [this, txDetails] { mainWindow_->onTXDetails(txDetails); }); - return true; } bool QtGuiAdapter::processLedgerEntries(const ArmoryMessage_LedgerEntries &response) @@ -980,12 +1059,11 @@ bool QtGuiAdapter::processLedgerEntries(const ArmoryMessage_LedgerEntries &respo } entries.emplace_back(std::move(txEntry)); } - QMetaObject::invokeMethod(mainWindow_, [this, entries, filter=response.filter() + return QMetaObject::invokeMethod(mainWindow_, [this, entries, filter=response.filter() , totPages=response.total_pages(), curPage=response.cur_page() , curBlock=response.cur_block()] { mainWindow_->onLedgerEntries(filter, totPages, curPage, curBlock, entries); }); - return true; } @@ -1022,10 +1100,9 @@ bool QtGuiAdapter::processAddressHist(const ArmoryMessage_AddressHistory& respon } entries.emplace_back(std::move(txEntry)); } - QMetaObject::invokeMethod(mainWindow_, [this, entries, addr, curBlock = response.cur_block()] { + return QMetaObject::invokeMethod(mainWindow_, [this, entries, addr, curBlock = response.cur_block()] { mainWindow_->onAddressHistory(addr, curBlock, entries); }); - return true; } bool QtGuiAdapter::processFeeLevels(const ArmoryMessage_FeeLevelsResponse& response) @@ -1034,10 +1111,44 @@ bool QtGuiAdapter::processFeeLevels(const ArmoryMessage_FeeLevelsResponse& respo for (const auto& pair : response.fee_levels()) { feeLevels[pair.level()] = pair.fee(); } - QMetaObject::invokeMethod(mainWindow_, [this, feeLevels]{ + return QMetaObject::invokeMethod(mainWindow_, [this, feeLevels]{ mainWindow_->onFeeLevels(feeLevels); }); - return true; +} + +bool QtGuiAdapter::processWalletsList(const WalletsMessage_WalletsListResponse& response) +{ + std::vector wallets; + wallets.reserve(response.wallets_size()); + for (const auto& wallet : response.wallets()) { + wallets.push_back(bs::sync::HDWalletData::fromCommonMessage(wallet)); + } + + return QMetaObject::invokeMethod(mainWindow_, [this, wallets, id = response.id()]{ + mainWindow_->onWalletsList(id, wallets); + }); +} + +bool QtGuiAdapter::processUTXOs(const WalletsMessage_UtxoListResponse& response) +{ + std::vector utxos; + utxos.reserve(response.utxos_size()); + for (const auto& serUtxo : response.utxos()) { + UTXO utxo; + utxo.unserialize(BinaryData::fromString(serUtxo)); + utxos.emplace_back(std::move(utxo)); + } + return QMetaObject::invokeMethod(mainWindow_, [this, utxos, response]{ + mainWindow_->onUTXOs(response.id(), response.wallet_id(), utxos); + }); +} + +bool QtGuiAdapter::processSignTX(const BlockSettle::Common::SignerMessage_SignTxResponse& response) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, response] { + mainWindow_->onSignedTX(response.id(), BinaryData::fromString(response.signed_tx()) + , static_cast(response.error_code())); + }); } #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 27fd8a473..f34b1f9a7 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -15,7 +15,7 @@ #include #include "Address.h" #include "ApiAdapter.h" -#include "SignerDefs.h" +#include "SignContainer.h" #include "ThreadSafeClasses.h" #include "UiUtils.h" @@ -31,8 +31,11 @@ namespace BlockSettle { class ArmoryMessage_AddressHistory; class ArmoryMessage_FeeLevelsResponse; class ArmoryMessage_LedgerEntries; + class SignerMessage_SignTxResponse; class WalletsMessage_TXDetailsResponse; + class WalletsMessage_UtxoListResponse; class WalletsMessage_WalletBalances; + class WalletsMessage_WalletsListResponse; } namespace Terminal { class SettingsMessage_SettingsResponse; @@ -86,12 +89,14 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processLedgerEntries(const BlockSettle::Common::ArmoryMessage_LedgerEntries &); bool processAddressHist(const BlockSettle::Common::ArmoryMessage_AddressHistory&); bool processFeeLevels(const BlockSettle::Common::ArmoryMessage_FeeLevelsResponse&); + bool processWalletsList(const BlockSettle::Common::WalletsMessage_WalletsListResponse&); + bool processUTXOs(const BlockSettle::Common::WalletsMessage_UtxoListResponse&); + bool processSignTX(const BlockSettle::Common::SignerMessage_SignTxResponse&); private slots: void onPutSetting(int idx, const QVariant &value); void onNeedHDWalletDetails(const std::string &walletId); void onNeedWalletBalances(const std::string &walletId); - void onNeedSpendableUTXOs(const std::string &walletId); void onNeedExtAddresses(const std::string &walletId); void onNeedIntAddresses(const std::string &walletId); void onNeedUsedAddresses(const std::string &walletId); @@ -101,13 +106,20 @@ private slots: void onNeedLedgerEntries(const std::string &filter); void onNeedTXDetails(const std::vector &, const bs::Address &); void onNeedAddressHistory(const bs::Address&); - void onNeedWalletsList(UiUtils::WalletsTypes); + void onNeedWalletsList(UiUtils::WalletsTypes, const std::string &id); void onNeedFeeLevels(const std::vector&); + void onNeedUTXOs(const std::string& id, const std::string& walletId + , bool confOnly, bool swOnly); + void onNeedSignTX(const std::string& id, const bs::core::wallet::TXSignRequest& + , bool keepDupRecips, SignContainer::TXSignMode); + void onNeedBroadcastZC(const std::string& id, const BinaryData&); + void onNeedSetTxComment(const std::string& walletId, const BinaryData& txHash + , const std::string& comment); private: std::shared_ptr logger_; std::shared_ptr userSettings_, userWallets_; - std::shared_ptr userBlockchain_; + std::shared_ptr userBlockchain_, userSigner_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; diff --git a/common b/common index 599aee4e0..7b6e65e84 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 599aee4e004dc7f6bef232a7fa07213399ad1c77 +Subproject commit 7b6e65e84f0550befacfbff5847d7b913c8c40d5 From 700160958dfbed4a876c44e2a4788da9a977e278 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 29 Sep 2020 21:16:05 +0300 Subject: [PATCH 013/146] TX tray notifications --- BlockSettleUILib/CoinControlModel.cpp | 4 +- BlockSettleUILib/NotificationCenter.cpp | 51 ++++++++++++++++++++++++- BlockSettleUILib/NotificationCenter.h | 14 +++++-- BlockSettleUILib/UiUtils.cpp | 2 +- BlockSettleUILib/UiUtils.h | 2 +- Core/ApiAdapter.cpp | 24 +++++++++++- GUI/QtWidgets/MainWindow.cpp | 37 +++++++++++++++++- GUI/QtWidgets/MainWindow.h | 1 + GUI/QtWidgets/QtGuiAdapter.cpp | 35 +++++++++++++++-- GUI/QtWidgets/QtGuiAdapter.h | 5 ++- common | 2 +- 11 files changed, 161 insertions(+), 16 deletions(-) diff --git a/BlockSettleUILib/CoinControlModel.cpp b/BlockSettleUILib/CoinControlModel.cpp index ee9dc2937..7167ff149 100644 --- a/BlockSettleUILib/CoinControlModel.cpp +++ b/BlockSettleUILib/CoinControlModel.cpp @@ -599,8 +599,10 @@ void CoinControlModel::loadInputs(const std::shared_ptrGetWallet(); addressNode = new AddressNode(CoinControlNode::Type::DoesNotMatter, QString::fromStdString(address.display()) - , QString::fromStdString(selectedInputs->GetWallet()->getAddressComment(bs::Address::fromHash(input.getRecipientScrAddr()))), row, cpfp_.get()); + , QString::fromStdString(wallet ? wallet->getAddressComment(bs::Address::fromHash(input.getRecipientScrAddr())) : "") + , row, cpfp_.get()); cpfp_->appendChildNode(addressNode); cpfpNodes_[addrStr] = addressNode; } diff --git a/BlockSettleUILib/NotificationCenter.cpp b/BlockSettleUILib/NotificationCenter.cpp index c385281dd..1af96deba 100644 --- a/BlockSettleUILib/NotificationCenter.cpp +++ b/BlockSettleUILib/NotificationCenter.cpp @@ -35,6 +35,18 @@ NotificationCenter::NotificationCenter(const std::shared_ptr &lo addResponder(std::make_shared(logger, mainWinUi, trayIcon, appSettings, this)); } +NotificationCenter::NotificationCenter(const std::shared_ptr& logger + , const Ui::BSTerminalMainWindow* mainWinUi + , const std::shared_ptr& trayIcon, QObject* parent) + : QObject(parent) +{ + qRegisterMetaType("NotifyType"); + qRegisterMetaType("NotifyMessage"); + + addResponder(std::make_shared(mainWinUi, this)); + addResponder(std::make_shared(logger, mainWinUi, trayIcon, this)); +} + void NotificationCenter::createInstance(const std::shared_ptr &logger, const std::shared_ptr &appSettings , const Ui::BSTerminalMainWindow *ui, const std::shared_ptr &trayIcon, QObject *parent) { @@ -82,6 +94,16 @@ NotificationTabResponder::NotificationTabResponder(const Ui::BSTerminalMainWindo }); } +NotificationTabResponder::NotificationTabResponder(const Ui::BSTerminalMainWindow* mainWinUi + , QObject* parent) + : NotificationResponder(parent), mainWinUi_(mainWinUi), iconDot_(QIcon(QLatin1String(":/ICON_DOT"))) +{ + mainWinUi_->tabWidget->setIconSize(QSize(8, 8)); + connect(mainWinUi_->tabWidget, &QTabWidget::currentChanged, [this](int index) { + mainWinUi_->tabWidget->setTabIcon(index, QIcon()); + }); +} + void NotificationTabResponder::respond(bs::ui::NotifyType nt, bs::ui::NotifyMessage msg) { if (nt == bs::ui::NotifyType::UpdateUnreadMessage) { @@ -142,6 +164,33 @@ NotificationTrayIconResponder::NotificationTrayIconResponder(const std::shared_p #endif // BS_USE_DBUS } +NotificationTrayIconResponder::NotificationTrayIconResponder(const std::shared_ptr& logger + , const Ui::BSTerminalMainWindow* mainWinUi + , const std::shared_ptr& trayIcon + , QObject* parent) + : NotificationResponder(parent) + , logger_(logger) + , mainWinUi_(mainWinUi) + , trayIcon_(trayIcon) + , notifMode_(QSystemTray) +#ifdef BS_USE_DBUS + , dbus_(new DBusNotification(tr("BlockSettle Terminal"), this)) +#endif +{ + connect(trayIcon_.get(), &QSystemTrayIcon::messageClicked, this, &NotificationTrayIconResponder::messageClicked); + +#ifdef BS_USE_DBUS + if (dbus_->isValid()) { + notifMode_ = Freedesktop; + + disconnect(trayIcon_.get(), &QSystemTrayIcon::messageClicked, + this, &NotificationTrayIconResponder::messageClicked); + connect(dbus_, &DBusNotification::actionInvoked, + this, &NotificationTrayIconResponder::notificationAction); + } +#endif // BS_USE_DBUS +} + static const QString c_newVersionAction = QLatin1String("BlockSettleNewVersionAction"); static const QString c_newOkAction = QLatin1String("BlockSettleNotificationActionOk"); @@ -167,7 +216,7 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif switch (nt) { case bs::ui::NotifyType::BlockchainTX: - if ((msg.size() < 2) || appSettings_ ? !appSettings_->get(ApplicationSettings::notifyOnTX) : true) { + if ((msg.size() < 2) || appSettings_ ? !appSettings_->get(ApplicationSettings::notifyOnTX) : false) { return; } title = msg[0].toString(); diff --git a/BlockSettleUILib/NotificationCenter.h b/BlockSettleUILib/NotificationCenter.h index 1ab134ab6..082fcdabb 100644 --- a/BlockSettleUILib/NotificationCenter.h +++ b/BlockSettleUILib/NotificationCenter.h @@ -80,8 +80,10 @@ class NotificationCenter : public QObject Q_OBJECT public: - NotificationCenter(const std::shared_ptr &, const std::shared_ptr & + [[deprecated]] NotificationCenter(const std::shared_ptr &, const std::shared_ptr & , const Ui::BSTerminalMainWindow *, const std::shared_ptr &, QObject *parent = nullptr); + NotificationCenter(const std::shared_ptr&, const Ui::BSTerminalMainWindow* + , const std::shared_ptr&, QObject* parent = nullptr); ~NotificationCenter() noexcept = default; static void createInstance(const std::shared_ptr &logger, const std::shared_ptr &, const Ui::BSTerminalMainWindow * @@ -89,13 +91,13 @@ class NotificationCenter : public QObject static NotificationCenter *instance(); static void destroyInstance(); static void notify(bs::ui::NotifyType, const bs::ui::NotifyMessage &); + void enqueue(bs::ui::NotifyType, const bs::ui::NotifyMessage&); signals: void notifyEndpoint(bs::ui::NotifyType, const bs::ui::NotifyMessage &); void newChatMessageClick(const QString &chatId); private: - void enqueue(bs::ui::NotifyType, const bs::ui::NotifyMessage &); void addResponder(const std::shared_ptr &); private: @@ -108,8 +110,10 @@ class NotificationTabResponder : public NotificationResponder { Q_OBJECT public: - NotificationTabResponder(const Ui::BSTerminalMainWindow *mainWinUi, + [[deprecated]] NotificationTabResponder(const Ui::BSTerminalMainWindow *mainWinUi, std::shared_ptr appSettings, QObject *parent = nullptr); + NotificationTabResponder(const Ui::BSTerminalMainWindow* mainWinUi, + QObject* parent = nullptr); void respond(bs::ui::NotifyType, bs::ui::NotifyMessage) override; @@ -131,9 +135,11 @@ class NotificationTrayIconResponder : public NotificationResponder { Q_OBJECT public: - NotificationTrayIconResponder(const std::shared_ptr &, const Ui::BSTerminalMainWindow *mainWinUi + [[deprecated]] NotificationTrayIconResponder(const std::shared_ptr &, const Ui::BSTerminalMainWindow *mainWinUi , const std::shared_ptr &trayIcon, const std::shared_ptr &appSettings , QObject *parent = nullptr); + NotificationTrayIconResponder(const std::shared_ptr&, const Ui::BSTerminalMainWindow* mainWinUi + , const std::shared_ptr& trayIcon, QObject* parent = nullptr); void respond(bs::ui::NotifyType, bs::ui::NotifyMessage) override; diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index e4ddae18e..fde906df6 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -63,7 +63,7 @@ QString UiUtils::displayDateTime(const QDateTime& datetime) return datetime.toString(format); } -QString UiUtils::displayDateTime(uint64_t time) +QString UiUtils::displayDateTime(uint32_t time) // in UnixTime { return displayDateTime(QDateTime::fromTime_t(time)); } diff --git a/BlockSettleUILib/UiUtils.h b/BlockSettleUILib/UiUtils.h index d5c4a2205..d5496ce4b 100644 --- a/BlockSettleUILib/UiUtils.h +++ b/BlockSettleUILib/UiUtils.h @@ -102,7 +102,7 @@ namespace UiUtils // return only number, product string is not included QString displayAmountForProduct(double quantity, const QString& product, bs::network::Asset::Type at); - QString displayDateTime(uint64_t time); + QString displayDateTime(uint32_t time); QString displayDateTime(const QDateTime& datetime); QString displayTimeMs(const QDateTime& datetime); diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp index bf3d56766..6e1303d19 100644 --- a/Core/ApiAdapter.cpp +++ b/Core/ApiAdapter.cpp @@ -83,7 +83,15 @@ class ApiBusGateway : public ApiBusAdapter envCopy.sender = parent_->user_; if (std::dynamic_pointer_cast(env.receiver)) { - return parent_->pushFill(envCopy); + if (parent_->pushFill(envCopy)) { + if (env.request) { + idMap_[envCopy.id] = { env.id, env.sender }; + } + return true; + } + else { + return false; + } } else if (env.receiver->isFallback()) { if (env.request) { @@ -113,7 +121,19 @@ class ApiBusGateway : public ApiBusAdapter { auto envCopy = env; envCopy.id = 0; - envCopy.receiver.reset(); // = std::make_shared(-1); + envCopy.receiver.reset(); + if (!env.request) { + const auto& itIdMap = idMap_.find(env.id); + if (itIdMap != idMap_.end()) { + envCopy.id = itIdMap->second.id; + envCopy.receiver = itIdMap->second.requester; + if (push(envCopy)) { + idMap_.erase(itIdMap); + return true; + } + return false; + } + } bool rc = pushFill(envCopy); if (rc && env.request) { idMap_[envCopy.id] = { env.id, env.sender }; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 447eb6a64..08a287a98 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -68,7 +68,7 @@ MainWindow::MainWindow(const std::shared_ptr &logger setupIcon(); UiUtils::setupIconFont(this); - notifCenter_ = std::make_shared(logger_, nullptr, ui_.get(), sysTrayIcon_, this); + notifCenter_ = std::make_shared(logger_, ui_.get(), sysTrayIcon_, this); statusBarView_ = std::make_shared(ui_->statusbar); @@ -254,6 +254,41 @@ void MainWindow::onTXDetails(const std::vector &txDet ui_->widgetExplorer->onTXDetails(txDet); } +void bs::gui::qt::MainWindow::onNewZCs(const std::vector& txDet) +{ + QStringList lines; + for (const auto& tx : txDet) { + lines << tr("Date: %1").arg(UiUtils::displayDateTime(tx.tx.getTxTime())); + lines << tr("TX: %1 %2 %3").arg(tr(bs::sync::Transaction::toString(tx.direction))) + .arg(QString::fromStdString(tx.amount)) + .arg(QString::fromStdString(tx.walletSymbol)); + lines << tr("Wallet: %1").arg(QString::fromStdString(tx.walletName)); + + QString mainAddress; + switch (tx.outAddresses.size()) { + case 0: + switch (tx.outputAddresses.size()) { + case 1: + mainAddress = QString::fromStdString(tx.outputAddresses.at(0).address.display()); + break; + default: + mainAddress = tr("%1 output addresses").arg(tx.outputAddresses.size()); + break; + } + break; + case 1: + mainAddress = QString::fromStdString(tx.outAddresses.at(0).display()); + break; + default: + mainAddress = tr("%1 output addresses").arg(tx.outAddresses.size()); + break; + } + lines << mainAddress << QString(); + } + const auto& title = tr("New blockchain transaction"); + notifCenter_->enqueue(bs::ui::NotifyType::BlockchainTX, { title, lines.join(tr("\n")) }); +} + void MainWindow::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) { ui_->widgetExplorer->onAddressHistory(addr, curBlock, entries); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index f58b58dd0..9321887d7 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -72,6 +72,7 @@ namespace bs { void onLedgerEntries(const std::string &filter, uint32_t totalPages , uint32_t curPage, uint32_t curBlock, const std::vector &); void onTXDetails(const std::vector &); + void onNewZCs(const std::vector&); void onAddressHistory(const bs::Address&, uint32_t curBlock , const std::vector&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 5c97f32b8..c404296f6 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -421,6 +421,8 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) return processAddressHist(msg.address_history()); case ArmoryMessage::kFeeLevelsResponse: return processFeeLevels(msg.fee_levels_response()); + case ArmoryMessage::kZcReceived: + return processZC(msg.zc_received()); default: break; } return true; @@ -517,7 +519,7 @@ bool QtGuiAdapter::processWallets(const Envelope &env) case WalletsMessage::kWalletBalances: return processWalletBalances(env, msg.wallet_balances()); case WalletsMessage::kTxDetailsResponse: - return processTXDetails(msg.tx_details_response()); + return processTXDetails(env.id, msg.tx_details_response()); case WalletsMessage::kWalletsListResponse: return processWalletsList(msg.wallets_list_response()); case WalletsMessage::kUtxos: @@ -968,13 +970,13 @@ bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env }); } -bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &response) +bool QtGuiAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetailsResponse &response) { std::vector txDetails; for (const auto &resp : response.responses()) { bs::sync::TXWalletDetails txDet{ BinaryData::fromString(resp.tx_hash()), resp.wallet_id() , resp.wallet_name(), static_cast(resp.wallet_type()) - , static_cast(resp.direction()) + , resp.wallet_symbol(), static_cast(resp.direction()) , resp.comment(), resp.valid(), resp.amount() }; const auto &ownTxHash = BinaryData::fromString(resp.tx_hash()); @@ -1029,6 +1031,13 @@ bool QtGuiAdapter::processTXDetails(const WalletsMessage_TXDetailsResponse &resp catch (const std::exception &) {} txDetails.push_back(txDet); } + const auto& itZC = newZCs_.find(msgId); + if (itZC != newZCs_.end()) { + newZCs_.erase(itZC); + return QMetaObject::invokeMethod(mainWindow_, [this, txDetails] { + mainWindow_->onNewZCs(txDetails); + }); + } return QMetaObject::invokeMethod(mainWindow_, [this, txDetails] { mainWindow_->onTXDetails(txDetails); }); @@ -1151,4 +1160,24 @@ bool QtGuiAdapter::processSignTX(const BlockSettle::Common::SignerMessage_SignTx }); } +bool QtGuiAdapter::processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived& zcs) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_tx_details_request(); + for (const auto& zcEntry : zcs.tx_entries()) { + auto txReq = msgReq->add_requests(); + txReq->set_tx_hash(zcEntry.tx_hash()); + if (zcEntry.wallet_ids_size() > 0) { + txReq->set_wallet_id(zcEntry.wallet_ids(0)); + } + txReq->set_value(zcEntry.value()); + } + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + if (!pushFill(env)) { + return false; + } + newZCs_.insert(env.id); + return true; +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index f34b1f9a7..af130087f 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -31,6 +31,7 @@ namespace BlockSettle { class ArmoryMessage_AddressHistory; class ArmoryMessage_FeeLevelsResponse; class ArmoryMessage_LedgerEntries; + class ArmoryMessage_ZCReceived; class SignerMessage_SignTxResponse; class WalletsMessage_TXDetailsResponse; class WalletsMessage_UtxoListResponse; @@ -85,13 +86,14 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu void processWalletLoaded(const bs::sync::WalletInfo &); bool processWalletBalances(const bs::message::Envelope & , const BlockSettle::Common::WalletsMessage_WalletBalances &); - bool processTXDetails(const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); + bool processTXDetails(uint64_t msgId, const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); bool processLedgerEntries(const BlockSettle::Common::ArmoryMessage_LedgerEntries &); bool processAddressHist(const BlockSettle::Common::ArmoryMessage_AddressHistory&); bool processFeeLevels(const BlockSettle::Common::ArmoryMessage_FeeLevelsResponse&); bool processWalletsList(const BlockSettle::Common::WalletsMessage_WalletsListResponse&); bool processUTXOs(const BlockSettle::Common::WalletsMessage_UtxoListResponse&); bool processSignTX(const BlockSettle::Common::SignerMessage_SignTxResponse&); + bool processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived&); private slots: void onPutSetting(int idx, const QVariant &value); @@ -132,6 +134,7 @@ private slots: bool walletsReady_{ false }; std::unordered_map hdWallets_; + std::set newZCs_; }; diff --git a/common b/common index 7b6e65e84..58e78a517 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 7b6e65e84f0550befacfbff5847d7b913c8c40d5 +Subproject commit 58e78a517837f54a5194030c5f50af313ec68cf0 From 117900146ef45808dca24fa189f6af7dc4e679c6 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 30 Sep 2020 20:26:07 +0300 Subject: [PATCH 014/146] TX list fixes --- BlockSettleUILib/AddressDetailDialog.h | 3 +- BlockSettleUILib/AddressDetailsWidget.cpp | 2 +- BlockSettleUILib/AddressDetailsWidget.h | 3 +- BlockSettleUILib/ExplorerWidget.h | 2 +- BlockSettleUILib/TransactionDetailsWidget.cpp | 4 +- BlockSettleUILib/TransactionDetailsWidget.h | 3 +- BlockSettleUILib/TransactionsViewModel.cpp | 105 +++++++++++++++--- BlockSettleUILib/TransactionsViewModel.h | 9 +- BlockSettleUILib/WalletsWidget.h | 3 +- GUI/QtWidgets/MainWindow.cpp | 18 ++- GUI/QtWidgets/MainWindow.h | 4 +- GUI/QtWidgets/QtGuiAdapter.cpp | 17 ++- GUI/QtWidgets/QtGuiAdapter.h | 5 +- common | 2 +- 14 files changed, 148 insertions(+), 32 deletions(-) diff --git a/BlockSettleUILib/AddressDetailDialog.h b/BlockSettleUILib/AddressDetailDialog.h index fbb930391..101f3049e 100644 --- a/BlockSettleUILib/AddressDetailDialog.h +++ b/BlockSettleUILib/AddressDetailDialog.h @@ -55,7 +55,8 @@ Q_OBJECT void onTXDetails(const std::vector &); signals: - void needTXDetails(const std::vector &, const bs::Address &); + void needTXDetails(const std::vector &, bool useCache + , const bs::Address &); private slots: void onCopyClicked() const; diff --git a/BlockSettleUILib/AddressDetailsWidget.cpp b/BlockSettleUILib/AddressDetailsWidget.cpp index 8e76ac623..65330bca2 100644 --- a/BlockSettleUILib/AddressDetailsWidget.cpp +++ b/BlockSettleUILib/AddressDetailsWidget.cpp @@ -559,7 +559,7 @@ void AddressDetailsWidget::onAddressHistory(const bs::Address& addr, uint32_t cu onTXDetails({}); } else { - emit needTXDetails(txDetRequest, currentAddr_); + emit needTXDetails(txDetRequest, false, currentAddr_); } } diff --git a/BlockSettleUILib/AddressDetailsWidget.h b/BlockSettleUILib/AddressDetailsWidget.h index 708b85a1f..7e9e2f28f 100644 --- a/BlockSettleUILib/AddressDetailsWidget.h +++ b/BlockSettleUILib/AddressDetailsWidget.h @@ -73,7 +73,8 @@ class AddressDetailsWidget : public QWidget void transactionClicked(QString txId); void finished() const; void needAddressHistory(const bs::Address&); - void needTXDetails(const std::vector&, const bs::Address &); + void needTXDetails(const std::vector&, bool useCache + , const bs::Address &); private slots: void onTxClicked(QTreeWidgetItem *item, int column); diff --git a/BlockSettleUILib/ExplorerWidget.h b/BlockSettleUILib/ExplorerWidget.h index 174edd209..42466e46d 100644 --- a/BlockSettleUILib/ExplorerWidget.h +++ b/BlockSettleUILib/ExplorerWidget.h @@ -60,7 +60,7 @@ Q_OBJECT signals: void needAddressHistory(const bs::Address&); - void needTXDetails(const std::vector&, const bs::Address&); + void needTXDetails(const std::vector&, bool useCache, const bs::Address&); protected slots: void onSearchStarted(bool saveToHistory); diff --git a/BlockSettleUILib/TransactionDetailsWidget.cpp b/BlockSettleUILib/TransactionDetailsWidget.cpp index 66d5ec242..9ec05db60 100644 --- a/BlockSettleUILib/TransactionDetailsWidget.cpp +++ b/BlockSettleUILib/TransactionDetailsWidget.cpp @@ -88,7 +88,7 @@ void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID if (!armoryPtr_) { if (rpcTXID.getSize() == 32) { curTxHash_ = rpcTXID; - emit needTXDetails({ { curTxHash_, {}, 0 } }, {}); + emit needTXDetails({ { curTxHash_, {}, 0 } }, false, {}); } else { Codec_SignerState::SignerState signerState; @@ -101,7 +101,7 @@ void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID throw std::runtime_error("Uninited TX"); } curTxHash_ = tx.getThisHash(); - emit needTXDetails({ { tx.getThisHash(), {}, 0 } }, {}); + emit needTXDetails({ { tx.getThisHash(), {}, 0 } }, false, {}); } catch (const std::exception& e) { logger_->error("[TransactionDetailsWidget::populateTransactionWidget]" diff --git a/BlockSettleUILib/TransactionDetailsWidget.h b/BlockSettleUILib/TransactionDetailsWidget.h index c234e0958..effc6214e 100644 --- a/BlockSettleUILib/TransactionDetailsWidget.h +++ b/BlockSettleUILib/TransactionDetailsWidget.h @@ -99,7 +99,8 @@ public slots: void addressClicked(QString addressId); void txHashClicked(QString txHash); void finished() const; - void needTXDetails(const std::vector&, const bs::Address&); + void needTXDetails(const std::vector&, bool useCache + , const bs::Address&); protected slots: void onAddressClicked(QTreeWidgetItem *item, int column); diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index 307865865..a9aa71cbf 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -171,14 +171,23 @@ void TXNode::add(TXNode *child) } void TXNode::del(int index) +{ + auto node = take(index); + if (node) { + delete node; + } +} + +TXNode* TXNode::take(int index) { if (index >= children_.size()) { - return; + return nullptr; } - children_.removeAt(index); + auto node = children_.takeAt(index); for (int i = index; i < children_.size(); ++i) { children_[i]->row_--; } + return node; } void TXNode::forEach(const std::function &cb) @@ -221,6 +230,20 @@ TXNode *TXNode::find(const bs::TXEntry &entry) const return nullptr; } +TXNode* TXNode::find(const BinaryData& txHash) const +{ + if (item_ && (item_->txEntry.txHash == txHash)) { + return const_cast(this); + } + for (const auto& child : children_) { + const auto found = child->find(txHash); + if (found != nullptr) { + return found; + } + } + return nullptr; +} + std::vector TXNode::nodesByTxHash(const BinaryData &txHash) const { std::vector result; @@ -1098,6 +1121,9 @@ void TransactionsViewModel::onNewBlock(unsigned int curBlock) curBlock_ = curBlock; for (const auto &node : rootNode_->children()) { + if (node->item()->confirmations == 0) { + continue; + } node->item()->confirmations += diff; node->item()->txEntry.nbConf += diff; } @@ -1149,23 +1175,60 @@ void TransactionsViewModel::onLedgerEntries(const std::string &, uint32_t for (const auto &entry : entries) { const auto &walletId = entry.walletIds.empty() ? std::string{} : *(entry.walletIds.cbegin()); txWallet.push_back({ entry.txHash, walletId, entry.value }); - } //TODO: can be optimized later to retrieve details only for visible rows - emit needTXDetails(txWallet, {}); + } + emit needTXDetails(txWallet, true, {}); +} + +void TransactionsViewModel::onZCsInvalidated(const std::vector& txHashes) +{ + for (const auto& txHash : txHashes) { + const auto node = rootNode_->find(txHash); + if (node) { + const int row = node->row(); + beginRemoveRows(QModelIndex(), row, row); + auto removedNode = rootNode_->take(row); + itemIndex_.erase({ node->item()->txEntry.txHash, node->item()->walletID.toStdString() }); + endRemoveRows(); + if (removedNode) { + removedNode->item()->curBlock = curBlock_; + invalidatedNodes_[txHash] = removedNode; + } + } + } + std::vector txWallet; + txWallet.reserve(invalidatedNodes_.size()); + for (const auto& invNode : invalidatedNodes_) { + const auto& entry = invNode.second->item()->txEntry; + const auto& walletId = entry.walletIds.empty() ? std::string{} : *(entry.walletIds.cbegin()); + txWallet.push_back({ entry.txHash, walletId, entry.value }); + } + emit needTXDetails(txWallet, false, {}); } void TransactionsViewModel::onTXDetails(const std::vector &txDet) { + std::vector newNodes; for (const auto &tx : txDet) { + TransactionPtr item; + int row = -1; const auto &itIndex = itemIndex_.find({tx.txHash, tx.walletId}); if (itIndex == itemIndex_.end()) { - continue; + const auto& itInv = invalidatedNodes_.find(tx.txHash); + if (itInv == invalidatedNodes_.end()) { + continue; + } + newNodes.push_back(itInv->second); + item = itInv->second->item(); + invalidatedNodes_.erase(itInv); } - const int row = itIndex->second; - if (row >= rootNode_->nbChildren()) { - logger_->warn("[{}] invalid row: {} of {}", __func__, row, rootNode_->nbChildren()); - continue; + else { + row = itIndex->second; + if (row >= rootNode_->nbChildren()) { + logger_->warn("[{}] invalid row: {} of {}", __func__, row, rootNode_->nbChildren()); + continue; + } + item = rootNode_->children()[row]->item(); } - const auto &item = rootNode_->children()[row]->item(); item->walletName = QString::fromStdString(tx.walletName); item->direction = tx.direction; item->dirStr = QObject::tr(bs::sync::Transaction::toStringDir(tx.direction)); @@ -1187,13 +1250,29 @@ void TransactionsViewModel::onTXDetails(const std::vectormainAddress = tr("%1 output addresses").arg(tx.outAddresses.size()); break; } - emit dataChanged(index(row, static_cast(Columns::Wallet)) - , index(row, static_cast(Columns::Comment))); - item->tx = tx.tx; item->inputAddresses = tx.inputAddresses; item->outputAddresses = tx.outputAddresses; item->changeAddress = tx.changeAddress; + if (row >= 0) { + emit dataChanged(index(row, static_cast(Columns::Wallet)) + , index(row, static_cast(Columns::Comment))); + } else { + item->confirmations = curBlock_ - item->curBlock + 1; + //FIXME: this code doesn't provide proper conf #, even with caching turned off: + // curBlock_ + 1 - tx.tx.getTxHeight(); + item->txEntry.nbConf = item->confirmations; + } + } + if (!newNodes.empty()) { + const int startRow = rootNode_->nbChildren(); + const int endRow = rootNode_->nbChildren() + newNodes.size() - 1; + beginInsertRows(QModelIndex(), startRow, endRow); + for (const auto& node : newNodes) { + itemIndex_[{node->item()->txEntry.txHash, node->item()->walletID.toStdString()}] = rootNode_->nbChildren(); + rootNode_->add(node); + } + endInsertRows(); } } diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index c59c35f2d..328642a81 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -80,6 +80,7 @@ struct TransactionsViewItem bool isPayin() const; bs::Address filterAddress; + uint32_t curBlock{ 0 }; private: bool txHashesReceived{ false }; @@ -102,12 +103,14 @@ class TXNode const QList &children() const { return children_; } TXNode *parent() const { return parent_; } TXNode *find(const bs::TXEntry &) const; + TXNode* find(const BinaryData &txHash) const; std::vector nodesByTxHash(const BinaryData &) const; void clear(bool del = true); void setData(const TransactionsViewItem &data) { *item_ = data; } void add(TXNode *child); void del(int index); + TXNode *take(int index); int row() const { return row_; } unsigned int level() const; QVariant data(int, int) const; @@ -172,11 +175,12 @@ Q_OBJECT void onLedgerEntries(const std::string &filter, uint32_t totalPages , uint32_t curPage, uint32_t curBlock, const std::vector &); + void onZCsInvalidated(const std::vector& txHashes); void onNewBlock(unsigned int topBlock); void onTXDetails(const std::vector &); signals: - void needTXDetails(const std::vector &, const bs::Address &); + void needTXDetails(const std::vector &, bool useCache, const bs::Address &); private slots: void updatePage(); @@ -234,7 +238,8 @@ private slots: }; private: - std::unique_ptr rootNode_; + std::unique_ptr rootNode_; + std::map invalidatedNodes_; TransactionPtr oldestItem_; std::shared_ptr logger_; std::shared_ptr ledgerDelegate_; diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 3e41afd9d..b25a11ba1 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -123,7 +123,8 @@ public slots: void setAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); void needLedgerEntries(const std::string &filter); - void needTXDetails(const std::vector &, const bs::Address &); + void needTXDetails(const std::vector &, bool useCache + , const bs::Address &); private slots: void showWalletProperties(const QModelIndex& index); diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 08a287a98..ef483b314 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -258,13 +258,14 @@ void bs::gui::qt::MainWindow::onNewZCs(const std::vectorenqueue(bs::ui::NotifyType::BlockchainTX, { title, lines.join(tr("\n")) }); } +void bs::gui::qt::MainWindow::onZCsInvalidated(const std::vector& txHashes) +{ + if (txModel_) { + txModel_->onZCsInvalidated(txHashes); + } +} + void MainWindow::onAddressHistory(const bs::Address& addr, uint32_t curBlock, const std::vector& entries) { ui_->widgetExplorer->onAddressHistory(addr, curBlock, entries); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 9321887d7..dbbf277d6 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -73,6 +73,7 @@ namespace bs { , uint32_t curPage, uint32_t curBlock, const std::vector &); void onTXDetails(const std::vector &); void onNewZCs(const std::vector&); + void onZCsInvalidated(const std::vector& txHashes); void onAddressHistory(const bs::Address&, uint32_t curBlock , const std::vector&); @@ -107,7 +108,8 @@ namespace bs { , const std::string &comment); void needLedgerEntries(const std::string &filter); - void needTXDetails(const std::vector&, const bs::Address& addr = {}); + void needTXDetails(const std::vector& + , bool useCache=true, const bs::Address& addr = {}); void needAddressHistory(const bs::Address&); void needFeeLevels(const std::vector&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index c404296f6..621b85f42 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -423,6 +423,8 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) return processFeeLevels(msg.fee_levels_response()); case ArmoryMessage::kZcReceived: return processZC(msg.zc_received()); + case ArmoryMessage::kZcInvalidated: + return processZCInvalidated(msg.zc_invalidated()); default: break; } return true; @@ -826,7 +828,7 @@ void QtGuiAdapter::onNeedLedgerEntries(const std::string &filter) } void QtGuiAdapter::onNeedTXDetails(const const std::vector &txWallet - , const bs::Address &addr) + , bool useCache, const bs::Address &addr) { WalletsMessage msg; auto msgReq = msg.mutable_tx_details_request(); @@ -839,6 +841,7 @@ void QtGuiAdapter::onNeedTXDetails(const const std::vector & if (!addr.empty()) { msgReq->set_address(addr.display()); } + msgReq->set_use_cache(useCache); Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } @@ -1180,4 +1183,16 @@ bool QtGuiAdapter::processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived return true; } +bool QtGuiAdapter::processZCInvalidated(const ArmoryMessage_ZCInvalidated& zcInv) +{ + std::vector txHashes; + txHashes.reserve(zcInv.tx_hashes_size()); + for (const auto& hashStr : zcInv.tx_hashes()) { + txHashes.push_back(BinaryData::fromString(hashStr)); + } + return QMetaObject::invokeMethod(mainWindow_, [this, txHashes] { + mainWindow_->onZCsInvalidated(txHashes); + }); +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index af130087f..403005b66 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -31,6 +31,7 @@ namespace BlockSettle { class ArmoryMessage_AddressHistory; class ArmoryMessage_FeeLevelsResponse; class ArmoryMessage_LedgerEntries; + class ArmoryMessage_ZCInvalidated; class ArmoryMessage_ZCReceived; class SignerMessage_SignTxResponse; class WalletsMessage_TXDetailsResponse; @@ -94,6 +95,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processUTXOs(const BlockSettle::Common::WalletsMessage_UtxoListResponse&); bool processSignTX(const BlockSettle::Common::SignerMessage_SignTxResponse&); bool processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived&); + bool processZCInvalidated(const BlockSettle::Common::ArmoryMessage_ZCInvalidated&); private slots: void onPutSetting(int idx, const QVariant &value); @@ -106,7 +108,8 @@ private slots: void onSetAddrComment(const std::string &walletId, const bs::Address & , const std::string &comment); void onNeedLedgerEntries(const std::string &filter); - void onNeedTXDetails(const std::vector &, const bs::Address &); + void onNeedTXDetails(const std::vector &, bool useCache + , const bs::Address &); void onNeedAddressHistory(const bs::Address&); void onNeedWalletsList(UiUtils::WalletsTypes, const std::string &id); void onNeedFeeLevels(const std::vector&); diff --git a/common b/common index 58e78a517..c1ae7aab7 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 58e78a517837f54a5194030c5f50af313ec68cf0 +Subproject commit c1ae7aab7a45ca3dc498a3cee55b877a180b748a From dd630e8b4489af7289230504f979a82132dd4de4 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 2 Oct 2020 21:00:13 +0300 Subject: [PATCH 015/146] Settings GUI reimplemented --- BlockSettleUILib/Settings/APISettingsPage.cpp | 91 +++-- BlockSettleUILib/Settings/APISettingsPage.h | 4 + .../Settings/ArmoryServersViewModel.cpp | 17 +- .../Settings/ArmoryServersViewModel.h | 6 +- .../Settings/ArmoryServersWidget.cpp | 236 ++++++++++--- .../Settings/ArmoryServersWidget.h | 17 +- BlockSettleUILib/Settings/ConfigDialog.cpp | 92 ++++- BlockSettleUILib/Settings/ConfigDialog.h | 27 +- .../Settings/DealingSettingsPage.cpp | 95 +++-- .../Settings/DealingSettingsPage.h | 1 + .../Settings/GeneralSettingsPage.cpp | 238 ++++++++----- .../Settings/GeneralSettingsPage.h | 3 +- .../Settings/NetworkSettingsPage.cpp | 180 +++++++--- .../Settings/NetworkSettingsPage.h | 16 +- .../Settings/SignerSettingsPage.cpp | 113 ++++-- .../Settings/SignerSettingsPage.h | 21 +- .../Settings/SignersManageWidget.cpp | 181 +++++++--- .../Settings/SignersManageWidget.h | 15 +- BlockSettleUILib/Settings/SignersModel.cpp | 16 +- BlockSettleUILib/Settings/SignersModel.h | 8 +- Core/SettingsAdapter.cpp | 327 +++++++++++++++++- Core/SettingsAdapter.h | 36 +- Core/SignerAdapter.cpp | 36 +- GUI/QtWidgets/MainWindow.cpp | 80 ++++- GUI/QtWidgets/MainWindow.h | 20 +- GUI/QtWidgets/QtGuiAdapter.cpp | 301 ++++++++++------ GUI/QtWidgets/QtGuiAdapter.h | 20 +- common | 2 +- 28 files changed, 1698 insertions(+), 501 deletions(-) diff --git a/BlockSettleUILib/Settings/APISettingsPage.cpp b/BlockSettleUILib/Settings/APISettingsPage.cpp index c3664aa92..f376e48d8 100644 --- a/BlockSettleUILib/Settings/APISettingsPage.cpp +++ b/BlockSettleUILib/Settings/APISettingsPage.cpp @@ -30,15 +30,27 @@ APISettingsPage::APISettingsPage(QWidget* parent) connect(ui_->pushButtonCopyOwnPubKey, &QPushButton::clicked, this, [this] { QApplication::clipboard()->setText(ui_->labelOwnPubKey->text()); }); - connect(ui_->pushButtonApiKeyImport, &QPushButton::clicked, this, [this] { - auto apiKey = ApiKeyEntryDialog::getApiKey(this); - if (apiKey.isEmpty()) { - return; - } - auto apiKeyCopy = SecureBinaryData::fromString(apiKey.toStdString()); + connect(ui_->pushButtonApiKeyImport, &QPushButton::clicked, this + , &APISettingsPage::onApiKeyImport); + connect(ui_->pushButtonApiKeyClear, &QPushButton::clicked, this, [this] { + apiKeyEncrypted_.clear(); + updateApiKeyStatus(); + }); +} + +APISettingsPage::~APISettingsPage() = default; + +void APISettingsPage::onApiKeyImport() +{ + auto apiKey = ApiKeyEntryDialog::getApiKey(this); + if (apiKey.isEmpty()) { + return; + } + auto apiKeyCopy = SecureBinaryData::fromString(apiKey.toStdString()); + if (walletsMgr_ && signContainer_) { auto handle = validityFlag_.handle(); ConfigDialog::encryptData(walletsMgr_, signContainer_, apiKeyCopy - , [this, handle](ConfigDialog::EncryptError error, const SecureBinaryData &data) { + , [this, handle](ConfigDialog::EncryptError error, const SecureBinaryData& data) { if (!handle.isValid()) { return; } @@ -52,26 +64,36 @@ APISettingsPage::APISettingsPage(QWidget* parent) apiKeyEncrypted_ = data.toHexStr(); updateApiKeyStatus(); }); - }); - connect(ui_->pushButtonApiKeyClear, &QPushButton::clicked, this, [this] { - apiKeyEncrypted_.clear(); - updateApiKeyStatus(); - }); + } + else { + //TODO: implement API key import without walletsMgr_ and signContainer_ + } } -APISettingsPage::~APISettingsPage() = default; - void APISettingsPage::display() { - ui_->toggleAutoSign->setChecked(appSettings_->get(ApplicationSettings::AutoSigning)); - ui_->toggleEnableRFQ->setChecked(appSettings_->get(ApplicationSettings::AutoQouting)); - ui_->lineEditConnName->setText(appSettings_->get(ApplicationSettings::ExtConnName)); - ui_->lineEditConnHost->setText(appSettings_->get(ApplicationSettings::ExtConnHost)); - ui_->lineEditConnPort->setText(appSettings_->get(ApplicationSettings::ExtConnPort)); - ui_->lineEditConnPubKey->setText(appSettings_->get(ApplicationSettings::ExtConnPubKey)); - ui_->labelOwnPubKey->setText(appSettings_->get(ApplicationSettings::ExtConnOwnPubKey)); + if (appSettings_) { + ui_->toggleAutoSign->setChecked(appSettings_->get(ApplicationSettings::AutoSigning)); + ui_->toggleEnableRFQ->setChecked(appSettings_->get(ApplicationSettings::AutoQouting)); + ui_->lineEditConnName->setText(appSettings_->get(ApplicationSettings::ExtConnName)); + ui_->lineEditConnHost->setText(appSettings_->get(ApplicationSettings::ExtConnHost)); + ui_->lineEditConnPort->setText(appSettings_->get(ApplicationSettings::ExtConnPort)); + ui_->lineEditConnPubKey->setText(appSettings_->get(ApplicationSettings::ExtConnPubKey)); + ui_->labelOwnPubKey->setText(appSettings_->get(ApplicationSettings::ExtConnOwnPubKey)); + + apiKeyEncrypted_ = appSettings_->get(ApplicationSettings::LoginApiKey); + } + else { + ui_->toggleAutoSign->setChecked(settings_.at(ApplicationSettings::AutoSigning).toBool()); + ui_->toggleEnableRFQ->setChecked(settings_.at(ApplicationSettings::AutoQouting).toBool()); + ui_->lineEditConnName->setText(settings_.at(ApplicationSettings::ExtConnName).toString()); + ui_->lineEditConnHost->setText(settings_.at(ApplicationSettings::ExtConnHost).toString()); + ui_->lineEditConnPort->setText(settings_.at(ApplicationSettings::ExtConnPort).toString()); + ui_->lineEditConnPubKey->setText(settings_.at(ApplicationSettings::ExtConnPubKey).toString()); + ui_->labelOwnPubKey->setText(settings_.at(ApplicationSettings::ExtConnOwnPubKey).toString()); - apiKeyEncrypted_ = appSettings_->get(ApplicationSettings::LoginApiKey); + apiKeyEncrypted_ = settings_.at(ApplicationSettings::LoginApiKey).toString().toStdString(); + } updateApiKeyStatus(); } @@ -82,13 +104,24 @@ void APISettingsPage::reset() void APISettingsPage::apply() { - appSettings_->set(ApplicationSettings::AutoSigning, ui_->toggleAutoSign->isChecked()); - appSettings_->set(ApplicationSettings::AutoQouting, ui_->toggleEnableRFQ->isChecked()); - appSettings_->set(ApplicationSettings::ExtConnName, ui_->lineEditConnName->text()); - appSettings_->set(ApplicationSettings::ExtConnHost, ui_->lineEditConnHost->text()); - appSettings_->set(ApplicationSettings::ExtConnPort, ui_->lineEditConnPort->text()); - appSettings_->set(ApplicationSettings::ExtConnPubKey, ui_->lineEditConnPubKey->text()); - appSettings_->set(ApplicationSettings::LoginApiKey, QString::fromStdString(apiKeyEncrypted_)); + if (appSettings_) { + appSettings_->set(ApplicationSettings::AutoSigning, ui_->toggleAutoSign->isChecked()); + appSettings_->set(ApplicationSettings::AutoQouting, ui_->toggleEnableRFQ->isChecked()); + appSettings_->set(ApplicationSettings::ExtConnName, ui_->lineEditConnName->text()); + appSettings_->set(ApplicationSettings::ExtConnHost, ui_->lineEditConnHost->text()); + appSettings_->set(ApplicationSettings::ExtConnPort, ui_->lineEditConnPort->text()); + appSettings_->set(ApplicationSettings::ExtConnPubKey, ui_->lineEditConnPubKey->text()); + appSettings_->set(ApplicationSettings::LoginApiKey, QString::fromStdString(apiKeyEncrypted_)); + } + else { + emit putSetting(ApplicationSettings::AutoSigning, ui_->toggleAutoSign->isChecked()); + emit putSetting(ApplicationSettings::AutoQouting, ui_->toggleEnableRFQ->isChecked()); + emit putSetting(ApplicationSettings::ExtConnName, ui_->lineEditConnName->text()); + emit putSetting(ApplicationSettings::ExtConnHost, ui_->lineEditConnHost->text()); + emit putSetting(ApplicationSettings::ExtConnPort, ui_->lineEditConnPort->text()); + emit putSetting(ApplicationSettings::ExtConnPubKey, ui_->lineEditConnPubKey->text()); + emit putSetting(ApplicationSettings::LoginApiKey, QString::fromStdString(apiKeyEncrypted_)); + } } void APISettingsPage::updateApiKeyStatus() diff --git a/BlockSettleUILib/Settings/APISettingsPage.h b/BlockSettleUILib/Settings/APISettingsPage.h index 43b422065..1a8988816 100644 --- a/BlockSettleUILib/Settings/APISettingsPage.h +++ b/BlockSettleUILib/Settings/APISettingsPage.h @@ -20,6 +20,7 @@ namespace Ui { } class AutoRFQ; +//TODO: rework to allow incoming API connections class APISettingsPage : public SettingsPage { public: @@ -30,6 +31,9 @@ class APISettingsPage : public SettingsPage void reset() override; void apply() override; +private slots: + void onApiKeyImport(); + private: void updateApiKeyStatus(); diff --git a/BlockSettleUILib/Settings/ArmoryServersViewModel.cpp b/BlockSettleUILib/Settings/ArmoryServersViewModel.cpp index 4b1ed2f0d..dc106ddaf 100644 --- a/BlockSettleUILib/Settings/ArmoryServersViewModel.cpp +++ b/BlockSettleUILib/Settings/ArmoryServersViewModel.cpp @@ -23,6 +23,10 @@ ArmoryServersViewModel::ArmoryServersViewModel(const std::shared_ptr(ArmoryServersViewModel::ColumnsCount); @@ -37,7 +41,7 @@ QVariant ArmoryServersViewModel::data(const QModelIndex &index, int role) const { if (index.row() >= servers_.size()) return QVariant(); ArmoryServer server = servers_.at(index.row()); - int currentServerIndex = serversProvider_->indexOfCurrent(); + const int currentServerIndex = serversProvider_ ? serversProvider_->indexOfCurrent() : currentServerIndex_; QString serverNetType = (server.netType == NetworkType::MainNet ? tr("MainNet") : tr("TestNet")); if (role == Qt::FontRole && index.row() == currentServerIndex) { @@ -110,9 +114,16 @@ void ArmoryServersViewModel::setSingleColumnMode(bool singleColumnMode) singleColumnMode_ = singleColumnMode; } +void ArmoryServersViewModel::onArmoryServers(const QList& servers + , int idxCur, int idxConn) +{ + currentServerIndex_ = idxCur; + beginResetModel(); + servers_ = servers; + endResetModel(); +} + void ArmoryServersViewModel::setHighLightSelectedServer(bool highLightSelectedServer) { highLightSelectedServer_ = highLightSelectedServer; } - - diff --git a/BlockSettleUILib/Settings/ArmoryServersViewModel.h b/BlockSettleUILib/Settings/ArmoryServersViewModel.h index aadca5f3b..c4d27a4b4 100644 --- a/BlockSettleUILib/Settings/ArmoryServersViewModel.h +++ b/BlockSettleUILib/Settings/ArmoryServersViewModel.h @@ -24,8 +24,9 @@ class ArmoryServersViewModel : public QAbstractTableModel { public: - ArmoryServersViewModel(const std::shared_ptr& serversProvider + [[deprecated]] ArmoryServersViewModel(const std::shared_ptr& serversProvider , QObject *parent = nullptr); + ArmoryServersViewModel(QObject* parent = nullptr); ~ArmoryServersViewModel() noexcept = default; ArmoryServersViewModel(const ArmoryServersViewModel&) = delete; @@ -43,6 +44,8 @@ class ArmoryServersViewModel : public QAbstractTableModel void setHighLightSelectedServer(bool highLightSelectedServer); void setSingleColumnMode(bool singleColumnMode); + void onArmoryServers(const QList&, int idxCur, int idxConn); + public slots: void update(); @@ -51,6 +54,7 @@ public slots: QList servers_; bool highLightSelectedServer_ = true; bool singleColumnMode_ = false; + int currentServerIndex_{ 0 }; enum ArmoryServersViewViewColumns : int { diff --git a/BlockSettleUILib/Settings/ArmoryServersWidget.cpp b/BlockSettleUILib/Settings/ArmoryServersWidget.cpp index fd31fddbe..a79c284f7 100644 --- a/BlockSettleUILib/Settings/ArmoryServersWidget.cpp +++ b/BlockSettleUILib/Settings/ArmoryServersWidget.cpp @@ -56,39 +56,10 @@ ArmoryServersWidget::ArmoryServersWidget(const std::shared_ptrtableViewArmory->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this](const QItemSelection &selected, const QItemSelection &deselected){ - // this check will prevent loop selectionChanged -> setupServerFromSelected -> select -> selectionChanged - if (deselected.isEmpty()) { - return; - } - - bool isEmpty = ui_->tableViewArmory->selectionModel()->selectedIndexes().isEmpty(); - ui_->pushButtonDeleteServer->setDisabled(isEmpty); - ui_->pushButtonEditServer->setDisabled(isEmpty); - ui_->pushButtonConnect->setDisabled(isEmpty); - ui_->pushButtonSelectServer->setDisabled(isEmpty); - - if (!isEmpty && selected.indexes().first().row() < ArmoryServersProvider::kDefaultServersCount) { - ui_->pushButtonDeleteServer->setDisabled(true); - ui_->pushButtonEditServer->setDisabled(true); - } - - resetForm(); - - // save to settings right after row highlight - setupServerFromSelected(true); - }); - - connect(ui_->comboBoxNetworkType, QOverload::of(&QComboBox::currentIndexChanged), - [this](int index){ - if (index == 1) { - ui_->spinBoxPort->setValue(appSettings_->GetDefaultArmoryRemotePort(NetworkType::MainNet)); - } - else if (index == 2){ - ui_->spinBoxPort->setValue(appSettings_->GetDefaultArmoryRemotePort(NetworkType::TestNet)); - } - }); + connect(ui_->tableViewArmory->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &ArmoryServersWidget::onSelectionChanged); + connect(ui_->comboBoxNetworkType, QOverload::of(&QComboBox::currentIndexChanged) + , this, &ArmoryServersWidget::onCurIndexChanged); resetForm(); @@ -114,16 +85,103 @@ ArmoryServersWidget::ArmoryServersWidget(const std::shared_ptrpushButtonKeyImport->hide(); } +ArmoryServersWidget::ArmoryServersWidget(QWidget* parent) + : QWidget(parent) + , ui_(new Ui::ArmoryServersWidget) +{ + armoryServersModel_ = new ArmoryServersViewModel(this); + ui_->setupUi(this); + + ui_->pushButtonConnect->setVisible(false); + ui_->spinBoxPort->setValue(kArmoryDefaultMainNetPort); + + ui_->tableViewArmory->setModel(armoryServersModel_); + ui_->tableViewArmory->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + // int defaultSectionSize = ui_->tableViewArmory->horizontalHeader()->defaultSectionSize(); + // ui_->tableViewArmory->horizontalHeader()->resizeSection(0, defaultSectionSize * 2); + // ui_->tableViewArmory->horizontalHeader()->resizeSection(1, defaultSectionSize); + // ui_->tableViewArmory->horizontalHeader()->resizeSection(2, defaultSectionSize); + // ui_->tableViewArmory->horizontalHeader()->resizeSection(3, defaultSectionSize); + ui_->tableViewArmory->horizontalHeader()->setStretchLastSection(true); + + isStartupDialog_ = false; + + connect(ui_->pushButtonAddServer, &QPushButton::clicked, this, &ArmoryServersWidget::onAddServer); + connect(ui_->pushButtonDeleteServer, &QPushButton::clicked, this, &ArmoryServersWidget::onDeleteServer); + connect(ui_->pushButtonEditServer, &QPushButton::clicked, this, &ArmoryServersWidget::onEdit); + connect(ui_->pushButtonSelectServer, &QPushButton::clicked, this, &ArmoryServersWidget::onSelect); + connect(ui_->pushButtonConnect, &QPushButton::clicked, this, &ArmoryServersWidget::onConnect); + connect(ui_->pushButtonCancelSaveServer, &QPushButton::clicked, this, &ArmoryServersWidget::resetForm); + connect(ui_->pushButtonSaveServer, &QPushButton::clicked, this, &ArmoryServersWidget::onSave); + connect(ui_->lineEditAddress, &QLineEdit::textChanged, this, &ArmoryServersWidget::onFormChanged); + + QRegExp rx(kRxAddress); + ui_->lineEditAddress->setValidator(new QRegExpValidator(rx, this)); + onFormChanged(); + + connect(ui_->pushButtonClose, &QPushButton::clicked, this, [this]() { + emit needClose(); + }); + + connect(ui_->tableViewArmory->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &ArmoryServersWidget::onSelectionChanged); + connect(ui_->comboBoxNetworkType, QOverload::of(&QComboBox::currentIndexChanged) + , this, &ArmoryServersWidget::onCurIndexChanged); + + resetForm(); + + connect(ui_->lineEditName, &QLineEdit::textEdited, this, &ArmoryServersWidget::updateSaveButton); + connect(ui_->lineEditAddress, &QLineEdit::textEdited, this, &ArmoryServersWidget::updateSaveButton); + connect(ui_->comboBoxNetworkType, &QComboBox::currentTextChanged, this, &ArmoryServersWidget::updateSaveButton); + connect(ui_->spinBoxPort, QOverload::of(&QSpinBox::valueChanged), this, &ArmoryServersWidget::updateSaveButton); + + updateSaveButton(); + + // TODO: remove select server button if it's not required anymore + ui_->pushButtonSelectServer->hide(); + ui_->pushButtonKeyImport->hide(); +} + +void ArmoryServersWidget::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + // this check will prevent loop selectionChanged -> setupServerFromSelected -> select -> selectionChanged + if (deselected.isEmpty()) { + return; + } + + bool isEmpty = ui_->tableViewArmory->selectionModel()->selectedIndexes().isEmpty(); + ui_->pushButtonDeleteServer->setDisabled(isEmpty); + ui_->pushButtonEditServer->setDisabled(isEmpty); + ui_->pushButtonConnect->setDisabled(isEmpty); + ui_->pushButtonSelectServer->setDisabled(isEmpty); + + if (!isEmpty && selected.indexes().first().row() < ArmoryServersProvider::kDefaultServersCount) { + ui_->pushButtonDeleteServer->setDisabled(true); + ui_->pushButtonEditServer->setDisabled(true); + } + + resetForm(); + + // save to settings right after row highlight + setupServerFromSelected(true); +} + void ArmoryServersWidget::setRowSelected(int row) { QModelIndex currentIndex; - if (armoryServersProvider_->servers().size() >= 0) { + if (armoryServersProvider_ && (armoryServersProvider_->servers().size() >= 0)) { int indexOfCurrent = row; if (indexOfCurrent < 0 || indexOfCurrent >= armoryServersProvider_->servers().size()) { indexOfCurrent = 0; } currentIndex = armoryServersModel_->index(indexOfCurrent, 0); } + else { + if (row >= armoryServersModel_->rowCount()) { + row = 0; + } + currentIndex = armoryServersModel_->index(row, 0); + } ui_->tableViewArmory->selectionModel()->select(currentIndex , QItemSelectionModel::Select | QItemSelectionModel::Rows); } @@ -146,11 +204,17 @@ void ArmoryServersWidget::onAddServer() server.armoryDBPort = ui_->spinBoxPort->value(); server.armoryDBKey = ui_->lineEditKey->text(); - bool ok = armoryServersProvider_->add(server); - if (ok) { + if (armoryServersProvider_) { + bool ok = armoryServersProvider_->add(server); + if (ok) { + resetForm(); + setRowSelected(armoryServersProvider_->servers().size() - 1); + setupServerFromSelected(true); + } + } + else { + emit addServer(server); resetForm(); - setRowSelected(armoryServersProvider_->servers().size() - 1); - setupServerFromSelected(true); } } @@ -165,9 +229,13 @@ void ArmoryServersWidget::onDeleteServer() if (selectedRow < ArmoryServersProvider::kDefaultServersCount) { return; } - armoryServersProvider_->remove(selectedRow); - setRowSelected(0); - setupServerFromSelected(true); + if (armoryServersProvider_) { + armoryServersProvider_->remove(selectedRow); + setupServerFromSelected(true); + } + else { + emit delServer(selectedRow); + } } void ArmoryServersWidget::onEdit() @@ -177,11 +245,20 @@ void ArmoryServersWidget::onEdit() } int index = ui_->tableViewArmory->selectionModel()->selectedIndexes().first().row(); - if (index >= armoryServersProvider_->servers().size()) { - return; + ArmoryServer server; + if (armoryServersProvider_) { + if (index >= armoryServersProvider_->servers().size()) { + return; + } + server = armoryServersProvider_->servers().at(index); + } + else { + if (index >= servers_.size()) { + return; + } + server = servers_.at(index); //FIXME: use model instead to retrieve server data } - ArmoryServer server = armoryServersProvider_->servers().at(index); ui_->stackedWidgetAddSave->setCurrentWidget(ui_->pageSaveServerButton); ui_->lineEditName->setText(server.name); @@ -208,7 +285,7 @@ void ArmoryServersWidget::onSave() } int index = ui_->tableViewArmory->selectionModel()->selectedIndexes().first().row(); - if (index >= armoryServersProvider_->servers().size()) { + if (armoryServersProvider_ && (index >= armoryServersProvider_->servers().size())) { return; } @@ -219,10 +296,16 @@ void ArmoryServersWidget::onSave() server.armoryDBPort = ui_->spinBoxPort->value(); server.armoryDBKey = ui_->lineEditKey->text(); - bool ok = armoryServersProvider_->replace(index, server); - if (ok) { + if (armoryServersProvider_) { + bool ok = armoryServersProvider_->replace(index, server); + if (ok) { + resetForm(); + setRowSelected(armoryServersProvider_->indexOfCurrent()); + } + } + else { + emit updServer(index, server); resetForm(); - setRowSelected(armoryServersProvider_->indexOfCurrent()); } } @@ -251,14 +334,17 @@ void ArmoryServersWidget::setupServerFromSelected(bool needUpdate) if (ui_->tableViewArmory->selectionModel()->selectedIndexes().isEmpty()) { return; } - int index = ui_->tableViewArmory->selectionModel()->selectedIndexes().first().row(); - if (index >= armoryServersProvider_->servers().size()) { + if (armoryServersProvider_ && (index >= armoryServersProvider_->servers().size())) { return; } - - armoryServersProvider_->setupServer(index, needUpdate); - setRowSelected(armoryServersProvider_->indexOfCurrent()); + if (armoryServersProvider_) { + armoryServersProvider_->setupServer(index, needUpdate); + setRowSelected(armoryServersProvider_->indexOfCurrent()); + } + else { + emit setServer(index); + } } void ArmoryServersWidget::resetForm() @@ -269,7 +355,7 @@ void ArmoryServersWidget::resetForm() ui_->comboBoxNetworkType->setCurrentIndex(0); ui_->lineEditAddress->clear(); ui_->spinBoxPort->setValue(0); - ui_->spinBoxPort->setSpecialValueText(tr(" ")); + ui_->spinBoxPort->setSpecialValueText(QLatin1String(" ")); ui_->lineEditKey->clear(); } @@ -289,9 +375,24 @@ bool ArmoryServersWidget::isExpanded() const return isExpanded_; } +void ArmoryServersWidget::onArmoryServers(const QList& servers + , int idxCur, int idxConn) +{ + servers_ = servers; + if (idxCur < ArmoryServersProvider::kDefaultServersCount) { + ui_->pushButtonDeleteServer->setDisabled(true); + ui_->pushButtonEditServer->setDisabled(true); + } + + if (armoryServersModel_) { + armoryServersModel_->onArmoryServers(servers, idxCur, idxConn); + } + setRowSelected(idxCur); +} + void ArmoryServersWidget::onFormChanged() { - bool acceptable = ui_->lineEditAddress->hasAcceptableInput(); + const bool acceptable = ui_->lineEditAddress->hasAcceptableInput(); bool exists = false; bool valid = false; if (acceptable) { @@ -302,10 +403,33 @@ void ArmoryServersWidget::onFormChanged() armoryHost.armoryDBKey = ui_->lineEditKey->text(); valid = armoryHost.isValid(); if (valid) { - exists = armoryServersProvider_->indexOf(armoryHost.name) != -1 + if (armoryServersProvider_) { + exists = armoryServersProvider_->indexOf(armoryHost.name) != -1 || armoryServersProvider_->indexOf(armoryHost) != -1; + } + else { + for (int i = 0; i < servers_.size(); ++i) { + if ((armoryHost.name == servers_.at(i).name) + || ((armoryHost.armoryDBIp == servers_.at(i).armoryDBIp) && + (armoryHost.armoryDBPort == servers_.at(i).armoryDBPort))) { + exists = true; + break; + } + } + } } } ui_->pushButtonAddServer->setEnabled(valid && acceptable && !exists); ui_->pushButtonSaveServer->setEnabled(valid && acceptable); } + +void ArmoryServersWidget::onCurIndexChanged(int index) +{ + if (appSettings_) { + if (index == 1) { + ui_->spinBoxPort->setValue(appSettings_->GetDefaultArmoryRemotePort(NetworkType::MainNet)); + } else if (index == 2) { + ui_->spinBoxPort->setValue(appSettings_->GetDefaultArmoryRemotePort(NetworkType::TestNet)); + } + } +} diff --git a/BlockSettleUILib/Settings/ArmoryServersWidget.h b/BlockSettleUILib/Settings/ArmoryServersWidget.h index 2ae5741f5..f37071b4a 100644 --- a/BlockSettleUILib/Settings/ArmoryServersWidget.h +++ b/BlockSettleUILib/Settings/ArmoryServersWidget.h @@ -11,6 +11,7 @@ #ifndef ARMORYSERVERSWIDGET_H #define ARMORYSERVERSWIDGET_H +#include #include #include @@ -26,15 +27,18 @@ class ArmoryServersWidget : public QWidget Q_OBJECT public: - explicit ArmoryServersWidget(const std::shared_ptr& armoryServersProvider + [[deprecated]] explicit ArmoryServersWidget(const std::shared_ptr& armoryServersProvider , const std::shared_ptr &appSettings , QWidget *parent = nullptr); + explicit ArmoryServersWidget(QWidget* parent = nullptr); ~ArmoryServersWidget(); void setRowSelected(int row); bool isExpanded() const; + void onArmoryServers(const QList&, int idxCur, int idxConn); + public slots: void onAddServer(); void onDeleteServer(); @@ -46,9 +50,17 @@ public slots: void onExpandToggled(); void onFormChanged(); +private slots: + void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void onCurIndexChanged(int index); + signals: void reconnectArmory(); void needClose(); + void setServer(int); + void addServer(const ArmoryServer&); + void delServer(int); + void updServer(int, const ArmoryServer&); private: void setupServerFromSelected(bool needUpdate); @@ -62,7 +74,8 @@ private slots: std::shared_ptr armoryServersProvider_; std::shared_ptr appSettings_; - ArmoryServersViewModel *armoryServersModel_; + ArmoryServersViewModel* armoryServersModel_{ nullptr }; + QList servers_; bool isStartupDialog_ = false; bool isExpanded_ = true; }; diff --git a/BlockSettleUILib/Settings/ConfigDialog.cpp b/BlockSettleUILib/Settings/ConfigDialog.cpp index 01a540cfc..01b546281 100644 --- a/BlockSettleUILib/Settings/ConfigDialog.cpp +++ b/BlockSettleUILib/Settings/ConfigDialog.cpp @@ -27,14 +27,14 @@ SettingsPage::SettingsPage(QWidget *parent) : QWidget(parent) -{ -} +{} void SettingsPage::init(const std::shared_ptr &appSettings , const std::shared_ptr &armoryServersProvider , const std::shared_ptr &signersProvider , const std::shared_ptr &signContainer - , const std::shared_ptr &walletsMgr) { + , const std::shared_ptr &walletsMgr) +{ appSettings_ = appSettings; armoryServersProvider_ = armoryServersProvider; signersProvider_ = signersProvider; @@ -44,6 +44,18 @@ void SettingsPage::init(const std::shared_ptr &appSettings display(); } +void SettingsPage::init(const ApplicationSettings::State& state) +{ + settings_ = state; + initSettings(); + display(); +} + +void SettingsPage::onSetting(int setting, const QVariant& value) +{ + settings_[static_cast(setting)] = value; +} + ConfigDialog::ConfigDialog(const std::shared_ptr& appSettings , const std::shared_ptr &armoryServersProvider @@ -71,7 +83,7 @@ ConfigDialog::ConfigDialog(const std::shared_ptr& appSettin for (const auto &page : pages_) { page->init(appSettings_, armoryServersProvider_, signersProvider_, signContainer_, walletsMgr); - connect(page, &SettingsPage::illformedSettings, this, &ConfigDialog::illformedSettings); + connect(page, &SettingsPage::illformedSettings, this, &ConfigDialog::onIllformedSettings); } ui_->listWidget->setCurrentRow(0); @@ -113,8 +125,66 @@ ConfigDialog::ConfigDialog(const std::shared_ptr& appSettin }); } +ConfigDialog::ConfigDialog(QWidget* parent) + : QDialog(parent) + , ui_(new Ui::ConfigDialog) +{ + ui_->setupUi(this); + + pages_ = { ui_->pageGeneral, ui_->pageNetwork, ui_->pageSigner, ui_->pageDealing + , ui_->pageAPI }; + + for (const auto& page : pages_) { + connect(page, &SettingsPage::illformedSettings, this, &ConfigDialog::onIllformedSettings); + connect(page, &SettingsPage::putSetting, this, &ConfigDialog::putSetting); + connect(page, &SettingsPage::resetSettings, this, &ConfigDialog::resetSettings); + } + connect(ui_->pageNetwork, &NetworkSettingsPage::reconnectArmory, this, &ConfigDialog::reconnectArmory); + connect(ui_->pageNetwork, &NetworkSettingsPage::setArmoryServer, this, &ConfigDialog::setArmoryServer); + connect(ui_->pageNetwork, &NetworkSettingsPage::addArmoryServer, this, &ConfigDialog::addArmoryServer); + connect(ui_->pageNetwork, &NetworkSettingsPage::delArmoryServer, this, &ConfigDialog::delArmoryServer); + connect(ui_->pageNetwork, &NetworkSettingsPage::updArmoryServer, this, &ConfigDialog::updArmoryServer); + connect(ui_->pageSigner, &SignerSettingsPage::setSigner, this, &ConfigDialog::setSigner); + + ui_->listWidget->setCurrentRow(0); + ui_->stackedWidget->setCurrentIndex(0); + + connect(ui_->listWidget, &QListWidget::currentRowChanged, this, &ConfigDialog::onSelectionChanged); + connect(ui_->pushButtonSetDefault, &QPushButton::clicked, this, &ConfigDialog::onDisplayDefault); + connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &ConfigDialog::reject); + connect(ui_->pushButtonOk, &QPushButton::clicked, this, &ConfigDialog::onAcceptSettings); +} + ConfigDialog::~ConfigDialog() = default; +void ConfigDialog::onSettingsState(const ApplicationSettings::State& state) +{ + if (prevState_.empty()) { + prevState_ = state; + } + for (const auto& page : pages_) { + page->init(state); + } +} + +void ConfigDialog::onSetting(int setting, const QVariant& value) +{ + for (const auto& page : pages_) { + page->onSetting(setting, value); + } +} + +void ConfigDialog::onArmoryServers(const QList& servers, int idxCur, int idxConn) +{ + ui_->pageNetwork->onArmoryServers(servers, idxCur, idxConn); +} + +void ConfigDialog::onSignerSettings(const QList& signers + , const std::string& ownKey, int idxCur) +{ + ui_->pageSigner->onSignerSettings(signers, ownKey, idxCur); +} + void ConfigDialog::popupNetworkSettings() { ui_->stackedWidget->setCurrentWidget(ui_->pageNetwork); @@ -201,8 +271,9 @@ void ConfigDialog::onAcceptSettings() for (const auto &page : pages_) { page->apply(); } - - appSettings_->SaveSettings(); + if (appSettings_) { + appSettings_->SaveSettings(); + } accept(); } @@ -211,13 +282,18 @@ void ConfigDialog::onSelectionChanged(int currentRow) ui_->stackedWidget->setCurrentIndex(currentRow); } -void ConfigDialog::illformedSettings(bool illformed) +void ConfigDialog::onIllformedSettings(bool illformed) { ui_->pushButtonOk->setEnabled(!illformed); } void ConfigDialog::reject() { - appSettings_->setState(prevState_); + if (appSettings_) { + appSettings_->setState(prevState_); + } + else { + emit resetSettingsToState(prevState_); + } QDialog::reject(); } diff --git a/BlockSettleUILib/Settings/ConfigDialog.h b/BlockSettleUILib/Settings/ConfigDialog.h index 32e2c1a7b..172ec46fb 100644 --- a/BlockSettleUILib/Settings/ConfigDialog.h +++ b/BlockSettleUILib/Settings/ConfigDialog.h @@ -15,6 +15,7 @@ #include #include "ApplicationSettings.h" #include "ArmoryServersProvider.h" +#include "Settings/SignersProvider.h" #include "SignContainer.h" class ArmoryServersProvider; @@ -31,11 +32,14 @@ class SettingsPage : public QWidget public: SettingsPage(QWidget *parent); - virtual void init(const std::shared_ptr &appSettings + [[deprecated]] virtual void init(const std::shared_ptr &appSettings , const std::shared_ptr &armoryServersProvider , const std::shared_ptr &signersProvider , const std::shared_ptr &signContainer , const std::shared_ptr &walletsMgr); + virtual void init(const ApplicationSettings::State &); + + virtual void onSetting(int setting, const QVariant& value); public slots: virtual void initSettings() {} @@ -44,9 +48,12 @@ public slots: virtual void apply() = 0; signals: + void putSetting(ApplicationSettings::Setting, const QVariant&); + void resetSettings(const std::vector&); void illformedSettings(bool illformed); protected: + ApplicationSettings::State settings_; std::shared_ptr appSettings_; std::shared_ptr armoryServersProvider_; std::shared_ptr signersProvider_; @@ -60,16 +67,22 @@ class ConfigDialog : public QDialog Q_OBJECT public: - ConfigDialog(const std::shared_ptr& appSettings + [[deprecated]] ConfigDialog(const std::shared_ptr& appSettings , const std::shared_ptr &armoryServersProvider , const std::shared_ptr &signersProvider , const std::shared_ptr &signContainer , const std::shared_ptr &walletsMgr , QWidget* parent = nullptr); + ConfigDialog(QWidget* parent = nullptr); ~ConfigDialog() override; void popupNetworkSettings(); + void onSettingsState(const ApplicationSettings::State &); + void onSetting(int setting, const QVariant& value); + void onArmoryServers(const QList&, int idxCur, int idxConn); + void onSignerSettings(const QList&, const std::string& ownKey, int idxCur); + enum class EncryptError { NoError, @@ -91,10 +104,18 @@ private slots: void onDisplayDefault(); void onAcceptSettings(); void onSelectionChanged(int currentRow); - void illformedSettings(bool illformed); + void onIllformedSettings(bool illformed); signals: void reconnectArmory(); + void putSetting(ApplicationSettings::Setting, const QVariant&); + void resetSettings(const std::vector&); + void resetSettingsToState(const ApplicationSettings::State &); + void setArmoryServer(int); + void addArmoryServer(const ArmoryServer&); + void delArmoryServer(int); + void updArmoryServer(int, const ArmoryServer&); + void setSigner(int); private: static void getChatPrivKey(const std::shared_ptr &walletsMgr diff --git a/BlockSettleUILib/Settings/DealingSettingsPage.cpp b/BlockSettleUILib/Settings/DealingSettingsPage.cpp index 2839d3a2a..4df06aacf 100644 --- a/BlockSettleUILib/Settings/DealingSettingsPage.cpp +++ b/BlockSettleUILib/Settings/DealingSettingsPage.cpp @@ -106,45 +106,92 @@ static inline int priceUpdateTimeout(int index) } } +void DealingSettingsPage::init(const ApplicationSettings::State& state) +{ + if (state.find(ApplicationSettings::dropQN) == state.end()) { + return; // not our snapshot + } + SettingsPage::init(state); +} + void DealingSettingsPage::display() { - ui_->checkBoxDrop->setChecked(appSettings_->get(ApplicationSettings::dropQN)); - ui_->showQuoted->setChecked(appSettings_->get(ApplicationSettings::ShowQuoted)); - ui_->fx->setCurrentIndex(limitIndex(appSettings_->get(ApplicationSettings::FxRfqLimit))); - ui_->xbt->setCurrentIndex(limitIndex(appSettings_->get(ApplicationSettings::XbtRfqLimit))); - ui_->pm->setCurrentIndex(limitIndex(appSettings_->get(ApplicationSettings::PmRfqLimit))); - ui_->disableBlueDot->setChecked(appSettings_->get( - ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter)); - ui_->priceUpdateTimeout->setCurrentIndex(priceUpdateIndex(appSettings_->get( - ApplicationSettings::PriceUpdateInterval))); + if (appSettings_) { + ui_->checkBoxDrop->setChecked(appSettings_->get(ApplicationSettings::dropQN)); + ui_->showQuoted->setChecked(appSettings_->get(ApplicationSettings::ShowQuoted)); + ui_->fx->setCurrentIndex(limitIndex(appSettings_->get(ApplicationSettings::FxRfqLimit))); + ui_->xbt->setCurrentIndex(limitIndex(appSettings_->get(ApplicationSettings::XbtRfqLimit))); + ui_->pm->setCurrentIndex(limitIndex(appSettings_->get(ApplicationSettings::PmRfqLimit))); + ui_->disableBlueDot->setChecked(appSettings_->get( + ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter)); + ui_->priceUpdateTimeout->setCurrentIndex(priceUpdateIndex(appSettings_->get( + ApplicationSettings::PriceUpdateInterval))); + } + else { + ui_->checkBoxDrop->setChecked(settings_.at(ApplicationSettings::dropQN).toBool()); + ui_->showQuoted->setChecked(settings_.at(ApplicationSettings::ShowQuoted).toBool()); + ui_->fx->setCurrentIndex(limitIndex(settings_.at(ApplicationSettings::FxRfqLimit).toInt())); + ui_->xbt->setCurrentIndex(limitIndex(settings_.at(ApplicationSettings::XbtRfqLimit).toInt())); + ui_->pm->setCurrentIndex(limitIndex(settings_.at(ApplicationSettings::PmRfqLimit).toInt())); + ui_->disableBlueDot->setChecked(settings_.at(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter).toBool()); + ui_->priceUpdateTimeout->setCurrentIndex(priceUpdateIndex(settings_.at( + ApplicationSettings::PriceUpdateInterval).toInt())); + } } void DealingSettingsPage::reset() { - for (const auto &setting : {ApplicationSettings::dropQN, ApplicationSettings::ShowQuoted + const std::vector resetList{ + ApplicationSettings::dropQN, ApplicationSettings::ShowQuoted , ApplicationSettings::FxRfqLimit, ApplicationSettings::XbtRfqLimit - , ApplicationSettings::PmRfqLimit, ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter - , ApplicationSettings::PriceUpdateInterval}) { - appSettings_->reset(setting, false); + , ApplicationSettings::PmRfqLimit + , ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter + , ApplicationSettings::PriceUpdateInterval + }; + if (appSettings_) { + for (const auto& setting : resetList) { + appSettings_->reset(setting, false); + } + display(); + } + else { + emit resetSettings(resetList); } - display(); } void DealingSettingsPage::apply() { - appSettings_->set(ApplicationSettings::dropQN, ui_->checkBoxDrop->isChecked()); - appSettings_->set(ApplicationSettings::ShowQuoted, ui_->showQuoted->isChecked()); - appSettings_->set(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter, - ui_->disableBlueDot->isChecked()); - appSettings_->set(ApplicationSettings::FxRfqLimit, limit(ui_->fx->currentIndex())); - appSettings_->set(ApplicationSettings::XbtRfqLimit, limit(ui_->xbt->currentIndex())); - appSettings_->set(ApplicationSettings::PmRfqLimit, limit(ui_->pm->currentIndex())); - appSettings_->set(ApplicationSettings::PriceUpdateInterval, priceUpdateTimeout( - ui_->priceUpdateTimeout->currentIndex())); + if (appSettings_) { + appSettings_->set(ApplicationSettings::dropQN, ui_->checkBoxDrop->isChecked()); + appSettings_->set(ApplicationSettings::ShowQuoted, ui_->showQuoted->isChecked()); + appSettings_->set(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter, + ui_->disableBlueDot->isChecked()); + appSettings_->set(ApplicationSettings::FxRfqLimit, limit(ui_->fx->currentIndex())); + appSettings_->set(ApplicationSettings::XbtRfqLimit, limit(ui_->xbt->currentIndex())); + appSettings_->set(ApplicationSettings::PmRfqLimit, limit(ui_->pm->currentIndex())); + appSettings_->set(ApplicationSettings::PriceUpdateInterval, priceUpdateTimeout( + ui_->priceUpdateTimeout->currentIndex())); + } + else { + emit putSetting(ApplicationSettings::dropQN, ui_->checkBoxDrop->isChecked()); + emit putSetting(ApplicationSettings::ShowQuoted, ui_->showQuoted->isChecked()); + emit putSetting(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter, + ui_->disableBlueDot->isChecked()); + emit putSetting(ApplicationSettings::FxRfqLimit, limit(ui_->fx->currentIndex())); + emit putSetting(ApplicationSettings::XbtRfqLimit, limit(ui_->xbt->currentIndex())); + emit putSetting(ApplicationSettings::PmRfqLimit, limit(ui_->pm->currentIndex())); + emit putSetting(ApplicationSettings::PriceUpdateInterval, priceUpdateTimeout( + ui_->priceUpdateTimeout->currentIndex())); + } } void DealingSettingsPage::onResetCounters() { - appSettings_->reset(ApplicationSettings::Filter_MD_QN_cnt); + if (appSettings_) { + appSettings_->reset(ApplicationSettings::Filter_MD_QN_cnt); + } + else { + emit resetSettings({ ApplicationSettings::Filter_MD_QN_cnt }); + } ui_->pushButtonResetCnt->setEnabled(false); } diff --git a/BlockSettleUILib/Settings/DealingSettingsPage.h b/BlockSettleUILib/Settings/DealingSettingsPage.h index 088688d7f..228a023b8 100644 --- a/BlockSettleUILib/Settings/DealingSettingsPage.h +++ b/BlockSettleUILib/Settings/DealingSettingsPage.h @@ -28,6 +28,7 @@ class DealingSettingsPage : public SettingsPage DealingSettingsPage(QWidget* parent = nullptr); ~DealingSettingsPage() override; + void init(const ApplicationSettings::State&) override; void display() override; void reset() override; void apply() override; diff --git a/BlockSettleUILib/Settings/GeneralSettingsPage.cpp b/BlockSettleUILib/Settings/GeneralSettingsPage.cpp index ebfce00be..448c65bb7 100644 --- a/BlockSettleUILib/Settings/GeneralSettingsPage.cpp +++ b/BlockSettleUILib/Settings/GeneralSettingsPage.cpp @@ -49,105 +49,185 @@ GeneralSettingsPage::GeneralSettingsPage(QWidget* parent) GeneralSettingsPage::~GeneralSettingsPage() = default; +void GeneralSettingsPage::init(const ApplicationSettings::State& state) +{ + if (state.find(ApplicationSettings::launchToTray) == state.end()) { + return; // not our snapshot + } + SettingsPage::init(state); +} + void GeneralSettingsPage::display() { - ui_->checkBoxLaunchToTray->setChecked(appSettings_->get(ApplicationSettings::launchToTray)); - ui_->checkBoxMinimizeToTray->setChecked(appSettings_->get(ApplicationSettings::minimizeToTray)); - ui_->checkBoxCloseToTray->setChecked(appSettings_->get(ApplicationSettings::closeToTray)); - ui_->checkBoxShowTxNotification->setChecked(appSettings_->get(ApplicationSettings::notifyOnTX)); - ui_->addvancedDialogByDefaultCheckBox->setChecked(appSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)); - ui_->subscribeToMdOnStartCheckBox->setChecked(appSettings_->get(ApplicationSettings::SubscribeToMDOnStart)); - ui_->detailedSettlementTxDialogByDefaultCheckBox->setChecked( - appSettings_->get(ApplicationSettings::DetailedSettlementTxDialogByDefault)); - - - // DetailedSettlementTxDialogByDefault - - const auto cfg = appSettings_->GetLogsConfig(); - ui_->logFileName->setText(QString::fromStdString(cfg.at(0).fileName)); - ui_->logMsgFileName->setText(QString::fromStdString(cfg.at(1).fileName)); - ui_->logLevel->setCurrentIndex(static_cast(cfg.at(0).level)); - ui_->logLevelMsg->setCurrentIndex(static_cast(cfg.at(1).level)); + if (appSettings_) { + ui_->checkBoxLaunchToTray->setChecked(appSettings_->get(ApplicationSettings::launchToTray)); + ui_->checkBoxMinimizeToTray->setChecked(appSettings_->get(ApplicationSettings::minimizeToTray)); + ui_->checkBoxCloseToTray->setChecked(appSettings_->get(ApplicationSettings::closeToTray)); + ui_->checkBoxShowTxNotification->setChecked(appSettings_->get(ApplicationSettings::notifyOnTX)); + ui_->addvancedDialogByDefaultCheckBox->setChecked(appSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)); + ui_->subscribeToMdOnStartCheckBox->setChecked(appSettings_->get(ApplicationSettings::SubscribeToMDOnStart)); + ui_->detailedSettlementTxDialogByDefaultCheckBox->setChecked( + appSettings_->get(ApplicationSettings::DetailedSettlementTxDialogByDefault)); + + // DetailedSettlementTxDialogByDefault + const auto cfg = appSettings_->GetLogsConfig(); + ui_->logFileName->setText(QString::fromStdString(cfg.at(0).fileName)); + ui_->logMsgFileName->setText(QString::fromStdString(cfg.at(1).fileName)); + ui_->logLevel->setCurrentIndex(static_cast(cfg.at(0).level)); + ui_->logLevelMsg->setCurrentIndex(static_cast(cfg.at(1).level)); + } + else { + ui_->checkBoxLaunchToTray->setChecked(settings_.at(ApplicationSettings::launchToTray).toBool()); + ui_->checkBoxMinimizeToTray->setChecked(settings_.at(ApplicationSettings::minimizeToTray).toBool()); + ui_->checkBoxCloseToTray->setChecked(settings_.at(ApplicationSettings::closeToTray).toBool()); + ui_->checkBoxShowTxNotification->setChecked(settings_.at(ApplicationSettings::notifyOnTX).toBool()); + ui_->addvancedDialogByDefaultCheckBox->setChecked(settings_.at(ApplicationSettings::AdvancedTxDialogByDefault).toBool()); + ui_->subscribeToMdOnStartCheckBox->setChecked(settings_.at(ApplicationSettings::SubscribeToMDOnStart).toBool()); + ui_->detailedSettlementTxDialogByDefaultCheckBox->setChecked( + settings_.at(ApplicationSettings::DetailedSettlementTxDialogByDefault).toBool()); + + const auto cfgLog = ApplicationSettings::parseLogConfig(settings_.at( + ApplicationSettings::logDefault).toStringList()); + const auto cfgMessages = ApplicationSettings::parseLogConfig(settings_.at( + ApplicationSettings::logMessages).toStringList()); + ui_->logFileName->setText(QString::fromStdString(cfgLog.fileName)); + ui_->logMsgFileName->setText(QString::fromStdString(cfgMessages.fileName)); + ui_->logLevel->setCurrentIndex(static_cast(cfgLog.level)); + ui_->logLevelMsg->setCurrentIndex(static_cast(cfgMessages.level)); + } ui_->warnLabel->hide(); } void GeneralSettingsPage::reset() { - for (const auto &setting : {ApplicationSettings::launchToTray, ApplicationSettings::minimizeToTray + const std::vector resetList = { + ApplicationSettings::launchToTray, ApplicationSettings::minimizeToTray , ApplicationSettings::closeToTray, ApplicationSettings::notifyOnTX - , ApplicationSettings::AdvancedTxDialogByDefault, ApplicationSettings::SubscribeToMDOnStart - , ApplicationSettings::logDefault, ApplicationSettings::logMessages}) { - appSettings_->reset(setting, false); + , ApplicationSettings::AdvancedTxDialogByDefault + , ApplicationSettings::SubscribeToMDOnStart + , ApplicationSettings::logDefault, ApplicationSettings::logMessages + }; + if (appSettings_) { + for (const auto& setting : resetList) { + appSettings_->reset(setting, false); + } + display(); + } + else { + emit resetSettings(resetList); } - display(); } static inline QString logLevel(int level) { switch(level) { - case 0 : return QLatin1String("trace"); - case 1 : return QLatin1String("debug"); - case 2 : return QLatin1String("info"); - case 3 : return QLatin1String("warn"); - case 4 : return QLatin1String("error"); - case 5 : return QLatin1String("crit"); - default : return QString(); + case 0 : return QObject::tr("trace"); + case 1 : return QObject::tr("debug"); + case 2 : return QObject::tr("info"); + case 3 : return QObject::tr("warn"); + case 4 : return QObject::tr("error"); + case 5 : return QObject::tr("crit"); + default : return QString(); } } void GeneralSettingsPage::apply() { - appSettings_->set(ApplicationSettings::launchToTray, ui_->checkBoxLaunchToTray->isChecked()); - appSettings_->set(ApplicationSettings::minimizeToTray, ui_->checkBoxMinimizeToTray->isChecked()); - appSettings_->set(ApplicationSettings::closeToTray, ui_->checkBoxCloseToTray->isChecked()); - appSettings_->set(ApplicationSettings::notifyOnTX, ui_->checkBoxShowTxNotification->isChecked()); - appSettings_->set(ApplicationSettings::AdvancedTxDialogByDefault, - ui_->addvancedDialogByDefaultCheckBox->isChecked()); - - appSettings_->set(ApplicationSettings::SubscribeToMDOnStart - , ui_->subscribeToMdOnStartCheckBox->isChecked()); - - appSettings_->set(ApplicationSettings::DetailedSettlementTxDialogByDefault - , ui_->detailedSettlementTxDialogByDefaultCheckBox->isChecked()); - - auto cfg = appSettings_->GetLogsConfig(); - - { - QStringList logSettings; - logSettings << ui_->logFileName->text(); - logSettings << QString::fromStdString(cfg.at(0).category); - logSettings << QString::fromStdString(cfg.at(0).pattern); - - if (ui_->logLevel->currentIndex() < static_cast(bs::LogLevel::off)) { - logSettings << logLevel(ui_->logLevel->currentIndex()); - } else { - logSettings << QString(); + if (appSettings_) { + appSettings_->set(ApplicationSettings::launchToTray, ui_->checkBoxLaunchToTray->isChecked()); + appSettings_->set(ApplicationSettings::minimizeToTray, ui_->checkBoxMinimizeToTray->isChecked()); + appSettings_->set(ApplicationSettings::closeToTray, ui_->checkBoxCloseToTray->isChecked()); + appSettings_->set(ApplicationSettings::notifyOnTX, ui_->checkBoxShowTxNotification->isChecked()); + appSettings_->set(ApplicationSettings::AdvancedTxDialogByDefault, + ui_->addvancedDialogByDefaultCheckBox->isChecked()); + + appSettings_->set(ApplicationSettings::SubscribeToMDOnStart + , ui_->subscribeToMdOnStartCheckBox->isChecked()); + + appSettings_->set(ApplicationSettings::DetailedSettlementTxDialogByDefault + , ui_->detailedSettlementTxDialogByDefaultCheckBox->isChecked()); + + auto cfg = appSettings_->GetLogsConfig(); + + { + QStringList logSettings; + logSettings << ui_->logFileName->text(); + logSettings << QString::fromStdString(cfg.at(0).category); + logSettings << QString::fromStdString(cfg.at(0).pattern); + + if (ui_->logLevel->currentIndex() < static_cast(bs::LogLevel::off)) { + logSettings << logLevel(ui_->logLevel->currentIndex()); + } else { + logSettings << QString(); + } + + appSettings_->set(ApplicationSettings::logDefault, logSettings); } - appSettings_->set(ApplicationSettings::logDefault, logSettings); - } + { + QStringList logSettings; + logSettings << ui_->logMsgFileName->text(); + logSettings << QString::fromStdString(cfg.at(1).category); + logSettings << QString::fromStdString(cfg.at(1).pattern); - { - QStringList logSettings; - logSettings << ui_->logMsgFileName->text(); - logSettings << QString::fromStdString(cfg.at(1).category); - logSettings << QString::fromStdString(cfg.at(1).pattern); + if (ui_->logLevelMsg->currentIndex() < static_cast(bs::LogLevel::off)) { + logSettings << logLevel(ui_->logLevelMsg->currentIndex()); + } else { + logSettings << QString(); + } - if (ui_->logLevelMsg->currentIndex() < static_cast(bs::LogLevel::off)) { - logSettings << logLevel(ui_->logLevelMsg->currentIndex()); - } else { - logSettings << QString(); + appSettings_->set(ApplicationSettings::logMessages, logSettings); + } + } + else { // don't update local settings_ yet - the update will arrive explicitly + emit putSetting(ApplicationSettings::launchToTray, ui_->checkBoxLaunchToTray->isChecked()); + emit putSetting(ApplicationSettings::minimizeToTray, ui_->checkBoxMinimizeToTray->isChecked()); + emit putSetting(ApplicationSettings::closeToTray, ui_->checkBoxCloseToTray->isChecked()); + emit putSetting(ApplicationSettings::notifyOnTX, ui_->checkBoxShowTxNotification->isChecked()); + emit putSetting(ApplicationSettings::AdvancedTxDialogByDefault, ui_->addvancedDialogByDefaultCheckBox->isChecked()); + emit putSetting(ApplicationSettings::SubscribeToMDOnStart, ui_->subscribeToMdOnStartCheckBox->isChecked()); + emit putSetting(ApplicationSettings::DetailedSettlementTxDialogByDefault + , ui_->detailedSettlementTxDialogByDefaultCheckBox->isChecked()); + + const auto cfgLog = ApplicationSettings::parseLogConfig(settings_.at( + ApplicationSettings::logDefault).toStringList()); + { + QStringList logSettings; + logSettings << ui_->logFileName->text(); + logSettings << QString::fromStdString(cfgLog.category); + logSettings << QString::fromStdString(cfgLog.pattern); + + if (ui_->logLevel->currentIndex() < static_cast(bs::LogLevel::off)) { + logSettings << logLevel(ui_->logLevel->currentIndex()); + } else { + logSettings << QString(); + } + emit putSetting(ApplicationSettings::logDefault, logSettings); } - appSettings_->set(ApplicationSettings::logMessages, logSettings); + const auto cfgMessages = ApplicationSettings::parseLogConfig(settings_.at( + ApplicationSettings::logMessages).toStringList()); + { + QStringList logSettings; + logSettings << ui_->logMsgFileName->text(); + logSettings << QString::fromStdString(cfgMessages.category); + logSettings << QString::fromStdString(cfgMessages.pattern); + + if (ui_->logLevelMsg->currentIndex() < static_cast(bs::LogLevel::off)) { + logSettings << logLevel(ui_->logLevelMsg->currentIndex()); + } else { + logSettings << QString(); + } + emit putSetting(ApplicationSettings::logMessages, logSettings); + } } } void GeneralSettingsPage::onSelectLogFile() { QString fileName = QFileDialog::getSaveFileName(this, - tr("Select file for General Terminal logs..."), + tr("Select file for General Terminal logs"), QFileInfo(ui_->logFileName->text()).path(), QString(), nullptr, QFileDialog::DontConfirmOverwrite); @@ -159,7 +239,7 @@ void GeneralSettingsPage::onSelectLogFile() void GeneralSettingsPage::onSelectMsgLogFile() { QString fileName = QFileDialog::getSaveFileName(this, - tr("Select file for Matching Engine logs..."), + tr("Select file for Matching Engine logs"), QFileInfo(ui_->logMsgFileName->text()).path(), QString(), nullptr, QFileDialog::DontConfirmOverwrite); @@ -170,49 +250,41 @@ void GeneralSettingsPage::onSelectMsgLogFile() void GeneralSettingsPage::onLogFileNameEdited(const QString &) { - checkSettings(); + checkLogSettings(); } void GeneralSettingsPage::onLogLevelChanged(int) { - checkSettings(); + checkLogSettings(); } -void GeneralSettingsPage::checkSettings() +void GeneralSettingsPage::checkLogSettings() { ui_->warnLabel->show(); if (ui_->groupBoxLogging->isChecked()) { if (ui_->logFileName->text().isEmpty()) { - ui_->warnLabel->setText(tr("Log files must be named.")); - + ui_->warnLabel->setText(tr("Log files must be named")); emit illformedSettings(true); - return; } } if (ui_->groupBoxLoggingMsg->isChecked()) { if (ui_->logMsgFileName->text().isEmpty()) { - ui_->warnLabel->setText(tr("Log files must be named.")); - + ui_->warnLabel->setText(tr("Log files must be named")); emit illformedSettings(true); - return; } } if (ui_->groupBoxLogging->isChecked() && ui_->groupBoxLoggingMsg->isChecked()) { if (ui_->logFileName->text() == ui_->logMsgFileName->text()) { - ui_->warnLabel->setText(tr("Logging requires multiple files.")); - + ui_->warnLabel->setText(tr("Logging requires multiple files")); emit illformedSettings(true); - return; } } - - ui_->warnLabel->setText(tr("Changes will take effect after the application is restarted.")); - + ui_->warnLabel->setText(tr("Changes will take effect after the application is restarted")); emit illformedSettings(false); } diff --git a/BlockSettleUILib/Settings/GeneralSettingsPage.h b/BlockSettleUILib/Settings/GeneralSettingsPage.h index 92baa3a6c..0121e9839 100644 --- a/BlockSettleUILib/Settings/GeneralSettingsPage.h +++ b/BlockSettleUILib/Settings/GeneralSettingsPage.h @@ -28,6 +28,7 @@ class GeneralSettingsPage : public SettingsPage GeneralSettingsPage(QWidget* parent = nullptr); ~GeneralSettingsPage() override; + void init(const ApplicationSettings::State&) override; void display() override; void reset() override; void apply() override; @@ -39,7 +40,7 @@ private slots: void onLogLevelChanged(int); private: - void checkSettings(); + void checkLogSettings(); signals: void requestDataEncryption(); diff --git a/BlockSettleUILib/Settings/NetworkSettingsPage.cpp b/BlockSettleUILib/Settings/NetworkSettingsPage.cpp index 0d7f3a57c..eebc6aa02 100644 --- a/BlockSettleUILib/Settings/NetworkSettingsPage.cpp +++ b/BlockSettleUILib/Settings/NetworkSettingsPage.cpp @@ -53,27 +53,43 @@ NetworkSettingsPage::NetworkSettingsPage(QWidget* parent) // workaround here - wrap widget by QDialog // TODO: fix stylesheet to support popup widgets - QDialog *d = new QDialog(this); - QVBoxLayout *l = new QVBoxLayout(d); - l->setContentsMargins(0,0,0,0); - d->setLayout(l); - d->setWindowTitle(tr("BlockSettleDB connection")); - d->resize(847, 593); + QDialog *dlg = new QDialog(this); + QVBoxLayout *layout = new QVBoxLayout(dlg); + layout->setContentsMargins(0,0,0,0); + dlg->setLayout(layout); + dlg->setWindowTitle(tr("BlockSettleDB connection")); + dlg->resize(847, 593); //FIXME: use custom dialog from resources - ArmoryServersWidget *armoryServersWidget = new ArmoryServersWidget(armoryServersProvider_, appSettings_, this); + armoryServersWidget_ = appSettings_ + ? new ArmoryServersWidget(armoryServersProvider_, appSettings_, this) + : new ArmoryServersWidget(this); // armoryServersWidget->setWindowModality(Qt::ApplicationModal); // armoryServersWidget->setWindowFlags(Qt::Dialog); - l->addWidget(armoryServersWidget); + layout->addWidget(armoryServersWidget_); - connect(armoryServersWidget, &ArmoryServersWidget::reconnectArmory, this, [this](){ + connect(dlg, &QDialog::finished, [this] { + armoryServersWidget_->deleteLater(); + armoryServersWidget_ = nullptr; + }); + connect(armoryServersWidget_, &ArmoryServersWidget::reconnectArmory, this, [this](){ emit reconnectArmory(); }); - connect(armoryServersWidget, &ArmoryServersWidget::needClose, this, [d](){ - d->reject(); + connect(armoryServersWidget_, &ArmoryServersWidget::addServer, this + , &NetworkSettingsPage::addArmoryServer); + connect(armoryServersWidget_, &ArmoryServersWidget::setServer, this + , &NetworkSettingsPage::setArmoryServer); + connect(armoryServersWidget_, &ArmoryServersWidget::delServer, this + , &NetworkSettingsPage::delArmoryServer); + connect(armoryServersWidget_, &ArmoryServersWidget::updServer, this + , &NetworkSettingsPage::updArmoryServer); + connect(armoryServersWidget_, &ArmoryServersWidget::needClose, this, [dlg](){ + dlg->reject(); }); - d->exec(); + armoryServersWidget_->onArmoryServers(armoryServers_, armorySrvCurrent_ + , armorySrvConnected_); + dlg->exec(); emit armoryServerChanged(); // Switch env if needed onArmorySelected(ui_->comboBoxArmoryServer->currentIndex()); @@ -101,14 +117,42 @@ NetworkSettingsPage::NetworkSettingsPage(QWidget* parent) }); } +void NetworkSettingsPage::init(const ApplicationSettings::State& state) +{ + if (state.find(ApplicationSettings::envConfiguration) == state.end()) { + return; // not our snapshot + } + SettingsPage::init(state); +} + +void NetworkSettingsPage::onArmoryServers(const QList& servers + , int idxCur, int idxConn) +{ + armoryServers_ = servers; + armorySrvCurrent_ = idxCur; + armorySrvConnected_ = idxConn; + if (armoryServerModel_) { + armoryServerModel_->onArmoryServers(servers, idxCur, idxConn); + } + if (armoryServersWidget_) { + armoryServersWidget_->onArmoryServers(servers, idxCur, idxConn); + } + displayArmorySettings(); +} + void NetworkSettingsPage::initSettings() { - armoryServerModel_ = new ArmoryServersViewModel(armoryServersProvider_); + if (armoryServersProvider_) { + armoryServerModel_ = new ArmoryServersViewModel(armoryServersProvider_); + connect(armoryServersProvider_.get(), &ArmoryServersProvider::dataChanged, this, &NetworkSettingsPage::displayArmorySettings); + } + else { + armoryServerModel_ = new ArmoryServersViewModel(this); + } armoryServerModel_->setSingleColumnMode(true); armoryServerModel_->setHighLightSelectedServer(false); ui_->comboBoxArmoryServer->setModel(armoryServerModel_); - connect(armoryServersProvider_.get(), &ArmoryServersProvider::dataChanged, this, &NetworkSettingsPage::displayArmorySettings); connect(ui_->comboBoxEnvironment, QOverload::of(&QComboBox::currentIndexChanged), this, &NetworkSettingsPage::onEnvSelected); connect(ui_->comboBoxArmoryServer, QOverload::of(&QComboBox::currentIndexChanged), this, &NetworkSettingsPage::onArmorySelected); } @@ -125,19 +169,32 @@ void NetworkSettingsPage::display() void NetworkSettingsPage::displayArmorySettings() { - // set index of selected server - ArmoryServer selectedServer = armoryServersProvider_->getArmorySettings(); - int selectedServerIndex = armoryServersProvider_->indexOfCurrent(); - - // Prevent NetworkSettingsPage::onArmorySelected call - auto oldBlock = ui_->comboBoxArmoryServer->blockSignals(true); + ArmoryServer selectedServer; + int selectedServerIndex = 0; + ArmoryServer connectedServerSettings; + int connectedServerIndex = 0; + if (armoryServersProvider_) { + // set index of selected server + selectedServer = armoryServersProvider_->getArmorySettings(); + selectedServerIndex = armoryServersProvider_->indexOfCurrent(); + connectedServerSettings = armoryServersProvider_->connectedArmorySettings(); + connectedServerIndex = armoryServersProvider_->indexOfConnected(); + } + else { + selectedServerIndex = armorySrvCurrent_; + if (selectedServerIndex >= armoryServers_.size()) { + return; + } + selectedServer = armoryServers_.at(selectedServerIndex); + connectedServerIndex = armorySrvConnected_; + if (connectedServerIndex >= armoryServers_.size()) { + return; + } + connectedServerSettings = armoryServers_.at(connectedServerIndex); + } ui_->comboBoxArmoryServer->setCurrentIndex(selectedServerIndex); - ui_->comboBoxArmoryServer->blockSignals(oldBlock); // display info of connected server - ArmorySettings connectedServerSettings = armoryServersProvider_->connectedArmorySettings(); - int connectedServerIndex = armoryServersProvider_->indexOfConnected(); - ui_->labelArmoryServerNetwork->setText(connectedServerSettings.netType == NetworkType::MainNet ? tr("MainNet") : tr("TestNet")); ui_->labelArmoryServerAddress->setText(connectedServerSettings.armoryDBIp); ui_->labelArmoryServerPort->setText(QString::number(connectedServerSettings.armoryDBPort)); @@ -155,7 +212,13 @@ void NetworkSettingsPage::displayArmorySettings() void NetworkSettingsPage::displayEnvironmentSettings() { - auto env = appSettings_->get(ApplicationSettings::envConfiguration); + int env = 0; + if (appSettings_) { + env = appSettings_->get(ApplicationSettings::envConfiguration); + } + else { + env = settings_.at(ApplicationSettings::envConfiguration).toInt(); + } ui_->comboBoxEnvironment->setCurrentIndex(env); onEnvSelected(env); } @@ -169,67 +232,80 @@ void NetworkSettingsPage::applyLocalSignerNetOption() void NetworkSettingsPage::reset() { - for (const auto &setting : { - ApplicationSettings::runArmoryLocally, - ApplicationSettings::netType, - ApplicationSettings::envConfiguration, - ApplicationSettings::armoryDbIp, - ApplicationSettings::armoryDbPort}) { - appSettings_->reset(setting, false); - } - display(); + const std::vector resetList{ + ApplicationSettings::runArmoryLocally, ApplicationSettings::netType + , ApplicationSettings::envConfiguration, ApplicationSettings::armoryDbIp + , ApplicationSettings::armoryDbPort + }; + if (appSettings_) { + for (const auto& setting : resetList) { + appSettings_->reset(setting, false); + } + display(); + } + else { + emit resetSettings(resetList); + } } void NetworkSettingsPage::apply() { - armoryServersProvider_->setupServer(ui_->comboBoxArmoryServer->currentIndex()); + if (armoryServersProvider_ && appSettings_ && signersProvider_) { + armoryServersProvider_->setupServer(ui_->comboBoxArmoryServer->currentIndex()); - appSettings_->set(ApplicationSettings::envConfiguration, ui_->comboBoxEnvironment->currentIndex()); + appSettings_->set(ApplicationSettings::envConfiguration, ui_->comboBoxEnvironment->currentIndex()); - if (signersProvider_->currentSignerIsLocal()) { - applyLocalSignerNetOption(); + if (signersProvider_->currentSignerIsLocal()) { + applyLocalSignerNetOption(); + } + } + else { + emit setArmoryServer(ui_->comboBoxArmoryServer->currentIndex()); + emit putSetting(ApplicationSettings::envConfiguration, ui_->comboBoxEnvironment->currentIndex()); } } void NetworkSettingsPage::onEnvSelected(int envIndex) { - auto env = ApplicationSettings::EnvConfiguration(envIndex); - if (disableSettingUpdate_) { return; } - - auto armoryServers = armoryServersProvider_->servers(); - auto armoryIndex = ui_->comboBoxArmoryServer->currentIndex(); + const auto env = ApplicationSettings::EnvConfiguration(envIndex); + const int armoryIndex = ui_->comboBoxArmoryServer->currentIndex(); + int serverIndex = armoryIndex; + const auto &armoryServers = armoryServersProvider_ ? armoryServersProvider_->servers() + : armoryServers_; if (armoryIndex < 0 || armoryIndex >= armoryServers.count()) { return; } auto armoryServer = armoryServers[armoryIndex]; - if ((armoryServer.netType == NetworkType::MainNet) != (env == ApplicationSettings::EnvConfiguration::Production)) { if (env == ApplicationSettings::EnvConfiguration::Production) { - ui_->comboBoxArmoryServer->setCurrentIndex(armoryServersProvider_->getIndexOfMainNetServer()); - } - else { - ui_->comboBoxArmoryServer->setCurrentIndex(armoryServersProvider_->getIndexOfTestNetServer()); + serverIndex = ArmoryServersProvider::getIndexOfMainNetServer(); + } else { + serverIndex = ArmoryServersProvider::getIndexOfTestNetServer(); } } + ui_->comboBoxArmoryServer->setCurrentIndex(serverIndex); } void NetworkSettingsPage::onArmorySelected(int armoryIndex) { - auto armoryServers = armoryServersProvider_->servers(); + int envIndex = ui_->comboBoxEnvironment->currentIndex(); + auto armoryServers = armoryServersProvider_ ? armoryServersProvider_->servers() + : armoryServers_; if (armoryIndex < 0 || armoryIndex >= armoryServers.count()) { return; } auto armoryServer = armoryServers[armoryIndex]; - auto envSelected = static_cast(ui_->comboBoxEnvironment->currentIndex()); + const auto envSelected = static_cast(ui_->comboBoxEnvironment->currentIndex()); if ((armoryServer.netType == NetworkType::MainNet) != (envSelected == ApplicationSettings::EnvConfiguration::Production)) { if (armoryServer.netType == NetworkType::MainNet) { - ui_->comboBoxEnvironment->setCurrentIndex(static_cast(ApplicationSettings::EnvConfiguration::Production)); + envIndex = static_cast(ApplicationSettings::EnvConfiguration::Production); } else { - ui_->comboBoxEnvironment->setCurrentIndex(static_cast(ApplicationSettings::EnvConfiguration::Test)); + envIndex = static_cast(ApplicationSettings::EnvConfiguration::Test); } } + ui_->comboBoxEnvironment->setCurrentIndex(envIndex); } diff --git a/BlockSettleUILib/Settings/NetworkSettingsPage.h b/BlockSettleUILib/Settings/NetworkSettingsPage.h index 9e82c33b4..37a57130c 100644 --- a/BlockSettleUILib/Settings/NetworkSettingsPage.h +++ b/BlockSettleUILib/Settings/NetworkSettingsPage.h @@ -20,15 +20,19 @@ namespace Ui { class ApplicationSettings; class ArmoryServersViewModel; +class ArmoryServersWidget; class NetworkSettingsPage : public SettingsPage { Q_OBJECT - public: NetworkSettingsPage(QWidget* parent = nullptr); ~NetworkSettingsPage() override; + void init(const ApplicationSettings::State&) override; + + void onArmoryServers(const QList&, int idxCur, int idxConn); + public slots: void initSettings() override; void display() override; @@ -38,6 +42,10 @@ public slots: signals: void reconnectArmory(); void armoryServerChanged(); + void setArmoryServer(int); + void addArmoryServer(const ArmoryServer&); + void delArmoryServer(int); + void updArmoryServer(int, const ArmoryServer&); private slots: void onEnvSelected(int index); @@ -50,8 +58,12 @@ private slots: private: std::unique_ptr ui_; - ArmoryServersViewModel *armoryServerModel_; + ArmoryServersViewModel* armoryServerModel_{ nullptr }; + ArmoryServersWidget* armoryServersWidget_{ nullptr }; bool disableSettingUpdate_{true}; + QList armoryServers_; + int armorySrvCurrent_{ 0 }; + int armorySrvConnected_{ 0 }; }; #endif // __NETWORK_SETTINGS_PAGE_H__ diff --git a/BlockSettleUILib/Settings/SignerSettingsPage.cpp b/BlockSettleUILib/Settings/SignerSettingsPage.cpp index 622351fd9..e0d64fc63 100644 --- a/BlockSettleUILib/Settings/SignerSettingsPage.cpp +++ b/BlockSettleUILib/Settings/SignerSettingsPage.cpp @@ -25,7 +25,6 @@ SignerSettingsPage::SignerSettingsPage(QWidget* parent) : SettingsPage{parent} , ui_{new Ui::SignerSettingsPage{}} - , reset_{false} { ui_->setupUi(this); ui_->widgetTwoWayAuth->hide(); @@ -61,24 +60,36 @@ SignerSettingsPage::~SignerSettingsPage() = default; void SignerSettingsPage::display() { - ui_->checkBoxTwoWayAuth->setChecked(appSettings_->get(ApplicationSettings::twoWaySignerAuth)); - ui_->comboBoxSigner->setCurrentIndex(signersProvider_->indexOfCurrent()); + if (appSettings_ && signersProvider_) { + ui_->checkBoxTwoWayAuth->setChecked(appSettings_->get(ApplicationSettings::twoWaySignerAuth)); + ui_->comboBoxSigner->setCurrentIndex(signersProvider_->indexOfCurrent()); - showLimits(signersProvider_->indexOfCurrent() == 0); - ui_->labelTerminalKey->setText(QString::fromStdString(signersProvider_->remoteSignerOwnKey().toHexStr())); + showLimits(signersProvider_->indexOfCurrent() == 0); + ui_->labelTerminalKey->setText(QString::fromStdString(signersProvider_->remoteSignerOwnKey().toHexStr())); + } + else { + ui_->checkBoxTwoWayAuth->setChecked(settings_.at(ApplicationSettings::twoWaySignerAuth).toBool()); + ui_->comboBoxSigner->setCurrentIndex(curSignerIdx_); + showLimits(curSignerIdx_ == 0); + ui_->labelTerminalKey->setText(QString::fromStdString(ownKey_)); + } } void SignerSettingsPage::reset() { - reset_ = true; - for (const auto &setting : { ApplicationSettings::remoteSigners - , ApplicationSettings::autoSignSpendLimit - , ApplicationSettings::twoWaySignerAuth - }) { - appSettings_->reset(setting, false); + const std::vector resetList { + ApplicationSettings::remoteSigners, ApplicationSettings::autoSignSpendLimit + , ApplicationSettings::twoWaySignerAuth + }; + if (appSettings_) { + for (const auto& setting : resetList) { + appSettings_->reset(setting, false); + } + display(); + } + else { + emit resetSettings(resetList); } - display(); - reset_ = false; } void SignerSettingsPage::showHost(bool show) @@ -119,48 +130,78 @@ void SignerSettingsPage::onManageSignerKeys() // workaround here - wrap widget by QDialog // TODO: fix stylesheet to support popup widgets - QDialog *d = new QDialog(this); - QVBoxLayout *l = new QVBoxLayout(d); - l->setContentsMargins(0,0,0,0); - d->setLayout(l); - d->setWindowTitle(tr("Manage Signer Connection")); + QDialog *dlg = new QDialog(this); + QVBoxLayout *layout = new QVBoxLayout(dlg); + layout->setContentsMargins(0,0,0,0); + dlg->setLayout(layout); + dlg->setWindowTitle(tr("Manage Signer Connection")); - SignerKeysWidget *signerKeysWidget = new SignerKeysWidget(signersProvider_, appSettings_, this); - d->resize(signerKeysWidget->size()); + if (appSettings_ && signersProvider_) { + signerKeysWidget_ = new SignerKeysWidget(signersProvider_, appSettings_, this); + } + else { + signerKeysWidget_ = new SignerKeysWidget(this); + } + dlg->resize(signerKeysWidget_->size()); - l->addWidget(signerKeysWidget); + layout->addWidget(signerKeysWidget_); - connect(signerKeysWidget, &SignerKeysWidget::needClose, this, [d](){ - d->reject(); + connect(signerKeysWidget_, &SignerKeysWidget::needClose, dlg, &QDialog::reject); + connect(dlg, &QDialog::finished, [this](int) { + signerKeysWidget_->deleteLater(); + signerKeysWidget_ = nullptr; }); - - d->exec(); + signerKeysWidget_->onSignerSettings(signers_, curSignerIdx_); + dlg->exec(); emit signersChanged(); } void SignerSettingsPage::apply() { - appSettings_->set(ApplicationSettings::twoWaySignerAuth, ui_->checkBoxTwoWayAuth->isChecked()); - signersProvider_->setupSigner(ui_->comboBoxSigner->currentIndex()); + if (appSettings_) { + appSettings_->set(ApplicationSettings::twoWaySignerAuth, ui_->checkBoxTwoWayAuth->isChecked()); + signersProvider_->setupSigner(ui_->comboBoxSigner->currentIndex()); + } + else { + emit putSetting(ApplicationSettings::twoWaySignerAuth, ui_->checkBoxTwoWayAuth->isChecked()); + emit setSigner(ui_->comboBoxSigner->currentIndex()); + } } void SignerSettingsPage::initSettings() { - signersModel_ = new SignersModel(signersProvider_, this); + if (signersProvider_) { + signersModel_ = new SignersModel(signersProvider_, this); + connect(signersProvider_.get(), &SignersProvider::dataChanged, this, &SignerSettingsPage::display); + } + else { + signersModel_ = new SignersModel(this); + } signersModel_->setSingleColumnMode(true); signersModel_->setHighLightSelectedServer(false); ui_->comboBoxSigner->setModel(signersModel_); +} - connect(signersProvider_.get(), &SignersProvider::dataChanged, this, &SignerSettingsPage::display); +void SignerSettingsPage::init(const ApplicationSettings::State& state) +{ + if (state.find(ApplicationSettings::twoWaySignerAuth) == state.end()) { + return; // not our snapshot + } + SettingsPage::init(state); } -void SignerSettingsPage::init(const std::shared_ptr &appSettings - , const std::shared_ptr &armoryServersProvider - , const std::shared_ptr &signersProvider, const std::shared_ptr &signContainer - , const std::shared_ptr &walletsMgr) +void SignerSettingsPage::onSignerSettings(const QList& signers + , const std::string& ownKey, int idxCur) { - reset_ = true; - SettingsPage::init(appSettings, armoryServersProvider, signersProvider, signContainer, walletsMgr); - reset_ = false; + signers_ = signers; + curSignerIdx_ = idxCur; + ownKey_ = ownKey; + if (signersModel_) { + signersModel_->onSignerSettings(signers, idxCur); + } + if (signerKeysWidget_) { + signerKeysWidget_->onSignerSettings(signers, idxCur); + } + display(); } diff --git a/BlockSettleUILib/Settings/SignerSettingsPage.h b/BlockSettleUILib/Settings/SignerSettingsPage.h index 21a64f324..e8409d834 100644 --- a/BlockSettleUILib/Settings/SignerSettingsPage.h +++ b/BlockSettleUILib/Settings/SignerSettingsPage.h @@ -15,12 +15,11 @@ #include "ConfigDialog.h" #include "SignersModel.h" - namespace Ui { class SignerSettingsPage; -}; - +} class ApplicationSettings; +class SignerKeysWidget; class SignerSettingsPage : public SettingsPage @@ -34,11 +33,9 @@ class SignerSettingsPage : public SettingsPage void reset() override; void apply() override; void initSettings() override; - void init(const std::shared_ptr &appSettings - , const std::shared_ptr &armoryServersProvider - , const std::shared_ptr &signersProvider - , const std::shared_ptr &signContainer - , const std::shared_ptr &walletsMgr) override; + void init(const ApplicationSettings::State&) override; + + void onSignerSettings(const QList&, const std::string& ownKey, int idxCur); private slots: void onAsSpendLimitChanged(double); @@ -46,6 +43,7 @@ private slots: signals: void signersChanged(); + void setSigner(int); private: void showHost(bool); @@ -55,8 +53,11 @@ private slots: private: std::unique_ptr ui_; - SignersModel *signersModel_; - bool reset_{}; + SignersModel* signersModel_{ nullptr }; + SignerKeysWidget* signerKeysWidget_{ nullptr }; + QList signers_; + int curSignerIdx_{ 0 }; + std::string ownKey_; }; #endif // __SIGNER_SETTINGS_PAGE_H__ diff --git a/BlockSettleUILib/Settings/SignersManageWidget.cpp b/BlockSettleUILib/Settings/SignersManageWidget.cpp index 7807f550e..5b34bd4f3 100644 --- a/BlockSettleUILib/Settings/SignersManageWidget.cpp +++ b/BlockSettleUILib/Settings/SignersManageWidget.cpp @@ -54,27 +54,8 @@ SignerKeysWidget::SignerKeysWidget(const std::shared_ptr &signe emit needClose(); }); - connect(ui_->tableViewSignerKeys->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this](const QItemSelection &selected, const QItemSelection &deselected){ - // this check will prevent loop selectionChanged -> setupSignerFromSelected -> select -> selectionChanged - if (deselected.isEmpty()) { - return; - } - - ui_->pushButtonSelect->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); - ui_->pushButtonDeleteSignerKey->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); - ui_->pushButtonEditSignerKey->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); - - if (selected.indexes().first().row() == 0) { - ui_->pushButtonDeleteSignerKey->setDisabled(true); - ui_->pushButtonEditSignerKey->setDisabled(true); - } - - resetForm(); - - // save to settings right after row highlight - setupSignerFromSelected(true); - }); + connect(ui_->tableViewSignerKeys->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &SignerKeysWidget::onSelectionChanged); resetForm(); @@ -96,27 +77,111 @@ SignerKeysWidget::SignerKeysWidget(const std::shared_ptr &signe ui_->pushButtonSelect->hide(); } +SignerKeysWidget::SignerKeysWidget(QWidget* parent) + : QWidget(parent) + , ui_(new Ui::SignerKeysWidget) +{ + signersModel_ = new SignersModel(this); + ui_->setupUi(this); + + ui_->spinBoxPort->setMinimum(0); + ui_->spinBoxPort->setMaximum(USHRT_MAX); + + ui_->tableViewSignerKeys->setModel(signersModel_); + + int defaultSectionSize = ui_->tableViewSignerKeys->horizontalHeader()->defaultSectionSize(); + ui_->tableViewSignerKeys->horizontalHeader()->resizeSection(0, defaultSectionSize * 2); + ui_->tableViewSignerKeys->horizontalHeader()->resizeSection(1, defaultSectionSize); + ui_->tableViewSignerKeys->horizontalHeader()->resizeSection(2, defaultSectionSize); + ui_->tableViewSignerKeys->horizontalHeader()->setStretchLastSection(true); + + connect(ui_->pushButtonAddSignerKey, &QPushButton::clicked, this, &SignerKeysWidget::onAddSignerKey); + connect(ui_->pushButtonDeleteSignerKey, &QPushButton::clicked, this, &SignerKeysWidget::onDeleteSignerKey); + connect(ui_->pushButtonEditSignerKey, &QPushButton::clicked, this, &SignerKeysWidget::onEdit); + connect(ui_->pushButtonCancelSaveSignerKey, &QPushButton::clicked, this, &SignerKeysWidget::resetForm); + connect(ui_->pushButtonSaveSignerKey, &QPushButton::clicked, this, &SignerKeysWidget::onSave); + connect(ui_->pushButtonSelect, &QPushButton::clicked, this, &SignerKeysWidget::onSelect); + connect(ui_->pushButtonKeyImport, &QPushButton::clicked, this, &SignerKeysWidget::onKeyImport); + + connect(ui_->lineEditName, &QLineEdit::textChanged, this, &SignerKeysWidget::onFormChanged); + connect(ui_->lineEditAddress, &QLineEdit::textChanged, this, &SignerKeysWidget::onFormChanged); + connect(ui_->spinBoxPort, QOverload::of(&QSpinBox::valueChanged), this, &SignerKeysWidget::onFormChanged); + + connect(ui_->pushButtonClose, &QPushButton::clicked, this, [this]() { + emit needClose(); + }); + + connect(ui_->tableViewSignerKeys->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &SignerKeysWidget::onSelectionChanged); + + resetForm(); + + ui_->pushButtonDeleteSignerKey->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); + ui_->pushButtonEditSignerKey->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); + + auto validator = new QRegExpValidator(this); + validator->setRegExp(kRxAddress); + ui_->lineEditAddress->setValidator(validator); + onFormChanged(); + + // TODO: remove select signer button if it's not required anymore + ui_->pushButtonSelect->hide(); +} + +void SignerKeysWidget::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + // this check will prevent loop selectionChanged -> setupSignerFromSelected -> select -> selectionChanged + if (deselected.isEmpty()) { + return; + } + + ui_->pushButtonSelect->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); + ui_->pushButtonDeleteSignerKey->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); + ui_->pushButtonEditSignerKey->setDisabled(ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()); + + if (selected.indexes().first().row() == 0) { + ui_->pushButtonDeleteSignerKey->setDisabled(true); + ui_->pushButtonEditSignerKey->setDisabled(true); + } + + resetForm(); + + // save to settings right after row highlight + setupSignerFromSelected(true); +} + void SignerKeysWidget::setRowSelected(int row) { QModelIndex currentIndex; - if (signersProvider_->signers().size() >= 0) { + if (signersProvider_ && (signersProvider_->signers().size() >= 0)) { int indexOfCurrent = row; if (indexOfCurrent < 0 || indexOfCurrent >= signersProvider_->signers().size()) { indexOfCurrent = 0; } currentIndex = signersModel_->index(indexOfCurrent, 0); } + else { + currentIndex = signersModel_->index(row, 0); + } ui_->tableViewSignerKeys->selectionModel()->select(currentIndex - , QItemSelectionModel::Select | QItemSelectionModel::Rows); + , QItemSelectionModel::Select | QItemSelectionModel::Rows); +} + +void SignerKeysWidget::onSignerSettings(const QList& signers + , int idxCur) +{ + signers_ = signers; + signersModel_->onSignerSettings(signers, idxCur); + setRowSelected(idxCur); } SignerKeysWidget::~SignerKeysWidget() = default; void SignerKeysWidget::onAddSignerKey() { - if (ui_->lineEditName->text().isEmpty() || ui_->lineEditAddress->text().isEmpty()) + if (ui_->lineEditName->text().isEmpty() || ui_->lineEditAddress->text().isEmpty()) { return; - + } SignerHost signerHost; signerHost.name = ui_->lineEditName->text(); @@ -124,11 +189,17 @@ void SignerKeysWidget::onAddSignerKey() signerHost.port = ui_->spinBoxPort->value(); signerHost.key = ui_->lineEditKey->text(); - signersProvider_->add(signerHost); - resetForm(); + if (signersProvider_) { + signersProvider_->add(signerHost); + resetForm(); - setRowSelected(signersProvider_->signers().size() - 1); - setupSignerFromSelected(true); + setRowSelected(signersProvider_->signers().size() - 1); + setupSignerFromSelected(true); + } + else { + resetForm(); + emit addSigner(signerHost); + } } void SignerKeysWidget::onDeleteSignerKey() @@ -142,11 +213,17 @@ void SignerKeysWidget::onDeleteSignerKey() return; } - signersProvider_->remove(selectedRow); - resetForm(); + if (signersProvider_) { + signersProvider_->remove(selectedRow); + resetForm(); - setRowSelected(0); - setupSignerFromSelected(true); + setRowSelected(0); + setupSignerFromSelected(true); + } + else { + resetForm(); + emit delSigner(selectedRow); + } } void SignerKeysWidget::onEdit() @@ -156,11 +233,12 @@ void SignerKeysWidget::onEdit() } int index = ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().first().row(); - if (index >= signersProvider_->signers().size()) { + if (signersProvider_ && (index >= signersProvider_->signers().size())) { return; } - SignerHost signerHost = signersProvider_->signers().at(index); + SignerHost signerHost = signersProvider_ ? signersProvider_->signers().at(index) + : signers_.at(index); ui_->stackedWidgetAddSave->setCurrentWidget(ui_->pageSaveSignerKeyButton); ui_->lineEditName->setText(signerHost.name); @@ -182,7 +260,12 @@ void SignerKeysWidget::onSave() signerHost.port = ui_->spinBoxPort->value(); signerHost.key = ui_->lineEditKey->text(); - signersProvider_->replace(index, signerHost); + if (signersProvider_) { + signersProvider_->replace(index, signerHost); + } + else { + emit updSigner(index, signerHost); + } resetForm(); } @@ -209,8 +292,20 @@ void SignerKeysWidget::onFormChanged() signerHost.key = ui_->lineEditKey->text(); valid = signerHost.isValid(); if (valid) { - exists = signersProvider_->indexOf(signerHost.name) != -1 + if (signersProvider_) { + exists = signersProvider_->indexOf(signerHost.name) != -1 || signersProvider_->indexOf(signerHost) != -1; + } + else { + for (const auto& signer : signers_) { + if ((signer.name == signerHost.name) || + ((signer.address == signerHost.address) && + (signer.port == signerHost.port))) { + exists = true; + break; + } + } + } } } ui_->pushButtonAddSignerKey->setEnabled(valid && acceptable && !exists); @@ -239,12 +334,12 @@ void SignerKeysWidget::setupSignerFromSelected(bool needUpdate) if (ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().isEmpty()) { return; } - int index = ui_->tableViewSignerKeys->selectionModel()->selectedIndexes().first().row(); - if (index >= signersProvider_->signers().size()) { - return; + if (signersProvider_) { + if (index >= signersProvider_->signers().size()) { + return; + } + signersProvider_->setupSigner(index, needUpdate); + setRowSelected(signersProvider_->indexOfCurrent()); } - - signersProvider_->setupSigner(index, needUpdate); - setRowSelected(signersProvider_->indexOfCurrent()); } diff --git a/BlockSettleUILib/Settings/SignersManageWidget.h b/BlockSettleUILib/Settings/SignersManageWidget.h index fd782b498..a4c8ba85b 100644 --- a/BlockSettleUILib/Settings/SignersManageWidget.h +++ b/BlockSettleUILib/Settings/SignersManageWidget.h @@ -11,6 +11,7 @@ #ifndef SIGNERS_MANAGE_WIDGET_H #define SIGNERS_MANAGE_WIDGET_H +#include #include #include @@ -25,12 +26,15 @@ class SignerKeysWidget : public QWidget Q_OBJECT public: - explicit SignerKeysWidget(const std::shared_ptr &signersProvider + [[deprecated]] explicit SignerKeysWidget(const std::shared_ptr &signersProvider , const std::shared_ptr &appSettings, QWidget *parent = nullptr); + explicit SignerKeysWidget(QWidget* parent = nullptr); ~SignerKeysWidget(); void setRowSelected(int row); + void onSignerSettings(const QList&, int idxCur); + public slots: void onAddSignerKey(); void onDeleteSignerKey(); @@ -41,6 +45,12 @@ public slots: signals: void needClose(); + void addSigner(const SignerHost&); + void delSigner(int); + void updSigner(int, const SignerHost&); + +private slots: + void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); private: void setupSignerFromSelected(bool needUpdate); @@ -53,8 +63,9 @@ private slots: std::unique_ptr ui_; std::shared_ptr appSettings_; std::shared_ptr signersProvider_; + QList signers_; - SignersModel *signersModel_; + SignersModel* signersModel_{ nullptr }; }; #endif // SIGNERS_MANAGE_WIDGET_H diff --git a/BlockSettleUILib/Settings/SignersModel.cpp b/BlockSettleUILib/Settings/SignersModel.cpp index d02549f1b..f6e897637 100644 --- a/BlockSettleUILib/Settings/SignersModel.cpp +++ b/BlockSettleUILib/Settings/SignersModel.cpp @@ -21,6 +21,10 @@ SignersModel::SignersModel(const std::shared_ptr& signersProvid connect(signersProvider.get(), &SignersProvider::dataChanged, this, &SignersModel::update); } +SignersModel::SignersModel(QObject* parent) + : QAbstractTableModel(parent) +{} + int SignersModel::columnCount(const QModelIndex&) const { return static_cast(SignersModel::ColumnsCount); @@ -39,7 +43,8 @@ QVariant SignersModel::data(const QModelIndex &index, int role) const SignerHost signerHost = signers_.at(index.row()); - int currentServerIndex = signersProvider_->indexOfCurrent(); + const int currentServerIndex = signersProvider_ ? signersProvider_->indexOfCurrent() + : currentServerIndex_; if (role == Qt::FontRole && index.row() == currentServerIndex) { // QFont font; @@ -100,6 +105,15 @@ void SignersModel::setSingleColumnMode(bool singleColumnMode) singleColumnMode_ = singleColumnMode; } +void SignersModel::onSignerSettings(const QList& signers + , int idxCur) +{ + currentServerIndex_ = idxCur; + beginResetModel(); + signers_ = signers; + endResetModel(); +} + void SignersModel::setHighLightSelectedServer(bool highLightSelectedServer) { highLightSelectedServer_ = highLightSelectedServer; diff --git a/BlockSettleUILib/Settings/SignersModel.h b/BlockSettleUILib/Settings/SignersModel.h index f2dd4fb45..63e954389 100644 --- a/BlockSettleUILib/Settings/SignersModel.h +++ b/BlockSettleUILib/Settings/SignersModel.h @@ -21,8 +21,9 @@ class SignersModel : public QAbstractTableModel { public: - SignersModel(const std::shared_ptr &signersProvider - , QObject *parent = nullptr); + [[deprecated]] SignersModel(const std::shared_ptr &signersProvider + , QObject *parent = nullptr); + SignersModel(QObject* parent = nullptr); ~SignersModel() noexcept = default; SignersModel(const SignersModel&) = delete; @@ -40,12 +41,15 @@ class SignersModel : public QAbstractTableModel void setHighLightSelectedServer(bool highLightSelectedServer); void setSingleColumnMode(bool singleColumnMode); + void onSignerSettings(const QList &, int idxCur); + public slots: void update(); private: std::shared_ptr signersProvider_; QList signers_; + int currentServerIndex_{ 0 }; bool highLightSelectedServer_ = true; bool singleColumnMode_ = false; diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 732db79aa..918427c37 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -9,10 +9,12 @@ */ #include "SettingsAdapter.h" +#include #include #include #include "ApplicationSettings.h" #include "ArmoryServersProvider.h" +#include "BootstrapDataManager.h" #include "Settings/SignersProvider.h" #include "LogManager.h" #include "PubKeyLoader.h" @@ -44,7 +46,25 @@ SettingsAdapter::SettingsAdapter(const std::shared_ptr &set appSettings_->selectNetwork(); logger_->debug("Settings loaded from {}", appSettings_->GetSettingsPath().toStdString()); - armoryServersProvider_ = std::make_shared(settings); + bootstrapDataManager_ = std::make_shared(logMgr_->logger(), appSettings_); + if (bootstrapDataManager_->hasLocalFile()) { + bootstrapDataManager_->loadFromLocalFile(); + } else { + // load from resources + const QString filePathInResources = appSettings_->bootstrapResourceFileName(); + + QFile file; + file.setFileName(filePathInResources); + if (file.open(QIODevice::ReadOnly)) { + const std::string bootstrapData = file.readAll().toStdString(); + bootstrapDataManager_->setReceivedData(bootstrapData); + } else { + logger_->error("[SettingsAdapter] failed to locate bootstrap file in resources: {}" + , filePathInResources.toStdString()); + } + } + + armoryServersProvider_ = std::make_shared(settings, bootstrapDataManager_); signersProvider_ = std::make_shared(appSettings_); } @@ -63,12 +83,36 @@ bool SettingsAdapter::process(const bs::message::Envelope &env) return processPutRequest(msg.put_request()); case SettingsMessage::kArmoryServer: return processArmoryServer(msg.armory_server()); + case SettingsMessage::kSetArmoryServer: + return processSetArmoryServer(env, msg.set_armory_server()); + case SettingsMessage::kArmoryServersGet: + return processGetArmoryServers(env); + case SettingsMessage::kAddArmoryServer: + return processAddArmoryServer(env, msg.add_armory_server()); + case SettingsMessage::kDelArmoryServer: + return processDelArmoryServer(env, msg.del_armory_server()); + case SettingsMessage::kUpdArmoryServer: + return processUpdArmoryServer(env, msg.upd_armory_server()); case SettingsMessage::kSignerRequest: return processSignerSettings(env); case SettingsMessage::kSignerSetKey: return processSignerSetKey(msg.signer_set_key()); case SettingsMessage::kSignerReset: return processSignerReset(); + case SettingsMessage::kSignerServersGet: + return processGetSigners(env); + case SettingsMessage::kSetSignerServer: + return processSetSigner(env, msg.set_signer_server()); + case SettingsMessage::kAddSignerServer: + return processAddSigner(env, msg.add_signer_server()); + case SettingsMessage::kDelSignerServer: + return processDelSigner(env, msg.del_signer_server()); + case SettingsMessage::kStateGet: + return processGetState(env); + case SettingsMessage::kReset: + return processReset(env, msg.reset()); + case SettingsMessage::kResetToState: + return processResetToState(env, msg.reset_to_state()); default: logger_->warn("[SettingsAdapter::process] unknown data case: {}" , msg.data_case()); @@ -115,6 +159,54 @@ bool SettingsAdapter::processRemoteSettings(uint64_t msgId) return true; } +bool SettingsAdapter::processGetState(const bs::message::Envelope& env) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_state(); + for (const auto& st : appSettings_->getState()) { + auto setResp = msgResp->add_responses(); + auto setReq = setResp->mutable_request(); + setReq->set_source(SettingSource_Local); + setReq->set_index(static_cast(st.first)); + setFromQVariant(st.second, setReq, setResp); + } + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + return pushFill(envResp); +} + +bool SettingsAdapter::processReset(const bs::message::Envelope& env + , const SettingsMessage_SettingsRequest& request) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_state(); + for (const auto& req : request.requests()) { + auto setResp = msgResp->add_responses(); + auto setReq = setResp->mutable_request(); + setReq->set_source(req.source()); + setReq->set_index(req.index()); + const auto& setting = static_cast(req.index()); + appSettings_->reset(setting); + const auto& value = appSettings_->get(setting); + setFromQVariant(value, setReq, setResp); + } + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + return pushFill(envResp); +} + +bool SettingsAdapter::processResetToState(const bs::message::Envelope& env + , const SettingsMessage_SettingsResponse& request) +{ + for (const auto& req : request.responses()) { + const auto& value = fromResponse(req); + const auto& setting = static_cast(req.request().index()); + appSettings_->set(setting, value); + } + SettingsMessage msg; + *msg.mutable_state() = request; + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + return pushFill(envResp); +} + bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env , const SettingsMessage_SettingsRequest &request) { @@ -261,7 +353,7 @@ bool SettingsAdapter::processPutRequest(const SettingsMessage_SettingsResponse & return true; } -bool SettingsAdapter::processArmoryServer(const BlockSettle::Terminal::SettingsMessage_ArmoryServerSet &request) +bool SettingsAdapter::processArmoryServer(const BlockSettle::Terminal::SettingsMessage_ArmoryServer &request) { int selIndex = 0; for (const auto &server : armoryServersProvider_->servers()) { @@ -279,6 +371,81 @@ bool SettingsAdapter::processArmoryServer(const BlockSettle::Terminal::SettingsM appSettings_->selectNetwork(); } +bool SettingsAdapter::processSetArmoryServer(const bs::message::Envelope& env, int index) +{ + armoryServersProvider_->setupServer(index); + appSettings_->selectNetwork(); + return processGetArmoryServers(env); +} + +bool SettingsAdapter::processGetArmoryServers(const bs::message::Envelope& env) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_armory_servers(); + msgResp->set_idx_current(armoryServersProvider_->indexOfCurrent()); + msgResp->set_idx_connected(armoryServersProvider_->indexOfConnected()); + for (const auto& server : armoryServersProvider_->servers()) { + auto msgSrv = msgResp->add_servers(); + msgSrv->set_network_type((int)server.netType); + msgSrv->set_server_name(server.name.toStdString()); + msgSrv->set_server_address(server.armoryDBIp.toStdString()); + msgSrv->set_server_port(std::to_string(server.armoryDBPort)); + msgSrv->set_server_key(server.armoryDBKey.toStdString()); + msgSrv->set_run_locally(server.runLocally); + msgSrv->set_one_way_auth(server.oneWayAuth_); + msgSrv->set_password(server.password.toBinStr()); + } + bs::message::Envelope envResp{ env.id, user_, env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(envResp); +} + +static ArmoryServer fromMessage(const SettingsMessage_ArmoryServer& msg) +{ + ArmoryServer result; + result.name = QString::fromStdString(msg.server_name()); + result.netType = static_cast(msg.network_type()); + result.armoryDBIp = QString::fromStdString(msg.server_address()); + result.armoryDBPort = std::stoi(msg.server_port()); + result.armoryDBKey = QString::fromStdString(msg.server_key()); + result.password = SecureBinaryData::fromString(msg.password()); + result.runLocally = msg.run_locally(); + result.oneWayAuth_ = msg.one_way_auth(); + return result; +} + +bool SettingsAdapter::processAddArmoryServer(const bs::message::Envelope& env + , const SettingsMessage_ArmoryServer& request) +{ + const auto& server = fromMessage(request); + if (armoryServersProvider_->add(server)) { + armoryServersProvider_->setupServer(armoryServersProvider_->indexOf(server)); + } + else { + logger_->warn("[{}] failed to add server", __func__); + } + return processGetArmoryServers(env); +} + +bool SettingsAdapter::processDelArmoryServer(const bs::message::Envelope& env + , int index) +{ + if (!armoryServersProvider_->remove(index)) { + logger_->warn("[{}] failed to remove server #{}", __func__, index); + } + return processGetArmoryServers(env); +} + +bool SettingsAdapter::processUpdArmoryServer(const bs::message::Envelope& env + , const SettingsMessage_ArmoryServerUpdate& request) +{ + const auto& server = fromMessage(request.server()); + if (!armoryServersProvider_->replace(request.index(), server)) { + logger_->warn("[{}] failed to update server #{}", __func__, request.index()); + } + return processGetArmoryServers(env); +} + bool SettingsAdapter::processSignerSettings(const bs::message::Envelope &env) { SettingsMessage msg; @@ -300,7 +467,6 @@ bool SettingsAdapter::processSignerSettings(const bs::message::Envelope &env) keyVal->set_key(signer.serverId()); keyVal->set_value(signer.key.toStdString()); } - msgResp->set_local_port(appSettings_->get(ApplicationSettings::localSignerPort)); msgResp->set_home_dir(appSettings_->GetHomeDir().toStdString()); msgResp->set_auto_sign_spend_limit(appSettings_->get(ApplicationSettings::autoSignSpendLimit)); @@ -320,3 +486,158 @@ bool SettingsAdapter::processSignerReset() signersProvider_->setupSigner(0, true); return true; } + +bool SettingsAdapter::processGetSigners(const bs::message::Envelope& env) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_signer_servers(); + msgResp->set_own_key(signersProvider_->remoteSignerOwnKey().toHexStr()); + msgResp->set_idx_current(signersProvider_->indexOfCurrent()); + for (const auto& signer : signersProvider_->signers()) { + auto msgSrv = msgResp->add_servers(); + msgSrv->set_name(signer.name.toStdString()); + msgSrv->set_host(signer.address.toStdString()); + msgSrv->set_port(std::to_string(signer.port)); + msgSrv->set_key(signer.key.toStdString()); + } + bs::message::Envelope envResp{ env.id, user_, env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(envResp); +} + +bool SettingsAdapter::processSetSigner(const bs::message::Envelope& env + , int index) +{ + signersProvider_->setupSigner(index); + return processGetSigners(env); +} + +static SignerHost fromMessage(const SettingsMessage_SignerServer& msg) +{ + SignerHost result; + result.name = QString::fromStdString(msg.name()); + result.address = QString::fromStdString(msg.host()); + result.port = std::stoi(msg.port()); + result.key = QString::fromStdString(msg.key()); + return result; +} + +bool SettingsAdapter::processAddSigner(const bs::message::Envelope& env + , const SettingsMessage_SignerServer& request) +{ + const auto& signer = fromMessage(request); + signersProvider_->add(signer); + signersProvider_->setupSigner(signersProvider_->indexOf(signer)); + return processGetSigners(env); +} + +bool SettingsAdapter::processDelSigner(const bs::message::Envelope& env + , int index) +{ + signersProvider_->remove(index); + return processGetSigners(env); +} + + +void bs::message::setFromQVariant(const QVariant& val, SettingRequest* req, SettingResponse* resp) +{ + switch (val.type()) { + case QVariant::Type::String: + req->set_type(SettingType_String); + resp->set_s(val.toString().toStdString()); + break; + case QVariant::Type::Int: + req->set_type(SettingType_Int); + resp->set_i(val.toInt()); + break; + case QVariant::Type::UInt: + req->set_type(SettingType_UInt); + resp->set_ui(val.toUInt()); + break; + case QVariant::Type::ULongLong: + case QVariant::Type::LongLong: + req->set_type(SettingType_UInt64); + resp->set_ui64(val.toULongLong()); + break; + case QVariant::Type::Double: + req->set_type(SettingType_Float); + resp->set_f(val.toDouble()); + break; + case QVariant::Type::Bool: + req->set_type(SettingType_Bool); + resp->set_b(val.toBool()); + break; + case QVariant::Type::Rect: + req->set_type(SettingType_Rect); + { + auto setRect = resp->mutable_rect(); + setRect->set_left(val.toRect().left()); + setRect->set_top(val.toRect().top()); + setRect->set_height(val.toRect().height()); + setRect->set_width(val.toRect().width()); + } + break; + case QVariant::Type::StringList: + req->set_type(SettingType_Strings); + for (const auto& s : val.toStringList()) { + resp->mutable_strings()->add_strings(s.toStdString()); + } + break; + case QVariant::Type::Map: + req->set_type(SettingType_StrMap); + for (const auto& key : val.toMap().keys()) { + auto kvData = resp->mutable_key_vals()->add_key_vals(); + kvData->set_key(key.toStdString()); + kvData->set_value(val.toMap()[key].toString().toStdString()); + } + break; + default: break; // ignore other types + } +} + +QVariant bs::message::fromResponse(const BlockSettle::Terminal::SettingResponse& setting) +{ + QVariant value; + switch (setting.request().type()) { + case SettingType_String: + value = QString::fromStdString(setting.s()); + break; + case SettingType_Int: + value = setting.i(); + break; + case SettingType_UInt: + value = setting.ui(); + break; + case SettingType_UInt64: + value = setting.ui64(); + break; + case SettingType_Bool: + value = setting.b(); + break; + case SettingType_Float: + value = setting.f(); + break; + case SettingType_Rect: + value = QRect(setting.rect().left(), setting.rect().top() + , setting.rect().width(), setting.rect().height()); + break; + case SettingType_Strings: { + QStringList sl; + for (const auto& s : setting.strings().strings()) { + sl << QString::fromStdString(s); + } + value = sl; + } + break; + case SettingType_StrMap: { + QVariantMap vm; + for (const auto& keyVal : setting.key_vals().key_vals()) { + vm[QString::fromStdString(keyVal.key())] = QString::fromStdString(keyVal.value()); + } + value = vm; + } + break; + default: break; + } + return value; +} diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index 90f9088b4..e180b6bdf 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -13,6 +13,7 @@ #include #include +#include #include "Message/Adapter.h" #include "TerminalMessage.h" @@ -25,17 +26,30 @@ namespace bs { } namespace BlockSettle { namespace Terminal { - class SettingsMessage_ArmoryServerSet; + class SettingsMessage_ArmoryServer; + class SettingsMessage_ArmoryServerUpdate; class SettingsMessage_SettingsRequest; class SettingsMessage_SettingsResponse; + class SettingsMessage_SignerServer; class SettingsMessage_SignerSetKey; + class SettingRequest; + class SettingResponse; + enum SettingType : int; } } class ApplicationSettings; class ArmoryServersProvider; +class BootstrapDataManager; class CCFileManager; class SignersProvider; +namespace bs { + namespace message { + void setFromQVariant(const QVariant &, BlockSettle::Terminal::SettingRequest * + , BlockSettle::Terminal::SettingResponse *); + QVariant fromResponse(const BlockSettle::Terminal::SettingResponse &); + } +} class SettingsAdapter : public bs::message::Adapter { @@ -57,17 +71,35 @@ class SettingsAdapter : public bs::message::Adapter bool processGetRequest(const bs::message::Envelope & , const BlockSettle::Terminal::SettingsMessage_SettingsRequest &); bool processPutRequest(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); - bool processArmoryServer(const BlockSettle::Terminal::SettingsMessage_ArmoryServerSet &); + bool processArmoryServer(const BlockSettle::Terminal::SettingsMessage_ArmoryServer &); + bool processSetArmoryServer(const bs::message::Envelope&, int index); + bool processGetArmoryServers(const bs::message::Envelope&); + bool processAddArmoryServer(const bs::message::Envelope& + , const BlockSettle::Terminal::SettingsMessage_ArmoryServer&); + bool processDelArmoryServer(const bs::message::Envelope&, int index); + bool processUpdArmoryServer(const bs::message::Envelope& + , const BlockSettle::Terminal::SettingsMessage_ArmoryServerUpdate&); bool processSignerSettings(const bs::message::Envelope &); bool processSignerSetKey(const BlockSettle::Terminal::SettingsMessage_SignerSetKey &); bool processSignerReset(); + bool processGetSigners(const bs::message::Envelope&); + bool processSetSigner(const bs::message::Envelope&, int); + bool processAddSigner(const bs::message::Envelope& + , const BlockSettle::Terminal::SettingsMessage_SignerServer&); + bool processDelSigner(const bs::message::Envelope&, int); bool processRemoteSettings(uint64_t msgId); + bool processGetState(const bs::message::Envelope&); + bool processReset(const bs::message::Envelope& + , const BlockSettle::Terminal::SettingsMessage_SettingsRequest&); + bool processResetToState(const bs::message::Envelope& + , const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); private: std::shared_ptr user_, userBC_; std::shared_ptr logMgr_; std::shared_ptr logger_; std::shared_ptr appSettings_; + std::shared_ptr bootstrapDataManager_; std::shared_ptr armoryServersProvider_; std::shared_ptr signersProvider_; std::shared_ptr ccFileManager_; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index ad4bc3ec3..048e4db1c 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -174,30 +174,34 @@ bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &re curServerId_ = response.id(); if (response.is_local()) { QLatin1String localSignerHost("127.0.0.1"); - const auto &localSignerPort = QString::fromStdString(response.local_port()); + QString localSignerPort; const auto &netType = static_cast(response.network_type()); - if (SignerConnectionExists(localSignerHost, localSignerPort)) { - logger_->error("[{}] failed to bind on local port {}", __func__, response.local_port()); + for (int attempts = 0; attempts < 10; ++attempts) { + // https://tools.ietf.org/html/rfc6335 + // the Dynamic Ports, also known as the Private or Ephemeral Ports, + // from 49152-65535 (never assigned) + auto port = 49152 + rand() % 16000; + + auto portToTest = QString::number(port); + + if (!SignerConnectionExists(localSignerHost, portToTest)) { + localSignerPort = portToTest; + break; + } else { + logger_->error("[SignerAdapter::processSignerSettings] attempt {}:" + " port {} used", port); + } + } + + if (localSignerPort.isEmpty()) { + logger_->error("[SignerAdapter::processSignerSettings] failed to find not busy port"); SignerMessage msg; auto msgError = msg.mutable_state(); msgError->set_code((int)SignContainer::SocketFailed); msgError->set_text("failed to bind local port"); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; return pushFill(env); -/* BSMessageBox mbox(BSMessageBox::Type::question - , tr("Local Signer Connection") - , tr("Continue with Remote connection in Local GUI mode?") - , tr("The Terminal failed to spawn the headless signer as the program is already running. " - "Would you like to continue with remote connection in Local GUI mode?") - , this); - if (mbox.exec() == QDialog::Rejected) { - return nullptr; - } - - // Use locally started signer as remote - signersProvider_->switchToLocalFullGUI(localSignerHost, localSignerPort); - return createRemoteSigner(true);*/ } const auto &connMgr = std::make_shared(logger_); diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index ef483b314..d90077a36 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -118,14 +118,12 @@ void MainWindow::onGetGeometry(const QRect &mainGeom) geom.setHeight(std::min(geom.height(), screenHeight)); geom.moveCenter(screenGeom.center()); } + /* const auto screen = qApp->screens()[screenNo]; const float pixelRatio = screen->devicePixelRatio(); if (pixelRatio > 1.0) { - const float coeff = (float)0.9999; // some coefficient that prevents oversizing of main window on HiRes display on Windows - geom.setWidth(geom.width() * coeff); - geom.setHeight(geom.height() * coeff); - } - setGeometry(geom); + //FIXME: re-check on hi-res screen + }*/ #else if (QApplication::desktop()->screenNumber(this) == -1) { auto currentScreenRect = QApplication::desktop()->screenGeometry(QCursor::pos()); @@ -133,9 +131,11 @@ void MainWindow::onGetGeometry(const QRect &mainGeom) geom.setWidth(std::min(geom.width(), static_cast(currentScreenRect.width() * 0.9))); geom.setHeight(std::min(geom.height(), static_cast(currentScreenRect.height() * 0.9))); geom.moveCenter(currentScreenRect.center()); - setGeometry(geom); } #endif // not Windows + QTimer::singleShot(10, [this, geom] { + setGeometry(geom); + }); } void MainWindow::onSetting(int setting, const QVariant &value) @@ -149,8 +149,24 @@ void MainWindow::onSetting(int setting, const QVariant &value) break; case ApplicationSettings::AdvancedTxDialogByDefault: advTxDlgByDefault_ = value.toBool(); + break; + case ApplicationSettings::closeToTray: + closeToTray_ = value.toBool(); + updateAppearance(); + break; default: break; } + + if (cfgDlg_) { + cfgDlg_->onSetting(setting, value); + } +} + +void bs::gui::qt::MainWindow::onSettingsState(const ApplicationSettings::State& state) +{ + if (cfgDlg_) { + cfgDlg_->onSettingsState(state); + } } void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) @@ -325,6 +341,21 @@ void bs::gui::qt::MainWindow::onSignedTX(const std::string& id, BinaryData signe } } +void bs::gui::qt::MainWindow::onArmoryServers(const QList& servers, int idxCur, int idxConn) +{ + if (cfgDlg_) { + cfgDlg_->onArmoryServers(servers, idxCur, idxConn); + } +} + +void bs::gui::qt::MainWindow::onSignerSettings(const QList& signers + , const std::string& ownKey, int idxCur) +{ + if (cfgDlg_) { + cfgDlg_->onSignerSettings(signers, ownKey, idxCur); + } +} + void MainWindow::showStartupDialog(bool showLicense) { StartupDialog startupDialog(showLicense, this); @@ -493,7 +524,7 @@ void MainWindow::setupInfoWidget() }); connect(ui_->closeBtn, &QPushButton::clicked, this, [this]() { ui_->infoWidget->setVisible(false); - emit putSetting(static_cast(ApplicationSettings::ShowInfoWidget), false); + emit putSetting(ApplicationSettings::ShowInfoWidget, false); }); } @@ -580,12 +611,12 @@ void MainWindow::raiseWindow() void MainWindow::updateAppearance() { -/* if (!applicationSettings_->get(ApplicationSettings::closeToTray) && isHidden()) { + if (!closeToTray_ && isHidden()) { setWindowState(windowState() & ~Qt::WindowMinimized); show(); raise(); activateWindow(); - }*/ + } setWindowTitle(tr("BlockSettle Terminal")); @@ -747,15 +778,30 @@ void MainWindow::setupMenu() void MainWindow::openConfigDialog(bool showInNetworkPage) { -/* ConfigDialog configDialog(applicationSettings_, armoryServersProvider_, signersProvider_, signContainer_, this); - connect(&configDialog, &ConfigDialog::reconnectArmory, this, &BSTerminalMainWindow::onArmoryNeedsReconnect); + cfgDlg_ = new ConfigDialog(this); + connect(cfgDlg_, &QDialog::finished, [this](int) { + cfgDlg_->deleteLater(); + cfgDlg_ = nullptr; + }); + connect(cfgDlg_, &ConfigDialog::reconnectArmory, this, &MainWindow::needArmoryReconnect); + connect(cfgDlg_, &ConfigDialog::putSetting, this, &MainWindow::putSetting); + connect(cfgDlg_, &ConfigDialog::resetSettings, this, &MainWindow::resetSettings); + connect(cfgDlg_, &ConfigDialog::resetSettingsToState, this, &MainWindow::resetSettingsToState); + connect(cfgDlg_, &ConfigDialog::resetSettingsToState, this, &MainWindow::resetSettingsToState); + connect(cfgDlg_, &ConfigDialog::setArmoryServer, this, &MainWindow::setArmoryServer); + connect(cfgDlg_, &ConfigDialog::addArmoryServer, this, &MainWindow::addArmoryServer); + connect(cfgDlg_, &ConfigDialog::delArmoryServer, this, &MainWindow::delArmoryServer); + connect(cfgDlg_, &ConfigDialog::updArmoryServer, this, &MainWindow::updArmoryServer); + connect(cfgDlg_, &ConfigDialog::setSigner, this, &MainWindow::setSigner); + + emit needSettingsState(); + emit needArmoryServers(); + emit needSigners(); if (showInNetworkPage) { - configDialog.popupNetworkSettings(); + cfgDlg_->popupNetworkSettings(); } - configDialog.exec();*/ - - updateAppearance(); + cfgDlg_->exec(); } void MainWindow::onLoggedIn() @@ -939,8 +985,8 @@ void MainWindow::showRunInBackgroundMessage() void MainWindow::closeEvent(QCloseEvent* event) { - emit putSetting(static_cast(ApplicationSettings::GUI_main_geometry), geometry()); - emit putSetting(static_cast(ApplicationSettings::GUI_main_tab), ui_->tabWidget->currentIndex()); + emit putSetting(ApplicationSettings::GUI_main_geometry, geometry()); + emit putSetting(ApplicationSettings::GUI_main_tab, ui_->tabWidget->currentIndex()); /* if (applicationSettings_->get(ApplicationSettings::closeToTray)) { hide(); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index dbbf277d6..63bf4a486 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -17,6 +17,7 @@ #include "Address.h" #include "ArmoryConnection.h" #include "SignContainer.h" +#include "Settings/SignersProvider.h" #include "UiUtils.h" namespace spdlog { @@ -34,6 +35,7 @@ namespace bs { class AboutDialog; class AuthAddressDialog; +class ConfigDialog; class CreateTransactionDialog; class NotificationCenter; class QSystemTrayIcon; @@ -54,6 +56,7 @@ namespace bs { ~MainWindow() override; void onSetting(int setting, const QVariant &value); + void onSettingsState(const ApplicationSettings::State&); void onGetGeometry(const QRect &); void showStartupDialog(bool showLic); @@ -80,6 +83,8 @@ namespace bs { void onFeeLevels(const std::map&); void onUTXOs(const std::string& id, const std::string& walletId, const std::vector&); void onSignedTX(const std::string &id, BinaryData signedTX, bs::error::ErrorCode result); + void onArmoryServers(const QList&, int idxCur, int idxConn); + void onSignerSettings(const QList&, const std::string& ownKey, int idxCur); public slots: void onReactivate(); @@ -94,7 +99,18 @@ namespace bs { };*/ signals: - void putSetting(int, const QVariant &); + void putSetting(ApplicationSettings::Setting, const QVariant &); + void resetSettings(const std::vector &); + void resetSettingsToState(const ApplicationSettings::State&); + void needSettingsState(); + void needArmoryServers(); + void setArmoryServer(int); + void addArmoryServer(const ArmoryServer&); + void delArmoryServer(int); + void updArmoryServer(int, const ArmoryServer&); + void needArmoryReconnect(); + void needSigners(); + void setSigner(int); void createNewWallet(); void needHDWalletDetails(const std::string &walletId); void needWalletsList(UiUtils::WalletsTypes, const std::string &id); @@ -202,6 +218,7 @@ namespace bs { std::shared_ptr txModel_; CreateTransactionDialog* txDlg_{ nullptr }; + ConfigDialog* cfgDlg_{ nullptr }; // std::shared_ptr walletsWizard_; @@ -209,6 +226,7 @@ namespace bs { QString loginButtonText_; QTimer * loginTimer_{}; + bool closeToTray_{ false }; bool initialWalletCreateDialogShown_ = false; bool deferCCsync_ = false; bool advTxDlgByDefault_{ false }; diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 621b85f42..0b9243ee3 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -28,6 +28,7 @@ #include "BSTerminalSplashScreen.h" #include "MainWindow.h" #include "ProtobufHeadlessUtils.h" +#include "SettingsAdapter.h" #include "common.pb.h" #include "terminal.pb.h" @@ -253,6 +254,14 @@ bool QtGuiAdapter::processSettings(const Envelope &env) switch (msg.data_case()) { case SettingsMessage::kGetResponse: return processSettingsGetResponse(msg.get_response()); + case SettingsMessage::kSettingsUpdated: + return processSettingsGetResponse(msg.settings_updated()); + case SettingsMessage::kState: + return processSettingsState(msg.state()); + case SettingsMessage::kArmoryServers: + return processArmoryServers(msg.armory_servers()); + case SettingsMessage::kSignerServers: + return processSignerServers(msg.signer_servers()); default: break; } return true; @@ -260,6 +269,7 @@ bool QtGuiAdapter::processSettings(const Envelope &env) bool QtGuiAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResponse &response) { + std::map settings; for (const auto &setting : response.responses()) { switch (setting.request().index()) { case SetIdx_GUI_MainGeom: { @@ -273,7 +283,7 @@ bool QtGuiAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResp ss->setGeometry(splashGeometry); }); } - QMetaObject::invokeMethod(splashScreen_, [mw=mainWindow_, mainGeometry] { + QMetaObject::invokeMethod(mainWindow_, [mw=mainWindow_, mainGeometry] { mw->onGetGeometry(mainGeometry); }); } @@ -291,63 +301,68 @@ bool QtGuiAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResp QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, showLicense] { mw->showStartupDialog(showLicense); }); + onResetSettings({}); } break; - default: { - int idx = setting.request().index(); - QVariant value; - switch (setting.request().type()) { - case SettingType_String: - value = QString::fromStdString(setting.s()); - break; - case SettingType_Int: - value = setting.i(); - break; - case SettingType_UInt: - value = setting.ui(); - break; - case SettingType_UInt64: - value = setting.ui64(); - break; - case SettingType_Bool: - value = setting.b(); - break; - case SettingType_Float: - value = setting.f(); - break; - case SettingType_Rect: - value = QRect(setting.rect().left(), setting.rect().top() - , setting.rect().width(), setting.rect().height()); - break; - case SettingType_Strings: { - QStringList sl; - for (const auto &s : setting.strings().strings()) { - sl << QString::fromStdString(s); - } - value = sl; - } - break; - case SettingType_StrMap: { - QVariantMap vm; - for (const auto &keyVal : setting.key_vals().key_vals()) { - vm[QString::fromStdString(keyVal.key())] = QString::fromStdString(keyVal.value()); - } - value = vm; - } - break; - default: break; - } - QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, idx, value] { - mw->onSetting(idx, value); - }); - } + default: + settings[setting.request().index()] = fromResponse(setting); break; } } + if (!settings.empty()) { + return QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, settings] { + for (const auto& setting : settings) { + mw->onSetting(setting.first, setting.second); + } + }); + } return true; } +bool QtGuiAdapter::processSettingsState(const SettingsMessage_SettingsResponse& response) +{ + ApplicationSettings::State state; + for (const auto& setting : response.responses()) { + state[static_cast(setting.request().index())] = + fromResponse(setting); + } + return QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, state] { + mw->onSettingsState(state); + }); +} + +bool QtGuiAdapter::processArmoryServers(const SettingsMessage_ArmoryServers& response) +{ + QList servers; + for (const auto& server : response.servers()) { + servers << ArmoryServer{ QString::fromStdString(server.server_name()) + , static_cast(server.network_type()) + , QString::fromStdString(server.server_address()) + , std::stoi(server.server_port()), QString::fromStdString(server.server_key()) + , SecureBinaryData::fromString(server.password()) + , server.run_locally(), server.one_way_auth() }; + } + logger_->debug("[{}] {} servers, cur: {}, conn: {}", __func__, servers.size() + , response.idx_current(), response.idx_connected()); + return QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, servers, response] { + mw->onArmoryServers(servers, response.idx_current(), response.idx_connected()); + }); +} + +bool QtGuiAdapter::processSignerServers(const SettingsMessage_SignerServers& response) +{ + QList servers; + for (const auto& server : response.servers()) { + servers << SignerHost{ QString::fromStdString(server.name()) + , QString::fromStdString(server.host()), std::stoi(server.port()) + , QString::fromStdString(server.key()) }; + } + return QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, servers, response] { + mw->onSignerSettings(servers, response.own_key(), response.idx_current()); + }); +} + bool QtGuiAdapter::processAdminMessage(const Envelope &env) { AdministrativeMessage msg; @@ -654,6 +669,11 @@ void QtGuiAdapter::requestInitialSettings() setReq->set_index(SetIdx_AdvancedTXisDefault); setReq->set_type(SettingType_Bool); + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_CloseToTray); + setReq->set_type(SettingType_Bool); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } @@ -661,6 +681,17 @@ void QtGuiAdapter::requestInitialSettings() void QtGuiAdapter::makeMainWinConnections() { connect(mainWindow_, &bs::gui::qt::MainWindow::putSetting, this, &QtGuiAdapter::onPutSetting); + connect(mainWindow_, &bs::gui::qt::MainWindow::resetSettings, this, &QtGuiAdapter::onResetSettings); + connect(mainWindow_, &bs::gui::qt::MainWindow::resetSettingsToState, this, &QtGuiAdapter::onResetSettingsToState); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSettingsState, this, &QtGuiAdapter::onNeedSettingsState); + connect(mainWindow_, &bs::gui::qt::MainWindow::needArmoryServers, this, &QtGuiAdapter::onNeedArmoryServers); + connect(mainWindow_, &bs::gui::qt::MainWindow::setArmoryServer, this, &QtGuiAdapter::onSetArmoryServer); + connect(mainWindow_, &bs::gui::qt::MainWindow::addArmoryServer, this, &QtGuiAdapter::onAddArmoryServer); + connect(mainWindow_, &bs::gui::qt::MainWindow::delArmoryServer, this, &QtGuiAdapter::onDelArmoryServer); + connect(mainWindow_, &bs::gui::qt::MainWindow::updArmoryServer, this, &QtGuiAdapter::onUpdArmoryServer); + connect(mainWindow_, &bs::gui::qt::MainWindow::needArmoryReconnect, this, &QtGuiAdapter::onNeedArmoryReconnect); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSigners, this, &QtGuiAdapter::onNeedSigners); + connect(mainWindow_, &bs::gui::qt::MainWindow::setSigner, this, &QtGuiAdapter::onSetSigner); connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); @@ -679,7 +710,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needSetTxComment, this, &QtGuiAdapter::onNeedSetTxComment); } -void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) +void QtGuiAdapter::onPutSetting(ApplicationSettings::Setting idx, const QVariant &value) { SettingsMessage msg; auto msgReq = msg.mutable_put_request(); @@ -687,65 +718,109 @@ void QtGuiAdapter::onPutSetting(int idx, const QVariant &value) auto setReq = setResp->mutable_request(); setReq->set_source(SettingSource_Local); setReq->set_index(static_cast(idx)); - switch (value.type()) { - case QVariant::Type::String: - setReq->set_type(SettingType_String); - setResp->set_s(value.toString().toStdString()); - break; - case QVariant::Type::Int: - setReq->set_type(SettingType_Int); - setResp->set_i(value.toInt()); - break; - case QVariant::Type::UInt: - setReq->set_type(SettingType_UInt); - setResp->set_ui(value.toUInt()); - break; - case QVariant::Type::ULongLong: - case QVariant::Type::LongLong: - setReq->set_type(SettingType_UInt64); - setResp->set_ui64(value.toULongLong()); - break; - case QVariant::Type::Double: - setReq->set_type(SettingType_Float); - setResp->set_f(value.toDouble()); - break; - case QVariant::Type::Bool: - setReq->set_type(SettingType_Bool); - setResp->set_b(value.toBool()); - break; - case QVariant::Type::Rect: - setReq->set_type(SettingType_Rect); - { - auto setRect = setResp->mutable_rect(); - setRect->set_left(value.toRect().left()); - setRect->set_top(value.toRect().top()); - setRect->set_height(value.toRect().height()); - setRect->set_width(value.toRect().width()); - } - break; - case QVariant::Type::StringList: - setReq->set_type(SettingType_Strings); - for (const auto &s : value.toStringList()) { - setResp->mutable_strings()->add_strings(s.toStdString()); - } - break; - case QVariant::Type::Map: - setReq->set_type(SettingType_StrMap); - for (const auto &key : value.toMap().keys()) { - auto kvData = setResp->mutable_key_vals()->add_key_vals(); - kvData->set_key(key.toStdString()); - kvData->set_value(value.toMap()[key].toString().toStdString()); - } - break; + setFromQVariant(value, setReq, setResp); + + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onResetSettings(const std::vector& settings) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_reset(); + for (const auto& setting : settings) { + auto msgReq = msgResp->add_requests(); + msgReq->set_index(static_cast(setting)); + msgReq->set_source(SettingSource_Local); } + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} +void QtGuiAdapter::onResetSettingsToState(const ApplicationSettings::State& state) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_reset_to_state(); + for (const auto& st : state) { + auto setResp = msgResp->add_responses(); + auto setReq = setResp->mutable_request(); + setReq->set_source(SettingSource_Local); + setReq->set_index(static_cast(st.first)); + setFromQVariant(st.second, setReq, setResp); + } + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedSettingsState() +{ + SettingsMessage msg; + msg.mutable_state_get(); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedArmoryServers() +{ + SettingsMessage msg; + msg.mutable_armory_servers_get(); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onSetArmoryServer(int index) +{ + SettingsMessage msg; + msg.set_set_armory_server(index); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onAddArmoryServer(const ArmoryServer& server) +{ + SettingsMessage msg; + auto msgReq = msg.mutable_add_armory_server(); + msgReq->set_network_type((int)server.netType); + msgReq->set_server_name(server.name.toStdString()); + msgReq->set_server_address(server.armoryDBIp.toStdString()); + msgReq->set_server_port(std::to_string(server.armoryDBPort)); + msgReq->set_server_key(server.armoryDBKey.toStdString()); + msgReq->set_run_locally(server.runLocally); + msgReq->set_one_way_auth(server.oneWayAuth_); + msgReq->set_password(server.password.toBinStr()); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onDelArmoryServer(int index) +{ + SettingsMessage msg; + msg.set_del_armory_server(index); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onUpdArmoryServer(int index, const ArmoryServer& server) +{ + SettingsMessage msg; + auto msgReq = msg.mutable_upd_armory_server(); + msgReq->set_index(index); + auto msgSrv = msgReq->mutable_server(); + msgSrv->set_network_type((int)server.netType); + msgSrv->set_server_name(server.name.toStdString()); + msgSrv->set_server_address(server.armoryDBIp.toStdString()); + msgSrv->set_server_port(std::to_string(server.armoryDBPort)); + msgSrv->set_server_key(server.armoryDBKey.toStdString()); + msgSrv->set_run_locally(server.runLocally); + msgSrv->set_one_way_auth(server.oneWayAuth_); + msgSrv->set_password(server.password.toBinStr()); Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } void QtGuiAdapter::createWallet(bool primary) { - logger_->debug("[{}]", __func__); + logger_->debug("[{}] primary: {}", __func__, primary); } void QtGuiAdapter::onNeedHDWalletDetails(const std::string &walletId) @@ -932,6 +1007,30 @@ void QtGuiAdapter::onNeedBroadcastZC(const std::string& id, const BinaryData& tx pushFill(env); } +void QtGuiAdapter::onNeedArmoryReconnect() +{ + ArmoryMessage msg; + msg.mutable_reconnect(); + Envelope env{ 0, user_, userBlockchain_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedSigners() +{ + SettingsMessage msg; + msg.mutable_signer_servers_get(); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onSetSigner(int index) +{ + SettingsMessage msg; + msg.set_set_signer_server(index); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::onNeedSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string& comment) { logger_->debug("[{}] {}: {}", __func__, txHash.toHexStr(true), comment); diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 403005b66..e90cd8e5c 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -40,7 +40,9 @@ namespace BlockSettle { class WalletsMessage_WalletsListResponse; } namespace Terminal { + class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; + class SettingsMessage_SignerServers; } } class BSTerminalSplashScreen; @@ -68,7 +70,10 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu private: bool processSettings(const bs::message::Envelope &); - bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); + bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); + bool processSettingsState(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); + bool processArmoryServers(const BlockSettle::Terminal::SettingsMessage_ArmoryServers&); + bool processSignerServers(const BlockSettle::Terminal::SettingsMessage_SignerServers&); bool processAdminMessage(const bs::message::Envelope &); bool processBlockchain(const bs::message::Envelope &); bool processSigner(const bs::message::Envelope &); @@ -98,7 +103,18 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processZCInvalidated(const BlockSettle::Common::ArmoryMessage_ZCInvalidated&); private slots: - void onPutSetting(int idx, const QVariant &value); + void onPutSetting(ApplicationSettings::Setting, const QVariant &value); + void onResetSettings(const std::vector&); + void onResetSettingsToState(const ApplicationSettings::State&); + void onNeedSettingsState(); + void onNeedArmoryServers(); + void onSetArmoryServer(int); + void onAddArmoryServer(const ArmoryServer&); + void onDelArmoryServer(int); + void onUpdArmoryServer(int, const ArmoryServer&); + void onNeedArmoryReconnect(); + void onNeedSigners(); + void onSetSigner(int); void onNeedHDWalletDetails(const std::string &walletId); void onNeedWalletBalances(const std::string &walletId); void onNeedExtAddresses(const std::string &walletId); diff --git a/common b/common index 51a18c64c..d726a7d05 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 51a18c64c789d6428efa39ddfa1f2fc51493c44e +Subproject commit d726a7d05180ea62ebad02571ff4f580284bd61a From d7368ad8e3e809c813f11a9ee5c2ea6d0c5ecd8a Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 5 Oct 2020 12:35:31 +0300 Subject: [PATCH 016/146] Build fix --- BlockSettleUILib/InfoDialogs/StartupDialog.h | 1 + 1 file changed, 1 insertion(+) diff --git a/BlockSettleUILib/InfoDialogs/StartupDialog.h b/BlockSettleUILib/InfoDialogs/StartupDialog.h index fb3910112..68cf31c27 100644 --- a/BlockSettleUILib/InfoDialogs/StartupDialog.h +++ b/BlockSettleUILib/InfoDialogs/StartupDialog.h @@ -38,6 +38,7 @@ class StartupDialog : public QDialog [[deprecated]] void init(const std::shared_ptr &appSettings); [[deprecated]] void applySelectedConnectivity(); + NetworkType getSelectedNetworkType() const; private slots: void onBack(); From 014041e18e649eb48092e38bee6b7307fc9ca2e5 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 7 Oct 2020 10:25:24 +0300 Subject: [PATCH 017/146] Initial login support --- BlockSettleApp/main.cpp | 17 +- BlockSettleUILib/BSTerminalMainWindow.cpp | 86 +++---- BlockSettleUILib/BSTerminalMainWindow.h | 8 +- BlockSettleUILib/LoginWindow.cpp | 129 +++++++++- BlockSettleUILib/LoginWindow.h | 27 ++- BlockSettleUILib/StatusBarView.cpp | 9 +- BlockSettleUILib/StatusBarView.h | 2 +- .../Trading/AutoSignQuoteProvider.cpp | 10 +- .../Trading/AutoSignQuoteProvider.h | 14 +- .../Trading/QuoteRequestsModel.cpp | 4 +- BlockSettleUILib/Trading/QuoteRequestsModel.h | 6 +- .../Trading/QuoteRequestsWidget.cpp | 9 +- .../Trading/QuoteRequestsWidget.h | 7 +- BlockSettleUILib/Trading/RFQDialog.cpp | 2 +- BlockSettleUILib/Trading/RFQDialog.h | 6 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 6 +- BlockSettleUILib/Trading/RFQReplyWidget.h | 8 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 6 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 8 +- .../Trading/RequestingQuoteWidget.cpp | 4 +- .../Trading/RequestingQuoteWidget.h | 6 +- Core/ApiAdapter.cpp | 3 +- Core/BsServerAdapter.cpp | 187 ++++++++++++++- Core/BsServerAdapter.h | 33 ++- Core/MatchingAdapter.cpp | 29 ++- Core/MatchingAdapter.h | 17 +- Core/SettingsAdapter.cpp | 42 +++- Core/SettingsAdapter.h | 1 + GUI/QtWidgets/MainWindow.cpp | 222 +++++++++++------- GUI/QtWidgets/MainWindow.h | 33 ++- GUI/QtWidgets/QtGuiAdapter.cpp | 127 ++++++++++ GUI/QtWidgets/QtGuiAdapter.h | 14 ++ UnitTests/TestEnv.h | 4 +- common | 2 +- 34 files changed, 844 insertions(+), 244 deletions(-) diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index a47dd4a2f..67fa0c81b 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -311,8 +311,9 @@ int main(int argc, char** argv) for (int i = 0; i < argc; ++i) { args << QLatin1String(argv[i]); } - +#ifdef NDEBUG try { +#endif //NDEBUG const auto &settings = std::make_shared(QLatin1Literal("BlockSettle Terminal") , QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + ApplicationSettings::appSubDir()); @@ -338,13 +339,13 @@ int main(int argc, char** argv) , bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets) , signAdapter->createClient() , bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain))); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger("bscon"))); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); + inprocBus.addAdapter(std::make_shared(logMgr->logger("match"))); + inprocBus.addAdapter(std::make_shared(logMgr->logger("settl"))); + inprocBus.addAdapter(std::make_shared(logMgr->logger("md"))); + inprocBus.addAdapter(std::make_shared(logMgr->logger("mdh"))); + inprocBus.addAdapter(std::make_shared(logMgr->logger("chat"))); inprocBus.addAdapter(std::make_shared(logMgr->logger() , bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain))); @@ -352,6 +353,7 @@ int main(int argc, char** argv) logMgr->logger()->error("No runnable adapter found on main inproc bus"); return EXIT_FAILURE; } +#ifdef NDEBUG } catch (const std::exception &e) { std::cerr << "Failed to start BlockSettle Terminal: " << e.what() << std::endl; @@ -359,6 +361,7 @@ int main(int argc, char** argv) , QObject::tr("Unhandled exception detected: %1").arg(QLatin1String(e.what()))).exec(); return EXIT_FAILURE; } +#endif //NDEBUG // return GuiApp(argc, argv); } diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 8085e3717..b6ae48b6e 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -437,9 +437,9 @@ void BSTerminalMainWindow::initConnections() connectionManager_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); celerConnection_ = std::make_shared(logMgr_->logger()); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectedToServer, this, &BSTerminalMainWindow::onCelerConnected); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectionClosed, this, &BSTerminalMainWindow::onCelerDisconnected); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectionError, this, &BSTerminalMainWindow::onCelerConnectionError, Qt::QueuedConnection); + connect(celerConnection_.get(), &CelerClientQt::OnConnectedToServer, this, &BSTerminalMainWindow::onCelerConnected); + connect(celerConnection_.get(), &CelerClientQt::OnConnectionClosed, this, &BSTerminalMainWindow::onCelerDisconnected); + connect(celerConnection_.get(), &CelerClientQt::OnConnectionError, this, &BSTerminalMainWindow::onCelerConnectionError, Qt::QueuedConnection); mdCallbacks_ = std::make_shared(); mdProvider_ = std::make_shared(connectionManager_ @@ -2227,10 +2227,10 @@ void BSTerminalMainWindow::processDeferredDialogs() deferredDialogRunning_ = false; } -std::shared_ptr BSTerminalMainWindow::createClient() +std::shared_ptr BSTerminalMainWindow::createClient() { auto logger = logMgr_->logger("proxy"); - auto bsClient = std::make_shared(logger); + auto bsClient = std::make_shared(logger); bs::network::BIP15xParams params; params.ephemeralPeers = true; @@ -2248,13 +2248,13 @@ std::shared_ptr BSTerminalMainWindow::createClient() bsClient->setConnection(std::move(connection)); // Must be connected before loginDialog.exec call (balances could be received before loginDialog.exec returns)! - connect(bsClient.get(), &BsClient::balanceLoaded, assetManager_.get(), &AssetManager::fxBalanceLoaded); - connect(bsClient.get(), &BsClient::balanceUpdated, assetManager_.get(), &AssetManager::onAccountBalanceLoaded); + connect(bsClient.get(), &BsClientQt::balanceLoaded, assetManager_.get(), &AssetManager::fxBalanceLoaded); + connect(bsClient.get(), &BsClientQt::balanceUpdated, assetManager_.get(), &AssetManager::onAccountBalanceLoaded); return bsClient; } -void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsClient +void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsClient , const BsClientLoginResult &result, const std::string &email) { currentUserLogin_ = QString::fromStdString(email); @@ -2274,36 +2274,36 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli onBootstrapDataLoaded(result.bootstrapDataSigned); - connect(bsClient_.get(), &BsClient::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); - connect(bsClient_.get(), &BsClient::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); - connect(bsClient_.get(), &BsClient::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); + connect(bsClient_.get(), &BsClientQt::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); + connect(bsClient_.get(), &BsClientQt::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); + connect(bsClient_.get(), &BsClientQt::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); // connect to RFQ dialog - connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); - connect(bsClient_.get(), &BsClient::disconnected, ui_->widgetRFQ, &RFQRequestWidget::onUserDisconnected); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClient::sendUnsignedPayin); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayinToPB, bsClient_.get(), &BsClient::sendSignedPayin); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClient::sendSignedPayout); + connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); + connect(bsClient_.get(), &BsClientQt::disconnected, ui_->widgetRFQ, &RFQRequestWidget::onUserDisconnected); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClientQt::sendUnsignedPayin); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayinToPB, bsClient_.get(), &BsClientQt::sendSignedPayin); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClientQt::sendSignedPayout); - connect(ui_->widgetRFQ, &RFQRequestWidget::cancelXBTTrade, bsClient_.get(), &BsClient::sendCancelOnXBTTrade); - connect(ui_->widgetRFQ, &RFQRequestWidget::cancelCCTrade, bsClient_.get(), &BsClient::sendCancelOnCCTrade); + connect(ui_->widgetRFQ, &RFQRequestWidget::cancelXBTTrade, bsClient_.get(), &BsClientQt::sendCancelOnXBTTrade); + connect(ui_->widgetRFQ, &RFQRequestWidget::cancelCCTrade, bsClient_.get(), &BsClientQt::sendCancelOnCCTrade); // connect to quote dialog - connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQReply, &RFQReplyWidget::onMessageFromPB); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClient::sendUnsignedPayin); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayinToPB, bsClient_.get(), &BsClient::sendSignedPayin); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClient::sendSignedPayout); + connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetRFQReply, &RFQReplyWidget::onMessageFromPB); + connect(ui_->widgetRFQReply, &RFQReplyWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClientQt::sendUnsignedPayin); + connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayinToPB, bsClient_.get(), &BsClientQt::sendSignedPayin); + connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClientQt::sendSignedPayout); - connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelXBTTrade, bsClient_.get(), &BsClient::sendCancelOnXBTTrade); - connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelCCTrade, bsClient_.get(), &BsClient::sendCancelOnCCTrade); + connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelXBTTrade, bsClient_.get(), &BsClientQt::sendCancelOnXBTTrade); + connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelCCTrade, bsClient_.get(), &BsClientQt::sendCancelOnCCTrade); - connect(ui_->widgetChat, &ChatWidget::emailHashRequested, bsClient_.get(), &BsClient::findEmailHash); - connect(bsClient_.get(), &BsClient::emailHashReceived, ui_->widgetChat, &ChatWidget::onEmailHashReceived); + connect(ui_->widgetChat, &ChatWidget::emailHashRequested, bsClient_.get(), &BsClientQt::findEmailHash); + connect(bsClient_.get(), &BsClientQt::emailHashReceived, ui_->widgetChat, &ChatWidget::onEmailHashReceived); - connect(bsClient_.get(), &BsClient::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); + connect(bsClient_.get(), &BsClientQt::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); utxoReservationMgr_->setFeeRatePb(result.feeRatePb); - connect(bsClient_.get(), &BsClient::feeRateReceived, this, [this] (float feeRate) { + connect(bsClient_.get(), &BsClientQt::feeRateReceived, this, [this] (float feeRate) { utxoReservationMgr_->setFeeRatePb(feeRate); }); @@ -2321,20 +2321,20 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli // Market data, charts and chat should be available for all Auth eID logins mdProvider_->SubscribeToMD(); - connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetChat, &ChatWidget::onProcessOtcPbMessage); - connect(ui_->widgetChat, &ChatWidget::sendOtcPbMessage, bsClient_.get(), &BsClient::sendPbMessage); + connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetChat, &ChatWidget::onProcessOtcPbMessage); + connect(ui_->widgetChat, &ChatWidget::sendOtcPbMessage, bsClient_.get(), &BsClientQt::sendPbMessage); - connect(bsClient_.get(), &BsClient::bootstrapDataUpdated, this, [this](const std::string& data) { + connect(bsClient_.get(), &BsClientQt::bootstrapDataUpdated, this, [this](const std::string& data) { onBootstrapDataLoaded(data); }); accountEnabled_ = true; onAccountTypeChanged(result.userType, result.enabled); - connect(bsClient_.get(), &BsClient::accountStateChanged, this, [this](bs::network::UserType userType, bool enabled) { + connect(bsClient_.get(), &BsClientQt::accountStateChanged, this, [this](bs::network::UserType userType, bool enabled) { onAccountTypeChanged(userType, enabled); }); - connect(bsClient_.get(), &BsClient::tradingStatusChanged, this, [](bool tradingEnabled) { + connect(bsClient_.get(), &BsClientQt::tradingStatusChanged, this, [](bool tradingEnabled) { NotificationCenter::notify(tradingEnabled ? bs::ui::NotifyType::TradingEnabledOnPB : bs::ui::NotifyType::TradingDisabledOnPB, {}); }); } @@ -2383,18 +2383,18 @@ void BSTerminalMainWindow::tryLoginUsingApiKey() } }; - connect(autoLoginClient_.get(), &BsClient::connected, this, [this, logger, apiKeyErrorCb] { - connect(autoLoginClient_.get(), &BsClient::authorizeDone, this, [this, logger, apiKeyErrorCb] - (BsClient::AuthorizeError error, const std::string &email) { - if (error != BsClient::AuthorizeError::NoError) { + connect(autoLoginClient_.get(), &BsClientQt::connected, this, [this, logger, apiKeyErrorCb] { + connect(autoLoginClient_.get(), &BsClientQt::authorizeDone, this, [this, logger, apiKeyErrorCb] + (BsClientCallbackTarget::AuthorizeError error, const std::string &email) { + if (error != BsClientCallbackTarget::AuthorizeError::NoError) { switch (error) { - case BsClient::AuthorizeError::UnknownIpAddr: + case BsClientCallbackTarget::AuthorizeError::UnknownIpAddr: apiKeyErrorCb(AutoLoginState::Failed, tr("Unexpected IP address")); break; - case BsClient::AuthorizeError::UnknownApiKey: + case BsClientCallbackTarget::AuthorizeError::UnknownApiKey: apiKeyErrorCb(AutoLoginState::Failed, tr("API key not found")); break; - case BsClient::AuthorizeError::Timeout: + case BsClientCallbackTarget::AuthorizeError::Timeout: apiKeyErrorCb(AutoLoginState::Idle, tr("Request timeout")); break; default: @@ -2404,7 +2404,7 @@ void BSTerminalMainWindow::tryLoginUsingApiKey() return; } - connect(autoLoginClient_.get(), &BsClient::getLoginResultDone, this, [this, logger, email, apiKeyErrorCb] + connect(autoLoginClient_.get(), &BsClientQt::getLoginResultDone, this, [this, logger, email, apiKeyErrorCb] (const BsClientLoginResult &result) { if (result.status != AutheIDClient::NoError) { apiKeyErrorCb(AutoLoginState::Idle, tr("Login failed")); @@ -2436,10 +2436,10 @@ void BSTerminalMainWindow::tryLoginUsingApiKey() }); }); - connect(autoLoginClient_.get(), &BsClient::disconnected, this, [logger, apiKeyErrorCb] { + connect(autoLoginClient_.get(), &BsClientQt::disconnected, this, [logger, apiKeyErrorCb] { apiKeyErrorCb(AutoLoginState::Idle, tr("Proxy disconnected")); }); - connect(autoLoginClient_.get(), &BsClient::connectionFailed, this, [logger, apiKeyErrorCb] { + connect(autoLoginClient_.get(), &BsClientQt::connectionFailed, this, [logger, apiKeyErrorCb] { apiKeyErrorCb(AutoLoginState::Idle, tr("Proxy connection failed")); }); diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index f6431a126..d1180f96f 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -283,8 +283,8 @@ private slots: void restartTerminal(); void processDeferredDialogs(); - std::shared_ptr createClient(); - void activateClient(const std::shared_ptr &bsClient + std::shared_ptr createClient(); + void activateClient(const std::shared_ptr &bsClient , const BsClientLoginResult &result, const std::string &email); const std::string &loginApiKeyEncrypted() const; void initApiKeyLogins(); @@ -307,7 +307,7 @@ private slots: QString autoLoginLastErrorMsg_; std::string loginApiKeyEncrypted_; QTimer *loginTimer_{}; - std::shared_ptr autoLoginClient_; + std::shared_ptr autoLoginClient_; bool initialWalletCreateDialogShown_ = false; bool deferCCsync_ = false; @@ -345,7 +345,7 @@ private slots: }; std::unique_ptr act_; - std::shared_ptr bsClient_; + std::shared_ptr bsClient_; Chat::ChatClientServicePtr chatClientServicePtr_; diff --git a/BlockSettleUILib/LoginWindow.cpp b/BlockSettleUILib/LoginWindow.cpp index f8d68fe3b..c5f023575 100644 --- a/BlockSettleUILib/LoginWindow.cpp +++ b/BlockSettleUILib/LoginWindow.cpp @@ -34,7 +34,7 @@ namespace { } LoginWindow::LoginWindow(const std::shared_ptr &logger - , const std::shared_ptr &bsClient + , const std::shared_ptr &bsClient , std::shared_ptr &settings , QWidget* parent) : QDialog(parent) @@ -92,8 +92,61 @@ LoginWindow::LoginWindow(const std::shared_ptr &logger updateState(); - connect(bsClient_.get(), &BsClient::startLoginDone, this, &LoginWindow::onStartLoginDone); - connect(bsClient_.get(), &BsClient::getLoginResultDone, this, &LoginWindow::onGetLoginResultDone); + connect(bsClient_.get(), &BsClientQt::startLoginDone, this, &LoginWindow::onStartLoginDone); + connect(bsClient_.get(), &BsClientQt::getLoginResultDone, this, &LoginWindow::onGetLoginResultDone); +} + +LoginWindow::LoginWindow(const std::shared_ptr& logger + , ApplicationSettings::EnvConfiguration envCfg, QWidget* parent) + : QDialog(parent) + , ui_(new Ui::LoginWindow()) + , logger_(logger) +{ + ui_->setupUi(this); + ui_->progressBar->setMaximum(kAutheIdTimeout * 2); // update every 0.5 sec + const auto version = ui_->loginVersionLabel->text().replace(QLatin1String("{Version}") + , tr("Version %1").arg(QString::fromStdString(AboutDialog::version()))); + ui_->loginVersionLabel->setText(version); + resize(minimumSize()); + + const bool isProd = (envCfg == ApplicationSettings::EnvConfiguration::Production); + + ApplicationSettings::Setting urlType; + auto getAccountText = ui_->labelGetAccount->text(); + QString title; + if (isProd) { + urlType = ApplicationSettings::GetAccount_UrlProd; + title = kProdTitle; + } else { + urlType = ApplicationSettings::GetAccount_UrlTest; + title = kTestTitle; + getAccountText.replace(kCreateAccountProd, kCreateAccountTest); + } + + ui_->widgetSignup->setProperty("prodEnv", QVariant(true)); +/* getAccountText.replace(QLatin1String("{GetAccountLink}") + , settings->get(urlType));*/ // TODO: use async retrieval + ui_->labelGetAccount->setText(getAccountText); + ui_->widgetSignup->update(); + + connect(ui_->lineEditUsername, &QLineEdit::textChanged, this, &LoginWindow::onTextChanged); + +// ui_->checkBoxRememberUsername->setChecked(settings_->get(ApplicationSettings::rememberLoginUserName)); + +/* const QString username = settings_->get(ApplicationSettings::celerUsername); + if (!username.isEmpty() && ui_->checkBoxRememberUsername->isChecked()) { + ui_->lineEditUsername->setText(username); + } else { + ui_->lineEditUsername->setFocus(); + }*/ //TODO: use async retrieval + + connect(ui_->signWithEidButton, &QPushButton::clicked, this, &LoginWindow::accept); + + timer_.setInterval(500); + connect(&timer_, &QTimer::timeout, this, &LoginWindow::onTimer); + timer_.start(); + + updateState(); } LoginWindow::~LoginWindow() = default; @@ -125,6 +178,43 @@ QString LoginWindow::email() const return ui_->lineEditUsername->text().toLower(); } +void LoginWindow::setLogin(const QString& login) +{ + ui_->lineEditUsername->setText(login); + updateState(); +} + +void LoginWindow::setRememberLogin(bool flag) +{ + ui_->checkBoxRememberUsername->setChecked(flag); +} + +void LoginWindow::onLoginStarted(const std::string& login, bool success + , const std::string& errMsg) +{ + if (success) { + setState(WaitLoginResult); + } + else { + setState(Idle); + displayError(errMsg); + } +} + +void LoginWindow::onLoggedIn(const BsClientLoginResult& result) +{ + if (result.status == AutheIDClient::Cancelled || result.status == AutheIDClient::Timeout) { + setState(Idle); + return; + } + if (result.status != AutheIDClient::NoError) { + setState(Idle); + displayError(result.errorMsg); + return; + } + QDialog::accept(); +} + void LoginWindow::onStartLoginDone(bool success, const std::string &errorMsg) { if (!success) { @@ -151,7 +241,7 @@ void LoginWindow::onGetLoginResultDone(const BsClientLoginResult &result) return; } - result_ = std::make_unique(std::move(result)); + result_ = std::make_unique(result); QDialog::accept(); } @@ -163,7 +253,12 @@ void LoginWindow::accept() void LoginWindow::reject() { if (state_ == WaitLoginResult) { - bsClient_->cancelLogin(); + if (bsClient_) { + bsClient_->cancelLogin(); + } + else { + emit needCancelLogin(); + } } QDialog::reject(); } @@ -215,14 +310,30 @@ void LoginWindow::onAuthPressed() QString login = ui_->lineEditUsername->text().trimmed(); ui_->lineEditUsername->setText(login); - bsClient_->startLogin(login.toStdString()); + if (bsClient_) { + bsClient_->startLogin(login.toStdString()); + } + else { + emit needStartLogin(login.toStdString()); + } if (ui_->checkBoxRememberUsername->isChecked()) { - settings_->set(ApplicationSettings::rememberLoginUserName, true); - settings_->set(ApplicationSettings::celerUsername, ui_->lineEditUsername->text()); + if (settings_) { + settings_->set(ApplicationSettings::rememberLoginUserName, true); + settings_->set(ApplicationSettings::celerUsername, ui_->lineEditUsername->text()); + } + else { + emit putSetting(ApplicationSettings::rememberLoginUserName, true); + emit putSetting(ApplicationSettings::celerUsername, ui_->lineEditUsername->text()); + } } else { - settings_->set(ApplicationSettings::rememberLoginUserName, false); + if (settings_) { + settings_->set(ApplicationSettings::rememberLoginUserName, false); + } + else { + emit putSetting(ApplicationSettings::rememberLoginUserName, false); + } } setState(WaitLoginResult); diff --git a/BlockSettleUILib/LoginWindow.h b/BlockSettleUILib/LoginWindow.h index 0ca0d6411..0993002ab 100644 --- a/BlockSettleUILib/LoginWindow.h +++ b/BlockSettleUILib/LoginWindow.h @@ -14,6 +14,7 @@ #include #include #include +#include "ApplicationSettings.h" #include "AutheIDClient.h" namespace Ui { @@ -27,17 +28,19 @@ struct BsClientLoginResult; struct NetworkSettings; class ApplicationSettings; -class BsClient; +class BsClientQt; class LoginWindow : public QDialog { Q_OBJECT public: - LoginWindow(const std::shared_ptr &logger - , const std::shared_ptr &bsClient + [[deprecated]] LoginWindow(const std::shared_ptr &logger + , const std::shared_ptr &bsClient , std::shared_ptr &settings , QWidget* parent = nullptr); + LoginWindow(const std::shared_ptr& logger + , ApplicationSettings::EnvConfiguration, QWidget* parent = nullptr); ~LoginWindow() override; enum State { @@ -46,11 +49,21 @@ Q_OBJECT }; QString email() const; - BsClientLoginResult *result() const { return result_.get(); } + [[deprecated]] BsClientLoginResult *result() const { return result_.get(); } + + void setLogin(const QString&); + void setRememberLogin(bool); + void onLoginStarted(const std::string& login, bool success, const std::string& errMsg); + void onLoggedIn(const BsClientLoginResult&); + +signals: + void putSetting(ApplicationSettings::Setting, const QVariant&); + void needStartLogin(const std::string& login); + void needCancelLogin(); private slots: void onStartLoginDone(bool success, const std::string &errorMsg); - void onGetLoginResultDone(const BsClientLoginResult &result); + void onGetLoginResultDone(const BsClientLoginResult &result); //deprecated void onTextChanged(); void onAuthPressed(); void onTimer(); @@ -71,8 +84,8 @@ private slots: State state_{State::Idle}; QTimer timer_; float timeLeft_{}; - std::shared_ptr bsClient_; - std::unique_ptr result_; + std::shared_ptr bsClient_; + std::unique_ptr result_; }; #endif // __LOGIN_WINDOW_H__ diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 5ee811eda..d2d7aa8e5 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -17,7 +17,8 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory , const std::shared_ptr &walletsManager - , std::shared_ptr assetManager, const std::shared_ptr &celerClient + , std::shared_ptr assetManager + , const std::shared_ptr &celerClient , const std::shared_ptr &container, QStatusBar *parent) : QObject(nullptr) , statusBar_(parent) @@ -88,9 +89,9 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportFinished, this, &StatusBarView::onWalletImportFinished); connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &StatusBarView::updateBalances); - connect(celerClient.get(), &BaseCelerClient::OnConnectedToServer, this, &StatusBarView::onConnectedToServer); - connect(celerClient.get(), &BaseCelerClient::OnConnectionClosed, this, &StatusBarView::onConnectionClosed); - connect(celerClient.get(), &BaseCelerClient::OnConnectionError, this, &StatusBarView::onConnectionError); + connect(celerClient.get(), &CelerClientQt::OnConnectedToServer, this, &StatusBarView::onConnectedToServer); + connect(celerClient.get(), &CelerClientQt::OnConnectionClosed, this, &StatusBarView::onConnectionClosed); + connect(celerClient.get(), &CelerClientQt::OnConnectionError, this, &StatusBarView::onConnectionError); // container might be null if user rejects remote signer key if (container) { diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index b02d57139..b11a10930 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -37,7 +37,7 @@ class StatusBarView : public QObject, public ArmoryCallbackTarget public: [[deprecated]] StatusBarView(const std::shared_ptr & , const std::shared_ptr & - , std::shared_ptr assetManager, const std::shared_ptr & + , std::shared_ptr assetManager, const std::shared_ptr & , const std::shared_ptr &, QStatusBar *parent); StatusBarView(QStatusBar *parent); ~StatusBarView() noexcept override; diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp index 5771faa80..b79cc9c61 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp @@ -26,7 +26,7 @@ AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptr &appSettings , const std::shared_ptr &container - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , QObject *parent) : QObject(parent), logger_(logger), scriptRunner_(scriptRunner) , appSettings_(appSettings) @@ -53,8 +53,8 @@ AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptr &lo , UserScriptRunner *scriptRunner , const std::shared_ptr &appSettings , const std::shared_ptr &container - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , QObject *parent) : AutoSignScriptProvider(logger, scriptRunner, appSettings, container, celerClient, parent) { @@ -279,7 +279,7 @@ AutoSignRFQProvider::AutoSignRFQProvider(const std::shared_ptr & , UserScriptRunner *scriptRunner , const std::shared_ptr &appSettings , const std::shared_ptr &container - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , QObject *parent) : AutoSignScriptProvider(logger, scriptRunner, appSettings, container, celerClient, parent) { diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h index ba0440f71..e44acc89e 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h @@ -17,7 +17,7 @@ #include #include "ApplicationSettings.h" -class BaseCelerClient; +class CelerClientQt; class SignContainer; class UserScriptRunner; class AssetManager; @@ -43,7 +43,7 @@ class AutoSignScriptProvider : public QObject , UserScriptRunner * , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , QObject *parent = nullptr); bool isScriptLoaded() const { return scriptLoaded_; } @@ -94,9 +94,9 @@ public slots: std::shared_ptr appSettings_; ApplicationSettings::Setting lastScript_{ ApplicationSettings::_last }; std::shared_ptr logger_; - std::shared_ptr signingContainer_; - std::shared_ptr walletsManager_; - std::shared_ptr celerClient_; + std::shared_ptr signingContainer_; + std::shared_ptr walletsManager_; + std::shared_ptr celerClient_; UserScriptRunner *scriptRunner_{}; bs::error::ErrorCode autoSignState_{ bs::error::ErrorCode::AutoSignDisabled }; @@ -114,7 +114,7 @@ class AutoSignAQProvider : public AutoSignScriptProvider , UserScriptRunner * , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , QObject *parent = nullptr); }; @@ -126,7 +126,7 @@ class AutoSignRFQProvider : public AutoSignScriptProvider , UserScriptRunner * , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , QObject *parent = nullptr); }; diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index ade65320a..eaefcad61 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -26,7 +26,7 @@ QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptr &statsCollector - , std::shared_ptr celerClient, std::shared_ptr appSettings + , std::shared_ptr celerClient, std::shared_ptr appSettings , QObject* parent) : QAbstractItemModel(parent) , secStatsCollector_(statsCollector) @@ -41,7 +41,7 @@ QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptrget(ApplicationSettings::PriceUpdateInterval)); - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &QuoteRequestsModel::clearModel); connect(this, &QuoteRequestsModel::deferredUpdate, this, &QuoteRequestsModel::onDeferredUpdate, Qt::QueuedConnection); diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h index 4317ada35..5ca25805b 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.h @@ -34,8 +34,8 @@ namespace bs { class SettlementContainer; } class AssetManager; -class BaseCelerClient; class ApplicationSettings; +class CelerClientQt; class QuoteRequestsModel : public QAbstractItemModel { @@ -94,7 +94,7 @@ class QuoteRequestsModel : public QAbstractItemModel public: QuoteRequestsModel(const std::shared_ptr & - , std::shared_ptr celerClient + , std::shared_ptr celerClient , std::shared_ptr appSettings , QObject* parent); ~QuoteRequestsModel() override; @@ -153,7 +153,7 @@ private slots: MDPrices mdPrices_; const QString groupNameSettlements_ = tr("Settlements"); std::shared_ptr secStatsCollector_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr appSettings_; std::unordered_set pendingDeleteIds_; int priceUpdateInterval_; diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp index 0650a2881..e336f7767 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp @@ -71,9 +71,12 @@ QuoteRequestsWidget::QuoteRequestsWidget(QWidget* parent) QuoteRequestsWidget::~QuoteRequestsWidget() = default; -void QuoteRequestsWidget::init(std::shared_ptr logger, const std::shared_ptr "eProvider - , const std::shared_ptr& assetManager, const std::shared_ptr &statsCollector - , const std::shared_ptr &appSettings, std::shared_ptr celerClient) +void QuoteRequestsWidget::init(std::shared_ptr logger + , const std::shared_ptr "eProvider + , const std::shared_ptr& assetManager + , const std::shared_ptr &statsCollector + , const std::shared_ptr &appSettings + , std::shared_ptr celerClient) { logger_ = logger; assetManager_ = assetManager; diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.h b/BlockSettleUILib/Trading/QuoteRequestsWidget.h index bf580d8e0..de6918776 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.h +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.h @@ -109,7 +109,7 @@ class RequestsProgressDelegate : public ProgressViewDelegateBase class AssetManager; -class BaseCelerClient; +class CelerClientQt; class QuoteRequestsModel; class QuoteReqSortModel; class RFQBlotterTreeView; @@ -124,9 +124,10 @@ Q_OBJECT ~QuoteRequestsWidget() override; void init(std::shared_ptr logger, const std::shared_ptr "eProvider - , const std::shared_ptr& assetManager, const std::shared_ptr &statsCollector + , const std::shared_ptr& assetManager + , const std::shared_ptr &statsCollector , const std::shared_ptr &appSettings - , std::shared_ptr celerClient); + , std::shared_ptr celerClient); void addSettlementContainer(const std::shared_ptr &); bool StartCCSignOnOrder(const QString& orderId, QDateTime timestamp); diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 6ccf57617..4c2dedf9a 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -34,7 +34,7 @@ RFQDialog::RFQDialog(const std::shared_ptr &logger , const std::shared_ptr &walletsManager , const std::shared_ptr &signContainer , const std::shared_ptr &armory - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , const std::shared_ptr &appSettings , const std::shared_ptr &rfqStorage , const std::shared_ptr &xbtWallet diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index f1664d50f..5a9ccbf90 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -41,7 +41,7 @@ class ApplicationSettings; class ArmoryConnection; class AssetManager; class AuthAddressManager; -class BaseCelerClient; +class CelerClientQt; class CCSettlementTransactionWidget; class QuoteProvider; class RFQRequestWidget; @@ -64,7 +64,7 @@ Q_OBJECT , const std::shared_ptr &walletsManager , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , const std::shared_ptr &appSettings , const std::shared_ptr &rfqStorage , const std::shared_ptr &xbtWallet @@ -133,7 +133,7 @@ private slots: std::shared_ptr signContainer_; std::shared_ptr assetMgr_; std::shared_ptr armory_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr appSettings_; std::shared_ptr rfqStorage_; std::shared_ptr xbtWallet_; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index c607be7cc..e2c0d9c2e 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -127,7 +127,7 @@ void RFQReplyWidget::shortcutActivated(ShortcutType s) } void RFQReplyWidget::init(const std::shared_ptr &logger - , const std::shared_ptr& celerClient + , const std::shared_ptr& celerClient , const std::shared_ptr &authAddressManager , const std::shared_ptr& quoteProvider , const std::shared_ptr& mdCallbacks @@ -223,9 +223,9 @@ void RFQReplyWidget::init(const std::shared_ptr &logger ui_->treeViewOrders->initWithModel(orderListModel); - connect(celerClient_.get(), &BaseCelerClient::OnConnectedToServer, this + connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this , &RFQReplyWidget::onConnectedToCeler); - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, this + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this , &RFQReplyWidget::onDisconnectedFromCeler); connect(ui_->widgetQuoteRequests->view(), &TreeViewWithEnterKey::enterKeyPressed diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 0ce6b9680..16ca91d32 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -47,7 +47,7 @@ class ArmoryConnection; class AssetManager; class AuthAddressManager; class AutoSignScriptProvider; -class BaseCelerClient; +class CelerClientQt; class ConnectionManager; class DialogManager; class MDCallbacksQt; @@ -78,8 +78,8 @@ Q_OBJECT RFQReplyWidget(QWidget* parent = nullptr); ~RFQReplyWidget() override; - void init(const std::shared_ptr & - , const std::shared_ptr & + [[deprecated]] void init(const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -173,7 +173,7 @@ private slots: private: std::unique_ptr ui_; std::shared_ptr logger_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr quoteProvider_; std::shared_ptr authAddressManager_; std::shared_ptr assetManager_; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 35355dcbf..c967591fa 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -208,7 +208,7 @@ void RFQRequestWidget::initWidgets(const std::shared_ptr& md } void RFQRequestWidget::init(const std::shared_ptr &logger - , const std::shared_ptr& celerClient + , const std::shared_ptr& celerClient , const std::shared_ptr &authAddressManager , const std::shared_ptr "eProvider , const std::shared_ptr &assetManager @@ -248,8 +248,8 @@ void RFQRequestWidget::init(const std::shared_ptr &logger , { false, QString::fromStdString(quoteId), QString::fromStdString(reason) }); }); - connect(celerClient_.get(), &BaseCelerClient::OnConnectedToServer, this, &RFQRequestWidget::onConnectedToCeler); - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, this, &RFQRequestWidget::onDisconnectedFromCeler); + connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this, &RFQRequestWidget::onConnectedToCeler); + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &RFQRequestWidget::onDisconnectedFromCeler); connect((RFQScriptRunner *)autoSignProvider_->scriptRunner(), &RFQScriptRunner::sendRFQ , ui_->pageRFQTicket, &RFQTicketXBT::onSendRFQ, Qt::QueuedConnection); diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 556e8532e..0a0b66de4 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -46,7 +46,7 @@ class ArmoryConnection; class AssetManager; class AuthAddressManager; class AutoSignScriptProvider; -class BaseCelerClient; +class CelerClientQt; class DialogManager; class MarketDataProvider; class MDCallbacksQt; @@ -68,8 +68,8 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr &); - void init(const std::shared_ptr & - , const std::shared_ptr & + [[deprecated]] void init(const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -143,7 +143,7 @@ public slots: std::unique_ptr ui_; std::shared_ptr logger_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr quoteProvider_; std::shared_ptr assetManager_; std::shared_ptr authAddressManager_; diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index d45d896d6..db56340bb 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -49,10 +49,10 @@ RequestingQuoteWidget::RequestingQuoteWidget(QWidget* parent) RequestingQuoteWidget::~RequestingQuoteWidget() = default; -void RequestingQuoteWidget::SetCelerClient(std::shared_ptr celerClient) { +void RequestingQuoteWidget::SetCelerClient(std::shared_ptr celerClient) { celerClient_ = celerClient; - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &RequestingQuoteWidget::onCelerDisconnected); } diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.h b/BlockSettleUILib/Trading/RequestingQuoteWidget.h index 4d883386a..240320118 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.h +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.h @@ -22,7 +22,7 @@ namespace Ui { class RequestingQuoteWidget; } class AssetManager; -class BaseCelerClient; +class CelerClientQt; class RequestingQuoteWidget : public QWidget { @@ -36,7 +36,7 @@ Q_OBJECT assetManager_ = assetManager; } - void SetCelerClient(std::shared_ptr celerClient); + void SetCelerClient(std::shared_ptr celerClient); void populateDetails(const bs::network::RFQ& rfq); @@ -79,7 +79,7 @@ public slots: bs::network::Quote quote_; std::shared_ptr assetManager_; bool balanceOk_ = true; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; private: void setupTimer(Status status, const QDateTime &expTime); diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp index 6e1303d19..db6f5a8dc 100644 --- a/Core/ApiAdapter.cpp +++ b/Core/ApiAdapter.cpp @@ -122,7 +122,7 @@ class ApiBusGateway : public ApiBusAdapter auto envCopy = env; envCopy.id = 0; envCopy.receiver.reset(); - if (!env.request) { + if (!env.request && env.receiver) { const auto& itIdMap = idMap_.find(env.id); if (itIdMap != idMap_.end()) { envCopy.id = itIdMap->second.id; @@ -141,7 +141,6 @@ class ApiBusGateway : public ApiBusAdapter return rc; } - private: std::shared_ptr logger_; ApiAdapter * parent_{ nullptr }; diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index aecdc4882..b8c0a6af3 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -31,6 +31,7 @@ BsServerAdapter::BsServerAdapter(const std::shared_ptr &logger) { connMgr_ = std::make_shared(logger_); connMgr_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); + bsClient_ = std::make_unique(logger_, this); } bool BsServerAdapter::process(const bs::message::Envelope &env) @@ -60,7 +61,7 @@ bool BsServerAdapter::process(const bs::message::Envelope &env) return processLocalSettings(msg.get_response()); } } - else if (env.receiver && (env.receiver->value() == TerminalUsers::BsServer)) { + else if (env.receiver && (env.receiver->value() == user_->value())) { return processOwnRequest(env); } return true; @@ -87,8 +88,16 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) return true; } switch (msg.data_case()) { + case BsServerMessage::kOpenConnection: + return processOpenConnection(); + case BsServerMessage::kStartLogin: + return processStartLogin(msg.start_login()); + case BsServerMessage::kCancelLastLogin: + return processCancelLogin(); case BsServerMessage::kPubNewKeyResponse: return processPuBKeyResponse(msg.pub_new_key_response()); + case BsServerMessage::kTimeout: + return processTimeout(msg.timeout()); default: break; } return true; @@ -98,15 +107,15 @@ bool BsServerAdapter::processLocalSettings(const SettingsMessage_SettingsRespons { for (const auto &setting : response.responses()) { switch (static_cast(setting.request().index())) { - case ApplicationSettings::envConfiguration: { - const auto &env = static_cast(setting.i()); - - AdministrativeMessage admMsg; - admMsg.set_component_loading(user_->value()); - Envelope envBC{ 0, UserTerminal::create(TerminalUsers::System), nullptr - , {}, {}, admMsg.SerializeAsString() }; - pushFill(envBC); - } + case ApplicationSettings::envConfiguration: + envConfig_ = static_cast(setting.i()); + { + AdministrativeMessage admMsg; + admMsg.set_component_loading(user_->value()); + Envelope envBC{ 0, UserTerminal::create(TerminalUsers::System), nullptr + , {}, {}, admMsg.SerializeAsString() }; + pushFill(envBC); + } break; default: break; @@ -124,3 +133,161 @@ bool BsServerAdapter::processPuBKeyResponse(bool allowed) futPuBkey_->setValue(allowed); futPuBkey_.reset(); } + +bool BsServerAdapter::processTimeout(const std::string& id) +{ + const auto& itTO = timeouts_.find(id); + if (itTO == timeouts_.end()) { + logger_->error("[{}] unknown timeout {}", __func__, id); + return true; + } + itTO->second(); + timeouts_.erase(itTO); +} + +bool BsServerAdapter::processOpenConnection() +{ + if (connected_) { + logger_->debug("[{}] already connected", __func__); + return true; + } + bs::network::BIP15xParams params; + params.ephemeralPeers = true; + params.authMode = bs::network::BIP15xAuthMode::OneWay; + const auto& bip15xTransport = std::make_shared(logger_, params); + bip15xTransport->setKeyCb([this](const std::string & oldKey, const std::string & newKey + , const std::string & srvAddrPort, const std::shared_ptr> &prompt) { + prompt->setValue(true); + return; //TODO: remove this and above after implementing GUI support for the code below + futPuBkey_ = prompt; + BsServerMessage msg; + auto msgReq = msg.mutable_pub_new_key_request(); + msgReq->set_old_key(oldKey); + msgReq->set_new_key(newKey); + msgReq->set_server_id(srvAddrPort); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString(), true }; + pushFill(envBC); + }); + + auto wsConnection = std::make_unique(logger_, WsDataConnectionParams{}); + auto connection = std::make_unique(logger_, std::move(wsConnection), bip15xTransport); + if (!connection->openConnection(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Proxy, envConfig_) + , PubKeyLoader::serverHttpPort(), bsClient_.get())) { + logger_->error("[{}] failed to set up connection to {}", __func__ + , PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Proxy, envConfig_)); + return false; //TODO: send negative result response, maybe? + } + bsClient_->setConnection(std::move(connection)); + return true; +} + +bool BsServerAdapter::processStartLogin(const std::string& login) +{ + if (!connected_) { + return false; // wait for connection to complete + } + if (!currentLogin_.empty()) { + logger_->warn("[{}] can't start before login {} processing is complete" + , __func__, currentLogin_); + return false; + } + currentLogin_ = login; + bsClient_->startLogin(login); + return true; +} + +bool BsServerAdapter::processCancelLogin() +{ + if (currentLogin_.empty()) { + logger_->warn("[BsServerAdapter::processCancelLogin] no login started - ignoring request"); + return true; + } + bsClient_->cancelLogin(); + return true; +} + +void BsServerAdapter::startTimer(std::chrono::milliseconds timeout + , const std::function&cb) +{ + BsServerMessage msg; + const auto& toKey = CryptoPRNG::generateRandom(4).toHexStr(); + timeouts_[toKey] = cb; + msg.set_timeout(toKey); + const auto& timeNow = std::chrono::system_clock::now(); + Envelope env{ 0, user_, user_, timeNow, timeNow + timeout, msg.SerializeAsString(), true }; + pushFill(env); +} + +void BsServerAdapter::onStartLoginDone(bool success, const std::string& errorMsg) +{ + if (currentLogin_.empty()) { + logger_->error("[{}] no pending login", __func__); + return; + } + BsServerMessage msg; + auto msgResp = msg.mutable_start_login_result(); + msgResp->set_login(currentLogin_); + msgResp->set_success(success); + msgResp->set_error_text(errorMsg); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + + if (success) { + bsClient_->getLoginResult(); + } + else { + currentLogin_.clear(); + } +} + +void BsServerAdapter::onGetLoginResultDone(const BsClientLoginResult& result) +{ + if (currentLogin_.empty()) { + logger_->error("[{}] no pending login", __func__); + return; + } + BsServerMessage msg; + auto msgResp = msg.mutable_login_result(); + msgResp->set_login(currentLogin_); + currentLogin_.clear(); + msgResp->set_status((int)result.status); + msgResp->set_user_type((int)result.userType); + msgResp->set_error_text(result.errorMsg); + msgResp->set_celer_login(result.celerLogin); + msgResp->set_chat_token(result.chatTokenData.toBinStr()); + msgResp->set_chat_token_signature(result.chatTokenSign.toBinStr()); + msgResp->set_bootstrap_signed_data(result.bootstrapDataSigned); + msgResp->set_enabled(result.enabled); + msgResp->set_fee_rate(result.feeRatePb); + auto msgTradeSet = msgResp->mutable_trade_settings(); + msgTradeSet->set_xbt_tier1_limit(result.tradeSettings.xbtTier1Limit); + msgTradeSet->set_xbt_price_band(result.tradeSettings.xbtPriceBand); + msgTradeSet->set_auth_reqd_settl_trades(result.tradeSettings.authRequiredSettledTrades); + msgTradeSet->set_auth_submit_addr_limit(result.tradeSettings.authSubmitAddressLimit); + msgTradeSet->set_dealer_auth_submit_addr_limit(result.tradeSettings.dealerAuthSubmitAddressLimit); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +void BsServerAdapter::Connected() +{ + connected_ = true; + BsServerMessage msg; + msg.mutable_connected(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +void BsServerAdapter::Disconnected() +{ + connected_ = false; + BsServerMessage msg; + msg.mutable_disconnected(); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +void BsServerAdapter::onConnectionFailed() +{ + Disconnected(); +} diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index be30b67b7..5623e29dc 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -11,8 +11,10 @@ #ifndef BS_SERVER_ADAPTER_H #define BS_SERVER_ADAPTER_H -#include "Message/Adapter.h" +#include "ApplicationSettings.h" +#include "BsClient.h" #include "FutureValue.h" +#include "Message/Adapter.h" namespace spdlog { class logger; @@ -25,7 +27,7 @@ namespace BlockSettle { class ConnectionManager; class RequestReplyCommand; -class BsServerAdapter : public bs::message::Adapter +class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarget { public: BsServerAdapter(const std::shared_ptr &); @@ -43,13 +45,40 @@ class BsServerAdapter : public bs::message::Adapter bool processOwnRequest(const bs::message::Envelope &); bool processLocalSettings(const BlockSettle::Terminal::SettingsMessage_SettingsResponse &); bool processPuBKeyResponse(bool); + bool processTimeout(const std::string& id); + bool processOpenConnection(); + bool processStartLogin(const std::string&); + bool processCancelLogin(); + + //BCT callbacks + void startTimer(std::chrono::milliseconds timeout, const std::function&) override; + void onStartLoginDone(bool success, const std::string& errorMsg) override; + void onGetLoginResultDone(const BsClientLoginResult& result) override; +/* void onAuthorizeDone(AuthorizeError authErr, const std::string& email) override; + void onCelerRecv(CelerAPI::CelerMessageType messageType, const std::string& data) override; + void onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response&) override;*/ + void Connected() override; + void Disconnected() override; + void onConnectionFailed() override; +/* void onEmailHashReceived(const std::string& email, const std::string& hash) override; + void onBootstrapDataUpdated(const std::string& data) override; + void onAccountStateChanged(bs::network::UserType userType, bool enabled) override; + void onFeeRateReceived(float feeRate) override; + void onBalanceLoaded() override; + void onBalanceUpdated(const std::string& currency, double balance) override; + void onTradingStatusChanged(bool tradingEnabled) override;*/ private: std::shared_ptr logger_; std::shared_ptr user_; std::shared_ptr connMgr_; + std::unique_ptr bsClient_; + ApplicationSettings::EnvConfiguration envConfig_{ ApplicationSettings::EnvConfiguration::Unknown }; + bool connected_{ false }; + std::string currentLogin_; std::shared_ptr> futPuBkey_; + std::unordered_map> timeouts_; }; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index c5b8e8645..d7598addd 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -20,19 +20,19 @@ using namespace bs::message; MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) - : QObject(nullptr), logger_(logger) + : logger_(logger) , user_(std::make_shared(bs::message::TerminalUsers::Matching)) { - celerConnection_ = std::make_shared(logger); +/* celerConnection_ = std::make_shared(logger); connect(celerConnection_.get(), &BaseCelerClient::OnConnectedToServer, this , &MatchingAdapter::onCelerConnected, Qt::QueuedConnection); connect(celerConnection_.get(), &BaseCelerClient::OnConnectionClosed, this , &MatchingAdapter::onCelerDisconnected, Qt::QueuedConnection); connect(celerConnection_.get(), &BaseCelerClient::OnConnectionError, this - , &MatchingAdapter::onCelerConnectionError, Qt::QueuedConnection); + , &MatchingAdapter::onCelerConnectionError, Qt::QueuedConnection);*/ } -void MatchingAdapter::onCelerConnected() +/*void MatchingAdapter::onCelerConnected() { MatchingMessage msg; auto loggedIn = msg.mutable_logged_in(); @@ -61,7 +61,7 @@ void MatchingAdapter::onCelerConnectionError(int errorCode) Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); } - +*/ bool MatchingAdapter::process(const bs::message::Envelope &env) { @@ -79,5 +79,24 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) pushFill(envBC); } } + else if (env.receiver && (env.receiver->value() == user_->value())) { + MatchingMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse message #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MatchingMessage::kLogin: + return processLogin(msg.login()); + default: + logger_->warn("[{}] unknown msg {} #{}", __func__, msg.data_case(), env.id); + break; + } + } return true; } + +bool MatchingAdapter::processLogin(const BlockSettle::Terminal::MatchingMessage_Login&) +{ + return false; +} diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 8aab02428..fcbe02fba 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -11,17 +11,19 @@ #ifndef MATCHING_ADAPTER_H #define MATCHING_ADAPTER_H -#include #include "Message/Adapter.h" namespace spdlog { class logger; } -class CelerClientProxy; +namespace BlockSettle { + namespace Terminal { + class MatchingMessage_Login; + } +} -class MatchingAdapter : public QObject, public bs::message::Adapter +class MatchingAdapter : public bs::message::Adapter { - Q_OBJECT public: MatchingAdapter(const std::shared_ptr &); ~MatchingAdapter() override = default; @@ -33,17 +35,12 @@ class MatchingAdapter : public QObject, public bs::message::Adapter } std::string name() const override { return "Matching"; } -private slots: - void onCelerConnected(); - void onCelerDisconnected(); - void onCelerConnectionError(int); - private: + bool processLogin(const BlockSettle::Terminal::MatchingMessage_Login&); private: std::shared_ptr logger_; std::shared_ptr user_; - std::shared_ptr celerConnection_; }; diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 918427c37..7ac06fc85 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -57,7 +57,10 @@ SettingsAdapter::SettingsAdapter(const std::shared_ptr &set file.setFileName(filePathInResources); if (file.open(QIODevice::ReadOnly)) { const std::string bootstrapData = file.readAll().toStdString(); - bootstrapDataManager_->setReceivedData(bootstrapData); + if (!bootstrapDataManager_->setReceivedData(bootstrapData)) { + logger_->error("[SettingsAdapter] invalid bootstrap data: {}" + , filePathInResources.toStdString()); + } } else { logger_->error("[SettingsAdapter] failed to locate bootstrap file in resources: {}" , filePathInResources.toStdString()); @@ -113,6 +116,8 @@ bool SettingsAdapter::process(const bs::message::Envelope &env) return processReset(env, msg.reset()); case SettingsMessage::kResetToState: return processResetToState(env, msg.reset_to_state()); + case SettingsMessage::kLoadBootstrap: + return processBootstrap(msg.load_bootstrap()); default: logger_->warn("[SettingsAdapter::process] unknown data case: {}" , msg.data_case()); @@ -207,6 +212,27 @@ bool SettingsAdapter::processResetToState(const bs::message::Envelope& env return pushFill(envResp); } +bool SettingsAdapter::processBootstrap(const std::string& bsData) +{ + SettingsMessage msg; + auto msgResp = msg.mutable_bootstrap(); + msgResp->set_loaded(bootstrapDataManager_->setReceivedData(bsData)); + if (msgResp->loaded()) { + for (const auto& validationAddr : bootstrapDataManager_->GetAuthValidationList()) { + msgResp->add_auth_validations(validationAddr); + } + for (const auto& ccDef : bootstrapDataManager_->GetCCDefinitions()) { + auto msgCcDef = msgResp->add_cc_definitions(); + msgCcDef->set_security_id(ccDef.securityId); + msgCcDef->set_product(ccDef.product); + msgCcDef->set_genesis_address(ccDef.genesisAddr.display()); + msgCcDef->set_lot_size(ccDef.nbSatoshis); + } + } + Envelope envResp{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(envResp); +} + bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env , const SettingsMessage_SettingsRequest &request) { @@ -219,6 +245,20 @@ bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env if (req.source() == SettingSource_Local) { const auto setting = static_cast(req.index()); switch (req.type()) { + case SettingType_Unknown: { + const auto& value = appSettings_->get(setting); + switch (value.type()) { + case QVariant::Type::String: + resp->set_s(value.toString().toStdString()); + resp->mutable_request()->set_type(SettingType_String); + break; + default: // normally string is used for all unknown values + logger_->warn("[{}] {}: unhandled QVariant type: {}", __func__ + , (int)setting, (int)value.type()); + break; + } + } + break; case SettingType_String: resp->set_s(appSettings_->get(setting)); break; diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index e180b6bdf..32895c053 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -93,6 +93,7 @@ class SettingsAdapter : public bs::message::Adapter , const BlockSettle::Terminal::SettingsMessage_SettingsRequest&); bool processResetToState(const bs::message::Envelope& , const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); + bool processBootstrap(const std::string&); private: std::shared_ptr user_, userBC_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index d90077a36..7bdd6926d 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -34,6 +34,7 @@ #include "InfoDialogs/AboutDialog.h" #include "InfoDialogs/StartupDialog.h" #include "InfoDialogs/SupportDialog.h" +#include "LoginWindow.h" #include "NotificationCenter.h" #include "Settings/ConfigDialog.h" #include "StatusBarView.h" @@ -154,6 +155,24 @@ void MainWindow::onSetting(int setting, const QVariant &value) closeToTray_ = value.toBool(); updateAppearance(); break; + case ApplicationSettings::envConfiguration: { + const auto& newEnvCfg = static_cast(value.toInt()); + if (envConfig_ != newEnvCfg) { + envConfig_ = newEnvCfg; + //TODO: maybe initiate relog and Celer/proxy reconnect + } + } + break; + case ApplicationSettings::rememberLoginUserName: + if (loginDlg_) { + loginDlg_->setRememberLogin(value.toBool()); + } + break; + case ApplicationSettings::celerUsername: + if (loginDlg_) { + loginDlg_->setLogin(value.toString()); + } + break; default: break; } @@ -420,21 +439,21 @@ MainWindow::~MainWindow() void MainWindow::setupToolbar() { - action_send_ = new QAction(tr("Send Bitcoin"), this); - connect(action_send_, &QAction::triggered, this, &MainWindow::onSend); + actSend_ = new QAction(tr("Send Bitcoin"), this); + connect(actSend_, &QAction::triggered, this, &MainWindow::onSend); - action_generate_address_ = new QAction(tr("Generate &Address"), this); - connect(action_generate_address_, &QAction::triggered, this, &MainWindow::onGenerateAddress); + actNewAddress_ = new QAction(tr("Generate &Address"), this); + connect(actNewAddress_, &QAction::triggered, this, &MainWindow::onGenerateAddress); - action_login_ = new QAction(tr("Login to BlockSettle"), this); - connect(action_login_, &QAction::triggered, this, &MainWindow::onLoggedIn); + actLogin_ = new QAction(tr("Login to BlockSettle"), this); + connect(actLogin_, &QAction::triggered, this, &MainWindow::onLoginInitiated); - action_logout_ = new QAction(tr("Logout from BlockSettle"), this); - connect(action_logout_, &QAction::triggered, this, &MainWindow::onLoggedOut); + actLogout_ = new QAction(tr("Logout from BlockSettle"), this); + connect(actLogout_, &QAction::triggered, this, &MainWindow::onLogoutInitiated); setupTopRightWidget(); - action_logout_->setVisible(false); + actLogout_->setVisible(false); connect(ui_->pushButtonUser, &QPushButton::clicked, this, &MainWindow::onButtonUserClicked); @@ -443,8 +462,8 @@ void MainWindow::setupToolbar() connect(trayShowAction, &QAction::triggered, this, &QMainWindow::show); trayMenu->addSeparator(); - trayMenu->addAction(action_send_); - trayMenu->addAction(action_generate_address_); + trayMenu->addAction(actSend_); + trayMenu->addAction(actNewAddress_); trayMenu->addAction(ui_->actionSettings); trayMenu->addSeparator(); @@ -459,13 +478,13 @@ void MainWindow::setupTopRightWidget() toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); ui_->tabWidget->setCornerWidget(toolBar, Qt::TopRightCorner); - toolBar->addAction(action_send_); - toolBar->addAction(action_generate_address_); + toolBar->addAction(actSend_); + toolBar->addAction(actNewAddress_); for (int i = 0; i < toolBar->children().size(); ++i) { auto *toolButton = qobject_cast(toolBar->children().at(i)); - if (toolButton && (toolButton->defaultAction() == action_send_ - || toolButton->defaultAction() == action_generate_address_)) { + if (toolButton && (toolButton->defaultAction() == actSend_ + || toolButton->defaultAction() == actNewAddress_)) { toolButton->setObjectName(QLatin1String("mainToolBarActions")); } } @@ -714,14 +733,14 @@ void MainWindow::onSend() void MainWindow::setupMenu() { // menu role erquired for OSX only, to place it to first menu item - action_login_->setMenuRole(QAction::ApplicationSpecificRole); - action_logout_->setMenuRole(QAction::ApplicationSpecificRole); + actLogin_->setMenuRole(QAction::ApplicationSpecificRole); + actLogout_->setMenuRole(QAction::ApplicationSpecificRole); - ui_->menuFile->insertAction(ui_->actionSettings, action_login_); - ui_->menuFile->insertAction(ui_->actionSettings, action_logout_); + ui_->menuFile->insertAction(ui_->actionSettings, actLogin_); + ui_->menuFile->insertAction(ui_->actionSettings, actLogout_); - ui_->menuFile->insertSeparator(action_login_); + ui_->menuFile->insertSeparator(actLogin_); ui_->menuFile->insertSeparator(ui_->actionSettings); /* AboutDialog *aboutDlg = new AboutDialog(applicationSettings_->get(ApplicationSettings::ChangeLog_Base_Url), this); @@ -804,64 +823,53 @@ void MainWindow::openConfigDialog(bool showInNetworkPage) cfgDlg_->exec(); } -void MainWindow::onLoggedIn() +void MainWindow::onLoginInitiated() { -// onNetworkSettingsRequired(NetworkSettingsClient::Login); + if (!actLogin_->isEnabled()) { + return; + } + //TODO: prompt for primary wallet creation if needed + emit needOpenBsConnection(); + loginDlg_ = new LoginWindow(logger_, envConfig_, this); + emit getSettings({ ApplicationSettings::rememberLoginUserName, ApplicationSettings::celerUsername }); + connect(loginDlg_, &QDialog::finished, [this] { + loginDlg_->deleteLater(); + loginDlg_ = nullptr; + }); + connect(loginDlg_, &LoginWindow::putSetting, this, &MainWindow::putSetting); + connect(loginDlg_, &LoginWindow::needStartLogin, this, &MainWindow::needStartLogin); + connect(loginDlg_, &LoginWindow::needCancelLogin, this, &MainWindow::needCancelLogin); + + loginDlg_->exec(); } -/*void MainWindow::onLoginProceed(const NetworkSettings &networkSettings) +void bs::gui::qt::MainWindow::onLoginStarted(const std::string& login, bool success, const std::string& errMsg) { - auto envType = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration).toInt()); - -#ifdef PRODUCTION_BUILD - if (networkSettings.status == Blocksettle::Communication::GetNetworkSettingsResponse_Status_LIVE_TRADING_COMING_SOON) { - BSMessageBox mbox(BSMessageBox::question, tr("Login to BlockSettle"), tr("Live trading is coming soon...") - , tr("In the meantime, you can try p2p trading in our testnet environment. Would you like to do so now?"), this); - mbox.setCancelButtonText(tr("Cancel")); - mbox.setConfirmButtonText(tr("Yes")); - int rc = mbox.exec(); - if (rc == QDialog::Accepted) { - switchToTestEnv(); - restartTerminal(); - } - return; + if (loginDlg_) { + loginDlg_->onLoginStarted(login, success, errMsg); } -#endif +} - if (walletsSynched_ && !walletsMgr_->getPrimaryWallet()) { - addDeferredDialog([this] { - CreatePrimaryWalletPrompt dlg; - int rc = dlg.exec(); - if (rc == CreatePrimaryWalletPrompt::CreateWallet) { - ui_->widgetWallets->CreateNewWallet(); - } else if (rc == CreatePrimaryWalletPrompt::ImportWallet) { - ui_->widgetWallets->ImportNewWallet(); - } - }); - return; +void MainWindow::onLoggedIn(const BsClientLoginResult& result) +{ + if (loginDlg_) { + loginDlg_->onLoggedIn(result); } - - auto bsClient = createClient(); - - auto logger = logMgr_->logger("proxy"); - LoginWindow loginDialog(logger, bsClient, applicationSettings_, this); - - int rc = loginDialog.exec(); - if (rc != QDialog::Accepted && !loginDialog.result()) { + if (result.status != AutheIDClient::ErrorType::NoError) { + onLoggedOut(); return; } + bool isRegistered = (result.userType == bs::network::UserType::Market + || result.userType == bs::network::UserType::Trading + || result.userType == bs::network::UserType::Dealing); - bool isRegistered = (loginDialog.result()->userType == bs::network::UserType::Market - || loginDialog.result()->userType == bs::network::UserType::Trading - || loginDialog.result()->userType == bs::network::UserType::Dealing); - - if (!isRegistered && envType == ApplicationSettings::EnvConfiguration::Test) { - auto createTestAccountUrl = applicationSettings_->get(ApplicationSettings::GetAccount_UrlTest); + if (!isRegistered && envConfig_ == ApplicationSettings::EnvConfiguration::Test) { + auto createTestAccountUrl = tr("async retrieval of GetAccount_UrlTest"); BSMessageBox dlg(BSMessageBox::info, tr("Create Test Account") , tr("Create a BlockSettle test account") , tr("

Login requires a test account - create one in minutes on test.blocksettle.com

" - "

Once you have registered, return to login in the Terminal.

" - "Create Test Account Now") + "

Once you have registered, return to login in the Terminal.

" + "Create Test Account Now") .arg(createTestAccountUrl).arg(BSMessageBox::kUrlColor), this); dlg.setOkVisible(false); dlg.setCancelVisible(true); @@ -870,13 +878,13 @@ void MainWindow::onLoggedIn() return; } - if (!isRegistered && envType == ApplicationSettings::EnvConfiguration::Production) { - auto createAccountUrl = applicationSettings_->get(ApplicationSettings::GetAccount_UrlProd); + if (!isRegistered && envConfig_ == ApplicationSettings::EnvConfiguration::Production) { + auto createAccountUrl = tr("async retrieval of GetAccount_UrlProd"); BSMessageBox dlg(BSMessageBox::info, tr("Create Account") , tr("Create a BlockSettle account") , tr("

Login requires an account - create one in minutes on blocksettle.com

" - "

Once you have registered, return to login in the Terminal.

" - "Create Account Now") + "

Once you have registered, return to login in the Terminal.

" + "Create Account Now") .arg(createAccountUrl).arg(BSMessageBox::kUrlColor), this); dlg.setOkVisible(false); dlg.setCancelVisible(true); @@ -885,30 +893,78 @@ void MainWindow::onLoggedIn() return; } - networkSettingsReceived(networkSettings, NetworkSettingsClient::MarketData); + activateClient(result); +} + +void MainWindow::activateClient(const BsClientLoginResult& result) +{ + currentUserLogin_ = QString::fromStdString(result.login); +/* chatTokenData_ = result.chatTokenData; + chatTokenSign_ = result.chatTokenSign; + tryLoginIntoChat(); - activateClient(bsClient, *loginDialog.result(), loginDialog.email().toStdString()); -}*/ + bsClient_ = bsClient; + ccFileManager_->setBsClient(bsClient); + authAddrDlg_->setBsClient(bsClient);*/ -void MainWindow::onLoggedOut() + auto tradeSettings = std::make_shared(result.tradeSettings); + emit putSetting(ApplicationSettings::SubmittedAddressXbtLimit, static_cast(tradeSettings->xbtTier1Limit)); + +// authManager_->initLogin(celerConnection_, tradeSettings_); + emit bootstrapDataLoaded(result.bootstrapDataSigned); + + setLoginButtonText(currentUserLogin_); + setWidgetsAuthorized(true); + + emit setRecommendedFeeRate(result.feeRatePb); +// utxoReservationMgr_->setFeeRatePb(result.feeRatePb); +// celerConnection_->LoginToServer(bsClient_.get(), result.celerLogin, email); + + ui_->widgetWallets->setUsername(currentUserLogin_); + actLogout_->setVisible(false); + actLogin_->setEnabled(false); + + // Market data, charts and chat should be available for all Auth eID logins +// mdProvider_->SubscribeToMD(); + + accountEnabled_ = result.enabled; + onAccountTypeChanged(result.userType, result.enabled); +} + +void MainWindow::onAccountTypeChanged(bs::network::UserType userType, bool enabled) +{ +// userType_ = userType; + if ((accountEnabled_ != enabled) && (userType != bs::network::UserType::Chat)) { + notifCenter_->enqueue(enabled ? bs::ui::NotifyType::AccountEnabled + : bs::ui::NotifyType::AccountDisabled, {}); + } +// authManager_->setUserType(userType); + ui_->widgetChat->setUserType(enabled ? userType : bs::network::UserType::Chat); +} + +void bs::gui::qt::MainWindow::onLogoutInitiated() { ui_->widgetWallets->setUsername(QString()); -/* if (chatClientServicePtr_) { - chatClientServicePtr_->LogoutFromServer(); - }*/ + /* if (chatClientServicePtr_) { + chatClientServicePtr_->LogoutFromServer(); + }*/ ui_->widgetChart->disconnect(); + /* if (celerConnection_->IsConnected()) { + celerConnection_->CloseConnection(); + }*/ -/* if (celerConnection_->IsConnected()) { - celerConnection_->CloseConnection(); - }*/ - -// mdProvider_->UnsubscribeFromMD(); + // mdProvider_->UnsubscribeFromMD(); setLoginButtonText(loginButtonText_); setWidgetsAuthorized(false); -// bsClient_.reset(); + // bsClient_.reset(); +} + +void MainWindow::onLoggedOut() +{ + } void MainWindow::onUserLoggedIn() @@ -1094,7 +1150,7 @@ void MainWindow::setupShortcuts() void MainWindow::onButtonUserClicked() { if (ui_->pushButtonUser->text() == loginButtonText_) { - onLoggedIn(); + onLoginInitiated(); } else { if (BSMessageBox(BSMessageBox::question, tr("User Logout"), tr("You are about to logout") , tr("Do you want to continue?")).exec() == QDialog::Accepted) diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 63bf4a486..e21ad63cc 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -16,6 +16,7 @@ #include #include "Address.h" #include "ArmoryConnection.h" +#include "BsClient.h" #include "SignContainer.h" #include "Settings/SignersProvider.h" #include "UiUtils.h" @@ -37,6 +38,7 @@ class AboutDialog; class AuthAddressDialog; class ConfigDialog; class CreateTransactionDialog; +class LoginWindow; class NotificationCenter; class QSystemTrayIcon; class StatusBarView; @@ -86,6 +88,10 @@ namespace bs { void onArmoryServers(const QList&, int idxCur, int idxConn); void onSignerSettings(const QList&, const std::string& ownKey, int idxCur); + void onLoginStarted(const std::string &login, bool success, const std::string &errMsg); + void onLoggedIn(const BsClientLoginResult&); + void onAccountTypeChanged(bs::network::UserType userType, bool enabled); + public slots: void onReactivate(); void raiseWindow(); @@ -99,6 +105,7 @@ namespace bs { };*/ signals: + void getSettings(const std::vector &); void putSetting(ApplicationSettings::Setting, const QVariant &); void resetSettings(const std::vector &); void resetSettingsToState(const ApplicationSettings::State&); @@ -111,6 +118,7 @@ namespace bs { void needArmoryReconnect(); void needSigners(); void setSigner(int); + void bootstrapDataLoaded(const std::string &); void createNewWallet(); void needHDWalletDetails(const std::string &walletId); void needWalletsList(UiUtils::WalletsTypes, const std::string &id); @@ -137,6 +145,12 @@ namespace bs { void needBroadcastZC(const std::string& id, const BinaryData&); void needSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string& comment); + void needOpenBsConnection(); + void needStartLogin(const std::string& login); + void needCancelLogin(); + void needMatchingLogin(const std::string &mtchLogin, const std::string &bsLogin); + void setRecommendedFeeRate(float); + private slots: void onSend(); void onGenerateAddress(); @@ -146,8 +160,8 @@ namespace bs { // void openAccountInfoDialog(); // void openCCTokenDialog(); - void onLoggedIn(); - // void onLoginProceed(const NetworkSettings &networkSettings); + void onLoginInitiated(); + void onLogoutInitiated(); void onLoggedOut(); void onButtonUserClicked(); @@ -197,16 +211,18 @@ namespace bs { void addDeferredDialog(const std::function &); void processDeferredDialogs(); + void activateClient(const BsClientLoginResult&); + private: std::unique_ptr ui_; std::shared_ptr logger_; std::shared_ptr queue_; std::shared_ptr guiUser_, settingsUser_; - QAction *action_send_{ nullptr }; - QAction *action_generate_address_{ nullptr }; - QAction *action_login_{ nullptr }; - QAction *action_logout_{ nullptr }; + QAction *actSend_{ nullptr }; + QAction *actNewAddress_{ nullptr }; + QAction *actLogin_{ nullptr }; + QAction *actLogout_{ nullptr }; std::shared_ptr statusBarView_; std::shared_ptr sysTrayIcon_; @@ -219,10 +235,12 @@ namespace bs { std::shared_ptr txModel_; CreateTransactionDialog* txDlg_{ nullptr }; ConfigDialog* cfgDlg_{ nullptr }; + LoginWindow* loginDlg_{ nullptr }; // std::shared_ptr walletsWizard_; - QString currentUserLogin_; + bool accountEnabled_{ false }; + QString currentUserLogin_; QString loginButtonText_; QTimer * loginTimer_{}; @@ -230,6 +248,7 @@ namespace bs { bool initialWalletCreateDialogShown_ = false; bool deferCCsync_ = false; bool advTxDlgByDefault_{ false }; + ApplicationSettings::EnvConfiguration envConfig_{ ApplicationSettings::EnvConfiguration::Unknown }; std::queue> deferredDialogs_; bool deferredDialogRunning_ = false; diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 0b9243ee3..d4f6e03b8 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -24,6 +24,7 @@ #include #include "Address.h" #include "AppNap.h" +#include "BsClient.h" #include "BSMessageBox.h" #include "BSTerminalSplashScreen.h" #include "MainWindow.h" @@ -40,6 +41,7 @@ using namespace bs::message; Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult) Q_DECLARE_METATYPE(std::string) Q_DECLARE_METATYPE(std::vector) +Q_DECLARE_METATYPE(std::vector); #if defined (Q_OS_MAC) class MacOsApp : public QApplication @@ -118,6 +120,8 @@ QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) , userWallets_(std::make_shared(TerminalUsers::Wallets)) , userBlockchain_(std::make_shared(TerminalUsers::Blockchain)) , userSigner_(std::make_shared(TerminalUsers::Signer)) + , userBS_(std::make_shared(TerminalUsers::BsServer)) + , userMatch_(std::make_shared(TerminalUsers::Matching)) {} QtGuiAdapter::~QtGuiAdapter() @@ -190,6 +194,7 @@ void QtGuiAdapter::run(int &argc, char **argv) qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaType>(); + qRegisterMetaType>(); QString logoIcon; logoIcon = QLatin1String(":/SPLASH_LOGO"); @@ -234,6 +239,8 @@ bool QtGuiAdapter::process(const Envelope &env) return processSigner(env); case TerminalUsers::Wallets: return processWallets(env); + case TerminalUsers::BsServer: + return processBsServer(env); case TerminalUsers::AuthEid: return processAuthEid(env); case TerminalUsers::OnChainTracker: @@ -674,12 +681,18 @@ void QtGuiAdapter::requestInitialSettings() setReq->set_index(SetIdx_CloseToTray); setReq->set_type(SettingType_Bool); + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_Environment); + setReq->set_type(SettingType_Int); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } void QtGuiAdapter::makeMainWinConnections() { + connect(mainWindow_, &bs::gui::qt::MainWindow::getSettings, this, &QtGuiAdapter::onGetSettings); connect(mainWindow_, &bs::gui::qt::MainWindow::putSetting, this, &QtGuiAdapter::onPutSetting); connect(mainWindow_, &bs::gui::qt::MainWindow::resetSettings, this, &QtGuiAdapter::onResetSettings); connect(mainWindow_, &bs::gui::qt::MainWindow::resetSettingsToState, this, &QtGuiAdapter::onResetSettingsToState); @@ -692,6 +705,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needArmoryReconnect, this, &QtGuiAdapter::onNeedArmoryReconnect); connect(mainWindow_, &bs::gui::qt::MainWindow::needSigners, this, &QtGuiAdapter::onNeedSigners); connect(mainWindow_, &bs::gui::qt::MainWindow::setSigner, this, &QtGuiAdapter::onSetSigner); + connect(mainWindow_, &bs::gui::qt::MainWindow::bootstrapDataLoaded, this, &QtGuiAdapter::onBootstrapDataLoaded); connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); @@ -708,6 +722,24 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needSignTX, this, &QtGuiAdapter::onNeedSignTX); connect(mainWindow_, &bs::gui::qt::MainWindow::needBroadcastZC, this, &QtGuiAdapter::onNeedBroadcastZC); connect(mainWindow_, &bs::gui::qt::MainWindow::needSetTxComment, this, &QtGuiAdapter::onNeedSetTxComment); + connect(mainWindow_, &bs::gui::qt::MainWindow::needOpenBsConnection, this, &QtGuiAdapter::onNeedOpenBsConnection); + connect(mainWindow_, &bs::gui::qt::MainWindow::needStartLogin, this, &QtGuiAdapter::onNeedStartLogin); + connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelLogin, this, &QtGuiAdapter::onNeedCancelLogin); + connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogin, this, &QtGuiAdapter::onNeedMatchingLogin); + connect(mainWindow_, &bs::gui::qt::MainWindow::setRecommendedFeeRate, this, &QtGuiAdapter::onSetRecommendedFeeRate); +} + +void QtGuiAdapter::onGetSettings(const std::vector& settings) +{ + SettingsMessage msg; + auto msgReq = msg.mutable_get_request(); + for (const auto& setting : settings) { + auto setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(static_cast(setting)); + } + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); } void QtGuiAdapter::onPutSetting(ApplicationSettings::Setting idx, const QVariant &value) @@ -1043,6 +1075,52 @@ void QtGuiAdapter::onNeedSetTxComment(const std::string& walletId, const BinaryD pushFill(env); } +void QtGuiAdapter::onNeedOpenBsConnection() +{ + BsServerMessage msg; + msg.mutable_open_connection(); + Envelope env{ 0, user_, userBS_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedStartLogin(const std::string& login) +{ + BsServerMessage msg; + msg.set_start_login(login); + Envelope env{ 0, user_, userBS_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedCancelLogin() +{ + BsServerMessage msg; + msg.mutable_cancel_last_login(); + Envelope env{ 0, user_, userBS_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onBootstrapDataLoaded(const std::string& bsData) +{ + SettingsMessage msg; + msg.set_load_bootstrap(bsData); + Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedMatchingLogin(const std::string& mtchLogin, const std::string& bsLogin) +{ + MatchingMessage msg; + auto msgReq = msg.mutable_login(); + msgReq->set_matching_login(mtchLogin); + msgReq->set_terminal_login(bsLogin); + Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onSetRecommendedFeeRate(float) +{ +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -1294,4 +1372,53 @@ bool QtGuiAdapter::processZCInvalidated(const ArmoryMessage_ZCInvalidated& zcInv }); } +bool QtGuiAdapter::processBsServer(const bs::message::Envelope& env) +{ + BsServerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case BsServerMessage::kStartLoginResult: + return processStartLogin(msg.start_login_result()); + case BsServerMessage::kLoginResult: + return processLogin(msg.login_result()); + default: break; + } + return true; +} + +bool QtGuiAdapter::processStartLogin(const BsServerMessage_StartLoginResult& response) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, response] { + mainWindow_->onLoginStarted(response.login(), response.success() + , response.error_text()); + }); +} + +bool QtGuiAdapter::processLogin(const BsServerMessage_LoginResult& response) +{ + BsClientLoginResult result; + result.login = response.login(); + result.status = static_cast(response.status()); + result.userType = static_cast(response.user_type()); + result.errorMsg = response.error_text(); + result.celerLogin = response.celer_login(); + result.chatTokenData = BinaryData::fromString(response.chat_token()); + result.chatTokenSign = BinaryData::fromString(response.chat_token_signature()); + result.bootstrapDataSigned = response.bootstrap_signed_data(); + result.enabled = response.enabled(); + result.feeRatePb = response.fee_rate(); + result.tradeSettings.xbtTier1Limit = response.trade_settings().xbt_tier1_limit(); + result.tradeSettings.xbtPriceBand = response.trade_settings().xbt_price_band(); + result.tradeSettings.authRequiredSettledTrades = response.trade_settings().auth_reqd_settl_trades(); + result.tradeSettings.authSubmitAddressLimit = response.trade_settings().auth_submit_addr_limit(); + result.tradeSettings.dealerAuthSubmitAddressLimit = response.trade_settings().dealer_auth_submit_addr_limit(); + + return QMetaObject::invokeMethod(mainWindow_, [this, result] { + mainWindow_->onLoggedIn(result); + }); +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index e90cd8e5c..0222f6f57 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -40,6 +40,8 @@ namespace BlockSettle { class WalletsMessage_WalletsListResponse; } namespace Terminal { + class BsServerMessage_LoginResult; + class BsServerMessage_StartLoginResult; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; class SettingsMessage_SignerServers; @@ -102,7 +104,12 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived&); bool processZCInvalidated(const BlockSettle::Common::ArmoryMessage_ZCInvalidated&); + bool processBsServer(const bs::message::Envelope&); + bool processStartLogin(const BlockSettle::Terminal::BsServerMessage_StartLoginResult&); + bool processLogin(const BlockSettle::Terminal::BsServerMessage_LoginResult&); + private slots: + void onGetSettings(const std::vector&); void onPutSetting(ApplicationSettings::Setting, const QVariant &value); void onResetSettings(const std::vector&); void onResetSettingsToState(const ApplicationSettings::State&); @@ -136,11 +143,18 @@ private slots: void onNeedBroadcastZC(const std::string& id, const BinaryData&); void onNeedSetTxComment(const std::string& walletId, const BinaryData& txHash , const std::string& comment); + void onNeedOpenBsConnection(); + void onNeedStartLogin(const std::string& login); + void onNeedCancelLogin(); + void onBootstrapDataLoaded(const std::string&); + void onNeedMatchingLogin(const std::string& mtchLogin, const std::string& bsLogin); + void onSetRecommendedFeeRate(float); private: std::shared_ptr logger_; std::shared_ptr userSettings_, userWallets_; std::shared_ptr userBlockchain_, userSigner_; + std::shared_ptr userBS_, userMatch_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index d7ce4cc64..1eea845ad 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -462,7 +462,7 @@ class TestEnv std::shared_ptr assetMgr() { return assetMgr_; } std::shared_ptr blockMonitor() { return blockMonitor_; } std::shared_ptr connectionMgr() { return connMgr_; } - std::shared_ptr celerConnection() { return celerConn_; } + std::shared_ptr celerConnection() { return celerConn_; } std::shared_ptr logger() { return logger_; } std::shared_ptr walletsMgr() { return walletsMgr_; } std::shared_ptr mdProvider() { return mdProvider_; } @@ -477,7 +477,7 @@ class TestEnv std::shared_ptr appSettings_; std::shared_ptr assetMgr_; std::shared_ptr blockMonitor_; - std::shared_ptr celerConn_; + std::shared_ptr celerConn_; std::shared_ptr connMgr_; std::shared_ptr mdCallbacks_; std::shared_ptr mdProvider_; diff --git a/common b/common index 1682a9714..1a4a892dc 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1682a9714adc75f42113f2d2d0fd2645711fb40e +Subproject commit 1a4a892dcae518273670c2bcfc416d7da2545cec From 97519649d527ea473af5134d78f75a03fb3c7634 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 7 Oct 2020 21:49:07 +0300 Subject: [PATCH 018/146] proxy/matching login --- BlockSettleUILib/StatusBarView.cpp | 16 +-- BlockSettleUILib/StatusBarView.h | 6 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 11 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 2 +- Core/BsServerAdapter.cpp | 24 +++- Core/BsServerAdapter.h | 4 +- Core/MatchingAdapter.cpp | 65 +++++++--- Core/MatchingAdapter.h | 26 +++- Core/SignerAdapter.cpp | 8 ++ Core/SignerAdapter.h | 4 +- GUI/QtWidgets/MainWindow.cpp | 117 +++++++----------- GUI/QtWidgets/MainWindow.h | 13 +- GUI/QtWidgets/QtGuiAdapter.cpp | 51 ++++++++ GUI/QtWidgets/QtGuiAdapter.h | 6 + common | 2 +- 15 files changed, 239 insertions(+), 116 deletions(-) diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index d2d7aa8e5..8d5f733a7 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -89,9 +89,9 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportFinished, this, &StatusBarView::onWalletImportFinished); connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &StatusBarView::updateBalances); - connect(celerClient.get(), &CelerClientQt::OnConnectedToServer, this, &StatusBarView::onConnectedToServer); - connect(celerClient.get(), &CelerClientQt::OnConnectionClosed, this, &StatusBarView::onConnectionClosed); - connect(celerClient.get(), &CelerClientQt::OnConnectionError, this, &StatusBarView::onConnectionError); + connect(celerClient.get(), &CelerClientQt::OnConnectedToServer, this, &StatusBarView::onConnectedToMatching); + connect(celerClient.get(), &CelerClientQt::OnConnectionClosed, this, &StatusBarView::onDisconnectedFromMatching); + connect(celerClient.get(), &CelerClientQt::OnConnectionError, this, &StatusBarView::onMatchingConnError); // container might be null if user rejects remote signer key if (container) { @@ -102,7 +102,7 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory } onArmoryStateChanged(armory_->state(), armory_->topBlock()); - onConnectionClosed(); + onDisconnectedFromMatching(); setBalances(); containerStatusLabel_->setPixmap(iconContainerOffline_); @@ -166,7 +166,7 @@ StatusBarView::StatusBarView(QStatusBar *parent) SetLoggedOutStatus(); - onConnectionClosed(); + onDisconnectedFromMatching(); connectionStatusLabel_->setPixmap(iconContainerOffline_); containerStatusLabel_->setPixmap(iconContainerOffline_); @@ -528,17 +528,17 @@ void StatusBarView::SetCelerConnectingStatus() celerConnectionIconLabel_->setPixmap(iconCelerConnecting_); } -void StatusBarView::onConnectedToServer() +void StatusBarView::onConnectedToMatching() { SetLoggedinStatus(); } -void StatusBarView::onConnectionClosed() +void StatusBarView::onDisconnectedFromMatching() { SetLoggedOutStatus(); } -void StatusBarView::onConnectionError(int errorCode) +void StatusBarView::onMatchingConnError(int errorCode) { switch(errorCode) { diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index b11a10930..451172469 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -53,9 +53,9 @@ public slots: void onArmoryStateChanged(ArmoryState, unsigned int topBlock); void onArmoryProgress(BDMPhase, float progress, unsigned int secondsRem); void onArmoryError(QString); - void onConnectedToServer(); - void onConnectionClosed(); - void onConnectionError(int errorCode); + void onConnectedToMatching(); + void onDisconnectedFromMatching(); + void onMatchingConnError(int errorCode); void onContainerAuthorized(); void onSignerStatusChanged(SignContainer::ConnectionError error, const QString &details); void updateBalances(); //deprecated diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index e2c0d9c2e..68a263187 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -287,10 +287,15 @@ void RFQReplyWidget::onPulled(const std::string& settlementId, const std::string void RFQReplyWidget::onUserConnected(const bs::network::UserType &) { - const bool autoSigning = appSettings_->get(ApplicationSettings::AutoSigning); - const bool autoQuoting = appSettings_->get(ApplicationSettings::AutoQouting); + if (appSettings_) { + const bool autoSigning = appSettings_->get(ApplicationSettings::AutoSigning); + const bool autoQuoting = appSettings_->get(ApplicationSettings::AutoQouting); - ui_->widgetAutoSignQuote->onUserConnected(autoSigning, autoQuoting); + ui_->widgetAutoSignQuote->onUserConnected(autoSigning, autoQuoting); + } + else { + //TODO: query settings asynchronously + } } void RFQReplyWidget::onResetCurrentReservation(const std::shared_ptr &data) diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index c967591fa..a25d98a4c 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -499,7 +499,7 @@ void RFQRequestWidget::onMessageFromPB(const Blocksettle::Communication::ProxyTe void RFQRequestWidget::onUserConnected(const bs::network::UserType &ut) { - if (appSettings_->get(ApplicationSettings::AutoStartRFQScript)) { + if (appSettings_ && appSettings_->get(ApplicationSettings::AutoStartRFQScript)) { QTimer::singleShot(1000, [this] { // add some delay to allow initial sync of data ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->start( autoSignProvider_->getLastScript()); diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index b8c0a6af3..2db7c6226 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -31,7 +31,6 @@ BsServerAdapter::BsServerAdapter(const std::shared_ptr &logger) { connMgr_ = std::make_shared(logger_); connMgr_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); - bsClient_ = std::make_unique(logger_, this); } bool BsServerAdapter::process(const bs::message::Envelope &env) @@ -90,6 +89,9 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) switch (msg.data_case()) { case BsServerMessage::kOpenConnection: return processOpenConnection(); + case BsServerMessage::kCloseConnection: + bsClient_.reset(); + break; case BsServerMessage::kStartLogin: return processStartLogin(msg.start_login()); case BsServerMessage::kCancelLastLogin: @@ -98,6 +100,12 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) return processPuBKeyResponse(msg.pub_new_key_response()); case BsServerMessage::kTimeout: return processTimeout(msg.timeout()); + case BsServerMessage::kSendMatching: + if (bsClient_) { + bsClient_->celerSend(static_cast(msg.send_matching().message_type()) + , msg.send_matching().data()); + } + break; default: break; } return true; @@ -148,9 +156,10 @@ bool BsServerAdapter::processTimeout(const std::string& id) bool BsServerAdapter::processOpenConnection() { if (connected_) { - logger_->debug("[{}] already connected", __func__); + logger_->error("[{}] already connected", __func__); return true; } + bsClient_ = std::make_unique(logger_, this); bs::network::BIP15xParams params; params.ephemeralPeers = true; params.authMode = bs::network::BIP15xAuthMode::OneWay; @@ -269,6 +278,17 @@ void BsServerAdapter::onGetLoginResultDone(const BsClientLoginResult& result) pushFill(env); } +void BsServerAdapter::onCelerRecv(CelerAPI::CelerMessageType messageType, const std::string& data) +{ + BsServerMessage msg; + auto msgResp = msg.mutable_recv_matching(); + msgResp->set_message_type((int)messageType); + msgResp->set_data(data); + Envelope env{ 0, user_, bs::message::UserTerminal::create(bs::message::TerminalUsers::Matching) + , {}, {}, msg.SerializeAsString() }; // send directly to matching adapter, not broadcast + pushFill(env); +} + void BsServerAdapter::Connected() { connected_ = true; diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index 5623e29dc..6b95ea188 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -54,9 +54,9 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg void startTimer(std::chrono::milliseconds timeout, const std::function&) override; void onStartLoginDone(bool success, const std::string& errorMsg) override; void onGetLoginResultDone(const BsClientLoginResult& result) override; -/* void onAuthorizeDone(AuthorizeError authErr, const std::string& email) override; +// void onAuthorizeDone(AuthorizeError authErr, const std::string& email) override; void onCelerRecv(CelerAPI::CelerMessageType messageType, const std::string& data) override; - void onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response&) override;*/ +// void onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response&) override; void Connected() override; void Disconnected() override; void onConnectionFailed() override; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index d7598addd..364353d91 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -23,17 +23,12 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) : logger_(logger) , user_(std::make_shared(bs::message::TerminalUsers::Matching)) { -/* celerConnection_ = std::make_shared(logger); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectedToServer, this - , &MatchingAdapter::onCelerConnected, Qt::QueuedConnection); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectionClosed, this - , &MatchingAdapter::onCelerDisconnected, Qt::QueuedConnection); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectionError, this - , &MatchingAdapter::onCelerConnectionError, Qt::QueuedConnection);*/ + celerConnection_ = std::make_unique(logger, this, true, true); } -/*void MatchingAdapter::onCelerConnected() +void MatchingAdapter::connectedToServer() { + logger_->debug("[{}]", __func__); MatchingMessage msg; auto loggedIn = msg.mutable_logged_in(); loggedIn->set_user_type(static_cast(celerConnection_->celerUserType())); @@ -44,9 +39,10 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) pushFill(env); } -void MatchingAdapter::onCelerDisconnected() +void MatchingAdapter::connectionClosed() { celerConnection_->CloseConnection(); + logger_->debug("[{}]", __func__); MatchingMessage msg; msg.mutable_logged_out(); @@ -54,14 +50,15 @@ void MatchingAdapter::onCelerDisconnected() pushFill(env); } -void MatchingAdapter::onCelerConnectionError(int errorCode) +void MatchingAdapter::connectionError(int errorCode) { + logger_->debug("[{}] {}", __func__, errorCode); MatchingMessage msg; msg.set_connection_error(errorCode); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); } -*/ + bool MatchingAdapter::process(const bs::message::Envelope &env) { @@ -79,6 +76,20 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) pushFill(envBC); } } + else if (env.sender->value() == bs::message::TerminalUsers::BsServer) { + BsServerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse BsServer message #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case BsServerMessage::kRecvMatching: + celerConnection_->recvData(static_cast(msg.recv_matching().message_type()) + , msg.recv_matching().data()); + break; + default: break; + } + } else if (env.receiver && (env.receiver->value() == user_->value())) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { @@ -88,15 +99,41 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) switch (msg.data_case()) { case MatchingMessage::kLogin: return processLogin(msg.login()); + case MatchingMessage::kLogout: + if (celerConnection_ && celerConnection_->IsConnected()) { + celerConnection_->CloseConnection(); + } + break; default: - logger_->warn("[{}] unknown msg {} #{}", __func__, msg.data_case(), env.id); + logger_->warn("[{}] unknown msg {} #{} from {}", __func__, msg.data_case() + , env.id, env.sender->name()); break; } } return true; } -bool MatchingAdapter::processLogin(const BlockSettle::Terminal::MatchingMessage_Login&) +bool MatchingAdapter::processLogin(const MatchingMessage_Login& request) +{ + logger_->debug("[{}] {}", __func__, request.matching_login()); + return celerConnection_->SendLogin(request.matching_login(), request.terminal_login(), {}); +} + + +ClientCelerConnection::ClientCelerConnection(const std::shared_ptr& logger + , MatchingAdapter* parent, bool userIdRequired, bool useRecvTimer) + : BaseCelerClient(logger, parent, userIdRequired, useRecvTimer) + , parent_(parent) +{} + +void ClientCelerConnection::onSendData(CelerAPI::CelerMessageType messageType + , const std::string& data) { - return false; + BsServerMessage msg; + auto msgReq = msg.mutable_send_matching(); + msgReq->set_message_type((int)messageType); + msgReq->set_data(data); + Envelope env{ 0, parent_->user_, bs::message::UserTerminal::create(bs::message::TerminalUsers::BsServer) + , {}, {}, msg.SerializeAsString(), true }; + parent_->pushFill(env); } diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index fcbe02fba..c7554cbd4 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -11,6 +11,7 @@ #ifndef MATCHING_ADAPTER_H #define MATCHING_ADAPTER_H +#include "BaseCelerClient.h" #include "Message/Adapter.h" namespace spdlog { @@ -22,8 +23,25 @@ namespace BlockSettle { } } -class MatchingAdapter : public bs::message::Adapter +class MatchingAdapter; +class ClientCelerConnection : public BaseCelerClient { +public: + ClientCelerConnection(const std::shared_ptr& logger + , MatchingAdapter* parent, bool userIdRequired, bool useRecvTimer); + ~ClientCelerConnection() noexcept override = default; + +protected: + void onSendData(CelerAPI::CelerMessageType messageType, const std::string& data) override; + +private: + MatchingAdapter* parent_{ nullptr }; +}; + + +class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget +{ + friend class ClientCelerConnection; public: MatchingAdapter(const std::shared_ptr &); ~MatchingAdapter() override = default; @@ -36,11 +54,17 @@ class MatchingAdapter : public bs::message::Adapter std::string name() const override { return "Matching"; } private: + // CelerCallbackTarget overrides + void connectedToServer() override; + void connectionClosed() override; + void connectionError(int errorCode) override; + bool processLogin(const BlockSettle::Terminal::MatchingMessage_Login&); private: std::shared_ptr logger_; std::shared_ptr user_; + std::unique_ptr celerConnection_; }; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 048e4db1c..7b09ae0ac 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -122,6 +122,9 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processDelHdLeaf(request.del_hd_leaf()); case SignerMessage::kSignTxRequest: return processSignTx(env, request.sign_tx_request()); + case SignerMessage::kSetUserId: + return processSetUserId(request.set_user_id().user_id() + , request.set_user_id().wallet_id()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -639,3 +642,8 @@ bool SignerAdapter::processSignTx(const bs::message::Envelope& env , request.keep_dup_recips()); return true; } + +bool SignerAdapter::processSetUserId(const std::string& userId, const std::string& walletId) +{ + return (signer_->setUserId(BinaryData::fromString(userId), walletId) != 0); +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index ae8020f11..96744a232 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -51,9 +51,6 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget std::unique_ptr createClient() const; -protected: - - private: void start(); @@ -93,6 +90,7 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget bool processDelHdLeaf(const std::string &walletId); bool processSignTx(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_SignTxRequest&); + bool processSetUserId(const std::string& userId, const std::string& walletId); private: std::shared_ptr logger_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 7bdd6926d..27d393a9d 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -76,18 +76,15 @@ MainWindow::MainWindow(const std::shared_ptr &logger setupToolbar(); setupMenu(); - ui_->widgetTransactions->setEnabled(false); - initChartsView(); -// ui_->tabWidget->setCurrentIndex(settings->get(ApplicationSettings::GUI_main_tab)); - updateAppearance(); -// setWidgetsAuthorized(false); - - updateControlEnabledState(); + setWidgetsAuthorized(false); initWidgets(); + + ui_->widgetTransactions->setEnabled(false); + actSend_->setEnabled(false); } void MainWindow::setWidgetsAuthorized(bool authorized) @@ -213,6 +210,8 @@ void MainWindow::onNewBlock(int state, unsigned int blockNum) void MainWindow::onWalletsReady() { logger_->debug("[{}]", __func__); + ui_->widgetTransactions->setEnabled(true); + actSend_->setEnabled(true); emit needLedgerEntries({}); } @@ -547,31 +546,6 @@ void MainWindow::setupInfoWidget() }); } -void MainWindow::updateControlEnabledState() -{ -/* if (action_send_) { - action_send_->setEnabled(!walletsMgr_->hdWallets().empty() - && armory_->isOnline() && signContainer_ && signContainer_->isReady()); - }*/ - // Do not allow login until wallets synced (we need to check if user has primary wallet or not). - // Should be OK for both local and remote signer. -// ui_->pushButtonUser->setEnabled(walletsSynched_ && loginApiKey().empty()); -} - -/*void MainWindow::initPortfolioView() -{ - portfolioModel_ = std::make_shared(walletsMgr_, assetManager_, this); - ui_->widgetPortfolio->init(applicationSettings_, mdProvider_, mdCallbacks_ - , portfolioModel_, signContainer_, armory_, utxoReservationMgr_, logMgr_->logger("ui"), walletsMgr_); -} - -void MainWindow::initWalletsView() -{ - ui_->widgetWallets->init(logMgr_->logger("ui"), walletsMgr_, signContainer_ - , applicationSettings_, connectionManager_, assetManager_, authManager_, armory_); - connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &BSTerminalMainWindow::onInitWalletDialogWasShown); -}*/ - void MainWindow::initChartsView() { /* ui_->widgetChart->init(applicationSettings_, mdProvider_, mdCallbacks_ @@ -771,7 +745,7 @@ void MainWindow::setupMenu() connect(ui_->actionVideoTutorials, &QAction::triggered, supportDlgCb(1, QObject::tr("Video Tutorials"))); connect(ui_->actionContact, &QAction::triggered, supportDlgCb(2, QObject::tr("Support"))); - onUserLoggedOut(); + onMatchingLogout(); #ifndef Q_OS_MAC ui_->horizontalFrame->hide(); @@ -918,7 +892,7 @@ void MainWindow::activateClient(const BsClientLoginResult& result) emit setRecommendedFeeRate(result.feeRatePb); // utxoReservationMgr_->setFeeRatePb(result.feeRatePb); -// celerConnection_->LoginToServer(bsClient_.get(), result.celerLogin, email); + emit needMatchingLogin(result.celerLogin, result.login); ui_->widgetWallets->setUsername(currentUserLogin_); actLogout_->setVisible(false); @@ -942,6 +916,30 @@ void MainWindow::onAccountTypeChanged(bs::network::UserType userType, bool enabl ui_->widgetChat->setUserType(enabled ? userType : bs::network::UserType::Chat); } +void bs::gui::qt::MainWindow::onMatchingLogin(const std::string& mtchLogin + , BaseCelerClient::CelerUserType userType, const std::string& userId) +{ + emit needSetUserId(userId); + + ui_->actionAccountInformation->setEnabled(true); + ui_->actionAuthenticationAddresses->setEnabled(userType != BaseCelerClient::CelerUserType::Market); + ui_->actionOneTimePassword->setEnabled(true); + ui_->actionEnterColorCoinToken->setEnabled(true); + + ui_->actionDeposits->setEnabled(true); + ui_->actionWithdrawalRequest->setEnabled(true); + ui_->actionLinkAdditionalBankAccount->setEnabled(true); + + actLogin_->setVisible(false); + actLogout_->setVisible(true); + + // ccFileManager_->ConnectToCelerClient(celerConnection_); + ui_->widgetRFQ->onUserConnected(userType); + ui_->widgetRFQReply->onUserConnected(userType); + + statusBarView_->onConnectedToMatching(); +} + void bs::gui::qt::MainWindow::onLogoutInitiated() { ui_->widgetWallets->setUsername(QString()); @@ -958,43 +956,19 @@ void bs::gui::qt::MainWindow::onLogoutInitiated() setLoginButtonText(loginButtonText_); setWidgetsAuthorized(false); - - // bsClient_.reset(); } void MainWindow::onLoggedOut() { - + currentUserLogin_.clear(); + emit needMatchingLogout(); } -void MainWindow::onUserLoggedIn() +void MainWindow::onMatchingLogout() { - ui_->actionAccountInformation->setEnabled(true); -/* ui_->actionAuthenticationAddresses->setEnabled(celerConnection_->celerUserType() - != BaseCelerClient::CelerUserType::Market);*/ - ui_->actionOneTimePassword->setEnabled(true); - ui_->actionEnterColorCoinToken->setEnabled(true); - - ui_->actionDeposits->setEnabled(true); - ui_->actionWithdrawalRequest->setEnabled(true); - ui_->actionLinkAdditionalBankAccount->setEnabled(true); + emit needSetUserId({}); + emit needCloseBsConnection(); -// ccFileManager_->ConnectToCelerClient(celerConnection_); -// ui_->widgetRFQ->onUserConnected(userType_); -// ui_->widgetRFQReply->onUserConnected(userType_); - -// const auto userId = BinaryData::CreateFromHex(celerConnection_->userId()); -/* const auto &deferredDialog = [this, userId] { - walletsMgr_->setUserId(userId); - promoteToPrimaryIfNeeded(); - }; - addDeferredDialog(deferredDialog);*/ - - setLoginButtonText(currentUserLogin_); -} - -void MainWindow::onUserLoggedOut() -{ ui_->actionAccountInformation->setEnabled(false); ui_->actionAuthenticationAddresses->setEnabled(false); ui_->actionEnterColorCoinToken->setEnabled(false); @@ -1004,20 +978,16 @@ void MainWindow::onUserLoggedOut() ui_->actionWithdrawalRequest->setEnabled(false); ui_->actionLinkAdditionalBankAccount->setEnabled(false); -/* if (walletsMgr_) { - walletsMgr_->setUserId(BinaryData{}); - } - if (authManager_) { - authManager_->OnDisconnectedFromCeler(); - }*/ + actLogin_->setVisible(true); + actLogin_->setEnabled(true); + actLogout_->setVisible(false); + statusBarView_->onDisconnectedFromMatching(); setLoginButtonText(loginButtonText_); } /*void BSTerminalMainWindow::onCelerConnected() { - action_login_->setVisible(false); - action_logout_->setVisible(true); onUserLoggedIn(); } @@ -1189,7 +1159,10 @@ void MainWindow::initWidgets() connect(ui_->widgetExplorer, &ExplorerWidget::needTXDetails, this, &MainWindow::needTXDetails); initTransactionsView(); -// InitPortfolioView(); + + /* portfolioModel_ = std::make_shared(walletsMgr_, assetManager_, this); + ui_->widgetPortfolio->init(applicationSettings_, mdProvider_, mdCallbacks_ + , portfolioModel_, signContainer_, armory_, utxoReservationMgr_, logMgr_->logger("ui"), walletsMgr_);*/ // ui_->widgetRFQ->initWidgets(mdProvider_, mdCallbacks_, applicationSettings_); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index e21ad63cc..93101c4f2 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -16,6 +16,7 @@ #include #include "Address.h" #include "ArmoryConnection.h" +#include "BaseCelerClient.h" #include "BsClient.h" #include "SignContainer.h" #include "Settings/SignersProvider.h" @@ -91,6 +92,9 @@ namespace bs { void onLoginStarted(const std::string &login, bool success, const std::string &errMsg); void onLoggedIn(const BsClientLoginResult&); void onAccountTypeChanged(bs::network::UserType userType, bool enabled); + void onMatchingLogin(const std::string& mtchLogin, BaseCelerClient::CelerUserType + , const std::string &userId); + void onMatchingLogout(); public slots: void onReactivate(); @@ -146,9 +150,12 @@ namespace bs { void needSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string& comment); void needOpenBsConnection(); + void needCloseBsConnection(); void needStartLogin(const std::string& login); void needCancelLogin(); void needMatchingLogin(const std::string &mtchLogin, const std::string &bsLogin); + void needMatchingLogout(); + void needSetUserId(const std::string&); void setRecommendedFeeRate(float); private slots: @@ -181,11 +188,6 @@ namespace bs { void changeEvent(QEvent *) override; private: - void onUserLoggedIn(); - void onUserLoggedOut(); - -// void onAccountTypeChanged(bs::network::UserType userType, bool enabled); - void setLoginButtonText(const QString& text); void setupShortcuts(); @@ -196,7 +198,6 @@ namespace bs { void setupTopRightWidget(); void updateAppearance(); - void updateControlEnabledState(); void setWidgetsAuthorized(bool); void initWidgets(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index d4f6e03b8..81e24b521 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -241,6 +241,8 @@ bool QtGuiAdapter::process(const Envelope &env) return processWallets(env); case TerminalUsers::BsServer: return processBsServer(env); + case TerminalUsers::Matching: + return processMatching(env); case TerminalUsers::AuthEid: return processAuthEid(env); case TerminalUsers::OnChainTracker: @@ -723,9 +725,12 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needBroadcastZC, this, &QtGuiAdapter::onNeedBroadcastZC); connect(mainWindow_, &bs::gui::qt::MainWindow::needSetTxComment, this, &QtGuiAdapter::onNeedSetTxComment); connect(mainWindow_, &bs::gui::qt::MainWindow::needOpenBsConnection, this, &QtGuiAdapter::onNeedOpenBsConnection); + connect(mainWindow_, &bs::gui::qt::MainWindow::needCloseBsConnection, this, &QtGuiAdapter::onNeedCloseBsConnection); connect(mainWindow_, &bs::gui::qt::MainWindow::needStartLogin, this, &QtGuiAdapter::onNeedStartLogin); connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelLogin, this, &QtGuiAdapter::onNeedCancelLogin); connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogin, this, &QtGuiAdapter::onNeedMatchingLogin); + connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogout, this, &QtGuiAdapter::onNeedMatchingLogout); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSetUserId, this, &QtGuiAdapter::onNeedSetUserId); connect(mainWindow_, &bs::gui::qt::MainWindow::setRecommendedFeeRate, this, &QtGuiAdapter::onSetRecommendedFeeRate); } @@ -1083,6 +1088,14 @@ void QtGuiAdapter::onNeedOpenBsConnection() pushFill(env); } +void QtGuiAdapter::onNeedCloseBsConnection() +{ + BsServerMessage msg; + msg.mutable_close_connection(); + Envelope env{ 0, user_, userBS_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::onNeedStartLogin(const std::string& login) { BsServerMessage msg; @@ -1117,6 +1130,22 @@ void QtGuiAdapter::onNeedMatchingLogin(const std::string& mtchLogin, const std:: pushFill(env); } +void QtGuiAdapter::onNeedMatchingLogout() +{ + MatchingMessage msg; + msg.mutable_logout(); + Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedSetUserId(const std::string& userId) +{ + WalletsMessage msg; + msg.set_set_user_id(userId); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::onSetRecommendedFeeRate(float) { } @@ -1421,4 +1450,26 @@ bool QtGuiAdapter::processLogin(const BsServerMessage_LoginResult& response) }); } +bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) +{ + MatchingMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MatchingMessage::kLoggedIn: + return QMetaObject::invokeMethod(mainWindow_, [this, response=msg.logged_in()] { + mainWindow_->onMatchingLogin(response.user_name() + , static_cast(response.user_type()), response.user_id()); + }); + case MatchingMessage::kLoggedOut: + return QMetaObject::invokeMethod(mainWindow_, [this] { + mainWindow_->onMatchingLogout(); + }); + default: break; + } + return true; +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 0222f6f57..f0c77315a 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -42,6 +42,7 @@ namespace BlockSettle { namespace Terminal { class BsServerMessage_LoginResult; class BsServerMessage_StartLoginResult; + class MatchingMessage_LoggedIn; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; class SettingsMessage_SignerServers; @@ -108,6 +109,8 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processStartLogin(const BlockSettle::Terminal::BsServerMessage_StartLoginResult&); bool processLogin(const BlockSettle::Terminal::BsServerMessage_LoginResult&); + bool processMatching(const bs::message::Envelope&); + private slots: void onGetSettings(const std::vector&); void onPutSetting(ApplicationSettings::Setting, const QVariant &value); @@ -144,10 +147,13 @@ private slots: void onNeedSetTxComment(const std::string& walletId, const BinaryData& txHash , const std::string& comment); void onNeedOpenBsConnection(); + void onNeedCloseBsConnection(); void onNeedStartLogin(const std::string& login); void onNeedCancelLogin(); void onBootstrapDataLoaded(const std::string&); void onNeedMatchingLogin(const std::string& mtchLogin, const std::string& bsLogin); + void onNeedMatchingLogout(); + void onNeedSetUserId(const std::string&); void onSetRecommendedFeeRate(float); private: diff --git a/common b/common index 1a4a892dc..de9ca169a 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1a4a892dcae518273670c2bcfc416d7da2545cec +Subproject commit de9ca169a91f43833e5264de706372057363234b From 2491c16b4ab4abd301fba97081aec2ebdcc47a32 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 9 Oct 2020 22:32:16 +0300 Subject: [PATCH 019/146] MD prices --- BlockSettleUILib/PortfolioWidget.cpp | 6 + BlockSettleUILib/PortfolioWidget.h | 4 +- BlockSettleUILib/Trading/MarketDataModel.cpp | 3 +- BlockSettleUILib/Trading/MarketDataModel.h | 3 +- BlockSettleUILib/Trading/MarketDataWidget.cpp | 42 ++++++ BlockSettleUILib/Trading/MarketDataWidget.h | 12 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 10 ++ BlockSettleUILib/Trading/RFQRequestWidget.h | 9 +- Core/MktDataAdapter.cpp | 133 +++++++++++++++++- Core/MktDataAdapter.h | 18 ++- GUI/QtWidgets/MainWindow.cpp | 18 +-- GUI/QtWidgets/MainWindow.h | 4 + GUI/QtWidgets/QtGuiAdapter.cpp | 44 ++++++ GUI/QtWidgets/QtGuiAdapter.h | 6 +- common | 2 +- 15 files changed, 288 insertions(+), 26 deletions(-) diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index c47961a93..642e22939 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -114,6 +114,12 @@ void PortfolioWidget::setAuthorized(bool authorized) ui_->widgetMarketData->setAuthorized(authorized); } +void PortfolioWidget::onMDUpdated(bs::network::Asset::Type assetType + , const QString& security, const bs::network::MDFields& fields) +{ + ui_->widgetMarketData->onMDUpdated(assetType, security, fields); +} + void PortfolioWidget::showTransactionDetails(const QModelIndex& index) { if (filter_) { diff --git a/BlockSettleUILib/PortfolioWidget.h b/BlockSettleUILib/PortfolioWidget.h index da63903f7..3dc2c5b98 100644 --- a/BlockSettleUILib/PortfolioWidget.h +++ b/BlockSettleUILib/PortfolioWidget.h @@ -14,7 +14,7 @@ #include #include #include - +#include "CommonTypes.h" #include "TransactionsWidgetInterface.h" namespace spdlog { @@ -64,6 +64,8 @@ Q_OBJECT void shortcutActivated(ShortcutType s) override; void setAuthorized(bool authorized); + void onMDUpdated(bs::network::Asset::Type, const QString& security + , const bs::network::MDFields&); private slots: void showTransactionDetails(const QModelIndex& index); diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index 6f4477fe4..8b7526f2b 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -156,7 +156,8 @@ bool MarketDataModel::isVisible(const QString &id) const return false; } -void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType, const QString &security, bs::network::MDFields mdFields) +void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType + , const QString &security, const bs::network::MDFields &mdFields) { if ((assetType == bs::network::Asset::Undefined) && security.isEmpty()) { // Celer disconnected priceUpdates_.clear(); diff --git a/BlockSettleUILib/Trading/MarketDataModel.h b/BlockSettleUILib/Trading/MarketDataModel.h index a3bfd4867..91637169e 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.h +++ b/BlockSettleUILib/Trading/MarketDataModel.h @@ -61,7 +61,8 @@ Q_OBJECT QStringList getVisibilitySettings() const; public slots: - void onMDUpdated(bs::network::Asset::Type, const QString &security, bs::network::MDFields); + void onMDUpdated(bs::network::Asset::Type, const QString &security + , const bs::network::MDFields &); void onVisibilityToggled(bool filtered); signals: diff --git a/BlockSettleUILib/Trading/MarketDataWidget.cpp b/BlockSettleUILib/Trading/MarketDataWidget.cpp index 1a1018578..9842edd06 100644 --- a/BlockSettleUILib/Trading/MarketDataWidget.cpp +++ b/BlockSettleUILib/Trading/MarketDataWidget.cpp @@ -34,6 +34,36 @@ MarketDataWidget::MarketDataWidget(QWidget* parent) , mdSortFilterModel_(nullptr) { ui_->setupUi(this); + + marketDataModel_ = new MarketDataModel({}, ui_->treeViewMarketData); + mdSortFilterModel_ = new MDSortFilterProxyModel(ui_->treeViewMarketData); + mdSortFilterModel_->setSourceModel(marketDataModel_); + + ui_->treeViewMarketData->setModel(mdSortFilterModel_); + ui_->treeViewMarketData->setSortingEnabled(true); + + ui_->treeViewMarketData->setHeader(mdHeader_.get()); + ui_->treeViewMarketData->header()->setSortIndicator(static_cast(MarketDataModel::MarketDataColumns::First) + , Qt::AscendingOrder); + ui_->treeViewMarketData->header()->resizeSection(static_cast(MarketDataModel::MarketDataColumns::EmptyColumn) + , EMPTY_COLUMN_WIDTH); + + connect(marketDataModel_, &QAbstractItemModel::rowsInserted, [this]() { + if (mdHeader_ != nullptr) { + mdHeader_->setEnabled(true); + } + }); + connect(mdSortFilterModel_, &QAbstractItemModel::rowsInserted, this, &MarketDataWidget::resizeAndExpand); + connect(marketDataModel_, &MarketDataModel::needResize, this, &MarketDataWidget::resizeAndExpand); + + connect(ui_->treeViewMarketData, &QTreeView::clicked, this, &MarketDataWidget::clicked); + connect(ui_->treeViewMarketData->selectionModel(), &QItemSelectionModel::currentChanged + , this, &MarketDataWidget::onSelectionChanged); + + connect(ui_->pushButtonMDConnection, &QPushButton::clicked, this + , &MarketDataWidget::ChangeMDSubscriptionState); + + ui_->pushButtonMDConnection->setText(tr("Subscribe")); } MarketDataWidget::~MarketDataWidget() @@ -51,6 +81,12 @@ void MarketDataWidget::init(const std::shared_ptr &appSetti visSettings = appSettings->get(settingVisibility_); appSettings_ = appSettings; } + if (marketDataModel_) { + marketDataModel_->deleteLater(); + } + if (mdSortFilterModel_) { + mdSortFilterModel_->deleteLater(); + } marketDataModel_ = new MarketDataModel(visSettings, ui_->treeViewMarketData); mdSortFilterModel_ = new MDSortFilterProxyModel(ui_->treeViewMarketData); mdSortFilterModel_->setSourceModel(marketDataModel_); @@ -181,6 +217,12 @@ MarketSelectedInfo MarketDataWidget::getCurrentlySelectedInfo() const return getRowInfo(index); } +void MarketDataWidget::onMDUpdated(bs::network::Asset::Type assetType + , const QString& security, const bs::network::MDFields& fields) +{ + marketDataModel_->onMDUpdated(assetType, security, fields); +} + void MarketDataWidget::onMDRejected(const std::string &security, const std::string &reason) { if (security.empty()) { diff --git a/BlockSettleUILib/Trading/MarketDataWidget.h b/BlockSettleUILib/Trading/MarketDataWidget.h index 6fc760c43..3ef166914 100644 --- a/BlockSettleUILib/Trading/MarketDataWidget.h +++ b/BlockSettleUILib/Trading/MarketDataWidget.h @@ -15,6 +15,7 @@ #include #include #include "ApplicationSettings.h" +#include "CommonTypes.h" namespace Ui { @@ -45,10 +46,10 @@ class MarketDataWidget : public QWidget Q_OBJECT public: - MarketDataWidget(QWidget* parent = nullptr ); + MarketDataWidget(QWidget* parent = nullptr); ~MarketDataWidget() override; - void init(const std::shared_ptr &appSettings, ApplicationSettings::Setting paramVis + [[deprecated]] void init(const std::shared_ptr &appSettings, ApplicationSettings::Setting paramVis , const std::shared_ptr &, const std::shared_ptr &); TreeViewWithEnterKey* view() const; @@ -56,6 +57,9 @@ Q_OBJECT void setAuthorized(bool authorized); MarketSelectedInfo getCurrentlySelectedInfo() const; + void onMDUpdated(bs::network::Asset::Type, const QString& security + , const bs::network::MDFields&); + signals: void CurrencySelected(const MarketSelectedInfo& selectedInfo); void AskClicked(const MarketSelectedInfo& selectedInfo); @@ -84,8 +88,8 @@ private slots: private: std::unique_ptr ui_; - MarketDataModel * marketDataModel_; - MDSortFilterProxyModel * mdSortFilterModel_; + MarketDataModel* marketDataModel_{ nullptr }; + MDSortFilterProxyModel* mdSortFilterModel_{ nullptr }; std::shared_ptr appSettings_; ApplicationSettings::Setting settingVisibility_; std::shared_ptr mdHeader_; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index a25d98a4c..97825f6a3 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -161,6 +161,12 @@ void RFQRequestWidget::setAuthorized(bool authorized) ui_->widgetMarketData->setAuthorized(authorized); } +void RFQRequestWidget::onMDUpdated(bs::network::Asset::Type assetType + , const QString& security, const bs::network::MDFields &fields) +{ + ui_->widgetMarketData->onMDUpdated(assetType, security, fields); +} + void RFQRequestWidget::hideEvent(QHideEvent* event) { ui_->pageRFQTicket->onParentAboutToHide(); @@ -261,6 +267,10 @@ void RFQRequestWidget::init(const std::shared_ptr &logger connect(authAddressManager_.get(), &AuthAddressManager::AddressListUpdated, this, &RFQRequestWidget::forceCheckCondition); } +void RFQRequestWidget::init(const std::shared_ptr&, const std::shared_ptr&, OrderListModel* orderListModel) +{ +} + void RFQRequestWidget::onConnectedToCeler() { marketDataConnection.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::CurrencySelected, diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 0a0b66de4..af0f17d9e 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -64,7 +64,7 @@ Q_OBJECT RFQRequestWidget(QWidget* parent = nullptr); ~RFQRequestWidget() override; - void initWidgets(const std::shared_ptr & + [[deprecated]] void initWidgets(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &); @@ -80,12 +80,19 @@ Q_OBJECT , const std::shared_ptr & , OrderListModel *orderListModel); + void init(const std::shared_ptr& + , const std::shared_ptr& + , OrderListModel* orderListModel); + void setWalletsManager(const std::shared_ptr &); void shortcutActivated(ShortcutType s) override; void setAuthorized(bool authorized); + void onMDUpdated(bs::network::Asset::Type, const QString& security + , const bs::network::MDFields &); + protected: void hideEvent(QHideEvent* event) override; bool eventFilter(QObject* sender, QEvent* event) override; diff --git a/Core/MktDataAdapter.cpp b/Core/MktDataAdapter.cpp index b182f813a..fec9b2c76 100644 --- a/Core/MktDataAdapter.cpp +++ b/Core/MktDataAdapter.cpp @@ -12,6 +12,8 @@ #include #include "BSMarketDataProvider.h" #include "ConnectionManager.h" +#include "PubKeyLoader.h" +#include "SslCaBundle.h" #include "TerminalMessage.h" #include "terminal.pb.h" @@ -25,6 +27,7 @@ MktDataAdapter::MktDataAdapter(const std::shared_ptr &logger) , user_(std::make_shared(bs::message::TerminalUsers::MktData)) { auto connMgr = std::make_shared(logger_); + connMgr->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); mdProvider_ = std::make_shared(connMgr, logger_, this , true, false); } @@ -45,15 +48,141 @@ bool MktDataAdapter::process(const bs::message::Envelope &env) pushFill(envBC); } } + else if (env.receiver && (env.receiver->value() == user_->value())) { + MktDataMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse own request #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MktDataMessage::kStartConnection: + return processStartConnection(msg.start_connection()); + default: + logger_->warn("[{}] unknown md request {}", __func__, msg.data_case()); + break; + } + } return true; } void MktDataAdapter::userWantsToConnect() { - + logger_->debug("[{}]", __func__); } void MktDataAdapter::waitingForConnectionDetails() { - //TODO: request remote settings + logger_->debug("[{}]", __func__); +} + +void MktDataAdapter::connected() +{ + connected_ = true; + MktDataMessage msg; + msg.mutable_connected(); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); +} + +void MktDataAdapter::disconnected() +{ + connected_ = false; + MktDataMessage msg; + msg.mutable_disconnected(); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); +} + +void MktDataAdapter::onMDUpdate(bs::network::Asset::Type at, const std::string& name + , bs::network::MDFields fields) +{ + MktDataMessage msg; + auto msgPrices = msg.mutable_price_update(); + auto msgSecurity = msgPrices->mutable_security(); + msgSecurity->set_name(name); + msgSecurity->set_asset_type((int)at); + for (const auto& field : fields) { + switch (field.type) { + case bs::network::MDField::PriceOffer: + msgPrices->set_ask(field.value); + break; + case bs::network::MDField::PriceBid: + msgPrices->set_bid(field.value); + break; + case bs::network::MDField::PriceLast: + msgPrices->set_last(field.value); + break; + case bs::network::MDField::DailyVolume: + msgPrices->set_volume(field.value); + break; + case bs::network::MDField::MDTimestamp: + msgPrices->set_timestamp((uint64_t)field.value); + break; + default: break; + } + } + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); +} + +void MktDataAdapter::onMDSecurityReceived(const std::string& name, const bs::network::SecurityDef& sd) +{ + MktDataMessage msg; + auto msgBC = msg.mutable_new_security(); + msgBC->set_name(name); + msgBC->set_asset_type((int)sd.assetType); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); +} + +void MktDataAdapter::allSecuritiesReceived() +{ + logger_->debug("[{}]", __func__); +} + +void MktDataAdapter::onNewFXTrade(const bs::network::NewTrade& trade) +{ + sendTrade(trade); +} + +void MktDataAdapter::onNewXBTTrade(const bs::network::NewTrade& trade) +{ + sendTrade(trade); +} + +void MktDataAdapter::onNewPMTrade(const bs::network::NewPMTrade& trade) +{ + MktDataMessage msg; + auto msgTrade = msg.mutable_trade(); + msgTrade->set_product(trade.product); + msgTrade->set_price(trade.price); + msgTrade->set_amount(trade.amount); + msgTrade->set_timestamp(trade.timestamp); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); +} + +void MktDataAdapter::sendTrade(const bs::network::NewTrade& trade) +{ + MktDataMessage msg; + auto msgTrade = msg.mutable_trade(); + msgTrade->set_product(trade.product); + msgTrade->set_price(trade.price); + msgTrade->set_amount(trade.amount); + msgTrade->set_timestamp(trade.timestamp); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); +} + +bool MktDataAdapter::processStartConnection(int e) +{ + if (connected_) { + logger_->debug("[{}] already connected", __func__); + return true; + } + const auto env = static_cast(e); + mdProvider_->SetConnectionSettings(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::MdServer, env) + , PubKeyLoader::serverHttpsPort()); + mdProvider_->MDLicenseAccepted(); + return true; } diff --git a/Core/MktDataAdapter.h b/Core/MktDataAdapter.h index 819ebc398..de360898d 100644 --- a/Core/MktDataAdapter.h +++ b/Core/MktDataAdapter.h @@ -32,16 +32,32 @@ class MktDataAdapter : public bs::message::Adapter, public MDCallbackTarget } std::string name() const override { return "MktData"; } -protected: +protected: //MD callbacks override void userWantsToConnect() override; void waitingForConnectionDetails() override; + void connected() override; + void disconnected() override; + + void onMDUpdate(bs::network::Asset::Type, const std::string& + , bs::network::MDFields) override; + void onMDSecurityReceived(const std::string& + , const bs::network::SecurityDef&) override; + void allSecuritiesReceived() override; + + void onNewFXTrade(const bs::network::NewTrade&) override; + void onNewXBTTrade(const bs::network::NewTrade&) override; + void onNewPMTrade(const bs::network::NewPMTrade&) override; + private: + void sendTrade(const bs::network::NewTrade&); + bool processStartConnection(int env); private: std::shared_ptr logger_; std::shared_ptr user_; std::shared_ptr mdProvider_; + bool connected_{ false }; }; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 27d393a9d..537418967 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -920,6 +920,7 @@ void bs::gui::qt::MainWindow::onMatchingLogin(const std::string& mtchLogin , BaseCelerClient::CelerUserType userType, const std::string& userId) { emit needSetUserId(userId); + emit needMdConnection(envConfig_); ui_->actionAccountInformation->setEnabled(true); ui_->actionAuthenticationAddresses->setEnabled(userType != BaseCelerClient::CelerUserType::Market); @@ -986,22 +987,13 @@ void MainWindow::onMatchingLogout() setLoginButtonText(loginButtonText_); } -/*void BSTerminalMainWindow::onCelerConnected() +void MainWindow::onMDUpdated(bs::network::Asset::Type assetType + , const QString& security, const bs::network::MDFields &fields) { - - onUserLoggedIn(); + ui_->widgetRFQ->onMDUpdated(assetType, security, fields); + ui_->widgetPortfolio->onMDUpdated(assetType, security, fields); } -void BSTerminalMainWindow::onCelerDisconnected() -{ - action_logout_->setVisible(false); - action_login_->setEnabled(true); - action_login_->setVisible(true); - - onUserLoggedOut(); - celerConnection_->CloseConnection(); -}*/ - void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 93101c4f2..7faed83b4 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -96,6 +96,9 @@ namespace bs { , const std::string &userId); void onMatchingLogout(); + void onMDUpdated(bs::network::Asset::Type assetType + , const QString& security, const bs::network::MDFields &); + public slots: void onReactivate(); void raiseWindow(); @@ -156,6 +159,7 @@ namespace bs { void needMatchingLogin(const std::string &mtchLogin, const std::string &bsLogin); void needMatchingLogout(); void needSetUserId(const std::string&); + void needMdConnection(ApplicationSettings::EnvConfiguration); void setRecommendedFeeRate(float); private slots: diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 81e24b521..3eadcca7d 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -122,6 +122,7 @@ QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) , userSigner_(std::make_shared(TerminalUsers::Signer)) , userBS_(std::make_shared(TerminalUsers::BsServer)) , userMatch_(std::make_shared(TerminalUsers::Matching)) + , userMD_(std::make_shared(TerminalUsers::MktData)) {} QtGuiAdapter::~QtGuiAdapter() @@ -243,6 +244,8 @@ bool QtGuiAdapter::process(const Envelope &env) return processBsServer(env); case TerminalUsers::Matching: return processMatching(env); + case TerminalUsers::MktData: + return processMktData(env); case TerminalUsers::AuthEid: return processAuthEid(env); case TerminalUsers::OnChainTracker: @@ -732,6 +735,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogout, this, &QtGuiAdapter::onNeedMatchingLogout); connect(mainWindow_, &bs::gui::qt::MainWindow::needSetUserId, this, &QtGuiAdapter::onNeedSetUserId); connect(mainWindow_, &bs::gui::qt::MainWindow::setRecommendedFeeRate, this, &QtGuiAdapter::onSetRecommendedFeeRate); + connect(mainWindow_, &bs::gui::qt::MainWindow::needMdConnection, this, &QtGuiAdapter::onNeedMdConnection); } void QtGuiAdapter::onGetSettings(const std::vector& settings) @@ -1150,6 +1154,14 @@ void QtGuiAdapter::onSetRecommendedFeeRate(float) { } +void QtGuiAdapter::onNeedMdConnection(ApplicationSettings::EnvConfiguration ec) +{ + MktDataMessage msg; + msg.set_start_connection((int)ec); + Envelope env{ 0, user_, userMD_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -1472,4 +1484,36 @@ bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) return true; } +bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) +{ + MktDataMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MktDataMessage::kNewSecurity: + break; + case MktDataMessage::kPriceUpdate: + return processMdUpdate(msg.price_update()); + default: break; + } + return true; +} + +bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, msg] { + const bs::network::MDFields fields{ + { bs::network::MDField::PriceBid, msg.bid() }, + { bs::network::MDField::PriceOffer, msg.ask() }, + { bs::network::MDField::PriceLast, msg.last() }, + { bs::network::MDField::DailyVolume, msg.volume() }, + { bs::network::MDField::MDTimestamp, (double)msg.timestamp() } + }; + mainWindow_->onMDUpdated(static_cast(msg.security().asset_type()) + , QString::fromStdString(msg.security().name()), fields); + }); +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index f0c77315a..d9c018eb5 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -43,6 +43,7 @@ namespace BlockSettle { class BsServerMessage_LoginResult; class BsServerMessage_StartLoginResult; class MatchingMessage_LoggedIn; + class MktDataMessage_Prices; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; class SettingsMessage_SignerServers; @@ -110,6 +111,8 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processLogin(const BlockSettle::Terminal::BsServerMessage_LoginResult&); bool processMatching(const bs::message::Envelope&); + bool processMktData(const bs::message::Envelope&); + bool processMdUpdate(const BlockSettle::Terminal::MktDataMessage_Prices &); private slots: void onGetSettings(const std::vector&); @@ -155,12 +158,13 @@ private slots: void onNeedMatchingLogout(); void onNeedSetUserId(const std::string&); void onSetRecommendedFeeRate(float); + void onNeedMdConnection(ApplicationSettings::EnvConfiguration); private: std::shared_ptr logger_; std::shared_ptr userSettings_, userWallets_; std::shared_ptr userBlockchain_, userSigner_; - std::shared_ptr userBS_, userMatch_; + std::shared_ptr userBS_, userMatch_, userMD_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; diff --git a/common b/common index de9ca169a..03916e028 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit de9ca169a91f43833e5264de706372057363234b +Subproject commit 03916e028bbcb84a63040bc8d00deb7b34c28802 From 5abbb8b944f9e6c44b48f8d6f88da198f27ff780 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 16 Oct 2020 19:17:23 +0300 Subject: [PATCH 020/146] FX RFQ sending --- BlockSettleUILib/DialogManager.cpp | 3 +- BlockSettleUILib/OrderListModel.cpp | 6 + BlockSettleUILib/OrderListModel.h | 3 +- BlockSettleUILib/StatusBarView.cpp | 74 +++-- BlockSettleUILib/StatusBarView.h | 2 +- BlockSettleUILib/Trading/RFQDialog.cpp | 56 +++- BlockSettleUILib/Trading/RFQDialog.h | 16 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 6 + BlockSettleUILib/Trading/RFQReplyWidget.h | 4 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 136 +++++++-- BlockSettleUILib/Trading/RFQRequestWidget.h | 21 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 26 +- BlockSettleUILib/Trading/RFQTicketXBT.h | 8 +- .../Trading/RequestingQuoteWidget.cpp | 2 +- .../Trading/RequestingQuoteWidget.h | 2 +- BlockSettleUILib/Trading/WalletShieldBase.cpp | 6 +- BlockSettleUILib/Trading/WalletShieldBase.h | 2 +- Core/AssetsAdapter.cpp | 24 +- Core/AssetsAdapter.h | 3 +- Core/BsServerAdapter.cpp | 18 ++ Core/BsServerAdapter.h | 4 +- Core/MatchingAdapter.cpp | 269 +++++++++++++++++- Core/MatchingAdapter.h | 19 ++ Core/SignerAdapter.cpp | 2 +- Core/TerminalMessage.cpp | 45 +++ Core/TerminalMessage.h | 11 + GUI/QtWidgets/MainWindow.cpp | 101 +++---- GUI/QtWidgets/MainWindow.h | 15 +- GUI/QtWidgets/QtGuiAdapter.cpp | 71 +++++ GUI/QtWidgets/QtGuiAdapter.h | 9 + UnitTests/TestNetwork.cpp | 9 +- common | 2 +- 32 files changed, 801 insertions(+), 174 deletions(-) diff --git a/BlockSettleUILib/DialogManager.cpp b/BlockSettleUILib/DialogManager.cpp index 99a0ae06a..b7931ccf1 100644 --- a/BlockSettleUILib/DialogManager.cpp +++ b/BlockSettleUILib/DialogManager.cpp @@ -23,8 +23,7 @@ namespace { DialogManager::DialogManager(const QWidget *mainWindow) : mainWindow_(mainWindow) -{ -} +{} void DialogManager::adjustDialogPosition(QDialog *dlg) { diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index b406d0a51..620356eb4 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -57,6 +57,12 @@ OrderListModel::OrderListModel(const std::shared_ptr& assetManager reset(); } +OrderListModel::OrderListModel(QObject* parent) + : QAbstractItemModel(parent) +{ + reset(); +} + int OrderListModel::columnCount(const QModelIndex &) const { return Header::last; diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 03592aa37..d08a7e3d7 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -51,7 +51,8 @@ class OrderListModel : public QAbstractItemModel static QString toString(Index); }; - OrderListModel(const std::shared_ptr &, QObject *parent = nullptr); + [[deprecated]] OrderListModel(const std::shared_ptr &, QObject *parent = nullptr); + OrderListModel(QObject* parent = nullptr); ~OrderListModel() noexcept override = default; int columnCount(const QModelIndex &parent = QModelIndex()) const override; diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 8d5f733a7..2fbd534e0 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -198,17 +198,45 @@ void StatusBarView::onBlockchainStateChanged(int state, unsigned int blockNum) void StatusBarView::onXbtBalance(const bs::sync::WalletBalanceData &wbd) { // uppercase eliminates ext-int balance duplication xbtBalances_[QString::fromStdString(wbd.id).toUpper().toStdString()] = wbd.balTotal; - displayXbtBalance(); -} + if (balanceSymbols_.empty() || (balanceSymbols_[0] != bs::network::XbtCurrency)) { + balanceSymbols_.insert(balanceSymbols_.cbegin(), bs::network::XbtCurrency); + } -void StatusBarView::displayXbtBalance() -{ BTCNumericTypes::balance_type accBalance = 0; for (const auto& bal : xbtBalances_) { accBalance += bal.second; } - const auto xbt = UiUtils::displayAmount(accBalance); - QString text = tr(" XBT: %1 ").arg(xbt); + balances_[bs::network::XbtCurrency] = accBalance; + displayBalances(); +} + +void StatusBarView::displayBalances() +{ + QString text; + for (const auto& currency : balanceSymbols_) { + if (currency == bs::network::XbtCurrency) { + QString xbt; + switch (armoryConnState_) { + case ArmoryState::Ready: + xbt = UiUtils::displayAmount(balances_.at(currency)); + break; + case ArmoryState::Scanning: + case ArmoryState::Connected: + xbt = tr("Loading..."); + break; + case ArmoryState::Closing: + case ArmoryState::Offline: [[fallthrough]] + default: + xbt = tr("..."); + break; + } + text += tr(" XBT: %1 ").arg(xbt); + } else { + text += tr("| %1: %2 ") + .arg(QString::fromStdString(currency)) + .arg(UiUtils::displayCurrencyAmount(balances_.at(currency))); + } + } balanceLabel_->setText(text); progressBar_->setVisible(false); estimateLabel_->setVisible(false); @@ -402,39 +430,7 @@ void StatusBarView::onBalanceUpdated(const std::string &symbol, double balance) balanceSymbols_.push_back(symbol); } } - - QString text; - for (const auto ¤cy : balanceSymbols_) { - if (currency == bs::network::XbtCurrency) { - QString xbt; - switch (armoryConnState_) { - case ArmoryState::Ready: - xbt = UiUtils::displayAmount(balances_.at(currency)); - break; - - case ArmoryState::Scanning: - case ArmoryState::Connected: - xbt = tr("Loading..."); - break; - - case ArmoryState::Closing: - case ArmoryState::Offline: - xbt = tr("..."); - break; - - default: - xbt = tr("..."); - } - - text += tr(" XBT: %1 ").arg(xbt); - } - else { - text += tr("| %1: %2 ") - .arg(QString::fromStdString(currency)) - .arg(UiUtils::displayCurrencyAmount(balances_.at(currency))); - } - } - balanceLabel_->setText(text); + displayBalances(); } void StatusBarView::updateConnectionStatusDetails(ArmoryState state, unsigned int topBlock) diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index 451172469..ebfd9a2db 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -86,7 +86,7 @@ public slots: void SetCelerConnectingStatus(); QWidget *CreateSeparator(); [[deprecated]] void setBalances(); - void displayXbtBalance(); + void displayBalances(); void updateConnectionStatusDetails(ArmoryState state, unsigned int blockNum); private: diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 4c2dedf9a..9ea217ebe 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -79,7 +79,8 @@ RFQDialog::RFQDialog(const std::shared_ptr &logger connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::cancelRFQ, this, &RFQDialog::reject); connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::requestTimedOut, this, &RFQDialog::onTimeout); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteAccepted, this, &RFQDialog::onRFQResponseAccepted, Qt::QueuedConnection); + connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteAccepted, this + , &RFQDialog::onRFQResponseAccepted, Qt::QueuedConnection); connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFinished, this, &RFQDialog::onQuoteFinished); connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFailed, this, &RFQDialog::onQuoteFailed); @@ -97,6 +98,41 @@ RFQDialog::RFQDialog(const std::shared_ptr &logger quoteProvider_->SubmitRFQ(rfq_); } +RFQDialog::RFQDialog(const std::shared_ptr& logger + , const std::string& id, const bs::network::RFQ& rfq + , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet + , const bs::Address& authAddr + , bs::UtxoReservationToken fixedXbtUtxoRes + , bs::UtxoReservationToken ccUtxoRes + , std::unique_ptr purpose + , RFQRequestWidget* parent) + : QDialog(parent) + , ui_(new Ui::RFQDialog()) + , logger_(logger) + , id_(id), rfq_(rfq) + , recvXbtAddrIfSet_(recvXbtAddrIfSet) + , authAddr_(authAddr) + , fixedXbtUtxoRes_(std::move(fixedXbtUtxoRes)) + , requestWidget_(parent) + , ccUtxoRes_(std::move(ccUtxoRes)) + , walletPurpose_(std::move(purpose)) +{ + ui_->setupUi(this); + + connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::cancelRFQ, this + , &RFQDialog::reject); + connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::requestTimedOut + , this, &RFQDialog::onTimeout); + connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteAccepted, this + , &RFQDialog::onRFQResponseAccepted); + connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFinished, this + , &RFQDialog::onQuoteFinished); + connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFailed, this + , &RFQDialog::onQuoteFailed); + + ui_->pageRequestingQuote->populateDetails(rfq_); +} + RFQDialog::~RFQDialog() = default; void RFQDialog::onOrderFilled(const std::string "eId) @@ -122,13 +158,16 @@ void RFQDialog::onOrderFailed(const std::string& quoteId, const std::string& rea close(); } -void RFQDialog::onRFQResponseAccepted(const QString &reqId, const bs::network::Quote "e) +void RFQDialog::onRFQResponseAccepted(const std::string &reqId + , const bs::network::Quote "e) { - emit accepted(id_); + emit accepted(reqId, quote); quote_ = quote; if (rfq_.assetType == bs::network::Asset::SpotFX) { - quoteProvider_->AcceptQuoteFX(reqId, quote); + if (quoteProvider_) { + quoteProvider_->AcceptQuoteFX(QString::fromStdString(reqId), quote); + } } else { if (rfq_.assetType == bs::network::Asset::SpotXBT) { @@ -297,7 +336,12 @@ void RFQDialog::cancel(bool force) } if (cancelOnClose_) { - quoteProvider_->CancelQuote(QString::fromStdString(rfq_.requestId)); + if (quoteProvider_) { + quoteProvider_->CancelQuote(QString::fromStdString(rfq_.requestId)); + } + else { + emit cancelled(rfq_.requestId); + } } if (force) { close(); @@ -313,7 +357,7 @@ void RFQDialog::onTimeout() void RFQDialog::onQuoteFinished() { - emit accepted(id_); + emit accepted(id_, quote_); cancelOnClose_ = false; hide(); } diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index 5a9ccbf90..7a2eab7b7 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -56,7 +56,7 @@ class RFQDialog : public QDialog Q_OBJECT public: - RFQDialog(const std::shared_ptr &logger + [[deprecated]] RFQDialog(const std::shared_ptr &logger , const std::string &id, const bs::network::RFQ& rfq , const std::shared_ptr& quoteProvider , const std::shared_ptr& authAddressManager @@ -76,12 +76,20 @@ Q_OBJECT , bs::UtxoReservationToken ccUtxoRes , std::unique_ptr purpose , RFQRequestWidget* parent = nullptr); + RFQDialog(const std::shared_ptr& logger + , const std::string& id, const bs::network::RFQ& rfq + , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet + , const bs::Address& authAddr + , bs::UtxoReservationToken fixedXbtUtxoRes + , bs::UtxoReservationToken ccUtxoRes + , std::unique_ptr purpose + , RFQRequestWidget* parent = nullptr); ~RFQDialog() override; void cancel(bool force = true); signals: - void accepted(const std::string &id); + void accepted(const std::string &id, const bs::network::Quote&); void expired(const std::string &id); void cancelled(const std::string &id); @@ -93,6 +101,7 @@ public slots: void onSignedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash, QDateTime timestamp); void onSignedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); + void onQuoteReceived(const bs::network::Quote& quote); private slots: bool close(); @@ -100,8 +109,7 @@ private slots: void onQuoteFinished(); void onQuoteFailed(); - void onRFQResponseAccepted(const QString &reqId, const bs::network::Quote& quote); - void onQuoteReceived(const bs::network::Quote& quote); + void onRFQResponseAccepted(const std::string &reqId, const bs::network::Quote& quote); void onOrderFilled(const std::string "eId); void onOrderFailed(const std::string& quoteId, const std::string& reason); void onXBTSettlementAccepted(); diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 68a263187..f6b04b78f 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -232,6 +232,12 @@ void RFQReplyWidget::init(const std::shared_ptr &logger , this, &RFQReplyWidget::onEnterKeyPressed); } + void RFQReplyWidget::init(const std::shared_ptr& + , const std::shared_ptr&, OrderListModel* orderListModel) + { + //TODO + } + void RFQReplyWidget::forceCheckCondition() { const QModelIndex index = ui_->widgetQuoteRequests->view()->selectionModel()->currentIndex(); diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 16ca91d32..25e391949 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -92,7 +92,9 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr & , OrderListModel *orderListModel); - + void init(const std::shared_ptr& + , const std::shared_ptr& + , OrderListModel* orderListModel); void setWalletsManager(const std::shared_ptr &); void shortcutActivated(ShortcutType s) override; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 97825f6a3..6f12fcb89 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -167,6 +167,52 @@ void RFQRequestWidget::onMDUpdated(bs::network::Asset::Type assetType ui_->widgetMarketData->onMDUpdated(assetType, security, fields); } +void RFQRequestWidget::onBalance(const std::string& currency, double balance) +{ + ui_->pageRFQTicket->onBalance(currency, balance); +} + +void RFQRequestWidget::onMatchingLogin(const std::string& mtchLogin + , BaseCelerClient::CelerUserType userType, const std::string& userId) +{ + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::CurrencySelected, + this, &RFQRequestWidget::onCurrencySelected)); + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::BidClicked, + this, &RFQRequestWidget::onBidClicked)); + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::AskClicked, + this, &RFQRequestWidget::onAskClicked)); + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::MDHeaderClicked, + this, &RFQRequestWidget::onDisableSelectedInfo)); + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::clicked, + this, &RFQRequestWidget::onRefreshFocus)); + + userType_ = userType; + ui_->shieldPage->showShieldSelectTargetTrade(); + popShield(); +} + +void RFQRequestWidget::onMatchingLogout() +{ + for (QMetaObject::Connection& conn : marketDataConnection_) { + QObject::disconnect(conn); + } + userType_ = BaseCelerClient::CelerUserType::Undefined; + ui_->shieldPage->showShieldLoginToSubmitRequired(); + popShield(); +} + +void RFQRequestWidget::onVerifiedAuthAddresses(const std::vector&) +{ +} + +void RFQRequestWidget::onQuoteReceived(const bs::network::Quote& quote) +{ + const auto& itDlg = dialogs_.find(quote.requestId); + if (itDlg != dialogs_.end()) { + itDlg->second->onQuoteReceived(quote); + } +} + void RFQRequestWidget::hideEvent(QHideEvent* event) { ui_->pageRFQTicket->onParentAboutToHide(); @@ -267,21 +313,30 @@ void RFQRequestWidget::init(const std::shared_ptr &logger connect(authAddressManager_.get(), &AuthAddressManager::AddressListUpdated, this, &RFQRequestWidget::forceCheckCondition); } -void RFQRequestWidget::init(const std::shared_ptr&, const std::shared_ptr&, OrderListModel* orderListModel) +void RFQRequestWidget::init(const std::shared_ptr&logger + , const std::shared_ptr& dialogMgr, OrderListModel* orderListModel) { + dialogManager_ = dialogMgr; + ui_->pageRFQTicket->init(logger); + + ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui_->treeViewOrders->setModel(orderListModel); + ui_->treeViewOrders->initWithModel(orderListModel); + + ui_->pageRFQTicket->disablePanel(); } void RFQRequestWidget::onConnectedToCeler() { - marketDataConnection.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::CurrencySelected, + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::CurrencySelected, this, &RFQRequestWidget::onCurrencySelected)); - marketDataConnection.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::BidClicked, + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::BidClicked, this, &RFQRequestWidget::onBidClicked)); - marketDataConnection.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::AskClicked, + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::AskClicked, this, &RFQRequestWidget::onAskClicked)); - marketDataConnection.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::MDHeaderClicked, + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::MDHeaderClicked, this, &RFQRequestWidget::onDisableSelectedInfo)); - marketDataConnection.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::clicked, + marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::clicked, this, &RFQRequestWidget::onRefreshFocus)); ui_->shieldPage->showShieldSelectTargetTrade(); @@ -290,7 +345,7 @@ void RFQRequestWidget::onConnectedToCeler() void RFQRequestWidget::onDisconnectedFromCeler() { - for (QMetaObject::Connection &conn : marketDataConnection) { + for (QMetaObject::Connection &conn : marketDataConnection_) { QObject::disconnect(conn); } @@ -302,22 +357,32 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ , bs::UtxoReservationToken ccUtxoRes) { auto authAddr = ui_->pageRFQTicket->selectedAuthAddress(); - - auto xbtWallet = ui_->pageRFQTicket->xbtWallet(); + RFQDialog* dialog = nullptr; auto fixedXbtInputs = ui_->pageRFQTicket->fixedXbtInputs(); - std::unique_ptr purpose; - if (xbtWallet && !xbtWallet->canMixLeaves()) { - auto walletType = ui_->pageRFQTicket->xbtWalletType(); - purpose.reset(new bs::hd::Purpose(UiUtils::getHwWalletPurpose(walletType))); - } - RFQDialog* dialog = new RFQDialog(logger_, id, rfq, quoteProvider_ - , authAddressManager_, assetManager_, walletsManager_, signingContainer_ - , armory_, celerClient_, appSettings_, rfqStorage_, xbtWallet - , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, utxoReservationManager_ - , fixedXbtInputs.inputs, std::move(fixedXbtInputs.utxoRes) - , std::move(ccUtxoRes), std::move(purpose), this); + if (walletsManager_) { + auto xbtWallet = ui_->pageRFQTicket->xbtWallet(); + + if (xbtWallet && !xbtWallet->canMixLeaves()) { + auto walletType = ui_->pageRFQTicket->xbtWalletType(); + purpose.reset(new bs::hd::Purpose(UiUtils::getHwWalletPurpose(walletType))); + } + + dialog = new RFQDialog(logger_, id, rfq, quoteProvider_ + , authAddressManager_, assetManager_, walletsManager_, signingContainer_ + , armory_, celerClient_, appSettings_, rfqStorage_, xbtWallet + , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, utxoReservationManager_ + , fixedXbtInputs.inputs, std::move(fixedXbtInputs.utxoRes) + , std::move(ccUtxoRes), std::move(purpose), this); + } + else { + std::string xbtWalletId; //TODO - set + dialog = new RFQDialog(logger_, id, rfq, xbtWalletId + , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, std::move(fixedXbtInputs.utxoRes) + , std::move(ccUtxoRes), std::move(purpose), this); + emit needSubmitRFQ(rfq); + } connect(this, &RFQRequestWidget::unsignedPayinRequested, dialog, &RFQDialog::onUnsignedPayinRequested); connect(this, &RFQRequestWidget::signedPayoutRequested, dialog, &RFQDialog::onSignedPayoutRequested); @@ -376,7 +441,7 @@ bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) { ui_->stackedWidgetRFQ->setEnabled(true); using UserType = CelerClient::CelerUserType; - const UserType userType = celerClient_->celerUserType(); + const UserType userType = celerClient_ ? celerClient_->celerUserType() : userType_; using GroupType = RFQShieldPage::ProductType; const GroupType group = RFQShieldPage::getProductGroup(selectedInfo.productGroup_); @@ -418,7 +483,6 @@ bool RFQRequestWidget::checkWalletSettings(bs::network::Asset::Type productType, popShield(); return true; } - return false; } @@ -519,21 +583,39 @@ void RFQRequestWidget::onUserConnected(const bs::network::UserType &ut) void RFQRequestWidget::onUserDisconnected() { - ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->suspend(); + if (autoSignProvider_) { + ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->suspend(); + } } -void RFQRequestWidget::onRFQAccepted(const std::string &id) +void RFQRequestWidget::onRFQAccepted(const std::string &id + , const bs::network::Quote& quote) { - ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->rfqAccepted(id); + if (autoSignProvider_) { + ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->rfqAccepted(id); + } + else { + emit needAcceptRFQ(id, quote); + } } void RFQRequestWidget::onRFQExpired(const std::string &id) { deleteDialog(id); - ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->rfqExpired(id); + if (autoSignProvider_) { + ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->rfqExpired(id); + } + else { + emit needExpireRFQ(id); + } } void RFQRequestWidget::onRFQCancelled(const std::string &id) { - ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->rfqCancelled(id); + if (autoSignProvider_) { + ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->rfqCancelled(id); + } + else { + emit needCancelRFQ(id); + } } diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index af0f17d9e..f907ee4cf 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -14,7 +14,7 @@ #include #include #include - +#include "BaseCelerClient.h" #include "CommonTypes.h" #include "MarketDataWidget.h" #include "TabWithShortcut.h" @@ -92,6 +92,14 @@ Q_OBJECT void onMDUpdated(bs::network::Asset::Type, const QString& security , const bs::network::MDFields &); + void onBalance(const std::string& currency, double balance); + + void onMatchingLogin(const std::string& mtchLogin, BaseCelerClient::CelerUserType + , const std::string& userId); + void onMatchingLogout(); + void onVerifiedAuthAddresses(const std::vector&); + + void onQuoteReceived(const bs::network::Quote& quote); protected: void hideEvent(QHideEvent* event) override; @@ -113,6 +121,11 @@ Q_OBJECT void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); + void needSubmitRFQ(const bs::network::RFQ&); + void needAcceptRFQ(const std::string& id, const bs::network::Quote&); + void needExpireRFQ(const std::string& id); + void needCancelRFQ(const std::string& id); + private: void showEditableRFQPage(); void popShield(); @@ -139,7 +152,7 @@ public slots: private slots: void onConnectedToCeler(); void onDisconnectedFromCeler(); - void onRFQAccepted(const std::string &id); + void onRFQAccepted(const std::string &id, const bs::network::Quote&); void onRFQExpired(const std::string &id); void onRFQCancelled(const std::string &id); @@ -165,9 +178,11 @@ public slots: std::shared_ptr rfqStorage_; - QList marketDataConnection; + QList marketDataConnection_; std::unordered_map dialogs_; + + BaseCelerClient::CelerUserType userType_{ BaseCelerClient::CelerUserType::Undefined }; }; #endif // __RFQ_REQUEST_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 214867cf4..1252a200f 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -243,13 +243,13 @@ RFQTicketXBT::BalanceInfoContainer RFQTicketXBT::getBalanceInfo() const balance.productType = ProductGroupType::CCGroupType; } else { const double divisor = std::pow(10, UiUtils::GetAmountPrecisionFX()); - const double intBalance = std::floor((assetManager_ ? assetManager_->getBalance(productToSpend.toStdString()) : 0.0) * divisor); - balance.amount = intBalance / divisor; + const double bal = assetManager_ ? assetManager_->getBalance(productToSpend.toStdString()) + : balances_.at(productToSpend.toStdString()); + balance.amount = std::floor(bal * divisor) / divisor; balance.product = productToSpend; balance.productType = ProductGroupType::FXGroupType; } } - return balance; } @@ -288,6 +288,11 @@ void RFQTicketXBT::setWalletsManager(const std::shared_ptr& logger) +{ + logger_ = logger; +} + void RFQTicketXBT::walletsLoaded() { if (!signingContainer_ || !walletsManager_ || walletsManager_->hdWallets().empty()) { @@ -709,7 +714,8 @@ void RFQTicketXBT::updateSubmitButton() { ui_->pushButtonSubmit->setEnabled(false); - if (!assetManager_) { + if (!assetManager_ && signingContainer_) { + logger_->debug("[{}] 1", __func__); return; } @@ -717,6 +723,7 @@ void RFQTicketXBT::updateSubmitButton() if (signingContainer_) { if (signingContainer_->isOffline()) { showHelp(tr("Signer is offline - settlement will not be possible")); + logger_->debug("[{}] 2", __func__); return; } else { @@ -725,16 +732,19 @@ void RFQTicketXBT::updateSubmitButton() } if (getProductToSpend() == UiUtils::XbtCurrency && !getSendXbtWallet()) { + logger_->debug("[{}] 3", __func__); return; } if (getProductToRecv() == UiUtils::XbtCurrency && !getRecvXbtWallet()) { + logger_->debug("[{}] 4", __func__); return; } if (currentGroupType_ == ProductGroupType::CCGroupType) { auto ccWallet = getCCWallet(getProduct().toStdString()); if (!ccWallet) { + logger_->debug("[{}] 5", __func__); return; } } @@ -746,15 +756,18 @@ void RFQTicketXBT::updateSubmitButton() if (!isBalanceOk || !isAuthOk) { ui_->labelBalanceValue->setFont(invalidBalanceFont_); + logger_->debug("[{}] 6", __func__); return; } ui_->labelBalanceValue->setFont(QFont()); if (qFuzzyIsNull(qty)) { + logger_->debug("[{}] 7", __func__); return; } if ((currentGroupType_ == ProductGroupType::XBTGroupType) && authKey().empty()) { + logger_->debug("[{}] 8", __func__); return; } @@ -1164,6 +1177,11 @@ void RFQTicketXBT::onParentAboutToHide() fixedXbtInputs_ = {}; } +void RFQTicketXBT::onBalance(const std::string& currency, double balance) +{ + balances_[currency] = balance; +} + void RFQTicketXBT::enablePanel() { resetTicket(); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h index 13cb8de66..a48878e94 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ b/BlockSettleUILib/Trading/RFQTicketXBT.h @@ -66,14 +66,16 @@ Q_OBJECT RFQTicketXBT(QWidget* parent = nullptr); ~RFQTicketXBT() override; - void init(const std::shared_ptr &logger + [[deprecated]] void init(const std::shared_ptr &logger , const std::shared_ptr & , const std::shared_ptr &assetManager , const std::shared_ptr "eProvider , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &); - void setWalletsManager(const std::shared_ptr &); + [[deprecated]]void setWalletsManager(const std::shared_ptr &); + + void init(const std::shared_ptr&); void resetTicket(); @@ -100,6 +102,7 @@ Q_OBJECT UiUtils::WalletsTypes xbtWalletType() const; void onParentAboutToHide(); + void onBalance(const std::string& currency, double balance); public slots: void SetProductAndSide(const QString& productGroup, const QString& currencyPair @@ -270,6 +273,7 @@ private slots: std::vector deferredRFQs_; std::unordered_map mdInfo_; + std::unordered_map balances_; }; #endif // __RFQ_TICKET_XBT_H__ diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index db56340bb..c319c6eb4 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -265,7 +265,7 @@ void RequestingQuoteWidget::onAccept() } ui_->labelTimeLeft->clear(); - emit quoteAccepted(QString::fromStdString(rfq_.requestId), quote_); + emit quoteAccepted(rfq_.requestId, quote_); } void RequestingQuoteWidget::SetQuoteDetailsState(QuoteDetailsState state) diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.h b/BlockSettleUILib/Trading/RequestingQuoteWidget.h index 240320118..732f8bd16 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.h +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.h @@ -52,7 +52,7 @@ public slots: signals: void cancelRFQ(); void requestTimedOut(); - void quoteAccepted(const QString &reqId, const bs::network::Quote& quote); + void quoteAccepted(const std::string &reqId, const bs::network::Quote& quote); void quoteFinished(); void quoteFailed(); diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index d5dd7cfc2..75c3e06d6 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -77,8 +77,12 @@ void WalletShieldBase::setTabType(QString&& tabType) tabType_ = std::move(tabType); } -bool WalletShieldBase::checkWalletSettings(WalletShieldBase::ProductType productType, const QString& product) +bool WalletShieldBase::checkWalletSettings(WalletShieldBase::ProductType productType + , const QString& product) { + if (!walletsManager_ && !authMgr_) { + return true; // Temporary workaround for new arch - fix later if needed + } // No primary wallet bool hasFullWallet = false; if (walletsManager_) { diff --git a/BlockSettleUILib/Trading/WalletShieldBase.h b/BlockSettleUILib/Trading/WalletShieldBase.h index b32913d27..a4c9c7b0a 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.h +++ b/BlockSettleUILib/Trading/WalletShieldBase.h @@ -37,7 +37,7 @@ class WalletShieldBase : public QWidget void setShieldButtonAction(std::function&& action, bool oneShot = true); - void init(const std::shared_ptr &walletsManager + [[deprecated]] void init(const std::shared_ptr &walletsManager , const std::shared_ptr &authMgr , const std::shared_ptr &appSettings); diff --git a/Core/AssetsAdapter.cpp b/Core/AssetsAdapter.cpp index 32ccafcac..49f9aa4b5 100644 --- a/Core/AssetsAdapter.cpp +++ b/Core/AssetsAdapter.cpp @@ -72,6 +72,18 @@ bool AssetsAdapter::process(const bs::message::Envelope &env) default: break; } } + else if (env.sender->value() == bs::message::TerminalUsers::BsServer) { + BsServerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse BS message #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case BsServerMessage::kBalanceUpdated: + return processBalance(msg.balance_updated().currency(), msg.balance_updated().value()); + default: break; + } + } else if (env.receiver && (env.receiver->value() == user_->value())) { AssetsMessage msg; if (!msg.ParseFromString(env.message)) { @@ -103,7 +115,7 @@ void AssetsAdapter::onFxBalanceCleared() { } -void AssetsAdapter::onBalanceChanged(const std::string& currency) +void AssetsAdapter::onBalanceChanged(const std::string& currency, double value) { } @@ -217,3 +229,13 @@ bool AssetsAdapter::processSubmitAuth(const std::string& address) , {}, {}, msg.SerializeAsString(), true }; return pushFill(env); } + +bool AssetsAdapter::processBalance(const std::string& currency, double value) +{ + AssetsMessage msg; + auto msgBal = msg.mutable_balance(); + msgBal->set_currency(currency); + msgBal->set_value(value); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} diff --git a/Core/AssetsAdapter.h b/Core/AssetsAdapter.h index 124a5bc51..b469e819e 100644 --- a/Core/AssetsAdapter.h +++ b/Core/AssetsAdapter.h @@ -47,7 +47,7 @@ class AssetsAdapter : public bs::message::Adapter void onFxBalanceLoaded() override; void onFxBalanceCleared() override; - void onBalanceChanged(const std::string& currency) override; + void onBalanceChanged(const std::string& currency, double value) override; void onTotalChanged() override; void onSecuritiesChanged() override; @@ -62,6 +62,7 @@ class AssetsAdapter : public bs::message::Adapter bool onMatchingLogin(const BlockSettle::Terminal::MatchingMessage_LoggedIn&); bool processSubmittedAuth(const BlockSettle::Terminal::MatchingMessage_SubmittedAuthAddresses&); bool processSubmitAuth(const std::string&); + bool processBalance(const std::string& currency, double); private: std::shared_ptr logger_; diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 313316522..1b6f0c32f 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -360,3 +360,21 @@ void BsServerAdapter::onConnectionFailed() { Disconnected(); } + +void BsServerAdapter::onBalanceUpdated(const std::string& currency, double balance) +{ + BsServerMessage msg; + auto msgBal = msg.mutable_balance_updated(); + msgBal->set_currency(currency); + msgBal->set_value(balance); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +void BsServerAdapter::onTradingStatusChanged(bool tradingEnabled) +{ + BsServerMessage msg; + msg.set_trading_enabled(tradingEnabled); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index 7a140d5b1..d1a022a7c 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -65,9 +65,9 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg void onBootstrapDataUpdated(const std::string& data) override; void onAccountStateChanged(bs::network::UserType userType, bool enabled) override; void onFeeRateReceived(float feeRate) override; - void onBalanceLoaded() override; + void onBalanceLoaded() override;*/ void onBalanceUpdated(const std::string& currency, double balance) override; - void onTradingStatusChanged(bool tradingEnabled) override;*/ + void onTradingStatusChanged(bool tradingEnabled) override; private: std::shared_ptr logger_; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index dddc2831f..2a9614a3d 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -10,13 +10,28 @@ */ #include "MatchingAdapter.h" #include +#include "Celer/CommonCelerUtils.h" #include "CelerClientProxy.h" +#include "CelerCreateOrderSequence.h" +#include "CelerCreateFxOrderSequence.h" +#include "CelerGetAssignedAccountsListSequence.h" +#include "CelerSubmitRFQSequence.h" +#include "CurrencyPair.h" +#include "ProtobufUtils.h" #include "TerminalMessage.h" #include "terminal.pb.h" +#include "DownstreamQuoteProto.pb.h" +#include "DownstreamOrderProto.pb.h" +#include "bitcoin/DownstreamBitcoinTransactionSigningProto.pb.h" using namespace BlockSettle::Terminal; using namespace bs::message; +using namespace com::celertech::marketmerchant::api::enums::orderstatus; +using namespace com::celertech::marketmerchant::api::enums::producttype::quotenotificationtype; +using namespace com::celertech::marketmerchant::api::enums::side; +using namespace com::celertech::marketmerchant::api::order; +using namespace com::celertech::marketmerchant::api::quote; MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) @@ -24,11 +39,15 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) , user_(std::make_shared(bs::message::TerminalUsers::Matching)) { celerConnection_ = std::make_unique(logger, this, true, true); + + celerConnection_->RegisterHandler(CelerAPI::QuoteDownstreamEventType + , [this](const std::string& data) { + return onQuoteResponse(data); + }); } void MatchingAdapter::connectedToServer() { - logger_->debug("[{}]", __func__); MatchingMessage msg; auto loggedIn = msg.mutable_logged_in(); loggedIn->set_user_type(static_cast(celerConnection_->celerUserType())); @@ -37,12 +56,34 @@ void MatchingAdapter::connectedToServer() Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); + + const auto &cbAccounts = [this](const std::vector& accVec) + { // Remove duplicated entries if possible + std::set accounts(accVec.cbegin(), accVec.cend()); + if (accounts.size() == 1) { + assignedAccount_ = *accounts.cbegin(); + logger_->debug("[MatchingAdapter] assigned account: {}", assignedAccount_); + } + else if (accounts.empty()) { + logger_->error("[MatchingAdapter::onCelerConnected] no accounts"); + } else { + logger_->error("[MatchingAdapter::onCelerConnected] too many accounts ({})" + , accounts.size()); + for (const auto& account : accounts) { + logger_->error("[MatchingAdapter::onCelerConnected] acc: {}", account); + } + } + }; + if (!celerConnection_->ExecuteSequence(std::make_shared( + logger_, cbAccounts))) { + logger_->error("[{}] failed to get accounts", __func__); + } } void MatchingAdapter::connectionClosed() { + assignedAccount_.clear(); celerConnection_->CloseConnection(); - logger_->debug("[{}]", __func__); MatchingMessage msg; msg.mutable_logged_out(); @@ -59,7 +100,6 @@ void MatchingAdapter::connectionError(int errorCode) pushFill(env); } - bool MatchingAdapter::process(const bs::message::Envelope &env) { if (env.sender->isSystem()) { @@ -108,6 +148,10 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) return processGetSubmittedAuth(env); case MatchingMessage::kSubmitAuthAddress: return processSubmitAuth(env, msg.submit_auth_address()); + case MatchingMessage::kSendRfq: + return processSendRFQ(msg.send_rfq()); + case MatchingMessage::kAcceptRfq: + return processAcceptRFQ(msg.accept_rfq()); default: logger_->warn("[{}] unknown msg {} #{} from {}", __func__, msg.data_case() , env.id, env.sender->name()); @@ -119,7 +163,8 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) bool MatchingAdapter::processLogin(const MatchingMessage_Login& request) { - return celerConnection_->SendLogin(request.matching_login(), request.terminal_login(), {}); + return celerConnection_->SendLogin(request.matching_login() + , request.terminal_login(), {}); } bool MatchingAdapter::processGetSubmittedAuth(const bs::message::Envelope& env) @@ -142,6 +187,222 @@ bool MatchingAdapter::processSubmitAuth(const bs::message::Envelope& env return processGetSubmittedAuth(env); } +bool MatchingAdapter::processSendRFQ(const MatchingMessage_RFQ& request) +{ + if (assignedAccount_.empty()) { + logger_->error("[MatchingAdapter::processSendRFQ] submitting with empty account name"); + } + bs::network::RFQ rfq; + rfq.requestId = request.id(); + rfq.security = request.security(); + rfq.product = request.product(); + rfq.assetType = static_cast(request.asset_type()); + rfq.side = request.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + rfq.quantity = request.quantity(); + rfq.requestorAuthPublicKey = request.auth_pub_key(); + rfq.receiptAddress = request.receipt_address(); + rfq.coinTxInput = request.coin_tx_input(); + auto sequence = std::make_shared(assignedAccount_, rfq + , logger_, true); + if (!celerConnection_->ExecuteSequence(sequence)) { + logger_->error("[MatchingAdapter::processSendRFQ] failed to execute CelerSubmitRFQSequence"); + } else { + logger_->debug("[MatchingAdapter::processSendRFQ] RFQ submitted: {}", rfq.requestId); + submittedRFQs_[rfq.requestId] = rfq; + } + return true; +} + +bool MatchingAdapter::processAcceptRFQ(const MatchingMessage_AcceptRFQ& request) +{ + if (assignedAccount_.empty()) { + logger_->error("[MatchingAdapter::processAcceptRFQ] accepting with empty account name"); + } + const auto& reqId = QString::fromStdString(request.rfq_id()); + const auto& quote = fromMsg(request.quote()); + if (quote.assetType == bs::network::Asset::SpotFX) { + auto sequence = std::make_shared(assignedAccount_ + , reqId, quote, logger_); + if (!celerConnection_->ExecuteSequence(sequence)) { + logger_->error("[MatchingAdapter::processAcceptRFQ] failed to execute CelerCreateFxOrderSequence"); + } else { + logger_->debug("[MatchingAdapter::processAcceptRFQ] FX Order submitted"); + } + } + else { + auto sequence = std::make_shared(assignedAccount_ + , reqId, quote, request.payout_tx(), logger_); + if (!celerConnection_->ExecuteSequence(sequence)) { + logger_->error("[MatchingAdapter::processAcceptRFQ] failed to execute CelerCreateOrderSequence"); + } else { + logger_->debug("[MatchingAdapter::processAcceptRFQ] Order submitted"); + } + } + return true; +} + +void MatchingAdapter::saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId) +{ + quoteIdMap_[quoteId] = quoteReqId; + quoteIds_[quoteReqId].insert(quoteId); +} + +void MatchingAdapter::delQuoteReqId(const std::string& quoteReqId) +{ + const auto& itQuoteId = quoteIds_.find(quoteReqId); + if (itQuoteId != quoteIds_.end()) { + for (const auto& id : itQuoteId->second) { + quoteIdMap_.erase(id); + } + quoteIds_.erase(itQuoteId); + } + cleanQuoteRequestCcy(quoteReqId); +} + +std::string MatchingAdapter::getQuoteReqId(const std::string& quoteId) const +{ + const auto& itQuoteId = quoteIdMap_.find(quoteId); + return (itQuoteId == quoteIdMap_.end()) ? std::string{} : itQuoteId->second; +} + +void MatchingAdapter::saveQuoteRequestCcy(const std::string& id, const std::string& ccy) +{ + quoteCcys_.emplace(id, ccy); +} + +void MatchingAdapter::cleanQuoteRequestCcy(const std::string& id) +{ + auto it = quoteCcys_.find(id); + if (it != quoteCcys_.end()) { + quoteCcys_.erase(it); + } +} + +std::string MatchingAdapter::getQuoteRequestCcy(const std::string& id) const +{ + std::string ccy; + auto it = quoteCcys_.find(id); + if (it != quoteCcys_.end()) { + ccy = it->second; + } + return ccy; +} + +bool MatchingAdapter::onQuoteResponse(const std::string& data) +{ + QuoteDownstreamEvent response; + + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onQuoteResponse] Failed to parse QuoteDownstreamEvent"); + return false; + } + logger_->debug("[MatchingAdapter::onQuoteResponse]: {}", ProtobufUtils::toJsonCompact(response)); + + bs::network::Quote quote; + quote.quoteId = response.quoteid(); + quote.requestId = response.quoterequestid(); + quote.security = response.securitycode(); + quote.assetType = bs::celer::fromCelerProductType(response.producttype()); + quote.side = bs::celer::fromCeler(response.side()); + + if (quote.assetType == bs::network::Asset::PrivateMarket) { + quote.dealerAuthPublicKey = response.dealerreceiptaddress(); + quote.dealerTransaction = response.dealercointransactioninput(); + } + + switch (response.quotingtype()) + { + case com::celertech::marketmerchant::api::enums::quotingtype::AUTOMATIC: + quote.quotingType = bs::network::Quote::Automatic; + break; + case com::celertech::marketmerchant::api::enums::quotingtype::MANUAL: + quote.quotingType = bs::network::Quote::Manual; + break; + case com::celertech::marketmerchant::api::enums::quotingtype::DIRECT: + quote.quotingType = bs::network::Quote::Direct; + break; + case com::celertech::marketmerchant::api::enums::quotingtype::INDICATIVE: + quote.quotingType = bs::network::Quote::Indicative; + break; + case com::celertech::marketmerchant::api::enums::quotingtype::TRADEABLE: + quote.quotingType = bs::network::Quote::Tradeable; + break; + default: + quote.quotingType = bs::network::Quote::Indicative; + break; + } + + quote.expirationTime = QDateTime::fromMSecsSinceEpoch(response.validuntiltimeutcinmillis()); + quote.timeSkewMs = QDateTime::fromMSecsSinceEpoch(response.quotetimestamputcinmillis()).msecsTo(QDateTime::currentDateTime()); + quote.celerTimestamp = response.quotetimestamputcinmillis(); + + logger_->debug("[MatchingAdapter::onQuoteResponse] timeSkew = {}", quote.timeSkewMs); + CurrencyPair cp(quote.security); + + const auto itRFQ = submittedRFQs_.find(response.quoterequestid()); + if (itRFQ == submittedRFQs_.end()) { // Quote for dealer to indicate GBBO + const auto quoteCcy = getQuoteRequestCcy(quote.requestId); + if (!quoteCcy.empty()) { + double price = 0; + + if ((quote.side == bs::network::Side::Sell) ^ (quoteCcy != cp.NumCurrency())) { + price = response.offerpx(); + } else { + price = response.bidpx(); + } + + const bool own = response.has_quotedbysessionkey() && !response.quotedbysessionkey().empty(); + MatchingMessage msg; + auto msgBest = msg.mutable_best_quoted_price(); + msgBest->set_quote_req_id(response.quoterequestid()); + msgBest->set_price(price); + msgBest->set_own(own); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + } + } else { + if (response.legquotegroup_size() != 1) { + logger_->error("[MatchingAdapter::onQuoteResponse] invalid leg number: {}\n{}" + , response.legquotegroup_size() + , ProtobufUtils::toJsonCompact(response)); + return false; + } + + const auto& grp = response.legquotegroup(0); + + if (quote.assetType == bs::network::Asset::SpotXBT) { + quote.dealerAuthPublicKey = response.dealerauthenticationaddress(); + quote.requestorAuthPublicKey = itRFQ->second.requestorAuthPublicKey; + + if (response.has_settlementid() && !response.settlementid().empty()) { + quote.settlementId = response.settlementid(); + } + + quote.dealerTransaction = response.dealertransaction(); + } + + if ((quote.side == bs::network::Side::Sell) ^ (itRFQ->second.product != cp.NumCurrency())) { + quote.price = response.offerpx(); + quote.quantity = grp.offersize(); + } else { + quote.price = response.bidpx(); + quote.quantity = grp.bidsize(); + } + + quote.product = grp.currency(); + + if (quote.quotingType == bs::network::Quote::Tradeable) { + submittedRFQs_.erase(itRFQ); + } + } + saveQuoteReqId(quote.requestId, quote.quoteId); + + MatchingMessage msg; + toMsg(quote, msg.mutable_quote()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} + ClientCelerConnection::ClientCelerConnection(const std::shared_ptr& logger , MatchingAdapter* parent, bool userIdRequired, bool useRecvTimer) diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 857d34871..bf91b6c82 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -20,6 +20,8 @@ namespace spdlog { namespace BlockSettle { namespace Terminal { class MatchingMessage_Login; + class MatchingMessage_RFQ; + class MatchingMessage_AcceptRFQ; } } @@ -62,11 +64,28 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget bool processLogin(const BlockSettle::Terminal::MatchingMessage_Login&); bool processGetSubmittedAuth(const bs::message::Envelope&); bool processSubmitAuth(const bs::message::Envelope&, const std::string& address); + bool processSendRFQ(const BlockSettle::Terminal::MatchingMessage_RFQ&); + bool processAcceptRFQ(const BlockSettle::Terminal::MatchingMessage_AcceptRFQ&); + + std::string getQuoteReqId(const std::string& quoteId) const; + void saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId); + void delQuoteReqId(const std::string& quoteReqId); + std::string getQuoteRequestCcy(const std::string& id) const; + void saveQuoteRequestCcy(const std::string& id, const std::string& ccy); + void cleanQuoteRequestCcy(const std::string& id); + + bool onQuoteResponse(const std::string&); private: std::shared_ptr logger_; std::shared_ptr user_; std::unique_ptr celerConnection_; + + std::string assignedAccount_; + std::unordered_map submittedRFQs_; + std::unordered_map quoteIdMap_; + std::unordered_map> quoteIds_; + std::unordered_map quoteCcys_; }; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 4e7086000..1ddc982bf 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -192,7 +192,7 @@ bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &re localSignerPort = portToTest; break; } else { - logger_->error("[SignerAdapter::processSignerSettings] attempt {}:" + logger_->debug("[SignerAdapter::processSignerSettings] attempt #{}:" " port {} used", port); } } diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index 3c752dc85..a44f518e4 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -96,3 +96,48 @@ bool TerminalInprocBus::run(int &argc, char **argv) runnableAdapter_->run(argc, argv); return true; } + + +//TODO: move to another source file +using namespace BlockSettle::Terminal; +bs::network::Quote bs::message::fromMsg(const MatchingMessage_Quote& msg) +{ + bs::network::Quote quote; + quote.requestId = msg.request_id(); + quote.quoteId = msg.quote_id(); + quote.security = msg.security(); + quote.product = msg.product(); + quote.price = msg.price(); + quote.quantity = msg.quantity(); + quote.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + quote.assetType = static_cast(msg.asset_type()); + quote.quotingType = static_cast(msg.quoting_type()); + quote.requestorAuthPublicKey = msg.req_auth_pub_key(); + quote.dealerAuthPublicKey = msg.deal_auth_pub_key(); + quote.settlementId = msg.settlement_id(); + quote.dealerTransaction = msg.dealer_tx(); + quote.expirationTime = QDateTime::fromSecsSinceEpoch(msg.expiration_time()); + quote.timeSkewMs = msg.time_skew_ms(); + quote.celerTimestamp = msg.timestamp(); + return quote; +} + +void bs::message::toMsg(const bs::network::Quote& quote, MatchingMessage_Quote* msg) +{ + msg->set_request_id(quote.requestId); + msg->set_quote_id(quote.quoteId); + msg->set_security(quote.security); + msg->set_product(quote.product); + msg->set_price(quote.price); + msg->set_quantity(quote.quantity); + msg->set_buy(quote.side == bs::network::Side::Buy); + msg->set_asset_type((int)quote.assetType); + msg->set_quoting_type((int)quote.quotingType); + msg->set_req_auth_pub_key(quote.requestorAuthPublicKey); + msg->set_deal_auth_pub_key(quote.dealerAuthPublicKey); + msg->set_settlement_id(quote.settlementId); + msg->set_dealer_tx(quote.dealerTransaction); + msg->set_expiration_time(quote.expirationTime.toSecsSinceEpoch()); + msg->set_time_skew_ms(quote.timeSkewMs); + msg->set_timestamp(quote.celerTimestamp); +} diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index 610d83073..9ed66a1fd 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -13,6 +13,13 @@ #include "Message/Bus.h" #include "Message/Envelope.h" +#include "CommonTypes.h" + +namespace BlockSettle { + namespace Terminal { + class MatchingMessage_Quote; + } +} namespace bs { @@ -93,6 +100,10 @@ namespace bs { std::shared_ptr runnableAdapter_; }; + + //TODO: move to another source file + bs::network::Quote fromMsg(const BlockSettle::Terminal::MatchingMessage_Quote&); + void toMsg(const bs::network::Quote&, BlockSettle::Terminal::MatchingMessage_Quote*); } // namespace message } // namespace bs diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index a66185c14..7de4e63cd 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -37,6 +37,7 @@ #include "InfoDialogs/SupportDialog.h" #include "LoginWindow.h" #include "NotificationCenter.h" +#include "OrderListModel.h" #include "Settings/ConfigDialog.h" #include "StatusBarView.h" #include "TabWithShortcut.h" @@ -189,18 +190,15 @@ void bs::gui::qt::MainWindow::onSettingsState(const ApplicationSettings::State& void MainWindow::onArmoryStateChanged(int state, unsigned int blockNum) { topBlock_ = blockNum; - if (statusBarView_) { - statusBarView_->onBlockchainStateChanged(state, blockNum); - } + statusBarView_->onBlockchainStateChanged(state, blockNum); ui_->widgetExplorer->onNewBlock(blockNum); } void MainWindow::onNewBlock(int state, unsigned int blockNum) { topBlock_ = blockNum; - if (statusBarView_) { - statusBarView_->onBlockchainStateChanged(state, blockNum); - } + statusBarView_->onBlockchainStateChanged(state, blockNum); + if (txModel_) { txModel_->onNewBlock(blockNum); } @@ -218,10 +216,8 @@ void MainWindow::onWalletsReady() void MainWindow::onSignerStateChanged(int state, const std::string &details) { - if (statusBarView_) { - statusBarView_->onSignerStatusChanged(static_cast(state) - , QString::fromStdString(details)); - } + statusBarView_->onSignerStatusChanged(static_cast(state) + , QString::fromStdString(details)); } void MainWindow::onHDWallet(const bs::sync::WalletInfo &wi) @@ -945,7 +941,7 @@ void bs::gui::qt::MainWindow::onMatchingLogin(const std::string& mtchLogin actLogout_->setVisible(true); // ccFileManager_->ConnectToCelerClient(celerConnection_); - ui_->widgetRFQ->onUserConnected(userType); + ui_->widgetRFQ->onMatchingLogin(mtchLogin, userType, userId); ui_->widgetRFQReply->onUserConnected(userType); statusBarView_->onConnectedToMatching(); @@ -995,6 +991,8 @@ void MainWindow::onMatchingLogout() statusBarView_->onDisconnectedFromMatching(); setLoginButtonText(loginButtonText_); + + ui_->widgetRFQ->onMatchingLogout(); } void MainWindow::onMDUpdated(bs::network::Asset::Type assetType @@ -1004,7 +1002,13 @@ void MainWindow::onMDUpdated(bs::network::Asset::Type assetType ui_->widgetPortfolio->onMDUpdated(assetType, security, fields); } -void bs::gui::qt::MainWindow::onAuthAddresses(const std::vector &addrs +void bs::gui::qt::MainWindow::onBalance(const std::string& currency, double balance) +{ + statusBarView_->onBalanceUpdated(currency, balance); + ui_->widgetRFQ->onBalance(currency, balance); +} + +void MainWindow::onAuthAddresses(const std::vector &addrs , const std::map& states) { if (authAddrDlg_) { @@ -1012,13 +1016,23 @@ void bs::gui::qt::MainWindow::onAuthAddresses(const std::vector &ad } } -void bs::gui::qt::MainWindow::onSubmittedAuthAddresses(const std::vector& addrs) +void MainWindow::onSubmittedAuthAddresses(const std::vector& addrs) { if (authAddrDlg_) { authAddrDlg_->onSubmittedAuthAddresses(addrs); } } +void MainWindow::onVerifiedAuthAddresses(const std::vector& addrs) +{ + ui_->widgetRFQ->onVerifiedAuthAddresses(addrs); +} + +void bs::gui::qt::MainWindow::onQuoteReceived(const bs::network::Quote& quote) +{ + ui_->widgetRFQ->onQuoteReceived(quote); +} + void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") @@ -1160,7 +1174,7 @@ void MainWindow::onSignerVisibleChanged() void MainWindow::initWidgets() { ui_->widgetWallets->init(logger_); -// connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &MainWindow::createNewWallet); + connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &MainWindow::createNewWallet); connect(ui_->widgetWallets, &WalletsWidget::needHDWalletDetails, this, &MainWindow::needHDWalletDetails); connect(ui_->widgetWallets, &WalletsWidget::needWalletBalances, this, &MainWindow::needWalletBalances); connect(ui_->widgetWallets, &WalletsWidget::needUTXOs, this, &MainWindow::needUTXOs); @@ -1181,59 +1195,18 @@ void MainWindow::initWidgets() ui_->widgetPortfolio->init(applicationSettings_, mdProvider_, mdCallbacks_ , portfolioModel_, signContainer_, armory_, utxoReservationMgr_, logMgr_->logger("ui"), walletsMgr_);*/ -// ui_->widgetRFQ->initWidgets(mdProvider_, mdCallbacks_, applicationSettings_); - -#if 0 - const auto aqScriptRunner = new AQScriptRunner(quoteProvider, signContainer_ - , mdCallbacks_, assetManager_, logger); - if (!applicationSettings_->get(ApplicationSettings::ExtConnName).empty() - && !applicationSettings_->get(ApplicationSettings::ExtConnHost).empty() - && !applicationSettings_->get(ApplicationSettings::ExtConnPort).empty() - /*&& !applicationSettings_->get(ApplicationSettings::ExtConnPubKey).empty()*/) { - ExtConnections extConns; -/* bs::network::BIP15xParams params; - params.ephemeralPeers = true; - params.cookie = bs::network::BIP15xCookie::ReadServer; - params.serverPublicKey = BinaryData::CreateFromHex(applicationSettings_->get( - ApplicationSettings::ExtConnPubKey)); - const auto &bip15xTransport = std::make_shared(logger, params); - bip15xTransport->setKeyCb(cbApproveExtConn_);*/ - - logger->debug("Setting up ext connection"); - auto connection = std::make_shared(logger, WsDataConnectionParams{ }); - //TODO: BIP15x will be superceded with SSL with certificate checking on both ends -// auto wsConnection = std::make_unique(logger, WsDataConnectionParams{}); -// auto connection = std::make_shared(logger, std::move(wsConnection), bip15xTransport); - if (connection->openConnection(applicationSettings_->get(ApplicationSettings::ExtConnHost) - , applicationSettings_->get(ApplicationSettings::ExtConnPort) - , aqScriptRunner->getExtConnListener().get())) { - extConns[applicationSettings_->get(ApplicationSettings::ExtConnName)] = connection; - } - aqScriptRunner->setExtConnections(extConns); - } - - autoSignQuoteProvider_ = std::make_shared(logger - , aqScriptRunner, applicationSettings_, signContainer_, celerConnection_); - - const auto rfqScriptRunner = new RFQScriptRunner(mdCallbacks_, logger, nullptr); - autoSignRFQProvider_ = std::make_shared(logger - , rfqScriptRunner, applicationSettings_, signContainer_, celerConnection_); -#endif //0 - - auto dialogManager = std::make_shared(this); + orderListModel_ = std::make_shared(this); + dialogMgr_ = std::make_shared(this); -/* ui_->widgetRFQ->init(logger, celerConnection_, authManager_, quoteProvider - , assetManager_, dialogManager, signContainer_, armory_, autoSignRFQProvider_ - , utxoReservationMgr_, orderListModel_.get()); - ui_->widgetRFQReply->init(logger, celerConnection_, authManager_ - , quoteProvider, mdCallbacks_, assetManager_, applicationSettings_, dialogManager - , signContainer_, armory_, connectionManager_, autoSignQuoteProvider_ - , utxoReservationMgr_, orderListModel_.get()); + ui_->widgetRFQ->init(logger_, dialogMgr_, orderListModel_.get()); + connect(ui_->widgetRFQ, &RFQRequestWidget::requestPrimaryWalletCreation, this, &MainWindow::createNewWallet); + connect(ui_->widgetRFQ, &RFQRequestWidget::needSubmitRFQ, this, &MainWindow::needSubmitRFQ); + connect(ui_->widgetRFQ, &RFQRequestWidget::needAcceptRFQ, this, &MainWindow::needAcceptRFQ); + connect(ui_->widgetRFQ, &RFQRequestWidget::needCancelRFQ, this, &MainWindow::needCancelRFQ); - connect(ui_->widgetRFQ, &RFQRequestWidget::requestPrimaryWalletCreation, this - , &BSTerminalMainWindow::onCreatePrimaryWalletRequest); connect(ui_->widgetRFQReply, &RFQReplyWidget::requestPrimaryWalletCreation, this - , &BSTerminalMainWindow::onCreatePrimaryWalletRequest);*/ + , &MainWindow::createNewWallet); + ui_->widgetRFQReply->init(logger_, dialogMgr_, orderListModel_.get()); connect(ui_->tabWidget, &QTabWidget::tabBarClicked, this, [/*requestRFQ = QPointer(ui_->widgetRFQ) diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 1c870dfad..6e22cec0d 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -19,6 +19,7 @@ #include "AuthAddress.h" #include "BaseCelerClient.h" #include "BsClient.h" +#include "CommonTypes.h" #include "SignContainer.h" #include "Settings/SignersProvider.h" #include "UiUtils.h" @@ -40,8 +41,10 @@ class AboutDialog; class AuthAddressDialog; class ConfigDialog; class CreateTransactionDialog; +class DialogManager; class LoginWindow; class NotificationCenter; +class OrderListModel; class QSystemTrayIcon; class StatusBarView; class TransactionsViewModel; @@ -99,10 +102,14 @@ namespace bs { void onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &); + void onBalance(const std::string& currency, double balance); void onAuthAddresses(const std::vector& , const std::map &); void onSubmittedAuthAddresses(const std::vector&); + void onVerifiedAuthAddresses(const std::vector&); + + void onQuoteReceived(const bs::network::Quote&); public slots: void onReactivate(); @@ -169,6 +176,9 @@ namespace bs { void needNewAuthAddress(); void needSubmitAuthAddress(const bs::Address&); + void needSubmitRFQ(const bs::network::RFQ&); + void needAcceptRFQ(const std::string& id, const bs::network::Quote&); + void needCancelRFQ(const std::string& id); private slots: void onSend(); @@ -240,11 +250,12 @@ namespace bs { std::shared_ptr statusBarView_; std::shared_ptr sysTrayIcon_; std::shared_ptr notifCenter_; - // std::shared_ptr transactionsModel_; // std::shared_ptr portfolioModel_; - // std::shared_ptr orderListModel_; std::shared_ptr txModel_; + std::shared_ptr orderListModel_; + + std::shared_ptr dialogMgr_; CreateTransactionDialog* txDlg_{ nullptr }; ConfigDialog* cfgDlg_{ nullptr }; LoginWindow* loginDlg_{ nullptr }; diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 3ec451058..fccb1fbe6 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -30,6 +30,7 @@ #include "MainWindow.h" #include "ProtobufHeadlessUtils.h" #include "SettingsAdapter.h" +#include "TradesVerification.h" #include "common.pb.h" #include "terminal.pb.h" @@ -42,6 +43,7 @@ Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult) Q_DECLARE_METATYPE(std::string) Q_DECLARE_METATYPE(std::vector) Q_DECLARE_METATYPE(std::vector); +Q_DECLARE_METATYPE(bs::PayoutSignatureType) #if defined (Q_OS_MAC) class MacOsApp : public QApplication @@ -594,6 +596,8 @@ bool QtGuiAdapter::processOnChainTrack(const Envelope &env) break; case OnChainTrackMessage::kAuthState: return processAuthState(msg.auth_state()); + case OnChainTrackMessage::kVerifiedAuthAddresses: + return processVerifiedAuthAddrs(msg.verified_auth_addresses()); default: break; } return true; @@ -609,6 +613,8 @@ bool QtGuiAdapter::processAssets(const bs::message::Envelope& env) switch (msg.data_case()) { case AssetsMessage::kSubmittedAuthAddrs: return processSubmittedAuthAddrs(msg.submitted_auth_addrs()); + case AssetsMessage::kBalance: + return processBalance(msg.balance()); default: break; } return true; @@ -760,6 +766,9 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needMdConnection, this, &QtGuiAdapter::onNeedMdConnection); connect(mainWindow_, &bs::gui::qt::MainWindow::needNewAuthAddress, this, &QtGuiAdapter::onNeedNewAuthAddress); connect(mainWindow_, &bs::gui::qt::MainWindow::needSubmitAuthAddress, this, &QtGuiAdapter::onNeedSubmitAuthAddress); + connect(mainWindow_, &bs::gui::qt::MainWindow::needSubmitRFQ, this, &QtGuiAdapter::onNeedSubmitRFQ); + connect(mainWindow_, &bs::gui::qt::MainWindow::needAcceptRFQ, this, &QtGuiAdapter::onNeedAcceptRFQ); + connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelRFQ, this, &QtGuiAdapter::onNeedCancelRFQ); } void QtGuiAdapter::onGetSettings(const std::vector& settings) @@ -1199,6 +1208,37 @@ void QtGuiAdapter::onNeedSubmitAuthAddress(const bs::Address& addr) pushFill(env); } +void QtGuiAdapter::onNeedSubmitRFQ(const bs::network::RFQ& rfq) +{ + MatchingMessage msg; + auto msgReq = msg.mutable_send_rfq(); + msgReq->set_id(rfq.requestId); + msgReq->set_security(rfq.security); + msgReq->set_product(rfq.product); + msgReq->set_asset_type((int)rfq.assetType); + msgReq->set_buy(rfq.side == bs::network::Side::Buy); + msgReq->set_quantity(rfq.quantity); + msgReq->set_auth_pub_key(rfq.requestorAuthPublicKey); + msgReq->set_receipt_address(rfq.receiptAddress); + msgReq->set_coin_tx_input(rfq.coinTxInput); + Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedAcceptRFQ(const std::string& id, const bs::network::Quote& quote) +{ + MatchingMessage msg; + auto msgReq = msg.mutable_accept_rfq(); + msgReq->set_rfq_id(id); + toMsg(quote, msgReq->mutable_quote()); + Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedCancelRFQ(const std::string& id) +{ +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -1516,6 +1556,8 @@ bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMatchingLogout(); }); + case MatchingMessage::kQuote: + return processQuote(msg.quote()); default: break; } return true; @@ -1604,4 +1646,33 @@ bool QtGuiAdapter::processSubmittedAuthAddrs(const AssetsMessage_SubmittedAuthAd }); } +bool QtGuiAdapter::processBalance(const AssetsMessage_Balance& bal) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, bal] { + mainWindow_->onBalance(bal.currency(), bal.value()); + }); +} + +bool QtGuiAdapter::processVerifiedAuthAddrs(const OnChainTrackMessage_AuthAddresses& addrs) +{ + std::vector authAddresses; + authAddresses.reserve(addrs.addresses_size()); + for (const auto& addr : addrs.addresses()) { + try { + authAddresses.push_back(bs::Address::fromAddressString(addr)); + } catch (const std::exception&) {} + } + return QMetaObject::invokeMethod(mainWindow_, [this, authAddresses] { + mainWindow_->onVerifiedAuthAddresses(authAddresses); + }); +} + +bool QtGuiAdapter::processQuote(const MatchingMessage_Quote& msg) +{ + const auto& quote = fromMsg(msg); + return QMetaObject::invokeMethod(mainWindow_, [this, quote] { + mainWindow_->onQuoteReceived(quote); + }); +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 91d6114cb..71aa973a8 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -33,6 +33,7 @@ namespace BlockSettle { class ArmoryMessage_LedgerEntries; class ArmoryMessage_ZCInvalidated; class ArmoryMessage_ZCReceived; + class OnChainTrackMessage_AuthAddresses; class OnChainTrackMessage_AuthState; class SignerMessage_SignTxResponse; class WalletsMessage_TXDetailsResponse; @@ -42,10 +43,12 @@ namespace BlockSettle { class WalletsMessage_WalletsListResponse; } namespace Terminal { + class AssetsMessage_Balance; class AssetsMessage_SubmittedAuthAddresses; class BsServerMessage_LoginResult; class BsServerMessage_StartLoginResult; class MatchingMessage_LoggedIn; + class MatchingMessage_Quote; class MktDataMessage_Prices; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; @@ -120,6 +123,9 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processAuthWallet(const BlockSettle::Common::WalletsMessage_WalletData&); bool processAuthState(const BlockSettle::Common::OnChainTrackMessage_AuthState&); bool processSubmittedAuthAddrs(const BlockSettle::Terminal::AssetsMessage_SubmittedAuthAddresses&); + bool processBalance(const BlockSettle::Terminal::AssetsMessage_Balance&); + bool processVerifiedAuthAddrs(const BlockSettle::Common::OnChainTrackMessage_AuthAddresses&); + bool processQuote(const BlockSettle::Terminal::MatchingMessage_Quote&); private slots: void onGetSettings(const std::vector&); @@ -168,6 +174,9 @@ private slots: void onNeedMdConnection(ApplicationSettings::EnvConfiguration); void onNeedNewAuthAddress(); void onNeedSubmitAuthAddress(const bs::Address&); + void onNeedSubmitRFQ(const bs::network::RFQ&); + void onNeedAcceptRFQ(const std::string& id, const bs::network::Quote&); + void onNeedCancelRFQ(const std::string& id); private: std::shared_ptr logger_; diff --git a/UnitTests/TestNetwork.cpp b/UnitTests/TestNetwork.cpp index ab8cfe8c1..2064a9f7c 100644 --- a/UnitTests/TestNetwork.cpp +++ b/UnitTests/TestNetwork.cpp @@ -13,6 +13,7 @@ #include "Bip15xDataConnection.h" #include "Bip15xServerConnection.h" +#include "Celer/CommonCelerUtils.h" #include "CelerMessageMapper.h" #include "CommonTypes.h" #include "IdStringGenerator.h" @@ -76,14 +77,14 @@ using namespace bs::network; TEST(TestNetwork, Types) { for (const auto t : {Side::Buy, Side::Sell}) { - const auto celerType = Side::toCeler(t); - EXPECT_EQ(Side::fromCeler(celerType), t) << "Side " << Side::toString(t); + const auto celerType = bs::celer::toCeler(t); + EXPECT_EQ(bs::celer::fromCeler(celerType), t) << "Side " << Side::toString(t); } for (int i = bs::network::Asset::first; i < bs::network::Asset::last; i++) { const auto t = static_cast(i); - const auto celerProdType = bs::network::Asset::toCelerProductType(t); - EXPECT_EQ(bs::network::Asset::fromCelerProductType(celerProdType), t) << "Asset type " << bs::network::Asset::toString(t); + const auto celerProdType = bs::celer::toCelerProductType(t); + EXPECT_EQ(bs::celer::fromCelerProductType(celerProdType), t) << "Asset type " << bs::network::Asset::toString(t); } } diff --git a/common b/common index 11fa7be8f..c9389c634 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 11fa7be8fd404cb7a367fe72768224794d712c8e +Subproject commit c9389c634e7117ef1d5506c7f5f9e2f7e478e417 From 6466d248e321da98db44d8e4a534db03baa99412 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 19 Oct 2020 14:26:44 +0300 Subject: [PATCH 021/146] Celer as a separate lib --- BlockSettleSigner/SignerAdapterContainer.cpp | 2 +- BlockSettleSigner/SignerInterfaceListener.cpp | 2 +- BlockSettleUILib/BSTerminalMainWindow.h | 2 +- BlockSettleUILib/CelerAccountInfoDialog.cpp | 2 +- BlockSettleUILib/StatusBarView.h | 2 +- .../Trading/AutoSignQuoteProvider.cpp | 2 +- .../Trading/QuoteRequestsModel.cpp | 3 +-- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 4 ++-- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 2 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 2 +- .../Trading/RequestingQuoteWidget.cpp | 2 +- Core/MatchingAdapter.cpp | 20 +++++++++---------- Core/MatchingAdapter.h | 2 +- GUI/QtWidgets/MainWindow.h | 2 +- Scripts/RFQBot_LMAX.qml | 4 ++-- UnitTests/TestEnv.cpp | 2 +- UnitTests/TestNetwork.cpp | 4 ++-- common | 2 +- 18 files changed, 30 insertions(+), 31 deletions(-) diff --git a/BlockSettleSigner/SignerAdapterContainer.cpp b/BlockSettleSigner/SignerAdapterContainer.cpp index 220c433d0..cba832169 100644 --- a/BlockSettleSigner/SignerAdapterContainer.cpp +++ b/BlockSettleSigner/SignerAdapterContainer.cpp @@ -13,7 +13,7 @@ #include #include #include -#include "CelerClientConnection.h" +#include "Celer/ClientConnection.h" #include "DataConnection.h" #include "DataConnectionListener.h" #include "HeadlessApp.h" diff --git a/BlockSettleSigner/SignerInterfaceListener.cpp b/BlockSettleSigner/SignerInterfaceListener.cpp index 0ab4e75d4..38df73490 100644 --- a/BlockSettleSigner/SignerInterfaceListener.cpp +++ b/BlockSettleSigner/SignerInterfaceListener.cpp @@ -16,7 +16,7 @@ #include #include #include -#include "CelerClientConnection.h" +#include "Celer/ClientConnection.h" #include "DataConnection.h" #include "HeadlessApp.h" #include "Wallets/SyncWalletsManager.h" diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index d1180f96f..f0e2f5639 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -20,7 +20,7 @@ #include "ApplicationSettings.h" #include "ArmoryObject.h" #include "BsClient.h" -#include "CelerClientProxy.h" +#include "Celer/CelerClientProxy.h" #include "QWalletInfo.h" #include "SignContainer.h" #include "WalletSignerContainer.h" diff --git a/BlockSettleUILib/CelerAccountInfoDialog.cpp b/BlockSettleUILib/CelerAccountInfoDialog.cpp index b2cf3ca86..878dd418e 100644 --- a/BlockSettleUILib/CelerAccountInfoDialog.cpp +++ b/BlockSettleUILib/CelerAccountInfoDialog.cpp @@ -11,7 +11,7 @@ #include "CelerAccountInfoDialog.h" #include "ui_CelerAccountInfoDialog.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" CelerAccountInfoDialog::CelerAccountInfoDialog(std::shared_ptr celerConnection, QWidget* parent) : QDialog(parent) diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index ebfd9a2db..682b56ff4 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -19,7 +19,7 @@ #include #include "ArmoryConnection.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "CircleProgressBar.h" #include "SignContainer.h" diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp index b79cc9c61..a868467a6 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp @@ -16,7 +16,7 @@ #include "Wallets/SyncHDWallet.h" #include "UserScriptRunner.h" -#include +#include #include #include #include diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index eaefcad61..0a1de34f4 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -12,8 +12,7 @@ #include "ApplicationSettings.h" #include "AssetManager.h" -#include "CelerClient.h" -#include "CelerSubmitQuoteNotifSequence.h" +#include "Celer/CelerClient.h" #include "Colors.h" #include "CommonTypes.h" #include "CurrencyPair.h" diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index f6b04b78f..e849f440c 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -15,8 +15,8 @@ #include "AuthAddressManager.h" #include "AutoSignQuoteProvider.h" #include "BSMessageBox.h" -#include "CelerClient.h" -#include "CelerSubmitQuoteNotifSequence.h" +#include "Celer/CelerClient.h" +#include "Celer/SubmitQuoteNotifSequence.h" #include "CustomControls/CustomDoubleSpinBox.h" #include "DealerCCSettlementContainer.h" #include "DealerXBTSettlementContainer.h" diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 6f12fcb89..066f66ad4 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -16,7 +16,7 @@ #include "ApplicationSettings.h" #include "AuthAddressManager.h" #include "AutoSignQuoteProvider.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "CurrencyPair.h" #include "DialogManager.h" #include "NotificationCenter.h" diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index f907ee4cf..77fc4644e 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -14,7 +14,7 @@ #include #include #include -#include "BaseCelerClient.h" +#include "Celer/BaseCelerClient.h" #include "CommonTypes.h" #include "MarketDataWidget.h" #include "TabWithShortcut.h" diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index c319c6eb4..95e5a2aff 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -16,7 +16,7 @@ #include "BlockDataManagerConfig.h" #include "CurrencyPair.h" #include "UiUtils.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" // XXX [AT] : possible concurent change of states - could lead to multiple signals emited // add atomic flag diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index 2a9614a3d..e3a4be675 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -10,12 +10,12 @@ */ #include "MatchingAdapter.h" #include -#include "Celer/CommonCelerUtils.h" -#include "CelerClientProxy.h" -#include "CelerCreateOrderSequence.h" -#include "CelerCreateFxOrderSequence.h" -#include "CelerGetAssignedAccountsListSequence.h" -#include "CelerSubmitRFQSequence.h" +#include "Celer/CommonUtils.h" +#include "Celer/CelerClientProxy.h" +#include "Celer/CreateOrderSequence.h" +#include "Celer/CreateFxOrderSequence.h" +#include "Celer/GetAssignedAccountsListSequence.h" +#include "Celer/SubmitRFQSequence.h" #include "CurrencyPair.h" #include "ProtobufUtils.h" #include "TerminalMessage.h" @@ -74,7 +74,7 @@ void MatchingAdapter::connectedToServer() } } }; - if (!celerConnection_->ExecuteSequence(std::make_shared( + if (!celerConnection_->ExecuteSequence(std::make_shared( logger_, cbAccounts))) { logger_->error("[{}] failed to get accounts", __func__); } @@ -202,7 +202,7 @@ bool MatchingAdapter::processSendRFQ(const MatchingMessage_RFQ& request) rfq.requestorAuthPublicKey = request.auth_pub_key(); rfq.receiptAddress = request.receipt_address(); rfq.coinTxInput = request.coin_tx_input(); - auto sequence = std::make_shared(assignedAccount_, rfq + auto sequence = std::make_shared(assignedAccount_, rfq , logger_, true); if (!celerConnection_->ExecuteSequence(sequence)) { logger_->error("[MatchingAdapter::processSendRFQ] failed to execute CelerSubmitRFQSequence"); @@ -221,7 +221,7 @@ bool MatchingAdapter::processAcceptRFQ(const MatchingMessage_AcceptRFQ& request) const auto& reqId = QString::fromStdString(request.rfq_id()); const auto& quote = fromMsg(request.quote()); if (quote.assetType == bs::network::Asset::SpotFX) { - auto sequence = std::make_shared(assignedAccount_ + auto sequence = std::make_shared(assignedAccount_ , reqId, quote, logger_); if (!celerConnection_->ExecuteSequence(sequence)) { logger_->error("[MatchingAdapter::processAcceptRFQ] failed to execute CelerCreateFxOrderSequence"); @@ -230,7 +230,7 @@ bool MatchingAdapter::processAcceptRFQ(const MatchingMessage_AcceptRFQ& request) } } else { - auto sequence = std::make_shared(assignedAccount_ + auto sequence = std::make_shared(assignedAccount_ , reqId, quote, request.payout_tx(), logger_); if (!celerConnection_->ExecuteSequence(sequence)) { logger_->error("[MatchingAdapter::processAcceptRFQ] failed to execute CelerCreateOrderSequence"); diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index bf91b6c82..88b8fda1c 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -11,7 +11,7 @@ #ifndef MATCHING_ADAPTER_H #define MATCHING_ADAPTER_H -#include "BaseCelerClient.h" +#include "Celer/BaseCelerClient.h" #include "Message/Adapter.h" namespace spdlog { diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 6e22cec0d..e69671f17 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -17,8 +17,8 @@ #include "Address.h" #include "ArmoryConnection.h" #include "AuthAddress.h" -#include "BaseCelerClient.h" #include "BsClient.h" +#include "Celer/BaseCelerClient.h" #include "CommonTypes.h" #include "SignContainer.h" #include "Settings/SignersProvider.h" diff --git a/Scripts/RFQBot_LMAX.qml b/Scripts/RFQBot_LMAX.qml index fa0c38e00..c2fac2481 100644 --- a/Scripts/RFQBot_LMAX.qml +++ b/Scripts/RFQBot_LMAX.qml @@ -272,8 +272,8 @@ BSQuoteReqReply { return } if (isXBT()) { - if (quoteReq.quantity > 1.0) { - log('XBT amount exceeds limit: ' + quoteReq.quantity) + if (hedgeOrderAmount(price) > 1.0) { + log('XBT amount exceeds limit: ' + hedgeOrderAmount(price)) return } } diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index 6cebe7b4e..7224529d7 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -15,7 +15,7 @@ #include "ArmoryObject.h" #include "ArmorySettings.h" #include "AuthAddressManager.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "ConnectionManager.h" #include "CoreWalletsManager.h" #include "MarketDataProvider.h" diff --git a/UnitTests/TestNetwork.cpp b/UnitTests/TestNetwork.cpp index 2064a9f7c..e64827194 100644 --- a/UnitTests/TestNetwork.cpp +++ b/UnitTests/TestNetwork.cpp @@ -13,8 +13,8 @@ #include "Bip15xDataConnection.h" #include "Bip15xServerConnection.h" -#include "Celer/CommonCelerUtils.h" -#include "CelerMessageMapper.h" +#include "Celer/CommonUtils.h" +#include "Celer/MessageMapper.h" #include "CommonTypes.h" #include "IdStringGenerator.h" #include "QuoteProvider.h" diff --git a/common b/common index c9389c634..af4045f6f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit c9389c634e7117ef1d5506c7f5f9e2f7e478e417 +Subproject commit af4045f6f77e180bcc061691af4decfb4eb13d7a From 8c65a966e31173c44c5b55ebb54092c18f0f7827 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 20 Oct 2020 20:12:05 +0300 Subject: [PATCH 022/146] Trade blotter --- BlockSettleSigner/main.cpp | 2 +- BlockSettleUILib/OrderListModel.cpp | 40 +++++-- BlockSettleUILib/OrderListModel.h | 18 +-- BlockSettleUILib/Trading/RFQDialog.cpp | 14 ++- BlockSettleUILib/Trading/RFQDialog.h | 7 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 22 +++- BlockSettleUILib/Trading/RFQReplyWidget.h | 3 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 26 +++++ BlockSettleUILib/Trading/RFQRequestWidget.h | 4 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 19 ++- .../Trading/RequestingQuoteWidget.cpp | 28 ++++- .../Trading/RequestingQuoteWidget.h | 9 +- BlockSettleUILib/TransactionsViewModel.cpp | 7 +- Core/BsServerAdapter.cpp | 43 +++++++ Core/BsServerAdapter.h | 10 +- Core/MatchingAdapter.cpp | 109 ++++++++++++++++++ Core/MatchingAdapter.h | 9 ++ Core/MktDataAdapter.cpp | 5 +- Core/TerminalMessage.cpp | 46 ++++++++ Core/TerminalMessage.h | 4 + GUI/QtWidgets/MainWindow.cpp | 14 +++ GUI/QtWidgets/MainWindow.h | 2 + GUI/QtWidgets/QtGuiAdapter.cpp | 95 ++++++++++++--- GUI/QtWidgets/QtGuiAdapter.h | 9 ++ common | 2 +- 25 files changed, 482 insertions(+), 65 deletions(-) diff --git a/BlockSettleSigner/main.cpp b/BlockSettleSigner/main.cpp index e71e4fc35..e61ccb402 100644 --- a/BlockSettleSigner/main.cpp +++ b/BlockSettleSigner/main.cpp @@ -336,7 +336,7 @@ static int QMLApp(int argc, char **argv terminalConnectionTimer.setSingleShot(true); //BST-2786 - terminalConnectionTimer.setInterval(std::chrono::milliseconds{ 5000 }); + terminalConnectionTimer.setInterval(std::chrono::milliseconds{ /*5*/7000 }); // 5s is too little for some use cases // NOTE: SignerAdapter::ready is called multiple times QObject::connect(&adapter, SIGNAL(ready()), &terminalConnectionTimer, SLOT(start())); diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 620356eb4..e8e77ddbb 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -622,14 +622,27 @@ void OrderListModel::reset() endResetModel(); } -void OrderListModel::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) +void OrderListModel::onOrdersUpdate(const std::vector& orders) { + if (!connected_) { //FIXME: use BS connection event (currently matching one) to set connected_ flag + connected_ = true; + } // Save latest selected index first - resetLatestChangedStatus(message); + resetLatestChangedStatus(orders); // OrderListModel supposed to work correctly when orders states updated one by one. // We don't use this anymore (server sends all active orders every time) so just clear old caches. // Remove this if old behavior is needed reset(); + + for (const auto& order : orders) { + onOrderUpdated(order); + } +} + +void OrderListModel::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) +{ + std::vector orders; + // Use some fake orderId so old code works correctly int orderId = 0; @@ -671,18 +684,18 @@ void OrderListModel::processUpdateOrders(const Blocksettle::Communication::Proxy order.quantity = data.quantity(); order.security = data.product() + "/" + data.product_against(); order.price = data.price(); - - onOrderUpdated(order); + orders.push_back(order); } + onOrdersUpdate(orders); } -void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) +void OrderListModel::resetLatestChangedStatus(const std::vector &orders) { latestChangedTimestamp_ = {}; - std::vector> newOrderStatuses(message.orders_size()); - for (const auto &data : message.orders()) { - newOrderStatuses.push_back({ data.timestamp_ms(), static_cast(data.status()) }); + std::vector> newOrderStatuses(orders.size()); + for (const auto &order : orders) { + newOrderStatuses.push_back({ order.dateTime.toMSecsSinceEpoch(), static_cast(order.status) }); } std::sort(newOrderStatuses.begin(), newOrderStatuses.end(), [&](const auto &left, const auto &right) { return left.first < right.first; @@ -702,7 +715,6 @@ void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication:: } } } - sortedPeviousOrderStatuses_ = std::move(newOrderStatuses); } @@ -751,3 +763,13 @@ void OrderListModel::onOrderUpdated(const bs::network::Order& order) setOrderStatus(groupItem, found.second, order, true); } } + +void OrderListModel::onMatchingLogin() +{ + connected_ = true; +} + +void OrderListModel::onMatchingLogout() +{ + connected_ = false; +} diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index d08a7e3d7..5843671da 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -16,7 +16,6 @@ #include #include "CommonTypes.h" - #include #include #include @@ -62,6 +61,11 @@ class OrderListModel : public QAbstractItemModel int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + void onOrdersUpdate(const std::vector &); + void onMatchingLogin(); + void onMatchingLogout(); + public slots: void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); void onDisconnected(); @@ -87,8 +91,7 @@ public slots: : parent_(parent) , data_(data) , type_(type) - { - } + {} }; struct Data { @@ -164,15 +167,13 @@ public slots: : name_(name) , idx_(nullptr, this, DataType::StatusGroup) , row_(row) - { - } + {} static QString toString(Type); }; static StatusGroup::Type getStatusGroup(const bs::network::Order &); - void onOrderUpdated(const bs::network::Order &); int findGroup(Market *market, Group *group) const; int findMarket(StatusGroup *statusGroup, Market *market) const; std::pair findItem(const bs::network::Order &order); @@ -183,8 +184,9 @@ public slots: void createGroupsIfNeeded(const bs::network::Order &order, Market *&market, Group *&group); void reset(); - void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &msg); - void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message); + [[deprecated]] void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &msg); + void resetLatestChangedStatus(const std::vector&); + void onOrderUpdated(const bs::network::Order&); std::shared_ptr assetManager_; std::unordered_map groups_; diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 9ea217ebe..f54568533 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -348,6 +348,16 @@ void RFQDialog::cancel(bool force) } } +void RFQDialog::onBalance(const std::string& currency, double balance) +{ + ui_->pageRequestingQuote->onBalance(currency, balance); +} + +void RFQDialog::onMatchingLogout() +{ + ui_->pageRequestingQuote->onMatchingLogout(); +} + void RFQDialog::onTimeout() { emit expired(id_); @@ -357,7 +367,9 @@ void RFQDialog::onTimeout() void RFQDialog::onQuoteFinished() { - emit accepted(id_, quote_); + if (quoteProvider_) { + emit accepted(id_, quote_); + } cancelOnClose_ = false; hide(); } diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index 7a2eab7b7..ce40e94a8 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -88,6 +88,9 @@ Q_OBJECT void cancel(bool force = true); + void onBalance(const std::string& currency, double balance); + void onMatchingLogout(); + signals: void accepted(const std::string &id, const bs::network::Quote&); void expired(const std::string &id); @@ -102,6 +105,8 @@ public slots: void onSignedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); void onQuoteReceived(const bs::network::Quote& quote); + void onOrderFilled(const std::string& quoteId); + void onOrderFailed(const std::string& quoteId, const std::string& reason); private slots: bool close(); @@ -110,8 +115,6 @@ private slots: void onQuoteFailed(); void onRFQResponseAccepted(const std::string &reqId, const bs::network::Quote& quote); - void onOrderFilled(const std::string "eId); - void onOrderFailed(const std::string& quoteId, const std::string& reason); void onXBTSettlementAccepted(); void onSignTxRequested(QString orderId, QString reqId, QDateTime timestamp); diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index e849f440c..d4592c26a 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -222,7 +222,6 @@ void RFQReplyWidget::init(const std::shared_ptr &logger ui_->treeViewOrders->setModel(orderListModel); ui_->treeViewOrders->initWithModel(orderListModel); - connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this , &RFQReplyWidget::onConnectedToCeler); connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this @@ -232,11 +231,22 @@ void RFQReplyWidget::init(const std::shared_ptr &logger , this, &RFQReplyWidget::onEnterKeyPressed); } - void RFQReplyWidget::init(const std::shared_ptr& - , const std::shared_ptr&, OrderListModel* orderListModel) - { - //TODO - } +void RFQReplyWidget::init(const std::shared_ptr& logger + , const std::shared_ptr& dialogMgr, OrderListModel* orderListModel) +{ + logger_ = logger; + dialogManager_ = dialogMgr; + + connect(ui_->pageRFQReply, &RFQDealerReply::pullQuoteNotif, this + , &RFQReplyWidget::onPulled); + + ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui_->treeViewOrders->setModel(orderListModel); + ui_->treeViewOrders->initWithModel(orderListModel); + + connect(ui_->widgetQuoteRequests->view(), &TreeViewWithEnterKey::enterKeyPressed + , this, &RFQReplyWidget::onEnterKeyPressed); +} void RFQReplyWidget::forceCheckCondition() { diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 25e391949..7163b72bb 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -93,8 +93,7 @@ Q_OBJECT , const std::shared_ptr & , OrderListModel *orderListModel); void init(const std::shared_ptr& - , const std::shared_ptr& - , OrderListModel* orderListModel); + , const std::shared_ptr&, OrderListModel*); void setWalletsManager(const std::shared_ptr &); void shortcutActivated(ShortcutType s) override; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 066f66ad4..7d78b9447 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -170,6 +170,7 @@ void RFQRequestWidget::onMDUpdated(bs::network::Asset::Type assetType void RFQRequestWidget::onBalance(const std::string& currency, double balance) { ui_->pageRFQTicket->onBalance(currency, balance); + balances_[currency] = balance; } void RFQRequestWidget::onMatchingLogin(const std::string& mtchLogin @@ -196,6 +197,11 @@ void RFQRequestWidget::onMatchingLogout() for (QMetaObject::Connection& conn : marketDataConnection_) { QObject::disconnect(conn); } + for (const auto& dialog : dialogs_) { + dialog.second->onMatchingLogout(); + dialog.second->deleteLater(); + } + dialogs_.clear(); userType_ = BaseCelerClient::CelerUserType::Undefined; ui_->shieldPage->showShieldLoginToSubmitRequired(); popShield(); @@ -213,6 +219,21 @@ void RFQRequestWidget::onQuoteReceived(const bs::network::Quote& quote) } } +void RFQRequestWidget::onOrderReceived(const bs::network::Order& order) +{ + for (const auto& dialog : dialogs_) { + switch (order.status) { + case bs::network::Order::Filled: + dialog.second->onOrderFilled(order.quoteId); + break; + case bs::network::Order::Failed: + dialog.second->onOrderFailed(order.quoteId, order.info); + break; + default: break; + } + } +} + void RFQRequestWidget::hideEvent(QHideEvent* event) { ui_->pageRFQTicket->onParentAboutToHide(); @@ -316,6 +337,7 @@ void RFQRequestWidget::init(const std::shared_ptr &logger void RFQRequestWidget::init(const std::shared_ptr&logger , const std::shared_ptr& dialogMgr, OrderListModel* orderListModel) { + logger_ = logger; dialogManager_ = dialogMgr; ui_->pageRFQTicket->init(logger); @@ -403,6 +425,9 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ dialogs_[id] = dialog; } ui_->pageRFQTicket->resetTicket(); + for (const auto& bal : balances_) { + dialog->onBalance(bal.first, bal.second); + } const auto& currentInfo = ui_->widgetMarketData->getCurrentlySelectedInfo(); ui_->pageRFQTicket->SetProductAndSide(currentInfo.productGroup_ @@ -414,6 +439,7 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ if (dlg.second->isHidden()) { dlg.second->deleteLater(); closedDialogs.push_back(dlg.first); + logger_->debug("[{}] erasing dialog {}", __func__, dlg.first); } } for (const auto &dlg : closedDialogs) { diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 77fc4644e..23dc47847 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -99,7 +99,8 @@ Q_OBJECT void onMatchingLogout(); void onVerifiedAuthAddresses(const std::vector&); - void onQuoteReceived(const bs::network::Quote& quote); + void onQuoteReceived(const bs::network::Quote&); + void onOrderReceived(const bs::network::Order&); protected: void hideEvent(QHideEvent* event) override; @@ -180,6 +181,7 @@ public slots: QList marketDataConnection_; + std::unordered_map balances_; std::unordered_map dialogs_; BaseCelerClient::CelerUserType userType_{ BaseCelerClient::CelerUserType::Undefined }; diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 1252a200f..b40cfd7f4 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -229,6 +229,9 @@ void RFQTicketXBT::updateBalances() RFQTicketXBT::BalanceInfoContainer RFQTicketXBT::getBalanceInfo() const { BalanceInfoContainer balance; + if (!assetManager_ && balances_.empty()) { + return balance; + } QString productToSpend = getProductToSpend(); @@ -243,11 +246,14 @@ RFQTicketXBT::BalanceInfoContainer RFQTicketXBT::getBalanceInfo() const balance.productType = ProductGroupType::CCGroupType; } else { const double divisor = std::pow(10, UiUtils::GetAmountPrecisionFX()); - const double bal = assetManager_ ? assetManager_->getBalance(productToSpend.toStdString()) - : balances_.at(productToSpend.toStdString()); - balance.amount = std::floor(bal * divisor) / divisor; - balance.product = productToSpend; - balance.productType = ProductGroupType::FXGroupType; + try { + const double bal = assetManager_ ? assetManager_->getBalance(productToSpend.toStdString()) + : balances_.at(productToSpend.toStdString()); + balance.amount = std::floor(bal * divisor) / divisor; + balance.product = productToSpend; + balance.productType = ProductGroupType::FXGroupType; + } + catch (const std::exception &) {} } } return balance; @@ -925,7 +931,7 @@ void RFQTicketXBT::sendRFQ(const std::string &id) auto rfq = itRFQ->second; if (rfq->requestId.empty()) { - rfq->requestId = "blocksettle:" + id; + rfq->requestId = id; } if (rfq->assetType == bs::network::Asset::SpotXBT) { @@ -1180,6 +1186,7 @@ void RFQTicketXBT::onParentAboutToHide() void RFQTicketXBT::onBalance(const std::string& currency, double balance) { balances_[currency] = balance; + updateBalances(); } void RFQTicketXBT::enablePanel() diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index 95e5a2aff..97bc7e7de 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -49,7 +49,8 @@ RequestingQuoteWidget::RequestingQuoteWidget(QWidget* parent) RequestingQuoteWidget::~RequestingQuoteWidget() = default; -void RequestingQuoteWidget::SetCelerClient(std::shared_ptr celerClient) { +void RequestingQuoteWidget::SetCelerClient(std::shared_ptr celerClient) +{ celerClient_ = celerClient; connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, @@ -152,8 +153,8 @@ bool RequestingQuoteWidget::onQuoteReceived(const bs::network::Quote& quote) timeoutReply_ = quote.expirationTime.addMSecs(quote.timeSkewMs); - const auto assetType = assetManager_->GetAssetTypeForSecurity(quote.security); - ui_->labelQuoteValue->setText(UiUtils::displayPriceForAssetType(quote.price, assetType)); + ui_->labelQuoteValue->setText(UiUtils::displayPriceForAssetType(quote.price + , quote.assetType)); ui_->labelQuoteValue->show(); if (quote.assetType == bs::network::Asset::SpotFX) { @@ -182,7 +183,16 @@ bool RequestingQuoteWidget::onQuoteReceived(const bs::network::Quote& quote) if (rfq_.side == bs::network::Side::Buy) { const auto currency = contrProductString.toStdString(); - const auto balance = assetManager_->getBalance(currency); + double balance = 0; + if (assetManager_) { + balance = assetManager_->getBalance(currency); + } + else { + try { + balance = balances_.at(currency); + } + catch (const std::exception&) {} + } balanceOk_ = (value < balance); ui_->pushButtonAccept->setEnabled(balanceOk_); if (!balanceOk_) { @@ -254,6 +264,16 @@ void RequestingQuoteWidget::populateDetails(const bs::network::RFQ& rfq) } } +void RequestingQuoteWidget::onBalance(const std::string& currency, double balance) +{ + balances_[currency] = balance; +} + +void RequestingQuoteWidget::onMatchingLogout() +{ + onCancel(); +} + void RequestingQuoteWidget::onAccept() { requestTimer_.stop(); diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.h b/BlockSettleUILib/Trading/RequestingQuoteWidget.h index 732f8bd16..3ec666b47 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.h +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.h @@ -32,14 +32,16 @@ Q_OBJECT RequestingQuoteWidget(QWidget* parent = nullptr ); ~RequestingQuoteWidget() override; - void SetAssetManager(const std::shared_ptr &assetManager) { + [[deprecated]] void SetAssetManager(const std::shared_ptr &assetManager) { assetManager_ = assetManager; } - - void SetCelerClient(std::shared_ptr celerClient); + [[deprecated]] void SetCelerClient(std::shared_ptr celerClient); void populateDetails(const bs::network::RFQ& rfq); + void onBalance(const std::string& currency, double balance); + void onMatchingLogout(); + public slots: void ticker(); bool onQuoteReceived(const bs::network::Quote& quote); @@ -80,6 +82,7 @@ public slots: std::shared_ptr assetManager_; bool balanceOk_ = true; std::shared_ptr celerClient_; + std::unordered_map balances_; private: void setupTimer(Status status, const QDateTime &expTime); diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index a9aa71cbf..e2ae826ca 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -615,7 +615,9 @@ std::shared_ptr TransactionsViewModel::createTxItem(const item->txEntry = entry; item->displayDateTime = UiUtils::displayDateTime(entry.txTime); // item->filterAddress = filterAddress_; - item->walletID = QString::fromStdString(*entry.walletIds.cbegin()); + if (!entry.walletIds.empty()) { + item->walletID = QString::fromStdString(*entry.walletIds.cbegin()); + } item->confirmations = entry.nbConf; if (!item->wallets.empty()) { item->walletName = QString::fromStdString(item->wallets[0]->name()); @@ -1147,6 +1149,9 @@ void TransactionsViewModel::onLedgerEntries(const std::string &, uint32_t std::vector newNodes; for (const auto &entry : entries) { + if (entry.txHash.empty()) { // invalid entry + continue; + } const auto &item = createTxItem(entry); const auto &itItem = itemIndex_.find({entry.txHash, item->walletID.toStdString()}); if (itItem == itemIndex_.end()) { diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 1b6f0c32f..7c380c4c6 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -18,6 +18,7 @@ #include "TerminalMessage.h" #include "WsDataConnection.h" +#include "bs_proxy_terminal_pb.pb.h" #include "bs_communication.pb.h" #include "terminal.pb.h" @@ -264,6 +265,37 @@ bool BsServerAdapter::processSubmitAuthAddr(const bs::message::Envelope& env return true; } +void BsServerAdapter::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders& orders) +{ + BsServerMessage msg; + auto msgOrders = msg.mutable_orders_update(); + for (const auto& order : orders.orders()) { + auto msgOrder = msgOrders->add_orders(); + switch (order.status()) { + case bs::types::ORDER_STATUS_PENDING: + msgOrder->set_status((int)bs::network::Order::Pending); + break; + case bs::types::ORDER_STATUS_FILLED: + msgOrder->set_status((int)bs::network::Order::Filled); + break; + case bs::types::ORDER_STATUS_VOID: + msgOrder->set_status((int)bs::network::Order::Failed); + break; + default: + break; + } + msgOrder->set_status_text(order.status_text()); + msgOrder->set_product(order.product()); + msgOrder->set_contra_product(order.product_against()); + msgOrder->set_buy(order.side() == bs::types::Side::SIDE_BUY); + msgOrder->set_quantity(order.quantity()); + msgOrder->set_price(order.price()); + msgOrder->set_timestamp(order.timestamp_ms()); + } + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + void BsServerAdapter::startTimer(std::chrono::milliseconds timeout , const std::function&cb) { @@ -338,6 +370,17 @@ void BsServerAdapter::onCelerRecv(CelerAPI::CelerMessageType messageType, const pushFill(env); } +void BsServerAdapter::onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response& response) +{ + switch (response.data_case()) { + case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrders: + processUpdateOrders(response.update_orders()); + break; + default: + break; + } +} + void BsServerAdapter::Connected() { connected_ = true; diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index d1a022a7c..50c847aa7 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -19,6 +19,13 @@ namespace spdlog { class logger; } +namespace Blocksettle { + namespace Communication { + namespace ProxyTerminalPb { + class Response_UpdateOrders; + } + } +} namespace BlockSettle { namespace Terminal { class SettingsMessage_SettingsResponse; @@ -50,6 +57,7 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg bool processStartLogin(const std::string&); bool processCancelLogin(); bool processSubmitAuthAddr(const bs::message::Envelope&, const std::string &addr); + void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders&); //BCT callbacks void startTimer(std::chrono::milliseconds timeout, const std::function&) override; @@ -57,7 +65,7 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg void onGetLoginResultDone(const BsClientLoginResult& result) override; // void onAuthorizeDone(AuthorizeError authErr, const std::string& email) override; void onCelerRecv(CelerAPI::CelerMessageType messageType, const std::string& data) override; -// void onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response&) override; + void onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response&) override; void Connected() override; void Disconnected() override; void onConnectionFailed() override; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index e3a4be675..4cb1c4ba1 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -44,6 +44,42 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) , [this](const std::string& data) { return onQuoteResponse(data); }); + celerConnection_->RegisterHandler(CelerAPI::QuoteRequestRejectDownstreamEventType + , [this](const std::string& data) { + return onQuoteReject(data); + }); + celerConnection_->RegisterHandler(CelerAPI::CreateOrderRequestRejectDownstreamEventType + , [this](const std::string& data) { + return onOrderReject(data); + }); + celerConnection_->RegisterHandler(CelerAPI::BitcoinOrderSnapshotDownstreamEventType + , [this](const std::string& data) { + return onBitcoinOrderSnapshot(data); + }); + celerConnection_->RegisterHandler(CelerAPI::FxOrderSnapshotDownstreamEventType + , [this](const std::string& data) { + return onFxOrderSnapshot(data); + }); + celerConnection_->RegisterHandler(CelerAPI::QuoteCancelDownstreamEventType + , [this](const std::string& data) { + return onQuoteCancelled(data); + }); + celerConnection_->RegisterHandler(CelerAPI::SignTransactionNotificationType + , [this](const std::string& data) { + return onSignTxNotif(data); + }); + celerConnection_->RegisterHandler(CelerAPI::QuoteAckDownstreamEventType + , [this](const std::string& data) { + return onQuoteAck(data); + }); + celerConnection_->RegisterHandler(CelerAPI::QuoteRequestNotificationType + , [this](const std::string& data) { + return onQuoteReqNotification(data); + }); + celerConnection_->RegisterHandler(CelerAPI::QuoteCancelNotifReplyType + , [this](const std::string& data) { + return onQuoteNotifCancelled(data); + }); } void MatchingAdapter::connectedToServer() @@ -403,6 +439,79 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) return pushFill(env); } +bool MatchingAdapter::onQuoteReject(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onOrderReject(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onBitcoinOrderSnapshot(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onFxOrderSnapshot(const std::string& data) +{ + FxOrderSnapshotDownstreamEvent response; + if (!response.ParseFromString(data)) { + logger_->error("[QuoteProvider::onFxOrderSnapshot] Failed to parse FxOrderSnapshotDownstreamEvent"); + return false; + } + logger_->debug("[MatchingAdapter::onFxOrderSnapshot] {}", response.DebugString()); + + bs::network::Order order; + order.exchOrderId = QString::number(response.orderid()); + order.clOrderId = response.externalclorderid(); + order.quoteId = response.quoteid(); + order.dateTime = QDateTime::fromMSecsSinceEpoch(response.createdtimestamputcinmillis()); + order.security = response.securitycode(); + order.quantity = response.qty(); + order.leavesQty = response.leavesqty(); + order.price = response.price(); + order.avgPx = response.avgpx(); + order.product = response.currency(); + order.side = bs::celer::fromCeler(response.side()); + order.assetType = bs::network::Asset::SpotFX; + + order.status = bs::celer::mapFxOrderStatus(response.orderstatus()); + order.info = response.info(); + + MatchingMessage msg; + toMsg(order, msg.mutable_order()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(env); + return true; +} + +bool MatchingAdapter::onQuoteCancelled(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onSignTxNotif(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onQuoteAck(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onQuoteReqNotification(const std::string&) +{ + return false; +} + +bool MatchingAdapter::onQuoteNotifCancelled(const std::string&) +{ + return false; +} + ClientCelerConnection::ClientCelerConnection(const std::shared_ptr& logger , MatchingAdapter* parent, bool userIdRequired, bool useRecvTimer) diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 88b8fda1c..21d48b5ad 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -75,6 +75,15 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget void cleanQuoteRequestCcy(const std::string& id); bool onQuoteResponse(const std::string&); + bool onQuoteReject(const std::string&); + bool onOrderReject(const std::string&); + bool onBitcoinOrderSnapshot(const std::string&); + bool onFxOrderSnapshot(const std::string&); + bool onQuoteCancelled(const std::string&); + bool onSignTxNotif(const std::string&); + bool onQuoteAck(const std::string&); + bool onQuoteReqNotification(const std::string&); + bool onQuoteNotifCancelled(const std::string&); private: std::shared_ptr logger_; diff --git a/Core/MktDataAdapter.cpp b/Core/MktDataAdapter.cpp index fec9b2c76..c7086e988 100644 --- a/Core/MktDataAdapter.cpp +++ b/Core/MktDataAdapter.cpp @@ -137,7 +137,10 @@ void MktDataAdapter::onMDSecurityReceived(const std::string& name, const bs::net void MktDataAdapter::allSecuritiesReceived() { - logger_->debug("[{}]", __func__); + MktDataMessage msg; + auto msgBC = msg.mutable_all_instruments_received(); + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(envBC); } void MktDataAdapter::onNewFXTrade(const bs::network::NewTrade& trade) diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index a44f518e4..4cfcada45 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -141,3 +141,49 @@ void bs::message::toMsg(const bs::network::Quote& quote, MatchingMessage_Quote* msg->set_time_skew_ms(quote.timeSkewMs); msg->set_timestamp(quote.celerTimestamp); } + +bs::network::Order bs::message::fromMsg(const BlockSettle::Terminal::MatchingMessage_Order& msg) +{ + bs::network::Order order; + order.clOrderId = msg.cl_order_id(); + order.exchOrderId = QString::fromStdString(msg.exchange_id()); + order.quoteId = msg.quote_id(); + order.dateTime = QDateTime::fromMSecsSinceEpoch(msg.timestamp()); + order.security = msg.security(); + order.product = msg.product(); + order.settlementId = msg.settlement_id(); + order.reqTransaction = msg.requester_tx(); + order.dealerTransaction = msg.dealer_tx(); + order.pendingStatus = msg.pending_status(); + order.quantity = msg.quantity(); + order.leavesQty = msg.left_qty(); + order.price = msg.price(); + order.avgPx = msg.avg_price(); + order.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + order.assetType = static_cast(msg.asset_type()); + order.status = static_cast(msg.status()); + order.info = msg.info(); + return order; +} + +void bs::message::toMsg(const bs::network::Order& order, MatchingMessage_Order* msg) +{ + msg->set_cl_order_id(order.clOrderId); + msg->set_exchange_id(order.exchOrderId.toStdString()); + msg->set_quote_id(order.quoteId); + msg->set_timestamp(order.dateTime.toMSecsSinceEpoch()); + msg->set_security(order.security); + msg->set_product(order.product); + msg->set_settlement_id(order.settlementId); + msg->set_requester_tx(order.reqTransaction); + msg->set_dealer_tx(order.dealerTransaction); + msg->set_pending_status(order.pendingStatus); + msg->set_quantity(order.quantity); + msg->set_left_qty(order.leavesQty); + msg->set_price(order.price); + msg->set_avg_price(order.avgPx); + msg->set_buy(order.side == bs::network::Side::Buy); + msg->set_asset_type((int)order.assetType); + msg->set_status((int)order.status); + msg->set_info(order.info); +} diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index 9ed66a1fd..d9ee4b339 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -18,6 +18,7 @@ namespace BlockSettle { namespace Terminal { class MatchingMessage_Quote; + class MatchingMessage_Order; } } @@ -104,6 +105,9 @@ namespace bs { //TODO: move to another source file bs::network::Quote fromMsg(const BlockSettle::Terminal::MatchingMessage_Quote&); void toMsg(const bs::network::Quote&, BlockSettle::Terminal::MatchingMessage_Quote*); + + bs::network::Order fromMsg(const BlockSettle::Terminal::MatchingMessage_Order&); + void toMsg(const bs::network::Order&, BlockSettle::Terminal::MatchingMessage_Order*); } // namespace message } // namespace bs diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 7de4e63cd..3c18ca90e 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -945,6 +945,7 @@ void bs::gui::qt::MainWindow::onMatchingLogin(const std::string& mtchLogin ui_->widgetRFQReply->onUserConnected(userType); statusBarView_->onConnectedToMatching(); + orderListModel_->onMatchingLogin(); } void bs::gui::qt::MainWindow::onLogoutInitiated() @@ -992,6 +993,9 @@ void MainWindow::onMatchingLogout() statusBarView_->onDisconnectedFromMatching(); setLoginButtonText(loginButtonText_); + if (orderListModel_) { + orderListModel_->onMatchingLogout(); + } ui_->widgetRFQ->onMatchingLogout(); } @@ -1033,6 +1037,16 @@ void bs::gui::qt::MainWindow::onQuoteReceived(const bs::network::Quote& quote) ui_->widgetRFQ->onQuoteReceived(quote); } +void bs::gui::qt::MainWindow::onOrderReceived(const bs::network::Order& order) +{ + ui_->widgetRFQ->onOrderReceived(order); +} + +void bs::gui::qt::MainWindow::onOrdersUpdate(const std::vector& orders) +{ + orderListModel_->onOrdersUpdate(orders); +} + void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index e69671f17..1fe133d5d 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -110,6 +110,8 @@ namespace bs { void onVerifiedAuthAddresses(const std::vector&); void onQuoteReceived(const bs::network::Quote&); + void onOrderReceived(const bs::network::Order&); + void onOrdersUpdate(const std::vector&); public slots: void onReactivate(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index fccb1fbe6..b32070474 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -522,7 +522,6 @@ bool QtGuiAdapter::processWallets(const Envelope &env) case WalletsMessage::kWalletAddresses: { std::vector addresses; - addresses.reserve(msg.wallet_addresses().addresses_size()); for (const auto &addr : msg.wallet_addresses().addresses()) { try { addresses.push_back({ std::move(bs::Address::fromAddressString(addr.address())) @@ -1290,7 +1289,7 @@ bool QtGuiAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetai } for (const auto &addrStr : resp.out_addresses()) { try { - txDet.outAddresses.emplace_back(std::move(bs::Address::fromAddressString(addrStr))); + txDet.outAddresses.push_back(std::move(bs::Address::fromAddressString(addrStr))); } catch (const std::exception &e) { logger_->warn("[QtGuiAdapter::processTXDetails] out deser error: {}", e.what()); } @@ -1344,7 +1343,6 @@ bool QtGuiAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetai bool QtGuiAdapter::processLedgerEntries(const ArmoryMessage_LedgerEntries &response) { std::vector entries; - entries.reserve(response.entries_size()); for (const auto &entry : response.entries()) { bs::TXEntry txEntry; txEntry.txHash = BinaryData::fromString(entry.tx_hash()); @@ -1364,7 +1362,7 @@ bool QtGuiAdapter::processLedgerEntries(const ArmoryMessage_LedgerEntries &respo } catch (const std::exception &) {} } - entries.emplace_back(std::move(txEntry)); + entries.push_back(std::move(txEntry)); } return QMetaObject::invokeMethod(mainWindow_, [this, entries, filter=response.filter() , totPages=response.total_pages(), curPage=response.cur_page() @@ -1385,7 +1383,6 @@ bool QtGuiAdapter::processAddressHist(const ArmoryMessage_AddressHistory& respon return true; } std::vector entries; - entries.reserve(response.entries_size()); for (const auto& entry : response.entries()) { bs::TXEntry txEntry; txEntry.txHash = BinaryData::fromString(entry.tx_hash()); @@ -1405,7 +1402,7 @@ bool QtGuiAdapter::processAddressHist(const ArmoryMessage_AddressHistory& respon } catch (const std::exception&) {} } - entries.emplace_back(std::move(txEntry)); + entries.push_back(std::move(txEntry)); } return QMetaObject::invokeMethod(mainWindow_, [this, entries, addr, curBlock = response.cur_block()] { mainWindow_->onAddressHistory(addr, curBlock, entries); @@ -1426,7 +1423,6 @@ bool QtGuiAdapter::processFeeLevels(const ArmoryMessage_FeeLevelsResponse& respo bool QtGuiAdapter::processWalletsList(const WalletsMessage_WalletsListResponse& response) { std::vector wallets; - wallets.reserve(response.wallets_size()); for (const auto& wallet : response.wallets()) { wallets.push_back(bs::sync::HDWalletData::fromCommonMessage(wallet)); } @@ -1439,11 +1435,10 @@ bool QtGuiAdapter::processWalletsList(const WalletsMessage_WalletsListResponse& bool QtGuiAdapter::processUTXOs(const WalletsMessage_UtxoListResponse& response) { std::vector utxos; - utxos.reserve(response.utxos_size()); for (const auto& serUtxo : response.utxos()) { UTXO utxo; utxo.unserialize(BinaryData::fromString(serUtxo)); - utxos.emplace_back(std::move(utxo)); + utxos.push_back(std::move(utxo)); } return QMetaObject::invokeMethod(mainWindow_, [this, utxos, response]{ mainWindow_->onUTXOs(response.id(), response.wallet_id(), utxos); @@ -1481,7 +1476,6 @@ bool QtGuiAdapter::processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived bool QtGuiAdapter::processZCInvalidated(const ArmoryMessage_ZCInvalidated& zcInv) { std::vector txHashes; - txHashes.reserve(zcInv.tx_hashes_size()); for (const auto& hashStr : zcInv.tx_hashes()) { txHashes.push_back(BinaryData::fromString(hashStr)); } @@ -1502,6 +1496,8 @@ bool QtGuiAdapter::processBsServer(const bs::message::Envelope& env) return processStartLogin(msg.start_login_result()); case BsServerMessage::kLoginResult: return processLogin(msg.login_result()); + case BsServerMessage::kOrdersUpdate: + return processOrdersUpdate(msg.orders_update()); default: break; } return true; @@ -1558,6 +1554,8 @@ bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) }); case MatchingMessage::kQuote: return processQuote(msg.quote()); + case MatchingMessage::kOrder: + return processOrder(msg.order()); default: break; } return true; @@ -1571,8 +1569,16 @@ bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { + case MktDataMessage::kDisconnected: + mdInstrumentsReceived_ = false; + break; case MktDataMessage::kNewSecurity: + assetTypes_[msg.new_security().name()] = + static_cast(msg.new_security().asset_type()); break; + case MktDataMessage::kAllInstrumentsReceived: + mdInstrumentsReceived_ = true; + return sendPooledOrdersUpdate(); case MktDataMessage::kPriceUpdate: return processMdUpdate(msg.price_update()); default: break; @@ -1584,11 +1590,11 @@ bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) { return QMetaObject::invokeMethod(mainWindow_, [this, msg] { const bs::network::MDFields fields{ - { bs::network::MDField::PriceBid, msg.bid() }, - { bs::network::MDField::PriceOffer, msg.ask() }, - { bs::network::MDField::PriceLast, msg.last() }, - { bs::network::MDField::DailyVolume, msg.volume() }, - { bs::network::MDField::MDTimestamp, (double)msg.timestamp() } + { bs::network::MDField::Type::PriceBid, msg.bid() }, + { bs::network::MDField::Type::PriceOffer, msg.ask() }, + { bs::network::MDField::Type::PriceLast, msg.last() }, + { bs::network::MDField::Type::DailyVolume, msg.volume() }, + { bs::network::MDField::Type::MDTimestamp, (double)msg.timestamp() } }; mainWindow_->onMDUpdated(static_cast(msg.security().asset_type()) , QString::fromStdString(msg.security().name()), fields); @@ -1598,7 +1604,6 @@ bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) bool QtGuiAdapter::processAuthWallet(const WalletsMessage_WalletData& authWallet) { std::vector authAddresses; - authAddresses.reserve(authWallet.used_addresses_size()); OnChainTrackMessage msg; auto msgReq = msg.mutable_set_auth_addresses(); msgReq->set_wallet_id(authWallet.wallet_id()); @@ -1634,7 +1639,6 @@ bool QtGuiAdapter::processAuthState(const OnChainTrackMessage_AuthState& authSta bool QtGuiAdapter::processSubmittedAuthAddrs(const AssetsMessage_SubmittedAuthAddresses& addrs) { std::vector authAddresses; - authAddresses.reserve(addrs.addresses_size()); for (const auto& addr : addrs.addresses()) { try { authAddresses.push_back(bs::Address::fromAddressString(addr)); @@ -1656,7 +1660,6 @@ bool QtGuiAdapter::processBalance(const AssetsMessage_Balance& bal) bool QtGuiAdapter::processVerifiedAuthAddrs(const OnChainTrackMessage_AuthAddresses& addrs) { std::vector authAddresses; - authAddresses.reserve(addrs.addresses_size()); for (const auto& addr : addrs.addresses()) { try { authAddresses.push_back(bs::Address::fromAddressString(addr)); @@ -1675,4 +1678,60 @@ bool QtGuiAdapter::processQuote(const MatchingMessage_Quote& msg) }); } +bool QtGuiAdapter::processOrder(const MatchingMessage_Order& msg) +{ + const auto& order = fromMsg(msg); + return QMetaObject::invokeMethod(mainWindow_, [this, order] { + mainWindow_->onOrderReceived(order); + }); +} + +bool QtGuiAdapter::processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders& msg) +{ + // Use some fake orderId so old code works correctly + int orderId = 0; + std::vector orders; + for (const auto& o : msg.orders()) { + bs::network::Order order; + order.security = o.product() + "/" + o.contra_product(); + auto itAsset = assetTypes_.find(order.security); + if (itAsset != assetTypes_.end()) { + order.assetType = itAsset->second; + } + else { + order.assetType = bs::network::Asset::Undefined; + } + order.exchOrderId = QString::number(++orderId); + order.status = static_cast(o.status()); + order.side = o.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + order.pendingStatus = o.status_text(); + order.dateTime = QDateTime::fromMSecsSinceEpoch(o.timestamp()); + order.product = o.product(); + order.quantity = o.quantity(); + order.price = o.price(); + orders.push_back(order); + } + pooledOrders_ = std::move(orders); + return sendPooledOrdersUpdate(); +} + +bool QtGuiAdapter::sendPooledOrdersUpdate() +{ + if (!mdInstrumentsReceived_ || pooledOrders_.empty()) { + return true; + } + for (auto& order : pooledOrders_) { + if (order.assetType == bs::network::Asset::Undefined) { + const auto& itAsset = assetTypes_.find(order.security); + if (itAsset != assetTypes_.end()) { + order.assetType = itAsset->second; + } + } + } + return QMetaObject::invokeMethod(mainWindow_, [this] { + mainWindow_->onOrdersUpdate(pooledOrders_); + pooledOrders_.clear(); + }); +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 71aa973a8..c9def073f 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -46,9 +46,11 @@ namespace BlockSettle { class AssetsMessage_Balance; class AssetsMessage_SubmittedAuthAddresses; class BsServerMessage_LoginResult; + class BsServerMessage_Orders; class BsServerMessage_StartLoginResult; class MatchingMessage_LoggedIn; class MatchingMessage_Quote; + class MatchingMessage_Order; class MktDataMessage_Prices; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; @@ -126,6 +128,9 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processBalance(const BlockSettle::Terminal::AssetsMessage_Balance&); bool processVerifiedAuthAddrs(const BlockSettle::Common::OnChainTrackMessage_AuthAddresses&); bool processQuote(const BlockSettle::Terminal::MatchingMessage_Quote&); + bool processOrder(const BlockSettle::Terminal::MatchingMessage_Order&); + bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); + bool sendPooledOrdersUpdate(); private slots: void onGetSettings(const std::vector&); @@ -197,6 +202,10 @@ private slots: std::unordered_map hdWallets_; std::set newZCs_; + + std::unordered_map assetTypes_; + bool mdInstrumentsReceived_{ false }; + std::vector pooledOrders_; }; diff --git a/common b/common index af4045f6f..58b4e99c2 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit af4045f6f77e180bcc061691af4decfb4eb13d7a +Subproject commit 58b4e99c2a05bab83e1d0204e6b464b70ab8e680 From 01cc4eaa8976f6cd7be5e8d2e732501a3a9dd9a7 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 26 Oct 2020 13:26:02 +0300 Subject: [PATCH 023/146] Send XBT RFQ --- BlockSettleUILib/Trading/RFQDialog.cpp | 4 - BlockSettleUILib/Trading/RFQDialog.h | 2 - BlockSettleUILib/Trading/RFQRequestWidget.cpp | 81 ++- BlockSettleUILib/Trading/RFQRequestWidget.h | 24 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 478 +++++++++++++----- BlockSettleUILib/Trading/RFQTicketXBT.h | 40 +- BlockSettleUILib/Trading/WalletShieldBase.cpp | 2 +- BlockSettleUILib/UiUtils.cpp | 98 ++++ BlockSettleUILib/UiUtils.h | 13 +- Core/MatchingAdapter.cpp | 21 +- Core/MatchingAdapter.h | 9 +- Core/MessageUtils.cpp | 133 +++++ Core/MessageUtils.h | 41 ++ Core/SettlementAdapter.cpp | 145 +++++- Core/SettlementAdapter.h | 32 +- Core/SignerAdapter.cpp | 24 + Core/SignerAdapter.h | 1 + Core/TerminalMessage.cpp | 91 ---- Core/TerminalMessage.h | 8 - GUI/QtWidgets/MainWindow.cpp | 45 +- GUI/QtWidgets/MainWindow.h | 17 +- GUI/QtWidgets/QtGuiAdapter.cpp | 176 ++++++- GUI/QtWidgets/QtGuiAdapter.h | 28 +- common | 2 +- 24 files changed, 1196 insertions(+), 319 deletions(-) create mode 100644 Core/MessageUtils.cpp create mode 100644 Core/MessageUtils.h diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index f54568533..9f0f5ee05 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -102,8 +102,6 @@ RFQDialog::RFQDialog(const std::shared_ptr& logger , const std::string& id, const bs::network::RFQ& rfq , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet , const bs::Address& authAddr - , bs::UtxoReservationToken fixedXbtUtxoRes - , bs::UtxoReservationToken ccUtxoRes , std::unique_ptr purpose , RFQRequestWidget* parent) : QDialog(parent) @@ -112,9 +110,7 @@ RFQDialog::RFQDialog(const std::shared_ptr& logger , id_(id), rfq_(rfq) , recvXbtAddrIfSet_(recvXbtAddrIfSet) , authAddr_(authAddr) - , fixedXbtUtxoRes_(std::move(fixedXbtUtxoRes)) , requestWidget_(parent) - , ccUtxoRes_(std::move(ccUtxoRes)) , walletPurpose_(std::move(purpose)) { ui_->setupUi(this); diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index ce40e94a8..83ac38b81 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -80,8 +80,6 @@ Q_OBJECT , const std::string& id, const bs::network::RFQ& rfq , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet , const bs::Address& authAddr - , bs::UtxoReservationToken fixedXbtUtxoRes - , bs::UtxoReservationToken ccUtxoRes , std::unique_ptr purpose , RFQRequestWidget* parent = nullptr); ~RFQDialog() override; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 7d78b9447..383b087a3 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -56,6 +56,10 @@ RFQRequestWidget::RFQRequestWidget(QWidget* parent) connect(ui_->shieldPage, &RFQShieldPage::requestPrimaryWalletCreation, this, &RFQRequestWidget::requestPrimaryWalletCreation); connect(ui_->shieldPage, &RFQShieldPage::loginRequested, this, &RFQRequestWidget::loginRequested); + connect(ui_->pageRFQTicket, &RFQTicketXBT::needWalletData, this, &RFQRequestWidget::needWalletData); + connect(ui_->pageRFQTicket, &RFQTicketXBT::needAuthKey, this, &RFQRequestWidget::needAuthKey); + connect(ui_->pageRFQTicket, &RFQTicketXBT::needReserveUTXOs, this, &RFQRequestWidget::needReserveUTXOs); + ui_->pageRFQTicket->setSubmitRFQ([this] (const std::string &id, const bs::network::RFQ& rfq, bs::UtxoReservationToken utxoRes) { @@ -161,6 +165,11 @@ void RFQRequestWidget::setAuthorized(bool authorized) ui_->widgetMarketData->setAuthorized(authorized); } +void RFQRequestWidget::onNewSecurity(const std::string& name, bs::network::Asset::Type at) +{ + ui_->pageRFQTicket->onNewSecurity(name, at); +} + void RFQRequestWidget::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &fields) { @@ -173,6 +182,22 @@ void RFQRequestWidget::onBalance(const std::string& currency, double balance) balances_[currency] = balance; } +void RFQRequestWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + ui_->pageRFQTicket->onWalletBalance(wbd); +} + +void RFQRequestWidget::onHDWallet(const bs::sync::HDWalletData& wallet) +{ + ui_->pageRFQTicket->onHDWallet(wallet); +} + +void RFQRequestWidget::onWalletData(const std::string& walletId + , const bs::sync::WalletData& wd) +{ + ui_->pageRFQTicket->onWalletData(walletId, wd); +} + void RFQRequestWidget::onMatchingLogin(const std::string& mtchLogin , BaseCelerClient::CelerUserType userType, const std::string& userId) { @@ -207,8 +232,20 @@ void RFQRequestWidget::onMatchingLogout() popShield(); } -void RFQRequestWidget::onVerifiedAuthAddresses(const std::vector&) +void RFQRequestWidget::onVerifiedAuthAddresses(const std::vector& addrs) { + ui_->pageRFQTicket->onVerifiedAuthAddresses(addrs); + forceCheckCondition(); +} + +void RFQRequestWidget::onAuthKey(const bs::Address& addr, const BinaryData& authKey) +{ + ui_->pageRFQTicket->onAuthKey(addr, authKey); +} + +void RFQRequestWidget::onTradeSettings(const std::shared_ptr& ts) +{ + ui_->pageRFQTicket->onTradeSettings(ts); } void RFQRequestWidget::onQuoteReceived(const bs::network::Quote& quote) @@ -219,21 +256,29 @@ void RFQRequestWidget::onQuoteReceived(const bs::network::Quote& quote) } } -void RFQRequestWidget::onOrderReceived(const bs::network::Order& order) +void RFQRequestWidget::onQuoteMatched(const std::string& rfqId, const std::string& quoteId) { - for (const auto& dialog : dialogs_) { - switch (order.status) { - case bs::network::Order::Filled: - dialog.second->onOrderFilled(order.quoteId); - break; - case bs::network::Order::Failed: - dialog.second->onOrderFailed(order.quoteId, order.info); - break; - default: break; - } + const auto& itDlg = dialogs_.find(rfqId); + if (itDlg != dialogs_.end()) { + itDlg->second->onOrderFilled(quoteId); } } +void RFQRequestWidget::onQuoteFailed(const std::string& rfqId + , const std::string& quoteId, const std::string &info) +{ + const auto& itDlg = dialogs_.find(rfqId); + if (itDlg != dialogs_.end()) { + itDlg->second->onOrderFailed(quoteId, info); + } +} + +void RFQRequestWidget::onReservedUTXOs(const std::string& resId + , const std::string& subId, const std::vector& utxos) +{ + ui_->pageRFQTicket->onReservedUTXOs(resId, subId, utxos); +} + void RFQRequestWidget::hideEvent(QHideEvent* event) { ui_->pageRFQTicket->onParentAboutToHide(); @@ -399,10 +444,9 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ , std::move(ccUtxoRes), std::move(purpose), this); } else { - std::string xbtWalletId; //TODO - set + std::string xbtWalletId; dialog = new RFQDialog(logger_, id, rfq, xbtWalletId - , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, std::move(fixedXbtInputs.utxoRes) - , std::move(ccUtxoRes), std::move(purpose), this); + , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, std::move(purpose), this); emit needSubmitRFQ(rfq); } @@ -439,7 +483,6 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ if (dlg.second->isHidden()) { dlg.second->deleteLater(); closedDialogs.push_back(dlg.first); - logger_->debug("[{}] erasing dialog {}", __func__, dlg.first); } } for (const auto &dlg : closedDialogs) { @@ -514,8 +557,10 @@ bool RFQRequestWidget::checkWalletSettings(bs::network::Asset::Type productType, void RFQRequestWidget::forceCheckCondition() { - if (!ui_->widgetMarketData || !celerClient_->IsConnected()) { - return; + if (celerClient_) { + if (!ui_->widgetMarketData || !celerClient_->IsConnected()) { + return; + } } const auto& currentInfo = ui_->widgetMarketData->getCurrentlySelectedInfo(); diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 23dc47847..49c648d3e 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -17,6 +17,7 @@ #include "Celer/BaseCelerClient.h" #include "CommonTypes.h" #include "MarketDataWidget.h" +#include "SignerDefs.h" #include "TabWithShortcut.h" #include "UtxoReservationToken.h" @@ -30,6 +31,7 @@ namespace bs { namespace sync { class WalletsManager; } + struct TradeSettings; class UTXOReservationManager; } @@ -90,17 +92,28 @@ Q_OBJECT void setAuthorized(bool authorized); + void onNewSecurity(const std::string& name, bs::network::Asset::Type); void onMDUpdated(bs::network::Asset::Type, const QString& security , const bs::network::MDFields &); void onBalance(const std::string& currency, double balance); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onHDWallet(const bs::sync::HDWalletData&); + void onWalletData(const std::string& walletId, const bs::sync::WalletData&); void onMatchingLogin(const std::string& mtchLogin, BaseCelerClient::CelerUserType , const std::string& userId); void onMatchingLogout(); void onVerifiedAuthAddresses(const std::vector&); + void onAuthKey(const bs::Address&, const BinaryData& authKey); + void onTradeSettings(const std::shared_ptr&); void onQuoteReceived(const bs::network::Quote&); - void onOrderReceived(const bs::network::Order&); + void onQuoteMatched(const std::string &rfqId, const std::string& quoteId); + void onQuoteFailed(const std::string& rfqId, const std::string& quoteId + , const std::string& info); + + void onReservedUTXOs(const std::string& resId, const std::string &subId + , const std::vector&); protected: void hideEvent(QHideEvent* event) override; @@ -122,10 +135,15 @@ Q_OBJECT void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); - void needSubmitRFQ(const bs::network::RFQ&); + void needWalletData(const std::string& walletId); + void needSubmitRFQ(const bs::network::RFQ&, const std::string& reserveId = {}); void needAcceptRFQ(const std::string& id, const bs::network::Quote&); void needExpireRFQ(const std::string& id); void needCancelRFQ(const std::string& id); + void needAuthKey(const bs::Address&); + + void needReserveUTXOs(const std::string& reserveId, const std::string& subId + , uint64_t amount, bool partial = false, const std::vector& utxos = {}); private: void showEditableRFQPage(); @@ -146,7 +164,7 @@ public slots: void onDisableSelectedInfo(); void onRefreshFocus(); - void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); + void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); //deprecated void onUserConnected(const bs::network::UserType &); void onUserDisconnected(); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index b40cfd7f4..e64eb84f2 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -83,7 +83,8 @@ RFQTicketXBT::RFQTicketXBT(QWidget* parent) connect(ui_->lineEditAmount, &QLineEdit::textEdited, this, &RFQTicketXBT::onAmountEdited); - connect(ui_->authenticationAddressComboBox, qOverload(&QComboBox::currentIndexChanged), this, &RFQTicketXBT::onAuthAddrChanged); + connect(ui_->authenticationAddressComboBox, qOverload(&QComboBox::currentIndexChanged) + , this, &RFQTicketXBT::onAuthAddrChanged); ui_->comboBoxXBTWalletsRecv->setEnabled(false); ui_->comboBoxXBTWalletsSend->setEnabled(false); @@ -133,6 +134,7 @@ void RFQTicketXBT::init(const std::shared_ptr &logger signingContainer_ = container; armory_ = armory; utxoReservationManager_ = utxoReservationManager; + tradeSettings_ = authAddressManager_->tradeSettings(); if (signingContainer_) { connect(signingContainer_.get(), &SignContainer::ready, this, &RFQTicketXBT::onSignerReady); @@ -334,37 +336,42 @@ void RFQTicketXBT::onSignerReady() void RFQTicketXBT::fillRecvAddresses() { - auto recvWallet = getRecvXbtWallet(); - if (recvWallet) { - if (!recvWallet->canMixLeaves()) { - auto xbtGroup = recvWallet->getGroup(recvWallet->getXBTGroupType()); - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsRecv); - UiUtils::fillRecvAddressesComboBox(ui_->receivingAddressComboBox, { xbtGroup->getLeaf(purpose) }); - } - else { - UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, recvWallet, true); + if (walletsManager_) { + auto recvWallet = getRecvXbtWallet(); + if (recvWallet) { + if (!recvWallet->canMixLeaves()) { + auto xbtGroup = recvWallet->getGroup(recvWallet->getXBTGroupType()); + auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsRecv); + UiUtils::fillRecvAddressesComboBox(ui_->receivingAddressComboBox, { xbtGroup->getLeaf(purpose) }); + } else { + UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, recvWallet, true); + } } } + else { + const auto& walletId = ui_->comboBoxXBTWalletsRecv->currentData(UiUtils::WalletIdRole).toString().toStdString(); + emit needWalletData(walletId); + } } bool RFQTicketXBT::preSubmitCheck() { if (currentGroupType_ == ProductGroupType::XBTGroupType) { const auto qty = getQuantity(); - const auto& tradeSettings = authAddressManager_->tradeSettings(); - assert(tradeSettings); + assert(tradeSettings_); bool validAmount = false; if (currentProduct_ == UiUtils::XbtCurrency) { - validAmount = tradeSettings->xbtTier1Limit > bs::XBTAmount(qty).GetValue(); + validAmount = tradeSettings_->xbtTier1Limit > bs::XBTAmount(qty).GetValue(); } else { const double indPrice = getIndicativePrice(); - bs::XBTAmount price(indPrice * (1 + (tradeSettings->xbtPriceBand / 100))); + bs::XBTAmount price(indPrice * (1.0 + (tradeSettings_->xbtPriceBand / 100.0))); validAmount = price > bs::XBTAmount(qty); } if (!validAmount) { - auto amountStr = UiUtils::displayQuantity(bs::XBTAmount(tradeSettings->xbtTier1Limit).GetValueBitcoin(), bs::network::XbtCurrency); + auto amountStr = UiUtils::displayQuantity(bs::XBTAmount(tradeSettings_->xbtTier1Limit).GetValueBitcoin() + , bs::network::XbtCurrency); BSMessageBox(BSMessageBox::info , tr("Notice"), tr("Authentication Address not verified") , tr("Trades above %1 are not permitted for non-verified Authentication Addresses. " @@ -548,22 +555,30 @@ bs::network::Side::Type RFQTicketXBT::getSelectedSide() const return bs::network::Side::Buy; } -void RFQTicketXBT::onSettlLeavesLoaded(unsigned int) +void RFQTicketXBT::sendDeferredRFQs() { + if (authKey_.empty() || deferredRFQs_.empty()) { + return; + } decltype(deferredRFQs_) tmpRFQs; tmpRFQs.swap(deferredRFQs_); - logger_->debug("[RFQTicketXBT::onSettlLeavesLoaded] sending {} deferred RFQ[s]", tmpRFQs.size()); + logger_->debug("[RFQTicketXBT::sendDeferredRFQs] {} RFQ[s]", tmpRFQs.size()); + for (const auto& id : tmpRFQs) { + sendRFQ(id); + } +} + +void RFQTicketXBT::onSettlLeavesLoaded(unsigned int) +{ if (authKey_.empty()) { if (authAddr_.empty()) { logger_->warn("[RFQTicketXBT::onSettlLeavesLoaded] no default auth address"); return; } - const auto &cbPubKey = [this, tmpRFQs](const SecureBinaryData &pubKey) { + const auto &cbPubKey = [this](const SecureBinaryData &pubKey) { authKey_ = pubKey.toHexStr(); - for (const auto &id : tmpRFQs) { - sendRFQ(id); - } + sendDeferredRFQs(); }; const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); if (!settlLeaf) { @@ -574,9 +589,7 @@ void RFQTicketXBT::onSettlLeavesLoaded(unsigned int) settlLeaf->getRootPubkey(cbPubKey); } else { - for (const auto &id : tmpRFQs) { - sendRFQ(id); - } + sendDeferredRFQs(); } } @@ -594,19 +607,23 @@ void RFQTicketXBT::onAuthAddrChanged(int index) authAddr_ = bs::Address::fromAddressString(addressString); - authKey_.clear(); - const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); + if (walletsManager_) { + authKey_.clear(); + const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); - const auto &cbPubKey = [this](const SecureBinaryData &pubKey) { - authKey_ = pubKey.toHexStr(); - QMetaObject::invokeMethod(this, &RFQTicketXBT::updateSubmitButton); - }; + const auto& cbPubKey = [this](const SecureBinaryData& pubKey) { + authKey_ = pubKey.toHexStr(); + QMetaObject::invokeMethod(this, &RFQTicketXBT::updateSubmitButton); + }; - if (settlLeaf) { - settlLeaf->getRootPubkey(cbPubKey); + if (settlLeaf) { + settlLeaf->getRootPubkey(cbPubKey); + } else { + walletsManager_->createSettlementLeaf(authAddr_, cbPubKey); + } } else { - walletsManager_->createSettlementLeaf(authAddr_, cbPubKey); + emit needAuthKey(authAddr_); } } @@ -695,7 +712,7 @@ bool RFQTicketXBT::checkBalance(double qty) const } } -bool RFQTicketXBT::checkAuthAddr(double qty) const +bool RFQTicketXBT::checkAuthAddr() const { if (!ui_->authenticationAddressComboBox->isVisible()) { return true; @@ -704,13 +721,19 @@ bool RFQTicketXBT::checkAuthAddr(double qty) const return false; } - if (authAddressManager_->GetState(authAddr_) == AuthAddressManager::AuthAddressState::Verified) { - return true; - } + if (authAddressManager_) { + if (authAddressManager_->GetState(authAddr_) == AuthAddressManager::AuthAddressState::Verified) { + return true; + } - const auto& tradeSettings = authAddressManager_->tradeSettings(); - if (!tradeSettings) { - return false; + const auto& tradeSettings = authAddressManager_->tradeSettings(); + if (!tradeSettings) { + return false; + } + } + else { + //TODO: implement more thorough checking if needed + return (!authAddr_.empty()); } return true; @@ -721,7 +744,6 @@ void RFQTicketXBT::updateSubmitButton() ui_->pushButtonSubmit->setEnabled(false); if (!assetManager_ && signingContainer_) { - logger_->debug("[{}] 1", __func__); return; } @@ -729,7 +751,6 @@ void RFQTicketXBT::updateSubmitButton() if (signingContainer_) { if (signingContainer_->isOffline()) { showHelp(tr("Signer is offline - settlement will not be possible")); - logger_->debug("[{}] 2", __func__); return; } else { @@ -737,43 +758,54 @@ void RFQTicketXBT::updateSubmitButton() } } - if (getProductToSpend() == UiUtils::XbtCurrency && !getSendXbtWallet()) { - logger_->debug("[{}] 3", __func__); - return; + if (getProductToSpend() == UiUtils::XbtCurrency) { + if (walletsManager_ && !getSendXbtWallet()) { + return; + } + else if (!hasSendXbtWallet()) { + return; + } } - if (getProductToRecv() == UiUtils::XbtCurrency && !getRecvXbtWallet()) { - logger_->debug("[{}] 4", __func__); - return; + if (getProductToRecv() == UiUtils::XbtCurrency) { + if (walletsManager_ && !getRecvXbtWallet()) { + return; + } + else if (!hasRecvXbtWallet()) { + return; + } } if (currentGroupType_ == ProductGroupType::CCGroupType) { - auto ccWallet = getCCWallet(getProduct().toStdString()); - if (!ccWallet) { - logger_->debug("[{}] 5", __func__); - return; + if (walletsManager_) { + auto ccWallet = getCCWallet(getProduct().toStdString()); + if (!ccWallet) { + return; + } + } + else { + if (!hasCCWallet()) { + return; + } } } } const double qty = getQuantity(); + if (qFuzzyIsNull(qty)) { + return; + } + const bool isBalanceOk = checkBalance(qty); - const bool isAuthOk = checkAuthAddr(qty); + const bool isAuthOk = checkAuthAddr(); if (!isBalanceOk || !isAuthOk) { ui_->labelBalanceValue->setFont(invalidBalanceFont_); - logger_->debug("[{}] 6", __func__); return; } ui_->labelBalanceValue->setFont(QFont()); - if (qFuzzyIsNull(qty)) { - logger_->debug("[{}] 7", __func__); - return; - } - if ((currentGroupType_ == ProductGroupType::XBTGroupType) && authKey().empty()) { - logger_->debug("[{}] 8", __func__); return; } @@ -853,12 +885,14 @@ void RFQTicketXBT::submitButtonClicked() } if (currentGroupType_ == ProductGroupType::XBTGroupType) { - auto minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); - if (expectedXbtAmountMin().GetValue() < minXbtAmount.GetValue()) { - auto minAmountStr = UiUtils::displayQuantity(minXbtAmount.GetValueBitcoin(), bs::network::XbtCurrency); - BSMessageBox(BSMessageBox::critical, tr("Spot XBT"), tr("Invalid amount") - , tr("Expected bitcoin amount will not cover network fee.\nMinimum amount: %1").arg(minAmountStr), this).exec(); - return; + if (utxoReservationManager_) { + auto minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); + if (expectedXbtAmountMin().GetValue() < minXbtAmount.GetValue()) { + auto minAmountStr = UiUtils::displayQuantity(minXbtAmount.GetValueBitcoin(), bs::network::XbtCurrency); + BSMessageBox(BSMessageBox::critical, tr("Spot XBT"), tr("Invalid amount") + , tr("Expected bitcoin amount will not cover network fee.\nMinimum amount: %1").arg(minAmountStr), this).exec(); + return; + } } } @@ -895,7 +929,18 @@ void RFQTicketXBT::onSendRFQ(const std::string &id, const QString &symbol, doubl rfq->security = symbol.toStdString(); rfq->product = cp.NumCurrency(); rfq->quantity = amount; - rfq->assetType = assetManager_->GetAssetTypeForSecurity(rfq->security); + if (assetManager_) { + rfq->assetType = assetManager_->GetAssetTypeForSecurity(rfq->security); + } + else { + try { + rfq->assetType = assetTypes_.at(rfq->security); + } + catch (const std::exception&) { + logger_->error("[{}] no asset type found for {}", __func__, rfq->security); + return; + } + } if (rfq->security.empty() || rfq->product.empty() || qFuzzyIsNull(rfq->quantity)) { return; @@ -904,14 +949,18 @@ void RFQTicketXBT::onSendRFQ(const std::string &id, const QString &symbol, doubl pendingRFQs_[id] = rfq; if (rfq->assetType == bs::network::Asset::SpotXBT) { - authAddr_ = authAddressManager_->getDefault(); + if (authAddressManager_) { + authAddr_ = authAddressManager_->getDefault(); + } if (authAddr_.empty()) { deferredRFQs_.push_back(id); return; } - if (!walletsManager_->getSettlementLeaf(authAddr_)) { - deferredRFQs_.push_back(id); - return; + if (walletsManager_) { + if (!walletsManager_->getSettlementLeaf(authAddr_)) { + deferredRFQs_.push_back(id); + return; + } } } @@ -925,7 +974,6 @@ void RFQTicketXBT::sendRFQ(const std::string &id) logger_->error("[RFQTicketXBT::onSendRFQ] RFQ with id {} not found", id); return; } - logger_->debug("[RFQTicketXBT::sendRFQ] sending RFQ {}", id); auto rfq = itRFQ->second; @@ -1070,6 +1118,7 @@ void RFQTicketXBT::sendRFQ(const std::string &id) } submitRFQCb_(id, *rfq, bs::UtxoReservationToken{}); + pendingRFQs_.erase(itRFQ); } void RFQTicketXBT::onCancelRFQ(const std::string &id) @@ -1183,12 +1232,82 @@ void RFQTicketXBT::onParentAboutToHide() fixedXbtInputs_ = {}; } +void RFQTicketXBT::onVerifiedAuthAddresses(const std::vector& addrs) +{ + logger_->debug("[{}] {} addresses", __func__, addrs.size()); + UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, addrs); + onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); +} + void RFQTicketXBT::onBalance(const std::string& currency, double balance) { balances_[currency] = balance; updateBalances(); } +void RFQTicketXBT::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + balances_[wbd.id] = wbd.balSpendable; + updateBalances(); +} + +void RFQTicketXBT::onHDWallet(const bs::sync::HDWalletData& wallet) +{ + const auto &it = std::find_if(wallets_.cbegin(), wallets_.cend() + , [wallet](const bs::sync::HDWalletData &w) { return (wallet.id == w.id); }); + if (it == wallets_.end()) { + wallets_.push_back(wallet); + } + else { + wallets_.emplace(it, wallet); + } + UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWalletsRecv, wallets_, UiUtils::WalletsTypes::All); + // CC does not support to send from hardware wallets + int sendWalletTypes = (currentGroupType_ == ProductGroupType::CCGroupType) ? + UiUtils::WalletsTypes::Full : (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW); + UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWalletsSend, wallets_, sendWalletTypes); +} + +void RFQTicketXBT::onWalletData(const std::string& walletId, const bs::sync::WalletData& wd) +{ + if (ui_->comboBoxXBTWalletsRecv->currentData(UiUtils::WalletIdRole).toString().toStdString() == walletId) { + UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, { wd }); + } +} + +void RFQTicketXBT::onAuthKey(const bs::Address& addr, const BinaryData& authKey) +{ + logger_->debug("[{}] {} ({} {})", __func__, authKey.toHexStr(), addr.display(), authAddr_.display()); + if (addr == authAddr_) { + authKey_ = authKey.toHexStr(); + updateSubmitButton(); + sendDeferredRFQs(); + } +} + +void RFQTicketXBT::onTradeSettings(const std::shared_ptr& ts) +{ + tradeSettings_ = ts; +} + +void RFQTicketXBT::onReservedUTXOs(const std::string& resId + , const std::string& subId, const std::vector& utxos) +{ + const auto& it = pendingRFQs_.find(resId); + if (it == pendingRFQs_.end()) { + return; + } + logger_->debug("[{}] sending RFQ {} after {} UTXOs reservation", __func__ + , resId, utxos.size()); + if (utxos.empty()) { + logger_->error("[{}] failed to reserve {}/{}", resId, subId); + } + else { + submitRFQCb_(resId, *it->second, {}); + } + pendingRFQs_.erase(it); +} + void RFQTicketXBT::enablePanel() { resetTicket(); @@ -1417,9 +1536,14 @@ void RFQTicketXBT::productSelectionChanged() ui_->lineEditAmount->setValidator(fxAmountValidator_); ui_->lineEditAmount->setEnabled(true); } else { - bool canTradeXBT = (armory_->state() == ArmoryState::Ready) - && signingContainer_ - && !signingContainer_->isOffline(); + bool canTradeXBT = false; + if (armory_ && signingContainer_) { + canTradeXBT = (armory_->state() == ArmoryState::Ready) + && !signingContainer_->isOffline(); + } + else { + canTradeXBT = true; + } ui_->lineEditAmount->setEnabled(canTradeXBT); ui_->toolButtonMax->setEnabled(canTradeXBT); @@ -1494,6 +1618,49 @@ std::shared_ptr RFQTicketXBT::getRecvXbtWallet() const return wallet; } +std::string RFQTicketXBT::getXbtLeafId(const std::string& hdWalletId) const +{ + const auto& it = std::find_if(wallets_.cbegin(), wallets_.cend() + , [hdWalletId](const bs::sync::HDWalletData& wd) { return (wd.id == hdWalletId); }); + if (it == wallets_.end()) { + for (const auto& wallet : wallets_) { + for (const auto& group : wallet.groups) { + if ((group.type != bs::hd::CoinType::Bitcoin_main) && (group.type != bs::hd::Bitcoin_test)) { + continue; + } + for (const auto& leaf : group.leaves) { + if (std::find_if(leaf.ids.cbegin(), leaf.ids.cend() + , [hdWalletId](const std::string& id) { return (id == hdWalletId); }) + != leaf.ids.end()) { + return hdWalletId; + } + } + } + } + return {}; + } + for (const auto& group : it->groups) { + if ((group.type != bs::hd::CoinType::Bitcoin_main) && (group.type != bs::hd::Bitcoin_test)) { + continue; + } + if (!group.leaves.empty()) { + const auto& leaf = *group.leaves.cbegin(); + return *leaf.ids.cbegin(); + } + } + return {}; +} + +bool RFQTicketXBT::hasSendXbtWallet() const +{ + return !wallets_.empty(); //TODO: implement more thorough checking +} + +bool RFQTicketXBT::hasRecvXbtWallet() const +{ + return !wallets_.empty(); //TODO: implement more thorough checking +} + bs::XBTAmount RFQTicketXBT::getXbtBalance() const { const auto &fixedInputs = fixedXbtInputs_.inputs; @@ -1505,19 +1672,34 @@ bs::XBTAmount RFQTicketXBT::getXbtBalance() const return bs::XBTAmount(sum); } - auto xbtWallet = getSendXbtWallet(); - if (!xbtWallet) { - return bs::XBTAmount(0.0); - } + if (walletsManager_) { + auto xbtWallet = getSendXbtWallet(); + if (!xbtWallet) { + return bs::XBTAmount(0.0); + } - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsSend); - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId(), purpose)); + if (!xbtWallet->canMixLeaves()) { + auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsSend); + return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( + xbtWallet->walletId(), purpose)); + } else { + return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( + xbtWallet->walletId())); + } } else { - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId())); + const auto &walletId = ui_->comboBoxXBTWalletsSend->currentData(UiUtils::WalletIdRole).toString().toStdString(); + const auto& xbtLeafId = getXbtLeafId(walletId); + if (xbtLeafId.empty()) { + return bs::XBTAmount(0.0); //TODO: use default XBT leaf + } + try { + return bs::XBTAmount(balances_.at(xbtLeafId)); + } + catch (const std::exception&) { + logger_->error("[{}] balance for leaf {} not found", __func__, xbtLeafId); + return bs::XBTAmount(0.0); + } } } @@ -1557,68 +1739,98 @@ bs::XBTAmount RFQTicketXBT::getXbtReservationAmountForCc(double quantity, double void RFQTicketXBT::reserveBestUtxoSetAndSubmit(const std::string &id , const std::shared_ptr& rfq) { - const auto &submitRFQWrapper = [rfqTicket = QPointer(this), id, rfq] - { - if (!rfqTicket) { - return; - } - rfqTicket->submitRFQCb_(id, *rfq, std::move(rfqTicket->fixedXbtInputs_.utxoRes)); - }; - auto getWalletAndReserve = [rfqTicket = QPointer(this), submitRFQWrapper] - (BTCNumericTypes::satoshi_type amount, bool partial) - { - auto cbBestUtxoSet = [rfqTicket, submitRFQWrapper](bs::FixedXbtInputs&& fixedXbt) { + if (walletsManager_) { + const auto& submitRFQWrapper = [rfqTicket = QPointer(this), id, rfq] + { if (!rfqTicket) { return; } - rfqTicket->fixedXbtInputs_ = std::move(fixedXbt); - submitRFQWrapper(); + rfqTicket->submitRFQCb_(id, *rfq, std::move(rfqTicket->fixedXbtInputs_.utxoRes)); + }; + auto getWalletAndReserve = [rfqTicket = QPointer(this), submitRFQWrapper] + (BTCNumericTypes::satoshi_type amount, bool partial) + { + auto cbBestUtxoSet = [rfqTicket, submitRFQWrapper](bs::FixedXbtInputs&& fixedXbt) { + if (!rfqTicket) { + return; + } + rfqTicket->fixedXbtInputs_ = std::move(fixedXbt); + submitRFQWrapper(); + }; + + auto hdWallet = rfqTicket->getSendXbtWallet(); + if (!hdWallet->canMixLeaves()) { + auto purpose = UiUtils::getSelectedHwPurpose(rfqTicket->ui_->comboBoxXBTWalletsSend); + rfqTicket->utxoReservationManager_->reserveBestXbtUtxoSet( + hdWallet->walletId(), purpose, amount, + partial, std::move(cbBestUtxoSet), true); + } else { + rfqTicket->utxoReservationManager_->reserveBestXbtUtxoSet( + hdWallet->walletId(), amount, + partial, std::move(cbBestUtxoSet), true); + } }; - auto hdWallet = rfqTicket->getSendXbtWallet(); - if (!hdWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(rfqTicket->ui_->comboBoxXBTWalletsSend); - rfqTicket->utxoReservationManager_->reserveBestXbtUtxoSet( - hdWallet->walletId(), purpose, amount, - partial, std::move(cbBestUtxoSet), true); + if (rfq->assetType == bs::network::Asset::PrivateMarket + && rfq->side == bs::network::Side::Buy) { + auto maxXbtQuantity = getXbtReservationAmountForCc(rfq->quantity, getOfferPrice()).GetValue(); + getWalletAndReserve(maxXbtQuantity, true); + return; } - else { - rfqTicket->utxoReservationManager_->reserveBestXbtUtxoSet( - hdWallet->walletId(), amount, - partial, std::move(cbBestUtxoSet), true); + + if ((rfq->side == bs::network::Side::Sell && rfq->product != bs::network::XbtCurrency) || + (rfq->side == bs::network::Side::Buy && rfq->product == bs::network::XbtCurrency)) { + submitRFQWrapper(); + return; // Nothing to reserve } - }; - if (rfq->assetType == bs::network::Asset::PrivateMarket - && rfq->side == bs::network::Side::Buy) { - auto maxXbtQuantity = getXbtReservationAmountForCc(rfq->quantity, getOfferPrice()).GetValue(); - getWalletAndReserve(maxXbtQuantity, true); - return; - } + if (!fixedXbtInputs_.inputs.empty()) { + submitRFQWrapper(); + return; // already reserved by user + } - if ((rfq->side == bs::network::Side::Sell && rfq->product != bs::network::XbtCurrency) || - (rfq->side == bs::network::Side::Buy && rfq->product == bs::network::XbtCurrency)) { - submitRFQWrapper(); - return; // Nothing to reserve - } + auto quantity = bs::XBTAmount(rfq->quantity).GetValue(); + if (rfq->side == bs::network::Side::Buy) { + if (rfq->assetType == bs::network::Asset::PrivateMarket) { + quantity *= bs::XBTAmount(getOfferPrice()).GetValue(); + } else if (rfq->assetType == bs::network::Asset::SpotXBT) { + quantity /= getOfferPrice(); + } + } - if (!fixedXbtInputs_.inputs.empty()) { - submitRFQWrapper(); - return; // already reserved by user + const bool partial = rfq->assetType == bs::network::Asset::PrivateMarket; + getWalletAndReserve(quantity, partial); } - - auto quantity = bs::XBTAmount(rfq->quantity).GetValue(); - if (rfq->side == bs::network::Side::Buy) { - if (rfq->assetType == bs::network::Asset::PrivateMarket) { - quantity *= bs::XBTAmount(getOfferPrice()).GetValue(); + else { + const auto& walletId = ui_->comboBoxXBTWalletsSend->currentData(UiUtils::WalletIdRole).toString().toStdString(); + if (rfq->assetType == bs::network::Asset::PrivateMarket + && rfq->side == bs::network::Side::Buy) { + auto maxXbtQuantity = getXbtReservationAmountForCc(rfq->quantity, getOfferPrice()).GetValue(); + emit needReserveUTXOs(id, walletId, maxXbtQuantity, true); } - else if (rfq->assetType == bs::network::Asset::SpotXBT) { - quantity /= getOfferPrice(); + else { + if ((rfq->side == bs::network::Side::Sell && rfq->product != bs::network::XbtCurrency) || + (rfq->side == bs::network::Side::Buy && rfq->product == bs::network::XbtCurrency)) { + submitRFQCb_(id, *rfq, {}); // already reserved + pendingRFQs_.erase(id); + } + else if (!fixedXbtInputs_.inputs.empty()) { + submitRFQCb_(id, *rfq, {}); // already reserved + pendingRFQs_.erase(id); + } + + auto quantity = bs::XBTAmount(rfq->quantity).GetValue(); + if (rfq->side == bs::network::Side::Buy) { + if (rfq->assetType == bs::network::Asset::PrivateMarket) { + quantity *= bs::XBTAmount(getOfferPrice()).GetValue(); + } else if (rfq->assetType == bs::network::Asset::SpotXBT) { + quantity /= getOfferPrice(); + } + } + const bool partial = rfq->assetType == bs::network::Asset::PrivateMarket; + emit needReserveUTXOs(id, walletId, quantity, partial); } } - - const bool partial = rfq->assetType == bs::network::Asset::PrivateMarket; - getWalletAndReserve(quantity, partial); } void RFQTicketXBT::onCreateWalletClicked() diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h index a48878e94..aa3626a8d 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ b/BlockSettleUILib/Trading/RFQTicketXBT.h @@ -45,6 +45,7 @@ namespace bs { class Wallet; class WalletsManager; } + struct TradeSettings; class UTXOReservationManager; } class ArmoryConnection; @@ -102,7 +103,22 @@ Q_OBJECT UiUtils::WalletsTypes xbtWalletType() const; void onParentAboutToHide(); + + void onVerifiedAuthAddresses(const std::vector&); void onBalance(const std::string& currency, double balance); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onHDWallet(const bs::sync::HDWalletData&); + void onWalletData(const std::string& walletId, const bs::sync::WalletData&); + void onAuthKey(const bs::Address&, const BinaryData& authKey); + void onTradeSettings(const std::shared_ptr&); + void onReservedUTXOs(const std::string& resId, const std::string& subId + , const std::vector&); + +signals: + void needWalletData(const std::string& walletId); + void needAuthKey(const bs::Address&); + void needReserveUTXOs(const std::string& reserveId, const std::string& walletId + , uint64_t amount, bool partial = false, const std::vector& utxos = {}); public slots: void SetProductAndSide(const QString& productGroup, const QString& currencyPair @@ -120,12 +136,13 @@ public slots: void onSendRFQ(const std::string &id, const QString &symbol, double amount, bool buy); void onCancelRFQ(const std::string &id); + void onNewSecurity(const std::string& name, bs::network::Asset::Type at) { assetTypes_[name] = at; } void onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields); private slots: void updateBalances(); - void onSignerReady(); - void walletsLoaded(); + void onSignerReady(); //deprecated + void walletsLoaded(); //deprecated void onNumCcySelected(); void onDenomCcySelected(); @@ -149,7 +166,7 @@ private slots: void onCreateWalletClicked(); void onAuthAddrChanged(int); - void onSettlLeavesLoaded(unsigned int); + void onSettlLeavesLoaded(unsigned int); //deprecated void onUTXOReservationChanged(const std::string& walletId); @@ -185,9 +202,9 @@ private slots: BalanceInfoContainer getBalanceInfo() const; QString getProduct() const; - std::shared_ptr getCCWallet(const std::string &cc) const; + [[deprecated]] std::shared_ptr getCCWallet(const std::string &cc) const; bool checkBalance(double qty) const; - bool checkAuthAddr(double qty) const; + bool checkAuthAddr() const; bs::network::Side::Type getSelectedSide() const; std::string authKey() const; @@ -216,8 +233,12 @@ private slots: void productSelectionChanged(); - std::shared_ptr getSendXbtWallet() const; - std::shared_ptr getRecvXbtWallet() const; + [[deprecated]] std::shared_ptr getSendXbtWallet() const; + [[deprecated]] std::shared_ptr getRecvXbtWallet() const; + std::string getXbtLeafId(const std::string& hdWalletId) const; + bool hasSendXbtWallet() const; + bool hasRecvXbtWallet() const; + bool hasCCWallet() const { return true; } //TODO: add proper checking bs::XBTAmount getXbtBalance() const; QString getProductToSpend() const; QString getProductToRecv() const; @@ -227,6 +248,8 @@ private slots: void reserveBestUtxoSetAndSubmit(const std::string &id , const std::shared_ptr& rfq); + void sendDeferredRFQs(); + private: std::unique_ptr ui_; @@ -272,8 +295,11 @@ private slots: bool autoRFQenabled_{ false }; std::vector deferredRFQs_; + std::unordered_map assetTypes_; std::unordered_map mdInfo_; std::unordered_map balances_; + std::vector wallets_; + std::shared_ptr tradeSettings_; }; #endif // __RFQ_TICKET_XBT_H__ diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index 75c3e06d6..48ef1b66d 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -81,7 +81,7 @@ bool WalletShieldBase::checkWalletSettings(WalletShieldBase::ProductType product , const QString& product) { if (!walletsManager_ && !authMgr_) { - return true; // Temporary workaround for new arch - fix later if needed + return false; // Temporary workaround for new arch - fix later if needed } // No primary wallet bool hasFullWallet = false; diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index 0e6c44d02..706f34706 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -280,6 +280,73 @@ int UiUtils::fillHDWalletsComboBox(QComboBox* comboBox, const std::shared_ptr& wallets, int walletTypes) +{ + int selected = 0; + const auto b = comboBox->blockSignals(true); + comboBox->clear(); + + auto addRow = [comboBox](const std::string& label, const std::string& walletId, WalletsTypes type) { + if (WalletsTypes::None == type) { + return; + } + + int i = comboBox->count(); + comboBox->addItem(QString::fromStdString(label)); + comboBox->setItemData(i, QString::fromStdString(walletId), UiUtils::WalletIdRole); + comboBox->setItemData(i, QVariant::fromValue(static_cast(type)), UiUtils::WalletType); + }; + + for (const auto& hdWallet : wallets) { + if (hdWallet.primary) { + selected = comboBox->count(); + } + + WalletsTypes type = WalletsTypes::None; + // HW wallets marked as offline too, make sure to check that first +/* if (!hdWallet->canMixLeaves()) { + + if (hdWallet->isHardwareOfflineWallet() && !(walletTypes & WalletsTypes::WatchOnly)) { + continue; + } + + for (auto const& leaf : hdWallet->getGroup(hdWallet->getXBTGroupType())->getLeaves()) { + std::string label = hdWallet->name(); + type = WalletsTypes::None; + + auto purpose = leaf->purpose(); + if (purpose == bs::hd::Purpose::Native && + (walletTypes & WalletsTypes::HardwareNativeSW)) { + label += " Native"; + type = WalletsTypes::HardwareNativeSW; + } else if (purpose == bs::hd::Purpose::Nested && + (walletTypes & WalletsTypes::HardwareNestedSW)) { + label += " Nested"; + type = WalletsTypes::HardwareNestedSW; + } else if (purpose == bs::hd::Purpose::NonSegWit && + (walletTypes & WalletsTypes::HardwareLegacy) && leaf->getTotalBalance() > 0) { + label += " Legacy"; + type = WalletsTypes::HardwareLegacy; + } + addRow(label, hdWallet->walletId(), type); + } + continue; + }*/ //TODO + + if (hdWallet.offline) { + type = (walletTypes & WalletsTypes::WatchOnly) ? WalletsTypes::WatchOnly : WalletsTypes::None; + } else if (walletTypes & WalletsTypes::Full) { + type = WalletsTypes::Full; + } + + addRow(hdWallet.name, hdWallet.id, type); + } + comboBox->blockSignals(b); + comboBox->setCurrentIndex(selected); + return selected; +} + void UiUtils::fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox, const std::shared_ptr &authAddressManager) { comboBox->clear(); @@ -297,6 +364,23 @@ void UiUtils::fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox, const } } +void UiUtils::fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox + , const std::vector& addrs, int defaultIdx) +{ + const auto b = comboBox->blockSignals(true); + comboBox->clear(); + if (!addrs.empty()) { + for (const auto& address : addrs) { + comboBox->addItem(QString::fromStdString(address.display())); + } + comboBox->setEnabled(true); + comboBox->blockSignals(b); + comboBox->setCurrentIndex(defaultIdx); + } else { + comboBox->setEnabled(false); + } +} + void UiUtils::fillRecvAddressesComboBox(QComboBox* comboBox, const std::shared_ptr& targetWallet) { comboBox->clear(); @@ -335,6 +419,20 @@ void UiUtils::fillRecvAddressesComboBoxHDWallet(QComboBox* comboBox comboBox->setCurrentIndex(0); } +void UiUtils::fillRecvAddressesComboBoxHDWallet(QComboBox* comboBox + , const std::vector& wallets) +{ + comboBox->clear(); + comboBox->addItem(QObject::tr("Auto Create")); + for (const auto& wd : wallets) { + for (auto addr : wd.addresses) { + comboBox->addItem(QString::fromStdString(addr.address.display())); + } + } + comboBox->setEnabled(true); + comboBox->setCurrentIndex(0); +} + QPixmap UiUtils::getQRCode(const QString& address, int size) { QRcode* code = QRcode_encodeString(address.toLatin1(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); diff --git a/BlockSettleUILib/UiUtils.h b/BlockSettleUILib/UiUtils.h index d5496ce4b..d128f2461 100644 --- a/BlockSettleUILib/UiUtils.h +++ b/BlockSettleUILib/UiUtils.h @@ -22,6 +22,7 @@ #include "ApplicationSettings.h" #include "CommonTypes.h" #include "HDPath.h" +#include "SignerDefs.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; @@ -134,14 +135,18 @@ namespace UiUtils All = Full | HardwareSW | WatchOnly, All_AllowHwLegacy = All | HardwareAll }; - int fillHDWalletsComboBox(QComboBox* comboBox, const std::shared_ptr& walletsManager + [[deprecated]] int fillHDWalletsComboBox(QComboBox* comboBox, const std::shared_ptr& walletsManager , int walletTypes); + int fillHDWalletsComboBox(QComboBox* comboBox, const std::vector&, int walletTypes); - void fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox, const std::shared_ptr& authAddressManager); + [[deprecated]] void fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox, const std::shared_ptr& authAddressManager); + void fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox + , const std::vector &, int defaultIdx = 0); - void fillRecvAddressesComboBox(QComboBox* comboBox, const std::shared_ptr& targetWallet); - void fillRecvAddressesComboBoxHDWallet(QComboBox* comboBox + [[deprecated]] void fillRecvAddressesComboBox(QComboBox* comboBox, const std::shared_ptr& targetWallet); + [[deprecated]] void fillRecvAddressesComboBoxHDWallet(QComboBox* comboBox , const std::shared_ptr& targetHDWallet, bool showRegularWalletsOnly); + void fillRecvAddressesComboBoxHDWallet(QComboBox* comboBox, const std::vector& wallet); int selectWalletInCombobox(QComboBox* comboBox, const std::string& walletId, WalletsTypes type = WalletsTypes::None); std::string getSelectedWalletId(QComboBox* comboBox); diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index 4cb1c4ba1..5f29465fa 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -11,12 +11,14 @@ #include "MatchingAdapter.h" #include #include "Celer/CommonUtils.h" +#include "Celer/CancelRFQSequence.h" #include "Celer/CelerClientProxy.h" #include "Celer/CreateOrderSequence.h" #include "Celer/CreateFxOrderSequence.h" #include "Celer/GetAssignedAccountsListSequence.h" #include "Celer/SubmitRFQSequence.h" #include "CurrencyPair.h" +#include "MessageUtils.h" #include "ProtobufUtils.h" #include "TerminalMessage.h" @@ -188,6 +190,8 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) return processSendRFQ(msg.send_rfq()); case MatchingMessage::kAcceptRfq: return processAcceptRFQ(msg.accept_rfq()); + case MatchingMessage::kCancelRfq: + return processCancelRFQ(msg.cancel_rfq()); default: logger_->warn("[{}] unknown msg {} #{} from {}", __func__, msg.data_case() , env.id, env.sender->name()); @@ -223,7 +227,7 @@ bool MatchingAdapter::processSubmitAuth(const bs::message::Envelope& env return processGetSubmittedAuth(env); } -bool MatchingAdapter::processSendRFQ(const MatchingMessage_RFQ& request) +bool MatchingAdapter::processSendRFQ(const BlockSettle::Terminal::RFQ& request) { if (assignedAccount_.empty()) { logger_->error("[MatchingAdapter::processSendRFQ] submitting with empty account name"); @@ -249,7 +253,7 @@ bool MatchingAdapter::processSendRFQ(const MatchingMessage_RFQ& request) return true; } -bool MatchingAdapter::processAcceptRFQ(const MatchingMessage_AcceptRFQ& request) +bool MatchingAdapter::processAcceptRFQ(const AcceptRFQ& request) { if (assignedAccount_.empty()) { logger_->error("[MatchingAdapter::processAcceptRFQ] accepting with empty account name"); @@ -277,6 +281,19 @@ bool MatchingAdapter::processAcceptRFQ(const MatchingMessage_AcceptRFQ& request) return true; } +bool MatchingAdapter::processCancelRFQ(const std::string& rfqId) +{ + const auto &sequence = std::make_shared( + QString::fromStdString(rfqId), logger_); + if (!celerConnection_->ExecuteSequence(sequence)) { + logger_->error("[MatchingAdapter::processCancelRFQ] failed to execute CelerCancelRFQSequence"); + return false; + } else { + logger_->debug("[MatchingAdapter::processCancelRFQ] RFQ {} cancelled", rfqId); + } + return true; +} + void MatchingAdapter::saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId) { quoteIdMap_[quoteId] = quoteReqId; diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 21d48b5ad..dbefe65d9 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -19,9 +19,9 @@ namespace spdlog { } namespace BlockSettle { namespace Terminal { + class AcceptRFQ; class MatchingMessage_Login; - class MatchingMessage_RFQ; - class MatchingMessage_AcceptRFQ; + class RFQ; } } @@ -64,8 +64,9 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget bool processLogin(const BlockSettle::Terminal::MatchingMessage_Login&); bool processGetSubmittedAuth(const bs::message::Envelope&); bool processSubmitAuth(const bs::message::Envelope&, const std::string& address); - bool processSendRFQ(const BlockSettle::Terminal::MatchingMessage_RFQ&); - bool processAcceptRFQ(const BlockSettle::Terminal::MatchingMessage_AcceptRFQ&); + bool processSendRFQ(const BlockSettle::Terminal::RFQ&); + bool processAcceptRFQ(const BlockSettle::Terminal::AcceptRFQ&); + bool processCancelRFQ(const std::string& rfqId); std::string getQuoteReqId(const std::string& quoteId) const; void saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId); diff --git a/Core/MessageUtils.cpp b/Core/MessageUtils.cpp new file mode 100644 index 000000000..10c8ae1e2 --- /dev/null +++ b/Core/MessageUtils.cpp @@ -0,0 +1,133 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "MessageUtils.h" +#include "terminal.pb.h" + +using namespace bs::message; +using namespace BlockSettle::Terminal; + +bs::network::RFQ bs::message::fromMsg(const BlockSettle::Terminal::RFQ& msg) +{ + bs::network::RFQ rfq; + rfq.requestId = msg.id(); + rfq.security = msg.security(); + rfq.product = msg.product(); + rfq.assetType = static_cast(msg.asset_type()); + rfq.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + rfq.quantity = msg.quantity(); + rfq.requestorAuthPublicKey = msg.auth_pub_key(); + rfq.receiptAddress = msg.receipt_address(); + rfq.coinTxInput = msg.coin_tx_input(); + return rfq; +} + +void bs::message::toMsg(const bs::network::RFQ& rfq, BlockSettle::Terminal::RFQ* msg) +{ + msg->set_id(rfq.requestId); + msg->set_security(rfq.security); + msg->set_product(rfq.product); + msg->set_asset_type((int)rfq.assetType); + msg->set_buy(rfq.side == bs::network::Side::Buy); + msg->set_quantity(rfq.quantity); + msg->set_auth_pub_key(rfq.requestorAuthPublicKey); + msg->set_receipt_address(rfq.receiptAddress); + msg->set_coin_tx_input(rfq.coinTxInput); +} + + +bs::network::Quote bs::message::fromMsg(const BlockSettle::Terminal::Quote& msg) +{ + bs::network::Quote quote; + quote.requestId = msg.request_id(); + quote.quoteId = msg.quote_id(); + quote.security = msg.security(); + quote.product = msg.product(); + quote.price = msg.price(); + quote.quantity = msg.quantity(); + quote.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + quote.assetType = static_cast(msg.asset_type()); + quote.quotingType = static_cast(msg.quoting_type()); + quote.requestorAuthPublicKey = msg.req_auth_pub_key(); + quote.dealerAuthPublicKey = msg.deal_auth_pub_key(); + quote.settlementId = msg.settlement_id(); + quote.dealerTransaction = msg.dealer_tx(); + quote.expirationTime = QDateTime::fromSecsSinceEpoch(msg.expiration_time()); + quote.timeSkewMs = msg.time_skew_ms(); + quote.celerTimestamp = msg.timestamp(); + return quote; +} + +void bs::message::toMsg(const bs::network::Quote& quote, BlockSettle::Terminal::Quote* msg) +{ + msg->set_request_id(quote.requestId); + msg->set_quote_id(quote.quoteId); + msg->set_security(quote.security); + msg->set_product(quote.product); + msg->set_price(quote.price); + msg->set_quantity(quote.quantity); + msg->set_buy(quote.side == bs::network::Side::Buy); + msg->set_asset_type((int)quote.assetType); + msg->set_quoting_type((int)quote.quotingType); + msg->set_req_auth_pub_key(quote.requestorAuthPublicKey); + msg->set_deal_auth_pub_key(quote.dealerAuthPublicKey); + msg->set_settlement_id(quote.settlementId); + msg->set_dealer_tx(quote.dealerTransaction); + msg->set_expiration_time(quote.expirationTime.toSecsSinceEpoch()); + msg->set_time_skew_ms(quote.timeSkewMs); + msg->set_timestamp(quote.celerTimestamp); +} + + +bs::network::Order bs::message::fromMsg(const BlockSettle::Terminal::MatchingMessage_Order& msg) +{ + bs::network::Order order; + order.clOrderId = msg.cl_order_id(); + order.exchOrderId = QString::fromStdString(msg.exchange_id()); + order.quoteId = msg.quote_id(); + order.dateTime = QDateTime::fromMSecsSinceEpoch(msg.timestamp()); + order.security = msg.security(); + order.product = msg.product(); + order.settlementId = msg.settlement_id(); + order.reqTransaction = msg.requester_tx(); + order.dealerTransaction = msg.dealer_tx(); + order.pendingStatus = msg.pending_status(); + order.quantity = msg.quantity(); + order.leavesQty = msg.left_qty(); + order.price = msg.price(); + order.avgPx = msg.avg_price(); + order.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + order.assetType = static_cast(msg.asset_type()); + order.status = static_cast(msg.status()); + order.info = msg.info(); + return order; +} + +void bs::message::toMsg(const bs::network::Order& order, MatchingMessage_Order* msg) +{ + msg->set_cl_order_id(order.clOrderId); + msg->set_exchange_id(order.exchOrderId.toStdString()); + msg->set_quote_id(order.quoteId); + msg->set_timestamp(order.dateTime.toMSecsSinceEpoch()); + msg->set_security(order.security); + msg->set_product(order.product); + msg->set_settlement_id(order.settlementId); + msg->set_requester_tx(order.reqTransaction); + msg->set_dealer_tx(order.dealerTransaction); + msg->set_pending_status(order.pendingStatus); + msg->set_quantity(order.quantity); + msg->set_left_qty(order.leavesQty); + msg->set_price(order.price); + msg->set_avg_price(order.avgPx); + msg->set_buy(order.side == bs::network::Side::Buy); + msg->set_asset_type((int)order.assetType); + msg->set_status((int)order.status); + msg->set_info(order.info); +} diff --git a/Core/MessageUtils.h b/Core/MessageUtils.h new file mode 100644 index 000000000..971b0471b --- /dev/null +++ b/Core/MessageUtils.h @@ -0,0 +1,41 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef MESSAGE_UTILS_H +#define MESSAGE_UTILS_H + +#include "Message/Bus.h" +#include "Message/Envelope.h" +#include "CommonTypes.h" + +namespace BlockSettle { + namespace Terminal { + class RFQ; + class Quote; + class MatchingMessage_Order; + } +} + +namespace bs { + namespace message { + + bs::network::RFQ fromMsg(const BlockSettle::Terminal::RFQ&); + void toMsg(const bs::network::RFQ&, BlockSettle::Terminal::RFQ*); + + bs::network::Quote fromMsg(const BlockSettle::Terminal::Quote&); + void toMsg(const bs::network::Quote&, BlockSettle::Terminal::Quote*); + + bs::network::Order fromMsg(const BlockSettle::Terminal::MatchingMessage_Order&); + void toMsg(const bs::network::Order&, BlockSettle::Terminal::MatchingMessage_Order*); + + } // namespace message +} // namespace bs + +#endif // MESSAGE_UTILS_H diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index ec7e7d3f7..5dd4375e6 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -10,17 +10,22 @@ */ #include "SettlementAdapter.h" #include +#include "MessageUtils.h" #include "TerminalMessage.h" +#include "common.pb.h" #include "terminal.pb.h" +using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; SettlementAdapter::SettlementAdapter(const std::shared_ptr &logger) : logger_(logger) - , user_(std::make_shared(bs::message::TerminalUsers::Settlement)) + , user_(std::make_shared(TerminalUsers::Settlement)) + , userMtch_(std::make_shared(TerminalUsers::Matching)) + , userWallets_(std::make_shared(TerminalUsers::Wallets)) {} bool SettlementAdapter::process(const bs::message::Envelope &env) @@ -39,5 +44,143 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) pushFill(envBC); } } + else if (env.sender->value() == userMtch_->value()) { + MatchingMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse matching msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MatchingMessage::kQuote: + return processMatchingQuote(msg.quote()); + case MatchingMessage::kOrder: + return processMatchingOrder(msg.order()); + default: break; + } + } + else if (env.receiver && (env.receiver->value() == user_->value())) { + SettlementMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse own request #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettlementMessage::kCancelRfq: + return processCancelRFQ(msg.cancel_rfq()); + case SettlementMessage::kAcceptRfq: + return processAcceptRFQ(env, msg.accept_rfq()); + case SettlementMessage::kSendRfq: + return processSendRFQ(env, msg.send_rfq()); + default: + logger_->warn("[{}] unknown settlement request {}", __func__, msg.data_case()); + break; + } + } return true; } + +bool SettlementAdapter::processMatchingQuote(const BlockSettle::Terminal::Quote& response) +{ + const auto& itSettl = settlByRfqId_.find(response.request_id()); + if (itSettl == settlByRfqId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, response.request_id()); + return true; + } + SettlementMessage msg; + auto msgResp = msg.mutable_quote(); + *msgResp = response; + Envelope env{ 0, user_, itSettl->second.env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(env); +} + +bool SettlementAdapter::processMatchingOrder(const MatchingMessage_Order& response) +{ + const auto& itSettl = settlByQuoteId_.find(response.quote_id()); + if (itSettl == settlByRfqId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, response.quote_id()); + return true; + } + SettlementMessage msg; + const auto& order = fromMsg(response); + if (order.status == bs::network::Order::Status::Filled) { + settlBySettlId_[response.settlement_id()] = itSettl->second; + auto msgResp = msg.mutable_matched_quote(); + msgResp->set_rfq_id(itSettl->second.rfq.requestId); + msgResp->set_quote_id(response.quote_id()); + msgResp->set_price(response.price()); + } + else if (order.status == bs::network::Order::Status::Failed) { + auto msgResp = msg.mutable_failed_quote(); + msgResp->set_rfq_id(itSettl->second.rfq.requestId); + msgResp->set_quote_id(response.quote_id()); + msgResp->set_info(order.info); + } + else { + logger_->debug("[{}] {} unprocessed order status {}", __func__, order.quoteId + , (int)order.status); + return true; + } + Envelope env{ 0, user_, itSettl->second.env.sender, {}, {} + , msg.SerializeAsString() }; + settlByQuoteId_.erase(itSettl); + return pushFill(env); +} + +bool SettlementAdapter::processCancelRFQ(const std::string& rfqId) +{ + const auto& itSettl = settlByRfqId_.find(rfqId); + if (itSettl == settlByRfqId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, rfqId); + return true; + } + settlByRfqId_.erase(itSettl); + unreserve(rfqId); + MatchingMessage msg; + msg.set_cancel_rfq(rfqId); + Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; + return pushFill(envReq); +} + +bool SettlementAdapter::processAcceptRFQ(const bs::message::Envelope& env + , const AcceptRFQ& request) +{ + const auto& itSettl = settlByRfqId_.find(request.rfq_id()); + if (itSettl == settlByRfqId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, request.rfq_id()); + return true; + } + itSettl->second.env = env; + itSettl->second.quote = fromMsg(request.quote()); + settlByQuoteId_[request.quote().quote_id()] = itSettl->second; + settlByRfqId_.erase(itSettl); + + MatchingMessage msg; + auto msgReq = msg.mutable_accept_rfq(); + *msgReq = request; + Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; + return pushFill(envReq); +} + +bool SettlementAdapter::processSendRFQ(const bs::message::Envelope& env + , const SettlementMessage_SendRFQ& request) +{ + const auto& rfq = fromMsg(request.rfq()); + settlByRfqId_[rfq.requestId] = Settlement{ env, false, rfq, request.reserve_id() }; + + MatchingMessage msg; + auto msgReq = msg.mutable_send_rfq(); + *msgReq = request.rfq(); + Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; + return pushFill(envReq); +} + +void SettlementAdapter::unreserve(const std::string& id, const std::string &subId) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_unreserve_utxos(); + msgReq->set_id(id); + msgReq->set_sub_id(subId); + Envelope envReq{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(envReq); +} diff --git a/Core/SettlementAdapter.h b/Core/SettlementAdapter.h index aea09cefe..eb5afbf83 100644 --- a/Core/SettlementAdapter.h +++ b/Core/SettlementAdapter.h @@ -11,11 +11,20 @@ #ifndef SETTLEMENT_ADAPTER_H #define SETTLEMENT_ADAPTER_H +#include "CommonTypes.h" #include "Message/Adapter.h" namespace spdlog { class logger; } +namespace BlockSettle { + namespace Terminal { + class AcceptRFQ; + class MatchingMessage_Order; + class Quote; + class SettlementMessage_SendRFQ; + } +} class SettlementAdapter : public bs::message::Adapter { @@ -31,10 +40,31 @@ class SettlementAdapter : public bs::message::Adapter std::string name() const override { return "Settlement"; } private: + bool processMatchingQuote(const BlockSettle::Terminal::Quote&); + bool processMatchingOrder(const BlockSettle::Terminal::MatchingMessage_Order&); + + bool processCancelRFQ(const std::string& rfqId); + bool processAcceptRFQ(const bs::message::Envelope& + , const BlockSettle::Terminal::AcceptRFQ&); + bool processSendRFQ(const bs::message::Envelope& + , const BlockSettle::Terminal::SettlementMessage_SendRFQ&); + + void unreserve(const std::string& id, const std::string& subId = {}); private: std::shared_ptr logger_; - std::shared_ptr user_; + std::shared_ptr user_, userMtch_, userWallets_; + + struct Settlement { + bs::message::Envelope env; + bool dealer{ false }; + bs::network::RFQ rfq; + std::string reserveId; + bs::network::Quote quote; + }; + std::unordered_map settlByRfqId_; + std::unordered_map settlByQuoteId_; + std::unordered_map settlBySettlId_; }; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 1ddc982bf..5c37a90d8 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -125,6 +125,8 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env case SignerMessage::kSetUserId: return processSetUserId(request.set_user_id().user_id() , request.set_user_id().wallet_id()); + case SignerMessage::kCreateSettlWallet: + return processCreateSettlWallet(env, request.create_settl_wallet()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -649,3 +651,25 @@ bool SignerAdapter::processSetUserId(const std::string& userId, const std::strin { return (signer_->setUserId(BinaryData::fromString(userId), walletId) != 0); } + +bool SignerAdapter::processCreateSettlWallet(const bs::message::Envelope& env + , const std::string& addrStr) +{ + bs::Address authAddr; + try { + authAddr = bs::Address::fromAddressString(addrStr); + } + catch (const std::exception& e) { + logger_->error("[{}] invalid auth address {}: {}", __func__, addrStr, e.what()); + return true; + } + const auto& cb = [this, env](const SecureBinaryData& authPubKey) + { + SignerMessage msg; + msg.set_auth_pubkey(authPubKey.toBinStr()); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(envResp); + }; + signer_->createSettlementWallet(authAddr, cb); + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 96744a232..3a7901f45 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -91,6 +91,7 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget bool processSignTx(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_SignTxRequest&); bool processSetUserId(const std::string& userId, const std::string& walletId); + bool processCreateSettlWallet(const bs::message::Envelope&, const std::string &); private: std::shared_ptr logger_; diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index 4cfcada45..3c752dc85 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -96,94 +96,3 @@ bool TerminalInprocBus::run(int &argc, char **argv) runnableAdapter_->run(argc, argv); return true; } - - -//TODO: move to another source file -using namespace BlockSettle::Terminal; -bs::network::Quote bs::message::fromMsg(const MatchingMessage_Quote& msg) -{ - bs::network::Quote quote; - quote.requestId = msg.request_id(); - quote.quoteId = msg.quote_id(); - quote.security = msg.security(); - quote.product = msg.product(); - quote.price = msg.price(); - quote.quantity = msg.quantity(); - quote.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - quote.assetType = static_cast(msg.asset_type()); - quote.quotingType = static_cast(msg.quoting_type()); - quote.requestorAuthPublicKey = msg.req_auth_pub_key(); - quote.dealerAuthPublicKey = msg.deal_auth_pub_key(); - quote.settlementId = msg.settlement_id(); - quote.dealerTransaction = msg.dealer_tx(); - quote.expirationTime = QDateTime::fromSecsSinceEpoch(msg.expiration_time()); - quote.timeSkewMs = msg.time_skew_ms(); - quote.celerTimestamp = msg.timestamp(); - return quote; -} - -void bs::message::toMsg(const bs::network::Quote& quote, MatchingMessage_Quote* msg) -{ - msg->set_request_id(quote.requestId); - msg->set_quote_id(quote.quoteId); - msg->set_security(quote.security); - msg->set_product(quote.product); - msg->set_price(quote.price); - msg->set_quantity(quote.quantity); - msg->set_buy(quote.side == bs::network::Side::Buy); - msg->set_asset_type((int)quote.assetType); - msg->set_quoting_type((int)quote.quotingType); - msg->set_req_auth_pub_key(quote.requestorAuthPublicKey); - msg->set_deal_auth_pub_key(quote.dealerAuthPublicKey); - msg->set_settlement_id(quote.settlementId); - msg->set_dealer_tx(quote.dealerTransaction); - msg->set_expiration_time(quote.expirationTime.toSecsSinceEpoch()); - msg->set_time_skew_ms(quote.timeSkewMs); - msg->set_timestamp(quote.celerTimestamp); -} - -bs::network::Order bs::message::fromMsg(const BlockSettle::Terminal::MatchingMessage_Order& msg) -{ - bs::network::Order order; - order.clOrderId = msg.cl_order_id(); - order.exchOrderId = QString::fromStdString(msg.exchange_id()); - order.quoteId = msg.quote_id(); - order.dateTime = QDateTime::fromMSecsSinceEpoch(msg.timestamp()); - order.security = msg.security(); - order.product = msg.product(); - order.settlementId = msg.settlement_id(); - order.reqTransaction = msg.requester_tx(); - order.dealerTransaction = msg.dealer_tx(); - order.pendingStatus = msg.pending_status(); - order.quantity = msg.quantity(); - order.leavesQty = msg.left_qty(); - order.price = msg.price(); - order.avgPx = msg.avg_price(); - order.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - order.assetType = static_cast(msg.asset_type()); - order.status = static_cast(msg.status()); - order.info = msg.info(); - return order; -} - -void bs::message::toMsg(const bs::network::Order& order, MatchingMessage_Order* msg) -{ - msg->set_cl_order_id(order.clOrderId); - msg->set_exchange_id(order.exchOrderId.toStdString()); - msg->set_quote_id(order.quoteId); - msg->set_timestamp(order.dateTime.toMSecsSinceEpoch()); - msg->set_security(order.security); - msg->set_product(order.product); - msg->set_settlement_id(order.settlementId); - msg->set_requester_tx(order.reqTransaction); - msg->set_dealer_tx(order.dealerTransaction); - msg->set_pending_status(order.pendingStatus); - msg->set_quantity(order.quantity); - msg->set_left_qty(order.leavesQty); - msg->set_price(order.price); - msg->set_avg_price(order.avgPx); - msg->set_buy(order.side == bs::network::Side::Buy); - msg->set_asset_type((int)order.assetType); - msg->set_status((int)order.status); - msg->set_info(order.info); -} diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index d9ee4b339..61b8c4334 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -13,7 +13,6 @@ #include "Message/Bus.h" #include "Message/Envelope.h" -#include "CommonTypes.h" namespace BlockSettle { namespace Terminal { @@ -101,13 +100,6 @@ namespace bs { std::shared_ptr runnableAdapter_; }; - - //TODO: move to another source file - bs::network::Quote fromMsg(const BlockSettle::Terminal::MatchingMessage_Quote&); - void toMsg(const bs::network::Quote&, BlockSettle::Terminal::MatchingMessage_Quote*); - - bs::network::Order fromMsg(const BlockSettle::Terminal::MatchingMessage_Order&); - void toMsg(const bs::network::Order&, BlockSettle::Terminal::MatchingMessage_Order*); } // namespace message } // namespace bs diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 3c18ca90e..6ee5be5be 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -228,6 +228,7 @@ void MainWindow::onHDWallet(const bs::sync::WalletInfo &wi) void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { ui_->widgetWallets->onHDWalletDetails(hdWallet); + ui_->widgetRFQ->onHDWallet(hdWallet); } void MainWindow::onWalletsList(const std::string &id, const std::vector& wallets) @@ -237,6 +238,12 @@ void MainWindow::onWalletsList(const std::string &id, const std::vectorwidgetRFQ->onWalletData(walletId, wd); +} + void MainWindow::onAddresses(const std::vector &addrs) { if (txDlg_) { @@ -264,6 +271,7 @@ void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) txDlg_->onAddressBalances(wbd.id, wbd.addrBalances); } ui_->widgetWallets->onWalletBalance(wbd); + ui_->widgetRFQ->onWalletBalance(wbd); statusBarView_->onXbtBalance(wbd); } @@ -884,12 +892,13 @@ void MainWindow::activateClient(const BsClientLoginResult& result) auto tradeSettings = std::make_shared(result.tradeSettings); emit putSetting(ApplicationSettings::SubmittedAddressXbtLimit, static_cast(tradeSettings->xbtTier1Limit)); -// authManager_->initLogin(celerConnection_, tradeSettings_); emit bootstrapDataLoaded(result.bootstrapDataSigned); setLoginButtonText(currentUserLogin_); setWidgetsAuthorized(true); + ui_->widgetRFQ->onTradeSettings(tradeSettings); + emit setRecommendedFeeRate(result.feeRatePb); // utxoReservationMgr_->setFeeRatePb(result.feeRatePb); emit needMatchingLogin(result.celerLogin, result.login); @@ -999,6 +1008,11 @@ void MainWindow::onMatchingLogout() ui_->widgetRFQ->onMatchingLogout(); } +void bs::gui::qt::MainWindow::onNewSecurity(const std::string& name, bs::network::Asset::Type at) +{ + ui_->widgetRFQ->onNewSecurity(name, at); +} + void MainWindow::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &fields) { @@ -1032,21 +1046,38 @@ void MainWindow::onVerifiedAuthAddresses(const std::vector& addrs) ui_->widgetRFQ->onVerifiedAuthAddresses(addrs); } -void bs::gui::qt::MainWindow::onQuoteReceived(const bs::network::Quote& quote) +void MainWindow::onAuthKey(const bs::Address& addr, const BinaryData& authKey) +{ + ui_->widgetRFQ->onAuthKey(addr, authKey); +} + +void MainWindow::onQuoteReceived(const bs::network::Quote& quote) { ui_->widgetRFQ->onQuoteReceived(quote); } -void bs::gui::qt::MainWindow::onOrderReceived(const bs::network::Order& order) +void MainWindow::onQuoteMatched(const std::string& rfqId, const std::string "eId) +{ + ui_->widgetRFQ->onQuoteMatched(rfqId, quoteId); +} + +void MainWindow::onQuoteFailed(const std::string& rfqId, const std::string& quoteId + , const std::string &info) { - ui_->widgetRFQ->onOrderReceived(order); + ui_->widgetRFQ->onQuoteFailed(rfqId, quoteId, info); } -void bs::gui::qt::MainWindow::onOrdersUpdate(const std::vector& orders) +void MainWindow::onOrdersUpdate(const std::vector& orders) { orderListModel_->onOrdersUpdate(orders); } +void MainWindow::onReservedUTXOs(const std::string& resId + , const std::string& subId, const std::vector& utxos) +{ + ui_->widgetRFQ->onReservedUTXOs(resId, subId, utxos); +} + void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") @@ -1214,9 +1245,13 @@ void MainWindow::initWidgets() ui_->widgetRFQ->init(logger_, dialogMgr_, orderListModel_.get()); connect(ui_->widgetRFQ, &RFQRequestWidget::requestPrimaryWalletCreation, this, &MainWindow::createNewWallet); + connect(ui_->widgetRFQ, &RFQRequestWidget::needWalletData, this, &MainWindow::needWalletData); + connect(ui_->widgetRFQ, &RFQRequestWidget::loginRequested, this, &MainWindow::onLoginInitiated); connect(ui_->widgetRFQ, &RFQRequestWidget::needSubmitRFQ, this, &MainWindow::needSubmitRFQ); connect(ui_->widgetRFQ, &RFQRequestWidget::needAcceptRFQ, this, &MainWindow::needAcceptRFQ); connect(ui_->widgetRFQ, &RFQRequestWidget::needCancelRFQ, this, &MainWindow::needCancelRFQ); + connect(ui_->widgetRFQ, &RFQRequestWidget::needAuthKey, this, &MainWindow::needAuthKey); + connect(ui_->widgetRFQ, &RFQRequestWidget::needReserveUTXOs, this, &MainWindow::needReserveUTXOs); connect(ui_->widgetRFQReply, &RFQReplyWidget::requestPrimaryWalletCreation, this , &MainWindow::createNewWallet); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 1fe133d5d..b75ffecdd 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -75,6 +75,7 @@ namespace bs { void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); void onWalletsList(const std::string &id, const std::vector&); + void onWalletData(const std::string &walletId, const bs::sync::WalletData&); void onAddresses(const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); @@ -100,6 +101,7 @@ namespace bs { , const std::string &userId); void onMatchingLogout(); + void onNewSecurity(const std::string& name, bs::network::Asset::Type); void onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &); void onBalance(const std::string& currency, double balance); @@ -108,11 +110,17 @@ namespace bs { , const std::map &); void onSubmittedAuthAddresses(const std::vector&); void onVerifiedAuthAddresses(const std::vector&); + void onAuthKey(const bs::Address&, const BinaryData& authKey); void onQuoteReceived(const bs::network::Quote&); - void onOrderReceived(const bs::network::Order&); + void onQuoteMatched(const std::string &rfqId, const std::string "eId); + void onQuoteFailed(const std::string& rfqId, const std::string& quoteId + , const std::string &info); void onOrdersUpdate(const std::vector&); + void onReservedUTXOs(const std::string& resId, const std::string& subId + , const std::vector&); + public slots: void onReactivate(); void raiseWindow(); @@ -144,6 +152,7 @@ namespace bs { void needHDWalletDetails(const std::string &walletId); void needWalletsList(UiUtils::WalletsTypes, const std::string &id); void needWalletBalances(const std::string &walletId); + void needWalletData(const std::string& walletId); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); @@ -178,9 +187,13 @@ namespace bs { void needNewAuthAddress(); void needSubmitAuthAddress(const bs::Address&); - void needSubmitRFQ(const bs::network::RFQ&); + void needSubmitRFQ(const bs::network::RFQ&, const std::string& reserveId = {}); void needAcceptRFQ(const std::string& id, const bs::network::Quote&); void needCancelRFQ(const std::string& id); + void needAuthKey(const bs::Address&); + void needReserveUTXOs(const std::string& reserveId, const std::string& subId + , uint64_t amount, bool partial = false, const std::vector& utxos = {}); + void needUnreserveUTXOs(const std::string& reserveId, const std::string& subId); private slots: void onSend(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index b32070474..dde45cb42 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -28,6 +28,7 @@ #include "BSMessageBox.h" #include "BSTerminalSplashScreen.h" #include "MainWindow.h" +#include "MessageUtils.h" #include "ProtobufHeadlessUtils.h" #include "SettingsAdapter.h" #include "TradesVerification.h" @@ -124,6 +125,7 @@ QtGuiAdapter::QtGuiAdapter(const std::shared_ptr &logger) , userSigner_(std::make_shared(TerminalUsers::Signer)) , userBS_(std::make_shared(TerminalUsers::BsServer)) , userMatch_(std::make_shared(TerminalUsers::Matching)) + , userSettl_(std::make_shared(TerminalUsers::Settlement)) , userMD_(std::make_shared(TerminalUsers::MktData)) , userTrk_(std::make_shared(TerminalUsers::OnChainTracker)) {} @@ -245,6 +247,8 @@ bool QtGuiAdapter::process(const Envelope &env) return processWallets(env); case TerminalUsers::BsServer: return processBsServer(env); + case TerminalUsers::Settlement: + return processSettlement(env); case TerminalUsers::Matching: return processMatching(env); case TerminalUsers::MktData: @@ -548,7 +552,8 @@ bool QtGuiAdapter::processWallets(const Envelope &env) }); } break; - + case WalletsMessage::kWalletData: + return processWalletData(env.id, msg.wallet_data()); case WalletsMessage::kWalletBalances: return processWalletBalances(env, msg.wallet_balances()); case WalletsMessage::kTxDetailsResponse: @@ -559,6 +564,10 @@ bool QtGuiAdapter::processWallets(const Envelope &env) return processUTXOs(msg.utxos()); case WalletsMessage::kAuthWallet: return processAuthWallet(msg.auth_wallet()); + case WalletsMessage::kAuthKey: + return processAuthKey(msg.auth_key()); + case WalletsMessage::kReservedUtxos: + return processReservedUTXOs(msg.reserved_utxos()); default: break; } return true; @@ -739,6 +748,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::setSigner, this, &QtGuiAdapter::onSetSigner); connect(mainWindow_, &bs::gui::qt::MainWindow::bootstrapDataLoaded, this, &QtGuiAdapter::onBootstrapDataLoaded); connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); + connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletData, this, &QtGuiAdapter::onNeedWalletData); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needIntAddresses, this, &QtGuiAdapter::onNeedIntAddresses); @@ -768,6 +778,9 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needSubmitRFQ, this, &QtGuiAdapter::onNeedSubmitRFQ); connect(mainWindow_, &bs::gui::qt::MainWindow::needAcceptRFQ, this, &QtGuiAdapter::onNeedAcceptRFQ); connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelRFQ, this, &QtGuiAdapter::onNeedCancelRFQ); + connect(mainWindow_, &bs::gui::qt::MainWindow::needAuthKey, this, &QtGuiAdapter::onNeedAuthKey); + connect(mainWindow_, &bs::gui::qt::MainWindow::needReserveUTXOs, this, &QtGuiAdapter::onNeedReserveUTXOs); + connect(mainWindow_, &bs::gui::qt::MainWindow::needUnreserveUTXOs, this, &QtGuiAdapter::onNeedUnreserveUTXOs); } void QtGuiAdapter::onGetSettings(const std::vector& settings) @@ -913,6 +926,16 @@ void QtGuiAdapter::onNeedWalletBalances(const std::string &walletId) pushFill(env); } +void QtGuiAdapter::onNeedWalletData(const std::string& walletId) +{ + WalletsMessage msg; + msg.set_wallet_get(walletId); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + if (pushFill(env)) { + walletGetMap_[env.id] = walletId; + } +} + void QtGuiAdapter::onNeedExtAddresses(const std::string &walletId) { logger_->debug("[{}] {}", __func__, walletId); @@ -1207,30 +1230,23 @@ void QtGuiAdapter::onNeedSubmitAuthAddress(const bs::Address& addr) pushFill(env); } -void QtGuiAdapter::onNeedSubmitRFQ(const bs::network::RFQ& rfq) +void QtGuiAdapter::onNeedSubmitRFQ(const bs::network::RFQ& rfq, const std::string& reserveId) { - MatchingMessage msg; + SettlementMessage msg; auto msgReq = msg.mutable_send_rfq(); - msgReq->set_id(rfq.requestId); - msgReq->set_security(rfq.security); - msgReq->set_product(rfq.product); - msgReq->set_asset_type((int)rfq.assetType); - msgReq->set_buy(rfq.side == bs::network::Side::Buy); - msgReq->set_quantity(rfq.quantity); - msgReq->set_auth_pub_key(rfq.requestorAuthPublicKey); - msgReq->set_receipt_address(rfq.receiptAddress); - msgReq->set_coin_tx_input(rfq.coinTxInput); - Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; + toMsg(rfq, msgReq->mutable_rfq()); + msgReq->set_reserve_id(reserveId); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } void QtGuiAdapter::onNeedAcceptRFQ(const std::string& id, const bs::network::Quote& quote) { - MatchingMessage msg; + SettlementMessage msg; auto msgReq = msg.mutable_accept_rfq(); msgReq->set_rfq_id(id); toMsg(quote, msgReq->mutable_quote()); - Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } @@ -1238,6 +1254,43 @@ void QtGuiAdapter::onNeedCancelRFQ(const std::string& id) { } +void QtGuiAdapter::onNeedAuthKey(const bs::Address& addr) +{ + WalletsMessage msg; + msg.set_get_auth_key(addr.display()); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedReserveUTXOs(const std::string& reserveId + , const std::string& subId, uint64_t amount, bool partial + , const std::vector& utxos) +{ + logger_->debug("[{}] {}/{} {}", __func__, reserveId, subId, amount); + WalletsMessage msg; + auto msgReq = msg.mutable_reserve_utxos(); + msgReq->set_id(reserveId); + msgReq->set_sub_id(subId); + msgReq->set_amount(amount); + msgReq->set_partial(partial); + for (const auto& utxo : utxos) { + msgReq->add_utxos(utxo.serialize().toBinStr()); + } + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onNeedUnreserveUTXOs(const std::string& reserveId + , const std::string& subId) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_unreserve_utxos(); + msgReq->set_id(reserveId); + msgReq->set_sub_id(subId); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -1248,6 +1301,24 @@ void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) } } +bool QtGuiAdapter::processWalletData(uint64_t msgId + , const WalletsMessage_WalletData& response) +{ + const auto& itWallet = walletGetMap_.find(msgId); + if (itWallet == walletGetMap_.end()) { + return true; + } + const auto& walletId = itWallet->second; + const auto& walletData = bs::sync::WalletData::fromCommonMessage(response); + if (QMetaObject::invokeMethod(mainWindow_, [this, walletId, walletData] { + mainWindow_->onWalletData(walletId, walletData); + })) { + walletGetMap_.erase(itWallet); + return true; + } + return false; +} + bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env , const WalletsMessage_WalletBalances &response) { @@ -1535,6 +1606,25 @@ bool QtGuiAdapter::processLogin(const BsServerMessage_LoginResult& response) }); } +bool QtGuiAdapter::processSettlement(const bs::message::Envelope& env) +{ + SettlementMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettlementMessage::kQuote: + return processQuote(msg.quote()); + case SettlementMessage::kMatchedQuote: + return processMatchedQuote(msg.matched_quote()); + case SettlementMessage::kFailedQuote: + return processFailedQuote(msg.failed_quote()); + default: break; + } + return true; +} + bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) { MatchingMessage msg; @@ -1552,10 +1642,8 @@ bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMatchingLogout(); }); - case MatchingMessage::kQuote: - return processQuote(msg.quote()); - case MatchingMessage::kOrder: - return processOrder(msg.order()); +/* case MatchingMessage::kQuoteNotif: + return processQuoteNotif(msg.quote_notif());*/ default: break; } return true; @@ -1573,9 +1661,7 @@ bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) mdInstrumentsReceived_ = false; break; case MktDataMessage::kNewSecurity: - assetTypes_[msg.new_security().name()] = - static_cast(msg.new_security().asset_type()); - break; + return processSecurity(msg.new_security().name(), msg.new_security().asset_type()); case MktDataMessage::kAllInstrumentsReceived: mdInstrumentsReceived_ = true; return sendPooledOrdersUpdate(); @@ -1586,6 +1672,12 @@ bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) return true; } +bool QtGuiAdapter::processSecurity(const std::string& name, int assetType) +{ + assetTypes_[name] = static_cast(assetType); + return true; +} + bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) { return QMetaObject::invokeMethod(mainWindow_, [this, msg] { @@ -1670,7 +1762,18 @@ bool QtGuiAdapter::processVerifiedAuthAddrs(const OnChainTrackMessage_AuthAddres }); } -bool QtGuiAdapter::processQuote(const MatchingMessage_Quote& msg) +bool QtGuiAdapter::processAuthKey(const WalletsMessage_AuthKey& response) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, response] { + try { + mainWindow_->onAuthKey(bs::Address::fromAddressString(response.auth_address()) + , BinaryData::fromString(response.auth_key())); + } + catch (const std::exception&) {} + }); +} + +bool QtGuiAdapter::processQuote(const BlockSettle::Terminal::Quote& msg) { const auto& quote = fromMsg(msg); return QMetaObject::invokeMethod(mainWindow_, [this, quote] { @@ -1678,11 +1781,17 @@ bool QtGuiAdapter::processQuote(const MatchingMessage_Quote& msg) }); } -bool QtGuiAdapter::processOrder(const MatchingMessage_Order& msg) +bool QtGuiAdapter::processMatchedQuote(const SettlementMessage_MatchedQuote& msg) { - const auto& order = fromMsg(msg); - return QMetaObject::invokeMethod(mainWindow_, [this, order] { - mainWindow_->onOrderReceived(order); + return QMetaObject::invokeMethod(mainWindow_, [this, msg] { + mainWindow_->onQuoteMatched(msg.rfq_id(), msg.quote_id()); + }); +} + +bool QtGuiAdapter::processFailedQuote(const SettlementMessage_FailedQuote& msg) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, msg] { + mainWindow_->onQuoteFailed(msg.rfq_id(), msg.quote_id(), msg.info()); }); } @@ -1734,4 +1843,17 @@ bool QtGuiAdapter::sendPooledOrdersUpdate() }); } +bool QtGuiAdapter::processReservedUTXOs(const WalletsMessage_ReservedUTXOs& response) +{ + std::vector utxos; + for (const auto& utxoSer : response.utxos()) { + UTXO utxo; + utxo.unserialize(BinaryData::fromString(utxoSer)); + utxos.push_back(std::move(utxo)); + } + return QMetaObject::invokeMethod(mainWindow_, [this, response, utxos] { + mainWindow_->onReservedUTXOs(response.id(), response.sub_id(), utxos); + }); +} + #include "QtGuiAdapter.moc" diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index c9def073f..182ef7eaf 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -36,6 +36,8 @@ namespace BlockSettle { class OnChainTrackMessage_AuthAddresses; class OnChainTrackMessage_AuthState; class SignerMessage_SignTxResponse; + class WalletsMessage_AuthKey; + class WalletsMessage_ReservedUTXOs; class WalletsMessage_TXDetailsResponse; class WalletsMessage_UtxoListResponse; class WalletsMessage_WalletBalances; @@ -49,9 +51,11 @@ namespace BlockSettle { class BsServerMessage_Orders; class BsServerMessage_StartLoginResult; class MatchingMessage_LoggedIn; - class MatchingMessage_Quote; + class Quote; class MatchingMessage_Order; class MktDataMessage_Prices; + class SettlementMessage_FailedQuote; + class SettlementMessage_MatchedQuote; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; class SettingsMessage_SignerServers; @@ -103,6 +107,8 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu void makeMainWinConnections(); void processWalletLoaded(const bs::sync::WalletInfo &); + bool processWalletData(const uint64_t msgId + , const BlockSettle::Common::WalletsMessage_WalletData&); bool processWalletBalances(const bs::message::Envelope & , const BlockSettle::Common::WalletsMessage_WalletBalances &); bool processTXDetails(uint64_t msgId, const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); @@ -119,18 +125,23 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processStartLogin(const BlockSettle::Terminal::BsServerMessage_StartLoginResult&); bool processLogin(const BlockSettle::Terminal::BsServerMessage_LoginResult&); + bool processSettlement(const bs::message::Envelope&); bool processMatching(const bs::message::Envelope&); bool processMktData(const bs::message::Envelope&); + bool processSecurity(const std::string&, int); bool processMdUpdate(const BlockSettle::Terminal::MktDataMessage_Prices &); bool processAuthWallet(const BlockSettle::Common::WalletsMessage_WalletData&); bool processAuthState(const BlockSettle::Common::OnChainTrackMessage_AuthState&); bool processSubmittedAuthAddrs(const BlockSettle::Terminal::AssetsMessage_SubmittedAuthAddresses&); bool processBalance(const BlockSettle::Terminal::AssetsMessage_Balance&); bool processVerifiedAuthAddrs(const BlockSettle::Common::OnChainTrackMessage_AuthAddresses&); - bool processQuote(const BlockSettle::Terminal::MatchingMessage_Quote&); - bool processOrder(const BlockSettle::Terminal::MatchingMessage_Order&); + bool processAuthKey(const BlockSettle::Common::WalletsMessage_AuthKey&); + bool processQuote(const BlockSettle::Terminal::Quote&); + bool processMatchedQuote(const BlockSettle::Terminal::SettlementMessage_MatchedQuote&); + bool processFailedQuote(const BlockSettle::Terminal::SettlementMessage_FailedQuote&); bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); bool sendPooledOrdersUpdate(); + bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); private slots: void onGetSettings(const std::vector&); @@ -148,6 +159,7 @@ private slots: void onSetSigner(int); void onNeedHDWalletDetails(const std::string &walletId); void onNeedWalletBalances(const std::string &walletId); + void onNeedWalletData(const std::string& walletId); void onNeedExtAddresses(const std::string &walletId); void onNeedIntAddresses(const std::string &walletId); void onNeedUsedAddresses(const std::string &walletId); @@ -179,15 +191,20 @@ private slots: void onNeedMdConnection(ApplicationSettings::EnvConfiguration); void onNeedNewAuthAddress(); void onNeedSubmitAuthAddress(const bs::Address&); - void onNeedSubmitRFQ(const bs::network::RFQ&); + void onNeedSubmitRFQ(const bs::network::RFQ&, const std::string& reserveId = {}); void onNeedAcceptRFQ(const std::string& id, const bs::network::Quote&); void onNeedCancelRFQ(const std::string& id); + void onNeedAuthKey(const bs::Address&); + void onNeedReserveUTXOs(const std::string& reserveId, const std::string& subId + , uint64_t amount, bool partial = false, const std::vector& utxos = {}); + void onNeedUnreserveUTXOs(const std::string& reserveId, const std::string& subId); private: std::shared_ptr logger_; std::shared_ptr userSettings_, userWallets_; std::shared_ptr userBlockchain_, userSigner_; - std::shared_ptr userBS_, userMatch_, userMD_; + std::shared_ptr userBS_, userMatch_, userSettl_; + std::shared_ptr userMD_; std::shared_ptr userTrk_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; @@ -200,6 +217,7 @@ private slots: std::string signerDetails_; bool walletsReady_{ false }; + std::map walletGetMap_; std::unordered_map hdWallets_; std::set newZCs_; diff --git a/common b/common index 58b4e99c2..e6fc679b8 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 58b4e99c2a05bab83e1d0204e6b464b70ab8e680 +Subproject commit e6fc679b89bd52f19aa4c45086f1f684398c0f0b From c1a669e7261dbb0633715b8521bdf43acd928ffd Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 2 Nov 2020 18:15:12 +0200 Subject: [PATCH 024/146] Properly display futures in MD view --- BlockSettleUILib/Trading/MarketDataModel.cpp | 23 +++++++++--- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 38 +++++++++++++------- BlockSettleUILib/Trading/RFQTicketXBT.h | 3 +- BlockSettleUILib/UiUtils.cpp | 1 + common | 2 +- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index 6f4477fe4..c485c68a6 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -9,12 +9,13 @@ */ #include "MarketDataModel.h" -#include "CommonTypes.h" -#include "Colors.h" -#include +#include "Colors.h" +#include "FuturesDefinitions.h" #include "UiUtils.h" +#include + MarketDataModel::MarketDataModel(const QStringList &showSettings, QObject* parent) : QStandardItemModel(parent) { @@ -165,7 +166,21 @@ void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType, const QStr } PriceMap fieldsMap; - FieldsToMap(assetType, mdFields, fieldsMap); + + if (assetType == bs::network::Asset::Futures) { + const auto& definition = bs::network::getFutureDefinition(security.toStdString()); + + if (!definition.isValid()) { + return; + } + + FieldsToMap(definition.displayAssetType, mdFields, fieldsMap); + + } else { + FieldsToMap(assetType, mdFields, fieldsMap); + } + + auto groupItem = getGroup(assetType); auto childRow = groupItem->findRowWithText(security); const auto timeNow = QDateTime::currentDateTime(); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 21754d94f..4bd99026c 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -11,11 +11,6 @@ #include "RFQTicketXBT.h" #include "ui_RFQTicketXBT.h" -#include -#include -#include -#include -#include #include "AssetManager.h" #include "AuthAddressManager.h" #include "BSErrorCodeStrings.h" @@ -25,22 +20,29 @@ #include "CoinSelection.h" #include "CurrencyPair.h" #include "EncryptionUtils.h" +#include "FuturesDefinitions.h" #include "FXAmountValidator.h" #include "QuoteProvider.h" #include "SelectedTransactionInputs.h" #include "SignContainer.h" +#include "TradeSettings.h" #include "TradesUtils.h" #include "TxClasses.h" #include "UiUtils.h" +#include "UtxoReservation.h" +#include "UtxoReservationManager.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" #include "XbtAmountValidator.h" -#include "UtxoReservationManager.h" -#include "UtxoReservation.h" -#include "TradeSettings.h" -#include +#include +#include +#include +#include +#include + +#include namespace { static const QString kEmptyInformationalLabelText = QString::fromStdString("--"); @@ -486,10 +488,20 @@ void RFQTicketXBT::SetCurrencyPair(const QString& currencyPair) ui_->labelSecurityId->setText(currencyPair); - CurrencyPair cp(currencyPair.toStdString()); + const std::string& currencyString = currencyPair.toStdString(); + + const auto futureDefinition = bs::network::getFutureDefinition(currencyString); + if (futureDefinition.isValid()) { + CurrencyPair cp(futureDefinition.ccyPair); - currentProduct_ = QString::fromStdString(cp.NumCurrency()); - contraProduct_ = QString::fromStdString(cp.DenomCurrency()); + currentProduct_ = QString::fromStdString(cp.NumCurrency()); + contraProduct_ = QString::fromStdString(cp.DenomCurrency()); + } else { + CurrencyPair cp(currencyString); + + currentProduct_ = QString::fromStdString(cp.NumCurrency()); + contraProduct_ = QString::fromStdString(cp.DenomCurrency()); + } ui_->pushButtonNumCcy->setText(currentProduct_); ui_->pushButtonNumCcy->setChecked(true); @@ -1219,6 +1231,8 @@ void RFQTicketXBT::initProductGroupMap() , ProductGroupType::XBTGroupType); groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::SpotFX) , ProductGroupType::FXGroupType); + groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::Futures) + , ProductGroupType::FuturesGroupType); } RFQTicketXBT::ProductGroupType RFQTicketXBT::getProductGroupType(const QString& productGroup) diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h index 13cb8de66..04086a345 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ b/BlockSettleUILib/Trading/RFQTicketXBT.h @@ -159,7 +159,8 @@ private slots: GroupNotSelected, FXGroupType, XBTGroupType, - CCGroupType + CCGroupType, + FuturesGroupType }; struct BalanceInfoContainer diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index cfe95a3a3..4b0dcd8e7 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -437,6 +437,7 @@ QString UiUtils::displayPriceForAssetType(double price, bs::network::Asset::Type case bs::network::Asset::SpotFX: return UiUtils::displayPriceFX(price); case bs::network::Asset::SpotXBT: + case bs::network::Asset::Futures: return UiUtils::displayPriceXBT(price); case bs::network::Asset::PrivateMarket: return UiUtils::displayPriceCC(price); diff --git a/common b/common index 772fedb12..42e2435d7 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 772fedb122a30c9f0fc6c897c8bd6c490a539097 +Subproject commit 42e2435d7d93bf9adb5fcceb41928efe01098817 From 1c3527bef629e88847832e9ea1471c9ec6969cdc Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 2 Nov 2020 18:52:15 +0200 Subject: [PATCH 025/146] Select futures group in UI for RFQ --- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 4 ++++ BlockSettleUILib/Trading/WalletShieldBase.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 4bd99026c..26df990d4 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -702,6 +702,10 @@ bs::Address RFQTicketXBT::recvXbtAddressIfSet() const bool RFQTicketXBT::checkBalance(double qty) const { + if (currentGroupType_ == ProductGroupType::FuturesGroupType) { + return true; + } + const auto balance = getBalanceInfo(); if (getSelectedSide() == bs::network::Side::Buy) { if (currentGroupType_ == ProductGroupType::CCGroupType) { diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index d5dd7cfc2..6b324cc2e 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -155,6 +155,9 @@ WalletShieldBase::ProductType WalletShieldBase::getProductGroup(const QString &p else if (productGroup == QLatin1String("Spot FX")) { return ProductType::SpotFX; } + else if (productGroup == QLatin1String("Futures")) { + return ProductType::Futures; + } #ifndef QT_NO_DEBUG // You need to add logic for new Product group type Q_ASSERT(false); From c948f3cab9a51f48f6197474ac2e0c4c02485846 Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 2 Nov 2020 23:18:03 +0200 Subject: [PATCH 026/146] Fix submit RFQ for a future --- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 26df990d4..1ca37644a 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -890,6 +890,9 @@ void RFQTicketXBT::submitButtonClicked() case ProductGroupType::CCGroupType: rfq->assetType = bs::network::Asset::PrivateMarket; break; + case ProductGroupType::FuturesGroupType: + rfq->assetType = bs::network::Asset::Futures; + break; } const auto &rfqId = CryptoPRNG::generateRandom(8).toHexStr(); From 9650714e9eeb4330bee5735e038f4b3ce4cc89c4 Mon Sep 17 00:00:00 2001 From: Ation Date: Tue, 3 Nov 2020 19:10:07 +0200 Subject: [PATCH 027/146] Submit quote on future step1 --- .../Trading/QuoteRequestsModel.cpp | 58 ++++++++++++------- BlockSettleUILib/Trading/QuoteRequestsModel.h | 9 ++- .../Trading/QuoteRequestsWidget.cpp | 3 +- BlockSettleUILib/Trading/RFQDealerReply.cpp | 52 ++++++++++++----- .../Trading/RequestingQuoteWidget.cpp | 3 +- BlockSettleUILib/UiUtils.cpp | 30 +++++++++- BlockSettleUILib/UiUtils.h | 2 +- common | 2 +- 8 files changed, 112 insertions(+), 47 deletions(-) diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index ade65320a..69c189883 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -18,6 +18,7 @@ #include "CommonTypes.h" #include "CurrencyPair.h" #include "DealerCCSettlementContainer.h" +#include "FuturesDefinitions.h" #include "QuoteRequestsWidget.h" #include "SettlementContainer.h" #include "UiUtils.h" @@ -69,7 +70,7 @@ QVariant QuoteRequestsModel::data(const QModelIndex &index, int role) const case Qt::DisplayRole : { switch(static_cast(index.column())) { case Column::SecurityID : { - return r->security_; + return r->securityDefinition_; } case Column::Product : { @@ -174,7 +175,7 @@ QVariant QuoteRequestsModel::data(const QModelIndex &index, int role) const case Qt::TextColorRole: { if (secStatsCollector_ && index.column() < static_cast(Column::Status)) { - return secStatsCollector_->getColorFor(r->security_.toStdString()); + return secStatsCollector_->getColorFor(r->securityDefinition_.toStdString()); } else { return QVariant(); @@ -870,8 +871,20 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti auto itQRN = notifications_.find(qrn.quoteRequestId); if (itQRN == notifications_.end()) { - const auto assetType = assetManager_->GetAssetTypeForSecurity(qrn.security); - const CurrencyPair cp(qrn.security); + + std::string quoteCcy; + + if (qrn.assetType == bs::network::Asset::Futures) { + const auto definition = bs::network::getFutureDefinition(qrn.security); + if (!definition.isValid()) { + return; + } + quoteCcy = definition.ccyPair; + } else { + quoteCcy = qrn.security; + } + + const CurrencyPair cp(quoteCcy); const bool isBid = (qrn.side == bs::network::Side::Buy) ^ (cp.NumCurrency() == qrn.product); const double indicPrice = isBid ? mdPrices_[qrn.security][Role::BidPrice] : mdPrices_[qrn.security][Role::OfferPrice]; @@ -881,13 +894,14 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti static_cast(group->rfqs_.size())); group->rfqs_.push_back(std::unique_ptr(new RFQ(QString::fromStdString(qrn.security), + QString::fromStdString(quoteCcy), QString::fromStdString(qrn.product), tr(bs::network::Side::toString(qrn.side)), QString(), (qrn.assetType == bs::network::Asset::Type::PrivateMarket) ? UiUtils::displayCCAmount(qrn.quantity) : UiUtils::displayQty(qrn.quantity, qrn.product), QString(), - (!qFuzzyIsNull(indicPrice) ? UiUtils::displayPriceForAssetType(indicPrice, assetType) + (!qFuzzyIsNull(indicPrice) ? UiUtils::displayPriceForAssetType(indicPrice, qrn.assetType) : QString()), QString(), { @@ -898,7 +912,7 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti }, indicPrice, 0.0, 0.0, qrn.side, - assetType, + qrn.assetType, qrn.quoteRequestId))); group->rfqs_.back()->idx_.parent_ = &group->idx_; @@ -959,7 +973,6 @@ void QuoteRequestsModel::addSettlementContainer(const std::shared_ptr(container); - const auto assetType = assetManager_->GetAssetTypeForSecurity(container->security()); const auto amountStr = (container->assetType() == bs::network::Asset::Type::PrivateMarket) ? UiUtils::displayCCAmount(container->quantity()) : UiUtils::displayQty(container->quantity(), container->product()); @@ -968,14 +981,16 @@ void QuoteRequestsModel::addSettlementContainer(const std::shared_ptr(market->groups_.size() + market->settl_.rfqs_.size()), static_cast(market->groups_.size() + market->settl_.rfqs_.size())); + // settlement containers not ment to be used with futures market->settl_.rfqs_.push_back(std::unique_ptr(new RFQ( + QString::fromStdString(container->security()), QString::fromStdString(container->security()), QString::fromStdString(container->product()), tr(bs::network::Side::toString(container->side())), QString(), amountStr, QString(), - UiUtils::displayPriceForAssetType(container->price(), assetType), + UiUtils::displayPriceForAssetType(container->price(), container->assetType()), QString(), { QString(), @@ -983,7 +998,7 @@ void QuoteRequestsModel::addSettlementContainer(const std::shared_ptrprice(), 0.0, 0.0, container->side(), - assetType, + container->assetType(), container->id()))); market->settl_.rfqs_.back()->idx_.parent_ = &market->idx_; @@ -1268,10 +1283,13 @@ void QuoteRequestsModel::setStatus(const std::string &reqId, bs::network::QuoteR void QuoteRequestsModel::updatePrices(const QString &security, const bs::network::MDField &pxBid, const bs::network::MDField &pxOffer, std::vector> *idxs) { - forEachSecurity(security, [security, pxBid, pxOffer, this, idxs](Group *grp, int index) { - const CurrencyPair cp(security.toStdString()); - const bool isBuy = (grp->rfqs_[static_cast(index)]->side_ == bs::network::Side::Buy) - ^ (cp.NumCurrency() == grp->rfqs_[static_cast(index)]->product_.toStdString()); + forEachSecurity(security, [pxBid, pxOffer, this, idxs](Group *grp, int index) { + + const auto& rfqOnIndex = grp->rfqs_[static_cast(index)]; + + const CurrencyPair cp(rfqOnIndex->securityCcyPair_.toStdString()); + const bool isBuy = (rfqOnIndex->side_ == bs::network::Side::Buy) + ^ (cp.NumCurrency() == rfqOnIndex->product_.toStdString()); double indicPrice = 0; if (isBuy && (pxBid.type != bs::network::MDField::Unknown)) { @@ -1281,23 +1299,23 @@ void QuoteRequestsModel::updatePrices(const QString &security, const bs::network } if (indicPrice > 0) { - const auto prevPrice = grp->rfqs_[static_cast(index)]->indicativePx_; - const auto assetType = grp->rfqs_[static_cast(index)]->assetType_; - grp->rfqs_[static_cast(index)]->indicativePxString_ = + const auto prevPrice = rfqOnIndex->indicativePx_; + const auto assetType = rfqOnIndex->assetType_; + rfqOnIndex->indicativePxString_ = UiUtils::displayPriceForAssetType(indicPrice, assetType); - grp->rfqs_[static_cast(index)]->indicativePx_ = indicPrice; + rfqOnIndex->indicativePx_ = indicPrice; if (!qFuzzyIsNull(prevPrice)) { if (indicPrice > prevPrice) { - grp->rfqs_[static_cast(index)]->indicativePxBrush_ = c_greenColor; + rfqOnIndex->indicativePxBrush_ = c_greenColor; } else if (indicPrice < prevPrice) { - grp->rfqs_[static_cast(index)]->indicativePxBrush_ = c_redColor; + rfqOnIndex->indicativePxBrush_ = c_redColor; } } const QModelIndex idx = createIndex(index, static_cast(Column::IndicPx), - &grp->rfqs_[static_cast(index)]->idx_); + &(rfqOnIndex->idx_)); if (!idxs) { emit dataChanged(idx, idx); diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h index 4317ada35..8e4f6bac0 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.h @@ -201,7 +201,8 @@ private slots: }; struct RFQ { - QString security_; + QString securityDefinition_; + QString securityCcyPair_; QString product_; QString sideString_; QString party_; @@ -230,7 +231,8 @@ private slots: , visible_(false) {} - RFQ(const QString &security, + RFQ(const QString &securityDefinition, + const QString &securityCcyPair, const QString &product, const QString &sideString, const QString &party, @@ -245,7 +247,8 @@ private slots: bs::network::Side::Type side, bs::network::Asset::Type assetType, const std::string &reqId) - : security_(security) + : securityDefinition_(securityDefinition) + , securityCcyPair_(securityCcyPair) , product_(product) , sideString_(sideString) , party_(party) diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp index 0650a2881..bf8bed2d1 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp @@ -152,8 +152,7 @@ void QuoteRequestsWidget::onQuoteReqNotifSelected(const QModelIndex& index) bool isBuy = (qrn.side == bs::network::Side::Buy) ^ (cp.NumCurrency() == qrn.product); const double quotedPx = sortModel_->data(quoteIndex, static_cast(QuoteRequestsModel::Role::QuotedPrice)).toDouble(); - auto assetType = assetManager_->GetAssetTypeForSecurity(qrn.security); - const auto pip = qFuzzyCompare(bestQPx, quotedPx) ? 0.0 : std::pow(10, -UiUtils::GetPricePrecisionForAssetType(assetType)); + const auto pip = qFuzzyCompare(bestQPx, quotedPx) ? 0.0 : std::pow(10, -UiUtils::GetPricePrecisionForAssetType(qrn.assetType)); if (isBuy) { bidPx = bestQPx + pip; } diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index 7d8661e02..2d81fb64d 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -29,6 +29,7 @@ #include "CurrencyPair.h" #include "CustomControls/CustomComboBox.h" #include "FastLock.h" +#include "FuturesDefinitions.h" #include "QuoteProvider.h" #include "SelectedTransactionInputs.h" #include "SignContainer.h" @@ -184,7 +185,8 @@ void RFQDealerReply::updateRespQuantity() void RFQDealerReply::reset() { payInRecipId_ = UINT_MAX; - if (currentQRN_.empty()) { + if (currentQRN_.empty() + || (currentQRN_.assetType == bs::network::Asset::Type::Undefined)) { ui_->labelProductGroup->clear(); ui_->labelSecurity->clear(); ui_->labelReqProduct->clear(); @@ -199,15 +201,20 @@ void RFQDealerReply::reset() setBalanceOk(true); } else { - CurrencyPair cp(currentQRN_.security); - baseProduct_ = cp.NumCurrency(); - product_ = cp.ContraCurrency(currentQRN_.product); + std::string ccyString; - const auto assetType = assetManager_->GetAssetTypeForSecurity(currentQRN_.security); - if (assetType == bs::network::Asset::Type::Undefined) { - logger_->error("[RFQDealerReply::reset] could not get asset type for {}", currentQRN_.security); + if (currentQRN_.assetType == bs::network::Asset::Type::Futures) { + auto definition = bs::network::getFutureDefinition(currentQRN_.security); + + ccyString = definition.ccyPair; + } else { + ccyString = currentQRN_.security; } - const auto priceDecimals = UiUtils::GetPricePrecisionForAssetType(assetType); + + CurrencyPair cp(ccyString); + baseProduct_ = cp.NumCurrency(); + product_ = cp.ContraCurrency(currentQRN_.product); + const auto priceDecimals = UiUtils::GetPricePrecisionForAssetType(currentQRN_.assetType); ui_->spinBoxBidPx->setDecimals(priceDecimals); ui_->spinBoxOfferPx->setDecimals(priceDecimals); ui_->spinBoxBidPx->setSingleStep(std::pow(10, -priceDecimals)); @@ -477,6 +484,10 @@ bool RFQDealerReply::checkBalance() const return false; } + if (currentQRN_.assetType == bs::network::Asset::Futures) { + return true; + } + // #UTXO_MANAGER: Balance check should account for fee? if ((currentQRN_.side == bs::network::Side::Buy) != (product_ == baseProduct_)) { @@ -631,7 +642,17 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d auto replyData = std::make_shared(); replyData->qn = bs::network::QuoteNotification(qrn, authKey_, price, ""); - if (qrn.assetType != bs::network::Asset::SpotFX) { + auto quoteAssetType = qrn.assetType; + if (quoteAssetType == bs::network::Asset::Futures) { + auto definition = bs::network::getFutureDefinition(qrn.security); + if (!definition.isValid()) { + return; + } + + quoteAssetType = definition.settlementAssetType; + } + + if (quoteAssetType != bs::network::Asset::SpotFX) { replyData->xbtWallet = getSelectedXbtWallet(replyType); if (!replyData->xbtWallet) { SPDLOG_LOGGER_ERROR(logger_, "can't submit CC/XBT reply without XBT wallet"); @@ -644,7 +665,7 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d } } - if (qrn.assetType == bs::network::Asset::SpotXBT) { + if (quoteAssetType == bs::network::Asset::SpotXBT) { replyData->authAddr = selectedAuthAddress(replyType); if (!replyData->authAddr.isValid()) { SPDLOG_LOGGER_ERROR(logger_, "can't submit XBT without valid auth address"); @@ -668,7 +689,7 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d activeQuoteSubmits_.insert(replyData->qn.quoteRequestId); updateSubmitButton(); - switch (qrn.assetType) { + switch (quoteAssetType) { case bs::network::Asset::SpotFX: { submit(price, replyData); break; @@ -723,12 +744,12 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d //group 1 for cc, group 2 for xbt unsigned spendGroup = isSpendCC ? RECIP_GROUP_SPEND_1 : RECIP_GROUP_SPEND_2; unsigned changGroup = isSpendCC ? RECIP_GROUP_CHANG_1 : RECIP_GROUP_CHANG_2; - + std::map>> recipientMap; const auto recipient = bs::Address::fromAddressString(qrn.requestorRecvAddress).getRecipient(bs::XBTAmount{ spendVal }); std::vector> recVec({recipient}); recipientMap.emplace(spendGroup, std::move(recVec)); - + Codec_SignerState::SignerState state; state.ParseFromString(BinaryData::CreateFromHex(qrn.requestorAuthPublicKey).toBinStr()); @@ -988,9 +1009,8 @@ void RFQDealerReply::onBestQuotePrice(const QString reqId, double price, bool ow auto priceWidget = getActivePriceWidget(); if (priceWidget && !own) { double improvedPrice = price; - const auto assetType = assetManager_->GetAssetTypeForSecurity(currentQRN_.security); - if (assetType != bs::network::Asset::Type::Undefined) { - const auto pip = std::pow(10, -UiUtils::GetPricePrecisionForAssetType(assetType)); + if (currentQRN_.assetType != bs::network::Asset::Type::Undefined) { + const auto pip = std::pow(10, -UiUtils::GetPricePrecisionForAssetType(currentQRN_.assetType)); if (priceWidget == ui_->spinBoxBidPx) { improvedPrice += pip; } else { diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index d45d896d6..bf0644c92 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -152,8 +152,7 @@ bool RequestingQuoteWidget::onQuoteReceived(const bs::network::Quote& quote) timeoutReply_ = quote.expirationTime.addMSecs(quote.timeSkewMs); - const auto assetType = assetManager_->GetAssetTypeForSecurity(quote.security); - ui_->labelQuoteValue->setText(UiUtils::displayPriceForAssetType(quote.price, assetType)); + ui_->labelQuoteValue->setText(UiUtils::displayPriceForAssetType(quote.price, quote.assetType)); ui_->labelQuoteValue->show(); if (quote.assetType == bs::network::Asset::SpotFX) { diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index 4b0dcd8e7..a0ba6c7bb 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -42,7 +42,8 @@ template bool contains(const std::vector& v, const T& value) return std::find(v.begin(), v.end(), value) != v.end(); } -const QLatin1String UiUtils::XbtCurrency = QLatin1String("XBT"); +static const std::string XbtCurrencyString = "XBT"; +const QString UiUtils::XbtCurrency = QString::fromStdString(XbtCurrencyString); void UiUtils::SetupLocale() { @@ -419,12 +420,14 @@ double UiUtils::truncatePriceForAsset(double price, bs::network::Asset::Type at) multiplier = 10000; break; case bs::network::Asset::SpotXBT: + case bs::network::Asset::Futures: multiplier = 100; break; case bs::network::Asset::PrivateMarket: multiplier = 1000000; break; default: + assert(false); return 0; } @@ -441,6 +444,9 @@ QString UiUtils::displayPriceForAssetType(double price, bs::network::Asset::Type return UiUtils::displayPriceXBT(price); case bs::network::Asset::PrivateMarket: return UiUtils::displayPriceCC(price); + default: + assert(false); + break; } return QString(); @@ -467,9 +473,13 @@ int UiUtils::GetPricePrecisionForAssetType(const bs::network::Asset::Type& asset case bs::network::Asset::SpotFX: return GetPricePrecisionFX(); case bs::network::Asset::SpotXBT: + case bs::network::Asset::Futures: return GetPricePrecisionXBT(); case bs::network::Asset::PrivateMarket: return GetPricePrecisionCC(); + default: + assert(false); + break; } // Allow entering floating point numbers if the asset type was detected as Undefined @@ -497,9 +507,11 @@ static void getPrecsFor(const std::string &security, const std::string &product, valuePrec = UiUtils::GetAmountPrecisionFX(); break; case bs::network::Asset::Type::SpotXBT: + case bs::network::Asset::Type::Futures: qtyPrec = UiUtils::GetAmountPrecisionXBT(); valuePrec = UiUtils::GetAmountPrecisionFX(); - if (security.substr(0, security.find('/')) != product) { + + if (product != XbtCurrencyString) { std::swap(qtyPrec, valuePrec); } break; @@ -508,6 +520,9 @@ static void getPrecsFor(const std::string &security, const std::string &product, // special case. display value for XBT with 6 decimals valuePrec = 6; break; + default: + assert(false); + break; } } @@ -691,7 +706,11 @@ ApplicationSettings::Setting UiUtils::limitRfqSetting(bs::network::Asset::Type t case bs::network::Asset::PrivateMarket : return ApplicationSettings::PmRfqLimit; + case bs::network::Asset::Futures : + return ApplicationSettings::FuturesLimit; + default : + assert(false); return ApplicationSettings::FxRfqLimit; } } @@ -705,7 +724,10 @@ ApplicationSettings::Setting UiUtils::limitRfqSetting(const QString &name) } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::PrivateMarket))) { return ApplicationSettings::PmRfqLimit; + } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::Futures))) { + return ApplicationSettings::FuturesLimit; } else { + assert(false); return ApplicationSettings::FxRfqLimit; } } @@ -722,7 +744,11 @@ QString UiUtils::marketNameForLimit(ApplicationSettings::Setting s) case ApplicationSettings::PmRfqLimit : return QObject::tr(bs::network::Asset::toString(bs::network::Asset::PrivateMarket)); + case ApplicationSettings::FuturesLimit : + return QObject::tr(bs::network::Asset::toString(bs::network::Asset::Futures)); + default : + assert(false); return QString(); } } diff --git a/BlockSettleUILib/UiUtils.h b/BlockSettleUILib/UiUtils.h index d5c4a2205..904ce4059 100644 --- a/BlockSettleUILib/UiUtils.h +++ b/BlockSettleUILib/UiUtils.h @@ -163,7 +163,7 @@ namespace UiUtils QString modelPath(const QModelIndex &index, QAbstractItemModel *model); - extern const QLatin1String XbtCurrency; + extern const QString XbtCurrency; double actualXbtPrice(bs::XBTAmount amount, double price); diff --git a/common b/common index 42e2435d7..2c3f4dc8b 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 42e2435d7d93bf9adb5fcceb41928efe01098817 +Subproject commit 2c3f4dc8ba3186261a0ef99babd1f3f61c7e1783 From f7e534f2b51db7b998c9e7a2a3128114e814a897 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 4 Nov 2020 12:03:32 +0200 Subject: [PATCH 028/146] Rename future to subproduct --- BlockSettleUILib/Trading/MarketDataModel.cpp | 11 ++-------- .../Trading/QuoteRequestsModel.cpp | 20 ++---------------- BlockSettleUILib/Trading/QuoteRequestsModel.h | 3 --- BlockSettleUILib/Trading/RFQDealerReply.cpp | 21 +++---------------- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 18 ++++------------ common | 2 +- 6 files changed, 12 insertions(+), 63 deletions(-) diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index c485c68a6..7c0910747 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -11,7 +11,7 @@ #include "MarketDataModel.h" #include "Colors.h" -#include "FuturesDefinitions.h" +#include "CommonTypes.h" #include "UiUtils.h" #include @@ -168,14 +168,7 @@ void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType, const QStr PriceMap fieldsMap; if (assetType == bs::network::Asset::Futures) { - const auto& definition = bs::network::getFutureDefinition(security.toStdString()); - - if (!definition.isValid()) { - return; - } - - FieldsToMap(definition.displayAssetType, mdFields, fieldsMap); - + FieldsToMap(bs::network::Asset::SpotXBT, mdFields, fieldsMap); } else { FieldsToMap(assetType, mdFields, fieldsMap); } diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index 69c189883..01eba2e7b 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -18,7 +18,6 @@ #include "CommonTypes.h" #include "CurrencyPair.h" #include "DealerCCSettlementContainer.h" -#include "FuturesDefinitions.h" #include "QuoteRequestsWidget.h" #include "SettlementContainer.h" #include "UiUtils.h" @@ -871,20 +870,7 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti auto itQRN = notifications_.find(qrn.quoteRequestId); if (itQRN == notifications_.end()) { - - std::string quoteCcy; - - if (qrn.assetType == bs::network::Asset::Futures) { - const auto definition = bs::network::getFutureDefinition(qrn.security); - if (!definition.isValid()) { - return; - } - quoteCcy = definition.ccyPair; - } else { - quoteCcy = qrn.security; - } - - const CurrencyPair cp(quoteCcy); + const CurrencyPair cp(qrn.security); const bool isBid = (qrn.side == bs::network::Side::Buy) ^ (cp.NumCurrency() == qrn.product); const double indicPrice = isBid ? mdPrices_[qrn.security][Role::BidPrice] : mdPrices_[qrn.security][Role::OfferPrice]; @@ -894,7 +880,6 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti static_cast(group->rfqs_.size())); group->rfqs_.push_back(std::unique_ptr(new RFQ(QString::fromStdString(qrn.security), - QString::fromStdString(quoteCcy), QString::fromStdString(qrn.product), tr(bs::network::Side::toString(qrn.side)), QString(), @@ -983,7 +968,6 @@ void QuoteRequestsModel::addSettlementContainer(const std::shared_ptrsettl_.rfqs_.push_back(std::unique_ptr(new RFQ( - QString::fromStdString(container->security()), QString::fromStdString(container->security()), QString::fromStdString(container->product()), tr(bs::network::Side::toString(container->side())), @@ -1287,7 +1271,7 @@ void QuoteRequestsModel::updatePrices(const QString &security, const bs::network const auto& rfqOnIndex = grp->rfqs_[static_cast(index)]; - const CurrencyPair cp(rfqOnIndex->securityCcyPair_.toStdString()); + const CurrencyPair cp(rfqOnIndex->securityDefinition_.toStdString()); const bool isBuy = (rfqOnIndex->side_ == bs::network::Side::Buy) ^ (cp.NumCurrency() == rfqOnIndex->product_.toStdString()); double indicPrice = 0; diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h index 8e4f6bac0..9afa94340 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.h @@ -202,7 +202,6 @@ private slots: struct RFQ { QString securityDefinition_; - QString securityCcyPair_; QString product_; QString sideString_; QString party_; @@ -232,7 +231,6 @@ private slots: {} RFQ(const QString &securityDefinition, - const QString &securityCcyPair, const QString &product, const QString &sideString, const QString &party, @@ -248,7 +246,6 @@ private slots: bs::network::Asset::Type assetType, const std::string &reqId) : securityDefinition_(securityDefinition) - , securityCcyPair_(securityCcyPair) , product_(product) , sideString_(sideString) , party_(party) diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index 2d81fb64d..e64ea86bb 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -26,10 +26,10 @@ #include "BSMessageBox.h" #include "CoinControlDialog.h" #include "CoinControlWidget.h" +#include "CommonTypes.h" #include "CurrencyPair.h" #include "CustomControls/CustomComboBox.h" #include "FastLock.h" -#include "FuturesDefinitions.h" #include "QuoteProvider.h" #include "SelectedTransactionInputs.h" #include "SignContainer.h" @@ -201,17 +201,7 @@ void RFQDealerReply::reset() setBalanceOk(true); } else { - std::string ccyString; - - if (currentQRN_.assetType == bs::network::Asset::Type::Futures) { - auto definition = bs::network::getFutureDefinition(currentQRN_.security); - - ccyString = definition.ccyPair; - } else { - ccyString = currentQRN_.security; - } - - CurrencyPair cp(ccyString); + CurrencyPair cp(currentQRN_.security); baseProduct_ = cp.NumCurrency(); product_ = cp.ContraCurrency(currentQRN_.product); const auto priceDecimals = UiUtils::GetPricePrecisionForAssetType(currentQRN_.assetType); @@ -644,12 +634,7 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d auto quoteAssetType = qrn.assetType; if (quoteAssetType == bs::network::Asset::Futures) { - auto definition = bs::network::getFutureDefinition(qrn.security); - if (!definition.isValid()) { - return; - } - - quoteAssetType = definition.settlementAssetType; + quoteAssetType = bs::network::Asset::SpotFX; } if (quoteAssetType != bs::network::Asset::SpotFX) { diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 1ca37644a..8e7a84433 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -18,9 +18,9 @@ #include "CCAmountValidator.h" #include "CoinControlDialog.h" #include "CoinSelection.h" +#include "CommonTypes.h" #include "CurrencyPair.h" #include "EncryptionUtils.h" -#include "FuturesDefinitions.h" #include "FXAmountValidator.h" #include "QuoteProvider.h" #include "SelectedTransactionInputs.h" @@ -488,20 +488,10 @@ void RFQTicketXBT::SetCurrencyPair(const QString& currencyPair) ui_->labelSecurityId->setText(currencyPair); - const std::string& currencyString = currencyPair.toStdString(); + CurrencyPair cp(currencyPair.toStdString()); - const auto futureDefinition = bs::network::getFutureDefinition(currencyString); - if (futureDefinition.isValid()) { - CurrencyPair cp(futureDefinition.ccyPair); - - currentProduct_ = QString::fromStdString(cp.NumCurrency()); - contraProduct_ = QString::fromStdString(cp.DenomCurrency()); - } else { - CurrencyPair cp(currencyString); - - currentProduct_ = QString::fromStdString(cp.NumCurrency()); - contraProduct_ = QString::fromStdString(cp.DenomCurrency()); - } + currentProduct_ = QString::fromStdString(cp.NumCurrency()); + contraProduct_ = QString::fromStdString(cp.DenomCurrency()); ui_->pushButtonNumCcy->setText(currentProduct_); ui_->pushButtonNumCcy->setChecked(true); diff --git a/common b/common index 2c3f4dc8b..4db4dad1d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 2c3f4dc8ba3186261a0ef99babd1f3f61c7e1783 +Subproject commit 4db4dad1dc0412d08cb284fc18af64b2722b40f9 From fc38810cf433ecd11bf9c3b69d77ffb9b1debcdf Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 4 Nov 2020 12:20:51 +0200 Subject: [PATCH 029/146] Minor fixes for price updates --- BlockSettleUILib/ChartWidget.cpp | 9 +++++++-- BlockSettleUILib/Trading/QuoteRequestsModel.cpp | 4 ++++ BlockSettleUILib/UserScript.cpp | 7 ++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/BlockSettleUILib/ChartWidget.cpp b/BlockSettleUILib/ChartWidget.cpp index 084ce91cd..bea9c71c4 100644 --- a/BlockSettleUILib/ChartWidget.cpp +++ b/BlockSettleUILib/ChartWidget.cpp @@ -189,6 +189,11 @@ void ChartWidget::SendEoDRequest() void ChartWidget::OnMdUpdated(bs::network::Asset::Type assetType, const QString& security, bs::network::MDFields mdFields) { + if (assetType == bs::network::Asset::Type::Futures) { + // ignore futures prices updates + return; + } + if ((assetType == bs::network::Asset::Undefined) && security.isEmpty()) // Celer disconnected { isProductListInitialized_ = false; @@ -386,7 +391,7 @@ void ChartWidget::ProcessOhlcHistoryResponse(const std::string& data) maxTimestamp = qMax(maxTimestamp, static_cast(candle.timestamp())); bool isLast = (i == 0); - if (candle.timestamp() >= lastCandle_.timestamp() + if (candle.timestamp() >= lastCandle_.timestamp() || lastCandle_.timestamp() - candle.timestamp() < IntervalWidth( interval, 1, QDateTime::fromMSecsSinceEpoch(candle.timestamp(), Qt::TimeSpec::UTC))) { if (lastCandle_.timestamp() != 0) { logger_->error("Invalid distance between candles from mdhs. The last timestamp: {} new timestamp: {}", @@ -964,7 +969,7 @@ void ChartWidget::OnResetBtnClick() void ChartWidget::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); - QMetaObject::invokeMethod(this, &ChartWidget::UpdatePrintFlag, Qt::QueuedConnection);//UpdatePrintFlag should be called after chart have resized, so we put this method to event loop's queue + QMetaObject::invokeMethod(this, &ChartWidget::UpdatePrintFlag, Qt::QueuedConnection);//UpdatePrintFlag should be called after chart have resized, so we put this method to event loop's queue } quint64 ChartWidget::GetCandleTimestamp(const uint64_t& timestamp, const Interval& interval) const diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index 01eba2e7b..0f89d7cdd 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -1355,6 +1355,10 @@ void QuoteRequestsModel::clearVisibleFlag(Group *g) void QuoteRequestsModel::onSecurityMDUpdated(const QString &security, const bs::network::MDFields &mdFields) { + // NOTE: + // there is duplicate for security name for future and XBT product XBT/EUR + // but that is fine for price updates, since it goes through all securities in all groups + const auto pxBid = bs::network::MDField::get(mdFields, bs::network::MDField::PriceBid); const auto pxOffer = bs::network::MDField::get(mdFields, bs::network::MDField::PriceOffer); if (pxBid.type != bs::network::MDField::Unknown) { diff --git a/BlockSettleUILib/UserScript.cpp b/BlockSettleUILib/UserScript.cpp index 45302925e..beaaf408d 100644 --- a/BlockSettleUILib/UserScript.cpp +++ b/BlockSettleUILib/UserScript.cpp @@ -164,9 +164,14 @@ double MarketData::ask(const QString &sec) const } } -void MarketData::onMDUpdated(bs::network::Asset::Type, const QString &security, +void MarketData::onMDUpdated(bs::network::Asset::Type assetType, const QString &security, bs::network::MDFields data) { + if (assetType == bs::network::Asset::Type::Futures) { + // ignore futures prices updates + return; + } + for (const auto &field : data) { data_[security][field.type] = field.value; } From 4b3ae273759d400e15008121b5dc62e41fd0faec Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 4 Nov 2020 12:26:42 +0200 Subject: [PATCH 030/146] Minor UI fixes --- BlockSettleUILib/Trading/RFQDealerReply.cpp | 3 ++- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index e64ea86bb..aeee69e44 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -290,7 +290,8 @@ void RFQDealerReply::updateQuoteReqNotification(const bs::network::QuoteReqNotif } if (qrn.assetType == bs::network::Asset::SpotFX || - qrn.assetType == bs::network::Asset::Undefined) { + qrn.assetType == bs::network::Asset::Undefined || + qrn.assetType == bs::network::Asset::Futures) { ui_->groupBoxSettlementInputs->hide(); } else { ui_->groupBoxSettlementInputs->show(); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 8e7a84433..559d0fda2 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -467,12 +467,16 @@ void RFQTicketXBT::SetProductGroup(const QString& productGroup) ui_->lineBeforeBalance->setVisible(true); ui_->balanceLayout->setVisible(true); - if (currentGroupType_ != ProductGroupType::FXGroupType) { - ui_->groupBoxSettlementInputs->setVisible(true); - - ui_->authAddressLayout->setVisible(currentGroupType_ == ProductGroupType::XBTGroupType); - } else { + switch (currentGroupType_) { + case ProductGroupType::FXGroupType: + case ProductGroupType::FuturesGroupType: ui_->groupBoxSettlementInputs->setVisible(false); + break; + case ProductGroupType::XBTGroupType: + ui_->authAddressLayout->setVisible(true); + case ProductGroupType::CCGroupType: + ui_->groupBoxSettlementInputs->setVisible(true); + break; } } else { ui_->labelProductGroup->setText(tr("XXX")); From 1cd9ca0f877eefbb1cdc3308bc6f4e9dc019a5c5 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 4 Nov 2020 14:42:31 +0200 Subject: [PATCH 031/146] Few fixes to futures quote processing --- BlockSettleUILib/Trading/RFQDialog.cpp | 9 ++++++--- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 3 ++- common | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 6ccf57617..3b35703b1 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -105,7 +105,8 @@ void RFQDialog::onOrderFilled(const std::string "eId) return; } - if (rfq_.assetType == bs::network::Asset::SpotFX) { + if (rfq_.assetType == bs::network::Asset::SpotFX + || rfq_.assetType == bs::network::Asset::Futures) { ui_->pageRequestingQuote->onOrderFilled(quoteId); } } @@ -116,7 +117,8 @@ void RFQDialog::onOrderFailed(const std::string& quoteId, const std::string& rea return; } - if (rfq_.assetType == bs::network::Asset::SpotFX) { + if (rfq_.assetType == bs::network::Asset::SpotFX + || rfq_.assetType == bs::network::Asset::Futures) { ui_->pageRequestingQuote->onOrderFailed(quoteId, reason); } close(); @@ -127,7 +129,8 @@ void RFQDialog::onRFQResponseAccepted(const QString &reqId, const bs::network::Q emit accepted(id_); quote_ = quote; - if (rfq_.assetType == bs::network::Asset::SpotFX) { + if (rfq_.assetType == bs::network::Asset::SpotFX + || rfq_.assetType == bs::network::Asset::Futures) { quoteProvider_->AcceptQuoteFX(reqId, quote); } else { diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 575a5b86e..beb2a80c8 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -313,7 +313,8 @@ void RFQReplyWidget::onResetCurrentReservation(const std::shared_ptrgetQuoteReqId(order.quoteId); - if (order.assetType == bs::network::Asset::SpotFX) { + if (order.assetType == bs::network::Asset::SpotFX + || order.assetType == bs::network::Asset::Futures) { if (order.status == bs::network::Order::Filled) { onSettlementComplete(quoteReqId); quoteProvider_->delQuoteReqId(quoteReqId); diff --git a/common b/common index 4db4dad1d..f0c7137cf 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 4db4dad1dc0412d08cb284fc18af64b2722b40f9 +Subproject commit f0c7137cf126ce9a2e09cff537eff11c2e0e15f1 From 7f4bf21605d8a98328038ae7a6ee15be42e53961 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 4 Nov 2020 16:16:55 +0200 Subject: [PATCH 032/146] Minor fix to future quote accepting --- BlockSettleUILib/Trading/RequestingQuoteWidget.cpp | 3 ++- common | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index bf0644c92..b569b5a4d 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -130,7 +130,8 @@ bool RequestingQuoteWidget::onQuoteReceived(const bs::network::Quote& quote) return false; } - if (quote.assetType == bs::network::Asset::SpotFX) { + if (quote.assetType == bs::network::Asset::SpotFX + || quote.assetType == bs::network::Asset::Futures) { ui_->pushButtonAccept->show(); setupTimer(Tradeable, quote.expirationTime.addMSecs(quote.timeSkewMs)); } else { diff --git a/common b/common index f0c7137cf..ecf60c765 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit f0c7137cf126ce9a2e09cff537eff11c2e0e15f1 +Subproject commit ecf60c76587c825087e6047990501b951c3b338d From 11316193d7f96ed690af70d21a9de4158abe9eac Mon Sep 17 00:00:00 2001 From: Ation Date: Thu, 5 Nov 2020 15:18:39 +0200 Subject: [PATCH 033/146] Trade type reported explicitly to blotter --- BlockSettleUILib/OrderListModel.cpp | 12 +----------- common | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index b406d0a51..7ae03f165 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -644,17 +644,7 @@ void OrderListModel::processUpdateOrders(const Blocksettle::Communication::Proxy break; } - const bool isXBT = (data.product() == "XBT") || (data.product_against() == "XBT"); - const bool isCC = (assetManager_->getCCLotSize(data.product())) > 0 - || (assetManager_->getCCLotSize(data.product_against()) > 0); - - if (isCC) { - order.assetType = bs::network::Asset::PrivateMarket; - } else if (isXBT) { - order.assetType = bs::network::Asset::SpotXBT; - } else { - order.assetType = bs::network::Asset::SpotFX; - } + order.assetType = static_cast(data.trade_type()); orderId += 1; order.exchOrderId = QString::number(orderId); diff --git a/common b/common index ecf60c765..c40312c48 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ecf60c76587c825087e6047990501b951c3b338d +Subproject commit c40312c48c6e90e437abd613f3fa3660dbf3df12 From 9467d523b3bddefbe2e970f78641b187c5468a84 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 10 Nov 2020 14:26:24 +0300 Subject: [PATCH 034/146] XBT RFQ is working --- BlockSettleApp/main.cpp | 2 - .../Trading/DealerXBTSettlementContainer.cpp | 2 +- .../Trading/DealerXBTSettlementContainer.h | 2 +- BlockSettleUILib/Trading/RFQDialog.cpp | 47 +- BlockSettleUILib/Trading/RFQDialog.h | 2 + BlockSettleUILib/Trading/RFQReplyWidget.cpp | 7 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 27 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 4 + .../Trading/RequestingQuoteWidget.cpp | 2 +- BlockSettleUILib/UiUtils.h | 2 +- Core/BsServerAdapter.cpp | 67 ++ Core/BsServerAdapter.h | 11 + Core/MatchingAdapter.cpp | 43 +- Core/MessageUtils.cpp | 4 +- Core/SettlementAdapter.cpp | 623 +++++++++++++++++- Core/SettlementAdapter.h | 58 +- Core/SignerAdapter.cpp | 71 ++ Core/SignerAdapter.h | 8 + Core/TerminalMessage.h | 19 +- GUI/QtWidgets/MainWindow.cpp | 12 + GUI/QtWidgets/MainWindow.h | 4 + GUI/QtWidgets/QtGuiAdapter.cpp | 58 +- GUI/QtWidgets/QtGuiAdapter.h | 8 +- common | 2 +- 24 files changed, 995 insertions(+), 90 deletions(-) diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 8b2969a54..63de7932a 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -333,8 +333,6 @@ int main(int argc, char** argv) inprocBus.addAdapter(signAdapter); const auto& userBlockchain = bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain); -/* inprocBus.addAdapter(std::make_shared(logMgr->logger() - , bs::message::UserTerminal::create(bs::message::TerminalUsers::AuthEid)));*/ inprocBus.addAdapter(std::make_shared(logMgr->logger("trk") , bs::message::UserTerminal::create(bs::message::TerminalUsers::OnChainTracker) , userBlockchain, adSettings->createOnChainPlug())); diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp index 166897cd2..92c3b29b0 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp @@ -70,7 +70,7 @@ DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr throw std::runtime_error("no wallet"); } - auto qn = quoteProvider->getSubmittedXBTQuoteNotification(order.settlementId); + auto qn = quoteProvider->getSubmittedXBTQuoteNotification(order.settlementId.toBinStr()); if (qn.authKey.empty() || qn.reqAuthKey.empty() || qn.settlementId.empty()) { throw std::invalid_argument("failed to get submitted QN for " + order.quoteId); } diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h index 364de5362..9b6031e66 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h @@ -69,7 +69,7 @@ class DealerXBTSettlementContainer : public bs::SettlementContainer void activate() override; void deactivate() override; - std::string id() const override { return order_.settlementId; } + std::string id() const override { return order_.settlementId.toBinStr(); } bs::network::Asset::Type assetType() const override { return order_.assetType; } std::string security() const override { return order_.security; } std::string product() const override { return order_.product; } diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 9f0f5ee05..c95ac35dd 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -166,25 +166,30 @@ void RFQDialog::onRFQResponseAccepted(const std::string &reqId } } else { - if (rfq_.assetType == bs::network::Asset::SpotXBT) { - curContainer_ = newXBTcontainer(); - } else { - curContainer_ = newCCcontainer(); - } + if (armory_ && walletsManager_) { + if (rfq_.assetType == bs::network::Asset::SpotXBT) { + curContainer_ = newXBTcontainer(); + } else { + curContainer_ = newCCcontainer(); + } - if (curContainer_) { - rfqStorage_->addSettlementContainer(curContainer_); - curContainer_->activate(); + if (curContainer_) { + rfqStorage_->addSettlementContainer(curContainer_); + curContainer_->activate(); - // Do not capture `this` here! - auto failedCb = [qId = quote_.quoteId, curContainer = curContainer_.get()] + // Do not capture `this` here! + auto failedCb = [qId = quote_.quoteId, curContainer = curContainer_.get()] (const std::string& quoteId, const std::string& reason) - { - if (qId == quoteId) { - curContainer->cancel(); - } - }; - connect(quoteProvider_.get(), &QuoteProvider::orderFailed, curContainer_.get(), failedCb); + { + if (qId == quoteId) { + curContainer->cancel(); + } + }; + connect(quoteProvider_.get(), &QuoteProvider::orderFailed, curContainer_.get(), failedCb); + } + } + else { + logger_->debug("[{}] non-FX", __func__); } } } @@ -354,6 +359,16 @@ void RFQDialog::onMatchingLogout() ui_->pageRequestingQuote->onMatchingLogout(); } +void RFQDialog::onSettlementPending(const std::string& quoteId, const BinaryData& settlementId) +{ + //TODO: update UI state +} + +void RFQDialog::onSettlementComplete() +{ + accept(); +} + void RFQDialog::onTimeout() { emit expired(id_); diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index 83ac38b81..8a1a131dc 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -88,6 +88,8 @@ Q_OBJECT void onBalance(const std::string& currency, double balance); void onMatchingLogout(); + void onSettlementPending(const std::string& quoteId, const BinaryData& settlementId); + void onSettlementComplete(); signals: void accepted(const std::string &id, const bs::network::Quote&); diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 69515ca42..31d2d9c4b 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -394,10 +394,11 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) box.exec(); } } else { - const auto &it = sentXbtReplies_.find(order.settlementId); + const auto &it = sentXbtReplies_.find(order.settlementId.toBinStr()); if (it == sentXbtReplies_.end()) { // Looks like this is not error, not sure why we need this - SPDLOG_LOGGER_DEBUG(logger_, "haven't seen QuoteNotif with settlId={}", order.settlementId); + SPDLOG_LOGGER_DEBUG(logger_, "haven't seen QuoteNotif with settlId={}" + , order.settlementId.toBinStr()); return; } try { @@ -462,7 +463,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) sentCCReplies_.erase(quoteReqId); quoteProvider_->delQuoteReqId(quoteReqId); } - sentXbtReplies_.erase(order.settlementId); + sentXbtReplies_.erase(order.settlementId.toBinStr()); } } diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 383b087a3..28f35aa04 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -273,6 +273,29 @@ void RFQRequestWidget::onQuoteFailed(const std::string& rfqId } } +void RFQRequestWidget::onSettlementPending(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId) +{ + const auto& itDlg = dialogs_.find(rfqId); + if (itDlg != dialogs_.end()) { + itDlg->second->onSettlementPending(quoteId, settlementId); + } + else { + logger_->warn("[{}] RFQ dialog for {} not found", __func__, rfqId); + } +} + +void RFQRequestWidget::onSettlementComplete(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId) +{ + const auto& itDlg = dialogs_.find(rfqId); + if (itDlg != dialogs_.end()) { + itDlg->second->onSettlementComplete(); + } else { + logger_->warn("[{}] RFQ dialog for {} not found", __func__, rfqId); + } +} + void RFQRequestWidget::onReservedUTXOs(const std::string& resId , const std::string& subId, const std::vector& utxos) { @@ -447,7 +470,9 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ std::string xbtWalletId; dialog = new RFQDialog(logger_, id, rfq, xbtWalletId , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, std::move(purpose), this); - emit needSubmitRFQ(rfq); + const std::string reserveId = (rfq.assetType == bs::network::Asset::SpotFX) ? + "" : rfq.requestId; + emit needSubmitRFQ(rfq, reserveId); } connect(this, &RFQRequestWidget::unsignedPayinRequested, dialog, &RFQDialog::onUnsignedPayinRequested); diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 49c648d3e..101c61802 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -111,6 +111,10 @@ Q_OBJECT void onQuoteMatched(const std::string &rfqId, const std::string& quoteId); void onQuoteFailed(const std::string& rfqId, const std::string& quoteId , const std::string& info); + void onSettlementPending(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId); + void onSettlementComplete(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId); void onReservedUTXOs(const std::string& resId, const std::string &subId , const std::vector&); diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index 97bc7e7de..c29bf3f4f 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -41,7 +41,7 @@ RequestingQuoteWidget::RequestingQuoteWidget(QWidget* parent) ui_->labelHint->clear(); ui_->labelHint->hide(); - setupTimer(Indicative, QDateTime::currentDateTime().addSecs(30)); + setupTimer(Indicative, QDateTime::currentDateTime().addSecs(29)); //TODO: receive end time from SettlementAdapter connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &RequestingQuoteWidget::onCancel); connect(ui_->pushButtonAccept, &QPushButton::clicked, this, &RequestingQuoteWidget::onAccept); diff --git a/BlockSettleUILib/UiUtils.h b/BlockSettleUILib/UiUtils.h index d128f2461..49e299ef8 100644 --- a/BlockSettleUILib/UiUtils.h +++ b/BlockSettleUILib/UiUtils.h @@ -170,7 +170,7 @@ namespace UiUtils extern const QLatin1String XbtCurrency; - double actualXbtPrice(bs::XBTAmount amount, double price); + double actualXbtPrice(bs::XBTAmount amount, double price); // FIXME: shouldn't be in UiUtils bs::hd::Purpose getHwWalletPurpose(WalletsTypes hwType); WalletsTypes getHwWalletType(bs::hd::Purpose purpose); diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 7c380c4c6..6fe96b606 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -109,6 +109,12 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) break; case BsServerMessage::kSubmitAuthAddress: return processSubmitAuthAddr(env, msg.submit_auth_address()); + case BsServerMessage::kSendUnsignedPayin: + return processOutUnsignedPayin(msg.send_unsigned_payin()); + case BsServerMessage::kSendSignedPayin: + return processOutSignedPayin(msg.send_signed_payin()); + case BsServerMessage::kSendSignedPayout: + return processOutSignedPayout(msg.send_signed_payout()); default: break; } return true; @@ -296,6 +302,58 @@ void BsServerAdapter::processUpdateOrders(const Blocksettle::Communication::Prox pushFill(env); } +void BsServerAdapter::processUnsignedPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_UnsignedPayinRequest& response) +{ + BsServerMessage msg; + msg.set_unsigned_payin_requested(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +void BsServerAdapter::processSignPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayinRequest& response) +{ + BsServerMessage msg; + auto msgBC = msg.mutable_signed_payin_requested(); + msgBC->set_settlement_id(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); + msgBC->set_unsigned_payin(BinaryData::fromString(response.unsigned_payin_data()).toBinStr()); + msgBC->set_payin_hash(BinaryData::fromString(response.payin_hash()).toBinStr()); + msgBC->set_timestamp(response.timestamp_ms()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +void BsServerAdapter::processSignPayout(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayoutRequest& response) +{ + BsServerMessage msg; + auto msgBC = msg.mutable_signed_payout_requested(); + msgBC->set_settlement_id(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); + msgBC->set_unsigned_payin(BinaryData::fromString(response.payin_data()).toBinStr()); + msgBC->set_timestamp(response.timestamp_ms()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} + +bool BsServerAdapter::processOutUnsignedPayin(const BsServerMessage_XbtTransaction& request) +{ + const auto& settlementId = BinaryData::fromString(request.settlement_id()); + bsClient_->sendUnsignedPayin(settlementId.toHexStr(), { request.tx() }); + return true; +} + +bool BsServerAdapter::processOutSignedPayin(const BsServerMessage_XbtTransaction& request) +{ + const auto& settlementId = BinaryData::fromString(request.settlement_id()); + bsClient_->sendSignedPayin(settlementId.toHexStr(), BinaryData::fromString(request.tx())); + return true; +} + +bool BsServerAdapter::processOutSignedPayout(const BsServerMessage_XbtTransaction& request) +{ + const auto& settlementId = BinaryData::fromString(request.settlement_id()); + bsClient_->sendSignedPayout(settlementId.toHexStr(), BinaryData::fromString(request.tx())); + return true; +} + void BsServerAdapter::startTimer(std::chrono::milliseconds timeout , const std::function&cb) { @@ -376,6 +434,15 @@ void BsServerAdapter::onProcessPbMessage(const Blocksettle::Communication::Proxy case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrders: processUpdateOrders(response.update_orders()); break; + case Blocksettle::Communication::ProxyTerminalPb::Response::kSendUnsignedPayin: + processUnsignedPayin(response.send_unsigned_payin()); + break; + case Blocksettle::Communication::ProxyTerminalPb::Response::kSignPayout: + processSignPayout(response.sign_payout()); + break; + case Blocksettle::Communication::ProxyTerminalPb::Response::kSignPayin: + processSignPayin(response.sign_payin()); + break; default: break; } diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index 50c847aa7..2053600a6 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -22,12 +22,16 @@ namespace spdlog { namespace Blocksettle { namespace Communication { namespace ProxyTerminalPb { + class Response_SignPayinRequest; + class Response_SignPayoutRequest; class Response_UpdateOrders; + class Response_UnsignedPayinRequest; } } } namespace BlockSettle { namespace Terminal { + class BsServerMessage_XbtTransaction; class SettingsMessage_SettingsResponse; } } @@ -58,6 +62,13 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg bool processCancelLogin(); bool processSubmitAuthAddr(const bs::message::Envelope&, const std::string &addr); void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders&); + void processUnsignedPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_UnsignedPayinRequest&); + void processSignPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayinRequest&); + void processSignPayout(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayoutRequest&); + + bool processOutUnsignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); + bool processOutSignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); + bool processOutSignedPayout(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); //BCT callbacks void startTimer(std::chrono::milliseconds timeout, const std::function&) override; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index 5f29465fa..c8dcebd14 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -466,9 +466,47 @@ bool MatchingAdapter::onOrderReject(const std::string&) return false; } -bool MatchingAdapter::onBitcoinOrderSnapshot(const std::string&) +bool MatchingAdapter::onBitcoinOrderSnapshot(const std::string& data) { - return false; + BitcoinOrderSnapshotDownstreamEvent response; + + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onBitcoinOrderSnapshot] failed to parse"); + return false; + } + logger_->debug("[MatchingAdapter::onBitcoinOrderSnapshot] {}", ProtobufUtils::toJsonCompact(response)); + + auto orderDate = QDateTime::fromMSecsSinceEpoch(response.createdtimestamputcinmillis()); + //auto ageSeconds = orderDate.secsTo(QDateTime::currentDateTime()); + + bs::network::Order order; + order.exchOrderId = QString::number(response.orderid()); + order.clOrderId = response.externalclorderid(); + order.quoteId = response.quoteid(); + order.dateTime = QDateTime::fromMSecsSinceEpoch(response.createdtimestamputcinmillis()); + order.security = response.securitycode(); + order.quantity = response.qty(); + order.price = response.price(); + order.product = response.currency(); + order.side = bs::celer::fromCeler(response.side()); + order.assetType = bs::celer::fromCelerProductType(response.producttype()); + try { + order.settlementId = BinaryData::CreateFromHex(response.settlementid()); + } + catch (const std::exception& e) { + logger_->error("[MatchingAdapter::onBitcoinOrderSnapshot] failed to parse settlement id"); + return false; + } + order.reqTransaction = response.requestortransaction(); + order.dealerTransaction = response.dealertransaction(); + + order.status = order.status = bs::celer::mapBtcOrderStatus(response.orderstatus()); + order.pendingStatus = response.info(); + + MatchingMessage msg; + toMsg(order, msg.mutable_order()); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + return pushFill(env); } bool MatchingAdapter::onFxOrderSnapshot(const std::string& data) @@ -501,7 +539,6 @@ bool MatchingAdapter::onFxOrderSnapshot(const std::string& data) toMsg(order, msg.mutable_order()); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; return pushFill(env); - return true; } bool MatchingAdapter::onQuoteCancelled(const std::string&) diff --git a/Core/MessageUtils.cpp b/Core/MessageUtils.cpp index 10c8ae1e2..e38b7a4d0 100644 --- a/Core/MessageUtils.cpp +++ b/Core/MessageUtils.cpp @@ -95,7 +95,7 @@ bs::network::Order bs::message::fromMsg(const BlockSettle::Terminal::MatchingMes order.dateTime = QDateTime::fromMSecsSinceEpoch(msg.timestamp()); order.security = msg.security(); order.product = msg.product(); - order.settlementId = msg.settlement_id(); + order.settlementId = BinaryData::fromString(msg.settlement_id()); order.reqTransaction = msg.requester_tx(); order.dealerTransaction = msg.dealer_tx(); order.pendingStatus = msg.pending_status(); @@ -118,7 +118,7 @@ void bs::message::toMsg(const bs::network::Order& order, MatchingMessage_Order* msg->set_timestamp(order.dateTime.toMSecsSinceEpoch()); msg->set_security(order.security); msg->set_product(order.product); - msg->set_settlement_id(order.settlementId); + msg->set_settlement_id(order.settlementId.toBinStr()); msg->set_requester_tx(order.reqTransaction); msg->set_dealer_tx(order.dealerTransaction); msg->set_pending_status(order.pendingStatus); diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index 5dd4375e6..1bf2e1489 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -10,8 +10,12 @@ */ #include "SettlementAdapter.h" #include +#include "BSErrorCode.h" +#include "CurrencyPair.h" #include "MessageUtils.h" +#include "ProtobufHeadlessUtils.h" #include "TerminalMessage.h" +#include "UiUtils.h" // only for actualXbtPrice() and displayPriceXBT() #include "common.pb.h" #include "terminal.pb.h" @@ -20,12 +24,16 @@ using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; +constexpr auto kHandshakeTimeout = std::chrono::seconds{ 30 }; + SettlementAdapter::SettlementAdapter(const std::shared_ptr &logger) : logger_(logger) , user_(std::make_shared(TerminalUsers::Settlement)) + , userBS_(std::make_shared(TerminalUsers::BsServer)) , userMtch_(std::make_shared(TerminalUsers::Matching)) , userWallets_(std::make_shared(TerminalUsers::Wallets)) + , userSigner_(std::make_shared(TerminalUsers::Signer)) {} bool SettlementAdapter::process(const bs::message::Envelope &env) @@ -44,6 +52,21 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) pushFill(envBC); } } + else if (env.sender->value() == TerminalUsers::Blockchain) { + ArmoryMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse armory msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case ArmoryMessage::kZcReceived: + return processZC(msg.zc_received()); + default: break; + } + if (!env.receiver || env.receiver->isBroadcast()) { + return false; + } + } else if (env.sender->value() == userMtch_->value()) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { @@ -57,6 +80,58 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) return processMatchingOrder(msg.order()); default: break; } + if (!env.receiver || env.receiver->isBroadcast()) { + return false; + } + } + else if (env.sender->value() == userBS_->value()) { + BsServerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse BS msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case BsServerMessage::kUnsignedPayinRequested: + return processBsUnsignedPayin(BinaryData::fromString(msg.unsigned_payin_requested())); + case BsServerMessage::kSignedPayinRequested: + return processBsSignPayin(msg.signed_payin_requested()); + case BsServerMessage::kSignedPayoutRequested: + return processBsSignPayout(msg.signed_payout_requested()); + default: break; + } + if (!env.receiver || env.receiver->isBroadcast()) { + return false; + } + } + else if (env.sender->value() == userWallets_->value()) { + WalletsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse wallets msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case WalletsMessage::kXbtTxResponse: + return processXbtTx(env.id, msg.xbt_tx_response()); + default: break; + } + if (!env.receiver || env.receiver->isBroadcast()) { + return false; + } + } + else if (env.sender->value() == userSigner_->value()) { + SignerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse signer msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SignerMessage::kSignTxResponse: + return processSignedTx(env.id, msg.sign_tx_response()); + default: break; + } + if (!env.receiver || env.receiver->isBroadcast()) { + return false; + } } else if (env.receiver && (env.receiver->value() == user_->value())) { SettlementMessage msg; @@ -71,6 +146,8 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) return processAcceptRFQ(env, msg.accept_rfq()); case SettlementMessage::kSendRfq: return processSendRFQ(env, msg.send_rfq()); + case SettlementMessage::kHandshakeTimeout: + return processHandshakeTimeout(msg.handshake_timeout()); default: logger_->warn("[{}] unknown settlement request {}", __func__, msg.data_case()); break; @@ -79,6 +156,33 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) return true; } +bool SettlementAdapter::processZC(const ArmoryMessage_ZCReceived& zcData) +{ + for (const auto& zcEntry : zcData.tx_entries()) { + const auto& txHash = BinaryData::fromString(zcEntry.tx_hash()); + const auto& itZC = pendingZCs_.find(txHash); + if (itZC == pendingZCs_.end()) { + continue; + } + const auto settlementId = itZC->second; + pendingZCs_.erase(itZC); + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl == settlBySettlId_.end()) { + logger_->warn("[{}] settlement not found for {}", __func__, settlementId.toHexStr()); + continue; + } + SettlementMessage msg; + auto msgResponse = msg.mutable_settlement_complete(); + msgResponse->set_rfq_id(itSettl->second->rfq.requestId); + msgResponse->set_quote_id(itSettl->second->quote.quoteId); + msgResponse->set_settlement_id(settlementId.toBinStr()); + Envelope env{ 0, user_, itSettl->second->env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(env); + close(settlementId); + } + return true; +} + bool SettlementAdapter::processMatchingQuote(const BlockSettle::Terminal::Quote& response) { const auto& itSettl = settlByRfqId_.find(response.request_id()); @@ -89,7 +193,7 @@ bool SettlementAdapter::processMatchingQuote(const BlockSettle::Terminal::Quote& SettlementMessage msg; auto msgResp = msg.mutable_quote(); *msgResp = response; - Envelope env{ 0, user_, itSettl->second.env.sender, {}, {} + Envelope env{ 0, user_, itSettl->second->env.sender, {}, {} , msg.SerializeAsString() }; return pushFill(env); } @@ -97,36 +201,217 @@ bool SettlementAdapter::processMatchingQuote(const BlockSettle::Terminal::Quote& bool SettlementAdapter::processMatchingOrder(const MatchingMessage_Order& response) { const auto& itSettl = settlByQuoteId_.find(response.quote_id()); - if (itSettl == settlByRfqId_.end()) { + if (itSettl == settlByQuoteId_.end()) { logger_->error("[{}] unknown settlement for {}", __func__, response.quote_id()); return true; } SettlementMessage msg; const auto& order = fromMsg(response); if (order.status == bs::network::Order::Status::Filled) { - settlBySettlId_[response.settlement_id()] = itSettl->second; auto msgResp = msg.mutable_matched_quote(); - msgResp->set_rfq_id(itSettl->second.rfq.requestId); + msgResp->set_rfq_id(itSettl->second->rfq.requestId); msgResp->set_quote_id(response.quote_id()); msgResp->set_price(response.price()); } else if (order.status == bs::network::Order::Status::Failed) { - auto msgResp = msg.mutable_failed_quote(); - msgResp->set_rfq_id(itSettl->second.rfq.requestId); + auto msgResp = msg.mutable_failed_settlement(); + msgResp->set_rfq_id(itSettl->second->rfq.requestId); msgResp->set_quote_id(response.quote_id()); msgResp->set_info(order.info); } + else if (order.status == bs::network::Order::Status::Pending) { + auto msgResp = msg.mutable_pending_settlement(); + msgResp->set_rfq_id(itSettl->second->rfq.requestId); + msgResp->set_quote_id(response.quote_id()); + msgResp->set_settlement_id(response.settlement_id()); + } else { logger_->debug("[{}] {} unprocessed order status {}", __func__, order.quoteId , (int)order.status); return true; } - Envelope env{ 0, user_, itSettl->second.env.sender, {}, {} + Envelope env{ 0, user_, itSettl->second->env.sender, {}, {} , msg.SerializeAsString() }; - settlByQuoteId_.erase(itSettl); return pushFill(env); } +bool SettlementAdapter::processBsUnsignedPayin(const BinaryData& settlementId) +{ + logger_->debug("[{}] {}", __func__, settlementId.toHexStr()); + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl == settlBySettlId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, settlementId.toHexStr()); + return true; + } + + WalletsMessage msg; + auto msgReq = msg.mutable_payin_request(); + msgReq->set_own_auth_address(itSettl->second->ownAuthAddr.display()); + msgReq->set_counter_auth_address(itSettl->second->counterAuthAddr.display()); + msgReq->set_counter_auth_pubkey(itSettl->second->counterKey.toBinStr()); + msgReq->set_settlement_id(settlementId.toBinStr()); + msgReq->set_reserve_id(itSettl->second->reserveId); + msgReq->set_amount(itSettl->second->amount.GetValue()); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + if (pushFill(env)) { + payinRequests_[env.id] = settlementId; + } + return true; +} + +bs::sync::PasswordDialogData SettlementAdapter::getDialogData(const QDateTime& timestamp + , const Settlement& settlement) const +{ + bs::sync::PasswordDialogData dialogData; + dialogData.setValue(bs::sync::PasswordDialogData::SettlementId, settlement.settlementId.toHexStr()); + dialogData.setValue(bs::sync::PasswordDialogData::DurationLeft, 29 * 1000); // TODO: take from real timeout from SettlementAdapter + dialogData.setValue(bs::sync::PasswordDialogData::DurationTotal, 30 * 1000); // TODO: same + + // Set timestamp that will be used by auth eid server to update timers. + dialogData.setValue(bs::sync::PasswordDialogData::DurationTimestamp, static_cast(timestamp.toSecsSinceEpoch())); + + dialogData.setValue(bs::sync::PasswordDialogData::ProductGroup + , QObject::tr(bs::network::Asset::toString(settlement.rfq.assetType))); + dialogData.setValue(bs::sync::PasswordDialogData::Security, settlement.rfq.security); + dialogData.setValue(bs::sync::PasswordDialogData::Product, settlement.rfq.product); + dialogData.setValue(bs::sync::PasswordDialogData::Side + , QObject::tr(bs::network::Side::toString(settlement.rfq.side))); + dialogData.setValue(bs::sync::PasswordDialogData::ExpandTxInfo, true); //TODO: make configurable? + + dialogData.setValue(bs::sync::PasswordDialogData::Market, "XBT"); + dialogData.setValue(bs::sync::PasswordDialogData::AutoSignCategory + , static_cast(settlement.dealer ? bs::signer::AutoSignCategory::SettlementDealer + : bs::signer::AutoSignCategory::SettlementRequestor)); + + dialogData.setValue(bs::sync::PasswordDialogData::SettlementId, settlement.settlementId.toHexStr()); + dialogData.setValue(bs::sync::PasswordDialogData::SettlementAddress, settlement.settlementAddress.display()); + + // rfq details + QString qtyProd = UiUtils::XbtCurrency; + QString fxProd = QString::fromStdString(settlement.fxProduct); + + dialogData.setValue(bs::sync::PasswordDialogData::Price, UiUtils::displayPriceXBT(settlement.quote.price)); + dialogData.setValue(bs::sync::PasswordDialogData::FxProduct, fxProd); + + bool isFxProd = (settlement.rfq.product != bs::network::XbtCurrency); + + if (isFxProd) { + dialogData.setValue(bs::sync::PasswordDialogData::Quantity, QObject::tr("%1 %2") + .arg(UiUtils::displayAmountForProduct(settlement.rfq.quantity, fxProd, bs::network::Asset::Type::SpotXBT)) + .arg(fxProd)); + + dialogData.setValue(bs::sync::PasswordDialogData::TotalValue, QObject::tr("%1 XBT") + .arg(UiUtils::displayAmount(settlement.rfq.quantity / settlement.quote.price))); + } else { + dialogData.setValue(bs::sync::PasswordDialogData::Quantity, QObject::tr("%1 XBT") + .arg(UiUtils::displayAmount(settlement.amount))); + + dialogData.setValue(bs::sync::PasswordDialogData::TotalValue, QObject::tr("%1 %2") + .arg(UiUtils::displayAmountForProduct(settlement.amount.GetValueBitcoin() * settlement.quote.price + , fxProd, bs::network::Asset::Type::SpotXBT)) + .arg(fxProd)); + } + + dialogData.setValue(bs::sync::PasswordDialogData::RequesterAuthAddress, settlement.ownAuthAddr.display()); + dialogData.setValue(bs::sync::PasswordDialogData::RequesterAuthAddressVerified, true); + dialogData.setValue(bs::sync::PasswordDialogData::TxInputProduct, UiUtils::XbtCurrency); + + dialogData.setValue(bs::sync::PasswordDialogData::ResponderAuthAddress, settlement.counterAuthAddr.display()); + + return dialogData; +} + +bs::sync::PasswordDialogData SettlementAdapter::getPayinDialogData(const QDateTime& timestamp + , const Settlement& settlement) const +{ + auto dlgData = getDialogData(timestamp, settlement); + dlgData.setValue(bs::sync::PasswordDialogData::ResponderAuthAddressVerified, true); //TODO: put actual value + dlgData.setValue(bs::sync::PasswordDialogData::SigningAllowed, true); //TODO: same here + + dlgData.setValue(bs::sync::PasswordDialogData::Title, QObject::tr("Settlement Pay-In")); + return dlgData; +} + +bs::sync::PasswordDialogData SettlementAdapter::getPayoutDialogData(const QDateTime& timestamp + , const Settlement& settlement) const +{ + auto dlgData = getDialogData(timestamp, settlement); + dlgData.setValue(bs::sync::PasswordDialogData::Title, QObject::tr("Settlement Pay-Out")); + dlgData.setValue(bs::sync::PasswordDialogData::SettlementPayOutVisible, true); + + dlgData.setValue(bs::sync::PasswordDialogData::ResponderAuthAddressVerified, true); + dlgData.setValue(bs::sync::PasswordDialogData::SigningAllowed, true); + return dlgData; +} + +bool SettlementAdapter::processBsSignPayin(const BsServerMessage_SignXbtHalf& request) +{ + const auto &settlementId = BinaryData::fromString(request.settlement_id()); + const auto& payinHash = BinaryData::fromString(request.payin_hash()); + logger_->debug("[{}] {}: payin hash {}", __func__, settlementId.toHexStr() + , payinHash.toHexStr()); + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl == settlBySettlId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, settlementId.toHexStr()); + return true; + } + if (!itSettl->second->payin.isValid()) { + logger_->error("[{}] no payin TX for {}", __func__, settlementId.toHexStr()); + unreserve(itSettl->second->rfq.requestId); + SettlementMessage msg; + auto msgFail = msg.mutable_failed_settlement(); + msgFail->set_settlement_id(settlementId.toBinStr()); + msgFail->set_info("invalid handshake flow"); + Envelope env{ 0, user_, itSettl->second->env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(env); + settlByQuoteId_.erase(itSettl->second->quote.quoteId); + settlBySettlId_.erase(itSettl); + return true; + } + pendingZCs_[payinHash] = settlementId; + itSettl->second->payin.txHash = payinHash; + auto dlgData = getPayinDialogData(QDateTime::fromMSecsSinceEpoch(request.timestamp()) + , *itSettl->second); + SignerMessage msg; + auto msgReq = msg.mutable_sign_settlement_tx(); + msgReq->set_settlement_id(settlementId.toBinStr()); + *msgReq->mutable_tx_request() = bs::signer::coreTxRequestToPb(itSettl->second->payin); + *msgReq->mutable_details() = dlgData.toProtobufMessage(); + Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; + if (pushFill(env)) { + payinRequests_[env.id] = settlementId; + } + return true; +} + +bool SettlementAdapter::processBsSignPayout(const BsServerMessage_SignXbtHalf& request) +{ + const auto settlementId = BinaryData::fromString(request.settlement_id()); + logger_->debug("[{}] {}", __func__, settlementId.toHexStr()); + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl == settlBySettlId_.end()) { + logger_->error("[{}] unknown settlement for {}", __func__, settlementId.toHexStr()); + return true; + } + if (itSettl->second->recvAddress.empty()) { + logger_->error("[{}] empty receiving address", __func__); + return true; + } + WalletsMessage msg; + auto msgReq = msg.mutable_payout_request(); + msgReq->set_own_auth_address(itSettl->second->ownAuthAddr.display()); + msgReq->set_settlement_id(settlementId.toBinStr()); + msgReq->set_counter_auth_pubkey(itSettl->second->counterKey.toBinStr()); + msgReq->set_amount(itSettl->second->amount.GetValue()); + msgReq->set_payin_hash(request.payin_hash()); + msgReq->set_recv_address(itSettl->second->recvAddress.display()); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + if (pushFill(env)) { + payoutRequests_[env.id] = settlementId; + } + return true; +} + bool SettlementAdapter::processCancelRFQ(const std::string& rfqId) { const auto& itSettl = settlByRfqId_.find(rfqId); @@ -150,23 +435,42 @@ bool SettlementAdapter::processAcceptRFQ(const bs::message::Envelope& env logger_->error("[{}] unknown settlement for {}", __func__, request.rfq_id()); return true; } - itSettl->second.env = env; - itSettl->second.quote = fromMsg(request.quote()); + itSettl->second->env = env; + const auto& quote = fromMsg(request.quote()); + itSettl->second->quote = quote; settlByQuoteId_[request.quote().quote_id()] = itSettl->second; settlByRfqId_.erase(itSettl); - MatchingMessage msg; - auto msgReq = msg.mutable_accept_rfq(); - *msgReq = request; - Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; - return pushFill(envReq); + if (quote.assetType == bs::network::Asset::SpotFX) { + MatchingMessage msg; + auto msgReq = msg.mutable_accept_rfq(); + *msgReq = request; + Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; + return pushFill(envReq); + } + switch (quote.assetType) { + case bs::network::Asset::SpotXBT: + startXbtSettlement(quote); + break; + case bs::network::Asset::PrivateMarket: + startCCSettlement(quote); + break; + default: + logger_->error("[{}] unknown asset type {}", __func__, (int)quote.assetType); + break; + } } bool SettlementAdapter::processSendRFQ(const bs::message::Envelope& env , const SettlementMessage_SendRFQ& request) { const auto& rfq = fromMsg(request.rfq()); - settlByRfqId_[rfq.requestId] = Settlement{ env, false, rfq, request.reserve_id() }; + const auto &settlement = std::make_shared(Settlement{ env + , false, rfq, request.reserve_id() }); + if (rfq.side == bs::network::Side::Buy) { + settlement->recvAddress = bs::Address::fromAddressString(rfq.receiptAddress); + } + settlByRfqId_[rfq.requestId] = settlement; MatchingMessage msg; auto msgReq = msg.mutable_send_rfq(); @@ -175,6 +479,267 @@ bool SettlementAdapter::processSendRFQ(const bs::message::Envelope& env return pushFill(envReq); } +bool SettlementAdapter::processXbtTx(uint64_t msgId, const WalletsMessage_XbtTxResponse& response) +{ + const auto& itPayin = payinRequests_.find(msgId); + if (itPayin != payinRequests_.end()) { + const auto& itSettl = settlBySettlId_.find(itPayin->second); + if (itSettl != settlBySettlId_.end()) { + if (response.error_text().empty()) { + itSettl->second->payin = bs::signer::pbTxRequestToCore(response.tx_request(), logger_); + try { + itSettl->second->settlementAddress = bs::Address::fromAddressString(response.settlement_address()); + } + catch (const std::exception&) { + logger_->error("[{}] invalid settlement address", __func__); + } + BsServerMessage msg; + auto msgReq = msg.mutable_send_unsigned_payin(); + msgReq->set_settlement_id(itPayin->second.toBinStr()); + msgReq->set_tx(itSettl->second->payin.serializeState().SerializeAsString()); + Envelope env{ 0, user_, userBS_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); + } + else { + SettlementMessage msg; + auto msgResp = msg.mutable_failed_settlement(); + msgResp->set_rfq_id(itSettl->second->rfq.requestId); + msgResp->set_settlement_id(itPayin->second.toBinStr()); + msgResp->set_info(response.error_text()); + Envelope env{ 0, user_, itSettl->second->env.sender, {}, {} + , msg.SerializeAsString() }; + pushFill(env); + cancel(itPayin->second); + } + } + else { + logger_->error("[{}] settlement {} for payin not found", __func__ + , itPayin->second.toHexStr()); + } + payinRequests_.erase(itPayin); + return true; + } + const auto& itPayout = payoutRequests_.find(msgId); + if (itPayout != payoutRequests_.end()) { + const auto& itSettl = settlBySettlId_.find(itPayout->second); + if (itSettl != settlBySettlId_.end()) { + logger_->debug("[{}] got payout", __func__); + try { + itSettl->second->settlementAddress = bs::Address::fromAddressString(response.settlement_address()); + } catch (const std::exception&) { + logger_->error("[{}] invalid settlement address", __func__); + } + auto dlgData = getPayoutDialogData(QDateTime::currentDateTime(), *itSettl->second); + SignerMessage msg; + auto msgReq = msg.mutable_sign_settlement_tx(); + msgReq->set_settlement_id(itPayout->second.toBinStr()); + *msgReq->mutable_tx_request() = response.tx_request(); + *msgReq->mutable_details() = dlgData.toProtobufMessage(); + msgReq->set_contra_auth_pubkey(itSettl->second->counterKey.toBinStr()); + msgReq->set_own_key_first(!itSettl->second->dealer); + Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; + if (pushFill(env)) { + payoutRequests_[env.id] = itPayout->second; + } + } + else { + logger_->error("[{}] settlement {} for payout not found", __func__ + , itPayout->second.toHexStr()); + } + payoutRequests_.erase(itPayout); + return true; + } + logger_->error("[{}] unknown XBT TX response #{}", __func__, msgId); + return true; +} + +bool SettlementAdapter::processSignedTx(uint64_t msgId + , const SignerMessage_SignTxResponse& response) +{ + const auto& settlementId = BinaryData::fromString(response.id()); + const auto& sendSignedTx = [this, response, settlementId](bool payin) + { + if (static_cast(response.error_code()) == bs::error::ErrorCode::TxCancelled) { + cancel(settlementId); + return; + } + BsServerMessage msg; + auto msgReq = payin ? msg.mutable_send_signed_payin() : msg.mutable_send_signed_payout(); + msgReq->set_settlement_id(settlementId.toBinStr()); + msgReq->set_tx(response.signed_tx()); + Envelope env{ 0, user_, userBS_, {}, {}, msg.SerializeAsString(), true }; + logger_->debug("[SettlementAdapter::processSignedTx::sendSignedTX] {}", BinaryData::fromString(response.signed_tx()).toHexStr()); + pushFill(env); + }; + const auto& itPayin = payinRequests_.find(msgId); + if (itPayin != payinRequests_.end()) { + if (itPayin->second != settlementId) { + logger_->error("[{}] payin settlement id {} mismatch", __func__ + , settlementId.toHexStr()); + payinRequests_.erase(itPayin); + return true; //TODO: decide the consequences of this + } + const auto& itSettl = settlBySettlId_.find(itPayin->second); + payinRequests_.erase(itPayin); + if (itSettl == settlBySettlId_.end()) { + logger_->error("[{}] settlement for {} not found", __func__ + , settlementId.toHexStr()); + return true; + } + itSettl->second->handshakeComplete = true; + sendSignedTx(true); + return true; + } + + const auto& itPayout = payoutRequests_.find(msgId); + if (itPayout != payoutRequests_.end()) { + if (itPayout->second != settlementId) { + logger_->error("[{}] payout settlement id mismatch"); + payoutRequests_.erase(itPayout); + return true; //TODO: decide the consequences of this + } + try { + const Tx tx(BinaryData::fromString(response.signed_tx())); + pendingZCs_[tx.getThisHash()] = itPayout->second; + } + catch (const std::exception&) { + logger_->error("[{}] invalid signed payout TX", __func__); + payoutRequests_.erase(itPayout); + cancel(itPayout->second); + return true; + } + const auto& itSettl = settlBySettlId_.find(itPayout->second); + payoutRequests_.erase(itPayout); + if (itSettl == settlBySettlId_.end()) { + logger_->error("[{}] settlement for {} not found", __func__ + , settlementId.toHexStr()); + return true; + } + itSettl->second->handshakeComplete = true; + sendSignedTx(false); + return true; + } + logger_->error("[{}] unknown signed TX #{}", __func__, msgId); + return true; +} + +bool SettlementAdapter::processHandshakeTimeout(const std::string& id) +{ + const auto& settlementId = BinaryData::fromString(id); + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl != settlBySettlId_.end()) { + if (!itSettl->second->handshakeComplete) { + logger_->error("[{}] settlement {} handshake timeout", __func__ + , settlementId.toHexStr()); + SettlementMessage msg; + auto msgFail = msg.mutable_failed_settlement(); + msgFail->set_settlement_id(settlementId.toBinStr()); + msgFail->set_info("handshake timeout"); + Envelope env{ 0, user_, itSettl->second->env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(env); + } + } + return true; +} + +void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) +{ + const auto& sendFailedQuote = [this, quote](const std::string& info) + { + logger_->error("[SettlementAdapter::startXbtSettlement] {} - aborting " + "settlement", info); + unreserve(quote.requestId); + SettlementMessage msg; + auto msgResp = msg.mutable_failed_settlement(); + msgResp->set_rfq_id(quote.requestId); + msgResp->set_quote_id(quote.quoteId); + msgResp->set_info(info); + Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + pushFill(env); + }; + const auto& itSettl = settlByQuoteId_.find(quote.quoteId); + if (itSettl == settlByQuoteId_.end()) { + sendFailedQuote("unknown quote id"); + return; + } + if (quote.settlementId.empty()) { + sendFailedQuote("no settlement id"); + settlByQuoteId_.erase(itSettl); + return; + } + + CurrencyPair cp(quote.security); + const bool isFxProd = (quote.product != bs::network::XbtCurrency); + itSettl->second->fxProduct = cp.ContraCurrency(bs::network::XbtCurrency); + const double amount = isFxProd ? quote.quantity / quote.price : quote.quantity; + const auto xbtAmount = bs::XBTAmount(amount); + itSettl->second->amount = xbtAmount; + + // BST-2545: Use price as it see Genoa (and it computes it as ROUNDED_CCY / XBT) + const auto actualXbtPrice = UiUtils::actualXbtPrice(xbtAmount, quote.price); + itSettl->second->actualXbtPrice = actualXbtPrice; + + const auto side = (quote.product == bs::network::XbtCurrency) + ? bs::network::Side::invert(quote.side) : quote.side; + itSettl->second->txComment = fmt::format("{} {} @ {}", bs::network::Side::toString(side) + , quote.security, UiUtils::displayPriceXBT(actualXbtPrice).toStdString()); +// itSettl->second.dealerAddrValidationReqd = xbtAmount > bs::XBTAmount(tier1XbtLimit); + + BinaryData settlementId; + try { + settlementId = BinaryData::CreateFromHex(quote.settlementId); + } + catch (const std::exception&) { + sendFailedQuote("invalid settlement id format"); + settlByQuoteId_.erase(itSettl); + return; + } + itSettl->second->settlementId = settlementId; + + auto &data = settlBySettlId_[settlementId] = itSettl->second; + try { + if (data->dealer) { + data->ownKey = BinaryData::CreateFromHex(quote.dealerAuthPublicKey); + data->counterKey = BinaryData::CreateFromHex(quote.requestorAuthPublicKey); + } else { + data->ownKey = BinaryData::CreateFromHex(quote.requestorAuthPublicKey); + data->counterKey = BinaryData::CreateFromHex(quote.dealerAuthPublicKey); + } + data->ownAuthAddr = bs::Address::fromPubKey(data->ownKey, AddressEntryType_P2WPKH); + data->counterAuthAddr = bs::Address::fromPubKey(data->counterKey, AddressEntryType_P2WPKH); + } + catch (const std::exception&) { + sendFailedQuote("failed to decode data"); + settlBySettlId_.erase(data->settlementId); + settlByQuoteId_.erase(itSettl); + return; + } + + const auto& timeNow = std::chrono::system_clock::now(); + SettlementMessage msg; + msg.set_handshake_timeout(settlementId.toBinStr()); + Envelope env{ 0, user_, user_, timeNow, timeNow + kHandshakeTimeout + , msg.SerializeAsString(), true }; + pushFill(env); + + if (data->dealerAddrValidationReqd) { + //TODO: push counterAuthAddr to OnChainTracker for checking + } + + MatchingMessage msgMtch; + auto msgReq = msgMtch.mutable_accept_rfq(); + msgReq->set_rfq_id(quote.requestId); + toMsg(quote, msgReq->mutable_quote()); + msgReq->set_payout_tx("not used"); // copied from ReqXBTSettlementContainer + Envelope envReq{ 0, user_, userMtch_, {}, {}, msgMtch.SerializeAsString(), true }; + pushFill(envReq); +} + +void SettlementAdapter::startCCSettlement(const bs::network::Quote&) +{ +} + void SettlementAdapter::unreserve(const std::string& id, const std::string &subId) { WalletsMessage msg; @@ -184,3 +749,29 @@ void SettlementAdapter::unreserve(const std::string& id, const std::string &subI Envelope envReq{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; pushFill(envReq); } + +void SettlementAdapter::close(const BinaryData& settlementId) +{ + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl == settlBySettlId_.end()) { + return; + } + logger_->debug("[{}] {}", __func__, settlementId.toHexStr()); + unreserve(itSettl->second->rfq.requestId); + settlByQuoteId_.erase(itSettl->second->quote.quoteId); + settlBySettlId_.erase(itSettl); +} + +void SettlementAdapter::cancel(const BinaryData& settlementId) +{ + const auto& itSettl = settlBySettlId_.find(settlementId); + if (itSettl == settlBySettlId_.end()) { + return; + } + const auto sender = itSettl->second->env.sender; + close(settlementId); + SettlementMessage msg; + msg.set_settlement_cancelled(settlementId.toBinStr()); + Envelope env{ 0, user_, sender, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} diff --git a/Core/SettlementAdapter.h b/Core/SettlementAdapter.h index eb5afbf83..38372064d 100644 --- a/Core/SettlementAdapter.h +++ b/Core/SettlementAdapter.h @@ -12,14 +12,22 @@ #define SETTLEMENT_ADAPTER_H #include "CommonTypes.h" +#include "CoreWallet.h" #include "Message/Adapter.h" +#include "PasswordDialogData.h" namespace spdlog { class logger; } namespace BlockSettle { + namespace Common { + class ArmoryMessage_ZCReceived; + class SignerMessage_SignTxResponse; + class WalletsMessage_XbtTxResponse; + } namespace Terminal { class AcceptRFQ; + class BsServerMessage_SignXbtHalf; class MatchingMessage_Order; class Quote; class SettlementMessage_SendRFQ; @@ -40,31 +48,71 @@ class SettlementAdapter : public bs::message::Adapter std::string name() const override { return "Settlement"; } private: + bool processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived&); bool processMatchingQuote(const BlockSettle::Terminal::Quote&); bool processMatchingOrder(const BlockSettle::Terminal::MatchingMessage_Order&); + bool processBsUnsignedPayin(const BinaryData& settlementId); + bool processBsSignPayin(const BlockSettle::Terminal::BsServerMessage_SignXbtHalf&); + bool processBsSignPayout(const BlockSettle::Terminal::BsServerMessage_SignXbtHalf&); + bool processCancelRFQ(const std::string& rfqId); bool processAcceptRFQ(const bs::message::Envelope& , const BlockSettle::Terminal::AcceptRFQ&); bool processSendRFQ(const bs::message::Envelope& , const BlockSettle::Terminal::SettlementMessage_SendRFQ&); + bool processXbtTx(uint64_t msgId, const BlockSettle::Common::WalletsMessage_XbtTxResponse&); + bool processSignedTx(uint64_t msgId, const BlockSettle::Common::SignerMessage_SignTxResponse&); + bool processHandshakeTimeout(const std::string& id); + void startXbtSettlement(const bs::network::Quote&); + void startCCSettlement(const bs::network::Quote&); void unreserve(const std::string& id, const std::string& subId = {}); + void cancel(const BinaryData& settlementId); + void close(const BinaryData& settlementId); private: std::shared_ptr logger_; - std::shared_ptr user_, userMtch_, userWallets_; + std::shared_ptr user_, userMtch_, userWallets_, userBS_; + std::shared_ptr userSigner_; struct Settlement { bs::message::Envelope env; - bool dealer{ false }; + bool dealer{ false }; bs::network::RFQ rfq; std::string reserveId; bs::network::Quote quote; + std::string fxProduct; + bs::XBTAmount amount; + double actualXbtPrice; + bool dealerAddrValidationReqd{ false }; + BinaryData settlementId; + bs::Address settlementAddress; + std::string txComment; + bs::Address recvAddress; + bs::Address ownAuthAddr; + BinaryData ownKey; + BinaryData counterKey; + bs::Address counterAuthAddr; + bs::core::wallet::TXSignRequest payin; + bool otc{ false }; + bool handshakeComplete{ false }; }; - std::unordered_map settlByRfqId_; - std::unordered_map settlByQuoteId_; - std::unordered_map settlBySettlId_; + std::unordered_map> settlByRfqId_; + std::unordered_map> settlByQuoteId_; + std::map> settlBySettlId_; + std::map pendingZCs_; + + std::map payinRequests_; + std::map payoutRequests_; + +private: + bs::sync::PasswordDialogData getDialogData(const QDateTime& timestamp + , const Settlement &) const; + bs::sync::PasswordDialogData getPayinDialogData(const QDateTime& timestamp + , const Settlement&) const; + bs::sync::PasswordDialogData getPayoutDialogData(const QDateTime& timestamp + , const Settlement&) const; }; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 5c37a90d8..fcc35d23c 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -127,6 +127,13 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env , request.set_user_id().wallet_id()); case SignerMessage::kCreateSettlWallet: return processCreateSettlWallet(env, request.create_settl_wallet()); + case SignerMessage::kGetSettlPayinAddr: + return processGetPayinAddress(env, request.get_settl_payin_addr()); + case SignerMessage::kResolvePubSpenders: + return processResolvePubSpenders(env + , bs::signer::pbTxRequestToCore(request.resolve_pub_spenders())); + case SignerMessage::kSignSettlementTx: + return processSignSettlementTx(env, request.sign_settlement_tx()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -590,6 +597,33 @@ bool SignerAdapter::processSetSettlId(const bs::message::Envelope &env return true; } +bool SignerAdapter::processSignSettlementTx(const bs::message::Envelope& env + , const SignerMessage_SignSettlementTx& request) +{ + const auto& signCb = [this, env, settlementId=request.settlement_id()] + (bs::error::ErrorCode result, const BinaryData& signedTx) + { + SignerMessage msg; + auto msgResp = msg.mutable_sign_tx_response(); + msgResp->set_id(settlementId); + msgResp->set_error_code((int)result); + msgResp->set_signed_tx(signedTx.toBinStr()); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(envResp); + }; + + const auto& txReq = bs::signer::pbTxRequestToCore(request.tx_request()); + const bs::sync::PasswordDialogData dlgData(request.details()); + if (request.contra_auth_pubkey().empty()) { + return (signer_->signSettlementTXRequest(txReq, dlgData + , SignContainer::TXSignMode::Full, false, signCb) != 0); + } + const bs::core::wallet::SettlementData sd{ + BinaryData::fromString(request.settlement_id()), + BinaryData::fromString(request.contra_auth_pubkey()), request.own_key_first() }; + return (signer_->signSettlementPayoutTXRequest(txReq, sd, dlgData, signCb) != 0); +} + bool SignerAdapter::processGetRootPubKey(const bs::message::Envelope &env , const std::string &walletId) { @@ -673,3 +707,40 @@ bool SignerAdapter::processCreateSettlWallet(const bs::message::Envelope& env signer_->createSettlementWallet(authAddr, cb); return true; } + +bool SignerAdapter::processGetPayinAddress(const bs::message::Envelope& env + , const SignerMessage_GetSettlPayinAddr& request) +{ + const auto& cbAddr = [this, env](bool success, const bs::Address& settlAddr) + { + SignerMessage msg; + auto msgResp = msg.mutable_payin_address(); + msgResp->set_success(success); + msgResp->set_address(settlAddr.display()); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(envResp); + }; + bs::core::wallet::SettlementData settlData{ BinaryData::fromString(request.settlement_id()) + , BinaryData::fromString(request.contra_auth_pubkey()), request.own_key_first() }; + signer_->getSettlementPayinAddress(request.wallet_id(), settlData, cbAddr); + return true; +} + +bool SignerAdapter::processResolvePubSpenders(const bs::message::Envelope& env + , const bs::core::wallet::TXSignRequest& txReq) +{ + const auto& cbResolve = [this, env](bs::error::ErrorCode result + , const Codec_SignerState::SignerState& state) + { + SignerMessage msg; + auto msgResp = msg.mutable_resolved_spenders(); + msgResp->set_result((int)result); + msgResp->set_signer_state(state.SerializeAsString()); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + pushFill(envResp); + }; + if (signer_->resolvePublicSpenders(txReq, cbResolve) == 0) { + logger_->error("[{}] failed to send", __func__); + } + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 3a7901f45..c7580b4fe 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -22,7 +22,9 @@ namespace BlockSettle { namespace Common { class SignerMessage; class SignerMessage_ExtendAddrChain; + class SignerMessage_GetSettlPayinAddr; class SignerMessage_SetSettlementId; + class SignerMessage_SignSettlementTx; class SignerMessage_SignTxRequest; class SignerMessage_SyncAddresses; class SignerMessage_SyncAddressComment; @@ -85,6 +87,8 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget bool processSyncTxComment(const BlockSettle::Common::SignerMessage_SyncTxComment &); bool processSetSettlId(const bs::message::Envelope & , const BlockSettle::Common::SignerMessage_SetSettlementId &); + bool processSignSettlementTx(const bs::message::Envelope& + , const BlockSettle::Common::SignerMessage_SignSettlementTx&); bool processGetRootPubKey(const bs::message::Envelope &, const std::string &walletId); bool processDelHdRoot(const std::string &walletId); bool processDelHdLeaf(const std::string &walletId); @@ -92,6 +96,10 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget , const BlockSettle::Common::SignerMessage_SignTxRequest&); bool processSetUserId(const std::string& userId, const std::string& walletId); bool processCreateSettlWallet(const bs::message::Envelope&, const std::string &); + bool processGetPayinAddress(const bs::message::Envelope& + , const BlockSettle::Common::SignerMessage_GetSettlPayinAddr&); + bool processResolvePubSpenders(const bs::message::Envelope& + , const bs::core::wallet::TXSignRequest&); private: std::shared_ptr logger_; diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index 61b8c4334..e0d7c7eb0 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -35,20 +35,19 @@ namespace bs { Unknown = 0, BROADCAST, System, // Used only as a sender of administrative messages, no adapter registers it - Signer, - API, - Settings, + Signer, // TX signing and core wallets loading + API, // API adapter and bus for API clients + Settings, // All kinds of static data BsServer, // General target/origin of all sorts of BS server messages - AuthEid, - Matching, // Alias for Celer or other matching system - Assets, // Alias for Genoa data storage atm - MktData, + Matching, // Alias for Celer (or other matching system in the future) + Assets, // Assets manipulation + MktData, // Market data retranslation MDHistory, // Charts data storage Blockchain, // General name for Armory connection - Wallets, + Wallets, // Sync wallet routines (loading, balance, UTXO reservation) OnChainTracker,// Auth & CC tracker combined in one adapter - Settlement, - Chat, + Settlement, // All settlements (FX, XBT, CC) for both dealer and requester + Chat, // Chat network routines UsersCount }; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 6ee5be5be..43fc7273c 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -1067,6 +1067,18 @@ void MainWindow::onQuoteFailed(const std::string& rfqId, const std::string& quot ui_->widgetRFQ->onQuoteFailed(rfqId, quoteId, info); } +void bs::gui::qt::MainWindow::onSettlementPending(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId) +{ + ui_->widgetRFQ->onSettlementPending(rfqId, quoteId, settlementId); +} + +void bs::gui::qt::MainWindow::onSettlementComplete(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId) +{ + ui_->widgetRFQ->onSettlementComplete(rfqId, quoteId, settlementId); +} + void MainWindow::onOrdersUpdate(const std::vector& orders) { orderListModel_->onOrdersUpdate(orders); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index b75ffecdd..818f1ac96 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -116,6 +116,10 @@ namespace bs { void onQuoteMatched(const std::string &rfqId, const std::string "eId); void onQuoteFailed(const std::string& rfqId, const std::string& quoteId , const std::string &info); + void onSettlementPending(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId); + void onSettlementComplete(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId); void onOrdersUpdate(const std::vector&); void onReservedUTXOs(const std::string& resId, const std::string& subId diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index dde45cb42..0a9c67825 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -253,8 +253,6 @@ bool QtGuiAdapter::process(const Envelope &env) return processMatching(env); case TerminalUsers::MktData: return processMktData(env); - case TerminalUsers::AuthEid: - return processAuthEid(env); case TerminalUsers::OnChainTracker: return processOnChainTrack(env); case TerminalUsers::Assets: @@ -573,23 +571,6 @@ bool QtGuiAdapter::processWallets(const Envelope &env) return true; } -bool QtGuiAdapter::processAuthEid(const Envelope &env) -{ - AuthEidMessage msg; - if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id); - return true; - } - switch (msg.data_case()) { - case AuthEidMessage::kLoading: - loadingComponents_.insert(env.sender->value()); - updateSplashProgress(); - break; - default: break; - } - return true; -} - bool QtGuiAdapter::processOnChainTrack(const Envelope &env) { OnChainTrackMessage msg; @@ -1205,8 +1186,12 @@ void QtGuiAdapter::onNeedSetUserId(const std::string& userId) pushFill(env); } -void QtGuiAdapter::onSetRecommendedFeeRate(float) +void QtGuiAdapter::onSetRecommendedFeeRate(float fee) { + WalletsMessage msg; + msg.set_set_settlement_fee(fee); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); } void QtGuiAdapter::onNeedMdConnection(ApplicationSettings::EnvConfiguration ec) @@ -1618,8 +1603,12 @@ bool QtGuiAdapter::processSettlement(const bs::message::Envelope& env) return processQuote(msg.quote()); case SettlementMessage::kMatchedQuote: return processMatchedQuote(msg.matched_quote()); - case SettlementMessage::kFailedQuote: - return processFailedQuote(msg.failed_quote()); + case SettlementMessage::kFailedSettlement: + return processFailedSettl(msg.failed_settlement()); + case SettlementMessage::kPendingSettlement: + return processPendingSettl(msg.pending_settlement()); + case SettlementMessage::kSettlementComplete: + return processSettlComplete(msg.settlement_complete()); default: break; } return true; @@ -1788,10 +1777,31 @@ bool QtGuiAdapter::processMatchedQuote(const SettlementMessage_MatchedQuote& msg }); } -bool QtGuiAdapter::processFailedQuote(const SettlementMessage_FailedQuote& msg) +bool QtGuiAdapter::processFailedSettl(const SettlementMessage_FailedSettlement& msg) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, msg] { + if (!msg.quote_id().empty() && !msg.rfq_id().empty()) { + mainWindow_->onQuoteFailed(msg.rfq_id(), msg.quote_id(), msg.info()); + } + else { + //TODO: another type of failure + } + }); +} + +bool QtGuiAdapter::processPendingSettl(const SettlementMessage_SettlementIds& msg) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, msg] { + mainWindow_->onSettlementPending(msg.rfq_id(), msg.quote_id() + , BinaryData::fromString(msg.settlement_id())); + }); +} + +bool QtGuiAdapter::processSettlComplete(const SettlementMessage_SettlementIds& msg) { return QMetaObject::invokeMethod(mainWindow_, [this, msg] { - mainWindow_->onQuoteFailed(msg.rfq_id(), msg.quote_id(), msg.info()); + mainWindow_->onSettlementComplete(msg.rfq_id(), msg.quote_id() + , BinaryData::fromString(msg.settlement_id())); }); } diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 182ef7eaf..35c156e2e 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -54,8 +54,9 @@ namespace BlockSettle { class Quote; class MatchingMessage_Order; class MktDataMessage_Prices; - class SettlementMessage_FailedQuote; + class SettlementMessage_FailedSettlement; class SettlementMessage_MatchedQuote; + class SettlementMessage_SettlementIds; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; class SettingsMessage_SignerServers; @@ -94,7 +95,6 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processBlockchain(const bs::message::Envelope &); bool processSigner(const bs::message::Envelope &); bool processWallets(const bs::message::Envelope &); - bool processAuthEid(const bs::message::Envelope &); bool processOnChainTrack(const bs::message::Envelope &); bool processAssets(const bs::message::Envelope&); @@ -138,7 +138,9 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processAuthKey(const BlockSettle::Common::WalletsMessage_AuthKey&); bool processQuote(const BlockSettle::Terminal::Quote&); bool processMatchedQuote(const BlockSettle::Terminal::SettlementMessage_MatchedQuote&); - bool processFailedQuote(const BlockSettle::Terminal::SettlementMessage_FailedQuote&); + bool processFailedSettl(const BlockSettle::Terminal::SettlementMessage_FailedSettlement&); + bool processPendingSettl(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); + bool processSettlComplete(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); bool sendPooledOrdersUpdate(); bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); diff --git a/common b/common index e0ca4a945..a5a6053ca 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e0ca4a945fa4d2f910d20605702394b69f5da31f +Subproject commit a5a6053cac36104866d1a54034489473b526602e From b7b16c8c51a84588c916aed8204c7f35d02ab3c9 Mon Sep 17 00:00:00 2001 From: Ation Date: Tue, 10 Nov 2020 16:29:15 +0200 Subject: [PATCH 035/146] Display total delivery for futures --- BlockSettleUILib/OrderListModel.cpp | 97 ++++++++++++++++++++++------- BlockSettleUILib/OrderListModel.h | 46 +++++++++++--- common | 2 +- 3 files changed, 115 insertions(+), 30 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 7ae03f165..b49ea93c7 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -129,6 +129,15 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const case Qt::DisplayRole : { if (index.column() == 0) { return g->security_; + } else if (index.column() == Header::Quantity) { + return g->getQuantity(); + } else { + return QVariant(); + } + } + case Qt::ForegroundRole : { + if (index.column() == Header::Quantity) { + return g->getQuantityColor(); } else { return QVariant(); } @@ -590,8 +599,7 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke // Create market if it doesn't exist. if (!marketItem) { beginInsertRows(sidx, static_cast(sg->rows_.size()), static_cast(sg->rows_.size())); - sg->rows_.push_back(make_unique( - tr(bs::network::Asset::toString(order.assetType)), &sg->idx_)); + sg->rows_.push_back(make_unique(order.assetType, &sg->idx_)); marketItem = sg->rows_.back().get(); endInsertRows(); } @@ -600,8 +608,15 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke if (!groupItem) { beginInsertRows(createIndex(findMarket(sg, marketItem), 0, &marketItem->idx_), static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - marketItem->rows_.push_back(make_unique( - QString::fromStdString(order.security), &marketItem->idx_)); + + if (getStatusGroup(order) == StatusGroup::UnSettled && order.assetType == bs::network::Asset::Type::Futures) { + marketItem->rows_.push_back(make_unique( + QString::fromStdString(order.security), &marketItem->idx_)); + } else { + marketItem->rows_.push_back(make_unique( + QString::fromStdString(order.security), &marketItem->idx_)); + } + groupItem = marketItem->rows_.back().get(); endInsertRows(); } @@ -710,23 +725,7 @@ void OrderListModel::onOrderUpdated(const bs::network::Order& order) if (found.second < 0) { beginInsertRows(parentIndex, 0, 0); - // As quantity is now could be negative need to invert value - double value = - order.quantity * order.price; - if (order.security.substr(0, order.security.find('/')) != order.product) { - value = order.quantity / order.price; - } - - groupItem->rows_.push_front(make_unique( - UiUtils::displayTimeMs(order.dateTime), - QString::fromStdString(order.product), - tr(bs::network::Side::toString(order.side)), - UiUtils::displayQty(order.quantity, order.security, order.product, order.assetType), - UiUtils::displayPriceForAssetType(order.price, order.assetType), - UiUtils::displayValue(value, order.security, order.product, order.assetType), - QString(), - order.exchOrderId, - &groupItem->idx_)); - + groupItem->addOrder(order); setOrderStatus(groupItem, 0, order); endInsertRows(); @@ -735,3 +734,59 @@ void OrderListModel::onOrderUpdated(const bs::network::Order& order) setOrderStatus(groupItem, found.second, order, true); } } + + +void OrderListModel::Group::addOrder(const bs::network::Order& order) +{ + addRow(order); +} + +QVariant OrderListModel::Group::getQuantity() const +{ + return QVariant{}; +} + +QVariant OrderListModel::Group::getQuantityColor() const +{ + return QVariant{}; +} + +void OrderListModel::Group::addRow(const bs::network::Order& order) +{ + // As quantity is now could be negative need to invert value + double value = - order.quantity * order.price; + if (order.security.substr(0, order.security.find('/')) != order.product) { + value = order.quantity / order.price; + } + + rows_.push_front(make_unique( + UiUtils::displayTimeMs(order.dateTime), + QString::fromStdString(order.product), + tr(bs::network::Side::toString(order.side)), + UiUtils::displayQty(order.quantity, order.security, order.product, order.assetType), + UiUtils::displayPriceForAssetType(order.price, order.assetType), + UiUtils::displayValue(value, order.security, order.product, order.assetType), + QString(), + order.exchOrderId, + &idx_)); +} + +void OrderListModel::FuturesGroup::addOrder(const bs::network::Order& order) +{ + quantity_ += order.quantity; + addRow(order); +} + +QVariant OrderListModel::FuturesGroup::getQuantity() const +{ + return UiUtils::displayAmount(quantity_); +} + +QVariant OrderListModel::FuturesGroup::getQuantityColor() const +{ + if (quantity_ < 0) { + return kFailedColor; + } + + return kSettledColor; +} diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 03592aa37..498446687 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -121,25 +121,55 @@ public slots: }; struct Group { - std::deque> rows_; - QString security_; - IndexHelper idx_; + std::deque> rows_; + QString security_; + IndexHelper idx_; Group(const QString &sec, IndexHelper *parent) : security_(sec) , idx_(parent, this, DataType::Group) { } + + virtual ~Group() = default; + + virtual void addOrder(const bs::network::Order& order); + + virtual QVariant getQuantity() const; + virtual QVariant getQuantityColor() const; + protected: + void addRow(const bs::network::Order& order); }; + struct FuturesGroup : public Group + { + FuturesGroup(const QString &sec, IndexHelper *parent) + : Group(sec, parent) + {} + + ~FuturesGroup() override = default; + + void addOrder(const bs::network::Order& order) override; + + QVariant getQuantity() const override; + QVariant getQuantityColor() const override; + + private: + double quantity_ = 0; + }; + + struct Market { std::vector> rows_; - QString name_; - IndexHelper idx_; - QFont font_; - Market(const QString &name, IndexHelper *parent) - : name_(name) + bs::network::Asset::Type assetType_; + QString name_; + IndexHelper idx_; + QFont font_; + + Market(const bs::network::Asset::Type assetType, IndexHelper *parent) + : assetType_{assetType} + , name_{tr(bs::network::Asset::toString(assetType))} , idx_(parent, this, DataType::Market) { font_.setBold(true); diff --git a/common b/common index c40312c48..0620c5b44 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit c40312c48c6e90e437abd613f3fa3660dbf3df12 +Subproject commit 0620c5b44460c96543e544ef3647c4f337a39fbf From d4762569f400335fc1648664073254bb2b8a7685 Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 13 Nov 2020 16:09:42 +0200 Subject: [PATCH 036/146] Display delivery obligations notifications --- BlockSettleUILib/BSTerminalMainWindow.cpp | 24 ++++++++++++++++++++++- BlockSettleUILib/BSTerminalMainWindow.h | 9 +++++++++ common | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index ec173adf0..29b79ce67 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -84,6 +84,8 @@ #include "ui_BSTerminalMainWindow.h" +#include "bs_proxy_terminal_pb.pb.h" + namespace { const auto kAutoLoginTimer = std::chrono::seconds(10); } @@ -2222,9 +2224,9 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli onBootstrapDataLoaded(result.bootstrapDataSigned); - connect(bsClient_.get(), &BsClient::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); connect(bsClient_.get(), &BsClient::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); connect(bsClient_.get(), &BsClient::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); + connect(bsClient_.get(), &BsClient::processPbMessage, this, &BSTerminalMainWindow::onMessageFromPB); // connect to RFQ dialog connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); @@ -2248,6 +2250,7 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli connect(ui_->widgetChat, &ChatWidget::emailHashRequested, bsClient_.get(), &BsClient::findEmailHash); connect(bsClient_.get(), &BsClient::emailHashReceived, ui_->widgetChat, &ChatWidget::onEmailHashReceived); + connect(bsClient_.get(), &BsClient::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); connect(bsClient_.get(), &BsClient::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); utxoReservationMgr_->setFeeRatePb(result.feeRatePb); @@ -2479,3 +2482,22 @@ void BSTerminalMainWindow::onAuthLeafCreated() } } } + +void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &message) +{ + if (message.data_case() == Blocksettle::Communication::ProxyTerminalPb::Response::kDeliveryRequest) { + const auto& request = message.delivery_request(); + + const int64_t toDeliver = request.to_deliver(); + QString deliverMessage; + const QString header = tr("Trade obligations"); + + if (toDeliver < 0) { + deliverMessage = tr("You have obligations to deliver %1 XBT in next 24h.").arg(UiUtils::displayAmount(bs::XBTAmount{static_cast(-toDeliver)})); + } else { + deliverMessage = tr("You are about to receive %1 XBT. Please make sure to deliver your cash obligations.").arg(UiUtils::displayAmount(bs::XBTAmount{ static_cast(toDeliver)})); + } + + showInfo(header, deliverMessage); + } +} diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index 4ebdf0020..44c2ebdeb 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -41,6 +41,14 @@ namespace bs { } } +namespace Blocksettle { + namespace Communication { + namespace ProxyTerminalPb { + class Response; + } + } +} + class QLockFile; struct BsClientLoginResult; @@ -252,6 +260,7 @@ private slots: void onBsConnectionDisconnected(); void onBsConnectionFailed(); + void onMessageFromPB(const ProxyTerminalPb::Response &response); void onInitWalletDialogWasShown(); diff --git a/common b/common index 0620c5b44..0ec99602e 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 0620c5b44460c96543e544ef3647c4f337a39fbf +Subproject commit 0ec99602ebfa4997c78d5572829480a40999b985 From 105e906a923a0e670927b96e2a5c4cccd575cab4 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 13 Nov 2020 20:55:54 +0300 Subject: [PATCH 037/146] WIP RFQ reply --- BlockSettleUILib/BSTerminalMainWindow.cpp | 2 +- BlockSettleUILib/StatusBarView.cpp | 2 +- .../Trading/DealerCCSettlementContainer.cpp | 4 +- .../Trading/DealerCCSettlementContainer.h | 2 +- .../Trading/DealerXBTSettlementContainer.cpp | 10 +- .../Trading/DealerXBTSettlementContainer.h | 2 +- .../Trading/QuoteRequestsModel.cpp | 54 ++--- BlockSettleUILib/Trading/QuoteRequestsModel.h | 6 +- .../Trading/QuoteRequestsWidget.cpp | 81 +++++++- .../Trading/QuoteRequestsWidget.h | 13 +- BlockSettleUILib/Trading/RFQBlotterTreeView.h | 2 +- BlockSettleUILib/Trading/RFQDealerReply.cpp | 189 +++++++++++++----- BlockSettleUILib/Trading/RFQDealerReply.h | 30 ++- BlockSettleUILib/Trading/RFQDialog.cpp | 15 +- BlockSettleUILib/Trading/RFQDialog.h | 10 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 125 ++++++++---- BlockSettleUILib/Trading/RFQReplyWidget.h | 41 ++-- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 8 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 2 +- .../Trading/ReqCCSettlementContainer.cpp | 7 +- .../Trading/ReqCCSettlementContainer.h | 2 +- .../Trading/ReqXBTSettlementContainer.cpp | 10 +- .../Trading/ReqXBTSettlementContainer.h | 2 +- .../Trading/SettlementContainer.cpp | 4 +- .../Trading/SettlementContainer.h | 4 +- BlockSettleUILib/UserScriptRunner.cpp | 3 +- Core/MatchingAdapter.cpp | 89 ++++++++- Core/MatchingAdapter.h | 2 +- Core/MessageUtils.cpp | 94 +++++++++ Core/MessageUtils.h | 8 + Core/SettlementAdapter.cpp | 43 ++++ Core/SettlementAdapter.h | 3 + GUI/QtWidgets/MainWindow.cpp | 17 +- GUI/QtWidgets/MainWindow.h | 5 + GUI/QtWidgets/QtGuiAdapter.cpp | 32 +++ GUI/QtWidgets/QtGuiAdapter.h | 5 + common | 2 +- 37 files changed, 732 insertions(+), 198 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index ac3a62c67..c1c27707c 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2034,7 +2034,7 @@ void BSTerminalMainWindow::InitWidgets() , assetManager_, dialogManager, signContainer_, armory_, autoSignRFQProvider_ , utxoReservationMgr_, orderListModel_.get()); ui_->widgetRFQReply->init(logger, celerConnection_, authManager_ - , quoteProvider, mdCallbacks_, assetManager_, applicationSettings_, dialogManager + , quoteProvider, mdCallbacks_, assetManager_, applicationSettings_ , signContainer_, armory_, connectionManager_, autoSignQuoteProvider_ , utxoReservationMgr_, orderListModel_.get()); diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index f60c11183..628b11d05 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -454,7 +454,7 @@ void StatusBarView::updateConnectionStatusDetails(ArmoryState state, unsigned in case ArmoryState::Ready: { auto lastBlockMinutes = (std::chrono::steady_clock::now() - timeSinceLastBlock_) / std::chrono::minutes(1); - auto tooltip = tr("Connected to DB (%1 blocks, last block updated %2 minute(s) ago)").arg(armory_->topBlock()).arg(lastBlockMinutes); + auto tooltip = tr("Connected to DB (%1 blocks, last block updated %2 minute(s) ago)").arg(blockNum_).arg(lastBlockMinutes); connectionStatusLabel_->setToolTip(tooltip); break; } diff --git a/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp index 38a4c788c..83bfb4e51 100644 --- a/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp @@ -36,10 +36,10 @@ DealerCCSettlementContainer::DealerCCSettlementContainer(const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &walletsMgr - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bs::UtxoReservationToken utxoRes , bool expandTxDialogInfo) - : bs::SettlementContainer(std::move(utxoRes), std::move(walletPurpose), expandTxDialogInfo) + : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) , logger_(logger) , order_(order) , lotSize_(lotSize) diff --git a/BlockSettleUILib/Trading/DealerCCSettlementContainer.h b/BlockSettleUILib/Trading/DealerCCSettlementContainer.h index 68711784a..a09a48b61 100644 --- a/BlockSettleUILib/Trading/DealerCCSettlementContainer.h +++ b/BlockSettleUILib/Trading/DealerCCSettlementContainer.h @@ -47,7 +47,7 @@ class DealerCCSettlementContainer : public bs::SettlementContainer , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &walletsMgr - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bs::UtxoReservationToken utxoRes , bool expandTxDialogInfo); ~DealerCCSettlementContainer() override; diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp index 92c3b29b0..2373356df 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp @@ -42,11 +42,11 @@ DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr , const std::vector &utxosPayinFixed , const bs::Address &recvAddr , const std::shared_ptr &utxoReservationManager - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bs::UtxoReservationToken utxoRes , bool expandTxDialogInfo , uint64_t tier1XbtLimit ) - : bs::SettlementContainer(std::move(utxoRes), std::move(walletPurpose), expandTxDialogInfo) + : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) , order_(order) , weSellXbt_((order.side == bs::network::Side::Buy) != (order.product == bs::network::XbtCurrency)) , amount_((order.product != bs::network::XbtCurrency) ? order.quantity / order.price : order.quantity) @@ -367,8 +367,7 @@ void DealerXBTSettlementContainer::onUnsignedPayinRequested(const std::string& s const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); if (!xbtWallet_->canMixLeaves()) { - assert(walletPurpose_); - const auto leaf = xbtGroup->getLeaf(*walletPurpose_); + const auto leaf = xbtGroup->getLeaf(walletPurpose_); args.inputXbtWallets.push_back(leaf); } else { @@ -437,8 +436,7 @@ void DealerXBTSettlementContainer::onSignedPayoutRequested(const std::string& se const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); if (!xbtWallet_->canMixLeaves()) { - assert(walletPurpose_); - const auto leaf = xbtGroup->getLeaf(*walletPurpose_); + const auto leaf = xbtGroup->getLeaf(walletPurpose_); args.outputXbtWallet = leaf; } else { diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h index 9b6031e66..f6118ade9 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h @@ -58,7 +58,7 @@ class DealerXBTSettlementContainer : public bs::SettlementContainer , const std::vector &utxosPayinFixed , const bs::Address &recvAddr , const std::shared_ptr &utxoReservationManager - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bs::UtxoReservationToken utxoRes , bool expandTxDialogInfo , uint64_t tier1XbtLimit); diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index 0a1de34f4..223800fa0 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -46,6 +46,22 @@ QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptr& statsCollector + , QObject* parent) + : QAbstractItemModel(parent), secStatsCollector_(statsCollector) +{ + timer_.setInterval(500); + connect(&timer_, &QTimer::timeout, this, &QuoteRequestsModel::ticker); + timer_.start(); + + connect(&priceUpdateTimer_, &QTimer::timeout, this, &QuoteRequestsModel::onPriceUpdateTimer); + + setPriceUpdateInterval(-1); //FIXME + + connect(this, &QuoteRequestsModel::deferredUpdate, + this, &QuoteRequestsModel::onDeferredUpdate, Qt::QueuedConnection); +} + QuoteRequestsModel::~QuoteRequestsModel() { secStatsCollector_->saveState(); @@ -611,7 +627,8 @@ void QuoteRequestsModel::ticker() { pendingDeleteIds_.clear(); for (auto qrn : notifications_) { - const auto timeDiff = timeNow.msecsTo(qrn.second.expirationTime.addMSecs(qrn.second.timeSkewMs)); + const auto& expTime = QDateTime::fromMSecsSinceEpoch(qrn.second.expirationTime); + const auto timeDiff = timeNow.msecsTo(expTime.addMSecs(qrn.second.timeSkewMs)); if ((timeDiff < 0) || (qrn.second.status == bs::network::QuoteReqNotification::Withdrawn)) { forSpecificId(qrn.second.quoteRequestId, [this](Group *grp, int itemIndex) { const auto row = findGroup(&grp->idx_); @@ -794,13 +811,12 @@ void QuoteRequestsModel::onQuoteReqNotifReplied(const bs::network::QuoteNotifica g = group; const auto assetType = group->rfqs_[static_cast(i)]->assetType_; - const double quotedPrice = (qn.side == bs::network::Side::Buy) ? qn.bidPx : qn.offerPx; group->rfqs_[static_cast(i)]->quotedPriceString_ = - UiUtils::displayPriceForAssetType(quotedPrice, assetType); - group->rfqs_[static_cast(i)]->quotedPrice_ = quotedPrice; + UiUtils::displayPriceForAssetType(qn.price, assetType); + group->rfqs_[static_cast(i)]->quotedPrice_ = qn.price; group->rfqs_[static_cast(i)]->quotedPriceBrush_ = - colorForQuotedPrice(quotedPrice, group->rfqs_[static_cast(i)]->bestQuotedPx_); + colorForQuotedPrice(qn.price, group->rfqs_[static_cast(i)]->bestQuotedPx_); }); if (withdrawn) { @@ -840,7 +856,7 @@ void QuoteRequestsModel::onQuoteReqNotifReceived(const bs::network::QuoteReqNoti beginInsertRows(QModelIndex(), static_cast(data_.size()), static_cast(data_.size())); data_.push_back(std::unique_ptr(new Market(marketName, - appSettings_->get(UiUtils::limitRfqSetting(qrn.assetType))))); + appSettings_ ? appSettings_->get(UiUtils::limitRfqSetting(qrn.assetType)) : 5))); market = data_.back().get(); endInsertRows(); } @@ -869,7 +885,6 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti auto itQRN = notifications_.find(qrn.quoteRequestId); if (itQRN == notifications_.end()) { - const auto assetType = assetManager_->GetAssetTypeForSecurity(qrn.security); const CurrencyPair cp(qrn.security); const bool isBid = (qrn.side == bs::network::Side::Buy) ^ (cp.NumCurrency() == qrn.product); const double indicPrice = isBid ? mdPrices_[qrn.security][Role::BidPrice] : @@ -880,25 +895,16 @@ void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNoti static_cast(group->rfqs_.size())); group->rfqs_.push_back(std::unique_ptr(new RFQ(QString::fromStdString(qrn.security), - QString::fromStdString(qrn.product), - tr(bs::network::Side::toString(qrn.side)), - QString(), - (qrn.assetType == bs::network::Asset::Type::PrivateMarket) ? - UiUtils::displayCCAmount(qrn.quantity) : UiUtils::displayQty(qrn.quantity, qrn.product), - QString(), - (!qFuzzyIsNull(indicPrice) ? UiUtils::displayPriceForAssetType(indicPrice, assetType) - : QString()), - QString(), - { - quoteReqStatusDesc(qrn.status), + QString::fromStdString(qrn.product), tr(bs::network::Side::toString(qrn.side)) + , QString(), (qrn.assetType == bs::network::Asset::Type::PrivateMarket) ? + UiUtils::displayCCAmount(qrn.quantity) : UiUtils::displayQty(qrn.quantity, qrn.product) + , QString(), !qFuzzyIsNull(indicPrice) + ? UiUtils::displayPriceForAssetType(indicPrice, qrn.assetType) : QString() + , QString(), { quoteReqStatusDesc(qrn.status), ((qrn.status == bs::network::QuoteReqNotification::Status::PendingAck) || (qrn.status == bs::network::QuoteReqNotification::Status::Replied)), - 30000 - }, - indicPrice, 0.0, 0.0, - qrn.side, - assetType, - qrn.quoteRequestId))); + 30000 } + , indicPrice, 0.0, 0.0, qrn.side, qrn.assetType, qrn.quoteRequestId))); group->rfqs_.back()->idx_.parent_ = &group->idx_; diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h index 5ca25805b..3eb898345 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.h @@ -93,10 +93,12 @@ class QuoteRequestsModel : public QAbstractItemModel }; public: - QuoteRequestsModel(const std::shared_ptr & + [[deprecated]] QuoteRequestsModel(const std::shared_ptr & , std::shared_ptr celerClient , std::shared_ptr appSettings , QObject* parent); + QuoteRequestsModel(const std::shared_ptr& + , QObject* parent); ~QuoteRequestsModel() override; QuoteRequestsModel(const QuoteRequestsModel&) = delete; @@ -104,7 +106,7 @@ class QuoteRequestsModel : public QAbstractItemModel QuoteRequestsModel(QuoteRequestsModel&&) = delete; QuoteRequestsModel& operator=(QuoteRequestsModel&&) = delete; - void SetAssetManager(const std::shared_ptr& assetManager); + [[deprecated]] void SetAssetManager(const std::shared_ptr& assetManager); const bs::network::QuoteReqNotification &getQuoteReqNotification(const std::string &id) const; double getPrice(const std::string &security, Role) const; QString getMarketSecurity(const QModelIndex &index); diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp index e336f7767..20456194c 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp @@ -139,6 +139,62 @@ void QuoteRequestsWidget::init(std::shared_ptr logger width); } +void QuoteRequestsWidget::init(const std::shared_ptr&logger + , const std::shared_ptr& statsCollector) +{ + logger_ = logger; + + dropQN_ = false; //FIXME + + model_ = new QuoteRequestsModel(statsCollector, ui_->treeViewQuoteRequests); + + sortModel_ = new QuoteReqSortModel(model_, this); + sortModel_->setSourceModel(model_); + sortModel_->showQuoted(true); //FIXME + + ui_->treeViewQuoteRequests->setModel(sortModel_); + ui_->treeViewQuoteRequests->setRfqModel(model_); + ui_->treeViewQuoteRequests->setSortModel(sortModel_); + ui_->treeViewQuoteRequests->header()->setSectionResizeMode( + static_cast(QuoteRequestsModel::Column::SecurityID), + QHeaderView::ResizeToContents); + + connect(ui_->treeViewQuoteRequests, &QTreeView::collapsed, + this, &QuoteRequestsWidget::onCollapsed); + connect(ui_->treeViewQuoteRequests, &QTreeView::expanded, + this, &QuoteRequestsWidget::onExpanded); + connect(model_, &QuoteRequestsModel::quoteReqNotifStatusChanged, [this](const bs::network::QuoteReqNotification& qrn) { + emit quoteReqNotifStatusChanged(qrn); + }); + connect(model_, &QAbstractItemModel::rowsInserted, this, &QuoteRequestsWidget::onRowsInserted); + connect(model_, &QAbstractItemModel::rowsRemoved, this, &QuoteRequestsWidget::onRowsRemoved); + connect(sortModel_, &QSortFilterProxyModel::rowsInserted, this, &QuoteRequestsWidget::onRowsChanged); + connect(sortModel_, &QSortFilterProxyModel::rowsRemoved, this, &QuoteRequestsWidget::onRowsChanged); + + ui_->treeViewQuoteRequests->setItemDelegateForColumn( + static_cast(QuoteRequestsModel::Column::Status), new RequestsProgressDelegate(ui_->treeViewQuoteRequests)); + + auto* doNotDrawSelectionDelegate = new DoNotDrawSelectionDelegate(ui_->treeViewQuoteRequests); + ui_->treeViewQuoteRequests->setItemDelegateForColumn( + static_cast(QuoteRequestsModel::Column::QuotedPx), + doNotDrawSelectionDelegate); + ui_->treeViewQuoteRequests->setItemDelegateForColumn( + static_cast(QuoteRequestsModel::Column::IndicPx), + doNotDrawSelectionDelegate); + ui_->treeViewQuoteRequests->setItemDelegateForColumn( + static_cast(QuoteRequestsModel::Column::BestPx), + doNotDrawSelectionDelegate); + ui_->treeViewQuoteRequests->setItemDelegateForColumn( + static_cast(QuoteRequestsModel::Column::Empty), + doNotDrawSelectionDelegate); + + const auto opt = ui_->treeViewQuoteRequests->viewOptions(); + const int width = opt.fontMetrics.boundingRect(tr("No quote received")).width() + 10; + ui_->treeViewQuoteRequests->header()->resizeSection( + static_cast(QuoteRequestsModel::Column::Status), + width); +} + void QuoteRequestsWidget::onQuoteReqNotifSelected(const QModelIndex& index) { const auto quoteIndex = sortModel_->index(index.row(), 0, index.parent()); @@ -349,7 +405,12 @@ void QuoteRequestsWidget::onExpanded(const QModelIndex &index) void QuoteRequestsWidget::saveCollapsedState() { - appSettings_->set(ApplicationSettings::Filter_MD_QN, collapsed_); + if (appSettings_) { + appSettings_->set(ApplicationSettings::Filter_MD_QN, collapsed_); + } + else { + emit putSetting(ApplicationSettings::Filter_MD_QN, collapsed_); + } } void QuoteRequestsWidget::expandIfNeeded(const QModelIndex &index) @@ -361,7 +422,8 @@ void QuoteRequestsWidget::expandIfNeeded(const QModelIndex &index) expandIfNeeded(sortModel_->index(i, 0, index)); } -bs::SecurityStatsCollector::SecurityStatsCollector(const std::shared_ptr appSettings, ApplicationSettings::Setting param) +bs::SecurityStatsCollector::SecurityStatsCollector(const std::shared_ptr appSettings + , ApplicationSettings::Setting param) : appSettings_(appSettings), param_(param) { const auto map = appSettings_->get(param); @@ -378,6 +440,17 @@ bs::SecurityStatsCollector::SecurityStatsCollector(const std::shared_ptrset(param_, map); + if (appSettings_) { + appSettings_->set(param_, map); + } modified_ = false; } diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.h b/BlockSettleUILib/Trading/QuoteRequestsWidget.h index de6918776..c929b6d00 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.h +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.h @@ -52,7 +52,9 @@ namespace bs { { Q_OBJECT public: - SecurityStatsCollector(const std::shared_ptr, ApplicationSettings::Setting param); + [[deprecated]] SecurityStatsCollector(const std::shared_ptr + , ApplicationSettings::Setting param); + SecurityStatsCollector(ApplicationSettings::Setting param); QColor getColorFor(const std::string &key) const override; unsigned int getGradeFor(const std::string &key) const override; @@ -123,13 +125,15 @@ Q_OBJECT QuoteRequestsWidget(QWidget* parent = nullptr); ~QuoteRequestsWidget() override; - void init(std::shared_ptr logger, const std::shared_ptr "eProvider + [[deprecated]] void init(std::shared_ptr logger, const std::shared_ptr "eProvider , const std::shared_ptr& assetManager , const std::shared_ptr &statsCollector , const std::shared_ptr &appSettings , std::shared_ptr celerClient); + void init(const std::shared_ptr& + , const std::shared_ptr& statsCollector); - void addSettlementContainer(const std::shared_ptr &); + [[deprecated]] void addSettlementContainer(const std::shared_ptr &); bool StartCCSignOnOrder(const QString& orderId, QDateTime timestamp); RFQBlotterTreeView* view() const; @@ -137,8 +141,10 @@ Q_OBJECT signals: void Selected(const QString& productGroup, const bs::network::QuoteReqNotification& qrc, double indicBid, double indicAsk); void quoteReqNotifStatusChanged(const bs::network::QuoteReqNotification &qrn); + void putSetting(ApplicationSettings::Setting, const QVariant&); public slots: + void onQuoteRequest(const bs::network::QuoteReqNotification& qrn); void onQuoteReqNotifReplied(const bs::network::QuoteNotification &); void onQuoteReqNotifSelected(const QModelIndex& index); void onQuoteNotifCancelled(const QString &reqId); @@ -150,7 +156,6 @@ public slots: private slots: void onSettingChanged(int setting, QVariant val); - void onQuoteRequest(const bs::network::QuoteReqNotification &qrn); void onRowsChanged(); void onRowsInserted(const QModelIndex &parent, int first, int last); void onRowsRemoved(const QModelIndex &parent, int first, int last); diff --git a/BlockSettleUILib/Trading/RFQBlotterTreeView.h b/BlockSettleUILib/Trading/RFQBlotterTreeView.h index b726ef2ab..f7d321ae6 100644 --- a/BlockSettleUILib/Trading/RFQBlotterTreeView.h +++ b/BlockSettleUILib/Trading/RFQBlotterTreeView.h @@ -36,7 +36,7 @@ class RFQBlotterTreeView : public TreeViewWithEnterKey void setRfqModel(QuoteRequestsModel *model); void setSortModel(QuoteReqSortModel *model); - void setAppSettings(std::shared_ptr appSettings); + [[deprecated]] void setAppSettings(std::shared_ptr appSettings); void setLimit(ApplicationSettings::Setting s, int limit); diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index 7d8661e02..2215492a5 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -106,6 +106,11 @@ void RFQDealerReply::init(const std::shared_ptr logger this, &RFQDealerReply::onUTXOReservationChanged); } +void bs::ui::RFQDealerReply::init(const std::shared_ptr& logger) +{ + logger_ = logger; +} + void RFQDealerReply::initUi() { invalidBalanceFont_ = ui_->labelBalanceValue->font(); @@ -203,11 +208,10 @@ void RFQDealerReply::reset() baseProduct_ = cp.NumCurrency(); product_ = cp.ContraCurrency(currentQRN_.product); - const auto assetType = assetManager_->GetAssetTypeForSecurity(currentQRN_.security); - if (assetType == bs::network::Asset::Type::Undefined) { + if (currentQRN_.assetType == bs::network::Asset::Type::Undefined) { logger_->error("[RFQDealerReply::reset] could not get asset type for {}", currentQRN_.security); } - const auto priceDecimals = UiUtils::GetPricePrecisionForAssetType(assetType); + const auto priceDecimals = UiUtils::GetPricePrecisionForAssetType(currentQRN_.assetType); ui_->spinBoxBidPx->setDecimals(priceDecimals); ui_->spinBoxOfferPx->setDecimals(priceDecimals); ui_->spinBoxBidPx->setSingleStep(std::pow(10, -priceDecimals)); @@ -261,7 +265,8 @@ void RFQDealerReply::quoteReqNotifStatusChanged(const bs::network::QuoteReqNotif refreshSettlementDetails(); } -void RFQDealerReply::setQuoteReqNotification(const bs::network::QuoteReqNotification &qrn, double indicBid, double indicAsk) +void RFQDealerReply::setQuoteReqNotification(const bs::network::QuoteReqNotification &qrn + , double indicBid, double indicAsk) { indicBid_ = indicBid; indicAsk_ = indicAsk; @@ -347,7 +352,7 @@ void RFQDealerReply::getAddress(const std::string "eRequestId, const std::sh void RFQDealerReply::updateUiWalletFor(const bs::network::QuoteReqNotification &qrn) { - if (armory_->state() != ArmoryState::Ready) { + if (armory_ && (armory_->state() != ArmoryState::Ready)) { return; } if (qrn.assetType == bs::network::Asset::PrivateMarket) { @@ -373,10 +378,11 @@ void RFQDealerReply::updateUiWalletFor(const bs::network::QuoteReqNotification & } } } - updateWalletsList((qrn.side == bs::network::Side::Sell) ? UiUtils::WalletsTypes::Full : UiUtils::WalletsTypes::All); } else if (qrn.assetType == bs::network::Asset::SpotXBT) { - updateWalletsList((qrn.side == bs::network::Side::Sell) ? (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW) : UiUtils::WalletsTypes::All); + updateWalletsList((qrn.side == bs::network::Side::Sell) + ? (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW) + : UiUtils::WalletsTypes::All); } } @@ -424,7 +430,7 @@ void RFQDealerReply::updateSubmitButton() updateBalanceLabel(); bool isQRNRepliable = (!currentQRN_.empty() && QuoteProvider::isRepliableStatus(currentQRN_.status)); if ((currentQRN_.assetType != bs::network::Asset::SpotFX) - && (!signingContainer_ || signingContainer_->isOffline())) { + && signingContainer_ && signingContainer_->isOffline()) { isQRNRepliable = false; } @@ -450,12 +456,7 @@ void RFQDealerReply::updateSubmitButton() return; } - if (!assetManager_) { - ui_->pushButtonSubmit->setEnabled(false); - return; - } - - const bool isBalanceOk = checkBalance(); + const bool isBalanceOk = assetManager_ ? checkBalance() : true; ui_->pushButtonSubmit->setEnabled(isBalanceOk); setBalanceOk(isBalanceOk); } @@ -569,16 +570,13 @@ std::shared_ptr RFQDealerReply::getSelectedXbtWallet(Reply if (!walletsManager_) { return nullptr; } - if (replyType == ReplyType::Script) { - return walletsManager_->getPrimaryWallet(); - } - return walletsManager_->getHDWalletById(ui_->comboBoxXbtWallet->currentData(UiUtils::WalletIdRole).toString().toStdString()); + return walletsManager_->getHDWalletById(getSelectedXbtWalletId(replyType)); } bs::Address RFQDealerReply::selectedAuthAddress(ReplyType replyType) const { - if (replyType == ReplyType::Script) { - authAddressManager_->getDefault(); + if (authAddressManager_ && (replyType == ReplyType::Script)) { + return authAddressManager_->getDefault(); } return authAddr_; } @@ -615,7 +613,38 @@ void bs::ui::RFQDealerReply::onParentAboutToHide() selectedXbtRes_.release(); } -void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, double price, ReplyType replyType) +void bs::ui::RFQDealerReply::onHDWallet(const bs::sync::HDWalletData& wallet) +{ + const auto& it = std::find_if(wallets_.cbegin(), wallets_.cend() + , [wallet](const bs::sync::HDWalletData& w) { return (wallet.id == w.id); }); + if (it == wallets_.end()) { + wallets_.push_back(wallet); + } else { + wallets_.emplace(it, wallet); + } + +} + +void bs::ui::RFQDealerReply::onBalance(const std::string& currency, double balance) +{ + balances_[currency] = balance; +} + +void bs::ui::RFQDealerReply::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + balances_[wbd.id] = wbd.balSpendable; +} + +void bs::ui::RFQDealerReply::onAuthKey(const bs::Address& addr, const BinaryData& authKey) +{ + if (addr == authAddr_) { + logger_->debug("[{}] got auth key: {}", __func__, authKey.toHexStr()); + authKey_ = authKey.toHexStr(); + } +} + +void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn + , double price, ReplyType replyType) { if (qFuzzyIsNull(price)) { SPDLOG_LOGGER_ERROR(logger_, "invalid price"); @@ -632,15 +661,16 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d replyData->qn = bs::network::QuoteNotification(qrn, authKey_, price, ""); if (qrn.assetType != bs::network::Asset::SpotFX) { - replyData->xbtWallet = getSelectedXbtWallet(replyType); - if (!replyData->xbtWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't submit CC/XBT reply without XBT wallet"); - return; + replyData->walletPurpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet);; + if (walletsManager_) { + replyData->xbtWallet = getSelectedXbtWallet(replyType); + if (!replyData->xbtWallet) { + SPDLOG_LOGGER_ERROR(logger_, "can't submit CC/XBT reply without XBT wallet"); + return; + } } - - if (!replyData->xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - replyData->walletPurpose.reset(new bs::hd::Purpose(purpose)); + else { + replyData->xbtWalletId = getSelectedXbtWalletId(replyType); } } @@ -651,7 +681,13 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d return; } - auto minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); + bs::XBTAmount minXbtAmount; + if (utxoReservationManager_) { + minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); + } + else { + minXbtAmount = bs::tradeutils::minXbtAmount(1); //FIXME: should populate PB fee rate somehow + } auto xbtAmount = XBTAmount(qrn.product == bs::network::XbtCurrency ? qrn.quantity : qrn.quantity / price); if (xbtAmount.GetValue() < minXbtAmount.GetValue()) { SPDLOG_LOGGER_ERROR(logger_, "XBT amount is too low to cover network fee: {}, min. amount: {}" @@ -662,7 +698,8 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d auto it = activeQuoteSubmits_.find(replyData->qn.quoteRequestId); if (it != activeQuoteSubmits_.end()) { - SPDLOG_LOGGER_ERROR(logger_, "quote submit already active for quote request '{}'", replyData->qn.quoteRequestId); + SPDLOG_LOGGER_ERROR(logger_, "quote submit already active for quote request '{}'" + , replyData->qn.quoteRequestId); return; } activeQuoteSubmits_.insert(replyData->qn.quoteRequestId); @@ -832,7 +869,13 @@ void RFQDealerReply::updateWalletsList(int walletsFlags) { auto oldWalletId = UiUtils::getSelectedWalletId(ui_->comboBoxXbtWallet); auto oldType = UiUtils::getSelectedWalletType(ui_->comboBoxXbtWallet); - int defaultIndex = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXbtWallet, walletsManager_, walletsFlags); + int defaultIndex = 0; + if (walletsManager_) { + defaultIndex = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXbtWallet, walletsManager_, walletsFlags); + } + else { + defaultIndex = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXbtWallet, wallets_, walletsFlags); + } int oldIndex = UiUtils::selectWalletInCombobox(ui_->comboBoxXbtWallet, oldWalletId, oldType); if (oldIndex < 0) { ui_->comboBoxXbtWallet->setCurrentIndex(defaultIndex); @@ -847,6 +890,20 @@ bool RFQDealerReply::isXbtSpend() const return isXbtSpend; } +std::string RFQDealerReply::getSelectedXbtWalletId(ReplyType replyType) const +{ + std::string walletId; + if (replyType == ReplyType::Manual) { + walletId = ui_->comboBoxXbtWallet->currentData(UiUtils::WalletIdRole).toString().toStdString(); + } + else { + if (walletsManager_) { + walletId = walletsManager_->getPrimaryWallet()->walletId(); + } //new code doesn't support scripting in the GUI + } + return walletId; +} + void RFQDealerReply::onReservedUtxosChanged(const std::string &walletId, const std::vector &utxos) { onTransactionDataChanged(); @@ -1114,7 +1171,8 @@ void bs::ui::RFQDealerReply::onUTXOReservationChanged(const std::string& walletI void bs::ui::RFQDealerReply::submit(double price, const std::shared_ptr& replyData) { - SPDLOG_LOGGER_DEBUG(logger_, "submitted quote reply on {}: {}/{}", replyData->qn.quoteRequestId, replyData->qn.bidPx, replyData->qn.offerPx); + SPDLOG_LOGGER_DEBUG(logger_, "submitted quote reply on {}: {}" + , replyData->qn.quoteRequestId, replyData->qn.price); sentNotifs_[replyData->qn.quoteRequestId] = price; submitQuoteNotifCb_(replyData); activeQuoteSubmits_.erase(replyData->qn.quoteRequestId); @@ -1265,6 +1323,10 @@ void bs::ui::RFQDealerReply::updateBalanceLabel() .arg(UiUtils::displayCurrencyAmount(assetManager_->getBalance(product_))) .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); } + else { + totalBalance = tr("%1 %2").arg(UiUtils::displayCurrencyAmount(balances_.at(product_))) + .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); + } } ui_->labelBalanceValue->setText(totalBalance); @@ -1282,27 +1344,60 @@ bs::XBTAmount RFQDealerReply::getXbtBalance() const return bs::XBTAmount(sum); } - auto xbtWallet = getSelectedXbtWallet(ReplyType::Manual); - if (!xbtWallet) { - return {}; - } - - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId(), purpose)); + if (walletsManager_) { + auto xbtWallet = getSelectedXbtWallet(ReplyType::Manual); + if (!xbtWallet) { + return {}; + } + if (!xbtWallet->canMixLeaves()) { + auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); + return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( + xbtWallet->walletId(), purpose)); + } else { + return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( + xbtWallet->walletId())); + } } else { - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId())); + const auto& xbtWalletId = getSelectedXbtWalletId(ReplyType::Manual); + if (xbtWalletId.empty()) { // no wallet selected + return {}; + } + //TODO: distinguish between HW and SW wallets later + double balance = 0; + for (const auto& wallet : wallets_) { + if (wallet.id == xbtWalletId) { + for (const auto& group : wallet.groups) { + switch (group.type) { + case bs::hd::CoinType::Bitcoin_main: + case bs::hd::CoinType::Bitcoin_test: + for (const auto& leaf : group.leaves) { + for (const auto& id : leaf.ids) { + balance += balances_.at(id); + } + } + break; + default: break; + } + } + break; + } + } + return bs::XBTAmount{balance}; } } BTCNumericTypes::balance_type bs::ui::RFQDealerReply::getPrivateMarketCoinBalance() const { - auto ccWallet = getCCWallet(currentQRN_.product); - if (!ccWallet) { + if (walletsManager_) { + auto ccWallet = getCCWallet(currentQRN_.product); + if (!ccWallet) { + return 0; + } + return ccWallet->getSpendableBalance(); + } + else { + //TODO return 0; } - return ccWallet->getSpendableBalance(); } diff --git a/BlockSettleUILib/Trading/RFQDealerReply.h b/BlockSettleUILib/Trading/RFQDealerReply.h index 4b0f4c7f3..6bd7b6f40 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.h +++ b/BlockSettleUILib/Trading/RFQDealerReply.h @@ -77,9 +77,10 @@ namespace bs { bs::network::QuoteNotification qn; bs::UtxoReservationToken utxoRes; std::shared_ptr xbtWallet; + std::string xbtWalletId; bs::Address authAddr; std::vector fixedXbtInputs; - std::unique_ptr walletPurpose; + bs::hd::Purpose walletPurpose; }; class RFQDealerReply : public QWidget @@ -90,7 +91,7 @@ namespace bs { RFQDealerReply(QWidget* parent = nullptr); ~RFQDealerReply() override; - void init(const std::shared_ptr logger + [[deprecated]] void init(const std::shared_ptr logger , const std::shared_ptr & , const std::shared_ptr& assetManager , const std::shared_ptr& quoteProvider @@ -100,8 +101,9 @@ namespace bs { , const std::shared_ptr & , const std::shared_ptr &autoSignQuoteProvider , const std::shared_ptr& utxoReservationManager); + void init(const std::shared_ptr&); - void setWalletsManager(const std::shared_ptr &); + [[deprecated]] void setWalletsManager(const std::shared_ptr &); CustomDoubleSpinBox* bidSpinBox() const; CustomDoubleSpinBox* offerSpinBox() const; @@ -120,8 +122,16 @@ namespace bs { void onParentAboutToHide(); + void onHDWallet(const bs::sync::HDWalletData&); + void onBalance(const std::string& currency, double balance); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onAuthKey(const bs::Address&, const BinaryData& authKey); + signals: void pullQuoteNotif(const std::string& settlementId, const std::string& reqId, const std::string& reqSessToken); + void needAuthKey(const bs::Address&); + void needReserveUTXOs(const std::string& reserveId, const std::string& subId + , uint64_t amount, bool partial = false, const std::vector& utxos = {}); public slots: void setQuoteReqNotification(const network::QuoteReqNotification &, double indicBid, double indicAsk); @@ -202,7 +212,7 @@ namespace bs { enum class ReplyType { Manual, - Script, + Script, //obsoleted, as we won't support scripting in the GUI }; enum class AddressType @@ -222,9 +232,9 @@ namespace bs { double getPrice() const; double getValue() const; double getAmount() const; - std::shared_ptr getCCWallet(const std::string &cc) const; - std::shared_ptr getCCWallet(const bs::network::QuoteReqNotification &qrn) const; - void getAddress(const std::string "eRequestId, const std::shared_ptr &wallet + [[deprecated]] std::shared_ptr getCCWallet(const std::string &cc) const; + [[deprecated]] std::shared_ptr getCCWallet(const bs::network::QuoteReqNotification &qrn) const; + [[deprecated]] void getAddress(const std::string "eRequestId, const std::shared_ptr &wallet , AddressType type, std::function cb); void setBalanceOk(bool ok); bool checkBalance() const; @@ -236,7 +246,8 @@ namespace bs { void submitReply(const network::QuoteReqNotification &qrn, double price, ReplyType replyType); void updateWalletsList(int walletsFlags); bool isXbtSpend() const; - std::shared_ptr getSelectedXbtWallet(ReplyType replyType) const; + std::string getSelectedXbtWalletId(ReplyType replyType) const; + [[deprecated]] std::shared_ptr getSelectedXbtWallet(ReplyType replyType) const; bs::Address selectedAuthAddress(ReplyType replyType) const; std::vector selectedXbtInputs(ReplyType replyType) const; void submit(double price, const std::shared_ptr& replyData); @@ -247,6 +258,9 @@ namespace bs { std::set activeQuoteSubmits_; std::map(AddressType::Max) + 1>>> addresses_; + + std::vector wallets_; + std::unordered_map balances_; }; } //namespace ui diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index c95ac35dd..5d7187563 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -44,7 +44,7 @@ RFQDialog::RFQDialog(const std::shared_ptr &logger , const std::map &fixedXbtInputs , bs::UtxoReservationToken fixedXbtUtxoRes , bs::UtxoReservationToken ccUtxoRes - , std::unique_ptr purpose + , bs::hd::Purpose purpose , RFQRequestWidget *parent) : QDialog(parent) , ui_(new Ui::RFQDialog()) @@ -67,7 +67,7 @@ RFQDialog::RFQDialog(const std::shared_ptr &logger , requestWidget_(parent) , utxoReservationManager_(utxoReservationManager) , ccUtxoRes_(std::move(ccUtxoRes)) - , walletPurpose_(std::move(purpose)) + , walletPurpose_(purpose) { ui_->setupUi(this); @@ -102,7 +102,7 @@ RFQDialog::RFQDialog(const std::shared_ptr& logger , const std::string& id, const bs::network::RFQ& rfq , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet , const bs::Address& authAddr - , std::unique_ptr purpose + , bs::hd::Purpose purpose , RFQRequestWidget* parent) : QDialog(parent) , ui_(new Ui::RFQDialog()) @@ -111,7 +111,7 @@ RFQDialog::RFQDialog(const std::shared_ptr& logger , recvXbtAddrIfSet_(recvXbtAddrIfSet) , authAddr_(authAddr) , requestWidget_(parent) - , walletPurpose_(std::move(purpose)) + , walletPurpose_(purpose) { ui_->setupUi(this); @@ -223,7 +223,7 @@ std::shared_ptr RFQDialog::newXBTcontainer() xbtSettlContainer_ = std::make_shared(logger_ , authAddressManager_, signContainer_, armory_, xbtWallet_, walletsManager_ , rfq_, quote_, authAddr_, fixedXbtInputs_, std::move(fixedXbtUtxoRes_), utxoReservationManager_ - , std::move(walletPurpose_), recvXbtAddrIfSet_, expandTxInfo, tier1XbtLimit); + , walletPurpose_, recvXbtAddrIfSet_, expandTxInfo, tier1XbtLimit); connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::settlementAccepted , this, &RFQDialog::onXBTSettlementAccepted); @@ -268,8 +268,9 @@ std::shared_ptr RFQDialog::newCCcontainer() try { ccSettlContainer_ = std::make_shared(logger_ - , signContainer_, armory_, assetMgr_, walletsManager_, rfq_, quote_, xbtWallet_, - fixedXbtInputs_, utxoReservationManager_, std::move(walletPurpose_), std::move(ccUtxoRes_), expandTxInfo); + , signContainer_, armory_, assetMgr_, walletsManager_, rfq_, quote_ + , xbtWallet_, fixedXbtInputs_, utxoReservationManager_, walletPurpose_ + , std::move(ccUtxoRes_), expandTxInfo); connect(ccSettlContainer_.get(), &ReqCCSettlementContainer::txSigned , this, &RFQDialog::onCCTxSigned); diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index 8a1a131dc..a92b72ee3 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -74,13 +74,13 @@ Q_OBJECT , const std::map &fixedXbtInputs , bs::UtxoReservationToken fixedXbtUtxoRes , bs::UtxoReservationToken ccUtxoRes - , std::unique_ptr purpose + , bs::hd::Purpose purpose , RFQRequestWidget* parent = nullptr); RFQDialog(const std::shared_ptr& logger , const std::string& id, const bs::network::RFQ& rfq , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet , const bs::Address& authAddr - , std::unique_ptr purpose + , bs::hd::Purpose purpose , RFQRequestWidget* parent = nullptr); ~RFQDialog() override; @@ -163,9 +163,9 @@ private slots: RFQRequestWidget *requestWidget_{}; - QString ccOrderId_; - bs::UtxoReservationToken ccUtxoRes_; - std::unique_ptr walletPurpose_; + QString ccOrderId_; + bs::UtxoReservationToken ccUtxoRes_; + bs::hd::Purpose walletPurpose_; }; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 31d2d9c4b..3a9eb6980 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -58,10 +58,11 @@ RFQReplyWidget::RFQReplyWidget(QWidget* parent) connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::quoteReqNotifStatusChanged, ui_->pageRFQReply , &RFQDealerReply::quoteReqNotifStatusChanged, Qt::QueuedConnection); + connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::putSetting, this, &RFQReplyWidget::putSetting); + connect(ui_->shieldPage, &RFQShieldPage::requestPrimaryWalletCreation, this, &RFQReplyWidget::requestPrimaryWalletCreation); - ui_->shieldPage->showShieldLoginToResponseRequired(); popShield(); } @@ -126,6 +127,36 @@ void RFQReplyWidget::shortcutActivated(ShortcutType s) } } +void RFQReplyWidget::onMatchingLogout() +{ + userType_ = bs::network::UserType::Undefined; +} + +void RFQReplyWidget::onBalance(const std::string& currency, double balance) +{ + ui_->pageRFQReply->onBalance(currency, balance); +} + +void RFQReplyWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + ui_->pageRFQReply->onWalletBalance(wbd); +} + +void RFQReplyWidget::onHDWallet(const bs::sync::HDWalletData& wd) +{ + ui_->pageRFQReply->onHDWallet(wd); +} + +void RFQReplyWidget::onAuthKey(const bs::Address& addr, const BinaryData& authKey) +{ + ui_->pageRFQReply->onAuthKey(addr, authKey); +} + +void RFQReplyWidget::onQuoteReqNotification(const bs::network::QuoteReqNotification& qrn) +{ + ui_->widgetQuoteRequests->onQuoteRequest(qrn); +} + void RFQReplyWidget::init(const std::shared_ptr &logger , const std::shared_ptr& celerClient , const std::shared_ptr &authAddressManager @@ -133,7 +164,6 @@ void RFQReplyWidget::init(const std::shared_ptr &logger , const std::shared_ptr& mdCallbacks , const std::shared_ptr& assetManager , const std::shared_ptr &appSettings - , const std::shared_ptr &dialogManager , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &connectionManager @@ -147,7 +177,6 @@ void RFQReplyWidget::init(const std::shared_ptr &logger authAddressManager_ = authAddressManager; quoteProvider_ = quoteProvider; assetManager_ = assetManager; - dialogManager_ = dialogManager; signingContainer_ = container; armory_ = armory; appSettings_ = appSettings; @@ -232,13 +261,17 @@ void RFQReplyWidget::init(const std::shared_ptr &logger } void RFQReplyWidget::init(const std::shared_ptr& logger - , const std::shared_ptr& dialogMgr, OrderListModel* orderListModel) + , OrderListModel* orderListModel) { logger_ = logger; - dialogManager_ = dialogMgr; + statsCollector_ = std::make_shared(ApplicationSettings::Filter_MD_QN_cnt); - connect(ui_->pageRFQReply, &RFQDealerReply::pullQuoteNotif, this - , &RFQReplyWidget::onPulled); + ui_->widgetQuoteRequests->init(logger_, statsCollector_); + ui_->pageRFQReply->init(logger); + + connect(ui_->pageRFQReply, &RFQDealerReply::pullQuoteNotif, this, &RFQReplyWidget::onPulled); + connect(ui_->pageRFQReply, &RFQDealerReply::needAuthKey, this, &RFQReplyWidget::needAuthKey); + connect(ui_->pageRFQReply, &RFQDealerReply::needReserveUTXOs, this, &RFQReplyWidget::needReserveUTXOs); ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui_->treeViewOrders->setModel(orderListModel); @@ -246,6 +279,28 @@ void RFQReplyWidget::init(const std::shared_ptr& logger connect(ui_->widgetQuoteRequests->view(), &TreeViewWithEnterKey::enterKeyPressed , this, &RFQReplyWidget::onEnterKeyPressed); + connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::Selected, this + , &RFQReplyWidget::onSelected); + + ui_->pageRFQReply->setSubmitQuoteNotifCb([this] + (const std::shared_ptr& data) + { + statsCollector_->onQuoteSubmitted(data->qn); + emit submitQuote(data->qn); + ui_->widgetQuoteRequests->onQuoteReqNotifReplied(data->qn); + onReplied(data); + }); + + ui_->pageRFQReply->setGetLastSettlementReply([this] + (const std::string& settlementId) -> const std::vector* + { + auto lastReply = sentXbtReplies_.find(settlementId); + if (lastReply == sentXbtReplies_.end()) { + return nullptr; + } + + return &(lastReply->second.utxosPayinFixed); //FIXME + }); } void RFQReplyWidget::forceCheckCondition() @@ -272,7 +327,7 @@ void RFQReplyWidget::onReplied(const std::shared_ptrauthAddr; reply.utxosPayinFixed = data->fixedXbtInputs; reply.utxoRes = std::move(data->utxoRes); - reply.walletPurpose = std::move(data->walletPurpose); + reply.walletPurpose = data->walletPurpose; break; } @@ -283,7 +338,7 @@ void RFQReplyWidget::onReplied(const std::shared_ptrqn.reqAuthKey; reply.utxoRes = std::move(data->utxoRes); reply.xbtWallet = data->xbtWallet; - reply.walletPurpose = std::move(data->walletPurpose); + reply.walletPurpose = data->walletPurpose; break; } @@ -293,15 +348,22 @@ void RFQReplyWidget::onReplied(const std::shared_ptrCancelQuoteNotif(QString::fromStdString(reqId), QString::fromStdString(reqSessToken)); + if (quoteProvider_) { + quoteProvider_->CancelQuoteNotif(QString::fromStdString(reqId) + , QString::fromStdString(reqSessToken)); + } + else { + emit pullQuote(settlementId, reqId, reqSessToken); + } } -void RFQReplyWidget::onUserConnected(const bs::network::UserType &) +void RFQReplyWidget::onUserConnected(const bs::network::UserType &userType) { if (appSettings_) { const bool autoSigning = appSettings_->get(ApplicationSettings::AutoSigning); @@ -309,8 +371,8 @@ void RFQReplyWidget::onUserConnected(const bs::network::UserType &) ui_->widgetAutoSignQuote->onUserConnected(autoSigning, autoQuoting); } - else { - //TODO: query settings asynchronously + else { //no AQ support in new code + userType_ = userType; } } @@ -364,7 +426,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) , order, quoteReqId, assetManager_->getCCLotSize(order.product) , assetManager_->getCCGenesisAddr(order.product), sr.recipientAddress , sr.xbtWallet, signingContainer_, armory_, walletsManager_ - , std::move(sr.walletPurpose), std::move(sr.utxoRes), expandTxInfo); + , sr.walletPurpose, std::move(sr.utxoRes), expandTxInfo); connect(settlContainer.get(), &DealerCCSettlementContainer::signTxRequest , this, &RFQReplyWidget::saveTxData); connect(settlContainer.get(), &DealerCCSettlementContainer::error @@ -412,7 +474,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) const auto settlContainer = std::make_shared(logger_ , order, walletsManager_, reply.xbtWallet, quoteProvider_, signingContainer_ , armory_, authAddressManager_, reply.authAddr, reply.utxosPayinFixed - , recvXbtAddr, utxoReservationManager_, std::move(reply.walletPurpose) + , recvXbtAddr, utxoReservationManager_, reply.walletPurpose , std::move(reply.utxoRes), expandTxInfo, tier1XbtLimit); connect(settlContainer.get(), &DealerXBTSettlementContainer::sendUnsignedPayinToPB @@ -518,6 +580,7 @@ void RFQReplyWidget::onEnterKeyPressed(const QModelIndex &index) void RFQReplyWidget::onSelected(const QString& productGroup, const bs::network::QuoteReqNotification& request, double indicBid, double indicAsk) { if (!checkConditions(productGroup, request)) { + logger_->debug("[{}] checkConditions failed", __func__); return; } @@ -585,17 +648,8 @@ void RFQReplyWidget::onSignTxRequested(QString orderId, QString reqId, QDateTime } } - -void RFQReplyWidget::showSettlementDialog(QDialog *dlg) -{ - dlg->setAttribute(Qt::WA_DeleteOnClose); - - dialogManager_->adjustDialogPosition(dlg); - - dlg->show(); -} - -bool RFQReplyWidget::checkConditions(const QString& productGroup , const bs::network::QuoteReqNotification& request) +bool RFQReplyWidget::checkConditions(const QString& productGroup + , const bs::network::QuoteReqNotification& request) { ui_->stackedWidget->setEnabled(true); @@ -605,14 +659,13 @@ bool RFQReplyWidget::checkConditions(const QString& productGroup , const bs::net return true; } - using UserType = CelerClient::CelerUserType; - const UserType userType = celerClient_->celerUserType(); + const auto &userType = celerClient_ ? celerClient_->celerUserType() : userType_; using GroupType = RFQShieldPage::ProductType; const GroupType group = RFQShieldPage::getProductGroup(productGroup); switch (userType) { - case UserType::Market: { + case bs::network::UserType::Market: if (group == GroupType::SpotFX) { ui_->shieldPage->showShieldReservedTradingParticipant(); popShield(); @@ -628,8 +681,7 @@ bool RFQReplyWidget::checkConditions(const QString& productGroup , const bs::net return false; } break; - } - case UserType::Trading: { + case bs::network::UserType::Trading: if (group == GroupType::SpotXBT) { ui_->shieldPage->showShieldReservedDealingParticipant(); return false; @@ -639,19 +691,14 @@ bool RFQReplyWidget::checkConditions(const QString& productGroup , const bs::net return false; } break; - } - case UserType::Dealing: { + case bs::network::UserType::Dealing: if ((group == GroupType::SpotXBT || group == GroupType::PrivateMarket) && ui_->shieldPage->checkWalletSettings(group, QString::fromStdString(request.product))) { popShield(); return false; } break; - break; - } - default: { - break; - } + default: break; } if (ui_->stackedWidget->currentIndex() != static_cast(DealingPages::DealingPage)) { diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 7163b72bb..c3b87d220 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -17,13 +17,14 @@ #include #include #include - +#include "ApplicationSettings.h" #include "BSErrorCode.h" #include "CoinControlModel.h" #include "CommonTypes.h" +#include "HDPath.h" +#include "SignerDefs.h" #include "TabWithShortcut.h" #include "UtxoReservationToken.h" -#include "HDPath.h" namespace Ui { class RFQReplyWidget; @@ -49,7 +50,6 @@ class AuthAddressManager; class AutoSignScriptProvider; class CelerClientQt; class ConnectionManager; -class DialogManager; class MDCallbacksQt; class OrderListModel; class QuoteProvider; @@ -85,19 +85,25 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , OrderListModel *orderListModel); - void init(const std::shared_ptr& - , const std::shared_ptr&, OrderListModel*); - void setWalletsManager(const std::shared_ptr &); + void init(const std::shared_ptr&, OrderListModel*); + [[deprecated]] void setWalletsManager(const std::shared_ptr &); void shortcutActivated(ShortcutType s) override; + void onMatchingLogout(); + void onBalance(const std::string& currency, double balance); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onHDWallet(const bs::sync::HDWalletData&); + void onAuthKey(const bs::Address&, const BinaryData& authKey); + + void onQuoteReqNotification(const bs::network::QuoteReqNotification&); + signals: void orderFilled(); void requestPrimaryWalletCreation(); @@ -114,6 +120,15 @@ Q_OBJECT void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); + void submitQuote(const bs::network::QuoteNotification&); + void pullQuote(const std::string& settlementId, const std::string& reqId + , const std::string& reqSessToken); + + void putSetting(ApplicationSettings::Setting, const QVariant&); + void needAuthKey(const bs::Address&); + void needReserveUTXOs(const std::string& reserveId, const std::string& subId + , uint64_t amount, bool partial = false, const std::vector& utxos = {}); + public slots: void forceCheckCondition(); @@ -143,7 +158,6 @@ private slots: private: void onResetCurrentReservation(const std::shared_ptr &data); - void showSettlementDialog(QDialog *dlg); bool checkConditions(const QString& productGroup, const bs::network::QuoteReqNotification& request); void popShield(); void showEditableRFQPage(); @@ -157,9 +171,9 @@ private slots: { std::shared_ptr xbtWallet; bs::Address authAddr; - std::vector utxosPayinFixed; - bs::UtxoReservationToken utxoRes; - std::unique_ptr walletPurpose; + std::vector utxosPayinFixed; + bs::UtxoReservationToken utxoRes; + bs::hd::Purpose walletPurpose; }; struct SentCCReply @@ -168,7 +182,7 @@ private slots: std::string requestorAuthAddress; std::shared_ptr xbtWallet; bs::UtxoReservationToken utxoRes; - std::unique_ptr walletPurpose; + bs::hd::Purpose walletPurpose; }; private: @@ -179,7 +193,6 @@ private slots: std::shared_ptr authAddressManager_; std::shared_ptr assetManager_; std::shared_ptr walletsManager_; - std::shared_ptr dialogManager_; std::shared_ptr signingContainer_; std::shared_ptr armory_; std::shared_ptr appSettings_; @@ -191,6 +204,8 @@ private slots: std::unordered_map sentCCReplies_; std::shared_ptr statsCollector_; std::unordered_map sentReplyToSettlementsIds_, settlementToReplyIds_; + + bs::network::UserType userType_{ bs::network::UserType::Undefined }; }; #endif // __RFQ_REPLY_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 28f35aa04..f8068ca59 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -449,14 +449,14 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ auto authAddr = ui_->pageRFQTicket->selectedAuthAddress(); RFQDialog* dialog = nullptr; auto fixedXbtInputs = ui_->pageRFQTicket->fixedXbtInputs(); - std::unique_ptr purpose; + bs::hd::Purpose purpose = bs::hd::Purpose::Unknown; if (walletsManager_) { auto xbtWallet = ui_->pageRFQTicket->xbtWallet(); if (xbtWallet && !xbtWallet->canMixLeaves()) { auto walletType = ui_->pageRFQTicket->xbtWalletType(); - purpose.reset(new bs::hd::Purpose(UiUtils::getHwWalletPurpose(walletType))); + purpose = UiUtils::getHwWalletPurpose(walletType); } dialog = new RFQDialog(logger_, id, rfq, quoteProvider_ @@ -464,12 +464,12 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ , armory_, celerClient_, appSettings_, rfqStorage_, xbtWallet , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, utxoReservationManager_ , fixedXbtInputs.inputs, std::move(fixedXbtInputs.utxoRes) - , std::move(ccUtxoRes), std::move(purpose), this); + , std::move(ccUtxoRes), purpose, this); } else { std::string xbtWalletId; dialog = new RFQDialog(logger_, id, rfq, xbtWalletId - , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, std::move(purpose), this); + , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, purpose, this); const std::string reserveId = (rfq.assetType == bs::network::Asset::SpotFX) ? "" : rfq.requestId; emit needSubmitRFQ(rfq, reserveId); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index fd9f2cd81..3271ca9ee 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -1300,8 +1300,8 @@ void RFQTicketXBT::onWalletData(const std::string& walletId, const bs::sync::Wal void RFQTicketXBT::onAuthKey(const bs::Address& addr, const BinaryData& authKey) { - logger_->debug("[{}] {} ({} {})", __func__, authKey.toHexStr(), addr.display(), authAddr_.display()); if (addr == authAddr_) { + logger_->debug("[{}] got auth key: {}", __func__, authKey.toHexStr()); authKey_ = authKey.toHexStr(); updateSubmitButton(); sendDeferredRFQs(); diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp index 7fea869b2..b5f36104b 100644 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp @@ -34,10 +34,10 @@ ReqCCSettlementContainer::ReqCCSettlementContainer(const std::shared_ptr &xbtWallet , const std::map &manualXbtInputs , const std::shared_ptr &utxoReservationManager - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bs::UtxoReservationToken utxoRes , bool expandTxDialogInfo) - : bs::SettlementContainer(std::move(utxoRes), std::move(walletPurpose), expandTxDialogInfo) + : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) , logger_(logger) , signingContainer_(container) , xbtWallet_(xbtWallet) @@ -283,8 +283,7 @@ bool ReqCCSettlementContainer::createCCUnsignedTXdata() if (manualXbtInputs_.empty()) { std::vector utxos; if (!xbtWallet_->canMixLeaves()) { - assert(walletPurpose_); - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet_->walletId(), *walletPurpose_); + utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet_->walletId(), walletPurpose_); } else { utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet_->walletId()); diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.h b/BlockSettleUILib/Trading/ReqCCSettlementContainer.h index cb08c8c73..252abcb08 100644 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.h +++ b/BlockSettleUILib/Trading/ReqCCSettlementContainer.h @@ -47,7 +47,7 @@ class ReqCCSettlementContainer : public bs::SettlementContainer , const std::shared_ptr &xbtWallet , const std::map &manualXbtInputs , const std::shared_ptr &utxoReservationManager - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bs::UtxoReservationToken utxoRes , bool expandTxDialogInfo); ~ReqCCSettlementContainer() override; diff --git a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp index 3fdf6a8e1..59c70161b 100644 --- a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp @@ -44,11 +44,11 @@ ReqXBTSettlementContainer::ReqXBTSettlementContainer(const std::shared_ptr &utxosPayinFixed , bs::UtxoReservationToken utxoRes , const std::shared_ptr &utxoReservationManager - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , const bs::Address &recvAddrIfSet , bool expandTxDialogInfo , uint64_t tier1XbtLimit) - : bs::SettlementContainer(std::move(utxoRes), std::move(walletPurpose), expandTxDialogInfo) + : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) , logger_(logger) , authAddrMgr_(authAddrMgr) , walletsMgr_(walletsMgr) @@ -341,8 +341,7 @@ void ReqXBTSettlementContainer::onUnsignedPayinRequested(const std::string& sett const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); if (!xbtWallet_->canMixLeaves()) { - assert(walletPurpose_); - const auto leaf = xbtGroup->getLeaf(*walletPurpose_); + const auto leaf = xbtGroup->getLeaf(walletPurpose_); args.inputXbtWallets.push_back(leaf); } else { @@ -424,8 +423,7 @@ void ReqXBTSettlementContainer::onSignedPayoutRequested(const std::string& settl const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); if (!xbtWallet_->canMixLeaves()) { - assert(walletPurpose_); - const auto leaf = xbtGroup->getLeaf(*walletPurpose_); + const auto leaf = xbtGroup->getLeaf(walletPurpose_); args.outputXbtWallet = leaf; } else { diff --git a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h index 8055f1173..ba98a5e7b 100644 --- a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h +++ b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h @@ -53,7 +53,7 @@ class ReqXBTSettlementContainer : public bs::SettlementContainer , const std::map &utxosPayinFixed , bs::UtxoReservationToken utxoRes , const std::shared_ptr &utxoReservationManager - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , const bs::Address &recvAddrIfSet , bool expandTxDialogInfo , uint64_t tier1XbtLimit); diff --git a/BlockSettleUILib/Trading/SettlementContainer.cpp b/BlockSettleUILib/Trading/SettlementContainer.cpp index 448c4e452..45c38da56 100644 --- a/BlockSettleUILib/Trading/SettlementContainer.cpp +++ b/BlockSettleUILib/Trading/SettlementContainer.cpp @@ -21,10 +21,10 @@ namespace { } // namespace SettlementContainer::SettlementContainer(UtxoReservationToken utxoRes - , std::unique_ptr walletPurpose, bool expandTxDialogInfo) + , bs::hd::Purpose walletPurpose, bool expandTxDialogInfo) : QObject(nullptr) , utxoRes_(std::move(utxoRes)) - , walletPurpose_(std::move(walletPurpose)) + , walletPurpose_(walletPurpose) , expandTxDialogInfo_(expandTxDialogInfo) {} diff --git a/BlockSettleUILib/Trading/SettlementContainer.h b/BlockSettleUILib/Trading/SettlementContainer.h index 50cb87a66..673e3a28f 100644 --- a/BlockSettleUILib/Trading/SettlementContainer.h +++ b/BlockSettleUILib/Trading/SettlementContainer.h @@ -32,7 +32,7 @@ namespace bs { Q_OBJECT public: explicit SettlementContainer(bs::UtxoReservationToken - , std::unique_ptr walletPurpose + , bs::hd::Purpose walletPurpose , bool expandTxDialogInfo); ~SettlementContainer() override; @@ -78,7 +78,7 @@ namespace bs { ValidityFlag validityFlag_; bs::UtxoReservationToken utxoRes_; - std::unique_ptr walletPurpose_; + const bs::hd::Purpose walletPurpose_; bool expandTxDialogInfo_{}; private: diff --git a/BlockSettleUILib/UserScriptRunner.cpp b/BlockSettleUILib/UserScriptRunner.cpp index 533b67e99..8f8197c83 100644 --- a/BlockSettleUILib/UserScriptRunner.cpp +++ b/BlockSettleUILib/UserScriptRunner.cpp @@ -339,7 +339,8 @@ void AQScriptHandler::aqTick() if (!qr) continue; auto itQRN = aqQuoteReqs_.find(qr->requestId().toStdString()); if (itQRN == aqQuoteReqs_.end()) continue; - const auto timeDiff = timeNow.msecsTo(itQRN->second.expirationTime.addMSecs(itQRN->second.timeSkewMs)); + const auto& expTime = QDateTime::fromMSecsSinceEpoch(itQRN->second.expirationTime); + const auto timeDiff = timeNow.msecsTo(expTime.addMSecs(itQRN->second.timeSkewMs)); if (timeDiff <= 0) { expiredEntries << qr->requestId(); } diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index c8dcebd14..ad23cf3ca 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -38,7 +38,8 @@ using namespace com::celertech::marketmerchant::api::quote; MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) : logger_(logger) - , user_(std::make_shared(bs::message::TerminalUsers::Matching)) + , user_(std::make_shared(TerminalUsers::Matching)) + , userSettl_(std::make_shared(TerminalUsers::Settlement)) { celerConnection_ = std::make_unique(logger, this, true, true); @@ -410,7 +411,7 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) msgBest->set_quote_req_id(response.quoterequestid()); msgBest->set_price(price); msgBest->set_own(own); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; pushFill(env); } } else { @@ -452,7 +453,7 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) MatchingMessage msg; toMsg(quote, msg.mutable_quote()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; return pushFill(env); } @@ -505,7 +506,7 @@ bool MatchingAdapter::onBitcoinOrderSnapshot(const std::string& data) MatchingMessage msg; toMsg(order, msg.mutable_order()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; return pushFill(env); } @@ -537,7 +538,7 @@ bool MatchingAdapter::onFxOrderSnapshot(const std::string& data) MatchingMessage msg; toMsg(order, msg.mutable_order()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; return pushFill(env); } @@ -556,9 +557,81 @@ bool MatchingAdapter::onQuoteAck(const std::string&) return false; } -bool MatchingAdapter::onQuoteReqNotification(const std::string&) +bool MatchingAdapter::onQuoteReqNotification(const std::string& data) { - return false; + QuoteRequestNotification response; + + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onQuoteReqNotification] failed to parse"); + return false; + } + + if (response.quoterequestnotificationgroup_size() < 1) { + logger_->error("[MatchingAdapter::onQuoteReqNotification] missing at least 1 QRN group"); + return false; + } // For SpotFX and SpotXBT there should be only 1 group + + const QuoteRequestNotificationGroup& respgrp = response.quoterequestnotificationgroup(0); + + if (respgrp.quoterequestnotificationleggroup_size() != 1) { + logger_->error("[MatchingAdapter::onQuoteReqNotification] wrong leg group size: {}\n{}" + , respgrp.quoterequestnotificationleggroup_size() + , ProtobufUtils::toJsonCompact(response)); + return false; + } + + const auto& legGroup = respgrp.quoterequestnotificationleggroup(0); + + bs::network::QuoteReqNotification qrn; + qrn.quoteRequestId = response.quoterequestid(); + qrn.security = respgrp.securitycode(); + qrn.sessionToken = response.requestorsessiontoken(); + qrn.quantity = legGroup.qty(); + qrn.product = respgrp.currency(); + qrn.party = respgrp.partyid(); + qrn.reason = response.reason(); + qrn.account = response.account(); + qrn.expirationTime = response.expiretimeinutcinmillis(); + qrn.timestamp = response.timestampinutcinmillis(); + qrn.timeSkewMs = QDateTime::fromMSecsSinceEpoch(qrn.timestamp).msecsTo(QDateTime::currentDateTime()); + + qrn.side = bs::celer::fromCeler(legGroup.side()); + qrn.assetType = bs::celer::fromCelerProductType(respgrp.producttype()); + + switch (response.quotenotificationtype()) { + case QUOTE_WITHDRAWN: + qrn.status = bs::network::QuoteReqNotification::Withdrawn; + break; + case PENDING_ACKNOWLEDGE: + qrn.status = bs::network::QuoteReqNotification::PendingAck; + break; + default: + qrn.status = bs::network::QuoteReqNotification::StatusUndefined; + break; + } + + if (response.has_settlementid() && !response.settlementid().empty()) { + qrn.settlementId = response.settlementid(); + } + + switch (qrn.assetType) { + case bs::network::Asset::SpotXBT: + qrn.requestorAuthPublicKey = response.requestorauthenticationaddress(); + break; + case bs::network::Asset::PrivateMarket: + qrn.requestorAuthPublicKey = respgrp.requestorcointransactioninput(); + qrn.requestorRecvAddress = response.requestorreceiptaddress(); + break; + default: break; + } + + saveQuoteRequestCcy(qrn.quoteRequestId, qrn.product); + + logger_->debug("[MatchingAdapter::onQuoteReqNotification] {}", ProtobufUtils::toJsonCompact(response)); + MatchingMessage msg; + toMsg(qrn, msg.mutable_incoming_rfq()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); } bool MatchingAdapter::onQuoteNotifCancelled(const std::string&) @@ -580,7 +653,7 @@ void ClientCelerConnection::onSendData(CelerAPI::CelerMessageType messageType auto msgReq = msg.mutable_send_matching(); msgReq->set_message_type((int)messageType); msgReq->set_data(data); - Envelope env{ 0, parent_->user_, bs::message::UserTerminal::create(bs::message::TerminalUsers::BsServer) + Envelope env{ 0, parent_->user_, UserTerminal::create(TerminalUsers::BsServer) , {}, {}, msg.SerializeAsString(), true }; parent_->pushFill(env); } diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index dbefe65d9..33c0b0ddb 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -88,7 +88,7 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget private: std::shared_ptr logger_; - std::shared_ptr user_; + std::shared_ptr user_, userSettl_; std::unique_ptr celerConnection_; std::string assignedAccount_; diff --git a/Core/MessageUtils.cpp b/Core/MessageUtils.cpp index e38b7a4d0..b13683d85 100644 --- a/Core/MessageUtils.cpp +++ b/Core/MessageUtils.cpp @@ -131,3 +131,97 @@ void bs::message::toMsg(const bs::network::Order& order, MatchingMessage_Order* msg->set_status((int)order.status); msg->set_info(order.info); } + + +bs::network::QuoteReqNotification bs::message::fromMsg(const BlockSettle::Terminal::IncomingRFQ& msg) +{ + bs::network::QuoteReqNotification result; + result.quantity = msg.rfq().quantity(); + result.quoteRequestId = msg.rfq().id(); + result.security = msg.rfq().security(); + result.product = msg.rfq().product(); + result.requestorAuthPublicKey = msg.rfq().auth_pub_key(); + result.requestorRecvAddress = msg.rfq().receipt_address(); + result.side = msg.rfq().buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + result.assetType = static_cast(msg.rfq().asset_type()); + result.settlementId = msg.settlement_id(); + result.sessionToken = msg.session_token(); + result.party = msg.party(); + result.reason = msg.reason(); + result.account = msg.account(); + result.status = static_cast(msg.status()); + result.expirationTime = msg.expiration_ms(); + result.timestamp = msg.timestamp_ms(); + result.timeSkewMs = msg.time_skew_ms(); + return result; +} + +void bs::message::toMsg(const bs::network::QuoteReqNotification& qrn + , BlockSettle::Terminal::IncomingRFQ* msg) +{ + auto msgRFQ = msg->mutable_rfq(); + msgRFQ->set_quantity(qrn.quantity); + msgRFQ->set_id(qrn.quoteRequestId); + msgRFQ->set_security(qrn.security); + msgRFQ->set_product(qrn.product); + msgRFQ->set_auth_pub_key(qrn.requestorAuthPublicKey); + msgRFQ->set_receipt_address(qrn.requestorRecvAddress); + msgRFQ->set_buy(qrn.side == bs::network::Side::Buy); + msgRFQ->set_asset_type((int)qrn.assetType); + msg->set_settlement_id(qrn.settlementId); + msg->set_session_token(qrn.sessionToken); + msg->set_party(qrn.party); + msg->set_reason(qrn.reason); + msg->set_account(qrn.account); + msg->set_status((int)qrn.status); + msg->set_expiration_ms(qrn.expirationTime); + msg->set_timestamp_ms(qrn.timestamp); + msg->set_time_skew_ms(qrn.timeSkewMs); +} + + +bs::network::QuoteNotification bs::message::fromMsg(const BlockSettle::Terminal::ReplyToRFQ& msg) +{ + bs::network::QuoteNotification result; + result.authKey = msg.quote().deal_auth_pub_key(); + result.reqAuthKey = msg.quote().req_auth_pub_key(); + result.settlementId = msg.quote().settlement_id(); + result.quoteRequestId = msg.quote().request_id(); + result.security = msg.quote().security(); + result.product = msg.quote().product(); + result.transactionData = msg.quote().dealer_tx(); + result.assetType = static_cast(msg.quote().asset_type()); + result.side = msg.quote().buy() ? bs::network::Side::Buy : bs::network::Side::Sell; + result.validityInS = (msg.quote().expiration_time() - msg.quote().timestamp()) / 1000; + result.price = msg.quote().price(); + result.quantity = msg.quote().quantity(); + result.sessionToken = msg.session_token(); + result.account = msg.account(); + result.receiptAddress = msg.dealer_recv_addr(); + return result; +} + +void bs::message::toMsg(const bs::network::QuoteNotification& qn + , BlockSettle::Terminal::ReplyToRFQ* msg) +{ + auto msgQuote = msg->mutable_quote(); + msgQuote->set_deal_auth_pub_key(qn.authKey); + msgQuote->set_req_auth_pub_key(qn.reqAuthKey); + msgQuote->set_settlement_id(qn.settlementId); + msgQuote->set_request_id(qn.quoteRequestId); + msgQuote->set_security(qn.security); + msgQuote->set_product(qn.product); + msgQuote->set_dealer_tx(qn.transactionData); + msgQuote->set_asset_type((int)qn.assetType); + msgQuote->set_buy(qn.side == bs::network::Side::Buy); + msgQuote->set_price(qn.price); + msgQuote->set_quantity(qn.quantity); + msg->set_session_token(qn.sessionToken); + msg->set_account(qn.account); + msg->set_dealer_recv_addr(qn.receiptAddress); + + const auto& timeNow = std::chrono::system_clock::now(); + msgQuote->set_timestamp(std::chrono::duration_cast(timeNow.time_since_epoch()).count()); + msgQuote->set_expiration_time(std::chrono::duration_cast( + (timeNow + std::chrono::seconds{qn.validityInS}).time_since_epoch()).count()); +} diff --git a/Core/MessageUtils.h b/Core/MessageUtils.h index 971b0471b..dec999a16 100644 --- a/Core/MessageUtils.h +++ b/Core/MessageUtils.h @@ -19,7 +19,9 @@ namespace BlockSettle { namespace Terminal { class RFQ; class Quote; + class IncomingRFQ; class MatchingMessage_Order; + class ReplyToRFQ; } } @@ -35,6 +37,12 @@ namespace bs { bs::network::Order fromMsg(const BlockSettle::Terminal::MatchingMessage_Order&); void toMsg(const bs::network::Order&, BlockSettle::Terminal::MatchingMessage_Order*); + bs::network::QuoteReqNotification fromMsg(const BlockSettle::Terminal::IncomingRFQ&); + void toMsg(const bs::network::QuoteReqNotification&, BlockSettle::Terminal::IncomingRFQ*); + + bs::network::QuoteNotification fromMsg(const BlockSettle::Terminal::ReplyToRFQ&); + void toMsg(const bs::network::QuoteNotification&, BlockSettle::Terminal::ReplyToRFQ*); + } // namespace message } // namespace bs diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index 1bf2e1489..d97a93fba 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -78,6 +78,8 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) return processMatchingQuote(msg.quote()); case MatchingMessage::kOrder: return processMatchingOrder(msg.order()); + case MatchingMessage::kIncomingRfq: + return processMatchingInRFQ(msg.incoming_rfq()); default: break; } if (!env.receiver || env.receiver->isBroadcast()) { @@ -148,6 +150,8 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) return processSendRFQ(env, msg.send_rfq()); case SettlementMessage::kHandshakeTimeout: return processHandshakeTimeout(msg.handshake_timeout()); + case SettlementMessage::kQuoteReqTimeout: + return processInRFQTimeout(msg.quote_req_timeout()); default: logger_->warn("[{}] unknown settlement request {}", __func__, msg.data_case()); break; @@ -235,6 +239,35 @@ bool SettlementAdapter::processMatchingOrder(const MatchingMessage_Order& respon return pushFill(env); } +bool SettlementAdapter::processMatchingInRFQ(const IncomingRFQ& qrn) +{ + SettlementMessage msg; + *msg.mutable_quote_req_notif() = qrn; + Envelope envBC{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + + const auto& rfq = fromMsg(qrn.rfq()); + const auto& settlement = std::make_shared(); + settlement->rfq = rfq; + settlement->dealer = true; + try { + settlement->settlementId = BinaryData::CreateFromHex(qrn.settlement_id()); + } + catch (const std::exception&) { + logger_->error("[{}] invalid settlement id", __func__); + } + settlByRfqId_[rfq.requestId] = settlement; + + msg.set_quote_req_timeout(rfq.requestId); + const auto& timeNow = std::chrono::system_clock::now(); + const auto expTime = std::chrono::milliseconds(qrn.expiration_ms()) - timeNow.time_since_epoch(); + if (expTime.count() < 0) { + logger_->error("[{}] outdated expiry {} for {}", __func__, expTime.count(), rfq.requestId); + return true; + } + Envelope envTO{ 0, user_, user_, timeNow, timeNow + expTime, msg.SerializeAsString(), true }; + return (pushFill(envTO) && pushFill(envBC)); +} + bool SettlementAdapter::processBsUnsignedPayin(const BinaryData& settlementId) { logger_->debug("[{}] {}", __func__, settlementId.toHexStr()); @@ -643,6 +676,16 @@ bool SettlementAdapter::processHandshakeTimeout(const std::string& id) return true; } +bool SettlementAdapter::processInRFQTimeout(const std::string& id) +{ + const auto& itSettl = settlByRfqId_.find(id); + if (itSettl != settlByRfqId_.end()) { // do nothing - just remove unanswered RFQ + logger_->debug("[{}] {}", __func__, id); + settlByRfqId_.erase(itSettl); + } + return true; +} + void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) { const auto& sendFailedQuote = [this, quote](const std::string& info) diff --git a/Core/SettlementAdapter.h b/Core/SettlementAdapter.h index 38372064d..e2a28ee06 100644 --- a/Core/SettlementAdapter.h +++ b/Core/SettlementAdapter.h @@ -28,6 +28,7 @@ namespace BlockSettle { namespace Terminal { class AcceptRFQ; class BsServerMessage_SignXbtHalf; + class IncomingRFQ; class MatchingMessage_Order; class Quote; class SettlementMessage_SendRFQ; @@ -51,6 +52,7 @@ class SettlementAdapter : public bs::message::Adapter bool processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived&); bool processMatchingQuote(const BlockSettle::Terminal::Quote&); bool processMatchingOrder(const BlockSettle::Terminal::MatchingMessage_Order&); + bool processMatchingInRFQ(const BlockSettle::Terminal::IncomingRFQ&); bool processBsUnsignedPayin(const BinaryData& settlementId); bool processBsSignPayin(const BlockSettle::Terminal::BsServerMessage_SignXbtHalf&); @@ -64,6 +66,7 @@ class SettlementAdapter : public bs::message::Adapter bool processXbtTx(uint64_t msgId, const BlockSettle::Common::WalletsMessage_XbtTxResponse&); bool processSignedTx(uint64_t msgId, const BlockSettle::Common::SignerMessage_SignTxResponse&); bool processHandshakeTimeout(const std::string& id); + bool processInRFQTimeout(const std::string& id); void startXbtSettlement(const bs::network::Quote&); void startCCSettlement(const bs::network::Quote&); diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 43fc7273c..b7097b43e 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -229,6 +229,7 @@ void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { ui_->widgetWallets->onHDWalletDetails(hdWallet); ui_->widgetRFQ->onHDWallet(hdWallet); + ui_->widgetRFQReply->onHDWallet(hdWallet); } void MainWindow::onWalletsList(const std::string &id, const std::vector& wallets) @@ -272,6 +273,7 @@ void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) } ui_->widgetWallets->onWalletBalance(wbd); ui_->widgetRFQ->onWalletBalance(wbd); + ui_->widgetRFQReply->onWalletBalance(wbd); statusBarView_->onXbtBalance(wbd); } @@ -1006,6 +1008,7 @@ void MainWindow::onMatchingLogout() orderListModel_->onMatchingLogout(); } ui_->widgetRFQ->onMatchingLogout(); + ui_->widgetRFQReply->onMatchingLogout(); } void bs::gui::qt::MainWindow::onNewSecurity(const std::string& name, bs::network::Asset::Type at) @@ -1024,6 +1027,7 @@ void bs::gui::qt::MainWindow::onBalance(const std::string& currency, double bala { statusBarView_->onBalanceUpdated(currency, balance); ui_->widgetRFQ->onBalance(currency, balance); + ui_->widgetRFQReply->onBalance(currency, balance); } void MainWindow::onAuthAddresses(const std::vector &addrs @@ -1049,6 +1053,7 @@ void MainWindow::onVerifiedAuthAddresses(const std::vector& addrs) void MainWindow::onAuthKey(const bs::Address& addr, const BinaryData& authKey) { ui_->widgetRFQ->onAuthKey(addr, authKey); + ui_->widgetRFQReply->onAuthKey(addr, authKey); } void MainWindow::onQuoteReceived(const bs::network::Quote& quote) @@ -1079,6 +1084,11 @@ void bs::gui::qt::MainWindow::onSettlementComplete(const std::string& rfqId ui_->widgetRFQ->onSettlementComplete(rfqId, quoteId, settlementId); } +void bs::gui::qt::MainWindow::onQuoteReqNotification(const bs::network::QuoteReqNotification& qrn) +{ + ui_->widgetRFQReply->onQuoteReqNotification(qrn); +} + void MainWindow::onOrdersUpdate(const std::vector& orders) { orderListModel_->onOrdersUpdate(orders); @@ -1267,7 +1277,12 @@ void MainWindow::initWidgets() connect(ui_->widgetRFQReply, &RFQReplyWidget::requestPrimaryWalletCreation, this , &MainWindow::createNewWallet); - ui_->widgetRFQReply->init(logger_, dialogMgr_, orderListModel_.get()); + connect(ui_->widgetRFQReply, &RFQReplyWidget::putSetting, this, &MainWindow::putSetting); + connect(ui_->widgetRFQReply, &RFQReplyWidget::submitQuote, this, &MainWindow::submitQuote); + connect(ui_->widgetRFQReply, &RFQReplyWidget::pullQuote, this, &MainWindow::pullQuote); + connect(ui_->widgetRFQReply, &RFQReplyWidget::needAuthKey, this, &MainWindow::needAuthKey); + connect(ui_->widgetRFQReply, &RFQReplyWidget::needReserveUTXOs, this, &MainWindow::needReserveUTXOs); + ui_->widgetRFQReply->init(logger_, orderListModel_.get()); connect(ui_->tabWidget, &QTabWidget::tabBarClicked, this, [/*requestRFQ = QPointer(ui_->widgetRFQ) diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 818f1ac96..e4cdd9f2b 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -120,6 +120,7 @@ namespace bs { , const BinaryData& settlementId); void onSettlementComplete(const std::string& rfqId, const std::string& quoteId , const BinaryData& settlementId); + void onQuoteReqNotification(const bs::network::QuoteReqNotification&); void onOrdersUpdate(const std::vector&); void onReservedUTXOs(const std::string& resId, const std::string& subId @@ -199,6 +200,10 @@ namespace bs { , uint64_t amount, bool partial = false, const std::vector& utxos = {}); void needUnreserveUTXOs(const std::string& reserveId, const std::string& subId); + void submitQuote(const bs::network::QuoteNotification&); + void pullQuote(const std::string& settlementId, const std::string& reqId + , const std::string& reqSessToken); + private slots: void onSend(); void onGenerateAddress(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 0a9c67825..d63e3f0f6 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -762,6 +762,8 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needAuthKey, this, &QtGuiAdapter::onNeedAuthKey); connect(mainWindow_, &bs::gui::qt::MainWindow::needReserveUTXOs, this, &QtGuiAdapter::onNeedReserveUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::needUnreserveUTXOs, this, &QtGuiAdapter::onNeedUnreserveUTXOs); + connect(mainWindow_, &bs::gui::qt::MainWindow::submitQuote, this, &QtGuiAdapter::onSubmitQuote); + connect(mainWindow_, &bs::gui::qt::MainWindow::pullQuote, this, &QtGuiAdapter::onPullQuote); } void QtGuiAdapter::onGetSettings(const std::vector& settings) @@ -1276,6 +1278,26 @@ void QtGuiAdapter::onNeedUnreserveUTXOs(const std::string& reserveId pushFill(env); } +void QtGuiAdapter::onSubmitQuote(const bs::network::QuoteNotification& qn) +{ + SettlementMessage msg; + toMsg(qn, msg.mutable_reply_to_rfq()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + +void QtGuiAdapter::onPullQuote(const std::string& settlementId + , const std::string& reqId, const std::string& reqSessToken) +{ + SettlementMessage msg; + auto msgReq = msg.mutable_pull_rfq_reply(); + msgReq->set_settlement_id(settlementId); + msgReq->set_rfq_id(reqId); + msgReq->set_session_token(reqSessToken); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; @@ -1609,6 +1631,8 @@ bool QtGuiAdapter::processSettlement(const bs::message::Envelope& env) return processPendingSettl(msg.pending_settlement()); case SettlementMessage::kSettlementComplete: return processSettlComplete(msg.settlement_complete()); + case SettlementMessage::kQuoteReqNotif: + return processQuoteReqNotif(msg.quote_req_notif()); default: break; } return true; @@ -1805,6 +1829,14 @@ bool QtGuiAdapter::processSettlComplete(const SettlementMessage_SettlementIds& m }); } +bool QtGuiAdapter::processQuoteReqNotif(const IncomingRFQ& request) +{ + const auto& qrn = fromMsg(request); + return QMetaObject::invokeMethod(mainWindow_, [this, qrn] { + mainWindow_->onQuoteReqNotification(qrn); + }); +} + bool QtGuiAdapter::processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders& msg) { // Use some fake orderId so old code works correctly diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 35c156e2e..93ec2974a 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -52,6 +52,7 @@ namespace BlockSettle { class BsServerMessage_StartLoginResult; class MatchingMessage_LoggedIn; class Quote; + class IncomingRFQ; class MatchingMessage_Order; class MktDataMessage_Prices; class SettlementMessage_FailedSettlement; @@ -141,6 +142,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processFailedSettl(const BlockSettle::Terminal::SettlementMessage_FailedSettlement&); bool processPendingSettl(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); bool processSettlComplete(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); + bool processQuoteReqNotif(const BlockSettle::Terminal::IncomingRFQ&); bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); bool sendPooledOrdersUpdate(); bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); @@ -200,6 +202,9 @@ private slots: void onNeedReserveUTXOs(const std::string& reserveId, const std::string& subId , uint64_t amount, bool partial = false, const std::vector& utxos = {}); void onNeedUnreserveUTXOs(const std::string& reserveId, const std::string& subId); + void onSubmitQuote(const bs::network::QuoteNotification&); + void onPullQuote(const std::string& settlementId, const std::string& reqId + , const std::string& reqSessToken); private: std::shared_ptr logger_; diff --git a/common b/common index afc0efb45..67a3a6068 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit afc0efb45c88ab6a7e7b4175f5a7ca5e3e728f6e +Subproject commit 67a3a60687574426bf4540552f8924d1ccbc60f3 From c455610d818756fdb337ba3f2f70f78e2d677947 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 16 Nov 2020 18:51:38 +0300 Subject: [PATCH 038/146] RFQ reply - FX --- .../Trading/QuoteRequestsModel.cpp | 2 +- BlockSettleUILib/Trading/RFQDealerReply.cpp | 12 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 7 ++ BlockSettleUILib/Trading/RFQReplyWidget.h | 6 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 4 +- Core/MatchingAdapter.cpp | 116 ++++++++++++++++-- Core/MatchingAdapter.h | 4 + Core/SettlementAdapter.cpp | 100 ++++++++++++++- Core/SettlementAdapter.h | 10 ++ GUI/QtWidgets/MainWindow.cpp | 7 ++ GUI/QtWidgets/MainWindow.h | 2 + GUI/QtWidgets/QtGuiAdapter.cpp | 9 ++ GUI/QtWidgets/QtGuiAdapter.h | 2 + common | 2 +- 14 files changed, 264 insertions(+), 19 deletions(-) diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index 223800fa0..ec599633e 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -741,9 +741,9 @@ void QuoteRequestsModel::onAllQuoteNotifCancelled(const QString &reqId) void QuoteRequestsModel::onQuoteReqCancelled(const QString &reqId, bool byUser) { if (!byUser) { + onAllQuoteNotifCancelled(reqId); return; } - setStatus(reqId.toStdString(), bs::network::QuoteReqNotification::Withdrawn); } diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index 2215492a5..66cf488a4 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -1324,8 +1324,11 @@ void bs::ui::RFQDealerReply::updateBalanceLabel() .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); } else { - totalBalance = tr("%1 %2").arg(UiUtils::displayCurrencyAmount(balances_.at(product_))) - .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); + try { + totalBalance = tr("%1 %2").arg(UiUtils::displayCurrencyAmount(balances_.at(product_))) + .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); + } + catch (const std::exception&) {} } } @@ -1373,7 +1376,10 @@ bs::XBTAmount RFQDealerReply::getXbtBalance() const case bs::hd::CoinType::Bitcoin_test: for (const auto& leaf : group.leaves) { for (const auto& id : leaf.ids) { - balance += balances_.at(id); + try { + balance += balances_.at(id); + } + catch (const std::exception&) {} } } break; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 3a9eb6980..fb12beaa1 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -132,6 +132,13 @@ void RFQReplyWidget::onMatchingLogout() userType_ = bs::network::UserType::Undefined; } +void RFQReplyWidget::onMDUpdated(bs::network::Asset::Type at, const QString& security + , const bs::network::MDFields& fields) +{ + ui_->widgetQuoteRequests->onSecurityMDUpdated(at, security, fields); + ui_->pageRFQReply->onMDUpdate(at, security, fields); +} + void RFQReplyWidget::onBalance(const std::string& currency, double balance) { ui_->pageRFQReply->onBalance(currency, balance); diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index c3b87d220..51846f625 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -97,6 +97,8 @@ Q_OBJECT void shortcutActivated(ShortcutType s) override; void onMatchingLogout(); + void onMDUpdated(bs::network::Asset::Type, const QString& security + , const bs::network::MDFields&); void onBalance(const std::string& currency, double balance); void onWalletBalance(const bs::sync::WalletBalanceData&); void onHDWallet(const bs::sync::HDWalletData&); @@ -134,12 +136,12 @@ public slots: void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); void onUserConnected(const bs::network::UserType &); + void onQuoteCancelled(const QString& reqId, bool userCancelled); + void onQuoteNotifCancelled(const QString& reqId); private slots: void onOrder(const bs::network::Order &o); - void onQuoteCancelled(const QString &reqId, bool userCancelled); void onQuoteRejected(const QString &reqId, const QString &reason); - void onQuoteNotifCancelled(const QString &reqId); void saveTxData(QString orderId, std::string txData); void onSignTxRequested(QString orderId, QString reqId, QDateTime timestamp); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 3271ca9ee..b0af971c5 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -375,8 +375,8 @@ void RFQTicketXBT::fillRecvAddresses() bool RFQTicketXBT::preSubmitCheck() { if (currentGroupType_ == ProductGroupType::XBTGroupType) { - - if (authAddressManager_->GetState(authAddr_) == AuthAddressManager::AuthAddressState::Verified) { + if ((!authAddressManager_ && !authAddr_.empty()) + || (authAddressManager_->GetState(authAddr_) == AuthAddressManager::AuthAddressState::Verified)) { return true; } diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index ad23cf3ca..460b7159a 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -11,11 +11,13 @@ #include "MatchingAdapter.h" #include #include "Celer/CommonUtils.h" +#include "Celer/CancelQuoteNotifSequence.h" #include "Celer/CancelRFQSequence.h" #include "Celer/CelerClientProxy.h" #include "Celer/CreateOrderSequence.h" #include "Celer/CreateFxOrderSequence.h" #include "Celer/GetAssignedAccountsListSequence.h" +#include "Celer/SubmitQuoteNotifSequence.h" #include "Celer/SubmitRFQSequence.h" #include "CurrencyPair.h" #include "MessageUtils.h" @@ -83,6 +85,8 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) , [this](const std::string& data) { return onQuoteNotifCancelled(data); }); + celerConnection_->RegisterHandler(CelerAPI::SubLedgerSnapshotDownstreamEventType + , [](const std::string&) { return true; }); // remove warning from log } void MatchingAdapter::connectedToServer() @@ -193,6 +197,10 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) return processAcceptRFQ(msg.accept_rfq()); case MatchingMessage::kCancelRfq: return processCancelRFQ(msg.cancel_rfq()); + case MatchingMessage::kSubmitQuoteNotif: + return processSubmitQuote(msg.submit_quote_notif()); + case MatchingMessage::kPullQuoteNotif: + return processPullQuote(msg.pull_quote_notif()); default: logger_->warn("[{}] unknown msg {} #{} from {}", __func__, msg.data_case() , env.id, env.sender->name()); @@ -295,6 +303,42 @@ bool MatchingAdapter::processCancelRFQ(const std::string& rfqId) return true; } +bool MatchingAdapter::processSubmitQuote(const ReplyToRFQ& request) +{ + if (assignedAccount_.empty()) { + logger_->warn("[MatchingAdapter::processSubmitQuote] account name not set"); + } + const auto& qn = fromMsg(request); + const auto &sequence = std::make_shared(assignedAccount_, qn, logger_); + if (!celerConnection_->ExecuteSequence(sequence)) { + logger_->error("[MatchingAdapter::processSubmitQuote] failed to execute CelerSubmitQuoteNotifSequence"); + } else { + logger_->debug("[MatchingAdapter::processSubmitQuote] QuoteNotification on {} submitted" + , qn.quoteRequestId); + } + return true; +} + +bool MatchingAdapter::processPullQuote(const PullRFQReply& request) +{ + std::shared_ptr sequence; + try { + sequence = std::make_shared(request.rfq_id() + , request.session_token(), logger_); + } + catch (const std::exception& e) { + logger_->error("[{}] failed to init: {}", __func__, e.what()); + return true; + } + if (!celerConnection_->ExecuteSequence(sequence)) { + logger_->error("[MatchingAdapter::processPullQuote] failed to execute CancelQuoteNotifSequence"); + } else { + logger_->debug("[MatchingAdapter::processPullQuote] QuoteNotification on {} pulled" + , request.rfq_id()); + } + return true; +} + void MatchingAdapter::saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId) { quoteIdMap_[quoteId] = quoteReqId; @@ -457,14 +501,43 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) return pushFill(env); } -bool MatchingAdapter::onQuoteReject(const std::string&) +bool MatchingAdapter::onQuoteReject(const std::string& data) { - return false; + QuoteRequestRejectDownstreamEvent response; + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onQuoteReject] failed to parse"); + return false; + } + logger_->debug("[MatchingAdapter::onQuoteReject] {}", ProtobufUtils::toJsonCompact(response)); + + MatchingMessage msg; + auto msgReq = msg.mutable_quote_reject(); + msgReq->set_rfq_id(response.quoterequestid()); + if (response.quoterequestrejectgroup_size() > 0) { + const QuoteRequestRejectGroup& rejGrp = response.quoterequestrejectgroup(0); + msgReq->set_reject_text(rejGrp.text()); + } + msgReq->set_reject_code((int)response.quoterequestrejectreason()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); } -bool MatchingAdapter::onOrderReject(const std::string&) +bool MatchingAdapter::onOrderReject(const std::string& data) { - return false; + CreateOrderRequestRejectDownstreamEvent response; + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onQuoteReject] failed to parse"); + return false; + } + logger_->debug("[MatchingAdapter::onOrderReject] {}", ProtobufUtils::toJsonCompact(response)); + + MatchingMessage msg; + auto msgReq = msg.mutable_order_reject(); + msgReq->set_order_id(response.externalclorderid()); + msgReq->set_quote_id(response.quoteid()); + msgReq->set_reject_text(response.rejectreason()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); } bool MatchingAdapter::onBitcoinOrderSnapshot(const std::string& data) @@ -542,9 +615,22 @@ bool MatchingAdapter::onFxOrderSnapshot(const std::string& data) return pushFill(env); } -bool MatchingAdapter::onQuoteCancelled(const std::string&) +bool MatchingAdapter::onQuoteCancelled(const std::string& data) { - return false; + QuoteCancelDownstreamEvent response; + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onQuoteCancelled] failed to parse"); + return false; + } + logger_->debug("[MatchingAdapter::onQuoteCancelled] {}", ProtobufUtils::toJsonCompact(response)); + + MatchingMessage msg; + auto msgData = msg.mutable_quote_cancelled(); + msgData->set_rfq_id(response.quoterequestid()); + msgData->set_quote_id(response.quoteid()); + msgData->set_by_user(response.quotecanceltype() == com::celertech::marketmerchant::api::enums::quotecanceltype::CANCEL_ALL_QUOTES); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); } bool MatchingAdapter::onSignTxNotif(const std::string&) @@ -634,9 +720,23 @@ bool MatchingAdapter::onQuoteReqNotification(const std::string& data) return pushFill(env); } -bool MatchingAdapter::onQuoteNotifCancelled(const std::string&) +bool MatchingAdapter::onQuoteNotifCancelled(const std::string& data) { - return false; + QuoteCancelDownstreamEvent response; + if (!response.ParseFromString(data)) { + logger_->error("[MatchingAdapter::onQuoteNotifCancelled] failed to parse"); + return false; + } + logger_->debug("[MatchingAdapter::onQuoteNotifCancelled] {}", ProtobufUtils::toJsonCompact(response)); + + MatchingMessage msg; + auto msgReq = msg.mutable_quote_cancelled(); + msgReq->set_rfq_id(response.quoterequestid()); + if (response.quotecanceltype() != com::celertech::marketmerchant::api::enums::quotecanceltype::CANCEL_ALL_QUOTES) { + msgReq->set_quote_id(response.quoteid()); + } + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); } diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 33c0b0ddb..4861cdf2f 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -21,7 +21,9 @@ namespace BlockSettle { namespace Terminal { class AcceptRFQ; class MatchingMessage_Login; + class PullRFQReply; class RFQ; + class ReplyToRFQ; } } @@ -67,6 +69,8 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget bool processSendRFQ(const BlockSettle::Terminal::RFQ&); bool processAcceptRFQ(const BlockSettle::Terminal::AcceptRFQ&); bool processCancelRFQ(const std::string& rfqId); + bool processSubmitQuote(const BlockSettle::Terminal::ReplyToRFQ&); + bool processPullQuote(const BlockSettle::Terminal::PullRFQReply&); std::string getQuoteReqId(const std::string& quoteId) const; void saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId); diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index d97a93fba..21b6e692f 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -152,6 +152,10 @@ bool SettlementAdapter::process(const bs::message::Envelope &env) return processHandshakeTimeout(msg.handshake_timeout()); case SettlementMessage::kQuoteReqTimeout: return processInRFQTimeout(msg.quote_req_timeout()); + case SettlementMessage::kReplyToRfq: + return processSubmitQuote(env, msg.reply_to_rfq()); + case SettlementMessage::kPullRfqReply: + return processPullQuote(env, msg.pull_rfq_reply()); default: logger_->warn("[{}] unknown settlement request {}", __func__, msg.data_case()); break; @@ -194,9 +198,13 @@ bool SettlementAdapter::processMatchingQuote(const BlockSettle::Terminal::Quote& logger_->error("[{}] unknown settlement for {}", __func__, response.request_id()); return true; } + itSettl->second->quote = fromMsg(response); + const auto& itQuote = settlByQuoteId_.find(response.quote_id()); + if (itQuote == settlByQuoteId_.end()) { + settlByQuoteId_[response.quote_id()] = itSettl->second; + } SettlementMessage msg; - auto msgResp = msg.mutable_quote(); - *msgResp = response; + *msg.mutable_quote() = response; Envelope env{ 0, user_, itSettl->second->env.sender, {}, {} , msg.SerializeAsString() }; return pushFill(env); @@ -512,6 +520,94 @@ bool SettlementAdapter::processSendRFQ(const bs::message::Envelope& env return pushFill(envReq); } +bool SettlementAdapter::processSubmitQuote(const bs::message::Envelope& env + , const ReplyToRFQ& request) +{ + const auto& itSettl = settlByRfqId_.find(request.quote().request_id()); + if (itSettl == settlByRfqId_.end()) { + logger_->error("[{}] RFQ {} not found", __func__, request.quote().request_id()); + return true; + } + logger_->debug("[{}] sess token: {}, account: {}, recv addr: {}", __func__ + , request.session_token(), request.account(), request.dealer_recv_addr()); + itSettl->second->quote = fromMsg(request.quote()); + itSettl->second->env = env; + if (!request.dealer_recv_addr().empty()) { + try { + itSettl->second->dealerRecvAddress = bs::Address::fromAddressString(request.dealer_recv_addr()); + } catch (const std::exception&) { + logger_->warn("[{}] invalid dealer recv address {}", __func__, request.dealer_recv_addr()); + } + } + + BinaryData settlementId; + try { + settlementId = BinaryData::CreateFromHex(itSettl->second->quote.settlementId); + } + catch (const std::exception&) { + logger_->warn("[{}] invalid settlementId format: {}", __func__, itSettl->second->quote.settlementId); + } + if (!settlementId.empty()) { + if (!itSettl->second->settlementId.empty() && (itSettl->second->settlementId != settlementId)) { + logger_->error("[{}] settlementId mismatch", __func__); + return true; + } + itSettl->second->settlementId = settlementId; + settlBySettlId_[settlementId] = itSettl->second; + } + + MatchingMessage msg; + *msg.mutable_submit_quote_notif() = request; + Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; + return pushFill(envReq); +} + +bool SettlementAdapter::processPullQuote(const bs::message::Envelope& env + , const PullRFQReply& request) +{ + const auto& itSettl = settlByRfqId_.find(request.rfq_id()); + if (itSettl == settlByRfqId_.end()) { + logger_->error("[{}] RFQ {} not found", __func__, request.rfq_id()); + return true; + } + if (!itSettl->second->env.sender || (itSettl->second->env.sender->value() != env.sender->value())) { + logger_->error("[{}] invalid or different sender of quote submit", __func__); + return true; + } + + const auto& itSettlById = settlBySettlId_.find(itSettl->second->settlementId); + if (itSettlById != settlBySettlId_.end()) { + unreserve(itSettl->first); + settlBySettlId_.erase(itSettlById); + } + + MatchingMessage msg; + auto msgData = msg.mutable_pull_quote_notif(); + *msgData = request; + Envelope envReq{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString(), true }; + return pushFill(envReq); +} + +bool SettlementAdapter::processQuoteCancelled(const QuoteCancelled& request) +{ + const auto& itRFQ = settlByRfqId_.find(request.rfq_id()); + if (itRFQ == settlByRfqId_.end()) { + logger_->error("[{}] unknown RFQ {}", __func__, request.rfq_id()); + return true; + } + const auto& itQuote = settlByQuoteId_.find(request.quote_id()); + if (itQuote == settlByQuoteId_.end()) { + logger_->error("[{}] quote {} not found", __func__, request.quote_id()); + return true; + } + settlByQuoteId_.erase(itQuote); + + SettlementMessage msg; + *msg.mutable_quote_cancelled() = request; + Envelope env{ 0, user_, itRFQ->second->env.sender, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} + bool SettlementAdapter::processXbtTx(uint64_t msgId, const WalletsMessage_XbtTxResponse& response) { const auto& itPayin = payinRequests_.find(msgId); diff --git a/Core/SettlementAdapter.h b/Core/SettlementAdapter.h index e2a28ee06..696f3fc31 100644 --- a/Core/SettlementAdapter.h +++ b/Core/SettlementAdapter.h @@ -30,7 +30,10 @@ namespace BlockSettle { class BsServerMessage_SignXbtHalf; class IncomingRFQ; class MatchingMessage_Order; + class PullRFQReply; class Quote; + class QuoteCancelled; + class ReplyToRFQ; class SettlementMessage_SendRFQ; } } @@ -63,8 +66,14 @@ class SettlementAdapter : public bs::message::Adapter , const BlockSettle::Terminal::AcceptRFQ&); bool processSendRFQ(const bs::message::Envelope& , const BlockSettle::Terminal::SettlementMessage_SendRFQ&); + + bool processSubmitQuote(const bs::message::Envelope&, const BlockSettle::Terminal::ReplyToRFQ&); + bool processPullQuote(const bs::message::Envelope&, const BlockSettle::Terminal::PullRFQReply&); + bool processQuoteCancelled(const BlockSettle::Terminal::QuoteCancelled&); + bool processXbtTx(uint64_t msgId, const BlockSettle::Common::WalletsMessage_XbtTxResponse&); bool processSignedTx(uint64_t msgId, const BlockSettle::Common::SignerMessage_SignTxResponse&); + bool processHandshakeTimeout(const std::string& id); bool processInRFQTimeout(const std::string& id); @@ -93,6 +102,7 @@ class SettlementAdapter : public bs::message::Adapter bs::Address settlementAddress; std::string txComment; bs::Address recvAddress; + bs::Address dealerRecvAddress; bs::Address ownAuthAddr; BinaryData ownKey; BinaryData counterKey; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index b7097b43e..49f0fece4 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -1020,6 +1020,7 @@ void MainWindow::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &fields) { ui_->widgetRFQ->onMDUpdated(assetType, security, fields); + ui_->widgetRFQReply->onMDUpdated(assetType, security, fields); ui_->widgetPortfolio->onMDUpdated(assetType, security, fields); } @@ -1094,6 +1095,12 @@ void MainWindow::onOrdersUpdate(const std::vector& orders) orderListModel_->onOrdersUpdate(orders); } +void bs::gui::qt::MainWindow::onQuoteCancelled(const std::string& rfqId + , const std::string& quoteId, bool byUser) +{ + ui_->widgetRFQReply->onQuoteCancelled(QString::fromStdString(rfqId), byUser); +} + void MainWindow::onReservedUTXOs(const std::string& resId , const std::string& subId, const std::vector& utxos) { diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index e4cdd9f2b..da7c354e4 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -122,6 +122,8 @@ namespace bs { , const BinaryData& settlementId); void onQuoteReqNotification(const bs::network::QuoteReqNotification&); void onOrdersUpdate(const std::vector&); + void onQuoteCancelled(const std::string& rfqId, const std::string& quoteId + , bool byUser); void onReservedUTXOs(const std::string& resId, const std::string& subId , const std::vector&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index d63e3f0f6..e4f488df1 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -1633,6 +1633,8 @@ bool QtGuiAdapter::processSettlement(const bs::message::Envelope& env) return processSettlComplete(msg.settlement_complete()); case SettlementMessage::kQuoteReqNotif: return processQuoteReqNotif(msg.quote_req_notif()); + case SettlementMessage::kQuoteCancelled: + return processQuoteCancelled(msg.quote_cancelled()); default: break; } return true; @@ -1885,6 +1887,13 @@ bool QtGuiAdapter::sendPooledOrdersUpdate() }); } +bool QtGuiAdapter::processQuoteCancelled(const QuoteCancelled& msg) +{ + return QMetaObject::invokeMethod(mainWindow_, [this, msg]{ + mainWindow_->onQuoteCancelled(msg.rfq_id(), msg.quote_id(), msg.by_user()); + }); +} + bool QtGuiAdapter::processReservedUTXOs(const WalletsMessage_ReservedUTXOs& response) { std::vector utxos; diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 93ec2974a..ce634a389 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -52,6 +52,7 @@ namespace BlockSettle { class BsServerMessage_StartLoginResult; class MatchingMessage_LoggedIn; class Quote; + class QuoteCancelled; class IncomingRFQ; class MatchingMessage_Order; class MktDataMessage_Prices; @@ -145,6 +146,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processQuoteReqNotif(const BlockSettle::Terminal::IncomingRFQ&); bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); bool sendPooledOrdersUpdate(); + bool processQuoteCancelled(const BlockSettle::Terminal::QuoteCancelled&); bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); private slots: diff --git a/common b/common index 67a3a6068..6e9ac501f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 67a3a60687574426bf4540552f8924d1ccbc60f3 +Subproject commit 6e9ac501f7e446cb06d4180c8653ba4363536cf3 From 85bfd87488978ee474919cde9f3ed3b2aed440ae Mon Sep 17 00:00:00 2001 From: Ation Date: Tue, 17 Nov 2020 18:02:22 +0200 Subject: [PATCH 039/146] Create tx for delivery --- BlockSettleUILib/BSTerminalMainWindow.cpp | 55 ++++++++++++++++--- .../CreateTransactionDialogAdvanced.cpp | 2 +- BlockSettleUILib/UtxoReservationManager.h | 4 +- common | 2 +- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 29b79ce67..c9983c6de 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2419,19 +2419,19 @@ void BSTerminalMainWindow::openURIDialog() // open create transaction dialog const auto requestInfo = dlg.getRequestInfo(); - std::shared_ptr cerateTxDlg; + std::shared_ptr createTxDlg; if (applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { - cerateTxDlg = CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsMgr_ + createTxDlg = CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsMgr_ , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ , requestInfo, this); } else { - cerateTxDlg = CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsMgr_ + createTxDlg = CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsMgr_ , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ , requestInfo, this); } - DisplayCreateTransactionDialog(cerateTxDlg); + DisplayCreateTransactionDialog(createTxDlg); } } @@ -2486,18 +2486,57 @@ void BSTerminalMainWindow::onAuthLeafCreated() void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &message) { if (message.data_case() == Blocksettle::Communication::ProxyTerminalPb::Response::kDeliveryRequest) { - const auto& request = message.delivery_request(); + const auto& request = message.delivery_request();\ + auto uiLogger = logMgr_->logger("ui"); const int64_t toDeliver = request.to_deliver(); + bs::Address bsAddress; + + try { + bsAddress = bs::Address::fromAddressString(request.bs_address()); + } catch(...) { + uiLogger->error("[BSTerminalMainWindow::onMessageFromPB] could not parse BS address: {}" + , request.bs_address()); + return; + } + + QString deliverMessage; const QString header = tr("Trade obligations"); if (toDeliver < 0) { - deliverMessage = tr("You have obligations to deliver %1 XBT in next 24h.").arg(UiUtils::displayAmount(bs::XBTAmount{static_cast(-toDeliver)})); + + bs::XBTAmount amountToDeliver{static_cast(-toDeliver)}; + + deliverMessage = tr("You have obligations to deliver %1 XBT in next 24h to %2.").arg(UiUtils::displayAmount(amountToDeliver), QString::fromStdString(bsAddress.display())); + showInfo(header, deliverMessage); + + + std::shared_ptr createTxDlg; + + Bip21::PaymentRequestInfo requestInfo; + + requestInfo.address = QString::fromStdString(bsAddress.display()); + requestInfo.amount = amountToDeliver; + requestInfo.message = tr("EURXBT1 delivery"); + requestInfo.feePerByte = utxoReservationMgr_->feeRatePb(); + //requestInfo.requestExpireDateTime = ; + + if (applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { + createTxDlg = CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsMgr_ + , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ + , requestInfo, this); + } else { + createTxDlg = CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsMgr_ + , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ + , requestInfo, this); + } + + DisplayCreateTransactionDialog(createTxDlg); + } else { deliverMessage = tr("You are about to receive %1 XBT. Please make sure to deliver your cash obligations.").arg(UiUtils::displayAmount(bs::XBTAmount{ static_cast(toDeliver)})); + showInfo(header, deliverMessage); } - - showInfo(header, deliverMessage); } } diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index 450f1cb73..6c8d4bf42 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -152,7 +152,7 @@ std::shared_ptr CreateTransactionDialogAdvanced::Create if (!paymentInfo.requestURL.isEmpty()) { dlg->ui_->checkBoxRBF->setChecked(false); dlg->ui_->checkBoxRBF->setEnabled(false); - dlg->ui_->checkBoxRBF->setToolTip(tr("RBF disabled for BitPay request")); + dlg->ui_->checkBoxRBF->setToolTip(tr("RBF disabled for payment request")); dlg->nam_ = std::make_shared(); } diff --git a/BlockSettleUILib/UtxoReservationManager.h b/BlockSettleUILib/UtxoReservationManager.h index 69dbf1e50..cb36c90a6 100644 --- a/BlockSettleUILib/UtxoReservationManager.h +++ b/BlockSettleUILib/UtxoReservationManager.h @@ -71,7 +71,7 @@ namespace bs { BTCNumericTypes::satoshi_type getAvailableXbtUtxoSum(const HDWalletId& walletId) const; BTCNumericTypes::satoshi_type getAvailableXbtUtxoSum(const HDWalletId& walletId, bs::hd::Purpose purpose) const; - + std::vector getAvailableXbtUTXOs(const HDWalletId& walletId) const; std::vector getAvailableXbtUTXOs(const HDWalletId& walletId, bs::hd::Purpose purpose) const; @@ -80,7 +80,7 @@ namespace bs { std::function&&)>&& cb, bool checkPbFeeFloor, CheckAmount checkAmount); void getBestXbtUtxoSet(const HDWalletId& walletId, bs::hd::Purpose purpose, BTCNumericTypes::satoshi_type quantity, std::function&&)>&& cb, bool checkPbFeeFloor, CheckAmount checkAmount); - + // CC specific implementation BTCNumericTypes::balance_type getAvailableCCUtxoSum(const CCProductName& CCProduct) const; std::vector getAvailableCCUTXOs(const CCWalletId& walletId) const; diff --git a/common b/common index 0ec99602e..da17396c6 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 0ec99602ebfa4997c78d5572829480a40999b985 +Subproject commit da17396c62d95aaa0ac01cec7d4abe5e7d1542ea From f99414a45ee8181725a30d91a60c3bc50b6d3329 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 18 Nov 2020 13:49:56 +0200 Subject: [PATCH 040/146] Return user address for delivery --- BlockSettleUILib/BSTerminalMainWindow.cpp | 20 +++++++++++++++++++- BlockSettleUILib/BSTerminalMainWindow.h | 2 ++ common | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index c9983c6de..00fd43ad6 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2535,8 +2535,26 @@ void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &mess DisplayCreateTransactionDialog(createTxDlg); } else { - deliverMessage = tr("You are about to receive %1 XBT. Please make sure to deliver your cash obligations.").arg(UiUtils::displayAmount(bs::XBTAmount{ static_cast(toDeliver)})); + deliverMessage = tr("You are about to receive %1 XBT. Please make sure to deliver your cash obligations. Press OK to submit delivery address.").arg(UiUtils::displayAmount(bs::XBTAmount{ static_cast(toDeliver)})); showInfo(header, deliverMessage); + + SendBSDeliveryAddress(); } } } + +void BSTerminalMainWindow::SendBSDeliveryAddress() +{ + const auto wallet = walletsMgr_->getDefaultWallet(); + + const auto &cbAddr = [this, wallet](const bs::Address &address) { + if (address.isValid()) { + + bsClient_->submitDeliveryAddress(address); + + wallet->setAddressComment(address, "EURXBT1 delivery"); + wallet->syncAddresses(); + } + }; + wallet->getNewExtAddress(cbAddr); +} diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index 44c2ebdeb..3a751c8f5 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -305,6 +305,8 @@ private slots: void onBootstrapDataLoaded(const std::string& data); + void SendBSDeliveryAddress(); + private: enum class ChatInitState { diff --git a/common b/common index da17396c6..e182c6340 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit da17396c62d95aaa0ac01cec7d4abe5e7d1542ea +Subproject commit e182c6340bc4e9fd37e39597e6a790b468126298 From 164114644b6e75d199b0b55e14cb649e0a04bcd3 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 18 Nov 2020 14:31:20 +0200 Subject: [PATCH 041/146] Fix on delivery message processing --- BlockSettleUILib/BSTerminalMainWindow.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 00fd43ad6..49bc4ffa6 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2490,21 +2490,21 @@ void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &mess auto uiLogger = logMgr_->logger("ui"); const int64_t toDeliver = request.to_deliver(); - bs::Address bsAddress; - - try { - bsAddress = bs::Address::fromAddressString(request.bs_address()); - } catch(...) { - uiLogger->error("[BSTerminalMainWindow::onMessageFromPB] could not parse BS address: {}" - , request.bs_address()); - return; - } - - + QString deliverMessage; const QString header = tr("Trade obligations"); if (toDeliver < 0) { + bs::Address bsAddress; + + try { + bsAddress = bs::Address::fromAddressString(request.bs_address()); + } + catch (...) { + uiLogger->error("[BSTerminalMainWindow::onMessageFromPB] could not parse BS address: {}" + , request.bs_address()); + return; + } bs::XBTAmount amountToDeliver{static_cast(-toDeliver)}; From 7e541c0c2077aca66499430a7c4534606bc75e49 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 19 Nov 2020 20:08:16 +0300 Subject: [PATCH 042/146] XBT dealing --- .../Trading/QuoteRequestsModel.cpp | 37 ++- BlockSettleUILib/Trading/QuoteRequestsModel.h | 4 + .../Trading/QuoteRequestsWidget.cpp | 16 ++ .../Trading/QuoteRequestsWidget.h | 5 + BlockSettleUILib/Trading/RFQDealerReply.cpp | 246 ++++++++++++------ BlockSettleUILib/Trading/RFQDealerReply.h | 6 + BlockSettleUILib/Trading/RFQReplyWidget.cpp | 83 ++++-- BlockSettleUILib/Trading/RFQReplyWidget.h | 13 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 5 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 2 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 13 +- BlockSettleUILib/UiUtils.cpp | 4 +- Core/BsServerAdapter.cpp | 11 +- Core/BsServerAdapter.h | 2 +- Core/MatchingAdapter.cpp | 58 ++--- Core/MatchingAdapter.h | 4 - Core/SettlementAdapter.cpp | 102 +++++--- Core/SettlementAdapter.h | 2 +- Core/SignerAdapter.cpp | 18 +- GUI/QtWidgets/MainWindow.cpp | 10 +- GUI/QtWidgets/MainWindow.h | 2 +- GUI/QtWidgets/QtGuiAdapter.cpp | 6 +- GUI/QtWidgets/QtGuiAdapter.h | 3 +- common | 2 +- 24 files changed, 436 insertions(+), 218 deletions(-) diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index ec599633e..c17971a27 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -603,7 +603,8 @@ void QuoteRequestsModel::SetAssetManager(const std::shared_ptr& as assetManager_ = assetManager; } -void QuoteRequestsModel::ticker() { +void QuoteRequestsModel::ticker() +{ std::unordered_set deletedRows; const auto timeNow = QDateTime::currentDateTime(); @@ -667,12 +668,23 @@ void QuoteRequestsModel::ticker() { notifications_.erase(delRow); } - for (const auto &settlContainer : settlContainers_) { - forSpecificId(settlContainer.second->id(), - [timeLeft = settlContainer.second->timeLeftMs()](Group *grp, int itemIndex) { - grp->rfqs_[static_cast(itemIndex)]->status_.timeleft_ = - static_cast(timeLeft); - }); + if (!settlContainers_.empty()) { + for (const auto& settlContainer : settlContainers_) { + forSpecificId(settlContainer.second->id(), + [timeLeft = settlContainer.second->timeLeftMs()](Group* grp, int itemIndex) { + grp->rfqs_[static_cast(itemIndex)]->status_.timeleft_ = + static_cast(timeLeft); + }); + } + } + else { + for (const auto& settl : settlTimeLeft_) { + forSpecificId(settl.first, [timeLeft = settl.second] + (Group* grp, int itemIndex) { + grp->rfqs_[static_cast(itemIndex)]->status_.timeleft_ = + static_cast(timeLeft); + }); + } } if (!data_.empty()) { @@ -939,6 +951,17 @@ bool QuoteRequestsModel::StartCCSignOnOrder(const QString& orderId, QDateTime ti return false; } +void QuoteRequestsModel::onSettlementPending(const BinaryData& settlementId + , int timeLeftMS) +{ + settlTimeLeft_[settlementId.toHexStr()] = timeLeftMS; +} + +void QuoteRequestsModel::onSettlementComplete(const BinaryData& settlementId) +{ + settlTimeLeft_.erase(settlementId.toHexStr()); +} + void QuoteRequestsModel::addSettlementContainer(const std::shared_ptr &container) { const auto &id = container->id(); diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h index 3eb898345..0587fb5e4 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.h @@ -114,6 +114,9 @@ class QuoteRequestsModel : public QAbstractItemModel void addSettlementContainer(const std::shared_ptr &); bool StartCCSignOnOrder(const QString& orderId, QDateTime timestamp); + void onSettlementPending(const BinaryData& settlementId, int timeLeftMS); + void onSettlementComplete(const BinaryData& settlementId); + public: int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -150,6 +153,7 @@ private slots: std::shared_ptr assetManager_; std::unordered_map notifications_; std::unordered_map> settlContainers_; + std::unordered_map settlTimeLeft_; QTimer timer_; QTimer priceUpdateTimer_; MDPrices mdPrices_; diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp index 20456194c..706d628ac 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp @@ -247,6 +247,22 @@ RFQBlotterTreeView* QuoteRequestsWidget::view() const return ui_->treeViewQuoteRequests; } +void QuoteRequestsWidget::onSettlementPending(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) +{ + if (model_) { + model_->onSettlementPending(settlementId, timeLeftMS); + } +} + +void QuoteRequestsWidget::onSettlementComplete(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId) +{ + if (model_) { + model_->onSettlementComplete(settlementId); + } +} + void QuoteRequestsWidget::onQuoteReqNotifReplied(const bs::network::QuoteNotification &qn) { if (model_) { diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.h b/BlockSettleUILib/Trading/QuoteRequestsWidget.h index c929b6d00..c14cdb69d 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.h +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.h @@ -138,6 +138,11 @@ Q_OBJECT RFQBlotterTreeView* view() const; + void onSettlementPending(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId, int timeLeftMS); + void onSettlementComplete(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId); + signals: void Selected(const QString& productGroup, const bs::network::QuoteReqNotification& qrc, double indicBid, double indicAsk); void quoteReqNotifStatusChanged(const bs::network::QuoteReqNotification &qrn); diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index 66cf488a4..d4ec212bd 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -239,9 +239,11 @@ void RFQDealerReply::reset() selectedXbtInputs_.clear(); } else { - const auto* lastSettlement = getLastUTXOReplyCb_(currentQRN_.settlementId); - if (lastSettlement && selectedXbtInputs_ != *lastSettlement) { - selectedXbtInputs_ = *lastSettlement; + if (getLastUTXOReplyCb_) { + const auto* lastSettlement = getLastUTXOReplyCb_(currentQRN_.settlementId); + if (lastSettlement && selectedXbtInputs_ != *lastSettlement) { + selectedXbtInputs_ = *lastSettlement; + } } } } @@ -378,12 +380,14 @@ void RFQDealerReply::updateUiWalletFor(const bs::network::QuoteReqNotification & } } } - updateWalletsList((qrn.side == bs::network::Side::Sell) ? UiUtils::WalletsTypes::Full : UiUtils::WalletsTypes::All); + walletFlags_ = (qrn.side == bs::network::Side::Sell) ? UiUtils::WalletsTypes::Full + : UiUtils::WalletsTypes::All; } else if (qrn.assetType == bs::network::Asset::SpotXBT) { - updateWalletsList((qrn.side == bs::network::Side::Sell) - ? (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW) - : UiUtils::WalletsTypes::All); + walletFlags_ = (qrn.side == bs::network::Side::Sell) ? + (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW) + : UiUtils::WalletsTypes::All; } + updateWalletsList(walletFlags_); } void RFQDealerReply::priceChanged() @@ -396,30 +400,44 @@ void RFQDealerReply::onAuthAddrChanged(int index) { auto addressString = ui_->authenticationAddressComboBox->itemText(index).toStdString(); if (addressString.empty()) { + logger_->error("[{}] empty address string", __func__); + return; + } + const auto& authAddr = bs::Address::fromAddressString(addressString); + if ((authAddr_ == authAddr) && !authKey_.empty()) { return; } - authAddr_ = bs::Address::fromAddressString(addressString); + authAddr_ = authAddr; authKey_.clear(); if (authAddr_.empty()) { + logger_->warn("[{}] empty auth address", __func__); return; } - const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); + if (walletsManager_) { + const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); - const auto &cbPubKey = [this](const SecureBinaryData &pubKey) { - authKey_ = pubKey.toHexStr(); - QMetaObject::invokeMethod(this, &RFQDealerReply::updateSubmitButton); - }; + const auto& cbPubKey = [this](const SecureBinaryData& pubKey) { + authKey_ = pubKey.toHexStr(); + QMetaObject::invokeMethod(this, &RFQDealerReply::updateSubmitButton); + }; - if (settlLeaf) { - settlLeaf->getRootPubkey(cbPubKey); - } else { - walletsManager_->createSettlementLeaf(authAddr_, cbPubKey); + if (settlLeaf) { + settlLeaf->getRootPubkey(cbPubKey); + } else { + walletsManager_->createSettlementLeaf(authAddr_, cbPubKey); + } + } + else { + emit needAuthKey(authAddr_); } } void RFQDealerReply::updateSubmitButton() { + if (!logger_) { + return; + } if (!currentQRN_.empty() && activeQuoteSubmits_.find(currentQRN_.quoteRequestId) != activeQuoteSubmits_.end()) { // Do not allow re-enter into submitReply as it could cause problems ui_->pushButtonSubmit->setEnabled(false); @@ -622,7 +640,6 @@ void bs::ui::RFQDealerReply::onHDWallet(const bs::sync::HDWalletData& wallet) } else { wallets_.emplace(it, wallet); } - } void bs::ui::RFQDealerReply::onBalance(const std::string& currency, double balance) @@ -638,11 +655,39 @@ void bs::ui::RFQDealerReply::onWalletBalance(const bs::sync::WalletBalanceData& void bs::ui::RFQDealerReply::onAuthKey(const bs::Address& addr, const BinaryData& authKey) { if (addr == authAddr_) { - logger_->debug("[{}] got auth key: {}", __func__, authKey.toHexStr()); - authKey_ = authKey.toHexStr(); + const auto& authKeyHex = authKey.toHexStr(); + if (authKey_ != authKeyHex) { + logger_->debug("[{}] got auth key: {}", __func__, authKeyHex); + authKey_ = authKeyHex; + } } } +void bs::ui::RFQDealerReply::onVerifiedAuthAddresses(const std::vector& addrs) +{ + if (addrs.empty()) { + return; + } + UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, addrs); + onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); +} + +void bs::ui::RFQDealerReply::onReservedUTXOs(const std::string& resId + , const std::string& subId, const std::vector& utxos) +{ + const auto& itRes = pendingReservations_.find(resId); + if (itRes == pendingReservations_.end()) { + return; // not our request + } + if (utxos.empty()) { + logger_->warn("[{}] UTXO reservation failed", __func__); + pendingReservations_.erase(itRes); + return; + } + submit(itRes->second->price, itRes->second); + pendingReservations_.erase(itRes); +} + void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn , double price, ReplyType replyType) { @@ -661,13 +706,15 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn replyData->qn = bs::network::QuoteNotification(qrn, authKey_, price, ""); if (qrn.assetType != bs::network::Asset::SpotFX) { - replyData->walletPurpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet);; if (walletsManager_) { replyData->xbtWallet = getSelectedXbtWallet(replyType); if (!replyData->xbtWallet) { SPDLOG_LOGGER_ERROR(logger_, "can't submit CC/XBT reply without XBT wallet"); return; } + if (!replyData->xbtWallet->canMixLeaves()) { + replyData->walletPurpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); + } } else { replyData->xbtWalletId = getSelectedXbtWalletId(replyType); @@ -736,7 +783,7 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn SPDLOG_LOGGER_ERROR(logger_, "empty XBT leaves in wallet {}", replyData->xbtWallet->walletId()); return; } - auto xbtWallets = std::vector>(xbtLeaves.begin(), xbtLeaves.end()); + auto xbtWallets = std::vector>(xbtLeaves.begin(), xbtLeaves.end()); auto xbtWallet = xbtWallets.front(); const auto &spendWallet = isSpendCC ? ccWallet : xbtWallet; @@ -1183,89 +1230,120 @@ void bs::ui::RFQDealerReply::submit(double price, const std::shared_ptr& replyData, ReplyType replyType) { - auto replyRFQWrapper = [rfqReply = QPointer(this), - price, replyData, replyType] (std::vector utxos) { - if (!rfqReply) { - return; - } + if (walletsManager_ && utxoReservationManager_) { + auto replyRFQWrapper = [rfqReply = QPointer(this), + price, replyData, replyType](std::vector utxos) { + if (!rfqReply) { + return; + } + + if (utxos.empty()) { + if (replyType == ReplyType::Manual) { + replyData->fixedXbtInputs = rfqReply->selectedXbtInputs_; + replyData->utxoRes = std::move(rfqReply->selectedXbtRes_); + } + + rfqReply->submit(price, replyData); + return; + } - if (utxos.empty()) { if (replyType == ReplyType::Manual) { - replyData->fixedXbtInputs = rfqReply->selectedXbtInputs_; - replyData->utxoRes = std::move(rfqReply->selectedXbtRes_); + rfqReply->selectedXbtInputs_ = utxos; } + replyData->utxoRes = rfqReply->utxoReservationManager_->makeNewReservation(utxos); + replyData->fixedXbtInputs = std::move(utxos); + rfqReply->submit(price, replyData); - return; + }; + + if ((replyData->qn.side == bs::network::Side::Sell && replyData->qn.product != bs::network::XbtCurrency) || + (replyData->qn.side == bs::network::Side::Buy && replyData->qn.product == bs::network::XbtCurrency)) { + replyRFQWrapper({}); + return; // Nothing to reserve } - if (replyType == ReplyType::Manual) { - rfqReply->selectedXbtInputs_ = utxos; + // We shouldn't recalculate better utxo set if that not first quote response + // otherwise, we should chose best set if that wasn't done by user and this is not auto quoting script + if (sentNotifs_.count(replyData->qn.quoteRequestId) || (!selectedXbtInputs_.empty() && replyType == ReplyType::Manual)) { + replyRFQWrapper({}); + return; // already reserved by user } - replyData->utxoRes = rfqReply->utxoReservationManager_->makeNewReservation(utxos); - replyData->fixedXbtInputs = std::move(utxos); + auto security = mdInfo_.find(replyData->qn.security); + if (security == mdInfo_.end()) { + // there is no MD data available so we really can't forecast + replyRFQWrapper({}); + return; + } - rfqReply->submit(price, replyData); - }; + BTCNumericTypes::satoshi_type xbtQuantity = 0; + if (replyData->qn.side == bs::network::Side::Buy) { + if (replyData->qn.assetType == bs::network::Asset::PrivateMarket) { + xbtQuantity = XBTAmount(quantity * mdInfo_[replyData->qn.security].bidPrice).GetValue(); + } else if (replyData->qn.assetType == bs::network::Asset::SpotXBT) { + xbtQuantity = XBTAmount(quantity / mdInfo_[replyData->qn.security].askPrice).GetValue(); + } + } else { + xbtQuantity = XBTAmount(quantity).GetValue(); + } + xbtQuantity = static_cast(xbtQuantity * tradeutils::reservationQuantityMultiplier()); - if ((replyData->qn.side == bs::network::Side::Sell && replyData->qn.product != bs::network::XbtCurrency) || - (replyData->qn.side == bs::network::Side::Buy && replyData->qn.product == bs::network::XbtCurrency)) { - replyRFQWrapper({}); - return; // Nothing to reserve - } + auto cbBestUtxoSet = [rfqReply = QPointer(this), + replyRFQ = std::move(replyRFQWrapper)](std::vector&& utxos) { + if (!rfqReply) { + return; + } + replyRFQ(std::move(utxos)); + }; - // We shouldn't recalculate better utxo set if that not first quote response - // otherwise, we should chose best set if that wasn't done by user and this is not auto quoting script - if (sentNotifs_.count(replyData->qn.quoteRequestId) || (!selectedXbtInputs_.empty() && replyType == ReplyType::Manual)) { - replyRFQWrapper({}); - return; // already reserved by user - } + // Check amount (required for AQ scripts) + auto checkAmount = bs::UTXOReservationManager::CheckAmount::Enabled; - auto security = mdInfo_.find(replyData->qn.security); - if (security == mdInfo_.end()) { - // there is no MD data available so we really can't forecast - replyRFQWrapper({}); - return; + if (!replyData->xbtWallet->canMixLeaves()) { + auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); + utxoReservationManager_->getBestXbtUtxoSet(replyData->xbtWallet->walletId(), purpose, + xbtQuantity, cbBestUtxoSet, true, checkAmount); + } else { + utxoReservationManager_->getBestXbtUtxoSet(replyData->xbtWallet->walletId(), + xbtQuantity, cbBestUtxoSet, true, checkAmount); + } } - - BTCNumericTypes::satoshi_type xbtQuantity = 0; - if (replyData->qn.side == bs::network::Side::Buy) { - if (replyData->qn.assetType == bs::network::Asset::PrivateMarket) { - xbtQuantity = XBTAmount(quantity * mdInfo_[replyData->qn.security].bidPrice).GetValue(); + else { // new code + replyData->price = price; + if ((replyData->qn.side == bs::network::Side::Sell && replyData->qn.product != bs::network::XbtCurrency) || + (replyData->qn.side == bs::network::Side::Buy && replyData->qn.product == bs::network::XbtCurrency)) { + submit(price, replyData); + return; // Nothing to reserve } - else if (replyData->qn.assetType == bs::network::Asset::SpotXBT) { - xbtQuantity = XBTAmount(quantity / mdInfo_[replyData->qn.security].askPrice).GetValue(); + // We shouldn't recalculate better utxo set if that not first quote response + // otherwise, we should chose best set if that wasn't done by user and this is not auto quoting script + if (sentNotifs_.count(replyData->qn.quoteRequestId) || + (!selectedXbtInputs_.empty() && replyType == ReplyType::Manual)) { + submit(price, replyData); + return; // already reserved by user } - } - else { - xbtQuantity = XBTAmount(quantity).GetValue(); - } - xbtQuantity = static_cast(xbtQuantity * tradeutils::reservationQuantityMultiplier()); - auto cbBestUtxoSet = [rfqReply = QPointer(this), - replyRFQ = std::move(replyRFQWrapper)](std::vector&& utxos) { - if (!rfqReply) { + if (mdInfo_.find(replyData->qn.security) == mdInfo_.end()) { + logger_->warn("[{}] no MD found for {}", __func__, replyData->qn.security); + submit(price, replyData); return; } - replyRFQ(std::move(utxos)); - }; - - // Check amount (required for AQ scripts) - auto checkAmount = bs::UTXOReservationManager::CheckAmount::Enabled; - - if (!replyData->xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - utxoReservationManager_->getBestXbtUtxoSet(replyData->xbtWallet->walletId(), purpose, - xbtQuantity, cbBestUtxoSet, true, checkAmount); - } - else { - utxoReservationManager_->getBestXbtUtxoSet(replyData->xbtWallet->walletId(), - xbtQuantity, cbBestUtxoSet, true, checkAmount); + pendingReservations_[replyData->qn.quoteRequestId] = replyData; + BTCNumericTypes::satoshi_type xbtQuantity = 0; + if (replyData->qn.side == bs::network::Side::Buy) { + if (replyData->qn.assetType == bs::network::Asset::PrivateMarket) { + xbtQuantity = XBTAmount(quantity * mdInfo_[replyData->qn.security].bidPrice).GetValue(); + } else if (replyData->qn.assetType == bs::network::Asset::SpotXBT) { + xbtQuantity = XBTAmount(quantity / mdInfo_[replyData->qn.security].askPrice).GetValue(); + } + } else { + xbtQuantity = XBTAmount(quantity).GetValue(); + } + xbtQuantity = static_cast(xbtQuantity * tradeutils::reservationQuantityMultiplier()); + emit needReserveUTXOs(replyData->qn.quoteRequestId, replyData->xbtWalletId, xbtQuantity); } - - } void bs::ui::RFQDealerReply::refreshSettlementDetails() diff --git a/BlockSettleUILib/Trading/RFQDealerReply.h b/BlockSettleUILib/Trading/RFQDealerReply.h index 6bd7b6f40..4c0186bdf 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.h +++ b/BlockSettleUILib/Trading/RFQDealerReply.h @@ -81,6 +81,7 @@ namespace bs { bs::Address authAddr; std::vector fixedXbtInputs; bs::hd::Purpose walletPurpose; + double price; }; class RFQDealerReply : public QWidget @@ -126,6 +127,9 @@ namespace bs { void onBalance(const std::string& currency, double balance); void onWalletBalance(const bs::sync::WalletBalanceData&); void onAuthKey(const bs::Address&, const BinaryData& authKey); + void onVerifiedAuthAddresses(const std::vector&); + void onReservedUTXOs(const std::string& resId, const std::string& subId + , const std::vector&); signals: void pullQuoteNotif(const std::string& settlementId, const std::string& reqId, const std::string& reqSessToken); @@ -259,8 +263,10 @@ namespace bs { std::set activeQuoteSubmits_; std::map(AddressType::Max) + 1>>> addresses_; + int walletFlags_{ 0 }; std::vector wallets_; std::unordered_map balances_; + std::unordered_map> pendingReservations_; }; } //namespace ui diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index fb12beaa1..5a1db340f 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -159,11 +159,69 @@ void RFQReplyWidget::onAuthKey(const bs::Address& addr, const BinaryData& authKe ui_->pageRFQReply->onAuthKey(addr, authKey); } +void RFQReplyWidget::onVerifiedAuthAddresses(const std::vector& addrs) +{ + ui_->pageRFQReply->onVerifiedAuthAddresses(addrs); +} + +void RFQReplyWidget::onReservedUTXOs(const std::string& resId + , const std::string& subId, const std::vector& utxos) +{ + ui_->pageRFQReply->onReservedUTXOs(resId, subId, utxos); +} + void RFQReplyWidget::onQuoteReqNotification(const bs::network::QuoteReqNotification& qrn) { ui_->widgetQuoteRequests->onQuoteRequest(qrn); } +void RFQReplyWidget::onQuoteMatched(const std::string& rfqId + , const std::string& quoteId) +{ + logger_->debug("[{}] {}", __func__, rfqId); + const auto& itAsset = submittedQuote_.find(rfqId); + if (itAsset == submittedQuote_.end()) { + logger_->debug("[{}] not our match on {}", __func__, rfqId); + return; + } + if (itAsset->second == bs::network::Asset::SpotFX) { + onCompleteSettlement(rfqId); + } + else if (itAsset->second == bs::network::Asset::SpotXBT) { + //TODO + } + else if (itAsset->second == bs::network::Asset::PrivateMarket) { + //TODO + } + submittedQuote_.erase(itAsset); +} + +void RFQReplyWidget::onQuoteFailed(const std::string& rfqId, const std::string& quoteId + , const std::string& info) +{ + logger_->debug("[{}] {}", __func__, rfqId); +} + +void RFQReplyWidget::onSettlementPending(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) +{ + logger_->debug("[{}] {}", __func__, rfqId); + const auto& itAsset = submittedQuote_.find(rfqId); + if (itAsset == submittedQuote_.end()) { + logger_->debug("[{}] not our settlement on {}", __func__, rfqId); + return; + } + ui_->widgetQuoteRequests->onSettlementPending(rfqId, quoteId, settlementId, timeLeftMS); +} + +void RFQReplyWidget::onSettlementComplete(const std::string& rfqId + , const std::string& quoteId, const BinaryData& settlementId) +{ + logger_->debug("[{}] {}", __func__, rfqId); + ui_->widgetQuoteRequests->onSettlementComplete(rfqId, quoteId, settlementId); + onCompleteSettlement(rfqId); +} + void RFQReplyWidget::init(const std::shared_ptr &logger , const std::shared_ptr& celerClient , const std::shared_ptr &authAddressManager @@ -294,19 +352,8 @@ void RFQReplyWidget::init(const std::shared_ptr& logger { statsCollector_->onQuoteSubmitted(data->qn); emit submitQuote(data->qn); + submittedQuote_[data->qn.quoteRequestId] = data->qn.assetType; ui_->widgetQuoteRequests->onQuoteReqNotifReplied(data->qn); - onReplied(data); - }); - - ui_->pageRFQReply->setGetLastSettlementReply([this] - (const std::string& settlementId) -> const std::vector* - { - auto lastReply = sentXbtReplies_.find(settlementId); - if (lastReply == sentXbtReplies_.end()) { - return nullptr; - } - - return &(lastReply->second.utxosPayinFixed); //FIXME }); } @@ -379,6 +426,7 @@ void RFQReplyWidget::onUserConnected(const bs::network::UserType &userType) ui_->widgetAutoSignQuote->onUserConnected(autoSigning, autoQuoting); } else { //no AQ support in new code + logger_->debug("[{}] user type = {}", __func__, (int)userType); userType_ = userType; } } @@ -405,7 +453,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) const auto "eReqId = quoteProvider_->getQuoteReqId(order.quoteId); if (order.assetType == bs::network::Asset::SpotFX) { if (order.status == bs::network::Order::Filled) { - onSettlementComplete(quoteReqId); + onCompleteSettlement(quoteReqId); quoteProvider_->delQuoteReqId(quoteReqId); } return; @@ -441,7 +489,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) connect(settlContainer.get(), &DealerCCSettlementContainer::cancelTrade , this, &RFQReplyWidget::onCancelCCTrade); connect(settlContainer.get(), &DealerCCSettlementContainer::completed - , this, &RFQReplyWidget::onSettlementComplete); + , this, &RFQReplyWidget::onCompleteSettlement); // Do not make circular dependency, capture bare pointer auto orderUpdatedCb = [settlContainer = settlContainer.get(), quoteId = order.quoteId] @@ -495,7 +543,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) connect(settlContainer.get(), &DealerXBTSettlementContainer::error , this, &RFQReplyWidget::onTransactionError); connect(settlContainer.get(), &DealerCCSettlementContainer::completed - , this, &RFQReplyWidget::onSettlementComplete); + , this, &RFQReplyWidget::onCompleteSettlement); connect(this, &RFQReplyWidget::unsignedPayinRequested, settlContainer.get() , &DealerXBTSettlementContainer::onUnsignedPayinRequested); @@ -628,8 +676,11 @@ void RFQReplyWidget::onCancelCCTrade(const std::string& clientOrderId) emit cancelCCTrade(clientOrderId); } -void RFQReplyWidget::onSettlementComplete(const std::string &id) +void RFQReplyWidget::onCompleteSettlement(const std::string &id) { + if (!autoSignProvider_) { + return; + } const auto &itReqId = settlementToReplyIds_.find(id); if (itReqId == settlementToReplyIds_.end()) { ((AQScriptRunner *)autoSignProvider_->scriptRunner())->settled(id); // FX settlement diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 51846f625..4bd25d2cb 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -103,8 +103,18 @@ Q_OBJECT void onWalletBalance(const bs::sync::WalletBalanceData&); void onHDWallet(const bs::sync::HDWalletData&); void onAuthKey(const bs::Address&, const BinaryData& authKey); + void onVerifiedAuthAddresses(const std::vector&); + void onReservedUTXOs(const std::string& resId, const std::string& subId + , const std::vector&); void onQuoteReqNotification(const bs::network::QuoteReqNotification&); + void onQuoteMatched(const std::string& rfqId, const std::string& quoteId); + void onQuoteFailed(const std::string& rfqId, const std::string& quoteId + , const std::string& info); + void onSettlementPending(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId, int timeLeftMS); + void onSettlementComplete(const std::string& rfqId, const std::string& quoteId + , const BinaryData& settlementId); signals: void orderFilled(); @@ -156,7 +166,7 @@ private slots: void onCancelXBTTrade(const std::string& settlementId); void onCancelCCTrade(const std::string& clientOrderId); - void onSettlementComplete(const std::string &id); + void onCompleteSettlement(const std::string &id); private: void onResetCurrentReservation(const std::shared_ptr &data); @@ -208,6 +218,7 @@ private slots: std::unordered_map sentReplyToSettlementsIds_, settlementToReplyIds_; bs::network::UserType userType_{ bs::network::UserType::Undefined }; + std::unordered_map submittedQuote_; }; #endif // __RFQ_REPLY_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index f8068ca59..82bd8c534 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -274,15 +274,12 @@ void RFQRequestWidget::onQuoteFailed(const std::string& rfqId } void RFQRequestWidget::onSettlementPending(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId) + , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) { const auto& itDlg = dialogs_.find(rfqId); if (itDlg != dialogs_.end()) { itDlg->second->onSettlementPending(quoteId, settlementId); } - else { - logger_->warn("[{}] RFQ dialog for {} not found", __func__, rfqId); - } } void RFQRequestWidget::onSettlementComplete(const std::string& rfqId diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 101c61802..0aaa532b6 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -112,7 +112,7 @@ Q_OBJECT void onQuoteFailed(const std::string& rfqId, const std::string& quoteId , const std::string& info); void onSettlementPending(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId); + , const BinaryData& settlementId, int timeLeftMS); void onSettlementComplete(const std::string& rfqId, const std::string& quoteId , const BinaryData& settlementId); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index b0af971c5..de29d72a6 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -1257,7 +1257,9 @@ void RFQTicketXBT::onParentAboutToHide() void RFQTicketXBT::onVerifiedAuthAddresses(const std::vector& addrs) { - logger_->debug("[{}] {} addresses", __func__, addrs.size()); + if (addrs.empty()) { + return; + } UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, addrs); onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); } @@ -1301,9 +1303,12 @@ void RFQTicketXBT::onWalletData(const std::string& walletId, const bs::sync::Wal void RFQTicketXBT::onAuthKey(const bs::Address& addr, const BinaryData& authKey) { if (addr == authAddr_) { - logger_->debug("[{}] got auth key: {}", __func__, authKey.toHexStr()); - authKey_ = authKey.toHexStr(); - updateSubmitButton(); + const auto& authKeyHex = authKey.toHexStr(); + if (authKey_ != authKeyHex) { + logger_->debug("[{}] got auth key: {}", __func__, authKeyHex); + authKey_ = authKeyHex; + updateSubmitButton(); + } sendDeferredRFQs(); } } diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index 58483fae4..be967b38b 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -287,11 +287,11 @@ int UiUtils::fillHDWalletsComboBox(QComboBox* comboBox const auto b = comboBox->blockSignals(true); comboBox->clear(); - auto addRow = [comboBox](const std::string& label, const std::string& walletId, WalletsTypes type) { + auto addRow = [comboBox](const std::string& label, const std::string& walletId, WalletsTypes type) + { if (WalletsTypes::None == type) { return; } - int i = comboBox->count(); comboBox->addItem(QString::fromStdString(label)); comboBox->setItemData(i, QString::fromStdString(walletId), UiUtils::WalletIdRole); diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 6fe96b606..9c11a6aa2 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -28,7 +28,8 @@ using namespace bs::message; BsServerAdapter::BsServerAdapter(const std::shared_ptr &logger) : logger_(logger) - , user_(std::make_shared(bs::message::TerminalUsers::BsServer)) + , user_(std::make_shared(TerminalUsers::BsServer)) + , userSettl_(std::make_shared(TerminalUsers::Settlement)) { connMgr_ = std::make_shared(logger_); connMgr_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); @@ -306,7 +307,7 @@ void BsServerAdapter::processUnsignedPayin(const Blocksettle::Communication::Pro { BsServerMessage msg; msg.set_unsigned_payin_requested(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; pushFill(env); } @@ -318,7 +319,7 @@ void BsServerAdapter::processSignPayin(const Blocksettle::Communication::ProxyTe msgBC->set_unsigned_payin(BinaryData::fromString(response.unsigned_payin_data()).toBinStr()); msgBC->set_payin_hash(BinaryData::fromString(response.payin_hash()).toBinStr()); msgBC->set_timestamp(response.timestamp_ms()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; pushFill(env); } @@ -327,9 +328,9 @@ void BsServerAdapter::processSignPayout(const Blocksettle::Communication::ProxyT BsServerMessage msg; auto msgBC = msg.mutable_signed_payout_requested(); msgBC->set_settlement_id(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); - msgBC->set_unsigned_payin(BinaryData::fromString(response.payin_data()).toBinStr()); + msgBC->set_payin_hash(BinaryData::fromString(response.payin_data()).toBinStr()); msgBC->set_timestamp(response.timestamp_ms()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; pushFill(env); } diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index 2053600a6..e1cdeb2eb 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -90,7 +90,7 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg private: std::shared_ptr logger_; - std::shared_ptr user_; + std::shared_ptr user_, userSettl_; std::shared_ptr connMgr_; std::unique_ptr bsClient_; ApplicationSettings::EnvConfiguration envConfig_{ ApplicationSettings::EnvConfiguration::Unknown }; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index 460b7159a..64fc4422c 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -339,30 +339,6 @@ bool MatchingAdapter::processPullQuote(const PullRFQReply& request) return true; } -void MatchingAdapter::saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId) -{ - quoteIdMap_[quoteId] = quoteReqId; - quoteIds_[quoteReqId].insert(quoteId); -} - -void MatchingAdapter::delQuoteReqId(const std::string& quoteReqId) -{ - const auto& itQuoteId = quoteIds_.find(quoteReqId); - if (itQuoteId != quoteIds_.end()) { - for (const auto& id : itQuoteId->second) { - quoteIdMap_.erase(id); - } - quoteIds_.erase(itQuoteId); - } - cleanQuoteRequestCcy(quoteReqId); -} - -std::string MatchingAdapter::getQuoteReqId(const std::string& quoteId) const -{ - const auto& itQuoteId = quoteIdMap_.find(quoteId); - return (itQuoteId == quoteIdMap_.end()) ? std::string{} : itQuoteId->second; -} - void MatchingAdapter::saveQuoteRequestCcy(const std::string& id, const std::string& ccy) { quoteCcys_.emplace(id, ccy); @@ -408,8 +384,7 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) quote.dealerTransaction = response.dealercointransactioninput(); } - switch (response.quotingtype()) - { + switch (response.quotingtype()) { case com::celertech::marketmerchant::api::enums::quotingtype::AUTOMATIC: quote.quotingType = bs::network::Quote::Automatic; break; @@ -437,8 +412,11 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) logger_->debug("[MatchingAdapter::onQuoteResponse] timeSkew = {}", quote.timeSkewMs); CurrencyPair cp(quote.security); - const auto itRFQ = submittedRFQs_.find(response.quoterequestid()); - if (itRFQ == submittedRFQs_.end()) { // Quote for dealer to indicate GBBO + const auto& grp = response.legquotegroup(0); + quote.product = grp.currency(); + + const auto& itRFQ = submittedRFQs_.find(response.quoterequestid()); + if (itRFQ == submittedRFQs_.end()) { // Quote for dealer to indicate GBBO //WTF? const auto quoteCcy = getQuoteRequestCcy(quote.requestId); if (!quoteCcy.empty()) { double price = 0; @@ -458,6 +436,8 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; pushFill(env); } + quote.quantity = grp.bidsize(); // equal to offersize/offerpx regardless of side + quote.price = response.bidpx(); } else { if (response.legquotegroup_size() != 1) { logger_->error("[MatchingAdapter::onQuoteResponse] invalid leg number: {}\n{}" @@ -466,17 +446,8 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) return false; } - const auto& grp = response.legquotegroup(0); - if (quote.assetType == bs::network::Asset::SpotXBT) { - quote.dealerAuthPublicKey = response.dealerauthenticationaddress(); quote.requestorAuthPublicKey = itRFQ->second.requestorAuthPublicKey; - - if (response.has_settlementid() && !response.settlementid().empty()) { - quote.settlementId = response.settlementid(); - } - - quote.dealerTransaction = response.dealertransaction(); } if ((quote.side == bs::network::Side::Sell) ^ (itRFQ->second.product != cp.NumCurrency())) { @@ -487,14 +458,18 @@ bool MatchingAdapter::onQuoteResponse(const std::string& data) quote.quantity = grp.bidsize(); } - quote.product = grp.currency(); - if (quote.quotingType == bs::network::Quote::Tradeable) { submittedRFQs_.erase(itRFQ); } } - saveQuoteReqId(quote.requestId, quote.quoteId); + if (quote.assetType == bs::network::Asset::SpotXBT) { + quote.dealerAuthPublicKey = response.dealerauthenticationaddress(); + quote.dealerTransaction = response.dealertransaction(); + if (response.has_settlementid() && !response.settlementid().empty()) { + quote.settlementId = response.settlementid(); + } + } MatchingMessage msg; toMsg(quote, msg.mutable_quote()); Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; @@ -573,8 +548,7 @@ bool MatchingAdapter::onBitcoinOrderSnapshot(const std::string& data) } order.reqTransaction = response.requestortransaction(); order.dealerTransaction = response.dealertransaction(); - - order.status = order.status = bs::celer::mapBtcOrderStatus(response.orderstatus()); + order.status = bs::celer::mapBtcOrderStatus(response.orderstatus()); order.pendingStatus = response.info(); MatchingMessage msg; diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 4861cdf2f..3dcaab1eb 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -72,9 +72,6 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget bool processSubmitQuote(const BlockSettle::Terminal::ReplyToRFQ&); bool processPullQuote(const BlockSettle::Terminal::PullRFQReply&); - std::string getQuoteReqId(const std::string& quoteId) const; - void saveQuoteReqId(const std::string& quoteReqId, const std::string& quoteId); - void delQuoteReqId(const std::string& quoteReqId); std::string getQuoteRequestCcy(const std::string& id) const; void saveQuoteRequestCcy(const std::string& id, const std::string& ccy); void cleanQuoteRequestCcy(const std::string& id); @@ -97,7 +94,6 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget std::string assignedAccount_; std::unordered_map submittedRFQs_; - std::unordered_map quoteIdMap_; std::unordered_map> quoteIds_; std::unordered_map quoteCcys_; }; diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index 21b6e692f..f9dee4ed1 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -217,6 +217,7 @@ bool SettlementAdapter::processMatchingOrder(const MatchingMessage_Order& respon logger_->error("[{}] unknown settlement for {}", __func__, response.quote_id()); return true; } + SettlementMessage msg; const auto& order = fromMsg(response); if (order.status == bs::network::Order::Status::Filled) { @@ -232,10 +233,25 @@ bool SettlementAdapter::processMatchingOrder(const MatchingMessage_Order& respon msgResp->set_info(order.info); } else if (order.status == bs::network::Order::Status::Pending) { + if (itSettl->second->dealer && (itSettl->second->quote.assetType == bs::network::Asset::SpotXBT) + && (itSettl->second->quote.quotingType == bs::network::Quote::Tradeable)) { + if (((itSettl->second->quote.side == bs::network::Side::Buy) || + (itSettl->second->quote.product != bs::network::XbtCurrency)) + && itSettl->second->dealerRecvAddress.empty()) { + //maybe obtain receiving address here instead of when constructing pay-out in WalletsAdapter + } + if (startXbtSettlement(itSettl->second->quote)) { + logger_->debug("[{}] started XBT settlement on {}", __func__, response.quote_id()); + } else { + return true; + } + } auto msgResp = msg.mutable_pending_settlement(); - msgResp->set_rfq_id(itSettl->second->rfq.requestId); - msgResp->set_quote_id(response.quote_id()); - msgResp->set_settlement_id(response.settlement_id()); + auto msgIds = msgResp->mutable_ids(); + msgIds->set_rfq_id(itSettl->second->rfq.requestId); + msgIds->set_quote_id(response.quote_id()); + msgIds->set_settlement_id(response.settlement_id()); + msgResp->set_time_left_ms(kHandshakeTimeout.count() * 1000); } else { logger_->debug("[{}] {} unprocessed order status {}", __func__, order.quoteId @@ -278,13 +294,16 @@ bool SettlementAdapter::processMatchingInRFQ(const IncomingRFQ& qrn) bool SettlementAdapter::processBsUnsignedPayin(const BinaryData& settlementId) { - logger_->debug("[{}] {}", __func__, settlementId.toHexStr()); const auto& itSettl = settlBySettlId_.find(settlementId); if (itSettl == settlBySettlId_.end()) { logger_->error("[{}] unknown settlement for {}", __func__, settlementId.toHexStr()); return true; } + if (itSettl->second->ownAuthAddr.empty() || itSettl->second->counterKey.empty()) { + return false; // postpone processing until auth addresses are set + } + logger_->debug("[{}] {} {}", __func__, settlementId.toHexStr(), itSettl->second->amount.GetValue()); WalletsMessage msg; auto msgReq = msg.mutable_payin_request(); msgReq->set_own_auth_address(itSettl->second->ownAuthAddr.display()); @@ -305,8 +324,10 @@ bs::sync::PasswordDialogData SettlementAdapter::getDialogData(const QDateTime& t { bs::sync::PasswordDialogData dialogData; dialogData.setValue(bs::sync::PasswordDialogData::SettlementId, settlement.settlementId.toHexStr()); - dialogData.setValue(bs::sync::PasswordDialogData::DurationLeft, 29 * 1000); // TODO: take from real timeout from SettlementAdapter - dialogData.setValue(bs::sync::PasswordDialogData::DurationTotal, 30 * 1000); // TODO: same + dialogData.setValue(bs::sync::PasswordDialogData::DurationLeft + , (int)((kHandshakeTimeout.count() - 1) * 1000)); + dialogData.setValue(bs::sync::PasswordDialogData::DurationTotal + , (int)(kHandshakeTimeout.count() * 1000)); // Set timestamp that will be used by auth eid server to update timers. dialogData.setValue(bs::sync::PasswordDialogData::DurationTimestamp, static_cast(timestamp.toSecsSinceEpoch())); @@ -358,6 +379,7 @@ bs::sync::PasswordDialogData SettlementAdapter::getDialogData(const QDateTime& t dialogData.setValue(bs::sync::PasswordDialogData::TxInputProduct, UiUtils::XbtCurrency); dialogData.setValue(bs::sync::PasswordDialogData::ResponderAuthAddress, settlement.counterAuthAddr.display()); + dialogData.setValue(bs::sync::PasswordDialogData::IsDealer, settlement.dealer); return dialogData; } @@ -367,9 +389,10 @@ bs::sync::PasswordDialogData SettlementAdapter::getPayinDialogData(const QDateTi { auto dlgData = getDialogData(timestamp, settlement); dlgData.setValue(bs::sync::PasswordDialogData::ResponderAuthAddressVerified, true); //TODO: put actual value - dlgData.setValue(bs::sync::PasswordDialogData::SigningAllowed, true); //TODO: same here + dlgData.setValue(bs::sync::PasswordDialogData::SigningAllowed, true); //TODO: same value here dlgData.setValue(bs::sync::PasswordDialogData::Title, QObject::tr("Settlement Pay-In")); + dlgData.setValue(bs::sync::PasswordDialogData::SettlementPayInVisible, true); return dlgData; } @@ -397,7 +420,7 @@ bool SettlementAdapter::processBsSignPayin(const BsServerMessage_SignXbtHalf& re return true; } if (!itSettl->second->payin.isValid()) { - logger_->error("[{}] no payin TX for {}", __func__, settlementId.toHexStr()); + logger_->error("[{}] invalid payin TX for {}", __func__, settlementId.toHexStr()); unreserve(itSettl->second->rfq.requestId); SettlementMessage msg; auto msgFail = msg.mutable_failed_settlement(); @@ -421,8 +444,9 @@ bool SettlementAdapter::processBsSignPayin(const BsServerMessage_SignXbtHalf& re Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; if (pushFill(env)) { payinRequests_[env.id] = settlementId; + return true; } - return true; + return false; } bool SettlementAdapter::processBsSignPayout(const BsServerMessage_SignXbtHalf& request) @@ -434,10 +458,12 @@ bool SettlementAdapter::processBsSignPayout(const BsServerMessage_SignXbtHalf& r logger_->error("[{}] unknown settlement for {}", __func__, settlementId.toHexStr()); return true; } - if (itSettl->second->recvAddress.empty()) { - logger_->error("[{}] empty receiving address", __func__); - return true; - } + const bs::Address& recvAddr = itSettl->second->dealer ? + itSettl->second->dealerRecvAddress : itSettl->second->recvAddress; +/* if (recvAddr.empty()) { + logger_->debug("[{}] waiting for own receiving address", __func__); + return false; + }*/ // recvAddr is now obtained in WalletsAdapter if empty WalletsMessage msg; auto msgReq = msg.mutable_payout_request(); msgReq->set_own_auth_address(itSettl->second->ownAuthAddr.display()); @@ -445,7 +471,7 @@ bool SettlementAdapter::processBsSignPayout(const BsServerMessage_SignXbtHalf& r msgReq->set_counter_auth_pubkey(itSettl->second->counterKey.toBinStr()); msgReq->set_amount(itSettl->second->amount.GetValue()); msgReq->set_payin_hash(request.payin_hash()); - msgReq->set_recv_address(itSettl->second->recvAddress.display()); + msgReq->set_recv_address(recvAddr.display()); Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; if (pushFill(env)) { payoutRequests_[env.id] = settlementId; @@ -553,6 +579,7 @@ bool SettlementAdapter::processSubmitQuote(const bs::message::Envelope& env return true; } itSettl->second->settlementId = settlementId; + itSettl->second->reserveId = itSettl->second->quote.requestId; settlBySettlId_[settlementId] = itSettl->second; } @@ -652,7 +679,7 @@ bool SettlementAdapter::processXbtTx(uint64_t msgId, const WalletsMessage_XbtTxR if (itPayout != payoutRequests_.end()) { const auto& itSettl = settlBySettlId_.find(itPayout->second); if (itSettl != settlBySettlId_.end()) { - logger_->debug("[{}] got payout", __func__); + logger_->debug("[{}] got payout {}", __func__, itPayout->second.toHexStr()); try { itSettl->second->settlementAddress = bs::Address::fromAddressString(response.settlement_address()); } catch (const std::exception&) { @@ -665,7 +692,7 @@ bool SettlementAdapter::processXbtTx(uint64_t msgId, const WalletsMessage_XbtTxR *msgReq->mutable_tx_request() = response.tx_request(); *msgReq->mutable_details() = dlgData.toProtobufMessage(); msgReq->set_contra_auth_pubkey(itSettl->second->counterKey.toBinStr()); - msgReq->set_own_key_first(!itSettl->second->dealer); + msgReq->set_own_key_first(true); Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; if (pushFill(env)) { payoutRequests_[env.id] = itPayout->second; @@ -686,6 +713,7 @@ bool SettlementAdapter::processSignedTx(uint64_t msgId , const SignerMessage_SignTxResponse& response) { const auto& settlementId = BinaryData::fromString(response.id()); + logger_->debug("[{}] {}", __func__, settlementId.toHexStr()); const auto& sendSignedTx = [this, response, settlementId](bool payin) { if (static_cast(response.error_code()) == bs::error::ErrorCode::TxCancelled) { @@ -723,7 +751,7 @@ bool SettlementAdapter::processSignedTx(uint64_t msgId const auto& itPayout = payoutRequests_.find(msgId); if (itPayout != payoutRequests_.end()) { if (itPayout->second != settlementId) { - logger_->error("[{}] payout settlement id mismatch"); + logger_->error("[{}] payout settlement id mismatch", __func__); payoutRequests_.erase(itPayout); return true; //TODO: decide the consequences of this } @@ -782,7 +810,7 @@ bool SettlementAdapter::processInRFQTimeout(const std::string& id) return true; } -void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) +bool SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) { const auto& sendFailedQuote = [this, quote](const std::string& info) { @@ -800,12 +828,12 @@ void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) const auto& itSettl = settlByQuoteId_.find(quote.quoteId); if (itSettl == settlByQuoteId_.end()) { sendFailedQuote("unknown quote id"); - return; + return false; } if (quote.settlementId.empty()) { sendFailedQuote("no settlement id"); settlByQuoteId_.erase(itSettl); - return; + return false; } CurrencyPair cp(quote.security); @@ -832,15 +860,24 @@ void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) catch (const std::exception&) { sendFailedQuote("invalid settlement id format"); settlByQuoteId_.erase(itSettl); - return; + return false; } itSettl->second->settlementId = settlementId; auto &data = settlBySettlId_[settlementId] = itSettl->second; try { if (data->dealer) { - data->ownKey = BinaryData::CreateFromHex(quote.dealerAuthPublicKey); - data->counterKey = BinaryData::CreateFromHex(quote.requestorAuthPublicKey); + if (data->ownKey.empty()) { + data->ownKey = BinaryData::CreateFromHex(quote.dealerAuthPublicKey); + } + if (data->counterKey.empty()) { + if (quote.requestorAuthPublicKey.empty()) { + data->counterKey = BinaryData::CreateFromHex(data->rfq.requestorAuthPublicKey); + } + else { + data->counterKey = BinaryData::CreateFromHex(quote.requestorAuthPublicKey); + } + } } else { data->ownKey = BinaryData::CreateFromHex(quote.requestorAuthPublicKey); data->counterKey = BinaryData::CreateFromHex(quote.dealerAuthPublicKey); @@ -852,7 +889,7 @@ void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) sendFailedQuote("failed to decode data"); settlBySettlId_.erase(data->settlementId); settlByQuoteId_.erase(itSettl); - return; + return false; } const auto& timeNow = std::chrono::system_clock::now(); @@ -866,13 +903,16 @@ void SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) //TODO: push counterAuthAddr to OnChainTracker for checking } - MatchingMessage msgMtch; - auto msgReq = msgMtch.mutable_accept_rfq(); - msgReq->set_rfq_id(quote.requestId); - toMsg(quote, msgReq->mutable_quote()); - msgReq->set_payout_tx("not used"); // copied from ReqXBTSettlementContainer - Envelope envReq{ 0, user_, userMtch_, {}, {}, msgMtch.SerializeAsString(), true }; - pushFill(envReq); + if (!itSettl->second->dealer) { + MatchingMessage msgMtch; + auto msgReq = msgMtch.mutable_accept_rfq(); + msgReq->set_rfq_id(quote.requestId); + toMsg(quote, msgReq->mutable_quote()); + msgReq->set_payout_tx("not used"); // copied from ReqXBTSettlementContainer + Envelope envReq{ 0, user_, userMtch_, {}, {}, msgMtch.SerializeAsString(), true }; + return pushFill(envReq); + } + return true; } void SettlementAdapter::startCCSettlement(const bs::network::Quote&) diff --git a/Core/SettlementAdapter.h b/Core/SettlementAdapter.h index 696f3fc31..712a74acf 100644 --- a/Core/SettlementAdapter.h +++ b/Core/SettlementAdapter.h @@ -77,7 +77,7 @@ class SettlementAdapter : public bs::message::Adapter bool processHandshakeTimeout(const std::string& id); bool processInRFQTimeout(const std::string& id); - void startXbtSettlement(const bs::network::Quote&); + bool startXbtSettlement(const bs::network::Quote&); void startCCSettlement(const bs::network::Quote&); void unreserve(const std::string& id, const std::string& subId = {}); void cancel(const BinaryData& settlementId); diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index fcc35d23c..d8cef8151 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -612,16 +612,20 @@ bool SignerAdapter::processSignSettlementTx(const bs::message::Envelope& env pushFill(envResp); }; - const auto& txReq = bs::signer::pbTxRequestToCore(request.tx_request()); + auto txReq = bs::signer::pbTxRequestToCore(request.tx_request(), logger_); const bs::sync::PasswordDialogData dlgData(request.details()); if (request.contra_auth_pubkey().empty()) { - return (signer_->signSettlementTXRequest(txReq, dlgData - , SignContainer::TXSignMode::Full, false, signCb) != 0); + signer_->signSettlementTXRequest(txReq, dlgData + , SignContainer::TXSignMode::Full, false, signCb); } - const bs::core::wallet::SettlementData sd{ - BinaryData::fromString(request.settlement_id()), - BinaryData::fromString(request.contra_auth_pubkey()), request.own_key_first() }; - return (signer_->signSettlementPayoutTXRequest(txReq, sd, dlgData, signCb) != 0); + else { + txReq.txHash = txReq.txId(); + const bs::core::wallet::SettlementData sd{ + BinaryData::fromString(request.settlement_id()), + BinaryData::fromString(request.contra_auth_pubkey()), request.own_key_first() }; + signer_->signSettlementPayoutTXRequest(txReq, sd, dlgData, signCb); + } + return true; } bool SignerAdapter::processGetRootPubKey(const bs::message::Envelope &env diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 49f0fece4..0ef0e8063 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -1049,6 +1049,7 @@ void MainWindow::onSubmittedAuthAddresses(const std::vector& addrs) void MainWindow::onVerifiedAuthAddresses(const std::vector& addrs) { ui_->widgetRFQ->onVerifiedAuthAddresses(addrs); + ui_->widgetRFQReply->onVerifiedAuthAddresses(addrs); } void MainWindow::onAuthKey(const bs::Address& addr, const BinaryData& authKey) @@ -1065,24 +1066,28 @@ void MainWindow::onQuoteReceived(const bs::network::Quote& quote) void MainWindow::onQuoteMatched(const std::string& rfqId, const std::string "eId) { ui_->widgetRFQ->onQuoteMatched(rfqId, quoteId); + ui_->widgetRFQReply->onQuoteMatched(rfqId, quoteId); } void MainWindow::onQuoteFailed(const std::string& rfqId, const std::string& quoteId , const std::string &info) { ui_->widgetRFQ->onQuoteFailed(rfqId, quoteId, info); + ui_->widgetRFQReply->onQuoteFailed(rfqId, quoteId, info); } void bs::gui::qt::MainWindow::onSettlementPending(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId) + , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) { - ui_->widgetRFQ->onSettlementPending(rfqId, quoteId, settlementId); + ui_->widgetRFQ->onSettlementPending(rfqId, quoteId, settlementId, timeLeftMS); + ui_->widgetRFQReply->onSettlementPending(rfqId, quoteId, settlementId, timeLeftMS); } void bs::gui::qt::MainWindow::onSettlementComplete(const std::string& rfqId , const std::string& quoteId, const BinaryData& settlementId) { ui_->widgetRFQ->onSettlementComplete(rfqId, quoteId, settlementId); + ui_->widgetRFQReply->onSettlementComplete(rfqId, quoteId, settlementId); } void bs::gui::qt::MainWindow::onQuoteReqNotification(const bs::network::QuoteReqNotification& qrn) @@ -1105,6 +1110,7 @@ void MainWindow::onReservedUTXOs(const std::string& resId , const std::string& subId, const std::vector& utxos) { ui_->widgetRFQ->onReservedUTXOs(resId, subId, utxos); + ui_->widgetRFQReply->onReservedUTXOs(resId, subId, utxos); } void MainWindow::showRunInBackgroundMessage() diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index da7c354e4..0a369a0dd 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -117,7 +117,7 @@ namespace bs { void onQuoteFailed(const std::string& rfqId, const std::string& quoteId , const std::string &info); void onSettlementPending(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId); + , const BinaryData& settlementId, int timeLeftMS); void onSettlementComplete(const std::string& rfqId, const std::string& quoteId , const BinaryData& settlementId); void onQuoteReqNotification(const bs::network::QuoteReqNotification&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index e4f488df1..9726ecc14 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -1815,11 +1815,11 @@ bool QtGuiAdapter::processFailedSettl(const SettlementMessage_FailedSettlement& }); } -bool QtGuiAdapter::processPendingSettl(const SettlementMessage_SettlementIds& msg) +bool QtGuiAdapter::processPendingSettl(const SettlementMessage_PendingSettlement& msg) { return QMetaObject::invokeMethod(mainWindow_, [this, msg] { - mainWindow_->onSettlementPending(msg.rfq_id(), msg.quote_id() - , BinaryData::fromString(msg.settlement_id())); + mainWindow_->onSettlementPending(msg.ids().rfq_id(), msg.ids().quote_id() + , BinaryData::fromString(msg.ids().settlement_id()), msg.time_left_ms()); }); } diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index ce634a389..f20a65d69 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -58,6 +58,7 @@ namespace BlockSettle { class MktDataMessage_Prices; class SettlementMessage_FailedSettlement; class SettlementMessage_MatchedQuote; + class SettlementMessage_PendingSettlement; class SettlementMessage_SettlementIds; class SettingsMessage_ArmoryServers; class SettingsMessage_SettingsResponse; @@ -141,7 +142,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processQuote(const BlockSettle::Terminal::Quote&); bool processMatchedQuote(const BlockSettle::Terminal::SettlementMessage_MatchedQuote&); bool processFailedSettl(const BlockSettle::Terminal::SettlementMessage_FailedSettlement&); - bool processPendingSettl(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); + bool processPendingSettl(const BlockSettle::Terminal::SettlementMessage_PendingSettlement&); bool processSettlComplete(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); bool processQuoteReqNotif(const BlockSettle::Terminal::IncomingRFQ&); bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); diff --git a/common b/common index 6e9ac501f..2bdedb70e 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 6e9ac501f7e446cb06d4180c8653ba4363536cf3 +Subproject commit 2bdedb70e97ae47de919b1a919d91909ce0b32d1 From bdd5876527020a8b2eae73f2c36c51d37253894e Mon Sep 17 00:00:00 2001 From: Ation Date: Tue, 24 Nov 2020 10:00:38 +0200 Subject: [PATCH 043/146] Update background role --- BlockSettleUILib/OrderListModel.cpp | 2 +- common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index b49ea93c7..a446c9140 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -135,7 +135,7 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const return QVariant(); } } - case Qt::ForegroundRole : { + case Qt::BackgroundRole: { if (index.column() == Header::Quantity) { return g->getQuantityColor(); } else { diff --git a/common b/common index e182c6340..a2d85e42e 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e182c6340bc4e9fd37e39597e6a790b468126298 +Subproject commit a2d85e42ef35120591ac2247866b595963f9a53b From 1f318d1a08a3d0c9bf19701aa4df993cab3622b0 Mon Sep 17 00:00:00 2001 From: Ation Date: Tue, 24 Nov 2020 14:10:16 +0200 Subject: [PATCH 044/146] Display delivery obligations in trade blotter --- BlockSettleUILib/CreateTransactionDialog.cpp | 1 - BlockSettleUILib/CreateTransactionDialog.h | 1 - .../CreateTransactionDialogAdvanced.h | 3 + BlockSettleUILib/OrderListModel.cpp | 137 +++++++++++++++--- BlockSettleUILib/OrderListModel.h | 37 ++++- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 4 + BlockSettleUILib/Trading/RFQReplyWidget.h | 77 ++++++++++ 7 files changed, 234 insertions(+), 26 deletions(-) diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index cb2cffb23..3376273b5 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -34,7 +34,6 @@ #include "SelectedTransactionInputs.h" #include "SignContainer.h" #include "TransactionData.h" -#include "TransactionOutputsModel.h" #include "UiUtils.h" #include "UsedInputsModel.h" #include "UtxoReservationManager.h" diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index 5062c124f..c94f757ea 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -44,7 +44,6 @@ class QTextEdit; class RecipientWidget; class SignContainer; class TransactionData; -class TransactionOutputsModel; class UsedInputsModel; class XbtAmountValidator; diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index e4254a0ee..9a615d0e9 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -17,6 +17,7 @@ namespace Ui { class CreateTransactionDialogAdvanced; } + namespace bs { namespace sync { namespace hd { @@ -27,6 +28,8 @@ namespace bs { } } +class TransactionOutputsModel; + class QNetworkAccessManager; class CreateTransactionDialogAdvanced : public CreateTransactionDialog diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index a446c9140..216cea03c 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -26,6 +26,16 @@ namespace { } // namespace +double getOrderValue(const bs::network::Order& order) +{ + double value = - order.quantity * order.price; + if (order.security.substr(0, order.security.find('/')) != order.product) { + value = order.quantity / order.price; + } + + return value; +} + QString OrderListModel::Header::toString(OrderListModel::Header::Index h) { switch (h) { @@ -43,8 +53,9 @@ QString OrderListModel::Header::toString(OrderListModel::Header::Index h) QString OrderListModel::StatusGroup::toString(OrderListModel::StatusGroup::Type sg) { switch (sg) { - case StatusGroup::UnSettled: return tr("UnSettled"); - case StatusGroup::Settled: return tr("Settled"); + case StatusGroup::UnSettled: return tr("UnSettled"); + case StatusGroup::Settled: return tr("Settled"); + case StatusGroup::PendingSettlements: return tr("Pending Settlements"); default: return tr("Unknown"); } } @@ -127,18 +138,24 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const switch (role) { case Qt::DisplayRole : { - if (index.column() == 0) { + switch(index.column()) { + case Header::NameColumn: return g->security_; - } else if (index.column() == Header::Quantity) { + case Header::Quantity: return g->getQuantity(); - } else { + case Header::Value: + return g->getValue(); + default: return QVariant(); } } case Qt::BackgroundRole: { - if (index.column() == Header::Quantity) { + switch(index.column()) { + case Header::Quantity: return g->getQuantityColor(); - } else { + case Header::Value: + return g->getValueColor(); + default: return QVariant(); } } @@ -153,7 +170,7 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const switch (role) { case Qt::DisplayRole : { - if (index.column() == 0) { + if (index.column() == Header::NameColumn) { return g->name_; } else { return QVariant(); @@ -174,7 +191,7 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const switch (role) { case Qt::DisplayRole : { - if (index.column() == 0) { + if (index.column() == Header::NameColumn) { return g->name_; } else { return QVariant(); @@ -241,6 +258,9 @@ QModelIndex OrderListModel::index(int row, int column, const QModelIndex &parent case StatusGroup::Settled : return createIndex(row, column, &settled_->idx_); + case StatusGroup::PendingSettlements : + return createIndex(row, column, &pendingFuturesSettlement_->idx_); + default : return QModelIndex(); } @@ -313,7 +333,7 @@ int OrderListModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { // Do not show Settled and UnSettled root items if not connected - return connected_ ? 2 : 0; + return connected_ ? 3 : 0; } auto idx = static_cast(parent.internalPointer()); @@ -453,7 +473,7 @@ OrderListModel::StatusGroup::Type OrderListModel::getStatusGroup(const bs::netwo return StatusGroup::Settled; } - return StatusGroup::last; + return StatusGroup::Undefined; } std::pair OrderListModel::findItem(const bs::network::Order &order) @@ -599,7 +619,7 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke // Create market if it doesn't exist. if (!marketItem) { beginInsertRows(sidx, static_cast(sg->rows_.size()), static_cast(sg->rows_.size())); - sg->rows_.push_back(make_unique(order.assetType, &sg->idx_)); + sg->rows_.emplace_back(make_unique(order.assetType, &sg->idx_)); marketItem = sg->rows_.back().get(); endInsertRows(); } @@ -610,10 +630,10 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); if (getStatusGroup(order) == StatusGroup::UnSettled && order.assetType == bs::network::Asset::Type::Futures) { - marketItem->rows_.push_back(make_unique( + marketItem->rows_.emplace_back(make_unique( QString::fromStdString(order.security), &marketItem->idx_)); } else { - marketItem->rows_.push_back(make_unique( + marketItem->rows_.emplace_back(make_unique( QString::fromStdString(order.security), &marketItem->idx_)); } @@ -628,6 +648,7 @@ void OrderListModel::reset() groups_.clear(); unsettled_ = std::make_unique(StatusGroup::toString(StatusGroup::UnSettled), 0); settled_ = std::make_unique(StatusGroup::toString(StatusGroup::Settled), 1); + pendingFuturesSettlement_ = std::make_unique(StatusGroup::toString(StatusGroup::PendingSettlements), 2); endResetModel(); } @@ -673,6 +694,8 @@ void OrderListModel::processUpdateOrders(const Blocksettle::Communication::Proxy onOrderUpdated(order); } + + DisplayFuturesDeliveryRow(); } void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) @@ -681,7 +704,7 @@ void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication:: std::vector> newOrderStatuses(message.orders_size()); for (const auto &data : message.orders()) { - newOrderStatuses.push_back({ data.timestamp_ms(), static_cast(data.status()) }); + newOrderStatuses.emplace_back(decltype(newOrderStatuses)::value_type{ data.timestamp_ms(), static_cast(data.status()) }); } std::sort(newOrderStatuses.begin(), newOrderStatuses.end(), [&](const auto &left, const auto &right) { return left.first < right.first; @@ -712,6 +735,9 @@ void OrderListModel::onOrderUpdated(const bs::network::Order& order) Group *groupItem = nullptr; Market *marketItem = nullptr; + // NOTE: because of batch orders update removeRowIfContainerChanged do nothing. + // Once batch update removed, that method should also be changed in order to display + // futures trades properly. ( Mostly futures group, since it display accumulated value ) removeRowIfContainerChanged(order, found.second); findMarketAndGroup(order, marketItem, groupItem); @@ -751,13 +777,20 @@ QVariant OrderListModel::Group::getQuantityColor() const return QVariant{}; } +QVariant OrderListModel::Group::getValue() const +{ + return QVariant{}; +} + +QVariant OrderListModel::Group::getValueColor() const +{ + return QVariant{}; +} + void OrderListModel::Group::addRow(const bs::network::Order& order) { // As quantity is now could be negative need to invert value - double value = - order.quantity * order.price; - if (order.security.substr(0, order.security.find('/')) != order.product) { - value = order.quantity / order.price; - } + double value = getOrderValue(order); rows_.push_front(make_unique( UiUtils::displayTimeMs(order.dateTime), @@ -774,6 +807,7 @@ void OrderListModel::Group::addRow(const bs::network::Order& order) void OrderListModel::FuturesGroup::addOrder(const bs::network::Order& order) { quantity_ += order.quantity; + value_ += getOrderValue(order); addRow(order); } @@ -790,3 +824,68 @@ QVariant OrderListModel::FuturesGroup::getQuantityColor() const return kSettledColor; } + +void OrderListModel::DisplayFuturesDeliveryRow() +{ + // check if there are pending futures + Market *futuresMarket = nullptr; + + for ( const auto& row : unsettled_->rows_) { + if (row->assetType_ == bs::network::Asset::Type::Futures) { + futuresMarket = row.get(); + break; + } + } + + if (futuresMarket == nullptr) { + return; + } + + beginInsertRows(createIndex(pendingFuturesSettlement_->row_, 0, &pendingFuturesSettlement_->idx_), static_cast(pendingFuturesSettlement_->rows_.size()), static_cast(pendingFuturesSettlement_->rows_.size())); + pendingFuturesSettlement_->rows_.emplace_back(make_unique(bs::network::Asset::Type::Futures, &pendingFuturesSettlement_->idx_)); + Market * marketItem = pendingFuturesSettlement_->rows_.back().get(); + endInsertRows(); + + for (const auto& group : futuresMarket->rows_) { + FuturesGroup* sourceGroup = dynamic_cast(group.get()); + + if (sourceGroup != nullptr) { + beginInsertRows(createIndex(findMarket(pendingFuturesSettlement_.get(), marketItem), 0, &marketItem->idx_), + static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); + + marketItem->rows_.emplace_back(make_unique( + sourceGroup, &marketItem->idx_)); + + endInsertRows(); + } + } +} + + +QVariant OrderListModel::FuturesDeliveryGroup::getQuantity() const +{ + return UiUtils::displayAmount(quantity_); +} + +QVariant OrderListModel::FuturesDeliveryGroup::getQuantityColor() const +{ + if (quantity_ < 0) { + return kFailedColor; + } + + return kSettledColor; +} + +QVariant OrderListModel::FuturesDeliveryGroup::getValue() const +{ + return UiUtils::displayCurrencyAmount(value_); +} + +QVariant OrderListModel::FuturesDeliveryGroup::getValueColor() const +{ + if (value_ < 0) { + return kFailedColor; + } + + return kSettledColor; +} diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 498446687..e32d46337 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -40,6 +40,7 @@ class OrderListModel : public QAbstractItemModel struct Header { enum Index { Time = 0, + NameColumn = 0, Product, Side, Quantity, @@ -137,6 +138,9 @@ public slots: virtual QVariant getQuantity() const; virtual QVariant getQuantityColor() const; + + virtual QVariant getValue() const; + virtual QVariant getValueColor() const; protected: void addRow(const bs::network::Order& order); }; @@ -154,8 +158,26 @@ public slots: QVariant getQuantity() const override; QVariant getQuantityColor() const override; - private: double quantity_ = 0; + double value_ = 0; + }; + + struct FuturesDeliveryGroup : public FuturesGroup + { + FuturesDeliveryGroup(FuturesGroup* source, IndexHelper *parent) + : FuturesGroup(source->security_, parent) + { + quantity_ = source->quantity_; + value_ = source->value_; + } + + ~FuturesDeliveryGroup() override = default; + + QVariant getQuantity() const override; + QVariant getQuantityColor() const override; + + QVariant getValue() const override; + QVariant getValueColor() const override; }; @@ -186,7 +208,8 @@ public slots: first = 0, UnSettled = first, Settled, - last + PendingSettlements, + Undefined }; StatusGroup(const QString &name, int row) @@ -215,10 +238,14 @@ public slots: void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &msg); void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message); - std::shared_ptr assetManager_; + void DisplayFuturesDeliveryRow(); + +private: + std::shared_ptr assetManager_; std::unordered_map groups_; - std::unique_ptr unsettled_; - std::unique_ptr settled_; + std::unique_ptr unsettled_; + std::unique_ptr settled_; + std::unique_ptr pendingFuturesSettlement_; QDateTime latestOrderTimestamp_; std::vector> sortedPeviousOrderStatuses_{}; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index beb2a80c8..649124ece 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -39,6 +39,8 @@ #include #include +#include +#include using namespace bs::ui; using namespace Blocksettle::Communication; @@ -222,6 +224,8 @@ void RFQReplyWidget::init(const std::shared_ptr &logger ui_->treeViewOrders->setModel(orderListModel); ui_->treeViewOrders->initWithModel(orderListModel); + ui_->treeViewOrders->setItemDelegateForColumn( + static_cast(OrderListModel::Header::Status), new PushButtonDelegate(ui_->treeViewOrders)); connect(celerClient_.get(), &BaseCelerClient::OnConnectedToServer, this , &RFQReplyWidget::onConnectedToCeler); diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 0ce6b9680..0a69f7d51 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -25,6 +25,11 @@ #include "UtxoReservationToken.h" #include "HDPath.h" +#include +#include +#include +#include + namespace Ui { class RFQReplyWidget; } @@ -192,4 +197,76 @@ private slots: std::unordered_map sentReplyToSettlementsIds_, settlementToReplyIds_; }; +#include + +class PushButtonDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit PushButtonDelegate(QWidget* parent) + { + button_ = new QPushButton(tr("Submit"), parent); + button_->hide(); + } + ~PushButtonDelegate() override = default; + + QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const override + { + auto statusGroupIndex = index; + int depth = 0; + while (statusGroupIndex.parent().isValid()) { + statusGroupIndex = statusGroupIndex.parent(); + ++depth; + } + + if (statusGroupIndex.row() != 2 || depth != 1) { + return QStyledItemDelegate::sizeHint(option, index); + } + + auto minSizeHint = button_->minimumSizeHint(); + auto minSize = button_->minimumSize(); + auto sizeHint = button_->sizeHint(); + + return sizeHint; + } + + void paint(QPainter* painter, const QStyleOptionViewItem& opt, + const QModelIndex& index) const override + { + auto statusGroupIndex = index; + int depth = 0; + while (statusGroupIndex.parent().isValid()) { + statusGroupIndex = statusGroupIndex.parent(); + ++depth; + } + + if (statusGroupIndex.row() != 2 || depth != 2) { + QStyledItemDelegate::paint(painter, opt, index); + return; + } + + auto minSizeHint = button_->minimumSizeHint(); + auto rect = opt.rect; + + if (rect.width() < minSizeHint.width()) { + rect.setWidth(minSizeHint.width()); + } + + if (rect.height() < minSizeHint.height()) { + rect.setHeight(minSizeHint.height()); + } + + button_->setGeometry(rect); + + qDebug() << "PushButtonDelegate " << opt.state; + + QPixmap map = button_->grab(); + painter->drawPixmap(rect.x(), rect.y(), map); + } + +private: + QPushButton *button_; +}; + #endif // __RFQ_REPLY_WIDGET_H__ From 4b202ef24d2fe9b89b0aa1e1b38fd8f5e1f3f0c2 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 24 Nov 2020 16:32:20 +0300 Subject: [PATCH 045/146] JSON API --- BlockSettleApp/main.cpp | 2 + BlockSettleUILib/Settings/APISettingsPage.cpp | 11 +- Core/ApiAdapter.h | 12 + Core/ApiJson.cpp | 590 ++++++++++++++++++ Core/ApiJson.h | 95 +++ Core/BsServerAdapter.cpp | 12 +- Core/SettingsAdapter.cpp | 78 +++ Core/SettingsAdapter.h | 2 + common | 2 +- 9 files changed, 790 insertions(+), 14 deletions(-) create mode 100644 Core/ApiJson.cpp create mode 100644 Core/ApiJson.h diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 63de7932a..a6aec0d6c 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -35,6 +35,7 @@ #include "Adapters/OnChainTrackerAdapter.h" #include "Adapters/WalletsAdapter.h" #include "ApiAdapter.h" +#include "ApiJson.h" #include "AssetsAdapter.h" #include "BsServerAdapter.h" #include "ChatAdapter.h" @@ -327,6 +328,7 @@ int main(int argc, char** argv) const auto &apiAdapter = std::make_shared(logMgr->logger("API")); const auto &guiAdapter = std::make_shared(logMgr->logger("ui")); apiAdapter->add(guiAdapter); + apiAdapter->add(std::make_shared(logMgr->logger("json"))); inprocBus.addAdapter(apiAdapter); const auto &signAdapter = std::make_shared(logMgr->logger()); diff --git a/BlockSettleUILib/Settings/APISettingsPage.cpp b/BlockSettleUILib/Settings/APISettingsPage.cpp index f376e48d8..a81c73dde 100644 --- a/BlockSettleUILib/Settings/APISettingsPage.cpp +++ b/BlockSettleUILib/Settings/APISettingsPage.cpp @@ -86,11 +86,11 @@ void APISettingsPage::display() else { ui_->toggleAutoSign->setChecked(settings_.at(ApplicationSettings::AutoSigning).toBool()); ui_->toggleEnableRFQ->setChecked(settings_.at(ApplicationSettings::AutoQouting).toBool()); - ui_->lineEditConnName->setText(settings_.at(ApplicationSettings::ExtConnName).toString()); - ui_->lineEditConnHost->setText(settings_.at(ApplicationSettings::ExtConnHost).toString()); + ui_->lineEditConnName->setEnabled(false); + ui_->lineEditConnHost->setEnabled(false); ui_->lineEditConnPort->setText(settings_.at(ApplicationSettings::ExtConnPort).toString()); - ui_->lineEditConnPubKey->setText(settings_.at(ApplicationSettings::ExtConnPubKey).toString()); - ui_->labelOwnPubKey->setText(settings_.at(ApplicationSettings::ExtConnOwnPubKey).toString()); + ui_->lineEditConnPubKey->setEnabled(false); + ui_->labelOwnPubKey->setEnabled(false); apiKeyEncrypted_ = settings_.at(ApplicationSettings::LoginApiKey).toString().toStdString(); } @@ -116,10 +116,7 @@ void APISettingsPage::apply() else { emit putSetting(ApplicationSettings::AutoSigning, ui_->toggleAutoSign->isChecked()); emit putSetting(ApplicationSettings::AutoQouting, ui_->toggleEnableRFQ->isChecked()); - emit putSetting(ApplicationSettings::ExtConnName, ui_->lineEditConnName->text()); - emit putSetting(ApplicationSettings::ExtConnHost, ui_->lineEditConnHost->text()); emit putSetting(ApplicationSettings::ExtConnPort, ui_->lineEditConnPort->text()); - emit putSetting(ApplicationSettings::ExtConnPubKey, ui_->lineEditConnPubKey->text()); emit putSetting(ApplicationSettings::LoginApiKey, QString::fromStdString(apiKeyEncrypted_)); } } diff --git a/Core/ApiAdapter.h b/Core/ApiAdapter.h index 1b323da4f..58dac9043 100644 --- a/Core/ApiAdapter.h +++ b/Core/ApiAdapter.h @@ -39,10 +39,21 @@ namespace bs { else if (value() < 0) { return "Broadcast"; } + else if (!name_.empty()) { + return name_; + } else { return "User#" + std::to_string(value()); } } + + void setName(const std::string& name) + { + name_ = name; + } + + private: + std::string name_; }; } } @@ -61,6 +72,7 @@ class ApiBusAdapter : public bs::message::Adapter void setUserId(const bs::message::UserValue userVal) { user_ = std::make_shared(userVal); + user_->setName(name()); } protected: diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp new file mode 100644 index 000000000..9d8727c4e --- /dev/null +++ b/Core/ApiJson.cpp @@ -0,0 +1,590 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "ApiJson.h" +#include +#include "Address.h" +#include "BsClient.h" +#include "MessageUtils.h" +#include "ProtobufUtils.h" +#include "SslServerConnection.h" +#include "StringUtils.h" + +#include "common.pb.h" +#include "json.pb.h" +#include "terminal.pb.h" + +using namespace BlockSettle::Common; +using namespace BlockSettle::Terminal; +using namespace BlockSettle::API::JSON; +using namespace bs::message; +using namespace ProtobufUtils; + +static constexpr auto kRequestTimeout = std::chrono::seconds{ 90 }; + + +ApiJsonAdapter::ApiJsonAdapter(const std::shared_ptr &logger) + : logger_(logger) + , userSettings_(UserTerminal::create(TerminalUsers::Settings)) +{} + +bool ApiJsonAdapter::process(const Envelope &env) +{ + if (std::dynamic_pointer_cast(env.sender)) { + switch (env.sender->value()) { + case TerminalUsers::System: + return processAdminMessage(env); + case TerminalUsers::Settings: + return processSettings(env); + case TerminalUsers::Blockchain: + return processBlockchain(env); + case TerminalUsers::Signer: + return processSigner(env); + case TerminalUsers::Wallets: + return processWallets(env); + case TerminalUsers::BsServer: + return processBsServer(env); + case TerminalUsers::Settlement: + return processSettlement(env); + case TerminalUsers::Matching: + return processMatching(env); + case TerminalUsers::MktData: + return processMktData(env); + case TerminalUsers::OnChainTracker: + return processOnChainTrack(env); + case TerminalUsers::Assets: + return processAssets(env); + default: break; + } + } + else if (env.receiver && (env.sender->value() == user_->value()) + && (env.sender->value() == env.receiver->value())) { // own to self messages + if (env.message == "GC") { + processGCtimeout(); + } + } + else { + logger_->warn("[{}] non-terminal #{} user {}", __func__, env.id + , env.sender->name()); + } + return true; +} + +static std::shared_ptr mapUser(const EnvelopeIn::MessageCase& user) +{ + static const std::map> usersMap = { + { EnvelopeIn::kSigner, UserTerminal::create(TerminalUsers::Signer) }, + { EnvelopeIn::kMatching, UserTerminal::create(TerminalUsers::Matching) }, + { EnvelopeIn::kAssets, UserTerminal::create(TerminalUsers::Assets) }, + { EnvelopeIn::kMarketData, UserTerminal::create(TerminalUsers::MktData) }, + { EnvelopeIn::kMdHist, UserTerminal::create(TerminalUsers::MDHistory) }, + { EnvelopeIn::kBlockchain, UserTerminal::create(TerminalUsers::Blockchain) }, + { EnvelopeIn::kWallets, UserTerminal::create(TerminalUsers::Wallets) }, + { EnvelopeIn::kOnChainTracker, UserTerminal::create(TerminalUsers::OnChainTracker) }, + { EnvelopeIn::kSettlement, UserTerminal::create(TerminalUsers::Settlement) }, + { EnvelopeIn::kChat, UserTerminal::create(TerminalUsers::Chat) } + }; + try { + return usersMap.at(user); + } + catch (const std::exception&) { + return {}; + } +} + +void ApiJsonAdapter::OnDataFromClient(const std::string& clientId + , const std::string& data) +{ + const auto& sendErrorReply = [this, clientId] + (const std::string& id, const std::string& errorMsg) + { + ErrorResponse msg; + if (!id.empty()) { + msg.set_id(id); + } + if (!errorMsg.empty()) { + msg.set_error_text(errorMsg); + } + const auto& jsonData = toJsonCompact(msg); + if (!connection_->SendDataToClient(clientId, jsonData)) { + logger_->error("[ApiJsonAdapter::OnDataFromClient::sendErrorReply] failed to send"); + } + }; + logger_->debug("[{}] received from {}:\n{}", __func__, bs::toHex(clientId), data); + EnvelopeIn jsonMsg; + if (!fromJson(data, &jsonMsg)) { + sendErrorReply({}, "failed to parse"); + return; + } + std::string serMsg; + switch (jsonMsg.message_case()) { + case EnvelopeIn::kSigner: + serMsg = jsonMsg.signer().SerializeAsString(); + break; + //TODO: case other users + default: break; + } + Envelope env{ 0, user_, mapUser(jsonMsg.message_case()), {}, {}, serMsg, true }; + if (!pushFill(env)) { + sendErrorReply(jsonMsg.id(), "internal error"); + } + requests_[env.id] = { clientId, jsonMsg.id(), std::chrono::system_clock::now() }; +} + +void ApiJsonAdapter::OnClientConnected(const std::string& clientId + , const Details& details) +{ + connectedClients_.insert(clientId); + logger_->info("[{}] {} (total {}) connected from {}", __func__, bs::toHex(clientId) + , connectedClients_.size(), details.at(Detail::IpAddr)); + ConnectedResponse msg; + msg.set_wallets_ready(walletsReady_); + msg.set_blockchain_state(armoryState_); + msg.set_top_block(blockNum_); + msg.set_signer_state(signerState_); + const auto& jsonData = toJsonCompact(msg); + if (!connection_->SendDataToClient(clientId, jsonData)) { + logger_->error("[ApiJsonAdapter::OnClientConnected] failed to send"); + } + if (connectedClients_.size() == 1) { + sendGCtimeout(); + } +} + +void ApiJsonAdapter::OnClientDisconnected(const std::string& clientId) +{ + connectedClients_.erase(clientId); + logger_->info("[{}] {} disconnected ({} remain)", __func__, bs::toHex(clientId) + , connectedClients_.size()); +} + +bool ApiJsonAdapter::processSettings(const Envelope &env) +{ + SettingsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettingsMessage::kGetResponse: + return processSettingsGetResponse(msg.get_response()); + case SettingsMessage::kSettingsUpdated: + return processSettingsGetResponse(msg.settings_updated()); + case SettingsMessage::kApiPrivkey: + connectionPrivKey_.assign(msg.api_privkey().cbegin(), msg.api_privkey().cend()); + connection_ = std::make_unique(logger_ + , SslServerConnectionParams{ true, false, true + , bs::network::ws::generateSelfSignedCert(connectionPrivKey_) + , connectionPrivKey_, [this](const std::string& publicKey) -> bool + { + if (clientPubKeys_.empty()) { // no client keys configured + return true; // TODO: change this if unknown clients are forbidden + } + if (clientPubKeys_.find(publicKey) == clientPubKeys_.end()) { + logger_->error("[ApiJsonAdapter] unknown client key {}" + , bs::toHex(publicKey)); + return false; + } + return true; + } } + ); + break; + case SettingsMessage::kApiClientKeys: + for (const auto& clientKey : msg.api_client_keys().pub_keys()) { + clientPubKeys_.insert(clientKey); + } + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResponse &response) +{ + std::map settings; + for (const auto &setting : response.responses()) { + switch (setting.request().index()) { + case SetIdx_ExtConnPort: + if (!connection_) { + SPDLOG_LOGGER_ERROR(logger_, "connection should be created at this point"); + break; + } + if (connection_->BindConnection("0.0.0.0", setting.s(), this)) { + logger_->debug("[ApiJsonAdapter] connection ready on port {}", setting.s()); + } + else { + SPDLOG_LOGGER_ERROR(logger_, "failed to bind to {}", setting.s()); + } + break; + } + } + return true; +} + +bool ApiJsonAdapter::processAdminMessage(const Envelope &env) +{ + AdministrativeMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse admin msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case AdministrativeMessage::kStart: + processStart(); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processBlockchain(const Envelope &env) +{ + ArmoryMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[QtGuiAdapter::processBlockchain] failed to parse msg #{}" + , env.id); + if (!env.receiver) { + logger_->debug("[{}] no receiver", __func__); + } + return true; + } + switch (msg.data_case()) { + case ArmoryMessage::kStateChanged: + armoryState_ = msg.state_changed().state(); + blockNum_ = msg.state_changed().top_block(); + sendReplyToClient(0, msg, env.sender); + break; + case ArmoryMessage::kNewBlock: + blockNum_ = msg.new_block().top_block(); + sendReplyToClient(0, msg, env.sender); + break; + case ArmoryMessage::kWalletRegistered: + if (msg.wallet_registered().success() && msg.wallet_registered().wallet_id().empty()) { + walletsReady_ = true; + } + break; + case ArmoryMessage::kLedgerEntries: [[fallthrough]] + case ArmoryMessage::kAddressHistory: [[fallthrough]] + case ArmoryMessage::kFeeLevelsResponse: + if (hasRequest(env.id)) { + sendReplyToClient(env.id, msg, env.sender); + } + break; + case ArmoryMessage::kZcReceived: [[fallthrough]] + case ArmoryMessage::kZcInvalidated: + sendReplyToClient(0, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processSigner(const Envelope &env) +{ + SignerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[QtGuiAdapter::processSigner] failed to parse msg #{}" + , env.id); + if (!env.receiver) { + logger_->debug("[{}] no receiver", __func__); + } + return true; + } + switch (msg.data_case()) { + case SignerMessage::kState: + signerState_ = msg.state().code(); + sendReplyToClient(0, msg, env.sender); + break; + case SignerMessage::kNeedNewWalletPrompt: + break; + case SignerMessage::kSignTxResponse: + sendReplyToClient(env.id, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processWallets(const Envelope &env) +{ + WalletsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case WalletsMessage::kWalletLoaded: [[fallthrough]] + case WalletsMessage::kHdWallet: + sendReplyToClient(0, msg, env.sender); + break; + case WalletsMessage::kWalletAddresses: [[fallthrough]] + case WalletsMessage::kAddrComments: [[fallthrough]] + case WalletsMessage::kWalletData: [[fallthrough]] + case WalletsMessage::kWalletBalances: [[fallthrough]] + case WalletsMessage::kTxDetailsResponse: [[fallthrough]] + case WalletsMessage::kWalletsListResponse: [[fallthrough]] + case WalletsMessage::kUtxos: [[fallthrough]] + case WalletsMessage::kAuthWallet: [[fallthrough]] + case WalletsMessage::kAuthKey: [[fallthrough]] + case WalletsMessage::kReservedUtxos: + sendReplyToClient(env.id, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processOnChainTrack(const Envelope &env) +{ + OnChainTrackMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case OnChainTrackMessage::kAuthState: [[fallthrough]] + case OnChainTrackMessage::kVerifiedAuthAddresses: + sendReplyToClient(env.id, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processAssets(const bs::message::Envelope& env) +{ + AssetsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case AssetsMessage::kSubmittedAuthAddrs: [[fallthrough]] + case AssetsMessage::kBalance: + sendReplyToClient(env.id, msg, env.sender); + break; + default: break; + } + return true; +} + +void ApiJsonAdapter::processStart() +{ + logger_->debug("[{}]", __func__); + SettingsMessage msg; + msg.set_api_privkey(""); + Envelope envReq1{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(envReq1); + + msg.mutable_api_client_keys(); + Envelope envReq2{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(envReq2); + + auto msgReq = msg.mutable_get_request(); + auto setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_ExtConnPort); + setReq->set_type(SettingType_String); + Envelope envReq3{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; + pushFill(envReq3); +} + +bool ApiJsonAdapter::processBsServer(const bs::message::Envelope& env) +{ + BsServerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case BsServerMessage::kStartLoginResult: [[fallthrough]] + case BsServerMessage::kLoginResult: + sendReplyToClient(0, msg, env.sender); // multicast login results to all connected clients + break; + case BsServerMessage::kOrdersUpdate: + sendReplyToClient(env.id, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processSettlement(const bs::message::Envelope& env) +{ + SettlementMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettlementMessage::kQuoteReqNotif: + sendReplyToClient(0, msg, env.sender); + break; + case SettlementMessage::kQuote: [[fallthrough]] + case SettlementMessage::kMatchedQuote: [[fallthrough]] + case SettlementMessage::kFailedSettlement: [[fallthrough]] + case SettlementMessage::kPendingSettlement: [[fallthrough]] + case SettlementMessage::kSettlementComplete: [[fallthrough]] + case SettlementMessage::kQuoteCancelled: + sendReplyToClient(env.id, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processMatching(const bs::message::Envelope& env) +{ + MatchingMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MatchingMessage::kLoggedIn: [[fallthrough]] + case MatchingMessage::kLoggedOut: + sendReplyToClient(0, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::processMktData(const bs::message::Envelope& env) +{ + MktDataMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MktDataMessage::kDisconnected: [[fallthrough]] + case MktDataMessage::kNewSecurity: [[fallthrough]] + case MktDataMessage::kAllInstrumentsReceived: [[fallthrough]] + case MktDataMessage::kPriceUpdate: + sendReplyToClient(0, msg, env.sender); + break; + default: break; + } + return true; +} + +bool ApiJsonAdapter::hasRequest(uint64_t msgId) const +{ + return (requests_.find(msgId) != requests_.end()); +} + +bool ApiJsonAdapter::sendReplyToClient(uint64_t msgId + , const google::protobuf::Message& msg + , const std::shared_ptr& sender) +{ + if (!connection_ || connectedClients_.empty()) { + return true; + } + EnvelopeOut envOut; + std::string clientId; + if (msgId) { + const auto& itReq = requests_.find(msgId); + if (itReq == requests_.end()) { + logger_->error("[{}] no request found for #{} from {}; reply:\n{}" + , __func__, msgId, sender->name(), msg.DebugString()); + return false; + } + clientId = itReq->second.clientId; + envOut.set_id(itReq->second.requestId); + itReq->second.replied = true; + } + google::protobuf::Message* targetMsg = nullptr; + switch (sender->value()) { + case TerminalUsers::Signer: + targetMsg = envOut.mutable_signer(); + break; + case TerminalUsers::Matching: + targetMsg = envOut.mutable_matching(); + break; + case TerminalUsers::Assets: + targetMsg = envOut.mutable_assets(); + break; + case TerminalUsers::MktData: + targetMsg = envOut.mutable_market_data(); + break; + case TerminalUsers::MDHistory: + targetMsg = envOut.mutable_md_hist(); + break; + case TerminalUsers::Blockchain: + targetMsg = envOut.mutable_blockchain(); + break; + case TerminalUsers::Wallets: + targetMsg = envOut.mutable_wallets(); + break; + case TerminalUsers::OnChainTracker: + targetMsg = envOut.mutable_on_chain_tracker(); + break; + case TerminalUsers::Settlement: + targetMsg = envOut.mutable_settlement(); + break; + case TerminalUsers::Chat: + targetMsg = envOut.mutable_chat(); + break; + default: + logger_->warn("[{}] unhandled sender {}", __func__, sender->value()); + break; + } + if (targetMsg) { + targetMsg->CopyFrom(msg); + } + else { + logger_->error("[{}] unknown target message", __func__); + return false; + } + const auto& jsonData = toJsonCompact(envOut); + if (clientId.empty()) { + return connection_->SendDataToAllClients(jsonData); + } + else { + if (connectedClients_.find(clientId) == connectedClients_.end()) { + logger_->error("[{}] client {} is not connected", __func__, bs::toHex(clientId)); + return false; + } + return connection_->SendDataToClient(clientId, jsonData); + } +} + +void ApiJsonAdapter::sendGCtimeout() +{ + const auto& timeNow = std::chrono::system_clock::now(); + Envelope env{ 0, user_, user_, timeNow, timeNow + kRequestTimeout, "GC" }; + pushFill(env); +} + +void ApiJsonAdapter::processGCtimeout() +{ + std::vector deleteRequests; + const auto& timeNow = std::chrono::system_clock::now(); + for (const auto& req : requests_) { + if ((timeNow - req.second.timestamp) > kRequestTimeout) { + if (!req.second.replied) { + logger_->info("[{}] request #{}/{} from {} was never replied", __func__ + , req.first, req.second.requestId, bs::toHex(req.second.clientId)); + } + deleteRequests.push_back(req.first); + } + } + if (!deleteRequests.empty()) { + logger_->debug("[{}] removing {} outdated request[s]", __func__ + , deleteRequests.size()); + for (const auto& id : deleteRequests) { + requests_.erase(id); + } + } + if (!connectedClients_.empty()) { + sendGCtimeout(); + } +} diff --git a/Core/ApiJson.h b/Core/ApiJson.h new file mode 100644 index 000000000..6bc6a785b --- /dev/null +++ b/Core/ApiJson.h @@ -0,0 +1,95 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef API_JSON_H +#define API_JSON_H + +#include +#include +#include "Address.h" +#include "ApiAdapter.h" +#include "ServerConnection.h" +#include "ServerConnectionListener.h" +#include "SignContainer.h" +#include "WsConnection.h" + +namespace BlockSettle { + namespace Terminal { + class SettingsMessage_SettingsResponse; + } +} + +class ApiJsonAdapter : public ApiBusAdapter, public ServerConnectionListener +{ +public: + ApiJsonAdapter(const std::shared_ptr &); + ~ApiJsonAdapter() override = default; + + bool process(const bs::message::Envelope &) override; + + std::set> supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "JSON API"; } + +protected: // ServerConnectionListener overrides + void OnDataFromClient(const std::string& clientId, const std::string& data) override; + void OnClientConnected(const std::string& clientId, const Details& details) override; + void OnClientDisconnected(const std::string& clientId) override; + +private: + bool processSettings(const bs::message::Envelope &); + bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); + + bool processAdminMessage(const bs::message::Envelope &); + bool processAssets(const bs::message::Envelope&); + bool processBlockchain(const bs::message::Envelope&); + bool processBsServer(const bs::message::Envelope&); + bool processMatching(const bs::message::Envelope&); + bool processMktData(const bs::message::Envelope&); + bool processOnChainTrack(const bs::message::Envelope&); + bool processSettlement(const bs::message::Envelope&); + bool processSigner(const bs::message::Envelope&); + bool processWallets(const bs::message::Envelope&); + + void processStart(); + + bool hasRequest(uint64_t msgId) const; + bool sendReplyToClient(uint64_t msgId, const google::protobuf::Message& + , const std::shared_ptr &sender); + + void sendGCtimeout(); + void processGCtimeout(); + +private: + std::shared_ptr logger_; + std::shared_ptr userSettings_; + std::unique_ptr connection_; + bs::network::ws::PrivateKey connectionPrivKey_; + std::set clientPubKeys_; + std::set connectedClients_; + + int armoryState_{ -1 }; + uint32_t blockNum_{ 0 }; + int signerState_{ -1 }; + bool walletsReady_{ false }; + + struct ClientRequest + { + std::string clientId; + std::string requestId; + std::chrono::system_clock::time_point timestamp; + bool replied{ false }; + }; + std::map requests_; +}; + + +#endif // API_JSON_H diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 9c11a6aa2..487d18097 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -205,13 +205,13 @@ bool BsServerAdapter::processStartLogin(const std::string& login) if (!connected_) { return false; // wait for connection to complete } - if (!currentLogin_.empty()) { - logger_->warn("[{}] can't start before login {} processing is complete" - , __func__, currentLogin_); - return false; + if (currentLogin_.empty()) { + currentLogin_ = login; + bsClient_->startLogin(login); + } + else { + onStartLoginDone(true, {}); } - currentLogin_ = login; - bsClient_->startLogin(login); return true; } diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 4f423a257..6f964e9f6 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -18,6 +18,7 @@ #include "Settings/SignersProvider.h" #include "LogManager.h" #include "PubKeyLoader.h" +#include "WsConnection.h" #include "common.pb.h" #include "terminal.pb.h" @@ -176,6 +177,10 @@ bool SettingsAdapter::process(const bs::message::Envelope &env) return processBootstrap(env, msg.load_bootstrap()); case SettingsMessage::kGetBootstrap: return processBootstrap(env, {}); + case SettingsMessage::kApiPrivkey: + return processApiPrivKey(env); + case SettingsMessage::kApiClientKeys: + return processApiClientsList(env); default: logger_->warn("[SettingsAdapter::process] unknown data case: {}" , msg.data_case()); @@ -300,6 +305,79 @@ bool SettingsAdapter::processBootstrap(const bs::message::Envelope &env return pushFill(envResp); } +static bs::network::ws::PrivateKey readOrCreatePrivateKey(const std::string& filename) +{ + bs::network::ws::PrivateKey result; + std::ifstream privKeyReader(filename, std::ios::binary); + if (privKeyReader.is_open()) { + std::string str; + str.assign(std::istreambuf_iterator(privKeyReader) + , std::istreambuf_iterator()); + result.reserve(str.size()); + std::for_each(str.cbegin(), str.cend(), [&result](char c) { + result.push_back(c); + }); + } + if (result.empty()) { + result = bs::network::ws::generatePrivKey(); + std::ofstream privKeyWriter(filename, std::ios::out | std::ios::binary); + privKeyWriter.write((char*)&result[0], result.size()); + const auto& pubKey = bs::network::ws::publicKey(result); + std::ofstream pubKeyWriter(filename + ".pub", std::ios::out | std::ios::binary); + pubKeyWriter << pubKey; + } + return result; +} + +bool SettingsAdapter::processApiPrivKey(const bs::message::Envelope& env) +{ //FIXME: should be re-implemented to avoid storing plain private key in a file on disk + const auto &apiKeyFN = appSettings_->AppendToWritableDir( + QString::fromStdString("apiPrivKey")).toStdString(); + const auto &apiPrivKey = readOrCreatePrivateKey(apiKeyFN); + SettingsMessage msg; + SecureBinaryData privKey(apiPrivKey.data(), apiPrivKey.size()); + msg.set_api_privkey(privKey.toBinStr()); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + return pushFill(envResp); +} + +static std::set readClientKeys(const std::string& filename) +{ + std::ifstream in(filename); + if (!in.is_open()) { + return {}; + } + std::set result; + std::string str; + while (std::getline(in, str)) { + if (str.empty()) { + continue; + } + try { + const auto& pubKey = BinaryData::CreateFromHex(str).toBinStr(); + result.insert(pubKey); + } catch (const std::exception&) {} // ignore invalid keys for now + } + return result; +} + +bool SettingsAdapter::processApiClientsList(const bs::message::Envelope& env) +{ + const auto& apiKeysFN = appSettings_->AppendToWritableDir( + QString::fromStdString("apiClientPubKeys")).toStdString(); + const auto& clientKeys = readClientKeys(apiKeysFN); + if (clientKeys.empty()) { + logger_->debug("[{}] no API client keys found", __func__); + } + SettingsMessage msg; + const auto msgResp = msg.mutable_api_client_keys(); + for (const auto& clientKey : clientKeys) { + msgResp->add_pub_keys(clientKey); + } + Envelope envResp{ env.id, user_, env.sender, {}, {}, msg.SerializeAsString() }; + return pushFill(envResp); +} + std::shared_ptr SettingsAdapter::createOnChainPlug() const { return std::make_shared(queue_); diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index fa64c95c6..36f7f5102 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -96,6 +96,8 @@ class SettingsAdapter : public bs::message::Adapter bool processResetToState(const bs::message::Envelope& , const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); bool processBootstrap(const bs::message::Envelope&, const std::string&); + bool processApiPrivKey(const bs::message::Envelope&); + bool processApiClientsList(const bs::message::Envelope&); private: std::shared_ptr user_, userBC_; diff --git a/common b/common index 2bdedb70e..16ccfc1c6 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 2bdedb70e97ae47de919b1a919d91909ce0b32d1 +Subproject commit 16ccfc1c6a2cf80e18e84133d59a0982602dbb93 From 4c5c922ba45c4b4f6f2d0355c6f96296acc0212a Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 25 Nov 2020 10:15:57 +0200 Subject: [PATCH 046/146] Display obligations properly --- BlockSettleUILib/BSTerminalMainWindow.cpp | 70 +++------------------ BlockSettleUILib/BSTerminalMainWindow.h | 1 - BlockSettleUILib/OrderListModel.cpp | 67 +++++++++++++------- BlockSettleUILib/OrderListModel.h | 30 +++++++-- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 4 +- common | 2 +- 6 files changed, 81 insertions(+), 93 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 49bc4ffa6..c880a2fbb 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2226,7 +2226,6 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli connect(bsClient_.get(), &BsClient::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); connect(bsClient_.get(), &BsClient::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); - connect(bsClient_.get(), &BsClient::processPbMessage, this, &BSTerminalMainWindow::onMessageFromPB); // connect to RFQ dialog connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); @@ -2483,66 +2482,7 @@ void BSTerminalMainWindow::onAuthLeafCreated() } } -void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &message) -{ - if (message.data_case() == Blocksettle::Communication::ProxyTerminalPb::Response::kDeliveryRequest) { - const auto& request = message.delivery_request();\ - auto uiLogger = logMgr_->logger("ui"); - - const int64_t toDeliver = request.to_deliver(); - - QString deliverMessage; - const QString header = tr("Trade obligations"); - - if (toDeliver < 0) { - bs::Address bsAddress; - - try { - bsAddress = bs::Address::fromAddressString(request.bs_address()); - } - catch (...) { - uiLogger->error("[BSTerminalMainWindow::onMessageFromPB] could not parse BS address: {}" - , request.bs_address()); - return; - } - - bs::XBTAmount amountToDeliver{static_cast(-toDeliver)}; - - deliverMessage = tr("You have obligations to deliver %1 XBT in next 24h to %2.").arg(UiUtils::displayAmount(amountToDeliver), QString::fromStdString(bsAddress.display())); - showInfo(header, deliverMessage); - - - std::shared_ptr createTxDlg; - - Bip21::PaymentRequestInfo requestInfo; - - requestInfo.address = QString::fromStdString(bsAddress.display()); - requestInfo.amount = amountToDeliver; - requestInfo.message = tr("EURXBT1 delivery"); - requestInfo.feePerByte = utxoReservationMgr_->feeRatePb(); - //requestInfo.requestExpireDateTime = ; - - if (applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { - createTxDlg = CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsMgr_ - , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ - , requestInfo, this); - } else { - createTxDlg = CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsMgr_ - , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ - , requestInfo, this); - } - - DisplayCreateTransactionDialog(createTxDlg); - - } else { - deliverMessage = tr("You are about to receive %1 XBT. Please make sure to deliver your cash obligations. Press OK to submit delivery address.").arg(UiUtils::displayAmount(bs::XBTAmount{ static_cast(toDeliver)})); - showInfo(header, deliverMessage); - - SendBSDeliveryAddress(); - } - } -} - +// NOT USED NOW. Keep until next iteration. void BSTerminalMainWindow::SendBSDeliveryAddress() { const auto wallet = walletsMgr_->getDefaultWallet(); @@ -2556,5 +2496,11 @@ void BSTerminalMainWindow::SendBSDeliveryAddress() wallet->syncAddresses(); } }; - wallet->getNewExtAddress(cbAddr); + + auto existingAddresses = wallet->getExtAddressList(); + if (existingAddresses.empty()) { + wallet->getNewExtAddress(cbAddr); + } else { + bsClient_->submitDeliveryAddress(existingAddresses[0]); + } } diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index 3a751c8f5..d018f07fc 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -260,7 +260,6 @@ private slots: void onBsConnectionDisconnected(); void onBsConnectionFailed(); - void onMessageFromPB(const ProxyTerminalPb::Response &response); void onInitWalletDialogWasShown(); diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 216cea03c..59941350a 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -145,6 +145,10 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const return g->getQuantity(); case Header::Value: return g->getValue(); + case Header::Price: + return g->getPrice(); + case Header::Status: + return g->getStatus(); default: return QVariant(); } @@ -695,7 +699,7 @@ void OrderListModel::processUpdateOrders(const Blocksettle::Communication::Proxy onOrderUpdated(order); } - DisplayFuturesDeliveryRow(); + DisplayFuturesDeliveryRow(message.obligation()); } void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) @@ -787,6 +791,16 @@ QVariant OrderListModel::Group::getValueColor() const return QVariant{}; } +QVariant OrderListModel::Group::getPrice() const +{ + return QVariant{}; +} + +QVariant OrderListModel::Group::getStatus() const +{ + return QVariant{}; +} + void OrderListModel::Group::addRow(const bs::network::Order& order) { // As quantity is now could be negative need to invert value @@ -825,19 +839,23 @@ QVariant OrderListModel::FuturesGroup::getQuantityColor() const return kSettledColor; } -void OrderListModel::DisplayFuturesDeliveryRow() +QVariant OrderListModel::FuturesGroup::getValue() const { - // check if there are pending futures - Market *futuresMarket = nullptr; + return UiUtils::displayCurrencyAmount(value_); +} - for ( const auto& row : unsettled_->rows_) { - if (row->assetType_ == bs::network::Asset::Type::Futures) { - futuresMarket = row.get(); - break; - } +QVariant OrderListModel::FuturesGroup::getValueColor() const +{ + if (value_ < 0) { + return kFailedColor; } - if (futuresMarket == nullptr) { + return kSettledColor; +} + +void OrderListModel::DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation) +{ + if (obligation.to_deliver() == 0) { return; } @@ -846,19 +864,12 @@ void OrderListModel::DisplayFuturesDeliveryRow() Market * marketItem = pendingFuturesSettlement_->rows_.back().get(); endInsertRows(); - for (const auto& group : futuresMarket->rows_) { - FuturesGroup* sourceGroup = dynamic_cast(group.get()); + beginInsertRows(createIndex(findMarket(pendingFuturesSettlement_.get(), marketItem), 0, &marketItem->idx_), + static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - if (sourceGroup != nullptr) { - beginInsertRows(createIndex(findMarket(pendingFuturesSettlement_.get(), marketItem), 0, &marketItem->idx_), - static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); + marketItem->rows_.emplace_back(make_unique(QStringLiteral("XBT/EUR"), &marketItem->idx_, obligation.to_deliver(), obligation.price())); - marketItem->rows_.emplace_back(make_unique( - sourceGroup, &marketItem->idx_)); - - endInsertRows(); - } - } + endInsertRows(); } @@ -889,3 +900,17 @@ QVariant OrderListModel::FuturesDeliveryGroup::getValueColor() const return kSettledColor; } + +QVariant OrderListModel::FuturesDeliveryGroup::getPrice() const +{ + return UiUtils::displayPriceXBT(price_); +} + +QVariant OrderListModel::FuturesDeliveryGroup::getStatus() const +{ + if (quantity_ < 0) { + return tr("Create TX"); + } + + return QVariant{}; +} diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index e32d46337..1824dfdb7 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -25,6 +25,7 @@ namespace Blocksettle { namespace Communication { namespace ProxyTerminalPb { class Response; + class Response_DeliveryObligationsRequest; class Response_UpdateOrders; } } @@ -141,6 +142,9 @@ public slots: virtual QVariant getValue() const; virtual QVariant getValueColor() const; + virtual QVariant getPrice() const; + + virtual QVariant getStatus() const; protected: void addRow(const bs::network::Order& order); }; @@ -158,17 +162,23 @@ public slots: QVariant getQuantity() const override; QVariant getQuantityColor() const override; + QVariant getValue() const override; + QVariant getValueColor() const override; + + private: double quantity_ = 0; double value_ = 0; }; - struct FuturesDeliveryGroup : public FuturesGroup + struct FuturesDeliveryGroup : public Group { - FuturesDeliveryGroup(FuturesGroup* source, IndexHelper *parent) - : FuturesGroup(source->security_, parent) + FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, double quantity, double price) + : Group(sec, parent) { - quantity_ = source->quantity_; - value_ = source->value_; + quantity_ = quantity; + price_ = price; + + value_ = quantity_ * price_; } ~FuturesDeliveryGroup() override = default; @@ -178,6 +188,14 @@ public slots: QVariant getValue() const override; QVariant getValueColor() const override; + + QVariant getPrice() const override; + QVariant getStatus() const override; + + private: + double quantity_ = 0; + double value_ = 0; + double price_ = 0; }; @@ -238,7 +256,7 @@ public slots: void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &msg); void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message); - void DisplayFuturesDeliveryRow(); + void DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation); private: std::shared_ptr assetManager_; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 649124ece..c10b8a4d0 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -224,8 +224,8 @@ void RFQReplyWidget::init(const std::shared_ptr &logger ui_->treeViewOrders->setModel(orderListModel); ui_->treeViewOrders->initWithModel(orderListModel); - ui_->treeViewOrders->setItemDelegateForColumn( - static_cast(OrderListModel::Header::Status), new PushButtonDelegate(ui_->treeViewOrders)); + // ui_->treeViewOrders->setItemDelegateForColumn( + // static_cast(OrderListModel::Header::Status), new PushButtonDelegate(ui_->treeViewOrders)); connect(celerClient_.get(), &BaseCelerClient::OnConnectedToServer, this , &RFQReplyWidget::onConnectedToCeler); diff --git a/common b/common index a2d85e42e..51b85e3e1 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a2d85e42ef35120591ac2247866b595963f9a53b +Subproject commit 51b85e3e144c9d9e00085b0708aaeec2798c90e1 From d98d4fdfd8a1913e0bc8708da1cbecfcb30cd8e6 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 25 Nov 2020 10:16:11 +0200 Subject: [PATCH 047/146] Switch to a new update message --- BlockSettleUILib/OrderListModel.cpp | 8 ++++---- BlockSettleUILib/OrderListModel.h | 6 +++--- common | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 59941350a..10badf173 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -384,8 +384,8 @@ void OrderListModel::onMessageFromPB(const Blocksettle::Communication::ProxyTerm connected_ = true; switch (response.data_case()) { - case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrders: - processUpdateOrders(response.update_orders()); + case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrdersObligations: + processUpdateOrders(response.update_orders_obligations()); break; default: break; @@ -656,7 +656,7 @@ void OrderListModel::reset() endResetModel(); } -void OrderListModel::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) +void OrderListModel::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message) { // Save latest selected index first resetLatestChangedStatus(message); @@ -702,7 +702,7 @@ void OrderListModel::processUpdateOrders(const Blocksettle::Communication::Proxy DisplayFuturesDeliveryRow(message.obligation()); } -void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message) +void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message) { latestChangedTimestamp_ = {}; diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 1824dfdb7..ef506e2de 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -26,7 +26,7 @@ namespace Blocksettle { namespace ProxyTerminalPb { class Response; class Response_DeliveryObligationsRequest; - class Response_UpdateOrders; + class Response_UpdateOrdersAndObligations; } } } @@ -253,8 +253,8 @@ public slots: void createGroupsIfNeeded(const bs::network::Order &order, Market *&market, Group *&group); void reset(); - void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &msg); - void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders &message); + void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &msg); + void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message); void DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation); diff --git a/common b/common index 51b85e3e1..a6842f9b7 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 51b85e3e144c9d9e00085b0708aaeec2798c90e1 +Subproject commit a6842f9b7abe136f6acee55e852999605b76937c From d345f8dd6846a09ecf4ab83e1c857d6e694ad7a2 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 25 Nov 2020 11:37:08 +0200 Subject: [PATCH 048/146] Fix obligations display --- BlockSettleUILib/OrderListModel.cpp | 29 ++++++++++++++++++++++++++++- BlockSettleUILib/OrderListModel.h | 11 +++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 10badf173..6c58b5d8d 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -153,6 +153,20 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const return QVariant(); } } + + case Qt::TextAlignmentRole : { + switch (index.column()) { + case Header::Quantity : + case Header::Price : + case Header::Value : + return static_cast(Qt::AlignRight | Qt::AlignVCenter); + case Header::Status: + return static_cast(Qt::AlignLeft | Qt::AlignVCenter); + default : + return QVariant(); + } + } + case Qt::BackgroundRole: { switch(index.column()) { case Header::Quantity: @@ -867,11 +881,24 @@ void OrderListModel::DisplayFuturesDeliveryRow(const Blocksettle::Communication: beginInsertRows(createIndex(findMarket(pendingFuturesSettlement_.get(), marketItem), 0, &marketItem->idx_), static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - marketItem->rows_.emplace_back(make_unique(QStringLiteral("XBT/EUR"), &marketItem->idx_, obligation.to_deliver(), obligation.price())); + marketItem->rows_.emplace_back(make_unique(QStringLiteral("XBT/EUR") + , &marketItem->idx_, obligation.to_deliver(), obligation.price())); endInsertRows(); } +OrderListModel::FuturesDeliveryGroup::FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, int64_t quantity, double price) + : Group(sec, parent) +{ + quantity_ = static_cast(quantity) / BTCNumericTypes::BalanceDivider; + price_ = price; + + value_ = -quantity_ * price_; + + if (quantity < 0) { + toDeliver = bs::XBTAmount{ static_cast(-quantity) }; + } +} QVariant OrderListModel::FuturesDeliveryGroup::getQuantity() const { diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index ef506e2de..28da6f508 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -172,14 +172,7 @@ public slots: struct FuturesDeliveryGroup : public Group { - FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, double quantity, double price) - : Group(sec, parent) - { - quantity_ = quantity; - price_ = price; - - value_ = quantity_ * price_; - } + FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, int64_t quantity, double price); ~FuturesDeliveryGroup() override = default; @@ -196,6 +189,8 @@ public slots: double quantity_ = 0; double value_ = 0; double price_ = 0; + + bs::XBTAmount toDeliver; }; From e14f07450b377a16e06207f550bf53158d52dbfa Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 25 Nov 2020 12:39:02 +0200 Subject: [PATCH 049/146] Create TX for delivery obligation --- BlockSettleUILib/BSTerminalMainWindow.cpp | 47 +++++++++++ BlockSettleUILib/BSTerminalMainWindow.h | 2 + BlockSettleUILib/OrderListModel.cpp | 81 ++++++++++++++++++- BlockSettleUILib/OrderListModel.h | 26 +++++- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 14 ++++ BlockSettleUILib/Trading/RFQReplyWidget.h | 5 ++ BlockSettleUILib/Trading/RFQRequestWidget.cpp | 15 ++++ BlockSettleUILib/Trading/RFQRequestWidget.h | 5 ++ 8 files changed, 190 insertions(+), 5 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index c880a2fbb..3832bad4c 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2044,6 +2044,11 @@ void BSTerminalMainWindow::InitWidgets() connect(ui_->widgetRFQ, &RFQRequestWidget::loginRequested, this , &BSTerminalMainWindow::onLogin); + connect(ui_->widgetRFQ, &RFQRequestWidget::CreateObligationDeliveryTX, this + , &BSTerminalMainWindow::onDeliverFutureObligations); + connect(ui_->widgetRFQReply, &RFQReplyWidget::CreateObligationDeliveryTX, this + , &BSTerminalMainWindow::onDeliverFutureObligations); + connect(ui_->tabWidget, &QTabWidget::tabBarClicked, this, [requestRFQ = QPointer(ui_->widgetRFQ) , replyRFQ = QPointer(ui_->widgetRFQReply) @@ -2504,3 +2509,45 @@ void BSTerminalMainWindow::SendBSDeliveryAddress() bsClient_->submitDeliveryAddress(existingAddresses[0]); } } + +void BSTerminalMainWindow::onDeliverFutureObligations(const QModelIndex& index) +{ + auto deliveryObligationData = orderListModel_->getDeliveryObligationData(index); + if (!deliveryObligationData.isValid()) { + return; + } + + auto uiLogger = logMgr_->logger("ui"); + bs::Address bsAddress; + + try { + bsAddress = bs::Address::fromAddressString(deliveryObligationData.bsAddress); + } + catch (...) { + uiLogger->error("[BSTerminalMainWindow::onDeliverFutureObligations] could not parse BS address: {}" + , deliveryObligationData.bsAddress); + return; + } + + std::shared_ptr createTxDlg; + + Bip21::PaymentRequestInfo requestInfo; + + requestInfo.address = QString::fromStdString(deliveryObligationData.bsAddress); + requestInfo.amount = deliveryObligationData.deliveryAmount; + requestInfo.message = tr("EURXBT1 delivery"); + requestInfo.feePerByte = utxoReservationMgr_->feeRatePb(); + //requestInfo.requestExpireDateTime = ; + + if (applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { + createTxDlg = CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsMgr_ + , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ + , requestInfo, this); + } else { + createTxDlg = CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsMgr_ + , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ + , requestInfo, this); + } + + DisplayCreateTransactionDialog(createTxDlg); +} diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index d018f07fc..250b588bd 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -178,6 +178,8 @@ private slots: void onAuthLeafCreated(); + void onDeliverFutureObligations(const QModelIndex& index); + private: std::unique_ptr ui_; QAction *action_send_ = nullptr; diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 6c58b5d8d..1e9591a3d 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -882,21 +882,24 @@ void OrderListModel::DisplayFuturesDeliveryRow(const Blocksettle::Communication: static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); marketItem->rows_.emplace_back(make_unique(QStringLiteral("XBT/EUR") - , &marketItem->idx_, obligation.to_deliver(), obligation.price())); + , &marketItem->idx_, obligation.to_deliver(), obligation.price(), obligation.bs_address())); endInsertRows(); } -OrderListModel::FuturesDeliveryGroup::FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, int64_t quantity, double price) +OrderListModel::FuturesDeliveryGroup::FuturesDeliveryGroup(const QString &sec, IndexHelper *parent + , int64_t quantity, double price + , const std::string& bsAddress) : Group(sec, parent) { quantity_ = static_cast(quantity) / BTCNumericTypes::BalanceDivider; price_ = price; + bsAddress_ = bsAddress; value_ = -quantity_ * price_; if (quantity < 0) { - toDeliver = bs::XBTAmount{ static_cast(-quantity) }; + toDeliver_ = bs::XBTAmount{ static_cast(-quantity) }; } } @@ -941,3 +944,75 @@ QVariant OrderListModel::FuturesDeliveryGroup::getStatus() const return QVariant{}; } + +bool OrderListModel::FuturesDeliveryGroup::deliveryRequired() const +{ + return toDeliver_.GetValue() != 0; +} + +OrderListModel::DeliveryObligationData OrderListModel::FuturesDeliveryGroup::getDeliveryObligationData() const +{ + return DeliveryObligationData{bsAddress_, toDeliver_}; +} + +bool OrderListModel::DeliveryRequired(const QModelIndex &index) +{ + if (!isFutureDeliveryIndex(index)) { + return false; + } + + auto futuresDelivery = GetFuturesDeliveryGroup(index); + if (futuresDelivery == nullptr) { + return false; + } + + return futuresDelivery->deliveryRequired(); +} + +bool OrderListModel::isFutureDeliveryIndex(const QModelIndex &index) const +{ + if (!index.isValid()) { + return false; + } + + if (index.column() != Header::Status) { + return false; + } + + { + auto statusGroupIndex = index; + int depth = 0; + while (statusGroupIndex.parent().isValid()) { + statusGroupIndex = statusGroupIndex.parent(); + ++depth; + } + + if (statusGroupIndex.row() != 2 || depth != 2) { + return false; + } + } + + return true; +} + +OrderListModel::FuturesDeliveryGroup* OrderListModel::GetFuturesDeliveryGroup(const QModelIndex &index) const +{ + auto marketIndex = index.parent(); + Market* market = pendingFuturesSettlement_->rows_[marketIndex.row()].get(); + + return dynamic_cast(market->rows_[index.row()].get()); +} + +OrderListModel::DeliveryObligationData OrderListModel::getDeliveryObligationData(const QModelIndex &index) const +{ + if (!isFutureDeliveryIndex(index)) { + return DeliveryObligationData{}; + } + + auto futuresDelivery = GetFuturesDeliveryGroup(index); + if (futuresDelivery == nullptr) { + return DeliveryObligationData{}; + } + + return futuresDelivery->getDeliveryObligationData(); +} diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 28da6f508..6842a4e74 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -53,6 +53,16 @@ class OrderListModel : public QAbstractItemModel static QString toString(Index); }; + struct DeliveryObligationData + { + std::string bsAddress; + bs::XBTAmount deliveryAmount; + + bool isValid() const { + return !bsAddress.empty(); + } + }; + OrderListModel(const std::shared_ptr &, QObject *parent = nullptr); ~OrderListModel() noexcept override = default; @@ -63,6 +73,10 @@ class OrderListModel : public QAbstractItemModel int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + bool DeliveryRequired(const QModelIndex &index); + + DeliveryObligationData getDeliveryObligationData(const QModelIndex &index) const; public slots: void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); void onDisconnected(); @@ -172,7 +186,7 @@ public slots: struct FuturesDeliveryGroup : public Group { - FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, int64_t quantity, double price); + FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, int64_t quantity, double price, const std::string& bsAddress); ~FuturesDeliveryGroup() override = default; @@ -185,12 +199,17 @@ public slots: QVariant getPrice() const override; QVariant getStatus() const override; + bool deliveryRequired() const; + + DeliveryObligationData getDeliveryObligationData() const; + private: double quantity_ = 0; double value_ = 0; double price_ = 0; - bs::XBTAmount toDeliver; + bs::XBTAmount toDeliver_; + std::string bsAddress_; }; @@ -253,6 +272,9 @@ public slots: void DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation); + bool isFutureDeliveryIndex(const QModelIndex &index) const; + FuturesDeliveryGroup* GetFuturesDeliveryGroup(const QModelIndex &index) const; + private: std::shared_ptr assetManager_; std::unordered_map groups_; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index c10b8a4d0..dfd3c6419 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -156,6 +156,7 @@ void RFQReplyWidget::init(const std::shared_ptr &logger connectionManager_ = connectionManager; autoSignProvider_ = autoSignProvider; utxoReservationManager_ = utxoReservationManager; + orderListModel_ = orderListModel; statsCollector_ = std::make_shared(appSettings , ApplicationSettings::Filter_MD_QN_cnt); @@ -224,6 +225,8 @@ void RFQReplyWidget::init(const std::shared_ptr &logger ui_->treeViewOrders->setModel(orderListModel); ui_->treeViewOrders->initWithModel(orderListModel); + connect(ui_->treeViewOrders, &QTreeView::clicked, this, &RFQReplyWidget::onOrderClicked); + // ui_->treeViewOrders->setItemDelegateForColumn( // static_cast(OrderListModel::Header::Status), new PushButtonDelegate(ui_->treeViewOrders)); @@ -708,3 +711,14 @@ void RFQReplyWidget::onMessageFromPB(const ProxyTerminalPb::Response &response) } // if not processed - not RFQ releated message. not error } + +void RFQReplyWidget::onOrderClicked(const QModelIndex &index) +{ + if (!index.isValid()) { + return; + } + + if (orderListModel_->DeliveryRequired(index)) { + emit CreateObligationDeliveryTX(index); + } +} diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 0a69f7d51..4ae1d22d1 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -118,6 +118,8 @@ Q_OBJECT void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); + void CreateObligationDeliveryTX(const QModelIndex& index); + public slots: void forceCheckCondition(); @@ -145,6 +147,8 @@ private slots: void onCancelCCTrade(const std::string& clientOrderId); void onSettlementComplete(const std::string &id); + void onOrderClicked(const QModelIndex &index); + private: void onResetCurrentReservation(const std::shared_ptr &data); void showSettlementDialog(QDialog *dlg); @@ -190,6 +194,7 @@ private slots: std::shared_ptr connectionManager_; std::shared_ptr autoSignProvider_; std::shared_ptr utxoReservationManager_; + OrderListModel* orderListModel_; std::unordered_map sentXbtReplies_; std::unordered_map sentCCReplies_; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 35355dcbf..e0076cff8 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -229,6 +229,7 @@ void RFQRequestWidget::init(const std::shared_ptr &logger armory_ = armory; autoSignProvider_ = autoSignProvider; utxoReservationManager_ = utxoReservationManager; + orderListModel_ = orderListModel; if (walletsManager_) { autoSignProvider_->scriptRunner()->setWalletsManager(walletsManager_); @@ -240,6 +241,9 @@ void RFQRequestWidget::init(const std::shared_ptr &logger ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui_->treeViewOrders->setModel(orderListModel); ui_->treeViewOrders->initWithModel(orderListModel); + + connect(ui_->treeViewOrders, &QTreeView::clicked, this, &RFQRequestWidget::onOrderClicked); + connect(quoteProvider_.get(), &QuoteProvider::quoteOrderFilled, [](const std::string "eId) { NotificationCenter::notify(bs::ui::NotifyType::CelerOrder, {true, QString::fromStdString(quoteId)}); }); @@ -527,3 +531,14 @@ void RFQRequestWidget::onRFQCancelled(const std::string &id) { ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->rfqCancelled(id); } + +void RFQRequestWidget::onOrderClicked(const QModelIndex &index) +{ + if (!index.isValid()) { + return; + } + + if (orderListModel_->DeliveryRequired(index)) { + emit CreateObligationDeliveryTX(index); + } +} diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 556e8532e..fcd1e5e5b 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -106,6 +106,8 @@ Q_OBJECT void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin , const BinaryData &payinHash, QDateTime timestamp); + void CreateObligationDeliveryTX(const QModelIndex& index); + private: void showEditableRFQPage(); void popShield(); @@ -136,6 +138,8 @@ private slots: void onRFQExpired(const std::string &id); void onRFQCancelled(const std::string &id); + void onOrderClicked(const QModelIndex &index); + public slots: void forceCheckCondition(); @@ -148,6 +152,7 @@ public slots: std::shared_ptr assetManager_; std::shared_ptr authAddressManager_; std::shared_ptr dialogManager_; + OrderListModel* orderListModel_; std::shared_ptr walletsManager_; std::shared_ptr signingContainer_; From ccdb13efb68406b011cfb30f6e66a948d64a1a5e Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 25 Nov 2020 19:23:49 +0300 Subject: [PATCH 050/146] JSON API fixes --- Core/ApiJson.cpp | 40 ++++++++++++++++++++++++++++------------ Core/ApiJson.h | 2 ++ common | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index 9d8727c4e..b447c63c3 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -105,14 +105,15 @@ void ApiJsonAdapter::OnDataFromClient(const std::string& clientId const auto& sendErrorReply = [this, clientId] (const std::string& id, const std::string& errorMsg) { - ErrorResponse msg; + EnvelopeOut env; if (!id.empty()) { - msg.set_id(id); + env.set_id(id); } + auto msg = env.mutable_error(); if (!errorMsg.empty()) { - msg.set_error_text(errorMsg); + msg->set_error_text(errorMsg); } - const auto& jsonData = toJsonCompact(msg); + const auto& jsonData = toJsonCompact(env); if (!connection_->SendDataToClient(clientId, jsonData)) { logger_->error("[ApiJsonAdapter::OnDataFromClient::sendErrorReply] failed to send"); } @@ -144,12 +145,15 @@ void ApiJsonAdapter::OnClientConnected(const std::string& clientId connectedClients_.insert(clientId); logger_->info("[{}] {} (total {}) connected from {}", __func__, bs::toHex(clientId) , connectedClients_.size(), details.at(Detail::IpAddr)); - ConnectedResponse msg; - msg.set_wallets_ready(walletsReady_); - msg.set_blockchain_state(armoryState_); - msg.set_top_block(blockNum_); - msg.set_signer_state(signerState_); - const auto& jsonData = toJsonCompact(msg); + EnvelopeOut env; + auto msg = env.mutable_connected(); + msg->set_wallets_ready(walletsReady_); + msg->set_logged_user(loggedInUser_); + msg->set_matching_connected(matchingConnected_); + msg->set_blockchain_state(armoryState_); + msg->set_top_block(blockNum_); + msg->set_signer_state(signerState_); + const auto& jsonData = toJsonCompact(env); if (!connection_->SendDataToClient(clientId, jsonData)) { logger_->error("[ApiJsonAdapter::OnClientConnected] failed to send"); } @@ -404,10 +408,17 @@ bool ApiJsonAdapter::processBsServer(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case BsServerMessage::kStartLoginResult: [[fallthrough]] + case BsServerMessage::kStartLoginResult: + sendReplyToClient(0, msg, env.sender); // multicast login results to all connected clients + break; case BsServerMessage::kLoginResult: + loggedInUser_ = msg.login_result().login(); sendReplyToClient(0, msg, env.sender); // multicast login results to all connected clients break; + case BsServerMessage::kDisconnected: + loggedInUser_.clear(); + sendReplyToClient(0, msg, env.sender); + break; case BsServerMessage::kOrdersUpdate: sendReplyToClient(env.id, msg, env.sender); break; @@ -448,8 +459,13 @@ bool ApiJsonAdapter::processMatching(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case MatchingMessage::kLoggedIn: [[fallthrough]] + case MatchingMessage::kLoggedIn: + matchingConnected_ = true; + sendReplyToClient(0, msg, env.sender); + break; case MatchingMessage::kLoggedOut: + matchingConnected_ = false; + loggedInUser_.clear(); sendReplyToClient(0, msg, env.sender); break; default: break; diff --git a/Core/ApiJson.h b/Core/ApiJson.h index 6bc6a785b..ba7770c0c 100644 --- a/Core/ApiJson.h +++ b/Core/ApiJson.h @@ -76,6 +76,8 @@ class ApiJsonAdapter : public ApiBusAdapter, public ServerConnectionListener std::set clientPubKeys_; std::set connectedClients_; + std::string loggedInUser_; + bool matchingConnected_{ false }; int armoryState_{ -1 }; uint32_t blockNum_{ 0 }; int signerState_{ -1 }; diff --git a/common b/common index 16ccfc1c6..800724532 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 16ccfc1c6a2cf80e18e84133d59a0982602dbb93 +Subproject commit 8007245325ebc56f81372312ab69dab73910b6c0 From 399bc8a51bd95b835c304492e944ee33170a140b Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 25 Nov 2020 20:15:10 +0300 Subject: [PATCH 051/146] Add BsServer to JSON API --- Core/ApiJson.cpp | 6 +++++- common | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index b447c63c3..d0e52dabe 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -89,7 +89,8 @@ static std::shared_ptr mapUser(const EnvelopeIn::MessageCase& user { EnvelopeIn::kWallets, UserTerminal::create(TerminalUsers::Wallets) }, { EnvelopeIn::kOnChainTracker, UserTerminal::create(TerminalUsers::OnChainTracker) }, { EnvelopeIn::kSettlement, UserTerminal::create(TerminalUsers::Settlement) }, - { EnvelopeIn::kChat, UserTerminal::create(TerminalUsers::Chat) } + { EnvelopeIn::kChat, UserTerminal::create(TerminalUsers::Chat) }, + { EnvelopeIn::kBsServer, UserTerminal::create(TerminalUsers::BsServer) } }; try { return usersMap.at(user); @@ -549,6 +550,9 @@ bool ApiJsonAdapter::sendReplyToClient(uint64_t msgId case TerminalUsers::Chat: targetMsg = envOut.mutable_chat(); break; + case TerminalUsers::BsServer: + targetMsg = envOut.mutable_bs_server(); + break; default: logger_->warn("[{}] unhandled sender {}", __func__, sender->value()); break; diff --git a/common b/common index 800724532..ace77093c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 8007245325ebc56f81372312ab69dab73910b6c0 +Subproject commit ace77093c85be29855943d1ea9a00bc5c89473b4 From 7dd45a11e6bd9163b36e9a13d42182f3f2630290 Mon Sep 17 00:00:00 2001 From: Ation Date: Thu, 26 Nov 2020 09:28:24 +0200 Subject: [PATCH 052/146] Fix build --- BlockSettleUILib/BSTerminalMainWindow.cpp | 23 ----------------------- BlockSettleUILib/BSTerminalMainWindow.h | 2 -- common | 2 +- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 3832bad4c..36572d59b 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2487,29 +2487,6 @@ void BSTerminalMainWindow::onAuthLeafCreated() } } -// NOT USED NOW. Keep until next iteration. -void BSTerminalMainWindow::SendBSDeliveryAddress() -{ - const auto wallet = walletsMgr_->getDefaultWallet(); - - const auto &cbAddr = [this, wallet](const bs::Address &address) { - if (address.isValid()) { - - bsClient_->submitDeliveryAddress(address); - - wallet->setAddressComment(address, "EURXBT1 delivery"); - wallet->syncAddresses(); - } - }; - - auto existingAddresses = wallet->getExtAddressList(); - if (existingAddresses.empty()) { - wallet->getNewExtAddress(cbAddr); - } else { - bsClient_->submitDeliveryAddress(existingAddresses[0]); - } -} - void BSTerminalMainWindow::onDeliverFutureObligations(const QModelIndex& index) { auto deliveryObligationData = orderListModel_->getDeliveryObligationData(index); diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index 250b588bd..f55622008 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -306,8 +306,6 @@ private slots: void onBootstrapDataLoaded(const std::string& data); - void SendBSDeliveryAddress(); - private: enum class ChatInitState { diff --git a/common b/common index 5b4241e71..732c930ee 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 5b4241e71219e3406349a410f02238e9f9f2a847 +Subproject commit 732c930eeb524895032ca41d578daac96b7c39cb From c279a480b508a6ce5c0f038746f0179ffa24ca6c Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 27 Nov 2020 17:23:51 +0300 Subject: [PATCH 053/146] Moved MessageUtils from terminal/Core to common --- Core/ApiJson.cpp | 67 ++++++++++--- Core/MessageUtils.cpp | 227 ------------------------------------------ Core/MessageUtils.h | 49 --------- common | 2 +- 4 files changed, 56 insertions(+), 289 deletions(-) delete mode 100644 Core/MessageUtils.cpp delete mode 100644 Core/MessageUtils.h diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index d0e52dabe..7fc5d3465 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -130,10 +130,47 @@ void ApiJsonAdapter::OnDataFromClient(const std::string& clientId case EnvelopeIn::kSigner: serMsg = jsonMsg.signer().SerializeAsString(); break; - //TODO: case other users - default: break; + case EnvelopeIn::kMatching: + serMsg = jsonMsg.matching().SerializeAsString(); + break; + case EnvelopeIn::kAssets: + serMsg = jsonMsg.assets().SerializeAsString(); + break; + case EnvelopeIn::kMarketData: + serMsg = jsonMsg.market_data().SerializeAsString(); + break; + case EnvelopeIn::kMdHist: + serMsg = jsonMsg.md_hist().SerializeAsString(); + break; + case EnvelopeIn::kBlockchain: + serMsg = jsonMsg.blockchain().SerializeAsString(); + break; + case EnvelopeIn::kWallets: + serMsg = jsonMsg.wallets().SerializeAsString(); + break; + case EnvelopeIn::kOnChainTracker: + serMsg = jsonMsg.on_chain_tracker().SerializeAsString(); + break; + case EnvelopeIn::kSettlement: + serMsg = jsonMsg.settlement().SerializeAsString(); + break; + case EnvelopeIn::kChat: + serMsg = jsonMsg.chat().SerializeAsString(); + break; + case EnvelopeIn::kBsServer: + serMsg = jsonMsg.bs_server().SerializeAsString(); + break; + default: + logger_->warn("[{}] unknown message for {}", __func__, jsonMsg.message_case()); + break; } - Envelope env{ 0, user_, mapUser(jsonMsg.message_case()), {}, {}, serMsg, true }; + const auto& user = mapUser(jsonMsg.message_case()); + if (!user) { + logger_->error("[{}] failed to map user from {}", __func__ + , jsonMsg.message_case()); + return; + } + Envelope env{ 0, user_, user, {}, {}, serMsg, true }; if (!pushFill(env)) { sendErrorReply(jsonMsg.id(), "internal error"); } @@ -339,7 +376,9 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) case WalletsMessage::kAuthWallet: [[fallthrough]] case WalletsMessage::kAuthKey: [[fallthrough]] case WalletsMessage::kReservedUtxos: - sendReplyToClient(env.id, msg, env.sender); + if (hasRequest(env.id)) { + sendReplyToClient(env.id, msg, env.sender); + } break; default: break; } @@ -356,7 +395,9 @@ bool ApiJsonAdapter::processOnChainTrack(const Envelope &env) switch (msg.data_case()) { case OnChainTrackMessage::kAuthState: [[fallthrough]] case OnChainTrackMessage::kVerifiedAuthAddresses: - sendReplyToClient(env.id, msg, env.sender); + if (hasRequest(env.id)) { + sendReplyToClient(env.id, msg, env.sender); + } break; default: break; } @@ -371,9 +412,13 @@ bool ApiJsonAdapter::processAssets(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case AssetsMessage::kSubmittedAuthAddrs: [[fallthrough]] + case AssetsMessage::kSubmittedAuthAddrs: + if (hasRequest(env.id)) { + sendReplyToClient(env.id, msg, env.sender); + } + break; case AssetsMessage::kBalance: - sendReplyToClient(env.id, msg, env.sender); + sendReplyToClient(0, msg, env.sender); break; default: break; } @@ -409,8 +454,9 @@ bool ApiJsonAdapter::processBsServer(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case BsServerMessage::kStartLoginResult: - sendReplyToClient(0, msg, env.sender); // multicast login results to all connected clients + case BsServerMessage::kStartLoginResult: [[fallback]] + case BsServerMessage::kOrdersUpdate: + sendReplyToClient(0, msg, env.sender); // multicast to all connected clients break; case BsServerMessage::kLoginResult: loggedInUser_ = msg.login_result().login(); @@ -420,9 +466,6 @@ bool ApiJsonAdapter::processBsServer(const bs::message::Envelope& env) loggedInUser_.clear(); sendReplyToClient(0, msg, env.sender); break; - case BsServerMessage::kOrdersUpdate: - sendReplyToClient(env.id, msg, env.sender); - break; default: break; } return true; diff --git a/Core/MessageUtils.cpp b/Core/MessageUtils.cpp deleted file mode 100644 index b13683d85..000000000 --- a/Core/MessageUtils.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "MessageUtils.h" -#include "terminal.pb.h" - -using namespace bs::message; -using namespace BlockSettle::Terminal; - -bs::network::RFQ bs::message::fromMsg(const BlockSettle::Terminal::RFQ& msg) -{ - bs::network::RFQ rfq; - rfq.requestId = msg.id(); - rfq.security = msg.security(); - rfq.product = msg.product(); - rfq.assetType = static_cast(msg.asset_type()); - rfq.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - rfq.quantity = msg.quantity(); - rfq.requestorAuthPublicKey = msg.auth_pub_key(); - rfq.receiptAddress = msg.receipt_address(); - rfq.coinTxInput = msg.coin_tx_input(); - return rfq; -} - -void bs::message::toMsg(const bs::network::RFQ& rfq, BlockSettle::Terminal::RFQ* msg) -{ - msg->set_id(rfq.requestId); - msg->set_security(rfq.security); - msg->set_product(rfq.product); - msg->set_asset_type((int)rfq.assetType); - msg->set_buy(rfq.side == bs::network::Side::Buy); - msg->set_quantity(rfq.quantity); - msg->set_auth_pub_key(rfq.requestorAuthPublicKey); - msg->set_receipt_address(rfq.receiptAddress); - msg->set_coin_tx_input(rfq.coinTxInput); -} - - -bs::network::Quote bs::message::fromMsg(const BlockSettle::Terminal::Quote& msg) -{ - bs::network::Quote quote; - quote.requestId = msg.request_id(); - quote.quoteId = msg.quote_id(); - quote.security = msg.security(); - quote.product = msg.product(); - quote.price = msg.price(); - quote.quantity = msg.quantity(); - quote.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - quote.assetType = static_cast(msg.asset_type()); - quote.quotingType = static_cast(msg.quoting_type()); - quote.requestorAuthPublicKey = msg.req_auth_pub_key(); - quote.dealerAuthPublicKey = msg.deal_auth_pub_key(); - quote.settlementId = msg.settlement_id(); - quote.dealerTransaction = msg.dealer_tx(); - quote.expirationTime = QDateTime::fromSecsSinceEpoch(msg.expiration_time()); - quote.timeSkewMs = msg.time_skew_ms(); - quote.celerTimestamp = msg.timestamp(); - return quote; -} - -void bs::message::toMsg(const bs::network::Quote& quote, BlockSettle::Terminal::Quote* msg) -{ - msg->set_request_id(quote.requestId); - msg->set_quote_id(quote.quoteId); - msg->set_security(quote.security); - msg->set_product(quote.product); - msg->set_price(quote.price); - msg->set_quantity(quote.quantity); - msg->set_buy(quote.side == bs::network::Side::Buy); - msg->set_asset_type((int)quote.assetType); - msg->set_quoting_type((int)quote.quotingType); - msg->set_req_auth_pub_key(quote.requestorAuthPublicKey); - msg->set_deal_auth_pub_key(quote.dealerAuthPublicKey); - msg->set_settlement_id(quote.settlementId); - msg->set_dealer_tx(quote.dealerTransaction); - msg->set_expiration_time(quote.expirationTime.toSecsSinceEpoch()); - msg->set_time_skew_ms(quote.timeSkewMs); - msg->set_timestamp(quote.celerTimestamp); -} - - -bs::network::Order bs::message::fromMsg(const BlockSettle::Terminal::MatchingMessage_Order& msg) -{ - bs::network::Order order; - order.clOrderId = msg.cl_order_id(); - order.exchOrderId = QString::fromStdString(msg.exchange_id()); - order.quoteId = msg.quote_id(); - order.dateTime = QDateTime::fromMSecsSinceEpoch(msg.timestamp()); - order.security = msg.security(); - order.product = msg.product(); - order.settlementId = BinaryData::fromString(msg.settlement_id()); - order.reqTransaction = msg.requester_tx(); - order.dealerTransaction = msg.dealer_tx(); - order.pendingStatus = msg.pending_status(); - order.quantity = msg.quantity(); - order.leavesQty = msg.left_qty(); - order.price = msg.price(); - order.avgPx = msg.avg_price(); - order.side = msg.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - order.assetType = static_cast(msg.asset_type()); - order.status = static_cast(msg.status()); - order.info = msg.info(); - return order; -} - -void bs::message::toMsg(const bs::network::Order& order, MatchingMessage_Order* msg) -{ - msg->set_cl_order_id(order.clOrderId); - msg->set_exchange_id(order.exchOrderId.toStdString()); - msg->set_quote_id(order.quoteId); - msg->set_timestamp(order.dateTime.toMSecsSinceEpoch()); - msg->set_security(order.security); - msg->set_product(order.product); - msg->set_settlement_id(order.settlementId.toBinStr()); - msg->set_requester_tx(order.reqTransaction); - msg->set_dealer_tx(order.dealerTransaction); - msg->set_pending_status(order.pendingStatus); - msg->set_quantity(order.quantity); - msg->set_left_qty(order.leavesQty); - msg->set_price(order.price); - msg->set_avg_price(order.avgPx); - msg->set_buy(order.side == bs::network::Side::Buy); - msg->set_asset_type((int)order.assetType); - msg->set_status((int)order.status); - msg->set_info(order.info); -} - - -bs::network::QuoteReqNotification bs::message::fromMsg(const BlockSettle::Terminal::IncomingRFQ& msg) -{ - bs::network::QuoteReqNotification result; - result.quantity = msg.rfq().quantity(); - result.quoteRequestId = msg.rfq().id(); - result.security = msg.rfq().security(); - result.product = msg.rfq().product(); - result.requestorAuthPublicKey = msg.rfq().auth_pub_key(); - result.requestorRecvAddress = msg.rfq().receipt_address(); - result.side = msg.rfq().buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - result.assetType = static_cast(msg.rfq().asset_type()); - result.settlementId = msg.settlement_id(); - result.sessionToken = msg.session_token(); - result.party = msg.party(); - result.reason = msg.reason(); - result.account = msg.account(); - result.status = static_cast(msg.status()); - result.expirationTime = msg.expiration_ms(); - result.timestamp = msg.timestamp_ms(); - result.timeSkewMs = msg.time_skew_ms(); - return result; -} - -void bs::message::toMsg(const bs::network::QuoteReqNotification& qrn - , BlockSettle::Terminal::IncomingRFQ* msg) -{ - auto msgRFQ = msg->mutable_rfq(); - msgRFQ->set_quantity(qrn.quantity); - msgRFQ->set_id(qrn.quoteRequestId); - msgRFQ->set_security(qrn.security); - msgRFQ->set_product(qrn.product); - msgRFQ->set_auth_pub_key(qrn.requestorAuthPublicKey); - msgRFQ->set_receipt_address(qrn.requestorRecvAddress); - msgRFQ->set_buy(qrn.side == bs::network::Side::Buy); - msgRFQ->set_asset_type((int)qrn.assetType); - msg->set_settlement_id(qrn.settlementId); - msg->set_session_token(qrn.sessionToken); - msg->set_party(qrn.party); - msg->set_reason(qrn.reason); - msg->set_account(qrn.account); - msg->set_status((int)qrn.status); - msg->set_expiration_ms(qrn.expirationTime); - msg->set_timestamp_ms(qrn.timestamp); - msg->set_time_skew_ms(qrn.timeSkewMs); -} - - -bs::network::QuoteNotification bs::message::fromMsg(const BlockSettle::Terminal::ReplyToRFQ& msg) -{ - bs::network::QuoteNotification result; - result.authKey = msg.quote().deal_auth_pub_key(); - result.reqAuthKey = msg.quote().req_auth_pub_key(); - result.settlementId = msg.quote().settlement_id(); - result.quoteRequestId = msg.quote().request_id(); - result.security = msg.quote().security(); - result.product = msg.quote().product(); - result.transactionData = msg.quote().dealer_tx(); - result.assetType = static_cast(msg.quote().asset_type()); - result.side = msg.quote().buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - result.validityInS = (msg.quote().expiration_time() - msg.quote().timestamp()) / 1000; - result.price = msg.quote().price(); - result.quantity = msg.quote().quantity(); - result.sessionToken = msg.session_token(); - result.account = msg.account(); - result.receiptAddress = msg.dealer_recv_addr(); - return result; -} - -void bs::message::toMsg(const bs::network::QuoteNotification& qn - , BlockSettle::Terminal::ReplyToRFQ* msg) -{ - auto msgQuote = msg->mutable_quote(); - msgQuote->set_deal_auth_pub_key(qn.authKey); - msgQuote->set_req_auth_pub_key(qn.reqAuthKey); - msgQuote->set_settlement_id(qn.settlementId); - msgQuote->set_request_id(qn.quoteRequestId); - msgQuote->set_security(qn.security); - msgQuote->set_product(qn.product); - msgQuote->set_dealer_tx(qn.transactionData); - msgQuote->set_asset_type((int)qn.assetType); - msgQuote->set_buy(qn.side == bs::network::Side::Buy); - msgQuote->set_price(qn.price); - msgQuote->set_quantity(qn.quantity); - msg->set_session_token(qn.sessionToken); - msg->set_account(qn.account); - msg->set_dealer_recv_addr(qn.receiptAddress); - - const auto& timeNow = std::chrono::system_clock::now(); - msgQuote->set_timestamp(std::chrono::duration_cast(timeNow.time_since_epoch()).count()); - msgQuote->set_expiration_time(std::chrono::duration_cast( - (timeNow + std::chrono::seconds{qn.validityInS}).time_since_epoch()).count()); -} diff --git a/Core/MessageUtils.h b/Core/MessageUtils.h deleted file mode 100644 index dec999a16..000000000 --- a/Core/MessageUtils.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef MESSAGE_UTILS_H -#define MESSAGE_UTILS_H - -#include "Message/Bus.h" -#include "Message/Envelope.h" -#include "CommonTypes.h" - -namespace BlockSettle { - namespace Terminal { - class RFQ; - class Quote; - class IncomingRFQ; - class MatchingMessage_Order; - class ReplyToRFQ; - } -} - -namespace bs { - namespace message { - - bs::network::RFQ fromMsg(const BlockSettle::Terminal::RFQ&); - void toMsg(const bs::network::RFQ&, BlockSettle::Terminal::RFQ*); - - bs::network::Quote fromMsg(const BlockSettle::Terminal::Quote&); - void toMsg(const bs::network::Quote&, BlockSettle::Terminal::Quote*); - - bs::network::Order fromMsg(const BlockSettle::Terminal::MatchingMessage_Order&); - void toMsg(const bs::network::Order&, BlockSettle::Terminal::MatchingMessage_Order*); - - bs::network::QuoteReqNotification fromMsg(const BlockSettle::Terminal::IncomingRFQ&); - void toMsg(const bs::network::QuoteReqNotification&, BlockSettle::Terminal::IncomingRFQ*); - - bs::network::QuoteNotification fromMsg(const BlockSettle::Terminal::ReplyToRFQ&); - void toMsg(const bs::network::QuoteNotification&, BlockSettle::Terminal::ReplyToRFQ*); - - } // namespace message -} // namespace bs - -#endif // MESSAGE_UTILS_H diff --git a/common b/common index ace77093c..7270b8b19 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ace77093c85be29855943d1ea9a00bc5c89473b4 +Subproject commit 7270b8b199eb7d848f95d056a50c7fb8f5093e04 From df4c4de5d458b0d58b7c389ca213c4108153fffe Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 1 Dec 2020 19:15:54 +0300 Subject: [PATCH 054/146] API changes --- BlockSettleApp/main.cpp | 6 ++--- Core/ApiJson.cpp | 16 +++++-------- Core/BsServerAdapter.cpp | 28 +++++++++++++++++++--- Core/BsServerAdapter.h | 3 ++- Core/MatchingAdapter.cpp | 19 +++++++++++++-- Core/MatchingAdapter.h | 4 +++- GUI/QtWidgets/MainWindow.cpp | 16 ++----------- GUI/QtWidgets/MainWindow.h | 4 ---- GUI/QtWidgets/QtGuiAdapter.cpp | 44 ---------------------------------- GUI/QtWidgets/QtGuiAdapter.h | 7 +----- common | 2 +- 11 files changed, 60 insertions(+), 89 deletions(-) diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index a6aec0d6c..4cdd361fb 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -335,13 +335,13 @@ int main(int argc, char** argv) inprocBus.addAdapter(signAdapter); const auto& userBlockchain = bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain); + const auto& userWallets = bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets); inprocBus.addAdapter(std::make_shared(logMgr->logger("trk") , bs::message::UserTerminal::create(bs::message::TerminalUsers::OnChainTracker) - , userBlockchain, adSettings->createOnChainPlug())); + , userBlockchain, userWallets, adSettings->createOnChainPlug())); inprocBus.addAdapter(std::make_shared(logMgr->logger())); inprocBus.addAdapter(std::make_shared(logMgr->logger() - , bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets) - , signAdapter->createClient(), userBlockchain)); + , userWallets, signAdapter->createClient(), userBlockchain)); inprocBus.addAdapter(std::make_shared(logMgr->logger("bscon"))); inprocBus.addAdapter(std::make_shared(logMgr->logger("match"))); diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index 7fc5d3465..3a659fc41 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -363,6 +363,7 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) } switch (msg.data_case()) { case WalletsMessage::kWalletLoaded: [[fallthrough]] + case WalletsMessage::kAuthWallet: [[fallthrough]] case WalletsMessage::kHdWallet: sendReplyToClient(0, msg, env.sender); break; @@ -373,7 +374,6 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) case WalletsMessage::kTxDetailsResponse: [[fallthrough]] case WalletsMessage::kWalletsListResponse: [[fallthrough]] case WalletsMessage::kUtxos: [[fallthrough]] - case WalletsMessage::kAuthWallet: [[fallthrough]] case WalletsMessage::kAuthKey: [[fallthrough]] case WalletsMessage::kReservedUtxos: if (hasRequest(env.id)) { @@ -395,9 +395,7 @@ bool ApiJsonAdapter::processOnChainTrack(const Envelope &env) switch (msg.data_case()) { case OnChainTrackMessage::kAuthState: [[fallthrough]] case OnChainTrackMessage::kVerifiedAuthAddresses: - if (hasRequest(env.id)) { - sendReplyToClient(env.id, msg, env.sender); - } + sendReplyToClient(0, msg, env.sender); break; default: break; } @@ -479,16 +477,14 @@ bool ApiJsonAdapter::processSettlement(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case SettlementMessage::kQuoteReqNotif: - sendReplyToClient(0, msg, env.sender); - break; case SettlementMessage::kQuote: [[fallthrough]] case SettlementMessage::kMatchedQuote: [[fallthrough]] case SettlementMessage::kFailedSettlement: [[fallthrough]] case SettlementMessage::kPendingSettlement: [[fallthrough]] case SettlementMessage::kSettlementComplete: [[fallthrough]] - case SettlementMessage::kQuoteCancelled: - sendReplyToClient(env.id, msg, env.sender); + case SettlementMessage::kQuoteCancelled: [[fallthrough]] + case SettlementMessage::kQuoteReqNotif: + sendReplyToClient(0, msg, env.sender); break; default: break; } @@ -634,7 +630,7 @@ void ApiJsonAdapter::processGCtimeout() for (const auto& req : requests_) { if ((timeNow - req.second.timestamp) > kRequestTimeout) { if (!req.second.replied) { - logger_->info("[{}] request #{}/{} from {} was never replied", __func__ + logger_->debug("[{}] request #{}/{} from {} was never replied", __func__ , req.first, req.second.requestId, bs::toHex(req.second.clientId)); } deleteRequests.push_back(req.first); diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 487d18097..28492a0d8 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -22,6 +22,7 @@ #include "bs_communication.pb.h" #include "terminal.pb.h" +using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; @@ -30,6 +31,9 @@ BsServerAdapter::BsServerAdapter(const std::shared_ptr &logger) : logger_(logger) , user_(std::make_shared(TerminalUsers::BsServer)) , userSettl_(std::make_shared(TerminalUsers::Settlement)) + , userMtch_(std::make_shared(TerminalUsers::Matching)) + , userSettings_(std::make_shared(TerminalUsers::Settings)) + , userWallets_(std::make_shared(TerminalUsers::Wallets)) { connMgr_ = std::make_shared(logger_); connMgr_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); @@ -398,7 +402,6 @@ void BsServerAdapter::onGetLoginResultDone(const BsClientLoginResult& result) BsServerMessage msg; auto msgResp = msg.mutable_login_result(); msgResp->set_login(currentLogin_); - currentLogin_.clear(); msgResp->set_status((int)result.status); msgResp->set_user_type((int)result.userType); msgResp->set_error_text(result.errorMsg); @@ -416,6 +419,26 @@ void BsServerAdapter::onGetLoginResultDone(const BsClientLoginResult& result) msgTradeSet->set_dealer_auth_submit_addr_limit(result.tradeSettings.dealerAuthSubmitAddressLimit); Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); + + SettingsMessage msgSet; + msgSet.set_load_bootstrap(result.bootstrapDataSigned); + Envelope envSet{ 0, user_, userSettings_, {}, {}, msgSet.SerializeAsString(), true }; + pushFill(envSet); + + MatchingMessage msgMtch; + auto msgReq = msgMtch.mutable_login(); + msgReq->set_matching_login(result.celerLogin); + msgReq->set_terminal_login(currentLogin_); + Envelope envMtch{ 0, user_, userMtch_, {}, {}, msgMtch.SerializeAsString(), true }; + pushFill(envMtch); + + WalletsMessage msgWlt; + msgWlt.set_set_settlement_fee(result.feeRatePb); + Envelope envWlt{ 0, user_, userWallets_, {}, {}, msgWlt.SerializeAsString(), true }; + pushFill(envWlt); + + //TODO: send chat login + currentLogin_.clear(); } void BsServerAdapter::onCelerRecv(CelerAPI::CelerMessageType messageType, const std::string& data) @@ -424,8 +447,7 @@ void BsServerAdapter::onCelerRecv(CelerAPI::CelerMessageType messageType, const auto msgResp = msg.mutable_recv_matching(); msgResp->set_message_type((int)messageType); msgResp->set_data(data); - Envelope env{ 0, user_, bs::message::UserTerminal::create(bs::message::TerminalUsers::Matching) - , {}, {}, msg.SerializeAsString() }; // send directly to matching adapter, not broadcast + Envelope env{ 0, user_, userMtch_, {}, {}, msg.SerializeAsString() }; pushFill(env); } diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index e1cdeb2eb..046e84303 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -90,7 +90,8 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg private: std::shared_ptr logger_; - std::shared_ptr user_, userSettl_; + std::shared_ptr user_, userSettl_, userSettings_; + std::shared_ptr userMtch_, userWallets_; std::shared_ptr connMgr_; std::unique_ptr bsClient_; ApplicationSettings::EnvConfiguration envConfig_{ ApplicationSettings::EnvConfiguration::Unknown }; diff --git a/Core/MatchingAdapter.cpp b/Core/MatchingAdapter.cpp index 64fc4422c..b6c48e497 100644 --- a/Core/MatchingAdapter.cpp +++ b/Core/MatchingAdapter.cpp @@ -29,6 +29,7 @@ #include "DownstreamOrderProto.pb.h" #include "bitcoin/DownstreamBitcoinTransactionSigningProto.pb.h" +using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; using namespace com::celertech::marketmerchant::api::enums::orderstatus; @@ -42,6 +43,7 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) : logger_(logger) , user_(std::make_shared(TerminalUsers::Matching)) , userSettl_(std::make_shared(TerminalUsers::Settlement)) + , userWallets_(std::make_shared(TerminalUsers::Wallets)) { celerConnection_ = std::make_unique(logger, this, true, true); @@ -91,15 +93,17 @@ MatchingAdapter::MatchingAdapter(const std::shared_ptr &logger) void MatchingAdapter::connectedToServer() { + logger_->debug("[{}]", __func__); MatchingMessage msg; auto loggedIn = msg.mutable_logged_in(); loggedIn->set_user_type(static_cast(celerConnection_->celerUserType())); loggedIn->set_user_id(celerConnection_->userId()); loggedIn->set_user_name(celerConnection_->userName()); - Envelope env{ 0, user_, nullptr, {}, {}, msg.SerializeAsString() }; pushFill(env); + sendSetUserId(celerConnection_->userId()); + const auto &cbAccounts = [this](const std::vector& accVec) { // Remove duplicated entries if possible std::set accounts(accVec.cbegin(), accVec.cend()); @@ -125,6 +129,8 @@ void MatchingAdapter::connectedToServer() void MatchingAdapter::connectionClosed() { + sendSetUserId({}); + assignedAccount_.clear(); celerConnection_->CloseConnection(); @@ -159,7 +165,7 @@ bool MatchingAdapter::process(const bs::message::Envelope &env) pushFill(envBC); } } - else if (env.sender->value() == bs::message::TerminalUsers::BsServer) { + else if (!env.request && (env.sender->value() == bs::message::TerminalUsers::BsServer)) { BsServerMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[{}] failed to parse BsServer message #{}", __func__, env.id); @@ -352,6 +358,15 @@ void MatchingAdapter::cleanQuoteRequestCcy(const std::string& id) } } +void MatchingAdapter::sendSetUserId(const std::string& userId) +{ + logger_->debug("[{}] setting userId {}", __func__, userId); + WalletsMessage msg; + msg.set_set_user_id(celerConnection_->userId()); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + std::string MatchingAdapter::getQuoteRequestCcy(const std::string& id) const { std::string ccy; diff --git a/Core/MatchingAdapter.h b/Core/MatchingAdapter.h index 3dcaab1eb..0e6816019 100644 --- a/Core/MatchingAdapter.h +++ b/Core/MatchingAdapter.h @@ -76,6 +76,8 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget void saveQuoteRequestCcy(const std::string& id, const std::string& ccy); void cleanQuoteRequestCcy(const std::string& id); + void sendSetUserId(const std::string &userId); + bool onQuoteResponse(const std::string&); bool onQuoteReject(const std::string&); bool onOrderReject(const std::string&); @@ -89,7 +91,7 @@ class MatchingAdapter : public bs::message::Adapter, public CelerCallbackTarget private: std::shared_ptr logger_; - std::shared_ptr user_, userSettl_; + std::shared_ptr user_, userSettl_, userWallets_; std::unique_ptr celerConnection_; std::string assignedAccount_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 0ef0e8063..f36affc24 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -887,24 +887,16 @@ void MainWindow::onLoggedIn(const BsClientLoginResult& result) void MainWindow::activateClient(const BsClientLoginResult& result) { currentUserLogin_ = QString::fromStdString(result.login); -/* chatTokenData_ = result.chatTokenData; - chatTokenSign_ = result.chatTokenSign; - tryLoginIntoChat();*/ auto tradeSettings = std::make_shared(result.tradeSettings); - emit putSetting(ApplicationSettings::SubmittedAddressXbtLimit, static_cast(tradeSettings->xbtTier1Limit)); - - emit bootstrapDataLoaded(result.bootstrapDataSigned); + emit putSetting(ApplicationSettings::SubmittedAddressXbtLimit + , static_cast(tradeSettings->xbtTier1Limit)); setLoginButtonText(currentUserLogin_); setWidgetsAuthorized(true); ui_->widgetRFQ->onTradeSettings(tradeSettings); - emit setRecommendedFeeRate(result.feeRatePb); -// utxoReservationMgr_->setFeeRatePb(result.feeRatePb); - emit needMatchingLogin(result.celerLogin, result.login); - ui_->widgetWallets->setUsername(currentUserLogin_); actLogout_->setVisible(false); actLogin_->setEnabled(false); @@ -930,15 +922,12 @@ void MainWindow::onAccountTypeChanged(bs::network::UserType userType, bool enabl notifCenter_->enqueue(enabled ? bs::ui::NotifyType::AccountEnabled : bs::ui::NotifyType::AccountDisabled, {}); } -// authManager_->setUserType(userType); ui_->widgetChat->setUserType(enabled ? userType : bs::network::UserType::Chat); } void bs::gui::qt::MainWindow::onMatchingLogin(const std::string& mtchLogin , BaseCelerClient::CelerUserType userType, const std::string& userId) { - emit needSetUserId(userId); - ui_->actionAccountInformation->setEnabled(true); ui_->actionAuthenticationAddresses->setEnabled(userType != BaseCelerClient::CelerUserType::Market); ui_->actionOneTimePassword->setEnabled(true); @@ -985,7 +974,6 @@ void MainWindow::onLoggedOut() void MainWindow::onMatchingLogout() { - emit needSetUserId({}); emit needCloseBsConnection(); ui_->actionAccountInformation->setEnabled(false); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 0a369a0dd..d5737be96 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -154,7 +154,6 @@ namespace bs { void needArmoryReconnect(); void needSigners(); void setSigner(int); - void bootstrapDataLoaded(const std::string &); void createNewWallet(); void needHDWalletDetails(const std::string &walletId); void needWalletsList(UiUtils::WalletsTypes, const std::string &id); @@ -186,11 +185,8 @@ namespace bs { void needCloseBsConnection(); void needStartLogin(const std::string& login); void needCancelLogin(); - void needMatchingLogin(const std::string &mtchLogin, const std::string &bsLogin); void needMatchingLogout(); - void needSetUserId(const std::string&); void needMdConnection(ApplicationSettings::EnvConfiguration); - void setRecommendedFeeRate(float); void needNewAuthAddress(); void needSubmitAuthAddress(const bs::Address&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 9726ecc14..e1ed59070 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -727,7 +727,6 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needArmoryReconnect, this, &QtGuiAdapter::onNeedArmoryReconnect); connect(mainWindow_, &bs::gui::qt::MainWindow::needSigners, this, &QtGuiAdapter::onNeedSigners); connect(mainWindow_, &bs::gui::qt::MainWindow::setSigner, this, &QtGuiAdapter::onSetSigner); - connect(mainWindow_, &bs::gui::qt::MainWindow::bootstrapDataLoaded, this, &QtGuiAdapter::onBootstrapDataLoaded); connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletData, this, &QtGuiAdapter::onNeedWalletData); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); @@ -749,10 +748,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needCloseBsConnection, this, &QtGuiAdapter::onNeedCloseBsConnection); connect(mainWindow_, &bs::gui::qt::MainWindow::needStartLogin, this, &QtGuiAdapter::onNeedStartLogin); connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelLogin, this, &QtGuiAdapter::onNeedCancelLogin); - connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogin, this, &QtGuiAdapter::onNeedMatchingLogin); connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogout, this, &QtGuiAdapter::onNeedMatchingLogout); - connect(mainWindow_, &bs::gui::qt::MainWindow::needSetUserId, this, &QtGuiAdapter::onNeedSetUserId); - connect(mainWindow_, &bs::gui::qt::MainWindow::setRecommendedFeeRate, this, &QtGuiAdapter::onSetRecommendedFeeRate); connect(mainWindow_, &bs::gui::qt::MainWindow::needMdConnection, this, &QtGuiAdapter::onNeedMdConnection); connect(mainWindow_, &bs::gui::qt::MainWindow::needNewAuthAddress, this, &QtGuiAdapter::onNeedNewAuthAddress); connect(mainWindow_, &bs::gui::qt::MainWindow::needSubmitAuthAddress, this, &QtGuiAdapter::onNeedSubmitAuthAddress); @@ -1154,24 +1150,6 @@ void QtGuiAdapter::onNeedCancelLogin() pushFill(env); } -void QtGuiAdapter::onBootstrapDataLoaded(const std::string& bsData) -{ - SettingsMessage msg; - msg.set_load_bootstrap(bsData); - Envelope env{ 0, user_, userSettings_, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); -} - -void QtGuiAdapter::onNeedMatchingLogin(const std::string& mtchLogin, const std::string& bsLogin) -{ - MatchingMessage msg; - auto msgReq = msg.mutable_login(); - msgReq->set_matching_login(mtchLogin); - msgReq->set_terminal_login(bsLogin); - Envelope env{ 0, user_, userMatch_, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); -} - void QtGuiAdapter::onNeedMatchingLogout() { MatchingMessage msg; @@ -1180,22 +1158,6 @@ void QtGuiAdapter::onNeedMatchingLogout() pushFill(env); } -void QtGuiAdapter::onNeedSetUserId(const std::string& userId) -{ - WalletsMessage msg; - msg.set_set_user_id(userId); - Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); -} - -void QtGuiAdapter::onSetRecommendedFeeRate(float fee) -{ - WalletsMessage msg; - msg.set_set_settlement_fee(fee); - Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); -} - void QtGuiAdapter::onNeedMdConnection(ApplicationSettings::EnvConfiguration ec) { MktDataMessage msg; @@ -1711,18 +1673,12 @@ bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) bool QtGuiAdapter::processAuthWallet(const WalletsMessage_WalletData& authWallet) { std::vector authAddresses; - OnChainTrackMessage msg; - auto msgReq = msg.mutable_set_auth_addresses(); - msgReq->set_wallet_id(authWallet.wallet_id()); for (const auto& addr : authWallet.used_addresses()) { - msgReq->add_addresses(addr.address()); try { authAddresses.push_back(bs::Address::fromAddressString(addr.address())); } catch (const std::exception&) {} } - Envelope env{ 0, user_, userTrk_, {}, {}, msg.SerializeAsString(), true }; - pushFill(env); return QMetaObject::invokeMethod(mainWindow_, [this, authAddresses] { mainWindow_->onAuthAddresses(authAddresses, {}); }); diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index f20a65d69..038a8c1ed 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -190,11 +190,7 @@ private slots: void onNeedCloseBsConnection(); void onNeedStartLogin(const std::string& login); void onNeedCancelLogin(); - void onBootstrapDataLoaded(const std::string&); - void onNeedMatchingLogin(const std::string& mtchLogin, const std::string& bsLogin); void onNeedMatchingLogout(); - void onNeedSetUserId(const std::string&); - void onSetRecommendedFeeRate(float); void onNeedMdConnection(ApplicationSettings::EnvConfiguration); void onNeedNewAuthAddress(); void onNeedSubmitAuthAddress(const bs::Address&); @@ -214,8 +210,7 @@ private slots: std::shared_ptr userSettings_, userWallets_; std::shared_ptr userBlockchain_, userSigner_; std::shared_ptr userBS_, userMatch_, userSettl_; - std::shared_ptr userMD_; - std::shared_ptr userTrk_; + std::shared_ptr userMD_, userTrk_; bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; diff --git a/common b/common index 7270b8b19..b5feb3137 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 7270b8b199eb7d848f95d056a50c7fb8f5093e04 +Subproject commit b5feb3137de447e6d2de5fff3c870ab4efbebfa2 From f67dc9a74b8aadeae63f8a2e6974a7837fe53a36 Mon Sep 17 00:00:00 2001 From: Ation Date: Thu, 3 Dec 2020 10:05:22 +0200 Subject: [PATCH 055/146] Group rename --- BlockSettleUILib/Trading/RFQRequestWidget.ui | 23 +------------------ BlockSettleUILib/Trading/WalletShieldBase.cpp | 2 +- common | 2 +- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.ui b/BlockSettleUILib/Trading/RFQRequestWidget.ui index 7081d8c5b..5666035b2 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.ui +++ b/BlockSettleUILib/Trading/RFQRequestWidget.ui @@ -249,28 +249,7 @@ 0 - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - +
diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index 6b324cc2e..4d275ea13 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -155,7 +155,7 @@ WalletShieldBase::ProductType WalletShieldBase::getProductGroup(const QString &p else if (productGroup == QLatin1String("Spot FX")) { return ProductType::SpotFX; } - else if (productGroup == QLatin1String("Futures")) { + else if (productGroup == QLatin1String("1day Deliverable")) { return ProductType::Futures; } #ifndef QT_NO_DEBUG diff --git a/common b/common index 732c930ee..c25cffdcc 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 732c930eeb524895032ca41d578daac96b7c39cb +Subproject commit c25cffdcc8c3e3ae1a66a7019b1d728080276486 From 349790f69221ac04b02d52dc5078ba3b302759e6 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 3 Dec 2020 11:26:56 +0300 Subject: [PATCH 056/146] Compile fixes for GCC --- BlockSettleUILib/AddressListModel.cpp | 2 +- Core/SettingsAdapter.cpp | 4 ++-- GUI/QtWidgets/QtGuiAdapter.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index f3a6159fb..20483f59d 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -565,7 +565,7 @@ QVariant AddressListModel::data(const QModelIndex& index, int role) const return row.transactionCount; case BalanceRole: - return row.balance; + return qint64(row.balance); case Qt::ToolTipRole: if ((index.column() == ColumnComment) && row.isMultiLineComment()) { diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 6f964e9f6..68f94cc7e 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -499,7 +499,7 @@ bool SettingsAdapter::processPutRequest(const SettingsMessage_SettingsResponse & appSettings_->set(setting, req.ui()); break; case SettingType_UInt64: - appSettings_->set(setting, req.ui64()); + appSettings_->set(setting, quint64(req.ui64())); break; case SettingType_Bool: appSettings_->set(setting, req.b()); @@ -808,7 +808,7 @@ QVariant bs::message::fromResponse(const BlockSettle::Terminal::SettingResponse& value = setting.ui(); break; case SettingType_UInt64: - value = setting.ui64(); + value = quint64(setting.ui64()); break; case SettingType_Bool: value = setting.b(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index e1ed59070..883390688 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -977,7 +977,7 @@ void QtGuiAdapter::onNeedLedgerEntries(const std::string &filter) pushFill(env); } -void QtGuiAdapter::onNeedTXDetails(const const std::vector &txWallet +void QtGuiAdapter::onNeedTXDetails(const std::vector &txWallet , bool useCache, const bs::Address &addr) { WalletsMessage msg; From 1f734aadd18e46a20bd6f7a40f76e1ee14c32d06 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 3 Dec 2020 12:13:08 +0300 Subject: [PATCH 057/146] auto sign --- .../Trading/AutoSignQuoteProvider.h | 2 +- Core/SignerAdapter.cpp | 42 +++++++++++++++++++ Core/SignerAdapter.h | 6 +++ common | 2 +- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h index e44acc89e..b30dda6b9 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h @@ -39,7 +39,7 @@ class AutoSignScriptProvider : public QObject { Q_OBJECT public: - explicit AutoSignScriptProvider(const std::shared_ptr & + [[deprecated]] explicit AutoSignScriptProvider(const std::shared_ptr & , UserScriptRunner * , const std::shared_ptr & , const std::shared_ptr & diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index d8cef8151..1f54b4791 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -134,6 +134,8 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env , bs::signer::pbTxRequestToCore(request.resolve_pub_spenders())); case SignerMessage::kSignSettlementTx: return processSignSettlementTx(env, request.sign_settlement_tx()); + case SignerMessage::kAutoSign: + return processAutoSignRequest(env, request.auto_sign()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -297,6 +299,35 @@ void SignerAdapter::newWalletPrompt() pushFill(env); } +void SignerAdapter::autoSignStateChanged(bs::error::ErrorCode code + , const std::string& walletId) +{ + const auto& itAS = autoSignRequests_.find(walletId); + if (itAS == autoSignRequests_.end()) { + logger_->warn("[{}] no request found for {}", __func__, walletId); + return; + } + bool enabled = false; + if (code == bs::error::ErrorCode::NoError) { + enabled = true; + } + else if (code == bs::error::ErrorCode::AutoSignDisabled) { + enabled = false; + } + else { + logger_->error("[{}] auto sign {} error code: {}", __func__, walletId + , (int)code); + } + SignerMessage msg; + auto msgResp = msg.mutable_auto_sign(); + msgResp->set_wallet_id(walletId); + msgResp->set_enable(enabled); + Envelope envResp{ itAS->second.id, user_, itAS->second.sender, {}, {} + , msg.SerializeAsString() }; + pushFill(envResp); + autoSignRequests_.erase(itAS); +} + void SignerAdapter::windowIsVisible(bool flag) { SignerMessage msg; @@ -748,3 +779,14 @@ bool SignerAdapter::processResolvePubSpenders(const bs::message::Envelope& env } return true; } + +bool SignerAdapter::processAutoSignRequest(const bs::message::Envelope& env + , const SignerMessage_AutoSign& request) +{ + autoSignRequests_[request.wallet_id()] = env; + QVariantMap data; + data[QLatin1String("rootId")] = QString::fromStdString(request.wallet_id()); + data[QLatin1String("enable")] = request.enable(); + return (signer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ActivateAutoSign + , data) != 0); +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index c7580b4fe..016bbcddb 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -21,6 +21,7 @@ namespace spdlog { namespace BlockSettle { namespace Common { class SignerMessage; + class SignerMessage_AutoSign; class SignerMessage_ExtendAddrChain; class SignerMessage_GetSettlPayinAddr; class SignerMessage_SetSettlementId; @@ -64,6 +65,8 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget void onReady() override; void walletsReady() override; void newWalletPrompt() override; + void autoSignStateChanged(bs::error::ErrorCode + , const std::string& walletId) override; void windowIsVisible(bool) override; bool processOwnRequest(const bs::message::Envelope & @@ -100,6 +103,8 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget , const BlockSettle::Common::SignerMessage_GetSettlPayinAddr&); bool processResolvePubSpenders(const bs::message::Envelope& , const bs::core::wallet::TXSignRequest&); + bool processAutoSignRequest(const bs::message::Envelope& + , const BlockSettle::Common::SignerMessage_AutoSign&); private: std::shared_ptr logger_; @@ -111,6 +116,7 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget std::string connKey_; std::map> requests_; + std::unordered_map autoSignRequests_; }; diff --git a/common b/common index b5feb3137..1b7e7c133 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit b5feb3137de447e6d2de5fff3c870ab4efbebfa2 +Subproject commit 1b7e7c1338d595287ca1c67c76d3164bbd98306c From ee25c92a7b7378c2d1dbd618638637d94ee63293 Mon Sep 17 00:00:00 2001 From: Ation Date: Thu, 3 Dec 2020 11:59:17 +0200 Subject: [PATCH 058/146] Duiplicate cash settled MD --- BlockSettleUILib/Trading/MarketDataModel.cpp | 4 ++-- BlockSettleUILib/Trading/WalletShieldBase.cpp | 3 +++ common | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index 7c0910747..f490ecb6b 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -167,13 +167,13 @@ void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType, const QStr PriceMap fieldsMap; - if (assetType == bs::network::Asset::Futures) { + if (assetType == bs::network::Asset::Futures || + assetType == bs::network::Asset::CashSettledFutures) { FieldsToMap(bs::network::Asset::SpotXBT, mdFields, fieldsMap); } else { FieldsToMap(assetType, mdFields, fieldsMap); } - auto groupItem = getGroup(assetType); auto childRow = groupItem->findRowWithText(security); const auto timeNow = QDateTime::currentDateTime(); diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index 4d275ea13..70da22453 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -158,6 +158,9 @@ WalletShieldBase::ProductType WalletShieldBase::getProductGroup(const QString &p else if (productGroup == QLatin1String("1day Deliverable")) { return ProductType::Futures; } + else if (productGroup == QLatin1String("1day Cash Settled")) { + return ProductType::CashSettledFutures; + } #ifndef QT_NO_DEBUG // You need to add logic for new Product group type Q_ASSERT(false); diff --git a/common b/common index c25cffdcc..3667ebfec 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit c25cffdcc8c3e3ae1a66a7019b1d728080276486 +Subproject commit 3667ebfec68c0553b6680f0e5ef29a715fe9949a From bf06e8d1a023b2fb4900e7b674defbcfda0bd3c7 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 3 Dec 2020 14:34:05 +0300 Subject: [PATCH 059/146] WIP Futures ticker --- BlockSettleUILib/Trading/FuturesTicket.cpp | 220 +++++ BlockSettleUILib/Trading/FuturesTicket.h | 103 ++ BlockSettleUILib/Trading/FuturesTicket.ui | 878 ++++++++++++++++++ BlockSettleUILib/Trading/RFQRequestWidget.cpp | 26 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 1 + BlockSettleUILib/Trading/RFQRequestWidget.ui | 7 + BlockSettleUILib/UiUtils.cpp | 1 + 7 files changed, 1235 insertions(+), 1 deletion(-) create mode 100644 BlockSettleUILib/Trading/FuturesTicket.cpp create mode 100644 BlockSettleUILib/Trading/FuturesTicket.h create mode 100644 BlockSettleUILib/Trading/FuturesTicket.ui diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp new file mode 100644 index 000000000..ed74ccbf2 --- /dev/null +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -0,0 +1,220 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "FuturesTicket.h" +#include "ui_FuturesTicket.h" + +#include "AssetManager.h" +#include "BSMessageBox.h" +#include "CommonTypes.h" +#include "CurrencyPair.h" +#include "QuoteProvider.h" +#include "UiUtils.h" +#include "XbtAmountValidator.h" + +#include +#include + +#include + +namespace { + const QString kEmptyInformationalLabelText = QStringLiteral("--"); + + QString formatPrice(double price, bs::network::Asset::Type at) { + if (price == 0) { + return kEmptyInformationalLabelText; + } + return UiUtils::displayPriceForAssetType(price, at); + } +} + + +FuturesTicket::FuturesTicket(QWidget* parent) + : QWidget(parent) + , ui_(new Ui::FuturesTicket()) +{ + ui_->setupUi(this); + + invalidBalanceFont_ = ui_->labelBalanceValue->font(); + invalidBalanceFont_.setStrikeOut(true); + + xbtAmountValidator_ = new XbtAmountValidator(this); + + ui_->lineEditAmount->installEventFilter(this); + + connect(ui_->pushButtonSell, &QPushButton::clicked, this, &FuturesTicket::onSellSelected); + connect(ui_->pushButtonBuy, &QPushButton::clicked, this, &FuturesTicket::onBuySelected); + + connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &FuturesTicket::submitButtonClicked); + //connect(ui_->lineEditAmount, &QLineEdit::textEdited, this, &FuturesTicket::onAmountEdited); + + //disablePanel(); +} + +FuturesTicket::~FuturesTicket() = default; + +void FuturesTicket::resetTicket() +{ + ui_->labelProductGroup->setText(kEmptyInformationalLabelText); + ui_->labelSecurityId->setText(kEmptyInformationalLabelText); + ui_->labelIndicativePrice->setText(kEmptyInformationalLabelText); + + currentBidPrice_ = kEmptyInformationalLabelText; + currentOfferPrice_ = kEmptyInformationalLabelText; + + ui_->lineEditAmount->setValidator(nullptr); + ui_->lineEditAmount->setEnabled(false); + ui_->lineEditAmount->clear(); +} + +void FuturesTicket::init(const std::shared_ptr &logger + , const std::shared_ptr &authAddressManager + , const std::shared_ptr& assetManager + , const std::shared_ptr "eProvider) +{ + logger_ = logger; + authAddressManager_ = authAddressManager; + assetManager_ = assetManager; + + updateSubmitButton(); +} + +void FuturesTicket::setType(bs::network::Asset::Type type) +{ + type_ = type; +} + +void FuturesTicket::SetCurrencyPair(const QString& currencyPair) +{ + ui_->labelSecurityId->setText(currencyPair); + + CurrencyPair cp(currencyPair.toStdString()); + + currentProduct_ = QString::fromStdString(cp.NumCurrency()); + contraProduct_ = QString::fromStdString(cp.DenomCurrency()); + security_ = currencyPair; + + ui_->pushButtonNumCcy->setText(currentProduct_); + ui_->pushButtonNumCcy->setChecked(true); + + ui_->pushButtonDenomCcy->setText(contraProduct_); + ui_->pushButtonDenomCcy->setChecked(false); + + ui_->pushButtonDenomCcy->setEnabled(false); +} + +void FuturesTicket::SetProductAndSide(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice, bs::network::Side::Type side) +{ + resetTicket(); + + if (productGroup.isEmpty() || currencyPair.isEmpty()) { + return; + } + + //SetProductGroup(productGroup); + SetCurrencyPair(currencyPair); + //SetCurrentIndicativePrices(bidPrice, offerPrice); + + if (side == bs::network::Side::Type::Undefined) { + side = bs::network::Side::Buy; + } + + ui_->pushButtonSell->setChecked(side == bs::network::Side::Sell); + ui_->pushButtonBuy->setChecked(side == bs::network::Side::Buy); + + productSelectionChanged(); +} + +void FuturesTicket::setSecurityId(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice) +{ + SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Undefined); +} + +void FuturesTicket::setSecurityBuy(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice) +{ + SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Buy); +} + +void FuturesTicket::setSecuritySell(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice) +{ + SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Sell); +} + +bs::network::Side::Type FuturesTicket::getSelectedSide() const +{ + if (ui_->pushButtonSell->isChecked()) { + return bs::network::Side::Sell; + } + + return bs::network::Side::Buy; +} + +QString FuturesTicket::getProduct() const +{ + return currentProduct_; +} + +void FuturesTicket::productSelectionChanged() +{ + const auto &mdInfo = mdInfo_[type_][security_.toStdString()]; + + const auto bidPrice = formatPrice(mdInfo.bidPrice, type_); + const auto askPrice = formatPrice(mdInfo.askPrice, type_); + + ui_->labelBid->setText(bidPrice); + ui_->labelAsk->setText(askPrice); +} + +void FuturesTicket::updateSubmitButton() +{ +} + +bool FuturesTicket::eventFilter(QObject *watched, QEvent *evt) +{ + if (evt->type() == QEvent::KeyPress) { + auto keyID = static_cast(evt)->key(); + if (ui_->pushButtonSubmit->isEnabled() && ((keyID == Qt::Key_Return) || (keyID == Qt::Key_Enter))) { + submitButtonClicked(); + } + } + return QWidget::eventFilter(watched, evt); +} + +void FuturesTicket::submitButtonClicked() +{ +} + +void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) +{ + auto &mdInfo = mdInfo_[type][security.toStdString()]; + mdInfo.merge(bs::network::MDField::get(mdFields)); + + if (type == type_) { + productSelectionChanged(); + } +} + +void FuturesTicket::onSellSelected() +{ + ui_->pushButtonSell->setChecked(true); + ui_->pushButtonBuy->setChecked(false); + productSelectionChanged(); +} + +void FuturesTicket::onBuySelected() +{ + ui_->pushButtonSell->setChecked(false); + ui_->pushButtonBuy->setChecked(true); + productSelectionChanged(); +} diff --git a/BlockSettleUILib/Trading/FuturesTicket.h b/BlockSettleUILib/Trading/FuturesTicket.h new file mode 100644 index 000000000..85313a92e --- /dev/null +++ b/BlockSettleUILib/Trading/FuturesTicket.h @@ -0,0 +1,103 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef FUTURES_TICKET_H +#define FUTURES_TICKET_H + +#include +#include + +#include +#include +#include +#include + +#include "BSErrorCode.h" +#include "CommonTypes.h" +#include "UtxoReservationToken.h" +#include "XBTAmount.h" +#include "UiUtils.h" + +namespace spdlog { + class logger; +} +namespace Ui { + class FuturesTicket; +} +class AssetManager; +class QuoteProvider; +class XbtAmountValidator; + + +class FuturesTicket : public QWidget +{ +Q_OBJECT + +public: + FuturesTicket(QWidget* parent = nullptr); + ~FuturesTicket() override; + + void init(const std::shared_ptr &logger + , const std::shared_ptr & + , const std::shared_ptr &assetManager + , const std::shared_ptr "eProvider); + +public slots: + void setType(bs::network::Asset::Type type); + void SetProductAndSide(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice, bs::network::Side::Type side); + void setSecurityId(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice); + void setSecurityBuy(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice); + void setSecuritySell(const QString& productGroup, const QString& currencyPair + , const QString& bidPrice, const QString& offerPrice); + void SetCurrencyPair(const QString& currencyPair); + + void onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields); + +private slots: + void onSellSelected(); + void onBuySelected(); + +private: + bool eventFilter(QObject *watched, QEvent *evt) override; + + bs::network::Side::Type getSelectedSide() const; + QString getProduct() const; + + void resetTicket(); + void productSelectionChanged(); + void updateSubmitButton(); + void updateBalances(); + void submitButtonClicked(); + + std::unique_ptr ui_; + + std::shared_ptr logger_; + std::shared_ptr assetManager_; + std::shared_ptr authAddressManager_; + + QFont invalidBalanceFont_; + + XbtAmountValidator *xbtAmountValidator_{}; + + bs::network::Asset::Type type_{}; + QString currentProduct_; + QString contraProduct_; + QString security_; + + QString currentBidPrice_; + QString currentOfferPrice_; + + std::map> mdInfo_; +}; + +#endif // FUTURES_TICKET_H diff --git a/BlockSettleUILib/Trading/FuturesTicket.ui b/BlockSettleUILib/Trading/FuturesTicket.ui new file mode 100644 index 000000000..4e408f46f --- /dev/null +++ b/BlockSettleUILib/Trading/FuturesTicket.ui @@ -0,0 +1,878 @@ + + + FuturesTicket + + + + 0 + 0 + 460 + 757 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Form + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 25 + + + + true + + + + 5 + + + 5 + + + 0 + + + 0 + + + 0 + + + + + QUOTE REQUEST + + + 0 + + + true + + + + + + + + + + + 10 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + GENERAL + + + true + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 5 + + + 5 + + + 5 + + + 5 + + + 0 + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 100 + 16777215 + + + + Product Group + + + + + + + -- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + 0 + 0 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 100 + 16777215 + + + + Security ID + + + + + + + font-weight: bold; + + + -- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + 0 + 0 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 100 + 16777215 + + + + Indicative Price + + + + + + + -- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + + bid + + + + + + + ask + + + + + + + + + + 0 + 0 + + + + + 5 + + + 5 + + + 0 + + + 5 + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Product + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 60 + 30 + + + + + 60 + 30 + + + + --- + + + true + + + true + + + + + + + + 60 + 30 + + + + + 60 + 30 + + + + --- + + + true + + + true + + + + + + + + + + + + + + 0 + 0 + + + + + 3 + + + 1 + + + 0 + + + 0 + + + 0 + + + + + Side + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 60 + 30 + + + + + 60 + 30 + + + + Buy + + + true + + + true + + + + + + + + 60 + 30 + + + + + 60 + 30 + + + + Sell + + + true + + + true + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Quantity + + + + + + + + 0 + 0 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 21 + + + + + 16777215 + 21 + + + + + + + + + + + + 100 + 21 + + + + + 100 + 21 + + + + &Max + + + + + + + + + + + + + + + + + 16777215 + 1 + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 3 + + + 10 + + + 0 + + + 10 + + + 10 + + + + + Balance + + + + + + + font-weight: bold; + + + xxx + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + + + + 0 + 0 + + + + help text + + + true + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + 0 + 0 + + + + true + + + + 5 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 35 + + + + + + + SUBMIT QUOTE REQUEST + + + + + + + + + + + diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index e0076cff8..1057b2ac9 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -40,7 +40,8 @@ namespace { enum class RFQPages : int { ShieldPage = 0, - EditableRFQPage + EditableRFQPage, + Futures, }; } @@ -187,6 +188,13 @@ void RFQRequestWidget::showEditableRFQPage() ui_->stackedWidgetRFQ->setCurrentIndex(static_cast(RFQPages::EditableRFQPage)); } +void RFQRequestWidget::showFuturesPage(bs::network::Asset::Type type) +{ + ui_->stackedWidgetRFQ->setEnabled(true); + ui_->stackedWidgetRFQ->setCurrentIndex(static_cast(RFQPages::Futures)); + ui_->pageFutures->setType(type); +} + void RFQRequestWidget::popShield() { ui_->stackedWidgetRFQ->setEnabled(true); @@ -205,6 +213,7 @@ void RFQRequestWidget::initWidgets(const std::shared_ptr& md , mdProvider, mdCallbacks); connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, ui_->pageRFQTicket, &RFQTicketXBT::onMDUpdate); + connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, ui_->pageFutures, &FuturesTicket::onMDUpdate); } void RFQRequestWidget::init(const std::shared_ptr &logger @@ -237,6 +246,7 @@ void RFQRequestWidget::init(const std::shared_ptr &logger ui_->pageRFQTicket->init(logger, authAddressManager, assetManager, quoteProvider, container, armory, utxoReservationManager); + ui_->pageFutures->init(logger, authAddressManager, assetManager, quoteProvider); ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui_->treeViewOrders->setModel(orderListModel); @@ -337,6 +347,9 @@ void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ ui_->pageRFQTicket->SetProductAndSide(currentInfo.productGroup_ , currentInfo.currencyPair_, currentInfo.bidPrice_, currentInfo.offerPrice_ , bs::network::Side::Undefined); + ui_->pageFutures->SetProductAndSide(currentInfo.productGroup_ + , currentInfo.currencyPair_, currentInfo.bidPrice_, currentInfo.offerPrice_ + , bs::network::Side::Undefined); std::vector closedDialogs; for (const auto &dlg : dialogs_) { @@ -375,6 +388,11 @@ bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) using GroupType = RFQShieldPage::ProductType; const GroupType group = RFQShieldPage::getProductGroup(selectedInfo.productGroup_); + if (group == GroupType::Futures || group == GroupType::CashSettledFutures) { + showFuturesPage(group); + return true; + } + switch (userType) { case UserType::Market: { if (group == GroupType::SpotFX || group == GroupType::SpotXBT) { @@ -436,6 +454,8 @@ void RFQRequestWidget::onCurrencySelected(const MarketSelectedInfo& selectedInfo } ui_->pageRFQTicket->setSecurityId(selectedInfo.productGroup_ , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); + ui_->pageFutures->setSecurityId(selectedInfo.productGroup_ + , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); } void RFQRequestWidget::onBidClicked(const MarketSelectedInfo& selectedInfo) @@ -445,6 +465,8 @@ void RFQRequestWidget::onBidClicked(const MarketSelectedInfo& selectedInfo) } ui_->pageRFQTicket->setSecuritySell(selectedInfo.productGroup_ , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); + ui_->pageFutures->setSecuritySell(selectedInfo.productGroup_ + , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); } void RFQRequestWidget::onAskClicked(const MarketSelectedInfo& selectedInfo) @@ -454,6 +476,8 @@ void RFQRequestWidget::onAskClicked(const MarketSelectedInfo& selectedInfo) } ui_->pageRFQTicket->setSecurityBuy(selectedInfo.productGroup_ , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); + ui_->pageFutures->setSecurityBuy(selectedInfo.productGroup_ + , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); } void RFQRequestWidget::onDisableSelectedInfo() diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index fcd1e5e5b..535bbf23d 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -110,6 +110,7 @@ Q_OBJECT private: void showEditableRFQPage(); + void showFuturesPage(bs::network::Asset::Type type); void popShield(); bool checkConditions(const MarketSelectedInfo& productGroup); diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.ui b/BlockSettleUILib/Trading/RFQRequestWidget.ui index 5666035b2..87007cb90 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.ui +++ b/BlockSettleUILib/Trading/RFQRequestWidget.ui @@ -250,6 +250,7 @@ + @@ -281,6 +282,12 @@
RFQTicketXBT.h
1 + + FuturesTicket + QWidget +
FuturesTicket.h
+ 1 +
diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index a0ba6c7bb..c067c6e22 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -441,6 +441,7 @@ QString UiUtils::displayPriceForAssetType(double price, bs::network::Asset::Type return UiUtils::displayPriceFX(price); case bs::network::Asset::SpotXBT: case bs::network::Asset::Futures: + case bs::network::Asset::CashSettledFutures: return UiUtils::displayPriceXBT(price); case bs::network::Asset::PrivateMarket: return UiUtils::displayPriceCC(price); From d978245396e38cd6c709654f754bb657204ac798 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 3 Dec 2020 17:49:36 +0300 Subject: [PATCH 060/146] Fix RFQRequestWidget::checkConditions Show new ticker for GroupType::CashSettledFutures only --- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 1057b2ac9..7f469e138 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -388,7 +388,7 @@ bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) using GroupType = RFQShieldPage::ProductType; const GroupType group = RFQShieldPage::getProductGroup(selectedInfo.productGroup_); - if (group == GroupType::Futures || group == GroupType::CashSettledFutures) { + if (group == GroupType::CashSettledFutures) { showFuturesPage(group); return true; } From f6ccca6e7cad0b529e1ff56da51c6dc8f9e8a781 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 3 Dec 2020 18:44:28 +0300 Subject: [PATCH 061/146] Send future request to proxy --- BlockSettleUILib/BSTerminalMainWindow.cpp | 2 + BlockSettleUILib/Trading/FuturesTicket.cpp | 39 ++++++++++++++++++- BlockSettleUILib/Trading/FuturesTicket.h | 4 ++ BlockSettleUILib/Trading/RFQRequestWidget.cpp | 2 + BlockSettleUILib/Trading/RFQRequestWidget.h | 2 + 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 36572d59b..ca7a97c79 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2242,6 +2242,8 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli connect(ui_->widgetRFQ, &RFQRequestWidget::cancelXBTTrade, bsClient_.get(), &BsClient::sendCancelOnXBTTrade); connect(ui_->widgetRFQ, &RFQRequestWidget::cancelCCTrade, bsClient_.get(), &BsClient::sendCancelOnCCTrade); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendFutureRequestToPB, bsClient_.get(), &BsClient::sendFutureRequest); + // connect to quote dialog connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQReply, &RFQReplyWidget::onMessageFromPB); connect(ui_->widgetRFQReply, &RFQReplyWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClient::sendUnsignedPayin); diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index ed74ccbf2..fb056dcfb 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -69,8 +69,7 @@ void FuturesTicket::resetTicket() currentBidPrice_ = kEmptyInformationalLabelText; currentOfferPrice_ = kEmptyInformationalLabelText; - ui_->lineEditAmount->setValidator(nullptr); - ui_->lineEditAmount->setEnabled(false); + ui_->lineEditAmount->setValidator(xbtAmountValidator_); ui_->lineEditAmount->clear(); } @@ -165,6 +164,15 @@ QString FuturesTicket::getProduct() const return currentProduct_; } +double FuturesTicket::getQuantity() const +{ + const CustomDoubleValidator *validator = dynamic_cast(ui_->lineEditAmount->validator()); + if (validator == nullptr) { + return 0; + } + return validator->GetValue(ui_->lineEditAmount->text()); +} + void FuturesTicket::productSelectionChanged() { const auto &mdInfo = mdInfo_[type_][security_.toStdString()]; @@ -193,6 +201,33 @@ bool FuturesTicket::eventFilter(QObject *watched, QEvent *evt) void FuturesTicket::submitButtonClicked() { + const auto &mdInfo = mdInfo_[type_][security_.toStdString()]; + auto side = getSelectedSide(); + double price = 0; + switch (side) { + case bs::network::Side::Buy: + price = mdInfo.askPrice; + break; + case bs::network::Side::Sell: + price = mdInfo.bidPrice; + break; + default: + break; + } + double amount = getQuantity(); + + if (price == 0 || amount == 0) { + return; + } + + bs::network::FutureRequest request; + request.side = side; + request.price = price; + request.amount = bs::XBTAmount(amount); + + emit sendFutureRequestToPB(request); + + ui_->lineEditAmount->clear(); } void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) diff --git a/BlockSettleUILib/Trading/FuturesTicket.h b/BlockSettleUILib/Trading/FuturesTicket.h index 85313a92e..7a4e3c266 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.h +++ b/BlockSettleUILib/Trading/FuturesTicket.h @@ -49,6 +49,9 @@ Q_OBJECT , const std::shared_ptr &assetManager , const std::shared_ptr "eProvider); +signals: + void sendFutureRequestToPB(const bs::network::FutureRequest &details); + public slots: void setType(bs::network::Asset::Type type); void SetProductAndSide(const QString& productGroup, const QString& currencyPair @@ -72,6 +75,7 @@ private slots: bs::network::Side::Type getSelectedSide() const; QString getProduct() const; + double getQuantity() const; void resetTicket(); void productSelectionChanged(); diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 7f469e138..9ba4f2674 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -273,6 +273,8 @@ void RFQRequestWidget::init(const std::shared_ptr &logger ui_->pageRFQTicket->disablePanel(); connect(authAddressManager_.get(), &AuthAddressManager::AddressListUpdated, this, &RFQRequestWidget::forceCheckCondition); + + connect(ui_->pageFutures, &FuturesTicket::sendFutureRequestToPB, this, &RFQRequestWidget::sendFutureRequestToPB); } void RFQRequestWidget::onConnectedToCeler() diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 535bbf23d..8e73dcc75 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -98,6 +98,8 @@ Q_OBJECT void sendSignedPayinToPB(const std::string& settlementId, const BinaryData& signedPayin); void sendSignedPayoutToPB(const std::string& settlementId, const BinaryData& signedPayout); + void sendFutureRequestToPB(const bs::network::FutureRequest &details); + void cancelXBTTrade(const std::string& settlementId); void cancelCCTrade(const std::string& orderId); From e56c6c692ee48e291ce35fad9900339df4621b90 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 4 Dec 2020 15:28:19 +0300 Subject: [PATCH 062/146] CacheFile refactor (use only STL) --- UnitTests/TestEnv.cpp | 1 + common | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index 7224529d7..7484065a5 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/common b/common index 242104418..e9c0af11f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 242104418e307d6dbec50e1257093f3018a32cce +Subproject commit e9c0af11fb8b6bc07bdca29bb673cc5e7bc09263 From c23ab25d95e5805179a20bce2b37fd649f2aa64a Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 8 Dec 2020 00:05:42 +0300 Subject: [PATCH 063/146] FX settlement tests --- Core/SettingsAdapter.cpp | 3 +- Core/SettingsAdapter.h | 2 +- Core/SettlementAdapter.cpp | 3 +- Core/SignerAdapter.cpp | 11 +- Core/SignerAdapter.h | 3 +- Core/TerminalMessage.cpp | 1 + UnitTests/CMakeLists.txt | 4 +- UnitTests/MockTerminal.cpp | 227 ++++++++++++++++++ UnitTests/MockTerminal.h | 67 ++++++ UnitTests/TestAdapters.cpp | 323 +++++++++++++++++++++++++ UnitTests/TestAdapters.h | 94 ++++++++ UnitTests/TestEnv.cpp | 6 +- UnitTests/TestEnv.h | 18 +- UnitTests/TestSettlement.cpp | 448 +++++++++++++++++++++++++++++------ UnitTests/TestSettlement.h | 18 +- common | 2 +- 16 files changed, 1116 insertions(+), 114 deletions(-) create mode 100644 UnitTests/MockTerminal.cpp create mode 100644 UnitTests/MockTerminal.h create mode 100644 UnitTests/TestAdapters.cpp create mode 100644 UnitTests/TestAdapters.h diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 68f94cc7e..9206bfffa 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -88,7 +88,6 @@ SettingsAdapter::SettingsAdapter(const std::shared_ptr &set , const QStringList &appArgs) : appSettings_(settings) , user_(std::make_shared(TerminalUsers::Settings)) - , userBC_(std::make_shared(TerminalUsers::BROADCAST)) { if (!appSettings_->LoadApplicationSettings(appArgs)) { throw std::runtime_error("failed to load settings"); @@ -545,7 +544,7 @@ bool SettingsAdapter::processPutRequest(const SettingsMessage_SettingsResponse & if (nbUpdates) { SettingsMessage msg; *(msg.mutable_settings_updated()) = request; - bs::message::Envelope env{ 0, user_, userBC_, bs::message::TimeStamp{} + bs::message::Envelope env{ 0, user_, nullptr, bs::message::TimeStamp{} , bs::message::TimeStamp{}, msg.SerializeAsString() }; return pushFill(env); } diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index 36f7f5102..9d188636b 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -100,7 +100,7 @@ class SettingsAdapter : public bs::message::Adapter bool processApiClientsList(const bs::message::Envelope&); private: - std::shared_ptr user_, userBC_; + std::shared_ptr user_; std::shared_ptr logMgr_; std::shared_ptr logger_; std::shared_ptr appSettings_; diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index f9dee4ed1..c389257ca 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -534,7 +534,8 @@ bool SettlementAdapter::processSendRFQ(const bs::message::Envelope& env const auto& rfq = fromMsg(request.rfq()); const auto &settlement = std::make_shared(Settlement{ env , false, rfq, request.reserve_id() }); - if (rfq.side == bs::network::Side::Buy) { + if ((rfq.assetType != bs::network::Asset::SpotFX) && + (rfq.side == bs::network::Side::Buy)) { settlement->recvAddress = bs::Address::fromAddressString(rfq.receiptAddress); } settlByRfqId_[rfq.requestId] = settlement; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 1f54b4791..0f85bf531 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -24,8 +24,9 @@ using namespace BlockSettle::Terminal; using namespace bs::message; -SignerAdapter::SignerAdapter(const std::shared_ptr &logger) - : logger_(logger) +SignerAdapter::SignerAdapter(const std::shared_ptr &logger + , const std::shared_ptr& signer) + : logger_(logger), signer_(signer) , user_(std::make_shared(bs::message::TerminalUsers::Signer)) {} @@ -85,6 +86,12 @@ bool SignerAdapter::process(const bs::message::Envelope &env) void SignerAdapter::start() { + if (signer_) { + sendComponentLoading(); + onReady(); + walletsReady(); + return; + } SettingsMessage msg; msg.mutable_signer_request(); bs::message::Envelope env{ 0, user_, UserTerminal::create(TerminalUsers::Settings) diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 016bbcddb..6230ef882 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -42,7 +42,8 @@ class WalletSignerContainer; class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget { public: - SignerAdapter(const std::shared_ptr &); + SignerAdapter(const std::shared_ptr & + , const std::shared_ptr &signer = nullptr); ~SignerAdapter() override = default; bool process(const bs::message::Envelope &) override; diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index 3c752dc85..c2820429c 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -19,6 +19,7 @@ using namespace BlockSettle::Terminal; static const std::map kTerminalUsersMapping = { { static_cast(TerminalUsers::BROADCAST), "Broadcast" }, + { static_cast(TerminalUsers::System), "System " }, { static_cast(TerminalUsers::Signer), "Signer " }, { static_cast(TerminalUsers::API), "API " }, { static_cast(TerminalUsers::Settings), "Settings" }, diff --git a/UnitTests/CMakeLists.txt b/UnitTests/CMakeLists.txt index 0d5a2eb3e..b95a0ecc0 100644 --- a/UnitTests/CMakeLists.txt +++ b/UnitTests/CMakeLists.txt @@ -50,10 +50,9 @@ INCLUDE_DIRECTORIES( ${BS_NETWORK_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${COMMON_LIB_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${CRYPTO_LIB_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${WALLET_LIB_INCLUDE_DIR} ) - INCLUDE_DIRECTORIES( ${BS_COMMUNICATION_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${PATH_TO_GENERATED} ) - +INCLUDE_DIRECTORIES( ${BLOCK_SETTLE_ROOT}/Core ) INCLUDE_DIRECTORIES( ${NETTY_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${BS_COMMON_ENUMS_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( ${BS_TERMINAL_API_INCLUDE_DIR} ) @@ -70,6 +69,7 @@ TARGET_COMPILE_DEFINITIONS( ${UNIT_TESTS} PRIVATE ) TARGET_LINK_LIBRARIES( ${UNIT_TESTS} + ${TERMINAL_CORE_NAME} ${BLOCKSETTLE_UI_LIBRARY_NAME} ${CPP_WALLET_LIB_NAME} ${BS_NETWORK_LIB_NAME} diff --git a/UnitTests/MockTerminal.cpp b/UnitTests/MockTerminal.cpp new file mode 100644 index 000000000..da3028e58 --- /dev/null +++ b/UnitTests/MockTerminal.cpp @@ -0,0 +1,227 @@ +/* + +*********************************************************************************** +* Copyright (C) 2019 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "MockTerminal.h" +#include "Adapters/BlockchainAdapter.h" +#include "Adapters/WalletsAdapter.h" +#include "ArmorySettings.h" +#include "Message/Adapter.h" +#include "SettingsAdapter.h" +#include "SettlementAdapter.h" +#include "SignerAdapter.h" +#include "TerminalMessage.h" +#include "TestEnv.h" + +#include "common.pb.h" +#include "terminal.pb.h" + +using namespace BlockSettle::Common; +using namespace BlockSettle::Terminal; +using namespace bs::message; + +MockTerminalBus::MockTerminalBus(const std::shared_ptr& logger + , const std::string &name) + : logger_(logger) +{ + queue_ = std::make_shared(std::make_shared(logger) + , logger, name); +} + +MockTerminalBus::~MockTerminalBus() +{ + shutdown(); +} + +void MockTerminalBus::addAdapter(const std::shared_ptr& adapter) +{ + queue_->bindAdapter(adapter); + adapter->setQueue(queue_); + + static const auto& adminUser = UserTerminal::create(TerminalUsers::System); + for (const auto& user : adapter->supportedReceivers()) { + AdministrativeMessage msg; + msg.set_component_created(user->value()); + bs::message::Envelope env{ 0, adminUser, {}, {}, {}, msg.SerializeAsString() }; + queue_->pushFill(env); + } +} + +void MockTerminalBus::start() +{ + static const auto& adminUser = UserTerminal::create(TerminalUsers::System); + AdministrativeMessage msg; + msg.mutable_start(); + bs::message::Envelope env{ 0, adminUser, {}, {}, {}, msg.SerializeAsString() }; + queue_->pushFill(env); +} + +void MockTerminalBus::shutdown() +{ + queue_->terminate(); +} + +class SettingsMockAdapter : public bs::message::Adapter +{ +public: + SettingsMockAdapter(const std::shared_ptr& logger) + : logger_(logger) + , user_(std::make_shared(TerminalUsers::Settings)) + {} + ~SettingsMockAdapter() override = default; + + bool process(const bs::message::Envelope& env) override + { + if (env.receiver && (env.receiver->value() == TerminalUsers::Settings)) { + SettingsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case SettingsMessage::kGetRequest: + return processGetRequest(env, msg.get_request()); + case SettingsMessage::kPutRequest: break; + case SettingsMessage::kArmoryServer: break; + case SettingsMessage::kSetArmoryServer: break; + case SettingsMessage::kArmoryServersGet: break; + case SettingsMessage::kAddArmoryServer: break; + case SettingsMessage::kDelArmoryServer: break; + case SettingsMessage::kUpdArmoryServer: break; + case SettingsMessage::kSignerRequest: break; + case SettingsMessage::kSignerSetKey: break; + case SettingsMessage::kSignerReset: break; + case SettingsMessage::kSignerServersGet: break; + case SettingsMessage::kSetSignerServer: break; + case SettingsMessage::kAddSignerServer: break; + case SettingsMessage::kDelSignerServer: break; + case SettingsMessage::kStateGet: break; + case SettingsMessage::kReset: break; + case SettingsMessage::kResetToState: break; + case SettingsMessage::kLoadBootstrap: break; + case SettingsMessage::kGetBootstrap: break; + case SettingsMessage::kApiPrivkey: break; + case SettingsMessage::kApiClientKeys: break; + default: break; + } + } else if (env.sender->value() == TerminalUsers::Blockchain) { + ArmoryMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse armory msg #{}", __func__, env.id); + return true; + } + if (msg.data_case() == ArmoryMessage::kSettingsRequest) { + ArmoryMessage msgReply; + auto msgResp = msgReply.mutable_settings_response(); + ArmorySettings armorySettings; + armorySettings.name = QLatin1Literal("test"); + armorySettings.netType = NetworkType::TestNet; + armorySettings.armoryDBIp = QLatin1String("127.0.0.1"); + armorySettings.armoryDBPort = 82; + msgResp->set_socket_type(armorySettings.socketType); + msgResp->set_network_type((int)armorySettings.netType); + msgResp->set_host(armorySettings.armoryDBIp.toStdString()); + msgResp->set_port(std::to_string(armorySettings.armoryDBPort)); + msgResp->set_bip15x_key(armorySettings.armoryDBKey.toStdString()); + msgResp->set_run_locally(armorySettings.runLocally); + msgResp->set_data_dir(armorySettings.dataDir.toStdString()); + msgResp->set_executable_path(armorySettings.armoryExecutablePath.toStdString()); + msgResp->set_bitcoin_dir(armorySettings.bitcoinBlocksDir.toStdString()); + msgResp->set_db_dir(armorySettings.dbDir.toStdString()); + Envelope envResp{ env.id, user_, env.sender, {}, {}, msgReply.SerializeAsString() }; + pushFill(envResp); + } + } + return true; + } + + bs::message::Adapter::Users supportedReceivers() const override { + return { user_ }; + } + std::string name() const override { return "Settings"; } + +// std::shared_ptr createOnChainPlug() const; + +private: + bool processGetRequest(const bs::message::Envelope& env + , const BlockSettle::Terminal::SettingsMessage_SettingsRequest& request) + { + SettingsMessage msg; + auto msgResp = msg.mutable_get_response(); + for (const auto& req : request.requests()) { + if (req.source() == SettingSource_Local) { + switch (req.index()) { + case SetIdx_Initialized: { + auto resp = msgResp->add_responses(); + resp->set_allocated_request(new SettingRequest(req)); + resp->set_b(true); + } + break; + default: break; + } + } + } + if (msgResp->responses_size()) { + bs::message::Envelope envResp{ env.id, user_, env.sender, {}, {} + , msg.SerializeAsString() }; + return pushFill(envResp); + } + return true; + } + +private: + std::shared_ptr logger_; + std::shared_ptr user_; +}; + + +class ApiMockAdapter : public bs::message::Adapter +{ +public: + ApiMockAdapter() {} + ~ApiMockAdapter() override = default; + + bool process(const bs::message::Envelope& env) override { return true; } + bs::message::Adapter::Users supportedReceivers() const override { + return { UserTerminal::create(TerminalUsers::API) }; + } + std::string name() const override { return "MockAPI"; } +}; + + +MockTerminal::MockTerminal(const std::shared_ptr& logger + , const std::string &name, const std::shared_ptr& signer + , const std::shared_ptr& armory) + : logger_(logger), name_(name) +{ + bus_ = std::make_shared(logger, name); + const auto& userBlockchain = UserTerminal::create(TerminalUsers::Blockchain); + const auto& userWallets = UserTerminal::create(TerminalUsers::Wallets); + const auto& signAdapter = std::make_shared(logger, signer); + bus_->addAdapter(std::make_shared()); + bus_->addAdapter(std::make_shared(logger)); + bus_->addAdapter(signAdapter); + //TODO: add TrackerMockAdapter + //TODO: add BsMockAdapter + bus_->addAdapter(std::make_shared(logger_ + , userWallets, signAdapter->createClient(), userBlockchain)); + bus_->addAdapter(std::make_shared(logger)); + bus_->addAdapter(std::make_shared(logger, userBlockchain + , armory)); +} + +void MockTerminal::start() +{ + bus_->start(); +} + +void MockTerminal::stop() +{ + bus_->shutdown(); +} diff --git a/UnitTests/MockTerminal.h b/UnitTests/MockTerminal.h new file mode 100644 index 000000000..f76a4f65e --- /dev/null +++ b/UnitTests/MockTerminal.h @@ -0,0 +1,67 @@ +/* + +*********************************************************************************** +* Copyright (C) 2019 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef __MOCK_TERMINAL_H__ +#define __MOCK_TERMINAL_H__ + +#include +#include +#include "Message/Bus.h" + +namespace spdlog { + class logger; +} +namespace bs { + namespace sync { + } +} +class MockTerminal; +class TestArmoryConnection; +class WalletSignerContainer; + +class MockTerminalBus : public bs::message::Bus +{ + friend class MockTerminal; +public: + MockTerminalBus(const std::shared_ptr& + , const std::string &name); + ~MockTerminalBus() override; + + void addAdapter(const std::shared_ptr&) override; + +private: + void start(); + void shutdown(); + +private: + std::shared_ptr logger_; + std::shared_ptr queue_; +}; + +class MockTerminal +{ +public: + MockTerminal(const std::shared_ptr& logger + , const std::string &name, const std::shared_ptr & + , const std::shared_ptr &); + + std::shared_ptr bus() const { return bus_; } + std::string name() const { return name_; } + + void start(); + void stop(); + +private: + std::shared_ptr logger_; + std::shared_ptr bus_; + std::string name_; +}; + +#endif // __MOCK_TERMINAL_H__ diff --git a/UnitTests/TestAdapters.cpp b/UnitTests/TestAdapters.cpp new file mode 100644 index 000000000..69b7c2640 --- /dev/null +++ b/UnitTests/TestAdapters.cpp @@ -0,0 +1,323 @@ +#include "TestAdapters.h" +#include +#include "MessageUtils.h" +#include "TestEnv.h" +#include "terminal.pb.h" + +//#define MSG_DEBUGGING // comment this out if msg id debug output is not needed + +using namespace bs::message; +using namespace BlockSettle::Terminal; + +constexpr auto kExpirationTimeout = std::chrono::seconds{ 5 }; + +bool TestSupervisor::process(const Envelope &env) +{ +#ifdef MSG_DEBUGGING + StaticLogger::loggerPtr->debug("[{}] {}{}: {}({}) -> {}({}), {} bytes" + , name(), env.request ? '/' : '\\', env.id, env.sender->name() + , env.sender->value(), env.receiver ? env.receiver->name() : "null" + , env.receiver ? env.receiver->value() : -1, env.message.size()); +#endif //MSG_DEBUGGING + { + std::unique_lock lock(mtxWait_); + std::vector filtersToDelete; + for (const auto &filter : filterMultWait_) { + if (filter.second.first(env)) { + const auto seqNo = filter.first; + auto &filtPair = filterMap_[seqNo]; + filtPair.second.push_back(env); + if (filtPair.second.size() >= filtPair.first) { + filter.second.second->set_value(filtPair.second); + filtersToDelete.push_back(seqNo); + break; + } + return false; + } + } + for (const auto &filter : filterWait_) { + if (filter.second.first(env)) { + const auto seqNo = filter.first; + filter.second.second->set_value(env); + filtersToDelete.push_back(seqNo); + break; + } + } + if (!filtersToDelete.empty()) { + for (const auto &seqNo : filtersToDelete) { + filterMultWait_.erase(seqNo); + filterMap_.erase(seqNo); + filterWait_.erase(seqNo); + } + return false; + } + } + return true; +} + +uint64_t TestSupervisor::send(bs::message::TerminalUsers sender, bs::message::TerminalUsers receiver + , const std::string &message, bool request) +{ + Envelope env{ 0, UserTerminal::create(sender), UserTerminal::create(receiver) + , {}, {}, message, request }; + pushFill(env); + return env.id; +} + +std::future> TestSupervisor::waitFor(const FilterCb &cb + , size_t count, uint32_t *seqNoExt) +{ + const auto seqNo = ++seqNo_; + if (seqNoExt != nullptr) { + *seqNoExt = seqNo; + } + auto promWait = std::make_shared>>(); + std::unique_lock lock(mtxWait_); + filterMultWait_[seqNo] = { cb, promWait }; + filterMap_[seqNo] = { count, {} }; + return promWait->get_future(); +} + +std::future TestSupervisor::waitFor(const FilterCb &cb + , uint32_t *seqNoExt) +{ + const auto seqNo = ++seqNo_; + if (seqNoExt != nullptr) { + *seqNoExt = seqNo; + } + auto promWait = std::make_shared>(); + std::unique_lock lock(mtxWait_); + filterWait_[seqNo] = { cb, promWait }; + return promWait->get_future(); +} + +void TestSupervisor::unwaitFor(const uint32_t seqNo) +{ + std::unique_lock lock(mtxWait_); + filterMultWait_.erase(seqNo); + filterMap_.erase(seqNo); + filterWait_.erase(seqNo); +} + + +MatchingMock::MatchingMock(const std::shared_ptr& logger + , const std::string& name, const std::string &email) + : logger_(logger), name_(name), email_(email) + , user_(UserTerminal::create(TerminalUsers::Matching)) + , userSettl_(UserTerminal::create(TerminalUsers::Settlement)) +{} + +bool MatchingMock::process(const bs::message::Envelope& env) +{ + if (env.receiver && env.request && (env.receiver->value() == user_->value())) { + MatchingMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse own request #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case MatchingMessage::kSendRfq: + if (siblings_.empty()) { + logger_->warn("[{}] {}: no one to notify", __func__, name()); + //TODO: send result back to requester + } + else { + matches_[msg.send_rfq().id()] = {}; + for (const auto& sibling : siblings_) { + sibling->inject(msg, email_); + } + } + break; + + case MatchingMessage::kSubmitQuoteNotif: + if (siblings_.empty()) { + logger_->error("[{}] {}: no siblings", __func__, name()); + } else { + auto quote = msg.submit_quote_notif().quote(); + const auto& itMatch = matches_.find(quote.request_id()); + if (itMatch == matches_.end()) { + logger_->info("[{}] {}: not our quote", __func__, name()); + break; + } + quote.set_quote_id(CryptoPRNG::generateRandom(6).toHexStr()); + auto msgCopy = msg; + *msgCopy.mutable_submit_quote_notif()->mutable_quote() = quote; + itMatch->second.quote = fromMsg(msgCopy.submit_quote_notif().quote()); + logger_->debug("[{}] quoteId = {}", __func__, itMatch->second.quote.quoteId); + for (const auto& sibling : siblings_) { + sibling->inject(msgCopy, email_); + } + const auto& timeNow = std::chrono::system_clock::now(); + Envelope envTO{ 0, user_, user_, timeNow, timeNow + kExpirationTimeout + , quote.quote_id() }; //FIXME: put actual quote's expirationTime + pushFill(envTO); + + MatchingMessage msgSettl; + toMsg(itMatch->second.quote, msgSettl.mutable_quote()); + Envelope envSettl{ 0, user_, userSettl_, {}, {}, msgSettl.SerializeAsString() }; + pushFill(envSettl); + } + break; + + case MatchingMessage::kOrder: { // only requester's processing + const auto& status = static_cast(msg.order().status()); + for (auto& match : matches_) { + if (match.second.quote.quoteId == msg.order().quote_id()) { + if ((status == bs::network::Order::Filled) || (status == bs::network::Order::Failed)) { + matches_.erase(match.first); + } + else { + match.second.order = fromMsg(msg.order()); + } + Envelope envSettl{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(envSettl); + } + } + } + break; + + case MatchingMessage::kAcceptRfq: { // only requester's processing + const auto& itMatch = matches_.find(msg.accept_rfq().rfq_id()); + if (itMatch == matches_.end()) { + logger_->warn("[{}] not our RFQ {}", __func__, msg.accept_rfq().rfq_id()); + break; + } + sendFilledOrder(itMatch->first); + matches_.erase(itMatch); + for (const auto& sibling : siblings_) { + sibling->inject(msg, email_); + } + } + break; + default: break; + } + } + else if (env.receiver && (env.receiver->value() == env.sender->value()) + && (env.sender->value() == user_->value())) { //own to self + for (const auto& match : matches_) { + if (match.second.quote.quoteId == env.message) { + return sendPendingOrder(match.first); + } + } + } + return true; +} + +void MatchingMock::link(const std::shared_ptr& sibling) +{ + siblings_.insert(sibling); +} + +bool MatchingMock::inject(const MatchingMessage& msg, const std::string &email) +{ + switch (msg.data_case()) { + case MatchingMessage::kSendRfq: + return sendIncomingRFQ(msg.send_rfq(), email); + case MatchingMessage::kSubmitQuoteNotif: + return sendQuoteReply(msg.submit_quote_notif(), email); + case MatchingMessage::kAcceptRfq: // only for responder + return sendFilledOrder(msg.accept_rfq().rfq_id()); + + case MatchingMessage::kOrder: // only for requester + for (auto& match : matches_) { + if (match.second.quote.quoteId == msg.order().quote_id()) { + match.second.order = fromMsg(msg.order()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); + } + } + logger_->warn("[{}] match for {} not found", __func__, msg.order().quote_id()); + break; + default: break; + } + return true; +} + +bool MatchingMock::sendIncomingRFQ(const RFQ& rfq, const std::string &email) +{ + MatchingMessage msg; + auto msgInRFQ = msg.mutable_incoming_rfq(); + *msgInRFQ->mutable_rfq() = rfq; + Match match{ CryptoPRNG::generateRandom(4).toHexStr() }; + msgInRFQ->set_session_token(match.sesToken); + if (static_cast(rfq.asset_type()) == bs::network::Asset::SpotXBT) { + msgInRFQ->set_settlement_id(CryptoPRNG::generateRandom(32).toHexStr()); + } + msgInRFQ->set_party(email); + const auto& timeNow = std::chrono::system_clock::now(); + msgInRFQ->set_timestamp_ms(std::chrono::duration_cast( + timeNow.time_since_epoch()).count()); + msgInRFQ->set_expiration_ms(std::chrono::duration_cast( + (timeNow + kExpirationTimeout).time_since_epoch()).count()); + matches_[rfq.id()] = std::move(match); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} + +bool MatchingMock::sendQuoteReply(const ReplyToRFQ& reply, const std::string& email) +{ + const auto& itMatch = matches_.find(reply.quote().request_id()); + if (itMatch == matches_.end()) { + logger_->info("[{}] {}: not our quote", __func__, name()); + return true; + } + itMatch->second.quote = fromMsg(reply.quote()); + itMatch->second.sesToken = reply.session_token(); + return sendQuote(itMatch->second.quote); +} + +bool MatchingMock::sendQuote(const bs::network::Quote& quote) +{ + MatchingMessage msg; + toMsg(quote, msg.mutable_quote()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} + +bool MatchingMock::sendPendingOrder(const std::string& rfqId) +{ + const auto& itMatch = matches_.find(rfqId); + if (itMatch == matches_.end()) { + logger_->error("[{}] {}: unknown RFQ {}", __func__, name(), rfqId); + return true; + } + bs::network::Order order; + order.clOrderId = CryptoPRNG::generateRandom(7).toHexStr(); + order.exchOrderId = QString::fromStdString(CryptoPRNG::generateRandom(8).toHexStr()); + order.quoteId = itMatch->second.quote.quoteId; + order.dateTime = QDateTime::currentDateTime(); + order.security = itMatch->second.quote.security; + order.product = itMatch->second.quote.product; + order.settlementId = BinaryData::CreateFromHex(itMatch->second.quote.settlementId); + order.quantity = itMatch->second.quote.quantity; + order.price = itMatch->second.quote.price; + order.avgPx = itMatch->second.quote.price; + order.assetType = itMatch->second.quote.assetType; + order.side = itMatch->second.quote.side; + order.status = bs::network::Order::Pending; + itMatch->second.order = order; + MatchingMessage msg; + toMsg(order, msg.mutable_order()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + pushFill(env); + + order.side = bs::network::Side::invert(order.side); + toMsg(order, msg.mutable_order()); + for (const auto& sibling : siblings_) { + sibling->inject(msg, email_); + } + return true; +} + +bool MatchingMock::sendFilledOrder(const std::string& rfqId) +{ + const auto& itMatch = matches_.find(rfqId); + if (itMatch == matches_.end()) { + return true; // not our RFQ + } + itMatch->second.order.status = bs::network::Order::Filled; + MatchingMessage msg; + toMsg(itMatch->second.order, msg.mutable_order()); + Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; + pushFill(env); +} diff --git a/UnitTests/TestAdapters.h b/UnitTests/TestAdapters.h new file mode 100644 index 000000000..10a49d9aa --- /dev/null +++ b/UnitTests/TestAdapters.h @@ -0,0 +1,94 @@ +#ifndef TEST_ADAPTERS_H +#define TEST_ADAPTERS_H + +#include +#include +#include +#include "CommonTypes.h" +#include "Message/Adapter.h" +#include "TerminalMessage.h" + +namespace spdlog { + class logger; +} +namespace BlockSettle { + namespace Terminal { + class MatchingMessage; + class RFQ; + class ReplyToRFQ; + } +} + +class TestSupervisor : public bs::message::Adapter +{ +public: + TestSupervisor(const std::string& name) : name_(name) + {} + bool process(const bs::message::Envelope &) override; + + bs::message::Adapter::Users supportedReceivers() const override + { + return { std::make_shared() }; + } + std::string name() const override { return "sup" + name_; } + + uint64_t send(bs::message::TerminalUsers sender, bs::message::TerminalUsers receiver + , const std::string &message, bool request = false); + bool push(const bs::message::Envelope &env) { return bs::message::Adapter::push(env); } + bool pushFill(bs::message::Envelope &env) { return bs::message::Adapter::pushFill(env); } + + using FilterCb = std::function; + std::future> waitFor(const FilterCb &, size_t count + , uint32_t *seqNo = nullptr); + std::future waitFor(const FilterCb &, uint32_t *seqNo = nullptr); + void unwaitFor(const uint32_t seqNo); + +private: + std::string name_; + std::map>> filterMap_; + std::map>>>> filterMultWait_; + std::map>>> filterWait_; + std::mutex mtxWait_; + std::atomic_uint32_t seqNo_{ 0 }; +}; + + +class MatchingMock : public bs::message::Adapter +{ +public: + MatchingMock(const std::shared_ptr& logger + , const std::string& name, const std::string& email); + + bool process(const bs::message::Envelope&) override; + + bs::message::Adapter::Users supportedReceivers() const override + { + return { user_ }; + } + std::string name() const override { return "Match" + name_; } + + void link(const std::shared_ptr&); + bool inject(const BlockSettle::Terminal::MatchingMessage&, const std::string &email); + +private: + bool sendIncomingRFQ(const BlockSettle::Terminal::RFQ&, const std::string &email); + bool sendQuoteReply(const BlockSettle::Terminal::ReplyToRFQ&, const std::string& email); + bool sendQuote(const bs::network::Quote&); + bool sendPendingOrder(const std::string& rfqId); + bool sendFilledOrder(const std::string& rfqId); + +private: + std::shared_ptr logger_; + std::shared_ptr user_, userSettl_; + std::string name_, email_; + + std::set> siblings_; + + struct Match { + std::string sesToken; + bs::network::Quote quote; + bs::network::Order order; + }; + std::unordered_map matches_; +}; +#endif // TEST_ADAPTERS_H diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index 7484065a5..5f3337053 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -96,9 +96,9 @@ void TestEnv::shutdown() void TestEnv::requireArmory() { //init armorydb - if (armoryInstance_ != nullptr) + if (armoryInstance_ != nullptr) { return; - + } armoryInstance_ = std::make_shared(); auto armoryConnection = std::make_shared( @@ -114,7 +114,7 @@ void TestEnv::requireArmory() auto keyCb = [](const BinaryData&, const std::string&)->bool { return true; - }; + }; armoryConnection->setupConnection(settings, keyCb); armoryConnection_ = armoryConnection; diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index 1eea845ad..d37010ece 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -456,22 +456,22 @@ class TestEnv void shutdown(void); - std::shared_ptr appSettings() { return appSettings_; } + [[deprecated]] std::shared_ptr appSettings() { return appSettings_; } std::shared_ptr armoryConnection() { return armoryConnection_; } std::shared_ptr armoryInstance() { return armoryInstance_; } - std::shared_ptr assetMgr() { return assetMgr_; } + [[deprecated]] std::shared_ptr assetMgr() { return assetMgr_; } std::shared_ptr blockMonitor() { return blockMonitor_; } - std::shared_ptr connectionMgr() { return connMgr_; } - std::shared_ptr celerConnection() { return celerConn_; } + [[deprecated]] std::shared_ptr connectionMgr() { return connMgr_; } + [[deprecated]] std::shared_ptr celerConnection() { return celerConn_; } std::shared_ptr logger() { return logger_; } std::shared_ptr walletsMgr() { return walletsMgr_; } - std::shared_ptr mdProvider() { return mdProvider_; } - std::shared_ptr mdCallbacks() { return mdCallbacks_; } - std::shared_ptr quoteProvider() { return quoteProvider_; } + [[deprecated]] std::shared_ptr mdProvider() { return mdProvider_; } + [[deprecated]] std::shared_ptr mdCallbacks() { return mdCallbacks_; } + [[deprecated]] std::shared_ptr quoteProvider() { return quoteProvider_; } void requireArmory(); - void requireAssets(); - void requireConnections(); + [[deprecated]] void requireAssets(); + [[deprecated]] void requireConnections(); private: std::shared_ptr appSettings_; diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index bc45cf12b..e7339a52f 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -9,26 +9,25 @@ */ #include "TestSettlement.h" -#include -#include -#include -#include -#include #include -#include "ApplicationSettings.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" #include "InprocSigner.h" +#include "MessageUtils.h" +#include "MockTerminal.h" +#include "TestAdapters.h" #include "TestEnv.h" -#include "TransactionData.h" -#include "Wallets/SyncWalletsManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncPlainWallet.h" -#include "CheckRecipSigner.h" - -using std::make_unique; -using namespace std::chrono_literals; + +#include "common.pb.h" +#include "terminal.pb.h" + using namespace ArmorySigner; +using namespace bs::message; +using namespace BlockSettle::Common; +using namespace BlockSettle::Terminal; + +constexpr auto kFutureWaitTimeout = std::chrono::seconds(3); +constexpr auto kLongWaitTimeout = std::chrono::seconds(15); void TestSettlement::mineBlocks(unsigned count) { @@ -79,18 +78,17 @@ void TestSettlement::SetUp() std::make_shared(coinbasePrivKey_, coinbasePubKey_); envPtr_ = std::make_shared(StaticLogger::loggerPtr); - envPtr_->requireAssets(); -// act_ = std::make_unique(envPtr_->armoryConnection().get()); + envPtr_->requireArmory(); mineBlocks(101); const auto logger = envPtr_->logger(); - const auto amount = (initialTransferAmount_ + 10) * COIN; + const auto amount = initialTransferAmount_ * COIN; - walletsMgr_ = std::make_shared(logger); const bs::wallet::PasswordData pd{ passphrase_, { bs::wallet::EncryptionType::Password } }; for (size_t i = 0; i < nbParties_; i++) { + walletsMgr_.push_back(std::make_shared(logger)); auto hdWallet = std::make_shared( "Primary" + std::to_string(i), "" , NetworkType::TestNet, pd @@ -128,7 +126,7 @@ void TestSettlement::SetUp() logger->debug("[TestSettlement] {} fundAddr={}, authAddr={}, authKey={}" , hdWallet->name(), addr.display(), authAddr.display(), authKey.toHexStr()); - walletsMgr_->addWallet(hdWallet); + walletsMgr_[i]->addWallet(hdWallet); xbtWallet_.emplace_back(leaf); authWallet_.push_back(authLeaf); fundAddrs_.emplace_back(addr); @@ -136,54 +134,12 @@ void TestSettlement::SetUp() authAddrs_.emplace_back(authAddr); authKeys_.emplace_back(std::move(authKey)); hdWallet_.push_back(hdWallet); - } - auto inprocSigner = std::make_shared( - walletsMgr_, logger, "", NetworkType::TestNet); - inprocSigner->Start(); - syncMgr_ = std::make_shared(logger - , envPtr_->appSettings(), envPtr_->armoryConnection()); - syncMgr_->setSignContainer(inprocSigner); - auto promSync = std::make_shared>(); - auto futSync = promSync->get_future(); - syncMgr_->syncWallets([promSync](int cur, int total) { - if (cur == total) { - promSync->set_value(true); - } - }); - futSync.wait(); - - UnitTestWalletACT::clear(); - - for (const auto &hdWallet : syncMgr_->hdWallets()) { - hdWallet->setCustomACT(envPtr_->armoryConnection()); + inprocSigner_.push_back(std::make_shared( + walletsMgr_.at(i), logger, "", NetworkType::TestNet)); + inprocSigner_.at(i)->Start(); } - - const auto regIDs = syncMgr_->registerWallets(); - UnitTestWalletACT::waitOnRefresh(regIDs); - -// auto curHeight = envPtr_->armoryConnection()->topBlock(); mineBlocks(6); - - auto promPtr = std::make_shared>(); - auto fut = promPtr->get_future(); - auto ctrPtr = std::make_shared>(0); - auto wltCount = syncMgr_->getAllWallets().size() - 2; - - auto balLBD = [promPtr, ctrPtr, wltCount](void)->void - { - ctrPtr->fetch_add(1); - if (*ctrPtr == wltCount) - promPtr->set_value(true); - }; - - for (const auto &wallet : syncMgr_->getAllWallets()) { - wallet->updateBalances(balLBD); - } - - settlementId_ = CryptoPRNG::generateRandom(32); - - fut.wait(); } void TestSettlement::TearDown() @@ -193,8 +149,8 @@ void TestSettlement::TearDown() authAddrs_.clear(); fundAddrs_.clear(); hdWallet_.clear(); - userId_.clear(); - syncMgr_.reset(); + walletsMgr_.clear(); + inprocSigner_.clear(); } TestSettlement::~TestSettlement() @@ -203,24 +159,358 @@ TestSettlement::~TestSettlement() TEST_F(TestSettlement, Initial_balances) { ASSERT_FALSE(xbtWallet_.empty()); + ASSERT_EQ(nbParties_, 2); for (size_t i = 0; i < nbParties_; i++) { - ASSERT_NE(xbtWallet_[i], nullptr); - const auto syncWallet = syncMgr_->getWalletById(xbtWallet_[i]->walletId()); - ASSERT_NE(syncWallet, nullptr); - ASSERT_GE(syncWallet->getSpendableBalance(), initialTransferAmount_); + ASSERT_NE(xbtWallet_.at(i), nullptr); } + MockTerminal t1(StaticLogger::loggerPtr, "T1", inprocSigner_.at(0), envPtr_->armoryConnection()); + MockTerminal t2(StaticLogger::loggerPtr, "T2", inprocSigner_.at(1), envPtr_->armoryConnection()); + const auto& sup1 = std::make_shared(t1.name()); + t1.bus()->addAdapter(sup1); + const auto& sup2 = std::make_shared(t2.name()); + t2.bus()->addAdapter(sup2); + + const auto& walletReady = [](const auto& walletId) + { + return [walletId](const bs::message::Envelope& env) + { + if (env.request || env.receiver || + (env.sender->value() != TerminalUsers::Wallets)) { + return false; + } + WalletsMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == WalletsMessage::kWalletReady) { + return (msg.wallet_ready() == walletId); + } + } + return false; + }; + }; + auto fut1 = sup1->waitFor(walletReady(xbtWallet_.at(0)->walletId())); + auto fut2 = sup2->waitFor(walletReady(xbtWallet_.at(1)->walletId())); + t1.start(); + t2.start(); + ASSERT_EQ(fut1.wait_for(kFutureWaitTimeout), std::future_status::ready); + ASSERT_EQ(fut2.wait_for(kFutureWaitTimeout), std::future_status::ready); + + const auto& walletBalance = [](const std::string& walletId, double expectedBal) + { + return [walletId, expectedBal](const bs::message::Envelope& env) + { + if (!env.receiver || (env.receiver->value() != TerminalUsers::API) + || (env.sender->value() != TerminalUsers::Wallets)) { + return false; + } + WalletsMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == WalletsMessage::kWalletBalances) { + return ((msg.wallet_balances().wallet_id() == walletId) + && qFuzzyCompare(msg.wallet_balances().spendable_balance(), expectedBal)); + } + } + return false; + }; + }; + fut1 = sup1->waitFor(walletBalance(xbtWallet_.at(0)->walletId() + , initialTransferAmount_)); + fut2 = sup2->waitFor(walletBalance(xbtWallet_.at(1)->walletId() + , initialTransferAmount_)); + WalletsMessage msgWlt; + msgWlt.set_get_wallet_balances(xbtWallet_.at(0)->walletId()); + sup1->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); + msgWlt.set_get_wallet_balances(xbtWallet_.at(1)->walletId()); + sup2->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); + ASSERT_EQ(fut1.wait_for(kFutureWaitTimeout), std::future_status::ready); + ASSERT_EQ(fut2.wait_for(kFutureWaitTimeout), std::future_status::ready); +} + +TEST_F(TestSettlement, SpotFX_sell) +{ + ASSERT_GE(inprocSigner_.size(), 2); + const std::string& email1 = "aaa@example.com"; + const std::string& email2 = "bbb@example.com"; + MockTerminal t1(StaticLogger::loggerPtr, "T1", inprocSigner_.at(0), envPtr_->armoryConnection()); + MockTerminal t2(StaticLogger::loggerPtr, "T2", inprocSigner_.at(1), envPtr_->armoryConnection()); + const auto& sup1 = std::make_shared(t1.name()); + t1.bus()->addAdapter(sup1); + const auto& sup2 = std::make_shared(t2.name()); + t2.bus()->addAdapter(sup2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1", email1); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2", email2); + m1->link(m2); + m2->link(m1); + t1.bus()->addAdapter(m1); + t2.bus()->addAdapter(m2); + t1.start(); + t2.start(); + + const auto& rfqId = CryptoPRNG::generateRandom(5).toHexStr(); + const double qty = 123; + const auto& quoteReqNotif = [this, qty, rfqId](const Envelope& env) + { + if (env.receiver || (env.receiver && !env.receiver->isBroadcast()) || + (env.sender->value() != TerminalUsers::Settlement)) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == SettlementMessage::kQuoteReqNotif) { + const auto& rfq = msg.quote_req_notif().rfq(); + return ((rfq.security() == fxSecurity_) && (rfq.product() == fxProduct_) + && !rfq.buy() && (rfq.quantity() == qty) && (rfq.id() == rfqId)); + } + } + return false; + }; + auto fut = sup2->waitFor(quoteReqNotif); + + SettlementMessage msgSettl; + auto msgSendRFQ = msgSettl.mutable_send_rfq(); + auto msgRFQ = msgSendRFQ->mutable_rfq(); + msgRFQ->set_id(rfqId); + msgRFQ->set_security(fxSecurity_); + msgRFQ->set_product(fxProduct_); + msgRFQ->set_asset_type((int)bs::network::Asset::SpotFX); + msgRFQ->set_buy(false); + msgRFQ->set_quantity(qty); + sup1->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); + + const double replyPrice = 1.23; + const auto& quoteReply = [this, replyPrice, qty, rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kQuote)) { + return ((msg.quote().security() == fxSecurity_) && (msg.quote().product() == fxProduct_) + && (msg.quote().request_id() == rfqId) && (msg.quote().price() == replyPrice) + && (msg.quote().quantity() == qty) && msg.quote().buy()); + } + return false; + }; + SettlementMessage inMsg; + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& qrn = fromMsg(inMsg.quote_req_notif()); + bs::network::QuoteNotification qn(qrn, {}, replyPrice, {}); + qn.validityInS = 5; + toMsg(qn, msgSettl.mutable_reply_to_rfq()); + fut = sup1->waitFor(quoteReply); + sup2->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); + + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quote = fromMsg(inMsg.quote()); + const auto& pendingOrder = [this, rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kPendingSettlement)) { + return (msg.pending_settlement().ids().rfq_id() == rfqId); + } + return false; + }; + fut = sup1->waitFor(pendingOrder); + ASSERT_EQ(fut.wait_for(kLongWaitTimeout), std::future_status::ready); + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quoteId = inMsg.pending_settlement().ids().quote_id(); + ASSERT_EQ(quoteId, quote.quoteId); - //auto promPtr = std::make_shared>(); - //auto fut = promPtr->get_future(); - // - //const auto &cbFee = [promPtr](float feePerByte) { - // promPtr->set_value(feePerByte); - //}; - //syncMgr_->estimatedFeePerByte(1, cbFee); + const auto& filledOrder = [this, rfqId, quoteId, replyPrice](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kMatchedQuote)) { + const auto& matched = msg.matched_quote(); + return ((matched.rfq_id() == rfqId) && (matched.quote_id() == quoteId) + && (matched.price() == replyPrice)); + } + return false; + }; + fut = sup2->waitFor(filledOrder); + auto msgAccept = msgSettl.mutable_accept_rfq(); + msgAccept->set_rfq_id(rfqId); + toMsg(quote, msgAccept->mutable_quote()); + sup1->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); +} - //fut.wait(); +TEST_F(TestSettlement, SpotFX_buy) +{ + ASSERT_GE(inprocSigner_.size(), 2); + const std::string& email1 = "aaa@example.com"; + const std::string& email2 = "bbb@example.com"; + MockTerminal t1(StaticLogger::loggerPtr, "T1", inprocSigner_.at(0), envPtr_->armoryConnection()); + MockTerminal t2(StaticLogger::loggerPtr, "T2", inprocSigner_.at(1), envPtr_->armoryConnection()); + const auto& sup1 = std::make_shared(t1.name()); + t1.bus()->addAdapter(sup1); + const auto& sup2 = std::make_shared(t2.name()); + t2.bus()->addAdapter(sup2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1", email1); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2", email2); + m1->link(m2); + m2->link(m1); + t1.bus()->addAdapter(m1); + t2.bus()->addAdapter(m2); + t1.start(); + t2.start(); + + const auto& rfqId = CryptoPRNG::generateRandom(5).toHexStr(); + const double qty = 234; + const auto& quoteReqNotif = [this, qty, rfqId](const Envelope& env) + { + if (env.receiver || (env.receiver && !env.receiver->isBroadcast()) || + (env.sender->value() != TerminalUsers::Settlement)) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == SettlementMessage::kQuoteReqNotif) { + const auto& rfq = msg.quote_req_notif().rfq(); + return ((rfq.security() == fxSecurity_) && (rfq.product() == fxProduct_) + && rfq.buy() && (rfq.quantity() == qty) && (rfq.id() == rfqId)); + } + } + return false; + }; + auto fut = sup2->waitFor(quoteReqNotif); + + SettlementMessage msgSettl; + auto msgSendRFQ = msgSettl.mutable_send_rfq(); + auto msgRFQ = msgSendRFQ->mutable_rfq(); + msgRFQ->set_id(rfqId); + msgRFQ->set_security(fxSecurity_); + msgRFQ->set_product(fxProduct_); + msgRFQ->set_asset_type((int)bs::network::Asset::SpotFX); + msgRFQ->set_buy(true); + msgRFQ->set_quantity(qty); + sup1->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); + + const double replyPrice = 1.21; + const auto& quoteReply = [this, replyPrice, qty, rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kQuote)) { + return ((msg.quote().security() == fxSecurity_) && (msg.quote().product() == fxProduct_) + && (msg.quote().request_id() == rfqId) && (msg.quote().price() == replyPrice) + && (msg.quote().quantity() == qty) && !msg.quote().buy()); + } + return false; + }; + SettlementMessage inMsg; + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& qrn = fromMsg(inMsg.quote_req_notif()); + bs::network::QuoteNotification qn(qrn, {}, replyPrice, {}); + qn.validityInS = 5; + toMsg(qn, msgSettl.mutable_reply_to_rfq()); + fut = sup1->waitFor(quoteReply); + sup2->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); + + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quote = fromMsg(inMsg.quote()); + const auto& pendingOrder = [this, rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kPendingSettlement)) { + return (msg.pending_settlement().ids().rfq_id() == rfqId); + } + return false; + }; + fut = sup1->waitFor(pendingOrder); + ASSERT_EQ(fut.wait_for(kLongWaitTimeout), std::future_status::ready); + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quoteId = inMsg.pending_settlement().ids().quote_id(); + ASSERT_EQ(quoteId, quote.quoteId); + + const auto& filledOrder = [this, rfqId, quoteId, replyPrice](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kMatchedQuote)) { + const auto& matched = msg.matched_quote(); + return ((matched.rfq_id() == rfqId) && (matched.quote_id() == quoteId) + && (matched.price() == replyPrice)); + } + return false; + }; + fut = sup2->waitFor(filledOrder); + auto msgAccept = msgSettl.mutable_accept_rfq(); + msgAccept->set_rfq_id(rfqId); + toMsg(quote, msgAccept->mutable_quote()); + sup1->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); +} - //EXPECT_GE(fut.get(), 5); +TEST_F(TestSettlement, SpotXBT_sell) +{ + const auto& cbFromWallet = [](const Envelope& env) -> bool + { + if (env.request || (env.sender->value() != TerminalUsers::Wallets)) { + return false; + } + WalletsMessage msg; + if (msg.ParseFromString(env.message)) { + StaticLogger::loggerPtr->debug(msg.DebugString()); + } + return false; + }; + ASSERT_FALSE(xbtWallet_.empty()); + ASSERT_EQ(nbParties_, 2); + ASSERT_GE(inprocSigner_.size(), 2); + const std::string& email1 = "aaa@example.com"; + const std::string& email2 = "bbb@example.com"; + MockTerminal t1(StaticLogger::loggerPtr, "T1", inprocSigner_.at(0), envPtr_->armoryConnection()); + MockTerminal t2(StaticLogger::loggerPtr, "T2", inprocSigner_.at(1), envPtr_->armoryConnection()); + const auto& sup1 = std::make_shared(t1.name()); + t1.bus()->addAdapter(sup1); + const auto& sup2 = std::make_shared(t2.name()); + t2.bus()->addAdapter(sup2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1", email1); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2", email2); + m1->link(m2); + m2->link(m1); + t1.bus()->addAdapter(m1); + t2.bus()->addAdapter(m2); + + const auto& walletReady = [](const auto& walletId) + { + return [walletId](const bs::message::Envelope& env) + { + if (env.request || env.receiver || + (env.sender->value() != TerminalUsers::Wallets)) { + return false; + } + WalletsMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == WalletsMessage::kWalletReady) { + return (msg.wallet_ready() == walletId); + } + } + return false; + }; + }; + auto fut1 = sup1->waitFor(walletReady(xbtWallet_.at(0)->walletId())); + auto fut2 = sup2->waitFor(walletReady(xbtWallet_.at(1)->walletId())); + t1.start(); + t2.start(); + ASSERT_EQ(fut1.wait_for(kFutureWaitTimeout), std::future_status::ready); + ASSERT_EQ(fut2.wait_for(kFutureWaitTimeout), std::future_status::ready); } #if 0 //temporarily disabled diff --git a/UnitTests/TestSettlement.h b/UnitTests/TestSettlement.h index 0d2b8d6a2..6bd1f4dba 100644 --- a/UnitTests/TestSettlement.h +++ b/UnitTests/TestSettlement.h @@ -51,10 +51,6 @@ class TestSettlement : public ::testing::Test void SetUp() override; void TearDown() override; - bool waitForPayIn() { return BlockchainMonitor::waitForFlag(receivedPayIn_); } - bool waitForPayOut() { return BlockchainMonitor::waitForFlag(receivedPayOut_); } -// bool waitForSettlWallet() { return BlockchainMonitor::waitForFlag(settlWalletReady_); } - void mineBlocks(unsigned count); void sendTo(uint64_t value, bs::Address& addr); @@ -67,18 +63,16 @@ class TestSettlement : public ::testing::Test const double initialTransferAmount_ = 1.23; std::vector> hdWallet_; std::vector> authWallet_; - std::shared_ptr walletsMgr_; - std::shared_ptr syncMgr_; std::vector> xbtWallet_; + std::vector> walletsMgr_; + std::vector> inprocSigner_; std::vector authAddrs_; std::vector authKeys_; std::vector fundAddrs_; - SecureBinaryData settlementId_; - std::vector userId_; std::map> settlLeafMap_; - std::atomic_bool receivedPayIn_{ false }; - std::atomic_bool receivedPayOut_{ false }; - bs::PayoutSignatureType poType_{}; + + const std::string fxSecurity_{ "EUR/USD" }; + const std::string fxProduct_{ "EUR" }; private: QMutex mtxWalletId_; @@ -93,8 +87,6 @@ class TestSettlement : public ::testing::Test std::map coinbaseHashes_; unsigned coinbaseCounter_ = 0; - std::unique_ptr act_; - private: void onWalletReady(const QString &id); }; diff --git a/common b/common index e9c0af11f..9bb0ad344 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e9c0af11fb8b6bc07bdca29bb673cc5e7bc09263 +Subproject commit 9bb0ad344a01f1af13ed5d9e628f43e5fe28d96e From 46759aefa34390e378bcc0abdc29d8bf69c3b6fa Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 9 Dec 2020 20:10:55 +0300 Subject: [PATCH 064/146] XBT settlement tests --- BlockSettleSigner/SignerAdapterContainer.h | 3 +- Core/SettlementAdapter.cpp | 8 +- UnitTests/MockTerminal.cpp | 1 - UnitTests/TestAdapters.cpp | 206 +++++++- UnitTests/TestAdapters.h | 27 +- UnitTests/TestEnv.cpp | 37 +- UnitTests/TestEnv.h | 3 +- UnitTests/TestSettlement.cpp | 565 ++++++++++----------- UnitTests/TestSettlement.h | 3 +- common | 2 +- 10 files changed, 523 insertions(+), 332 deletions(-) diff --git a/BlockSettleSigner/SignerAdapterContainer.h b/BlockSettleSigner/SignerAdapterContainer.h index 459c427f2..ebfd83ed8 100644 --- a/BlockSettleSigner/SignerAdapterContainer.h +++ b/BlockSettleSigner/SignerAdapterContainer.h @@ -37,7 +37,8 @@ class SignAdapterContainer : public WalletSignerContainer [[deprecated]] bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & , TXSignMode = TXSignMode::Full, bool = false) override { return 0; } void signTXRequest(const bs::core::wallet::TXSignRequest& - , const std::function& + , const std::function& , TXSignMode mode = TXSignMode::Full, bool keepDuplicatedRecipients = false) override {} void createSettlementWallet(const bs::Address & diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index c389257ca..2392184e1 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -233,7 +233,7 @@ bool SettlementAdapter::processMatchingOrder(const MatchingMessage_Order& respon msgResp->set_info(order.info); } else if (order.status == bs::network::Order::Status::Pending) { - if (itSettl->second->dealer && (itSettl->second->quote.assetType == bs::network::Asset::SpotXBT) + if (/*itSettl->second->dealer &&*/ (itSettl->second->quote.assetType == bs::network::Asset::SpotXBT) && (itSettl->second->quote.quotingType == bs::network::Quote::Tradeable)) { if (((itSettl->second->quote.side == bs::network::Side::Buy) || (itSettl->second->quote.product != bs::network::XbtCurrency)) @@ -760,10 +760,10 @@ bool SettlementAdapter::processSignedTx(uint64_t msgId const Tx tx(BinaryData::fromString(response.signed_tx())); pendingZCs_[tx.getThisHash()] = itPayout->second; } - catch (const std::exception&) { - logger_->error("[{}] invalid signed payout TX", __func__); - payoutRequests_.erase(itPayout); + catch (const std::exception& e) { + logger_->error("[{}] invalid signed payout TX: {}", __func__, e.what()); cancel(itPayout->second); + payoutRequests_.erase(itPayout); return true; } const auto& itSettl = settlBySettlId_.find(itPayout->second); diff --git a/UnitTests/MockTerminal.cpp b/UnitTests/MockTerminal.cpp index da3028e58..03640d68b 100644 --- a/UnitTests/MockTerminal.cpp +++ b/UnitTests/MockTerminal.cpp @@ -208,7 +208,6 @@ MockTerminal::MockTerminal(const std::shared_ptr& logger bus_->addAdapter(std::make_shared(logger)); bus_->addAdapter(signAdapter); //TODO: add TrackerMockAdapter - //TODO: add BsMockAdapter bus_->addAdapter(std::make_shared(logger_ , userWallets, signAdapter->createClient(), userBlockchain)); bus_->addAdapter(std::make_shared(logger)); diff --git a/UnitTests/TestAdapters.cpp b/UnitTests/TestAdapters.cpp index 69b7c2640..a6a98c6f9 100644 --- a/UnitTests/TestAdapters.cpp +++ b/UnitTests/TestAdapters.cpp @@ -1,12 +1,18 @@ #include "TestAdapters.h" +#include #include #include "MessageUtils.h" +#include "ProtobufHeadlessUtils.h" #include "TestEnv.h" + +#include "common.pb.h" +#include "headless.pb.h" #include "terminal.pb.h" //#define MSG_DEBUGGING // comment this out if msg id debug output is not needed using namespace bs::message; +using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; constexpr auto kExpirationTimeout = std::chrono::seconds{ 5 }; @@ -101,9 +107,12 @@ void TestSupervisor::unwaitFor(const uint32_t seqNo) MatchingMock::MatchingMock(const std::shared_ptr& logger - , const std::string& name, const std::string &email) + , const std::string& name, const std::string &email + , const std::shared_ptr& armoryInst) : logger_(logger), name_(name), email_(email) + , armoryInst_(armoryInst) , user_(UserTerminal::create(TerminalUsers::Matching)) + , userBS_(UserTerminal::create(TerminalUsers::BsServer)) , userSettl_(UserTerminal::create(TerminalUsers::Settlement)) {} @@ -122,7 +131,7 @@ bool MatchingMock::process(const bs::message::Envelope& env) //TODO: send result back to requester } else { - matches_[msg.send_rfq().id()] = {}; + matches_[msg.send_rfq().id()] = { {}, msg.send_rfq().auth_pub_key() }; for (const auto& sibling : siblings_) { sibling->inject(msg, email_); } @@ -140,10 +149,10 @@ bool MatchingMock::process(const bs::message::Envelope& env) break; } quote.set_quote_id(CryptoPRNG::generateRandom(6).toHexStr()); + quote.set_quoting_type((int)bs::network::Quote::Tradeable); auto msgCopy = msg; *msgCopy.mutable_submit_quote_notif()->mutable_quote() = quote; itMatch->second.quote = fromMsg(msgCopy.submit_quote_notif().quote()); - logger_->debug("[{}] quoteId = {}", __func__, itMatch->second.quote.quoteId); for (const auto& sibling : siblings_) { sibling->inject(msgCopy, email_); } @@ -182,16 +191,34 @@ bool MatchingMock::process(const bs::message::Envelope& env) logger_->warn("[{}] not our RFQ {}", __func__, msg.accept_rfq().rfq_id()); break; } - sendFilledOrder(itMatch->first); - matches_.erase(itMatch); - for (const auto& sibling : siblings_) { - sibling->inject(msg, email_); + if (itMatch->second.quote.assetType == bs::network::Asset::SpotFX) { + sendFilledOrder(itMatch->first); + matches_.erase(itMatch); + for (const auto& sibling : siblings_) { + sibling->inject(msg, email_); + } } } break; default: break; } } + if (env.receiver && env.request && (env.receiver->value() == userBS_->value())) { + BsServerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse own request #{}", __func__, env.id); + return true; + } + switch (msg.data_case()) { + case BsServerMessage::kSendUnsignedPayin: + return processUnsignedPayin(msg.send_unsigned_payin()); + case BsServerMessage::kSendSignedPayin: + return processSignedTX(msg.send_signed_payin(), true, true); + case BsServerMessage::kSendSignedPayout: + return processSignedTX(msg.send_signed_payout(), false, true); + default: break; + } + } else if (env.receiver && (env.receiver->value() == env.sender->value()) && (env.sender->value() == user_->value())) { //own to self for (const auto& match : matches_) { @@ -233,6 +260,24 @@ bool MatchingMock::inject(const MatchingMessage& msg, const std::string &email) return true; } +bool MatchingMock::inject(const BsServerMessage& msg, const std::string& email) +{ + switch (msg.data_case()) { + case BsServerMessage::kUnsignedPayinRequested: + return sendUnsignedPayinRequest(msg.unsigned_payin_requested()); + case BsServerMessage::kSignedPayoutRequested: + return sendSignedPayoutRequest(msg.signed_payout_requested()); + case BsServerMessage::kSignedPayinRequested: + return sendSignedPayinRequest(msg.signed_payin_requested()); + case BsServerMessage::kSendSignedPayin: + return processSignedTX(msg.send_signed_payin(), true); + case BsServerMessage::kSendSignedPayout: + return processSignedTX(msg.send_signed_payout(), false); + default: break; + } + return true; +} + bool MatchingMock::sendIncomingRFQ(const RFQ& rfq, const std::string &email) { MatchingMessage msg; @@ -262,6 +307,7 @@ bool MatchingMock::sendQuoteReply(const ReplyToRFQ& reply, const std::string& em return true; } itMatch->second.quote = fromMsg(reply.quote()); + itMatch->second.quote.requestorAuthPublicKey = itMatch->second.reqAuthPubKey; itMatch->second.sesToken = reply.session_token(); return sendQuote(itMatch->second.quote); } @@ -306,6 +352,21 @@ bool MatchingMock::sendPendingOrder(const std::string& rfqId) for (const auto& sibling : siblings_) { sibling->inject(msg, email_); } + + if (itMatch->second.quote.assetType == bs::network::Asset::SpotXBT) { + const auto& settlementId = BinaryData::CreateFromHex(itMatch->second.quote.settlementId); + BsServerMessage msgBS; + msgBS.set_unsigned_payin_requested(settlementId.toBinStr()); + if (itMatch->second.isSellXBT()) { + for (const auto& sibling : siblings_) { + sibling->inject(msgBS, email_); + } + } + else { + Envelope envBS{ 0, userBS_, userSettl_, {}, {}, msgBS.SerializeAsString() }; + pushFill(envBS); + } + } return true; } @@ -319,5 +380,134 @@ bool MatchingMock::sendFilledOrder(const std::string& rfqId) MatchingMessage msg; toMsg(itMatch->second.order, msg.mutable_order()); Envelope env{ 0, user_, userSettl_, {}, {}, msg.SerializeAsString() }; - pushFill(env); + return pushFill(env); +} + +bool MatchingMock::sendUnsignedPayinRequest(const std::string& settlIdBin) +{ + const auto& settlIdHex = BinaryData::fromString(settlIdBin).toHexStr(); + const auto& itMatch = std::find_if(matches_.cbegin(), matches_.cend() + , [settlIdHex](const std::pair& match) { + return (match.second.quote.settlementId == settlIdHex); + }); + if (itMatch == matches_.end()) { + logger_->warn("[{}] can't find match with settlement id {}", __func__, settlIdHex); + return true; + } + BsServerMessage msgBS; + msgBS.set_unsigned_payin_requested(settlIdBin); + Envelope env{ 0, userBS_, userSettl_, {}, {}, msgBS.SerializeAsString() }; + return pushFill(env); +} + +bool MatchingMock::sendSignedPayinRequest(const BsServerMessage_SignXbtHalf& request) +{ + const auto& settlIdHex = BinaryData::fromString(request.settlement_id()).toHexStr(); + const auto& itMatch = std::find_if(matches_.cbegin(), matches_.cend() + , [settlIdHex](const std::pair& match) { + return (match.second.quote.settlementId == settlIdHex); + }); + if (itMatch == matches_.end()) { + logger_->warn("[{}] can't find match with settlement id {}", __func__, settlIdHex); + return true; + } + BsServerMessage msgBS; + *msgBS.mutable_signed_payin_requested() = request; + Envelope env{ 0, userBS_, userSettl_, {}, {}, msgBS.SerializeAsString() }; + return pushFill(env); +} + +bool MatchingMock::sendSignedPayoutRequest(const BsServerMessage_SignXbtHalf& request) +{ + const auto& settlIdHex = BinaryData::fromString(request.settlement_id()).toHexStr(); + const auto& itMatch = std::find_if(matches_.cbegin(), matches_.cend() + , [settlIdHex](const std::pair& match) { + return (match.second.quote.settlementId == settlIdHex); + }); + if (itMatch == matches_.end()) { + logger_->warn("[{}] can't find match with settlement id {}", __func__, settlIdHex); + return true; + } + BsServerMessage msgBS; + *msgBS.mutable_signed_payout_requested() = request; + Envelope env{ 0, userBS_, userSettl_, {}, {}, msgBS.SerializeAsString() }; + return pushFill(env); +} + +bool MatchingMock::processUnsignedPayin(const BsServerMessage_XbtTransaction& response) +{ + Codec_SignerState::SignerState signerState; + if (!signerState.ParseFromString(response.tx())) { + logger_->error("[{}] failed to parse SignerState", __func__); + return true; //TODO: send settlement failure? + } + bs::core::wallet::TXSignRequest txReq; + txReq.armorySigner_.deserializeState(signerState); + const auto& payinHash = txReq.txId(); + if (payinHash.empty()) { + logger_->error("[{}] invalid payin hash", __func__); + return true; //TODO: send settlement failure? + } + BsServerMessage msg; + auto msgReq = msg.mutable_signed_payout_requested(); + msgReq->set_settlement_id(response.settlement_id()); + msgReq->set_payin_hash(payinHash.toBinStr()); + for (const auto& sibling : siblings_) { + sibling->inject(msg, email_); + } + msgReq = msg.mutable_signed_payin_requested(); + msgReq->set_settlement_id(response.settlement_id()); + msgReq->set_unsigned_payin(response.tx()); + msgReq->set_payin_hash(payinHash.toBinStr()); + const auto curTime = QDateTime::currentDateTime(); + msgReq->set_timestamp(curTime.toMSecsSinceEpoch()); + Envelope env{ 0, userBS_, userSettl_, {}, {}, msg.SerializeAsString() }; + return pushFill(env); +} + +bool MatchingMock::processSignedTX(const BsServerMessage_XbtTransaction& response + , bool payin, bool recurse) +{ + const auto& settlIdHex = BinaryData::fromString(response.settlement_id()).toHexStr(); + const auto &itMatch = std::find_if(matches_.begin(), matches_.end() + , [settlIdHex](const std::pair &match) { + return (match.second.quote.settlementId == settlIdHex); + }); + if (itMatch == matches_.end()) { + logger_->warn("[{}] unknown settlement id {}", __func__, settlIdHex); + return true; + } + { + const auto& tx = BinaryData::fromString(response.tx()); + if (payin) { + itMatch->second.signedPayin = tx; + } else { + itMatch->second.signedPayout = tx; + } + } + if (!itMatch->second.signedPayin.empty() && !itMatch->second.signedPayout.empty()) { + armoryInst_->pushZC(itMatch->second.signedPayin); + armoryInst_->pushZC(itMatch->second.signedPayout); + + //TODO: monitor new blocks and provide gradual order status update on each conf + sendFilledOrder(itMatch->first); + } + else if (recurse) { + BsServerMessage msg; + if (payin) { + *msg.mutable_send_signed_payin() = response; + } else { + *msg.mutable_send_signed_payout() = response; + } + for (const auto& sibling : siblings_) { + sibling->inject(msg, email_); + } + } + return true; +} + +bool MatchingMock::Match::isSellXBT() const +{ + return ((quote.side == bs::network::Side::Buy) + || (quote.product != bs::network::XbtCurrency)); } diff --git a/UnitTests/TestAdapters.h b/UnitTests/TestAdapters.h index 10a49d9aa..c2cdd7e8a 100644 --- a/UnitTests/TestAdapters.h +++ b/UnitTests/TestAdapters.h @@ -13,11 +13,15 @@ namespace spdlog { } namespace BlockSettle { namespace Terminal { + class BsServerMessage; + class BsServerMessage_SignXbtHalf; + class BsServerMessage_XbtTransaction; class MatchingMessage; class RFQ; class ReplyToRFQ; } } +struct ArmoryInstance; class TestSupervisor : public bs::message::Adapter { @@ -57,18 +61,20 @@ class MatchingMock : public bs::message::Adapter { public: MatchingMock(const std::shared_ptr& logger - , const std::string& name, const std::string& email); + , const std::string& name, const std::string& email + , const std::shared_ptr &); // for pushing ZCs (mocking PB) bool process(const bs::message::Envelope&) override; bs::message::Adapter::Users supportedReceivers() const override { - return { user_ }; + return { user_, userBS_ }; } std::string name() const override { return "Match" + name_; } void link(const std::shared_ptr&); - bool inject(const BlockSettle::Terminal::MatchingMessage&, const std::string &email); + bool inject(const BlockSettle::Terminal::MatchingMessage&, const std::string& email); + bool inject(const BlockSettle::Terminal::BsServerMessage&, const std::string& email); private: bool sendIncomingRFQ(const BlockSettle::Terminal::RFQ&, const std::string &email); @@ -77,17 +83,30 @@ class MatchingMock : public bs::message::Adapter bool sendPendingOrder(const std::string& rfqId); bool sendFilledOrder(const std::string& rfqId); + bool sendUnsignedPayinRequest(const std::string& settlIdBin); + bool sendSignedPayinRequest(const BlockSettle::Terminal::BsServerMessage_SignXbtHalf&); + bool sendSignedPayoutRequest(const BlockSettle::Terminal::BsServerMessage_SignXbtHalf&); + bool processUnsignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); + bool processSignedTX(const BlockSettle::Terminal::BsServerMessage_XbtTransaction& + , bool payin, bool recurse = false); + private: std::shared_ptr logger_; - std::shared_ptr user_, userSettl_; + std::shared_ptr user_, userBS_, userSettl_; std::string name_, email_; + std::shared_ptr armoryInst_; std::set> siblings_; struct Match { std::string sesToken; + std::string reqAuthPubKey; bs::network::Quote quote; bs::network::Order order; + BinaryData signedPayin; + BinaryData signedPayout; + + bool isSellXBT() const; }; std::unordered_map matches_; }; diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index 5f3337053..f6aa074ef 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -93,7 +92,7 @@ void TestEnv::shutdown() ACTqueue::notifQueue_.clear(); } -void TestEnv::requireArmory() +void TestEnv::requireArmory(bool waitForReady) { //init armorydb if (armoryInstance_ != nullptr) { @@ -122,14 +121,21 @@ void TestEnv::requireArmory() qDebug() << "Waiting for ArmoryDB connection..."; while (armoryConnection_->state() != ArmoryState::Connected) { - QThread::msleep(1); + std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); + } + if (waitForReady) { + qDebug() << "Armory connected - waiting for ready state..."; + } + else { + qDebug() << "Armory connected - go online"; } - qDebug() << "Armory connected - waiting for ready state..."; armoryConnection_->goOnline(); - while (armoryConnection_->state() != ArmoryState::Ready) { - QThread::msleep(1); + if (waitForReady) { + while (armoryConnection_->state() != ArmoryState::Ready) { + std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); + } + logger_->debug("Armory is ready - continue execution"); } - logger_->debug("Armory is ready - continue execution"); } void TestEnv::requireAssets() @@ -281,21 +287,30 @@ void ArmoryInstance::pushZC(const BinaryData& zc, unsigned int blocksUntilMined, void ArmoryInstance::setReorgBranchPoint(const BinaryData& hash) { auto headerPtr = theBDMt_->bdm()->blockchain()->getHeaderByHash(hash); - if (headerPtr == nullptr) + if (headerPtr == nullptr) { throw std::runtime_error("null header ptr"); - + } nodePtr_->setReorgBranchPoint(headerPtr); } BinaryData ArmoryInstance::getCurrentTopBlockHash() const { auto headerPtr = theBDMt_->bdm()->blockchain()->top(); - if (headerPtr == nullptr) + if (headerPtr == nullptr) { throw std::runtime_error("null header ptr"); - + } return headerPtr->getThisHash(); } +uint32_t ArmoryInstance::getCurrentTopBlock(void) const +{ + auto headerPtr = theBDMt_->bdm()->blockchain()->top(); + if (headerPtr == nullptr) { + throw std::runtime_error("null header ptr"); + } + return headerPtr->getBlockHeight(); +} + //// SingleUTWalletACT::~SingleUTWalletACT() { diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index d37010ece..ed1bc6926 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -416,6 +416,7 @@ struct ArmoryInstance void setReorgBranchPoint(const BinaryData&); BinaryData getCurrentTopBlockHash(void) const; + uint32_t getCurrentTopBlock(void) const; }; class TestArmoryConnection : public ArmoryObject @@ -469,7 +470,7 @@ class TestEnv [[deprecated]] std::shared_ptr mdCallbacks() { return mdCallbacks_; } [[deprecated]] std::shared_ptr quoteProvider() { return quoteProvider_; } - void requireArmory(); + void requireArmory(bool waitForReady = true); [[deprecated]] void requireAssets(); [[deprecated]] void requireConnections(); diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index e7339a52f..927f4bf59 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -26,16 +26,17 @@ using namespace bs::message; using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; -constexpr auto kFutureWaitTimeout = std::chrono::seconds(3); +constexpr auto kFutureWaitTimeout = std::chrono::seconds(5); constexpr auto kLongWaitTimeout = std::chrono::seconds(15); void TestSettlement::mineBlocks(unsigned count) { - auto curHeight = envPtr_->armoryConnection()->topBlock(); +// auto curHeight = envPtr_->armoryConnection()->topBlock(); + auto curHeight = envPtr_->armoryInstance()->getCurrentTopBlock(); Recipient_P2PKH coinbaseRecipient(coinbaseScrAddr_, 50 * COIN); auto&& cbMap = envPtr_->armoryInstance()->mineNewBlock(&coinbaseRecipient, count); coinbaseHashes_.insert(cbMap.begin(), cbMap.end()); - envPtr_->blockMonitor()->waitForNewBlocks(curHeight + count); +// envPtr_->blockMonitor()->waitForNewBlocks(curHeight + count); // doesn't work if armoryConnection is not ready } void TestSettlement::sendTo(uint64_t value, bs::Address& addr) @@ -78,7 +79,7 @@ void TestSettlement::SetUp() std::make_shared(coinbasePrivKey_, coinbasePubKey_); envPtr_ = std::make_shared(StaticLogger::loggerPtr); - envPtr_->requireArmory(); + envPtr_->requireArmory(false); mineBlocks(101); @@ -100,11 +101,14 @@ void TestSettlement::SetUp() { const bs::core::WalletPasswordScoped lock(hdWallet, passphrase_); leaf = grp->createLeaf(AddressEntryType_P2SH, 0); - addr = leaf->getNewExtAddress(); } + addr = leaf->getNewExtAddress(); sendTo(amount, addr); + recvAddrs_.push_back(leaf->getNewExtAddress()); + changeAddrs_.push_back(leaf->getNewIntAddress()); + std::shared_ptr authLeaf, settlLeaf; bs::Address authAddr; SecureBinaryData authKey; @@ -136,7 +140,10 @@ void TestSettlement::SetUp() hdWallet_.push_back(hdWallet); inprocSigner_.push_back(std::make_shared( - walletsMgr_.at(i), logger, "", NetworkType::TestNet)); + walletsMgr_.at(i), logger, "", NetworkType::TestNet + , [this, hdWallet](const std::string&) { + return std::make_unique(hdWallet, passphrase_); + })); inprocSigner_.at(i)->Start(); } mineBlocks(6); @@ -236,8 +243,10 @@ TEST_F(TestSettlement, SpotFX_sell) t1.bus()->addAdapter(sup1); const auto& sup2 = std::make_shared(t2.name()); t2.bus()->addAdapter(sup2); - const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1", email1); - const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2", email2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1" + , email1, envPtr_->armoryInstance()); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2" + , email2, envPtr_->armoryInstance()); m1->link(m2); m2->link(m1); t1.bus()->addAdapter(m1); @@ -352,8 +361,10 @@ TEST_F(TestSettlement, SpotFX_buy) t1.bus()->addAdapter(sup1); const auto& sup2 = std::make_shared(t2.name()); t2.bus()->addAdapter(sup2); - const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1", email1); - const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2", email2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1" + , email1, envPtr_->armoryInstance()); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2" + , email2, envPtr_->armoryInstance()); m1->link(m2); m2->link(m1); t1.bus()->addAdapter(m1); @@ -459,20 +470,10 @@ TEST_F(TestSettlement, SpotFX_buy) TEST_F(TestSettlement, SpotXBT_sell) { - const auto& cbFromWallet = [](const Envelope& env) -> bool - { - if (env.request || (env.sender->value() != TerminalUsers::Wallets)) { - return false; - } - WalletsMessage msg; - if (msg.ParseFromString(env.message)) { - StaticLogger::loggerPtr->debug(msg.DebugString()); - } - return false; - }; ASSERT_FALSE(xbtWallet_.empty()); - ASSERT_EQ(nbParties_, 2); + ASSERT_GE(nbParties_, 2); ASSERT_GE(inprocSigner_.size(), 2); + const float fpbRate = 2.3; const std::string& email1 = "aaa@example.com"; const std::string& email2 = "bbb@example.com"; MockTerminal t1(StaticLogger::loggerPtr, "T1", inprocSigner_.at(0), envPtr_->armoryConnection()); @@ -481,8 +482,10 @@ TEST_F(TestSettlement, SpotXBT_sell) t1.bus()->addAdapter(sup1); const auto& sup2 = std::make_shared(t2.name()); t2.bus()->addAdapter(sup2); - const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1", email1); - const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2", email2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1" + , email1, envPtr_->armoryInstance()); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2" + , email2, envPtr_->armoryInstance()); m1->link(m2); m2->link(m1); t1.bus()->addAdapter(m1); @@ -509,311 +512,273 @@ TEST_F(TestSettlement, SpotXBT_sell) auto fut2 = sup2->waitFor(walletReady(xbtWallet_.at(1)->walletId())); t1.start(); t2.start(); + + WalletsMessage msgWlt; + msgWlt.set_set_settlement_fee(fpbRate); + sup1->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); + sup2->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); ASSERT_EQ(fut1.wait_for(kFutureWaitTimeout), std::future_status::ready); ASSERT_EQ(fut2.wait_for(kFutureWaitTimeout), std::future_status::ready); -} -#if 0 //temporarily disabled -TEST_F(TestSettlement, SpotXBT_sell) -{ - const auto feePerByte = TestEnv::walletsMgr()->estimatedFeePerByte(1); - const auto &settlWallet = TestEnv::walletsMgr()->GetSettlementWallet(); - uint32_t curHeight = 0; + const auto& rfqId = CryptoPRNG::generateRandom(5).toHexStr(); + const double qty = 0.23; - for (int i = 0; i < nbParties_; i++) { - ASSERT_FALSE(authAddr_[i].isNull()); - ASSERT_FALSE(authWallet_[i]->GetPubChainedKeyFor(authAddr_[i]).isNull()); - } - const auto settlementAddr = settlWallet->newAddress(settlementId_ - , authWallet_[0]->GetPubChainedKeyFor(authAddr_[0]), authWallet_[1]->GetPubChainedKeyFor(authAddr_[1])); - ASSERT_NE(settlementAddr, nullptr); - EXPECT_TRUE(waitForSettlWallet()); - - // Requester's side - TransactionData reqTxData([] {}); - reqTxData.SetFeePerByte(feePerByte); - reqTxData.SetWallet(wallet_[1]); - const auto reqRecip = reqTxData.RegisterNewRecipient(); - reqTxData.UpdateRecipientAddress(reqRecip, settlementAddr); - reqTxData.UpdateRecipientAmount(reqRecip, 0.1); - ASSERT_TRUE(reqTxData.IsTransactionValid()); - ASSERT_GE(reqTxData.GetTransactionSummary().selectedBalance, 0.1); - - auto monitor = settlWallet->createMonitor(settlementAddr, TestEnv::logger()); - ASSERT_NE(monitor, nullptr); - connect(monitor.get(), &bs::SettlementMonitor::payInDetected, [this] { receivedPayIn_ = true; }); - connect(monitor.get(), &bs::SettlementMonitor::payOutDetected, [this] (int nbConf, bs::PayoutSigner::Type poType) { - qDebug() << "poType=" << poType << "nbConf=" << nbConf; - receivedPayOut_ = true; - poType_ = poType; - }); - monitor->start(); - - auto txPayInReq = reqTxData.CreateTXRequest(); - const auto txPayIn = reqTxData.GetWallet()->SignTXRequest(txPayInReq); - const auto txHex = QString::fromStdString(txPayIn.toHexStr()); - ASSERT_FALSE(txPayIn.isNull()); - ASSERT_TRUE(TestEnv::regtestControl()->SendTx(txHex)); - - ASSERT_TRUE(waitForPayIn()); - settlWallet->UpdateBalanceFromDB(); - EXPECT_DOUBLE_EQ(settlWallet->GetUnconfirmedBalance(), 0.1); - - // Responder's side - const auto authKeys = authWallet_[0]->GetKeyPairFor(authAddr_[0], {}); - ASSERT_FALSE(authKeys.privKey.isNull()); - ASSERT_FALSE(authKeys.pubKey.isNull()); - - ASSERT_FALSE(settlWallet->getSpendableZCList().empty()); - const auto payInHash = Tx(txPayIn).getThisHash(); - auto txReq = settlWallet->CreatePayoutTXRequest(settlWallet->GetInputFor(settlementAddr) - , fundAddr_[0], feePerByte); - ASSERT_FALSE(txReq.inputs.empty()); - const auto txPayOut = settlWallet->SignPayoutTXRequest(txReq, authKeys, settlementAddr->getAsset()->settlementId() - , settlementAddr->getAsset()->buyAuthPubKey(), settlementAddr->getAsset()->sellAuthPubKey()); - ASSERT_FALSE(txPayOut.isNull()); - ASSERT_TRUE(TestEnv::regtestControl()->SendTx(QString::fromStdString(txPayOut.toHexStr()))); - -// ASSERT_TRUE(waitForPayOut()); - curHeight = PyBlockDataManager::instance()->GetTopBlockHeight(); - TestEnv::regtestControl()->GenerateBlocks(1); - TestEnv::blockMonitor()->waitForNewBlocks(curHeight + 1); - EXPECT_TRUE(waitForPayOut()); - EXPECT_EQ(poType_, bs::PayoutSigner::Type::SignedByBuyer); - - curHeight = PyBlockDataManager::instance()->GetTopBlockHeight(); - TestEnv::regtestControl()->GenerateBlocks(6); - TestEnv::blockMonitor()->waitForNewBlocks(curHeight + 6); - settlWallet->UpdateBalanceFromDB(); - wallet_[0]->UpdateBalanceFromDB(); - wallet_[1]->UpdateBalanceFromDB(); - EXPECT_GT(wallet_[0]->GetTotalBalance(), initialTransferAmount_ + 0.1 - 0.01); // buyer (dealer) - EXPECT_LT(wallet_[1]->GetTotalBalance(), initialTransferAmount_ - 0.1); // seller (requester) - EXPECT_DOUBLE_EQ(settlWallet->GetTotalBalance(), 0); - monitor = nullptr; -} -#endif //0 + auto msgReq = msgWlt.mutable_reserve_utxos(); + msgReq->set_id(rfqId); + msgReq->set_sub_id(xbtWallet_.at(0)->walletId()); + msgReq->set_amount(bs::XBTAmount(qty).GetValue()); + sup1->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); -#if 0 -TEST_F(TestSettlement, SpotXBT_buy) -{ - auto logger = StaticLogger::loggerPtr; - - const float feePerByte = 1.05; - ASSERT_GE(authAddrs_.size(), 2); - const auto settlWallet1 = std::dynamic_pointer_cast(settlLeafMap_[authAddrs_[0]]); - ASSERT_NE(settlWallet1, nullptr); - const auto settlWallet2 = std::dynamic_pointer_cast(settlLeafMap_[authAddrs_[1]]); - ASSERT_NE(settlWallet2, nullptr); - uint32_t curHeight = 0; - const double amount = 0.1; - - settlWallet1->addSettlementID(settlementId_); - settlWallet1->getNewExtAddress(); - settlWallet2->addSettlementID(settlementId_); - settlWallet2->getNewExtAddress(); - - ASSERT_GE(authKeys_.size(), 2); - ASSERT_FALSE(authKeys_[0].isNull()); - ASSERT_FALSE(authKeys_[1].isNull()); - - const bs::core::wallet::SettlementData settlDataBuy{ settlementId_, authKeys_[1], true }; - const bs::core::wallet::SettlementData settlDataSell{ settlementId_, authKeys_[0], false }; - const auto settlementAddr = hdWallet_[0]->getSettlementPayinAddress(settlDataBuy); - ASSERT_FALSE(settlementAddr.isNull()); - EXPECT_EQ(settlementAddr, hdWallet_[1]->getSettlementPayinAddress(settlDataSell)); - - const auto dummyWalletId = CryptoPRNG::generateRandom(8).toHexStr(); - auto syncSettlWallet = std::make_shared(dummyWalletId - , "temporary", "Dummy settlement wallet", nullptr, StaticLogger::loggerPtr); - syncSettlWallet->addAddress(settlementAddr, {}, false); - UnitTestWalletACT::clear(); - const auto settlRegId = syncSettlWallet->registerWallet(envPtr_->armoryConnection(), true); - UnitTestWalletACT::waitOnRefresh(settlRegId); - - const auto syncLeaf1 = syncMgr_->getWalletById(xbtWallet_[0]->walletId()); - const auto syncLeaf2 = syncMgr_->getWalletById(xbtWallet_[1]->walletId()); - - auto promUtxo2 = std::make_shared>(); - auto futUtxo2 = promUtxo2->get_future(); - const auto &cbTxOutList2 = [this, promUtxo2] - (const std::vector &inputs)->void + const auto& quoteReqNotif = [this, qty, rfqId](const Envelope& env) { - if (inputs.size() != 1) { - promUtxo2->set_value({}); + if (env.receiver || (env.receiver && !env.receiver->isBroadcast()) || + (env.sender->value() != TerminalUsers::Settlement)) { + return false; } - else { - promUtxo2->set_value(inputs.front()); + SettlementMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == SettlementMessage::kQuoteReqNotif) { + const auto& rfq = msg.quote_req_notif().rfq(); + return ((rfq.security() == xbtSecurity_) && (rfq.product() == bs::network::XbtCurrency) + && !rfq.buy() && (rfq.quantity() == qty) && (rfq.id() == rfqId)); + } } + return false; }; - ASSERT_TRUE(syncLeaf2->getSpendableTxOutList(cbTxOutList2, UINT64_MAX, true)); - const auto input2 = futUtxo2.get(); - ASSERT_TRUE(input2.isInitialized()); - - std::vector payinInputs = { input2 }; - - // Dealer's side - TransactionData dealerTxData([] {}, envPtr_->logger()); - dealerTxData.setFeePerByte(feePerByte); - dealerTxData.setWalletAndInputs(syncLeaf2, payinInputs, envPtr_->armoryConnection()->topBlock()); - const auto dealerRecip = dealerTxData.RegisterNewRecipient(); - dealerTxData.UpdateRecipientAddress(dealerRecip, settlementAddr); - dealerTxData.UpdateRecipientAmount(dealerRecip, amount); - ASSERT_TRUE(dealerTxData.IsTransactionValid()); - ASSERT_GE(dealerTxData.GetTransactionSummary().selectedBalance, amount); - - bs::core::wallet::TXSignRequest unsignedTxReq; - if (dealerTxData.GetTransactionSummary().hasChange) { - const auto changeAddr = xbtWallet_[1]->getNewChangeAddress(); - unsignedTxReq = dealerTxData.createUnsignedTransaction(false, changeAddr); - } - else { - unsignedTxReq = dealerTxData.createUnsignedTransaction(); - } - ASSERT_TRUE(unsignedTxReq.isValid()); - - auto payinResolver = xbtWallet_[1]->getPublicResolver(); - const auto dealerPayInHash = unsignedTxReq.txId(payinResolver); - EXPECT_FALSE(dealerPayInHash.isNull()); - - const auto serializedPayinRequest = unsignedTxReq.serializeState(payinResolver); - - ASSERT_FALSE(serializedPayinRequest.isNull()); - - bs::CheckRecipSigner deserializedSigner{serializedPayinRequest , envPtr_->armoryConnection() }; - - auto inputs = deserializedSigner.getTxInsData(); - - auto spenders = deserializedSigner.spenders(); - auto recipients = deserializedSigner.recipients(); - - logger->debug("Settlement address: {}", settlementAddr.display()); - auto settlementAddressRecipient = settlementAddr.getRecipient(bs::XBTAmount{ amount }); - auto settlementRecipientScript = settlementAddressRecipient->getSerializedScript(); + auto fut = sup2->waitFor(quoteReqNotif); - auto settAddressString = settlementAddr.display(); + SettlementMessage msgSettl; + auto msgSendRFQ = msgSettl.mutable_send_rfq(); + msgSendRFQ->set_reserve_id(rfqId); + auto msgRFQ = msgSendRFQ->mutable_rfq(); + msgRFQ->set_id(rfqId); + msgRFQ->set_security(xbtSecurity_); + msgRFQ->set_product(bs::network::XbtCurrency); + msgRFQ->set_asset_type((int)bs::network::Asset::SpotXBT); + msgRFQ->set_buy(false); + msgRFQ->set_quantity(qty); + msgRFQ->set_auth_pub_key(authKeys_.at(0).toHexStr()); + sup1->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); - bool recipientFound = false; + const double replyPrice = 12345.67; + const auto& quoteReply = [this, replyPrice, qty, rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kQuote)) { + return ((msg.quote().security() == xbtSecurity_) && (msg.quote().product() == bs::network::XbtCurrency) + && (msg.quote().request_id() == rfqId) && (msg.quote().price() == replyPrice) + && (msg.quote().quantity() == qty) && msg.quote().buy()); + } + return false; + }; + SettlementMessage inMsg; + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& qrn = fromMsg(inMsg.quote_req_notif()); + bs::network::QuoteNotification qn(qrn, authKeys_.at(1).toHexStr(), replyPrice, {}); + qn.receiptAddress = recvAddrs_.at(1).display(); + qn.validityInS = 5; + toMsg(qn, msgSettl.mutable_reply_to_rfq()); + fut = sup1->waitFor(quoteReply); + sup2->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); - // amount to settlement address ( single output ) - for (const auto recipient : recipients) { - auto amount = recipient->getValue(); - auto serializedRecipient = recipient->getSerializedScript(); + const auto& settlementId = BinaryData::CreateFromHex(qrn.settlementId); + ASSERT_FALSE(settlementId.empty()); + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quote = fromMsg(inMsg.quote()); - BtcUtils::pprintScript(serializedRecipient); - std::cout << '\n'; + const auto& pendingOrder = [rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kPendingSettlement)) { + return (msg.pending_settlement().ids().rfq_id() == rfqId); + } + return false; + }; + fut = sup1->waitFor(pendingOrder); + ASSERT_EQ(fut.wait_for(kLongWaitTimeout), std::future_status::ready); + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quoteId = inMsg.pending_settlement().ids().quote_id(); + ASSERT_EQ(quoteId, quote.quoteId); - if (serializedRecipient == settlementRecipientScript) { - recipientFound = true; - break; + const auto& settlementComplete = [rfqId, quoteId, settlementId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; } - } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kSettlementComplete)) { + return ((msg.settlement_complete().rfq_id() == rfqId) && + (msg.settlement_complete().quote_id() == quoteId) && + (msg.settlement_complete().settlement_id() == settlementId.toBinStr())); + } + return false; + }; + fut = sup2->waitFor(settlementComplete); + ASSERT_EQ(fut.wait_for(kLongWaitTimeout), std::future_status::ready); +} - for (const auto recipient : recipients) { - logger->debug("{} : {}", bs::CheckRecipSigner::getRecipientAddress(recipient).display(), recipient->getValue()); - } +TEST_F(TestSettlement, SpotXBT_buy) +{ + ASSERT_FALSE(xbtWallet_.empty()); + ASSERT_GE(nbParties_, 2); + ASSERT_GE(inprocSigner_.size(), 2); + const float fpbRate = 2.3; + const std::string& email1 = "aaa@example.com"; + const std::string& email2 = "bbb@example.com"; + MockTerminal t1(StaticLogger::loggerPtr, "T1", inprocSigner_.at(0), envPtr_->armoryConnection()); + MockTerminal t2(StaticLogger::loggerPtr, "T2", inprocSigner_.at(1), envPtr_->armoryConnection()); + const auto& sup1 = std::make_shared(t1.name()); + t1.bus()->addAdapter(sup1); + const auto& sup2 = std::make_shared(t2.name()); + t2.bus()->addAdapter(sup2); + const auto& m1 = std::make_shared(StaticLogger::loggerPtr, "T1" + , email1, envPtr_->armoryInstance()); + const auto& m2 = std::make_shared(StaticLogger::loggerPtr, "T2" + , email2, envPtr_->armoryInstance()); + m1->link(m2); + m2->link(m1); + t1.bus()->addAdapter(m1); + t2.bus()->addAdapter(m2); - // fee amount - // get all inputs amount - uint64_t totalInputAmount = 0; - for (const auto& input : spenders) { - totalInputAmount += input->getValue(); - } + const auto& walletReady = [](const auto& walletId) + { + return [walletId](const bs::message::Envelope& env) + { + if (env.request || env.receiver || + (env.sender->value() != TerminalUsers::Wallets)) { + return false; + } + WalletsMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == WalletsMessage::kWalletReady) { + return (msg.wallet_ready() == walletId); + } + } + return false; + }; + }; + auto fut1 = sup1->waitFor(walletReady(xbtWallet_.at(0)->walletId())); + auto fut2 = sup2->waitFor(walletReady(xbtWallet_.at(1)->walletId())); + t1.start(); + t2.start(); - // get all outputs amount - uint64_t totalOutputValue = 0; - for (const auto& output : recipients) { - totalOutputValue += output->getValue(); - } + WalletsMessage msgWlt; + msgWlt.set_set_settlement_fee(fpbRate); + sup1->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); + sup2->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); + ASSERT_EQ(fut1.wait_for(kFutureWaitTimeout), std::future_status::ready); + ASSERT_EQ(fut2.wait_for(kFutureWaitTimeout), std::future_status::ready); - try { - Tx tx{ serializedPayinRequest }; + const auto& rfqId = CryptoPRNG::generateRandom(5).toHexStr(); + const double qty = 0.237; - // XXX check that payin is not rbf - ASSERT_FALSE(tx.isRBF()); + const auto& quoteReqNotif = [this, qty, rfqId](const Envelope& env) + { + if (env.receiver || (env.receiver && !env.receiver->isBroadcast()) || + (env.sender->value() != TerminalUsers::Settlement)) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message)) { + if (msg.data_case() == SettlementMessage::kQuoteReqNotif) { + const auto& rfq = msg.quote_req_notif().rfq(); + return ((rfq.security() == xbtSecurity_) && (rfq.product() == bs::network::XbtCurrency) + && rfq.buy() && (rfq.quantity() == qty) && (rfq.id() == rfqId)); + } + } + return false; + }; + auto fut = sup2->waitFor(quoteReqNotif); - auto txOutCount = tx.getNumTxOut(); + SettlementMessage msgSettl; + auto msgSendRFQ = msgSettl.mutable_send_rfq(); + msgSendRFQ->set_reserve_id(rfqId); + auto msgRFQ = msgSendRFQ->mutable_rfq(); + msgRFQ->set_id(rfqId); + msgRFQ->set_security(xbtSecurity_); + msgRFQ->set_product(bs::network::XbtCurrency); + msgRFQ->set_asset_type((int)bs::network::Asset::SpotXBT); + msgRFQ->set_buy(true); + msgRFQ->set_quantity(qty); + msgRFQ->set_auth_pub_key(authKeys_.at(0).toHexStr()); + msgRFQ->set_receipt_address(recvAddrs_.at(0).display()); + sup1->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); - for (unsigned i = 0; i < txOutCount; ++i) { - auto txOut = tx.getTxOutCopy(i); + auto msgReq = msgWlt.mutable_reserve_utxos(); + msgReq->set_id(rfqId); + msgReq->set_sub_id(xbtWallet_.at(1)->walletId()); + msgReq->set_amount(bs::XBTAmount(qty).GetValue()); + sup2->send(TerminalUsers::API, TerminalUsers::Wallets, msgWlt.SerializeAsString(), true); + const double replyPrice = 12345.78; + const auto& quoteReply = [this, replyPrice, qty, rfqId](const Envelope& env) + { + if (env.sender->value() != TerminalUsers::Settlement) { + return false; } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kQuote)) { + return ((msg.quote().security() == xbtSecurity_) && (msg.quote().product() == bs::network::XbtCurrency) + && (msg.quote().request_id() == rfqId) && (msg.quote().price() == replyPrice) + && (msg.quote().quantity() == qty) && !msg.quote().buy()); + } + return false; + }; + SettlementMessage inMsg; + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& qrn = fromMsg(inMsg.quote_req_notif()); + bs::network::QuoteNotification qn(qrn, authKeys_.at(1).toHexStr(), replyPrice, {}); + //qn.receiptAddress = recvAddrs_.at(1).display(); + qn.validityInS = 5; + toMsg(qn, msgSettl.mutable_reply_to_rfq()); + fut = sup1->waitFor(quoteReply); + sup2->send(TerminalUsers::API, TerminalUsers::Settlement, msgSettl.SerializeAsString(), true); + ASSERT_EQ(fut.wait_for(kFutureWaitTimeout), std::future_status::ready); - auto txHash = tx.getThisHash(); - - StaticLogger::loggerPtr->debug("TX serialized"); - } catch (...) { - StaticLogger::loggerPtr->error("Failed to serialize TX"); - } - - // Requester's side - StaticLogger::loggerPtr->debug("[{}] payin hash: {}", __func__, dealerPayInHash.toHexStr(true)); - const auto payinInput = bs::SettlementMonitor::getInputFromTX(settlementAddr, dealerPayInHash - , bs::XBTAmount{ amount }); - const auto payoutTxReq = bs::SettlementMonitor::createPayoutTXRequest(payinInput - , fundAddrs_[0], feePerByte, envPtr_->armoryConnection()->topBlock()); - ASSERT_TRUE(payoutTxReq.isValid()); + const auto& settlementId = BinaryData::CreateFromHex(qrn.settlementId); + ASSERT_FALSE(settlementId.empty()); + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quote = fromMsg(inMsg.quote()); - BinaryData txPayOut; + const auto& pendingOrder = [rfqId](const Envelope& env) { - bs::core::WalletPasswordScoped passLock(hdWallet_[0], passphrase_); - txPayOut = hdWallet_[0]->signSettlementTXRequest(payoutTxReq, settlDataBuy); - } - ASSERT_FALSE(txPayOut.isNull()); + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kPendingSettlement)) { + return (msg.pending_settlement().ids().rfq_id() == rfqId); + } + return false; + }; + fut = sup1->waitFor(pendingOrder); + ASSERT_EQ(fut.wait_for(kLongWaitTimeout), std::future_status::ready); + ASSERT_TRUE(inMsg.ParseFromString(fut.get().message)); + const auto& quoteId = inMsg.pending_settlement().ids().quote_id(); + ASSERT_EQ(quoteId, quote.quoteId); - // Back to dealer - const auto payinTxReq = dealerTxData.getSignTxRequest(); - BinaryData txPayIn; - { - bs::core::WalletPasswordScoped passLock(hdWallet_[1], passphrase_); - txPayIn = xbtWallet_[1]->signTXRequest(payinTxReq); - } - ASSERT_FALSE(txPayIn.isNull()); - Tx txPayinObj(txPayIn); - EXPECT_EQ(txPayinObj.getThisHash(), dealerPayInHash); - - // get fee size. check against rate - const auto totalFee = totalInputAmount - totalOutputValue; - auto correctedFPB = feePerByte; - const auto estimatedFee = deserializedSigner.estimateFee(correctedFPB); - - UnitTestWalletACT::clear(); - StaticLogger::loggerPtr->debug("[{}] payin TX: {}", __func__, txPayIn.toHexStr()); - envPtr_->armoryInstance()->pushZC(txPayIn); - const auto& zcVecPayin = UnitTestWalletACT::waitOnZC(); - ASSERT_GE(zcVecPayin.size(), 1); - EXPECT_EQ(zcVecPayin[0].txHash, txPayinObj.getThisHash()); - - auto promUtxo = std::make_shared>(); - auto futUtxo = promUtxo->get_future(); - const auto &cbZCList = [this, promUtxo] - (const std::vector &inputs)->void + const auto& settlementComplete = [rfqId, quoteId, settlementId](const Envelope& env) { - if (inputs.size() != 1) { - promUtxo->set_value({}); - } else { - promUtxo->set_value(inputs.front()); + if (env.sender->value() != TerminalUsers::Settlement) { + return false; + } + SettlementMessage msg; + if (msg.ParseFromString(env.message) && (msg.data_case() == SettlementMessage::kSettlementComplete)) { + return ((msg.settlement_complete().rfq_id() == rfqId) && + (msg.settlement_complete().quote_id() == quoteId) && + (msg.settlement_complete().settlement_id() == settlementId.toBinStr())); } + return false; }; - ASSERT_TRUE(syncSettlWallet->getSpendableZCList(cbZCList)); - const auto zcPayin = futUtxo.get(); - ASSERT_TRUE(zcPayin.isInitialized()); - EXPECT_EQ(zcPayin, payinInput); - EXPECT_EQ(zcPayin.getScript(), payinInput.getScript()); - EXPECT_EQ(zcPayin.getTxOutIndex(), payinInput.getTxOutIndex()); - std::this_thread::sleep_for(20ms); - UnitTestWalletACT::clear(); - - Tx txPayoutObj(txPayOut); - ASSERT_TRUE(txPayoutObj.isInitialized()); - ASSERT_EQ(txPayoutObj.getNumTxIn(), 1); - ASSERT_EQ(txPayoutObj.getNumTxOut(), 1); - - StaticLogger::loggerPtr->debug("[{}] payout TX: {}", __func__, txPayOut.toHexStr()); - envPtr_->armoryInstance()->pushZC(txPayOut); - const auto& zcVecPayout = UnitTestWalletACT::waitOnZC(true); - ASSERT_GE(zcVecPayout.size(), 1); - EXPECT_EQ(zcVecPayout[0].txHash, txPayoutObj.getThisHash()); - std::this_thread::sleep_for(150ms); // have no idea yet, why it's required + fut = sup2->waitFor(settlementComplete); + ASSERT_EQ(fut.wait_for(kLongWaitTimeout), std::future_status::ready); } -#endif diff --git a/UnitTests/TestSettlement.h b/UnitTests/TestSettlement.h index 6bd1f4dba..a3726d78a 100644 --- a/UnitTests/TestSettlement.h +++ b/UnitTests/TestSettlement.h @@ -68,11 +68,12 @@ class TestSettlement : public ::testing::Test std::vector> inprocSigner_; std::vector authAddrs_; std::vector authKeys_; - std::vector fundAddrs_; + std::vector fundAddrs_, recvAddrs_, changeAddrs_; std::map> settlLeafMap_; const std::string fxSecurity_{ "EUR/USD" }; const std::string fxProduct_{ "EUR" }; + const std::string xbtSecurity_{ "XBT/EUR" }; private: QMutex mtxWalletId_; diff --git a/common b/common index 9bb0ad344..c45fab388 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 9bb0ad344a01f1af13ed5d9e628f43e5fe28d96e +Subproject commit c45fab388745bacc3879a139416890cfc2f3e44a From d190b89e9b7bee750988b7485518546626f105ac Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 9 Dec 2020 20:34:15 +0300 Subject: [PATCH 065/146] Switch to C++17 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2b61e42b..42bf68388 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ # CMAKE_MINIMUM_REQUIRED( VERSION 3.3 ) -SET(CMAKE_CXX_STANDARD 14) +SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_CXX_EXTENSIONS OFF) SET(QT_USE_QTDBUS ON) From f4afb2f6d7c98d0ab90ea7603847985828f71366 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 9 Dec 2020 21:38:08 +0300 Subject: [PATCH 066/146] C++17 compatibility --- Core/ApiJson.cpp | 46 +++++++++++++++++++++++----------------------- common | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index 3a659fc41..ba812215f 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -312,14 +312,14 @@ bool ApiJsonAdapter::processBlockchain(const Envelope &env) walletsReady_ = true; } break; - case ArmoryMessage::kLedgerEntries: [[fallthrough]] - case ArmoryMessage::kAddressHistory: [[fallthrough]] + case ArmoryMessage::kLedgerEntries: [[fallthrough]]; + case ArmoryMessage::kAddressHistory: [[fallthrough]]; case ArmoryMessage::kFeeLevelsResponse: if (hasRequest(env.id)) { sendReplyToClient(env.id, msg, env.sender); } break; - case ArmoryMessage::kZcReceived: [[fallthrough]] + case ArmoryMessage::kZcReceived: [[fallthrough]]; case ArmoryMessage::kZcInvalidated: sendReplyToClient(0, msg, env.sender); break; @@ -362,19 +362,19 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) return true; } switch (msg.data_case()) { - case WalletsMessage::kWalletLoaded: [[fallthrough]] - case WalletsMessage::kAuthWallet: [[fallthrough]] + case WalletsMessage::kWalletLoaded: [[fallthrough]]; + case WalletsMessage::kAuthWallet: [[fallthrough]]; case WalletsMessage::kHdWallet: sendReplyToClient(0, msg, env.sender); break; - case WalletsMessage::kWalletAddresses: [[fallthrough]] - case WalletsMessage::kAddrComments: [[fallthrough]] - case WalletsMessage::kWalletData: [[fallthrough]] - case WalletsMessage::kWalletBalances: [[fallthrough]] - case WalletsMessage::kTxDetailsResponse: [[fallthrough]] - case WalletsMessage::kWalletsListResponse: [[fallthrough]] - case WalletsMessage::kUtxos: [[fallthrough]] - case WalletsMessage::kAuthKey: [[fallthrough]] + case WalletsMessage::kWalletAddresses: [[fallthrough]]; + case WalletsMessage::kAddrComments: [[fallthrough]]; + case WalletsMessage::kWalletData: [[fallthrough]]; + case WalletsMessage::kWalletBalances: [[fallthrough]]; + case WalletsMessage::kTxDetailsResponse: [[fallthrough]]; + case WalletsMessage::kWalletsListResponse: [[fallthrough]]; + case WalletsMessage::kUtxos: [[fallthrough]]; + case WalletsMessage::kAuthKey: [[fallthrough]]; case WalletsMessage::kReservedUtxos: if (hasRequest(env.id)) { sendReplyToClient(env.id, msg, env.sender); @@ -393,7 +393,7 @@ bool ApiJsonAdapter::processOnChainTrack(const Envelope &env) return true; } switch (msg.data_case()) { - case OnChainTrackMessage::kAuthState: [[fallthrough]] + case OnChainTrackMessage::kAuthState: [[fallthrough]]; case OnChainTrackMessage::kVerifiedAuthAddresses: sendReplyToClient(0, msg, env.sender); break; @@ -477,12 +477,12 @@ bool ApiJsonAdapter::processSettlement(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case SettlementMessage::kQuote: [[fallthrough]] - case SettlementMessage::kMatchedQuote: [[fallthrough]] - case SettlementMessage::kFailedSettlement: [[fallthrough]] - case SettlementMessage::kPendingSettlement: [[fallthrough]] - case SettlementMessage::kSettlementComplete: [[fallthrough]] - case SettlementMessage::kQuoteCancelled: [[fallthrough]] + case SettlementMessage::kQuote: [[fallthrough]]; + case SettlementMessage::kMatchedQuote: [[fallthrough]]; + case SettlementMessage::kFailedSettlement: [[fallthrough]]; + case SettlementMessage::kPendingSettlement: [[fallthrough]]; + case SettlementMessage::kSettlementComplete: [[fallthrough]]; + case SettlementMessage::kQuoteCancelled: [[fallthrough]]; case SettlementMessage::kQuoteReqNotif: sendReplyToClient(0, msg, env.sender); break; @@ -521,9 +521,9 @@ bool ApiJsonAdapter::processMktData(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { - case MktDataMessage::kDisconnected: [[fallthrough]] - case MktDataMessage::kNewSecurity: [[fallthrough]] - case MktDataMessage::kAllInstrumentsReceived: [[fallthrough]] + case MktDataMessage::kDisconnected: [[fallthrough]]; + case MktDataMessage::kNewSecurity: [[fallthrough]]; + case MktDataMessage::kAllInstrumentsReceived: [[fallthrough]]; case MktDataMessage::kPriceUpdate: sendReplyToClient(0, msg, env.sender); break; diff --git a/common b/common index c45fab388..c08458258 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit c45fab388745bacc3879a139416890cfc2f3e44a +Subproject commit c08458258a6fb3729cc9f8f4c0995c170cb31433 From fbe6e953b9b464fd93726422f89685d31f5d5864 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 10 Dec 2020 15:43:38 +0300 Subject: [PATCH 067/146] Multiple tests run crash fixes --- UnitTests/TestEnv.cpp | 20 +++++++++++++++----- UnitTests/TestEnv.h | 5 ++++- UnitTests/TestSettlement.cpp | 18 +++++++++++------- UnitTests/TestSettlement.h | 2 +- common | 2 +- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index f6aa074ef..7b7d1ed8b 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -84,8 +84,8 @@ void TestEnv::shutdown() walletsMgr_ = nullptr; - armoryInstance_ = nullptr; - armoryConnection_ = nullptr; + armoryInstance_.reset(); + armoryConnection_.reset(); QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).removeRecursively(); @@ -96,6 +96,7 @@ void TestEnv::requireArmory(bool waitForReady) { //init armorydb if (armoryInstance_ != nullptr) { + armoryInstance_->init(); return; } armoryInstance_ = std::make_shared(); @@ -161,6 +162,11 @@ void TestEnv::requireConnections() /////////////////////////////////////////////////////////////////////////////// ArmoryInstance::ArmoryInstance() : blkdir_("./blkfiletest"), homedir_("./fakehomedir"), ldbdir_("./ldbtestdir") +{ + init(); +} + +void ArmoryInstance::init() { //setup armory folders SystemFileUtils::rmDir(blkdir_); @@ -247,15 +253,18 @@ ArmoryInstance::ArmoryInstance() } //// -ArmoryInstance::~ArmoryInstance() +void ArmoryInstance::shutdown() { + if (!theBDMt_) { + return; + } //shutdown server auto&& bdvObj2 = AsyncClient::BlockDataViewer::getNewBDV("127.0.0.1" , config_.listenPort_, BlockDataManagerConfig::getDataDir() , [](const std::set &) { return SecureBinaryData{}; } , BlockDataManagerConfig::ephemeralPeers_, true, nullptr); - auto&& serverPubkey = WebSocketServer::getPublicKey(); - bdvObj2->addPublicKey(serverPubkey); +// auto&& serverPubkey = WebSocketServer::getPublicKey(); // sometimes crash here +// bdvObj2->addPublicKey(serverPubkey); bdvObj2->connectToRemote(); bdvObj2->shutdown(config_.cookie_); @@ -311,6 +320,7 @@ uint32_t ArmoryInstance::getCurrentTopBlock(void) const return headerPtr->getBlockHeight(); } + //// SingleUTWalletACT::~SingleUTWalletACT() { diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index ed1bc6926..e6a218497 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -409,7 +409,7 @@ struct ArmoryInstance LMDBBlockDatabase* iface_; ArmoryInstance(); - ~ArmoryInstance(void); + ~ArmoryInstance(void) { shutdown(); } std::map mineNewBlock(ArmorySigner::ScriptRecipient*, unsigned); void pushZC(const BinaryData &, unsigned int blocksUntilMined = 0, bool stage = false); @@ -417,6 +417,9 @@ struct ArmoryInstance void setReorgBranchPoint(const BinaryData&); BinaryData getCurrentTopBlockHash(void) const; uint32_t getCurrentTopBlock(void) const; + + void init(); + void shutdown(); }; class TestArmoryConnection : public ArmoryObject diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index 927f4bf59..e6eeee680 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -29,14 +29,16 @@ using namespace BlockSettle::Terminal; constexpr auto kFutureWaitTimeout = std::chrono::seconds(5); constexpr auto kLongWaitTimeout = std::chrono::seconds(15); -void TestSettlement::mineBlocks(unsigned count) +void TestSettlement::mineBlocks(unsigned count, bool wait) { // auto curHeight = envPtr_->armoryConnection()->topBlock(); auto curHeight = envPtr_->armoryInstance()->getCurrentTopBlock(); Recipient_P2PKH coinbaseRecipient(coinbaseScrAddr_, 50 * COIN); auto&& cbMap = envPtr_->armoryInstance()->mineNewBlock(&coinbaseRecipient, count); coinbaseHashes_.insert(cbMap.begin(), cbMap.end()); -// envPtr_->blockMonitor()->waitForNewBlocks(curHeight + count); // doesn't work if armoryConnection is not ready + if (wait) { // don't use if armoryConnection is not ready + envPtr_->blockMonitor()->waitForNewBlocks(curHeight + count); + } } void TestSettlement::sendTo(uint64_t value, bs::Address& addr) @@ -68,9 +70,6 @@ void TestSettlement::sendTo(uint64_t value, bs::Address& addr) } TestSettlement::TestSettlement() -{} - -void TestSettlement::SetUp() { passphrase_ = SecureBinaryData::fromString("pass"); coinbasePubKey_ = CryptoECDSA().ComputePublicKey(coinbasePrivKey_, true); @@ -81,8 +80,11 @@ void TestSettlement::SetUp() envPtr_ = std::make_shared(StaticLogger::loggerPtr); envPtr_->requireArmory(false); - mineBlocks(101); + mineBlocks(101, false); +} +void TestSettlement::SetUp() +{ const auto logger = envPtr_->logger(); const auto amount = initialTransferAmount_ * COIN; @@ -161,7 +163,9 @@ void TestSettlement::TearDown() } TestSettlement::~TestSettlement() -{} +{ + envPtr_->armoryInstance()->shutdown(); +} TEST_F(TestSettlement, Initial_balances) { diff --git a/UnitTests/TestSettlement.h b/UnitTests/TestSettlement.h index a3726d78a..45ec4551c 100644 --- a/UnitTests/TestSettlement.h +++ b/UnitTests/TestSettlement.h @@ -51,7 +51,7 @@ class TestSettlement : public ::testing::Test void SetUp() override; void TearDown() override; - void mineBlocks(unsigned count); + void mineBlocks(unsigned count, bool wait = true); void sendTo(uint64_t value, bs::Address& addr); public: diff --git a/common b/common index c08458258..1c5a388e4 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit c08458258a6fb3729cc9f8f4c0995c170cb31433 +Subproject commit 1c5a388e4f65a1d7553006fa0f1df51462f50b14 From de757f4c30631f1aa3e80c6330876625fa2cc028 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Wed, 9 Dec 2020 17:44:51 +0300 Subject: [PATCH 068/146] Update futures ticker UI --- BlockSettleUILib/BSTerminalMainWindow.cpp | 2 + BlockSettleUILib/Stylesheet/stylesheet.css | 4 + BlockSettleUILib/Trading/FuturesTicket.cpp | 194 ++++--- BlockSettleUILib/Trading/FuturesTicket.h | 24 +- BlockSettleUILib/Trading/FuturesTicket.ui | 524 +++++++----------- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 29 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 2 + 7 files changed, 360 insertions(+), 419 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index ca7a97c79..8fe593fdf 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2259,6 +2259,8 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli connect(bsClient_.get(), &BsClient::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); connect(bsClient_.get(), &BsClient::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); + connect(bsClient_.get(), &BsClient::processPbMessage, assetManager_.get(), &AssetManager::onMessageFromPB); + utxoReservationMgr_->setFeeRatePb(result.feeRatePb); connect(bsClient_.get(), &BsClient::feeRateReceived, this, [this] (float feeRate) { utxoReservationMgr_->setFeeRatePb(feeRate); diff --git a/BlockSettleUILib/Stylesheet/stylesheet.css b/BlockSettleUILib/Stylesheet/stylesheet.css index b8a845a78..962ce682b 100644 --- a/BlockSettleUILib/Stylesheet/stylesheet.css +++ b/BlockSettleUILib/Stylesheet/stylesheet.css @@ -1696,3 +1696,7 @@ QDialog#LoginWindow QWidget#widgetSignup[testEnv="true"] { color: #5097bd; text-decoration: underline; text-transform: none;} + +#labelFuturePrice[selectedLine="true"] { + color: #ffffff; +} diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index fb056dcfb..18e230066 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -21,6 +21,7 @@ #include #include +#include #include @@ -33,6 +34,29 @@ namespace { } return UiUtils::displayPriceForAssetType(price, at); } + + const auto kAmounts = std::vector{1., 5., 10., 25.}; + + const auto kLabelCount = kAmounts.size() + 1; + + std::string getLabel(size_t i) + { + if (i < kAmounts.size()) { + return fmt::format("{:0.2f}", kAmounts.at(i)); + } else { + return fmt::format(">{:0.2f}", kAmounts.back()); + } + } + + size_t getSelectedLine(double amount) { + for (size_t i = 0; i < kAmounts.size(); ++i) { + if (amount < kAmounts[i]) { + return i; + } + } + return kAmounts.size(); + } + } @@ -42,20 +66,38 @@ FuturesTicket::FuturesTicket(QWidget* parent) { ui_->setupUi(this); - invalidBalanceFont_ = ui_->labelBalanceValue->font(); - invalidBalanceFont_.setStrikeOut(true); - xbtAmountValidator_ = new XbtAmountValidator(this); ui_->lineEditAmount->installEventFilter(this); - connect(ui_->pushButtonSell, &QPushButton::clicked, this, &FuturesTicket::onSellSelected); - connect(ui_->pushButtonBuy, &QPushButton::clicked, this, &FuturesTicket::onBuySelected); - - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &FuturesTicket::submitButtonClicked); - //connect(ui_->lineEditAmount, &QLineEdit::textEdited, this, &FuturesTicket::onAmountEdited); + connect(ui_->pushButtonBuy, &QPushButton::clicked, this, [this] { + submit(bs::network::Side::Buy); + }); + connect(ui_->pushButtonSell, &QPushButton::clicked, this, [this] { + submit(bs::network::Side::Sell); + }); + connect(ui_->lineEditAmount, &QLineEdit::textEdited, this, &FuturesTicket::onAmountEdited); + connect(ui_->pushButtonClose, &QPushButton::clicked, this, &FuturesTicket::onCloseAll); + + auto pricesLayout = new QGridLayout(ui_->widgetPrices); + pricesLayout->setSpacing(0); + pricesLayout->setContentsMargins(0, 0, 0, 0); + + labels_.resize(kLabelCount); + for (size_t i = 0; i < kLabelCount; ++i) { + auto label = new QLabel(this); + label->setText(QString::fromStdString(getLabel(i))); + pricesLayout->addWidget(label, int(i), 0, Qt::AlignCenter); + for (size_t j = 0; j < 2; ++j) { + auto label = new QLabel(this); + label->setObjectName(QStringLiteral("labelFuturePrice")); + pricesLayout->addWidget(label, int(i), int(j + 1), Qt::AlignCenter); + labels_[i][j] = label; + } + } - //disablePanel(); + ui_->helpLabel->hide(); + ui_->toolButtonMax->hide(); } FuturesTicket::~FuturesTicket() = default; @@ -64,10 +106,6 @@ void FuturesTicket::resetTicket() { ui_->labelProductGroup->setText(kEmptyInformationalLabelText); ui_->labelSecurityId->setText(kEmptyInformationalLabelText); - ui_->labelIndicativePrice->setText(kEmptyInformationalLabelText); - - currentBidPrice_ = kEmptyInformationalLabelText; - currentOfferPrice_ = kEmptyInformationalLabelText; ui_->lineEditAmount->setValidator(xbtAmountValidator_); ui_->lineEditAmount->clear(); @@ -82,7 +120,10 @@ void FuturesTicket::init(const std::shared_ptr &logger authAddressManager_ = authAddressManager; assetManager_ = assetManager; - updateSubmitButton(); + connect(assetManager_.get(), &AssetManager::netDeliverableBalanceChanged, this, &FuturesTicket::updatePanel); + connect(assetManager_.get(), &AssetManager::balanceChanged, this, &FuturesTicket::updatePanel); + + updatePanel(); } void FuturesTicket::setType(bs::network::Asset::Type type) @@ -96,17 +137,8 @@ void FuturesTicket::SetCurrencyPair(const QString& currencyPair) CurrencyPair cp(currencyPair.toStdString()); - currentProduct_ = QString::fromStdString(cp.NumCurrency()); - contraProduct_ = QString::fromStdString(cp.DenomCurrency()); + currentProduct_ = cp.NumCurrency() == bs::network::XbtCurrency ? cp.DenomCurrency() : cp.NumCurrency(); security_ = currencyPair; - - ui_->pushButtonNumCcy->setText(currentProduct_); - ui_->pushButtonNumCcy->setChecked(true); - - ui_->pushButtonDenomCcy->setText(contraProduct_); - ui_->pushButtonDenomCcy->setChecked(false); - - ui_->pushButtonDenomCcy->setEnabled(false); } void FuturesTicket::SetProductAndSide(const QString& productGroup, const QString& currencyPair @@ -118,18 +150,15 @@ void FuturesTicket::SetProductAndSide(const QString& productGroup, const QString return; } - //SetProductGroup(productGroup); + ui_->labelProductGroup->setText(productGroup); SetCurrencyPair(currencyPair); //SetCurrentIndicativePrices(bidPrice, offerPrice); - if (side == bs::network::Side::Type::Undefined) { - side = bs::network::Side::Buy; - } - - ui_->pushButtonSell->setChecked(side == bs::network::Side::Sell); - ui_->pushButtonBuy->setChecked(side == bs::network::Side::Buy); + if (side == bs::network::Side::Type::Undefined) { + side = bs::network::Side::Buy; + } - productSelectionChanged(); + productSelectionChanged(); } void FuturesTicket::setSecurityId(const QString& productGroup, const QString& currencyPair @@ -150,16 +179,7 @@ void FuturesTicket::setSecuritySell(const QString& productGroup, const QString& SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Sell); } -bs::network::Side::Type FuturesTicket::getSelectedSide() const -{ - if (ui_->pushButtonSell->isChecked()) { - return bs::network::Side::Sell; - } - - return bs::network::Side::Buy; -} - -QString FuturesTicket::getProduct() const +std::string FuturesTicket::getProduct() const { return currentProduct_; } @@ -180,29 +200,80 @@ void FuturesTicket::productSelectionChanged() const auto bidPrice = formatPrice(mdInfo.bidPrice, type_); const auto askPrice = formatPrice(mdInfo.askPrice, type_); - ui_->labelBid->setText(bidPrice); - ui_->labelAsk->setText(askPrice); + for (auto &labelPair : labels_) { + labelPair[0]->setText(bidPrice); + labelPair[1]->setText(askPrice); + } + + updatePanel(); +} + +void FuturesTicket::updatePanel() +{ + const double balance = assetManager_ ? + assetManager_->getBalance(currentProduct_, false, nullptr) : 0.0; + auto amountString = UiUtils::displayCurrencyAmount(balance); + QString text = tr("%1 %2").arg(amountString).arg(QString::fromStdString(currentProduct_)); + ui_->labelBalanceValue->setText(text); + + ui_->labelFutureBalanceValue->setText(UiUtils::displayAmount(assetManager_->netDeliverableBalanceXbt())); + + double qty = getQuantity(); + size_t selectedLine = getSelectedLine(qty); + + const auto selectedProperyName = "selectedLine"; + for (size_t lineIndex = 0; lineIndex < kLabelCount; ++lineIndex) { + const auto &labelsLine = labels_.at(lineIndex); + for (size_t labelIndex = 0; labelIndex < labelsLine.size(); ++labelIndex) { + auto label = labelsLine.at(labelIndex); + bool isSelectedOld = label->property(selectedProperyName).toBool(); + bool isSelectedNew = lineIndex == selectedLine && qty > 0; + if (isSelectedNew != isSelectedOld) { + label->setProperty(selectedProperyName, isSelectedNew); + label->style()->unpolish(label); + label->style()->polish(label); + label->update(); + } + } + } } -void FuturesTicket::updateSubmitButton() +void FuturesTicket::onCloseAll() { + auto netBalance = assetManager_->netDeliverableBalanceXbt(); + auto amount = bs::XBTAmount(static_cast(std::abs(netBalance))); + auto side = netBalance < 0 ? bs::network::Side::Buy : bs::network::Side::Sell; + sendRequest(side, amount); } bool FuturesTicket::eventFilter(QObject *watched, QEvent *evt) { if (evt->type() == QEvent::KeyPress) { - auto keyID = static_cast(evt)->key(); - if (ui_->pushButtonSubmit->isEnabled() && ((keyID == Qt::Key_Return) || (keyID == Qt::Key_Enter))) { - submitButtonClicked(); - } +// auto keyID = static_cast(evt)->key(); +// if (ui_->pushButtonSubmit->isEnabled() && ((keyID == Qt::Key_Return) || (keyID == Qt::Key_Enter))) { +// submitButtonClicked(); +// } } return QWidget::eventFilter(watched, evt); } -void FuturesTicket::submitButtonClicked() +void FuturesTicket::submit(bs::network::Side::Type side) +{ + double amount = getQuantity(); + if (amount == 0) { + return; + } + sendRequest(side, bs::XBTAmount(amount)); + ui_->lineEditAmount->clear(); +} + +void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amount) { + if (amount.GetValue() == 0) { + return; + } + // TODO: Select price depending on amount const auto &mdInfo = mdInfo_[type_][security_.toStdString()]; - auto side = getSelectedSide(); double price = 0; switch (side) { case bs::network::Side::Buy: @@ -214,20 +285,16 @@ void FuturesTicket::submitButtonClicked() default: break; } - double amount = getQuantity(); - - if (price == 0 || amount == 0) { + if (price == 0) { return; } bs::network::FutureRequest request; request.side = side; request.price = price; - request.amount = bs::XBTAmount(amount); + request.amount = amount; emit sendFutureRequestToPB(request); - - ui_->lineEditAmount->clear(); } void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) @@ -240,16 +307,7 @@ void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &sec } } -void FuturesTicket::onSellSelected() -{ - ui_->pushButtonSell->setChecked(true); - ui_->pushButtonBuy->setChecked(false); - productSelectionChanged(); -} - -void FuturesTicket::onBuySelected() +void FuturesTicket::onAmountEdited(const QString &) { - ui_->pushButtonSell->setChecked(false); - ui_->pushButtonBuy->setChecked(true); - productSelectionChanged(); + updatePanel(); } diff --git a/BlockSettleUILib/Trading/FuturesTicket.h b/BlockSettleUILib/Trading/FuturesTicket.h index 7a4e3c266..0029d0711 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.h +++ b/BlockSettleUILib/Trading/FuturesTicket.h @@ -34,6 +34,7 @@ namespace Ui { class AssetManager; class QuoteProvider; class XbtAmountValidator; +class QLabel; class FuturesTicket : public QWidget @@ -67,21 +68,20 @@ public slots: void onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields); private slots: - void onSellSelected(); - void onBuySelected(); + void onAmountEdited(const QString &); + void updatePanel(); + void onCloseAll(); private: bool eventFilter(QObject *watched, QEvent *evt) override; - bs::network::Side::Type getSelectedSide() const; - QString getProduct() const; + std::string getProduct() const; double getQuantity() const; void resetTicket(); void productSelectionChanged(); - void updateSubmitButton(); - void updateBalances(); - void submitButtonClicked(); + void submit(bs::network::Side::Type side); + void sendRequest(bs::network::Side::Type side, bs::XBTAmount amount); std::unique_ptr ui_; @@ -89,19 +89,15 @@ private slots: std::shared_ptr assetManager_; std::shared_ptr authAddressManager_; - QFont invalidBalanceFont_; - XbtAmountValidator *xbtAmountValidator_{}; bs::network::Asset::Type type_{}; - QString currentProduct_; - QString contraProduct_; + std::string currentProduct_; QString security_; - QString currentBidPrice_; - QString currentOfferPrice_; - std::map> mdInfo_; + + std::vector> labels_; }; #endif // FUTURES_TICKET_H diff --git a/BlockSettleUILib/Trading/FuturesTicket.ui b/BlockSettleUILib/Trading/FuturesTicket.ui index 4e408f46f..5b62d3e6f 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.ui +++ b/BlockSettleUILib/Trading/FuturesTicket.ui @@ -118,7 +118,7 @@ - 10 + 5 0 @@ -259,59 +259,6 @@
- - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 16777215 - - - - Indicative Price - - - - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - -
@@ -329,22 +276,7 @@ - - - - - bid - - - - - - - ask - - - - + @@ -370,252 +302,6 @@ 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Product - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 60 - 30 - - - - - 60 - 30 - - - - --- - - - true - - - true - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - --- - - - true - - - true - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 1 - - - 0 - - - 0 - - - 0 - - - - - Side - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Buy - - - true - - - true - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Sell - - - true - - - true - - - - - - - - - - - - @@ -724,6 +410,71 @@ + + + + + + + CLOSE ALL OPEN POSITIONS + + + + + + + Market + + + true + + + + + + + false + + + Limit + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + BUY + + + + + + + SELL + + + + + + + + + @@ -759,10 +510,51 @@ 10 - 10 + 0 + + Initial Margin + + + + + + + font-weight: bold; + + + --- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + 10 + + + 0 + + + 10 + + + 0 + + + Balance @@ -787,6 +579,88 @@ + + + + + 10 + + + 0 + + + 10 + + + 0 + + + + + Net Deliverable Balance + + + + + + + font-weight: bold; + + + xxx + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + 10 + + + 0 + + + 10 + + + 5 + + + + + Profit/Loss + + + + + + + font-weight: bold; + + + --- + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + @@ -852,22 +726,6 @@ 5 - - - - - 0 - 35 - - - - - - - SUBMIT QUOTE REQUEST - - - diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 9ba4f2674..52995e063 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -16,21 +16,22 @@ #include "ApplicationSettings.h" #include "AuthAddressManager.h" #include "AutoSignQuoteProvider.h" +#include "BSMessageBox.h" #include "CelerClient.h" #include "CurrencyPair.h" #include "DialogManager.h" +#include "MDCallbacksQt.h" #include "NotificationCenter.h" #include "OrderListModel.h" #include "OrdersView.h" #include "QuoteProvider.h" #include "RFQDialog.h" #include "RfqStorage.h" -#include "WalletSignerContainer.h" -#include "Wallets/SyncWalletsManager.h" -#include "Wallets/SyncHDWallet.h" #include "UserScriptRunner.h" #include "UtxoReservationManager.h" -#include "MDCallbacksQt.h" +#include "WalletSignerContainer.h" +#include "Wallets/SyncHDWallet.h" +#include "Wallets/SyncWalletsManager.h" #include "bs_proxy_terminal_pb.pb.h" @@ -381,6 +382,21 @@ void RFQRequestWidget::deleteDialog(const std::string &rfqId) dialogs_.erase(itDlg); } +void RFQRequestWidget::processFutureResponse(const ProxyTerminalPb::Response_FutureResponse &msg) +{ + QMetaObject::invokeMethod(this, [this, msg] { + if (!msg.success()) { + BSMessageBox errorMessage(BSMessageBox::critical, tr("Error") + , tr("Request failed: %1").arg(QString::fromStdString(msg.error_msg())), this); + errorMessage.exec(); + return; + } + auto details = tr("Price: %1").arg(UiUtils::displayPriceXBT(msg.price())); + BSMessageBox errorMessage(BSMessageBox::info, tr("Success"), tr("Order succeed"), details, this); + errorMessage.exec(); + }); +} + bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) { ui_->stackedWidgetRFQ->setEnabled(true); @@ -521,6 +537,11 @@ void RFQRequestWidget::onMessageFromPB(const Blocksettle::Communication::ProxyTe break; } + case Blocksettle::Communication::ProxyTerminalPb::Response::kFutureResponse: { + processFutureResponse(response.future_response()); + break; + } + default: break; } diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 8e73dcc75..635f70626 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -37,6 +37,7 @@ namespace Blocksettle { namespace Communication { namespace ProxyTerminalPb { class Response; + class Response_FutureResponse; } } } @@ -122,6 +123,7 @@ Q_OBJECT , bs::UtxoReservationToken ccUtxoRes); void onRFQCancel(const std::string &rfqId); void deleteDialog(const std::string &rfqId); + void processFutureResponse(const Blocksettle::Communication::ProxyTerminalPb::Response_FutureResponse &msg); public slots: void onCurrencySelected(const MarketSelectedInfo& selectedInfo); From 5579308d7e841497fd1a1ba7ff84194eede0dab2 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 10 Dec 2020 18:43:42 +0300 Subject: [PATCH 069/146] Wallet actions in signer --- .../RootWalletPropertiesDialog.cpp | 36 ++++++++++++++----- .../RootWalletPropertiesDialog.h | 1 + BlockSettleUILib/WalletsWidget.cpp | 1 + BlockSettleUILib/WalletsWidget.h | 1 + Core/SignerAdapter.cpp | 13 +++++++ Core/SignerAdapter.h | 3 ++ GUI/QtWidgets/MainWindow.cpp | 1 + GUI/QtWidgets/MainWindow.h | 1 + GUI/QtWidgets/QtGuiAdapter.cpp | 14 ++++++++ GUI/QtWidgets/QtGuiAdapter.h | 1 + common | 2 +- 11 files changed, 64 insertions(+), 10 deletions(-) diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 1b6143d0e..645a11673 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -163,23 +163,41 @@ RootWalletPropertiesDialog::~RootWalletPropertiesDialog() = default; void RootWalletPropertiesDialog::onDeleteWallet() { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::DeleteWallet - , {{ QLatin1String("rootId"), walletInfo_.rootId() }}); - close(); + if (signingContainer_) { + signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::DeleteWallet + , { { QLatin1String("rootId"), walletInfo_.rootId() } }); + close(); + } + else { + emit needWalletDialog(bs::signer::ui::GeneralDialogType::DeleteWallet + , walletInfo_.rootId().toStdString()); + } } void RootWalletPropertiesDialog::onBackupWallet() { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::BackupWallet - , {{ QLatin1String("rootId"), walletInfo_.rootId() }}); - close(); + if (signingContainer_) { + signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::BackupWallet + , { { QLatin1String("rootId"), walletInfo_.rootId() } }); + close(); + } + else { + emit needWalletDialog(bs::signer::ui::GeneralDialogType::BackupWallet + , walletInfo_.rootId().toStdString()); + } } void RootWalletPropertiesDialog::onChangePassword() { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ManageWallet - , {{ QLatin1String("rootId"), walletInfo_.rootId() }}); - close(); + if (signingContainer_) { + signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ManageWallet + , { { QLatin1String("rootId"), walletInfo_.rootId() } }); + close(); + } + else { + emit needWalletDialog(bs::signer::ui::GeneralDialogType::ManageWallet + , walletInfo_.rootId().toStdString()); + } } static inline QString encTypeToString(bs::wallet::EncryptionType enc) diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 3de1f6e79..96465704b 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -68,6 +68,7 @@ Q_OBJECT void needWalletBalances(const std::string &walletId); void needUTXOs(const std::string& id, const std::string& walletId , bool confOnly = false, bool swOnly = false); + void needWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); private slots: void onDeleteWallet(); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 34e9c290b..77e15677a 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -440,6 +440,7 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) connect(rootDlg_, &RootWalletPropertiesDialog::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); connect(rootDlg_, &RootWalletPropertiesDialog::needWalletBalances, this, &WalletsWidget::needWalletBalances); connect(rootDlg_, &RootWalletPropertiesDialog::needUTXOs, this, &WalletsWidget::needUTXOs); + connect(rootDlg_, &RootWalletPropertiesDialog::needWalletDialog, this, &WalletsWidget::needWalletDialog); connect(rootDlg_, &QDialog::finished, [this](int) { rootDlg_->deleteLater(); rootDlg_ = nullptr; diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index b25a11ba1..0f8ecf3b5 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -125,6 +125,7 @@ public slots: void needLedgerEntries(const std::string &filter); void needTXDetails(const std::vector &, bool useCache , const bs::Address &); + void needWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); private slots: void showWalletProperties(const QModelIndex& index); diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 0f85bf531..7d2cd2d5e 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -143,6 +143,8 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processSignSettlementTx(env, request.sign_settlement_tx()); case SignerMessage::kAutoSign: return processAutoSignRequest(env, request.auto_sign()); + case SignerMessage::kDialogRequest: + return processDialogRequest(env, request.dialog_request()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -797,3 +799,14 @@ bool SignerAdapter::processAutoSignRequest(const bs::message::Envelope& env return (signer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ActivateAutoSign , data) != 0); } + +bool SignerAdapter::processDialogRequest(const bs::message::Envelope& + , const SignerMessage_DialogRequest& request) +{ + const auto& dlgType = static_cast(request.dialog_type()); + QVariantMap data; + for (const auto& d : request.data()) { + data[QString::fromStdString(d.key())] = QString::fromStdString(d.value()); + } + return (signer_->customDialogRequest(dlgType, data) != 0); +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 6230ef882..0c986e432 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -22,6 +22,7 @@ namespace BlockSettle { namespace Common { class SignerMessage; class SignerMessage_AutoSign; + class SignerMessage_DialogRequest; class SignerMessage_ExtendAddrChain; class SignerMessage_GetSettlPayinAddr; class SignerMessage_SetSettlementId; @@ -106,6 +107,8 @@ class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget , const bs::core::wallet::TXSignRequest&); bool processAutoSignRequest(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_AutoSign&); + bool processDialogRequest(const bs::message::Envelope& + , const BlockSettle::Common::SignerMessage_DialogRequest&); private: std::shared_ptr logger_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index f36affc24..091a9fca1 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -1253,6 +1253,7 @@ void MainWindow::initWidgets() connect(ui_->widgetWallets, &WalletsWidget::setAddrComment, this, &MainWindow::setAddrComment); connect(ui_->widgetWallets, &WalletsWidget::needLedgerEntries, this, &MainWindow::needLedgerEntries); connect(ui_->widgetWallets, &WalletsWidget::needTXDetails, this, &MainWindow::needTXDetails); + connect(ui_->widgetWallets, &WalletsWidget::needWalletDialog, this, &MainWindow::needWalletDialog); connect(ui_->widgetExplorer, &ExplorerWidget::needAddressHistory, this, &MainWindow::needAddressHistory); connect(ui_->widgetExplorer, &ExplorerWidget::needTXDetails, this, &MainWindow::needTXDetails); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 5bd278790..fd93c14a2 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -159,6 +159,7 @@ namespace bs { void needWalletsList(UiUtils::WalletsTypes, const std::string &id); void needWalletBalances(const std::string &walletId); void needWalletData(const std::string& walletId); + void needWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 65e29ae55..eef76af88 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -760,6 +760,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needUnreserveUTXOs, this, &QtGuiAdapter::onNeedUnreserveUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::submitQuote, this, &QtGuiAdapter::onSubmitQuote); connect(mainWindow_, &bs::gui::qt::MainWindow::pullQuote, this, &QtGuiAdapter::onPullQuote); + connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletDialog, this, &QtGuiAdapter::onNeedWalletDialog); } void QtGuiAdapter::onGetSettings(const std::vector& settings) @@ -1260,6 +1261,19 @@ void QtGuiAdapter::onPullQuote(const std::string& settlementId pushFill(env); } +void QtGuiAdapter::onNeedWalletDialog(bs::signer::ui::GeneralDialogType dlgType + , const std::string& rootId) +{ + SignerMessage msg; + auto msgReq = msg.mutable_dialog_request(); + msgReq->set_dialog_type((int)dlgType); + auto msgData = msgReq->add_data(); + msgData->set_key("rootId"); + msgData->set_value(rootId); + Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) { hdWallets_[*wi.ids.cbegin()] = wi; diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 0f0bb9200..3504a9f2a 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -204,6 +204,7 @@ private slots: void onSubmitQuote(const bs::network::QuoteNotification&); void onPullQuote(const std::string& settlementId, const std::string& reqId , const std::string& reqSessToken); + void onNeedWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); private: std::shared_ptr logger_; diff --git a/common b/common index 1c5a388e4..717dcdd01 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1c5a388e4f65a1d7553006fa0f1df51462f50b14 +Subproject commit 717dcdd016c68cd6d956345a6728636e2bbc073d From 28204b418865514f78bb7c962062406f07eecdd0 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 11 Dec 2020 18:02:02 +0300 Subject: [PATCH 070/146] Wallet backup with new Armory code --- BlockSettleSigner/WalletsProxy.cpp | 10 ++++++++-- .../ManageEncryption/RootWalletPropertiesDialog.h | 1 + BlockSettleUILib/WalletsWidget.h | 1 + common | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/BlockSettleSigner/WalletsProxy.cpp b/BlockSettleSigner/WalletsProxy.cpp index 98a707e4e..f830a5bf5 100644 --- a/BlockSettleSigner/WalletsProxy.cpp +++ b/BlockSettleSigner/WalletsProxy.cpp @@ -19,7 +19,7 @@ #include #include - +#include "ArmoryBackups.h" #include "CoreHDWallet.h" #include "PaperBackupWriter.h" #include "SignerAdapter.h" @@ -289,8 +289,14 @@ bool WalletsProxy::backupPrivateKey(const QString &walletId, QString fileName, b if (!wallet) { throw std::runtime_error("failed to find wallet with id " + walletId.toStdString()); } - seedData = bs::core::wallet::Seed(chainCode, wallet->networkType()).toEasyCodeChecksum(); privKeyString = privKey.toBinStr(); + const auto &easy16data = ArmoryBackups::BackupEasy16::encode(chainCode + , (uint8_t)ArmoryBackups::BackupType::BIP32_Seed_Structured); + if (easy16data.size() != 2) { + throw std::runtime_error("failed to encode wallet " + walletId.toStdString() + " seed"); + } + seedData.part1 = easy16data.at(0); + seedData.part2 = easy16data.at(1); } catch (const std::exception &e) { logger_->error("[WalletsProxy] failed to encode private key: {}", e.what()); const auto errText = tr("Failed to encode private key for wallet %1").arg(walletId); diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 96465704b..b209a2140 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -15,6 +15,7 @@ #include #include "BinaryData.h" #include "SignerDefs.h" +#include "SignerUiDefs.h" #include "QWalletInfo.h" namespace Ui { diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 0f8ecf3b5..9186a29cf 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -17,6 +17,7 @@ #include #include "Address.h" #include "SignerDefs.h" +#include "SignerUiDefs.h" #include "TabWithShortcut.h" #include "BSErrorCode.h" #include "BSErrorCodeStrings.h" diff --git a/common b/common index 717dcdd01..3c69a4a16 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 717dcdd016c68cd6d956345a6728636e2bbc073d +Subproject commit 3c69a4a163c2076ce008b2aa7bb6dbcc610d21f2 From 63ee6a4781d7d22ee6ba222aa41c6da50163ba1b Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 11 Dec 2020 17:53:32 +0200 Subject: [PATCH 071/146] Update ZMQ --- CMakeLists.txt | 8 ++++---- common | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d0d0a6020..7ef28f934 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -211,9 +211,9 @@ INCLUDE_DIRECTORIES( ${ZEROMQ_INCLUDE_DIR} ) SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${ZEROMQ_ROOT}/lib) IF( WIN32 ) IF(CMAKE_BUILD_TYPE STREQUAL "Debug") - SET( ZMQ_LIB_NAME "libzmq-v142-mt-gd-4_3_2" ) + SET( ZMQ_LIB_NAME "libzmq-v142-mt-gd-4_3_3" ) ELSE() - SET( ZMQ_LIB_NAME "libzmq-v142-mt-4_3_2" ) + SET( ZMQ_LIB_NAME "libzmq-v142-mt-4_3_3" ) ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") ELSE () IF(BSTERMINAL_SHARED_LIBS) @@ -227,9 +227,9 @@ FIND_LIBRARY(ZMQ_LIB NAMES ${ZMQ_LIB_NAME} NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_E IF( NOT ZMQ_LIB ) IF( WIN32 ) IF(CMAKE_BUILD_TYPE STREQUAL "Debug") - SET( ZMQ_LIB_NAME "libzmq-v141-mt-gd-4_3_2" ) + SET( ZMQ_LIB_NAME "libzmq-v141-mt-gd-4_3_3" ) ELSE() - SET( ZMQ_LIB_NAME "libzmq-v141-mt-4_3_2" ) + SET( ZMQ_LIB_NAME "libzmq-v141-mt-4_3_3" ) ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") ENDIF( WIN32 ) FIND_LIBRARY(ZMQ_LIB NAMES ${ZMQ_LIB_NAME} NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) diff --git a/common b/common index 3667ebfec..f69a20fc7 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 3667ebfec68c0553b6680f0e5ef29a715fe9949a +Subproject commit f69a20fc70dc8eadcb10c86040428b1d5488500b From ddd44d244c1f5579f1d3b2177f705c7f9eb3a595 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Mon, 14 Dec 2020 13:54:00 +0300 Subject: [PATCH 072/146] Use correct futures price depending on volume --- BlockSettleUILib/Trading/FuturesTicket.cpp | 58 +++++++++++++++++----- BlockSettleUILib/Trading/FuturesTicket.h | 2 +- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 18e230066..643a55009 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -28,6 +28,9 @@ namespace { const QString kEmptyInformationalLabelText = QStringLiteral("--"); + const auto kInfValueStr = QStringLiteral("inf"); + const auto kInfValueAmount = bs::XBTAmount(std::numeric_limits::max()); + QString formatPrice(double price, bs::network::Asset::Type at) { if (price == 0) { return kEmptyInformationalLabelText; @@ -48,6 +51,13 @@ namespace { } } + bs::XBTAmount getAmount(size_t i) { + if (i >= kAmounts.size()) { + return kInfValueAmount; + } + return bs::XBTAmount(kAmounts[i]); + } + size_t getSelectedLine(double amount) { for (size_t i = 0; i < kAmounts.size(); ++i) { if (amount < kAmounts[i]) { @@ -195,14 +205,20 @@ double FuturesTicket::getQuantity() const void FuturesTicket::productSelectionChanged() { - const auto &mdInfo = mdInfo_[type_][security_.toStdString()]; - - const auto bidPrice = formatPrice(mdInfo.bidPrice, type_); - const auto askPrice = formatPrice(mdInfo.askPrice, type_); - - for (auto &labelPair : labels_) { - labelPair[0]->setText(bidPrice); - labelPair[1]->setText(askPrice); + const auto &mdInfos = mdInfo_[type_][security_.toStdString()]; + + for (size_t labelIndex = 0; labelIndex < labels_.size(); ++labelIndex) { + auto labelPair = labels_[labelIndex]; + auto mdInfoIt = mdInfos.find(getAmount(labelIndex)); + if (mdInfoIt != mdInfos.end()) { + const auto bidPrice = formatPrice(mdInfoIt->second.bidPrice, type_); + const auto askPrice = formatPrice(mdInfoIt->second.askPrice, type_); + labelPair[0]->setText(bidPrice); + labelPair[1]->setText(askPrice); + } else { + labelPair[0]->setText(kEmptyInformationalLabelText); + labelPair[1]->setText(kEmptyInformationalLabelText); + } } updatePanel(); @@ -272,8 +288,16 @@ void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amou if (amount.GetValue() == 0) { return; } - // TODO: Select price depending on amount - const auto &mdInfo = mdInfo_[type_][security_.toStdString()]; + + const auto &mdInfos = mdInfo_[type_][security_.toStdString()]; + // Use upper_bound to get correct category + const auto mdInfoIt = mdInfos.upper_bound(amount); + if (mdInfoIt == mdInfos.end()) { + SPDLOG_LOGGER_ERROR(logger_, "can't find correct MD category"); + return; + } + const auto &mdInfo = mdInfoIt->second; + double price = 0; switch (side) { case bs::network::Side::Buy: @@ -299,8 +323,18 @@ void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amou void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) { - auto &mdInfo = mdInfo_[type][security.toStdString()]; - mdInfo.merge(bs::network::MDField::get(mdFields)); + auto &mdInfos = mdInfo_[type][security.toStdString()]; + mdInfos.clear(); + for (const auto &field : mdFields) { + bs::XBTAmount amount; + if (field.desc == kInfValueStr) { + amount = kInfValueAmount; + } else { + amount = bs::XBTAmount(field.desc.toDouble()); + } + auto &mdInfo = mdInfos[amount]; + mdInfo.merge(bs::network::MDField::get(mdFields)); + } if (type == type_) { productSelectionChanged(); diff --git a/BlockSettleUILib/Trading/FuturesTicket.h b/BlockSettleUILib/Trading/FuturesTicket.h index 0029d0711..41bd56884 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.h +++ b/BlockSettleUILib/Trading/FuturesTicket.h @@ -95,7 +95,7 @@ private slots: std::string currentProduct_; QString security_; - std::map> mdInfo_; + std::map>> mdInfo_; std::vector> labels_; }; From 48badaecd2038e5186d28923b826b06dacf37a63 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Mon, 14 Dec 2020 17:56:26 +0300 Subject: [PATCH 073/146] Rename MDField::desc --- BlockSettleUILib/Trading/FuturesTicket.cpp | 4 ++-- BlockSettleUILib/Trading/MarketDataModel.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 643a55009..dd289705e 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -327,10 +327,10 @@ void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &sec mdInfos.clear(); for (const auto &field : mdFields) { bs::XBTAmount amount; - if (field.desc == kInfValueStr) { + if (field.levelQuantity == kInfValueStr) { amount = kInfValueAmount; } else { - amount = bs::XBTAmount(field.desc.toDouble()); + amount = bs::XBTAmount(field.levelQuantity.toDouble()); } auto &mdInfo = mdInfos[amount]; mdInfo.merge(bs::network::MDField::get(mdFields)); diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index f490ecb6b..1203d5f62 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -138,7 +138,7 @@ static void FieldsToMap(bs::network::Asset::Type at, const bs::network::MDFields map[MarketDataModel::MarketDataColumns::DailyVol] = { getVolumeString(field.value, at), field.value }; break; case bs::network::MDField::Reject: - map[MarketDataModel::MarketDataColumns::ColumnsCount] = { field.desc, 0 }; + map[MarketDataModel::MarketDataColumns::ColumnsCount] = { field.levelQuantity, 0 }; break; default: break; } From e1180be261aa1f86d093d3526d09f40c85edf02f Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 14 Dec 2020 19:28:51 +0200 Subject: [PATCH 074/146] Fix display for price book --- BlockSettleUILib/Trading/FuturesTicket.cpp | 18 +++++++++++++++++- BlockSettleUILib/Trading/MarketDataModel.h | 2 +- common | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index dd289705e..e38036e47 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -323,6 +323,10 @@ void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amou void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) { + if (type != bs::network::Asset::Futures && type != bs::network::Asset::CashSettledFutures) { + return; + } + auto &mdInfos = mdInfo_[type][security.toStdString()]; mdInfos.clear(); for (const auto &field : mdFields) { @@ -330,10 +334,22 @@ void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &sec if (field.levelQuantity == kInfValueStr) { amount = kInfValueAmount; } else { + double dValue = field.levelQuantity.toDouble(); amount = bs::XBTAmount(field.levelQuantity.toDouble()); } auto &mdInfo = mdInfos[amount]; - mdInfo.merge(bs::network::MDField::get(mdFields)); + + switch (field.type) { + case bs::network::MDField::PriceBid: + mdInfo.bidPrice = field.value; + break; + case bs::network::MDField::PriceOffer: + mdInfo.askPrice = field.value; + break; + case bs::network::MDField::PriceLast: + mdInfo.lastPrice = field.value; + break; + } } if (type == type_) { diff --git a/BlockSettleUILib/Trading/MarketDataModel.h b/BlockSettleUILib/Trading/MarketDataModel.h index a3bfd4867..23b99b766 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.h +++ b/BlockSettleUILib/Trading/MarketDataModel.h @@ -90,7 +90,7 @@ private slots: QList row; }; typedef std::map PriceByCol; - typedef std::map PriceUpdates; + typedef std::map PriceUpdates; std::set instrVisible_; PriceUpdates priceUpdates_; diff --git a/common b/common index f69a20fc7..b740b24e7 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit f69a20fc70dc8eadcb10c86040428b1d5488500b +Subproject commit b740b24e71c4843b2979b9fee63f1ee1e1deb00e From 9f98c00546266be706b869f67309f8ecff1c7918 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 14 Dec 2020 21:58:55 +0300 Subject: [PATCH 075/146] Wallet import and address creation --- BlockSettleSigner/EasyEncValidator.cpp | 6 +- BlockSettleSigner/EasyEncValidator.h | 16 +- BlockSettleSigner/QmlFactory.h | 38 ++-- BlockSettleUILib/AddressListModel.cpp | 24 ++- BlockSettleUILib/AddressListModel.h | 2 +- BlockSettleUILib/CreateTransactionDialog.h | 2 +- .../CreateTransactionDialogAdvanced.cpp | 5 +- .../CreateTransactionDialogAdvanced.h | 2 +- .../RootWalletPropertiesDialog.cpp | 4 +- .../RootWalletPropertiesDialog.h | 4 +- BlockSettleUILib/NewAddressDialog.cpp | 27 ++- BlockSettleUILib/NewAddressDialog.h | 9 +- BlockSettleUILib/NewWalletDialog.cpp | 68 ++++++-- BlockSettleUILib/NewWalletDialog.h | 8 +- BlockSettleUILib/SelectAddressDialog.cpp | 5 +- BlockSettleUILib/SelectAddressDialog.h | 2 +- BlockSettleUILib/SelectWalletDialog.cpp | 44 ++++- BlockSettleUILib/SelectWalletDialog.h | 14 +- BlockSettleUILib/WalletsViewModel.cpp | 12 +- BlockSettleUILib/WalletsWidget.cpp | 163 +++++++++++++++--- BlockSettleUILib/WalletsWidget.h | 14 +- GUI/QtWidgets/MainWindow.cpp | 39 +---- GUI/QtWidgets/MainWindow.h | 3 +- GUI/QtWidgets/QtGuiAdapter.cpp | 21 ++- GUI/QtWidgets/QtGuiAdapter.h | 1 + common | 2 +- 26 files changed, 387 insertions(+), 148 deletions(-) diff --git a/BlockSettleSigner/EasyEncValidator.cpp b/BlockSettleSigner/EasyEncValidator.cpp index c22e10ed3..8489673b1 100644 --- a/BlockSettleSigner/EasyEncValidator.cpp +++ b/BlockSettleSigner/EasyEncValidator.cpp @@ -25,19 +25,19 @@ QValidator::State EasyEncValidator::validate(QString &input, int &pos) const QString tmpInput = input.trimmed().toLower().remove(QChar::Space); QString newInput; int newPos = 0; - const auto &allowedChars = codec_->allowedChars(); +// const auto &allowedChars = codec_->allowedChars(); for (int i = 0; i < tmpInput.length(); i++) { if ( i && i % wordSize_ == 0) { newInput.append(QChar::Space); ++newPos; } const char c = tmpInput.at(i).toLatin1(); - if ((allowedChars.find(c) == allowedChars.end())) { +/* if ((allowedChars.find(c) == allowedChars.end())) { input = newInput; pos = newPos; setStatusMsg(invalidMsgTmpl_.arg(name_)); return QValidator::State::Invalid; - } + }*/ newInput.append(QChar::fromLatin1(c)); ++newPos; } diff --git a/BlockSettleSigner/EasyEncValidator.h b/BlockSettleSigner/EasyEncValidator.h index 751e96405..4f3219a13 100644 --- a/BlockSettleSigner/EasyEncValidator.h +++ b/BlockSettleSigner/EasyEncValidator.h @@ -21,7 +21,6 @@ class EasyEncValidator : public QValidator { - Q_OBJECT Q_PROPERTY(QString statusMsg READ getStatusMsg NOTIFY statusMsgChanged) Q_PROPERTY(QString name READ getName WRITE setName) @@ -35,21 +34,11 @@ class EasyEncValidator : public QValidator }; //contructor used by the QmlEngine - explicit EasyEncValidator(QObject *parent = nullptr) : + EasyEncValidator(QObject *parent = nullptr) : QValidator(parent), wordSize_(4), numWords_(9), - hasChecksum_(true), - codec_(std::make_shared()) - {} - - EasyEncValidator(const std::shared_ptr &codec, QObject *parent = nullptr - , size_t numWords = 8, bool hasChecksum = false, size_t wordSize = 4) - : QValidator(parent) - , wordSize_(wordSize) - , numWords_(numWords) - , hasChecksum_(hasChecksum) - , codec_(codec) + hasChecksum_(false) {} State validate(QString &input, int &pos) const override; @@ -78,7 +67,6 @@ class EasyEncValidator : public QValidator const size_t wordSize_; const size_t numWords_; const bool hasChecksum_; - std::shared_ptr codec_; mutable QString statusMsg_; QString name_ = QString::fromStdString("Line"); diff --git a/BlockSettleSigner/QmlFactory.h b/BlockSettleSigner/QmlFactory.h index d150b1336..111d2a309 100644 --- a/BlockSettleSigner/QmlFactory.h +++ b/BlockSettleSigner/QmlFactory.h @@ -63,43 +63,52 @@ class QmlFactory : public QObject void setWalletsManager(const std::shared_ptr &); - Q_INVOKABLE bs::wallet::QPasswordData *createPasswordData() { + Q_INVOKABLE bs::wallet::QPasswordData *createPasswordData() + { auto pd = new bs::wallet::QPasswordData(); QQmlEngine::setObjectOwnership(pd, QQmlEngine::JavaScriptOwnership); return pd; } // QSeed - Q_INVOKABLE bs::wallet::QSeed *createSeed(bool isTestNet){ + Q_INVOKABLE bs::wallet::QSeed *createSeed(bool isTestNet) + { auto seed = new bs::wallet::QSeed(isTestNet); QQmlEngine::setObjectOwnership(seed, QQmlEngine::JavaScriptOwnership); return seed; } - Q_INVOKABLE bs::wallet::QSeed *createSeedFromPaperBackup(const QString &key, bs::wallet::QSeed::QNetworkType netType) { + Q_INVOKABLE bs::wallet::QSeed *createSeedFromPaperBackup(const QString &key + , bs::wallet::QSeed::QNetworkType netType) + { auto seed = new bs::wallet::QSeed(bs::wallet::QSeed::fromPaperKey(key, netType)); QQmlEngine::setObjectOwnership(seed, QQmlEngine::JavaScriptOwnership); return seed; } - Q_INVOKABLE bs::wallet::QSeed *createSeedFromPaperBackupT(const QString &key, bool isTestNet) { + Q_INVOKABLE bs::wallet::QSeed *createSeedFromPaperBackupT(const QString &key, bool isTestNet) + { auto seed = new bs::wallet::QSeed(bs::wallet::QSeed::fromPaperKey(key - , isTestNet ? bs::wallet::QSeed::QNetworkType::TestNet : bs::wallet::QSeed::QNetworkType::MainNet)); + , isTestNet ? bs::wallet::QSeed::QNetworkType::TestNet : bs::wallet::QSeed::QNetworkType::MainNet)); QQmlEngine::setObjectOwnership(seed, QQmlEngine::JavaScriptOwnership); return seed; } Q_INVOKABLE bs::wallet::QSeed *createSeedFromMnemonic(const QString &key, bool isTestNet); - Q_INVOKABLE bs::wallet::QSeed *createSeedFromDigitalBackup(const QString &filename, bs::wallet::QSeed::QNetworkType netType) { + Q_INVOKABLE bs::wallet::QSeed *createSeedFromDigitalBackup(const QString &filename + , bs::wallet::QSeed::QNetworkType netType) + { auto seed = new bs::wallet::QSeed(bs::wallet::QSeed::fromDigitalBackup(filename, netType)); QQmlEngine::setObjectOwnership(seed, QQmlEngine::JavaScriptOwnership); return seed; } - Q_INVOKABLE bs::wallet::QSeed *createSeedFromDigitalBackupT(const QString &filename, bool isTestNet) { + Q_INVOKABLE bs::wallet::QSeed *createSeedFromDigitalBackupT(const QString &filename + , bool isTestNet) + { auto seed = new bs::wallet::QSeed(bs::wallet::QSeed::fromDigitalBackup(filename - , isTestNet ? bs::wallet::QSeed::QNetworkType::TestNet : bs::wallet::QSeed::QNetworkType::MainNet)); + , isTestNet ? bs::wallet::QSeed::QNetworkType::TestNet : bs::wallet::QSeed::QNetworkType::MainNet)); QQmlEngine::setObjectOwnership(seed, QQmlEngine::JavaScriptOwnership); return seed; } @@ -117,18 +126,21 @@ class QmlFactory : public QObject // Auth // used for signing Q_INVOKABLE AuthSignWalletObject *createAutheIDSignObject(AutheIDClient::RequestType requestType - , bs::hd::WalletInfo *walletInfo, const QString &authEidMessage, int expiration = AutheIDClient::kDefaultExpiration, int timestamp = 0); + , bs::hd::WalletInfo *walletInfo, const QString &authEidMessage + , int expiration = AutheIDClient::kDefaultExpiration, int timestamp = 0); // used for add eID - Q_INVOKABLE AuthSignWalletObject *createActivateEidObject(const QString &walletId, const QString &authEidMessage, QJSValue callback); + Q_INVOKABLE AuthSignWalletObject *createActivateEidObject(const QString &walletId + , const QString &authEidMessage, QJSValue callback); // used for add new eID device - Q_INVOKABLE AuthSignWalletObject *createAddEidObject(bs::hd::WalletInfo *walletInfo, const QString &authEidMessage, QJSValue callback); + Q_INVOKABLE AuthSignWalletObject *createAddEidObject(bs::hd::WalletInfo *walletInfo + , const QString &authEidMessage, QJSValue callback); // used for remove eID device // index: is encKeys index which should be deleted - Q_INVOKABLE AuthSignWalletObject *createRemoveEidObject(int index, bs::hd::WalletInfo *walletInfo - , const QString &authEidMessage); + Q_INVOKABLE AuthSignWalletObject *createRemoveEidObject(int index + , bs::hd::WalletInfo *walletInfo, const QString &authEidMessage); QString headlessPubKey() const; diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index 20483f59d..979926f65 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -199,12 +199,26 @@ void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) } } -void AddressListModel::onAddresses(const std::vector &addrs) +void AddressListModel::onAddresses(const std::string & + , const std::vector &addrs) { + if (addrs.empty()) { //TODO: check against walletId (first arg) + return; + } std::string mainWalletId; + std::vector newAddrs; + for (const auto& addr : addrs) { + const auto& itAddr = std::find_if(addressRows_.cbegin(), addressRows_.cend() + , [addr](const AddressRow& row) { + return (addr.address == row.address); + }); + if (itAddr == addressRows_.end()) { + newAddrs.push_back(addr); + } + } beginInsertRows(QModelIndex(), addressRows_.size() - , addressRows_.size() + addrs.size() - 1); - for (const auto &addr : addrs) { + , addressRows_.size() + newAddrs.size() - 1); + for (const auto &addr : newAddrs) { const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() , [walletId = addr.walletId](const bs::sync::WalletInfo &wi){ for (const auto &id : wi.ids) { @@ -240,13 +254,13 @@ void AddressListModel::onAddresses(const std::vector &addrs) std::vector addrsReq; addrsReq.reserve(addrs.size()); - for (const auto &addr : addrs) { + for (const auto &addr : newAddrs) { addrsReq.push_back(addr.address); } emit needAddrComments(mainWalletId, addrsReq); } -void AddressListModel::onAddressComments(const std::string &walletId +void AddressListModel::onAddressComments(const std::string & , const std::map &comments) { for (const auto &comm : comments) { diff --git a/BlockSettleUILib/AddressListModel.h b/BlockSettleUILib/AddressListModel.h index b2369b118..a07321db1 100644 --- a/BlockSettleUILib/AddressListModel.h +++ b/BlockSettleUILib/AddressListModel.h @@ -100,7 +100,7 @@ class AddressListModel : public QAbstractTableModel QVariant headerData(int section, Qt::Orientation orientation, int role) const override; void setWallets(const Wallets &, bool force, bool filterBtcOnly); - void onAddresses(const std::vector &); + void onAddresses(const std::string &walletId, const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); void onAddressBalances(const std::string &walletId diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index c76a77357..0636de562 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -77,7 +77,7 @@ Q_OBJECT virtual bool switchModeRequested() const= 0; virtual std::shared_ptr SwitchMode() = 0; - virtual void onAddresses(const std::vector&) {} + virtual void onAddresses(const std::string &walletId, const std::vector&) {} virtual void onAddressComments(const std::string& walletId , const std::map&) {} virtual void onAddressBalances(const std::string& walletId diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index 400471a28..d0080caf6 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -1537,10 +1537,11 @@ void CreateTransactionDialogAdvanced::onNewAddressSelectedForChange() showExistingChangeAddress(false); } -void CreateTransactionDialogAdvanced::onAddresses(const std::vector& addrs) +void CreateTransactionDialogAdvanced::onAddresses(const std::string &walletId + , const std::vector& addrs) { if (selChangeAddrDlg_) { - selChangeAddrDlg_->onAddresses(addrs); + selChangeAddrDlg_->onAddresses(walletId, addrs); } } diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index ea71fd215..bd90ec84f 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -94,7 +94,7 @@ Q_OBJECT protected: bool eventFilter(QObject *watched, QEvent *) override; - void onAddresses(const std::vector&) override; + void onAddresses(const std::string& walletId, const std::vector&) override; void onAddressComments(const std::string& walletId , const std::map&) override; void onAddressBalances(const std::string& walletId diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 645a11673..37395d851 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -307,7 +307,7 @@ void RootWalletPropertiesDialog::updateWalletDetails(const bs::sync::WalletInfo } } -void RootWalletPropertiesDialog::onHDWalletDetails() +void RootWalletPropertiesDialog::onHDWalletDetails(const bs::sync::HDWalletData& hdWallet) { unsigned int nbTotalAddresses = 0; /* for (const auto &leaf : wallet->getLeaves()) { @@ -326,7 +326,7 @@ void RootWalletPropertiesDialog::onSpendableUTXOs() // ui_->labelUTXOs->setText(QString::number(sizeUTXOs)); } -void RootWalletPropertiesDialog::onWalletBalances() +void RootWalletPropertiesDialog::onWalletBalances(const bs::sync::WalletBalanceData&) { // ui_->labelAddressesUsed->setText(QString::number(wallet->getUsedAddressCount())); // ui_->labelAddressesActive->setText(QString::number(activeAddrCnt)); diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index b209a2140..6ba570aa0 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -59,8 +59,8 @@ Q_OBJECT , QWidget* parent = nullptr); ~RootWalletPropertiesDialog() override; - void onHDWalletDetails(); - void onWalletBalances(); + void onHDWalletDetails(const bs::sync::HDWalletData&); + void onWalletBalances(const bs::sync::WalletBalanceData&); void onSpendableUTXOs(); signals: diff --git a/BlockSettleUILib/NewAddressDialog.cpp b/BlockSettleUILib/NewAddressDialog.cpp index 97b067dc1..8fcb4de12 100644 --- a/BlockSettleUILib/NewAddressDialog.cpp +++ b/BlockSettleUILib/NewAddressDialog.cpp @@ -68,24 +68,37 @@ NewAddressDialog::NewAddressDialog(const bs::sync::WalletInfo &wallet , QWidget* parent) : QDialog(parent) , ui_(new Ui::NewAddressDialog()) + , walletId_(*wallet.ids.cbegin()) { ui_->setupUi(this); ui_->labelWallet->setText(QString::fromStdString(wallet.name)); - auto copyButton = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); - connect(copyButton, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); + copyButton_ = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); + connect(copyButton_, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); connect(ui_->pushButtonCopyToClipboard, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); - const auto closeButton = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Close); - if (closeButton) { - connect(closeButton, &QPushButton::clicked, this, &NewAddressDialog::onClose); + closeButton_ = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Close); + if (closeButton_) { + connect(closeButton_, &QPushButton::clicked, this, &NewAddressDialog::onClose); } - copyButton->setEnabled(false); - closeButton->setEnabled(false); + copyButton_->setEnabled(false); + closeButton_->setEnabled(false); } NewAddressDialog::~NewAddressDialog() = default; +void NewAddressDialog::onAddresses(const std::string &walletId + , const std::vector& addrs) +{ + if (walletId != walletId_) { + return; // not our addresses + } + address_ = addrs.rbegin()->address; // get last address + closeButton_->setEnabled(true); + copyButton_->setEnabled(!address_.empty()); + displayAddress(); +} + void NewAddressDialog::displayAddress() { const auto dispAddress = QString::fromStdString(address_.display()); diff --git a/BlockSettleUILib/NewAddressDialog.h b/BlockSettleUILib/NewAddressDialog.h index 8d1bb401c..056e62a9b 100644 --- a/BlockSettleUILib/NewAddressDialog.h +++ b/BlockSettleUILib/NewAddressDialog.h @@ -24,7 +24,7 @@ namespace bs { class Wallet; } } - +class QPushButton; class NewAddressDialog : public QDialog { @@ -36,6 +36,8 @@ Q_OBJECT NewAddressDialog(const bs::sync::WalletInfo &, QWidget* parent = nullptr); ~NewAddressDialog() override; + void onAddresses(const std::string& walletId, const std::vector&); + protected: void showEvent(QShowEvent* event) override; @@ -49,7 +51,10 @@ private slots: private: std::unique_ptr ui_; - std::shared_ptr wallet_; + [[deprecated]] std::shared_ptr wallet_; + QPushButton* copyButton_{ nullptr }; + QPushButton* closeButton_{ nullptr }; + std::string walletId_; bs::Address address_; }; diff --git a/BlockSettleUILib/NewWalletDialog.cpp b/BlockSettleUILib/NewWalletDialog.cpp index 16c14466a..9bc058df3 100644 --- a/BlockSettleUILib/NewWalletDialog.cpp +++ b/BlockSettleUILib/NewWalletDialog.cpp @@ -20,7 +20,9 @@ namespace { const QString kSupportDialogLink = QLatin1String("SupportDialog"); } -NewWalletDialog::NewWalletDialog(bool noWalletsFound, const std::shared_ptr& appSettings, QWidget *parent) +NewWalletDialog::NewWalletDialog(bool noWalletsFound + , const std::shared_ptr& appSettings + , QWidget *parent) : QDialog(parent) , ui_(new Ui::NewWalletDialog) { @@ -59,25 +61,61 @@ NewWalletDialog::NewWalletDialog(bool noWalletsFound, const std::shared_ptrpushButtonHw, &QPushButton::clicked, this, [this] { done(ImportHw); }); + connect(ui_->labelMessage, &QLabel::linkActivated, this, &NewWalletDialog::onLinkActivated); +} - connect(ui_->labelMessage, &QLabel::linkActivated, this, [this](const QString & link) { - reject(); +NewWalletDialog::NewWalletDialog(bool noWalletsFound + , QWidget* parent) + : QDialog(parent) + , ui_(new Ui::NewWalletDialog) +{ + ui_->setupUi(this); + + if (noWalletsFound) { + ui_->labelPurpose->setText(tr("THE TERMINAL CAN'T FIND ANY EXISTING WALLET")); + } else { + ui_->labelPurpose->setText(tr("ADD NEW WALLET")); + } - if (link == kSupportDialogLink) { - auto* parent = parentWidget(); + const auto messageText = + tr("Need help? Please consult our ") + + QStringLiteral("").arg(kSupportDialogLink) + + QStringLiteral("Getting Started Guides") + .arg(BSMessageBox::kUrlColor); - SupportDialog* supportDlg = new SupportDialog(parent); - supportDlg->setTab(0); - supportDlg->show(); + ui_->labelMessage->setText(messageText); - auto* walletWidget = qobject_cast(parent); - if (walletWidget) { - connect(supportDlg, &QDialog::finished, walletWidget, [walletWidget]() { - walletWidget->onNewWallet(); - }); - } - } + connect(ui_->pushButtonCreate, &QPushButton::clicked, this, [this] { + done(CreateNew); }); + connect(ui_->pushButtonImport, &QPushButton::clicked, this, [this] { + done(ImportExisting); + }); + connect(ui_->pushButtonHw, &QPushButton::clicked, this, [this] { + done(ImportHw); + }); + connect(ui_->labelMessage, &QLabel::linkActivated, this, &NewWalletDialog::onLinkActivated); } NewWalletDialog::~NewWalletDialog() = default; + +void NewWalletDialog::onLinkActivated(const QString& link) +{ + if (link == kSupportDialogLink) { + auto* parent = parentWidget(); + + SupportDialog* supportDlg = new SupportDialog(parent); + supportDlg->setTab(0); + supportDlg->show(); + + auto* walletWidget = qobject_cast(parent); + if (walletWidget) { //FIXME: emit signal instead + connect(supportDlg, &QDialog::finished, walletWidget, [walletWidget]() { + walletWidget->onNewWallet(); + }); + } + } + else { + ui_->labelPurpose->setText(tr("Unknown link")); + } +} diff --git a/BlockSettleUILib/NewWalletDialog.h b/BlockSettleUILib/NewWalletDialog.h index 56d5cc602..d62e79e37 100644 --- a/BlockSettleUILib/NewWalletDialog.h +++ b/BlockSettleUILib/NewWalletDialog.h @@ -26,7 +26,9 @@ class NewWalletDialog : public QDialog Q_OBJECT public: - NewWalletDialog(bool noWalletsFound, const std::shared_ptr& appSettings, QWidget *parent = nullptr); + [[deprecated]] NewWalletDialog(bool noWalletsFound + , const std::shared_ptr&, QWidget *parent = nullptr); + NewWalletDialog(bool noWalletsFound, QWidget* parent = nullptr); ~NewWalletDialog() override; enum Result @@ -37,9 +39,11 @@ class NewWalletDialog : public QDialog ImportHw, }; +private slots: + void onLinkActivated(const QString&); + private: std::unique_ptr ui_; - }; #endif // __NEW_WALLET_DIALOG_H__ diff --git a/BlockSettleUILib/SelectAddressDialog.cpp b/BlockSettleUILib/SelectAddressDialog.cpp index e46267861..718ea8dc4 100644 --- a/BlockSettleUILib/SelectAddressDialog.cpp +++ b/BlockSettleUILib/SelectAddressDialog.cpp @@ -71,9 +71,10 @@ void SelectAddressDialog::setWallets(const AddressListModel::Wallets& wallets) onSelectionChanged(); } -void SelectAddressDialog::onAddresses(const std::vector& addrs) +void SelectAddressDialog::onAddresses(const std::string& walletId + , const std::vector& addrs) { - model_->onAddresses(addrs); + model_->onAddresses(walletId, addrs); } void SelectAddressDialog::onAddressComments(const std::string& walletId diff --git a/BlockSettleUILib/SelectAddressDialog.h b/BlockSettleUILib/SelectAddressDialog.h index bd934d0da..aee446f69 100644 --- a/BlockSettleUILib/SelectAddressDialog.h +++ b/BlockSettleUILib/SelectAddressDialog.h @@ -47,7 +47,7 @@ Q_OBJECT bs::Address getSelectedAddress() const; void setWallets(const AddressListModel::Wallets&); - void onAddresses(const std::vector&); + void onAddresses(const std::string& walletId, const std::vector&); void onAddressComments(const std::string& walletId , const std::map&); void onAddressBalances(const std::string& walletId diff --git a/BlockSettleUILib/SelectWalletDialog.cpp b/BlockSettleUILib/SelectWalletDialog.cpp index d55c7e60f..08a1684a7 100644 --- a/BlockSettleUILib/SelectWalletDialog.cpp +++ b/BlockSettleUILib/SelectWalletDialog.cpp @@ -20,7 +20,6 @@ SelectWalletDialog::SelectWalletDialog(const std::shared_ptrsetupUi(this); @@ -46,6 +45,29 @@ SelectWalletDialog::SelectWalletDialog(const std::shared_ptrtreeViewWallets->expandAll(); } +SelectWalletDialog::SelectWalletDialog(const std::string& selWalletId, QWidget* parent) + : QDialog(parent) + , ui_(new Ui::SelectWalletDialog) +{ + ui_->setupUi(this); + + ui_->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Select")); + + walletsModel_ = new WalletsViewModel(selWalletId, ui_->treeViewWallets, true); + walletsModel_->setBitcoinLeafSelectionMode(); + ui_->treeViewWallets->setModel(walletsModel_); + ui_->treeViewWallets->setItemsExpandable(true); + ui_->treeViewWallets->setRootIsDecorated(true); + + connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &SelectWalletDialog::onSelectionChanged); + connect(ui_->treeViewWallets, &QTreeView::doubleClicked + , this, &SelectWalletDialog::onDoubleClicked); + + connect(ui_->buttonBox, &QDialogButtonBox::accepted, this, &SelectWalletDialog::accept); + connect(ui_->buttonBox, &QDialogButtonBox::rejected, this, &SelectWalletDialog::reject); +} + SelectWalletDialog::~SelectWalletDialog() = default; void SelectWalletDialog::onSelectionChanged() @@ -55,7 +77,7 @@ void SelectWalletDialog::onSelectionChanged() selectedWallet_ = *walletsModel_->getWallet(selectedRows[0]).ids.cbegin(); } else { - selectedWallet_ = nullptr; + selectedWallet_.clear(); } walletsModel_->setSelectedWallet(selectedWallet_); @@ -68,6 +90,24 @@ std::string SelectWalletDialog::getSelectedWallet() const return selectedWallet_; } +void SelectWalletDialog::onHDWallet(const bs::sync::WalletInfo& wi) +{ + walletsModel_->onHDWallet(wi); +} + +void SelectWalletDialog::onHDWalletDetails(const bs::sync::HDWalletData& hdw) +{ + walletsModel_->onHDWalletDetails(hdw); + ui_->treeViewWallets->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + onSelectionChanged(); + ui_->treeViewWallets->expandAll(); +} + +void SelectWalletDialog::onWalletBalances(const bs::sync::WalletBalanceData& wbd) +{ + walletsModel_->onWalletBalances(wbd); +} + void SelectWalletDialog::onDoubleClicked(const QModelIndex& index) { if (selectedWallet_.empty()) { diff --git a/BlockSettleUILib/SelectWalletDialog.h b/BlockSettleUILib/SelectWalletDialog.h index b9c222c82..91873bf0d 100644 --- a/BlockSettleUILib/SelectWalletDialog.h +++ b/BlockSettleUILib/SelectWalletDialog.h @@ -13,6 +13,7 @@ #include #include +#include "SignerDefs.h" namespace Ui { class SelectWalletDialog; @@ -32,20 +33,25 @@ class SelectWalletDialog : public QDialog Q_OBJECT public: - SelectWalletDialog(const std::shared_ptr &, const std::string &selWalletId, QWidget* parent = nullptr); + [[deprecated]] SelectWalletDialog(const std::shared_ptr & + , const std::string &selWalletId, QWidget* parent = nullptr); + SelectWalletDialog(const std::string& selWalletId, QWidget* parent = nullptr); ~SelectWalletDialog() override; std::string getSelectedWallet() const; + void onHDWallet(const bs::sync::WalletInfo&); + void onHDWalletDetails(const bs::sync::HDWalletData&); + void onWalletBalances(const bs::sync::WalletBalanceData&); + public slots: void onSelectionChanged(); void onDoubleClicked(const QModelIndex& index); private: std::unique_ptr ui_; - WalletsViewModel * walletsModel_; - std::string selectedWallet_; - std::shared_ptr walletsManager_; + WalletsViewModel *walletsModel_; + std::string selectedWallet_; }; #endif // __SELECT_WALLET_DIALOG_H__ diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index 7c718bb85..9b667f0f3 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -659,10 +659,16 @@ void WalletsViewModel::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) default: break; } if (!groupNode) { - beginInsertRows(createIndex(node->row(), 0, static_cast(node)) - , node->nbChildren(), node->nbChildren()); + const auto& nbChildren = node->nbChildren(); groupNode = hdNode->addGroup(groupType, group.name, group.description); - endInsertRows(); + if (groupNode) { + beginInsertRows(createIndex(node->row(), 0, static_cast(node)) + , nbChildren, nbChildren); + endInsertRows(); + } + } + if (!groupNode) { + continue; } for (const auto &leaf : group.leaves) { const auto &leafNode = groupNode->findByWalletId(*leaf.ids.cbegin()); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 77e15677a..0e351c394 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -27,7 +27,9 @@ #include "ApplicationSettings.h" #include "AssetManager.h" #include "BSMessageBox.h" +#include "NewAddressDialog.h" #include "NewWalletDialog.h" +#include "SelectWalletDialog.h" #include "SignContainer.h" #include "WalletsViewModel.h" #include "WalletWarningDialog.h" @@ -300,7 +302,8 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) ui_->treeViewAddresses->header()->setSectionResizeMode(QHeaderView::ResizeToContents); updateAddresses(); - connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WalletsWidget::updateAddresses); + connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged + , this, &WalletsWidget::updateAddresses); connect(walletsModel_, &WalletsViewModel::updateAddresses, this, &WalletsWidget::updateAddresses); if (walletsManager_) { connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &WalletsWidget::onWalletBalanceChanged, Qt::QueuedConnection); @@ -353,13 +356,15 @@ void WalletsWidget::onNewBlock(unsigned int blockNum) void WalletsWidget::onHDWallet(const bs::sync::WalletInfo &wi) { + wallets_.insert(wi); walletsModel_->onHDWallet(wi); } void WalletsWidget::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { + walletDetails_[hdWallet.id] = hdWallet; if (rootDlg_) { - //TODO: pass wallet details to currently running root wallet dialog + rootDlg_->onHDWalletDetails(hdWallet); } walletsModel_->onHDWalletDetails(hdWallet); @@ -373,9 +378,85 @@ void WalletsWidget::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) //TODO: keep wallets treeView selection } -void WalletsWidget::onAddresses(const std::vector &addrs) +void WalletsWidget::onGenerateAddress(bool isActive) { - addressModel_->onAddresses(addrs); + if (wallets_.empty()) { + //TODO: invoke wallet create dialog + return; + } + + std::string selWalletId = curWalletId_; + if (!isActive || selWalletId.empty()) { + SelectWalletDialog selectWalletDialog(curWalletId_, this); + for (const auto& wallet : wallets_) { + selectWalletDialog.onHDWallet(wallet); + } + for (const auto& wallet : walletDetails_) { + selectWalletDialog.onHDWalletDetails(wallet.second); + } + for (const auto& bal : walletBalances_) { + selectWalletDialog.onWalletBalances(bal.second); + } + selectWalletDialog.exec(); + + if (selectWalletDialog.result() == QDialog::Rejected) { + return; + } + else { + selWalletId = selectWalletDialog.getSelectedWallet(); + } + } + bs::sync::WalletInfo selWalletInfo; + auto itWallet = walletDetails_.find(selWalletId); + if (itWallet == walletDetails_.end()) { + const auto& findLeaf = [selWalletId, wallets = walletDetails_]() -> bs::sync::WalletInfo + { + for (const auto& hdWallet : wallets) { + for (const auto& grp : hdWallet.second.groups) { + for (const auto& leaf : grp.leaves) { + for (const auto& id : leaf.ids) { + if (id == selWalletId) { + bs::sync::WalletInfo wi; + wi.name = leaf.name; + wi.ids = leaf.ids; + return wi; + } + } + } + } + } + return {}; + }; + selWalletInfo = findLeaf(); + if (selWalletInfo.ids.empty()) { + logger_->error("[{}] no leaf found for {}", __func__, selWalletId); + return; + } + } + else { + selWalletInfo.name = itWallet->second.name; + } + + if (newAddrDlg_) { + logger_->error("[{}] new address dialog already created", __func__); + return; + } + newAddrDlg_ = new NewAddressDialog(selWalletInfo, this); + emit createExtAddress(selWalletId); + connect(newAddrDlg_, &QDialog::finished, [this](int) { + newAddrDlg_->deleteLater(); + newAddrDlg_ = nullptr; + }); + newAddrDlg_->exec(); +} + +void WalletsWidget::onAddresses(const std::string &walletId + , const std::vector &addrs) +{ + if (newAddrDlg_) { + newAddrDlg_->onAddresses(walletId, addrs); + } + addressModel_->onAddresses(walletId, addrs); } void WalletsWidget::onLedgerEntries(const std::string &filter, uint32_t totalPages @@ -402,8 +483,9 @@ void WalletsWidget::onAddressComments(const std::string &walletId void WalletsWidget::onWalletBalance(const bs::sync::WalletBalanceData &wbd) { + walletBalances_[wbd.id] = wbd; if (rootDlg_) { - //TODO: pass data to root wallet dialog + rootDlg_->onWalletBalances(wbd); } walletsModel_->onWalletBalances(wbd); addressModel_->onAddressBalances(wbd.id, wbd.addrBalances); @@ -445,8 +527,7 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) rootDlg_->deleteLater(); rootDlg_ = nullptr; }); - rootDlg_->setModal(true); - rootDlg_->show(); + rootDlg_->exec(); } } } @@ -591,7 +672,13 @@ void WalletsWidget::updateAddresses() if (ui_->treeViewWallets->selectionModel()->hasSelection()) { prevSelectedWalletRow_ = ui_->treeViewWallets->selectionModel()->selectedIndexes().first().row(); } - + + if (selectedWallets.size() == 1) { + curWalletId_ = *selectedWallets.at(0).ids.cbegin(); + } + else { + curWalletId_.clear(); + } prevSelectedWallets_ = selectedWallets; if (!applyPreviousSelection()) { @@ -699,17 +786,12 @@ void WalletsWidget::onWalletBalanceChanged(std::string walletId) void WalletsWidget::onNewWallet() { - if (!signingContainer_) { - showError(tr("Signer not created (yet)")); - return; - } - if (!signingContainer_->isOffline()) { - NewWalletDialog newWalletDialog(false, appSettings_, this); - emit newWalletCreationRequest(); - - int rc = newWalletDialog.exec(); + emit newWalletCreationRequest(); + if (signingContainer_) { + if (!signingContainer_->isOffline()) { + NewWalletDialog newWalletDialog(false, appSettings_, this); - switch (rc) { + switch (newWalletDialog.exec()) { case NewWalletDialog::CreateNew: CreateNewWallet(); break; @@ -721,25 +803,60 @@ void WalletsWidget::onNewWallet() break; case NewWalletDialog::Cancel: break; + } + } else { + ImportNewWallet(); + } + } + else { + NewWalletDialog newWalletDialog(false, this); + switch (newWalletDialog.exec()) { + case NewWalletDialog::CreateNew: + CreateNewWallet(); + break; + case NewWalletDialog::ImportExisting: + ImportNewWallet(); + break; + case NewWalletDialog::ImportHw: + ImportHwWallet(); + break; + case NewWalletDialog::Cancel: + break; + default: + showError(tr("Unknown new wallet choice")); + break; } - } else { - ImportNewWallet(); } } void WalletsWidget::CreateNewWallet() { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::CreateWallet); + if (signingContainer_) { + signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::CreateWallet); + } + else { + emit needWalletDialog(bs::signer::ui::GeneralDialogType::CreateWallet); + } } void WalletsWidget::ImportNewWallet() { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ImportWallet); + if (signingContainer_) { + signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ImportWallet); + } + else { + emit needWalletDialog(bs::signer::ui::GeneralDialogType::ImportWallet); + } } void WalletsWidget::ImportHwWallet() { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ImportHwWallet); + if (signingContainer_) { + signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ImportHwWallet); + } + else { + emit needWalletDialog(bs::signer::ui::GeneralDialogType::ImportHwWallet); + } } void WalletsWidget::shortcutActivated(ShortcutType s) diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 9186a29cf..54cd47854 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -48,6 +48,7 @@ class AuthAddressManager; class ConnectionManager; class QAction; class QMenu; +class NewAddressDialog; class RootWalletPropertiesDialog; class SignContainer; class WalletNode; @@ -86,7 +87,8 @@ Q_OBJECT void onNewBlock(unsigned int blockNum); void onHDWallet(const bs::sync::WalletInfo &); void onHDWalletDetails(const bs::sync::HDWalletData &); - void onAddresses(const std::vector &); + void onGenerateAddress(bool isActive); + void onAddresses(const std::string& walletId, const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); void onWalletBalance(const bs::sync::WalletBalanceData &); @@ -126,7 +128,9 @@ public slots: void needLedgerEntries(const std::string &filter); void needTXDetails(const std::vector &, bool useCache , const bs::Address &); - void needWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); + void needWalletDialog(bs::signer::ui::GeneralDialogType + , const std::string& rootId = {}); + void createExtAddress(const std::string& walletId); private slots: void showWalletProperties(const QModelIndex& index); @@ -165,12 +169,16 @@ private slots: AddressListModel * addressModel_; AddressSortFilterModel * addressSortFilterModel_; RootWalletPropertiesDialog * rootDlg_{ nullptr }; + NewAddressDialog* newAddrDlg_{ nullptr }; QAction * actCopyAddr_ = nullptr; QAction * actEditComment_ = nullptr; QAction * actRevokeSettl_ = nullptr; //QAction * actDeleteWallet_ = nullptr; bs::Address curAddress_; - std::shared_ptr curWallet_; + [[deprecated]] std::shared_ptr curWallet_; + std::set wallets_; + std::unordered_map walletDetails_; + std::unordered_map walletBalances_; std::string curWalletId_; std::string curComment_; unsigned int revokeReqId_ = 0; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 091a9fca1..941a459af 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -245,13 +245,14 @@ void bs::gui::qt::MainWindow::onWalletData(const std::string& walletId ui_->widgetRFQ->onWalletData(walletId, wd); } -void MainWindow::onAddresses(const std::vector &addrs) +void MainWindow::onAddresses(const std::string& walletId + , const std::vector &addrs) { if (txDlg_) { - txDlg_->onAddresses(addrs); + txDlg_->onAddresses(walletId, addrs); } else { - ui_->widgetWallets->onAddresses(addrs); + ui_->widgetWallets->onAddresses(walletId, addrs); } } @@ -638,36 +639,7 @@ void MainWindow::updateAppearance() void MainWindow::onGenerateAddress() { -/* if (walletsMgr_->hdWallets().empty()) { - createWallet(true); - return; - } - - const auto defWallet = walletsMgr_->getDefaultWallet(); - std::string selWalletId = defWallet ? defWallet->walletId() : std::string{}; - - if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { - auto wallets = ui_->widgetWallets->getSelectedWallets(); - if (!wallets.empty()) { - selWalletId = wallets[0]->walletId(); - } else { - wallets = ui_->widgetWallets->getFirstWallets(); - - if (!wallets.empty()) { - selWalletId = wallets[0]->walletId(); - } - } - } - SelectWalletDialog selectWalletDialog(walletsMgr_, selWalletId, this); - selectWalletDialog.exec(); - - if (selectWalletDialog.result() == QDialog::Rejected) { - return; - } - - NewAddressDialog newAddressDialog(selectWalletDialog.getSelectedWallet(), this); - newAddressDialog.exec(); - */ + ui_->widgetWallets->onGenerateAddress(ui_->tabWidget->currentWidget() == ui_->widgetWallets); } void MainWindow::onSend() @@ -1254,6 +1226,7 @@ void MainWindow::initWidgets() connect(ui_->widgetWallets, &WalletsWidget::needLedgerEntries, this, &MainWindow::needLedgerEntries); connect(ui_->widgetWallets, &WalletsWidget::needTXDetails, this, &MainWindow::needTXDetails); connect(ui_->widgetWallets, &WalletsWidget::needWalletDialog, this, &MainWindow::needWalletDialog); + connect(ui_->widgetWallets, &WalletsWidget::createExtAddress, this, &MainWindow::createExtAddress); connect(ui_->widgetExplorer, &ExplorerWidget::needAddressHistory, this, &MainWindow::needAddressHistory); connect(ui_->widgetExplorer, &ExplorerWidget::needTXDetails, this, &MainWindow::needTXDetails); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index fd93c14a2..968f7785b 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -76,7 +76,7 @@ namespace bs { void onHDWalletDetails(const bs::sync::HDWalletData &); void onWalletsList(const std::string &id, const std::vector&); void onWalletData(const std::string &walletId, const bs::sync::WalletData&); - void onAddresses(const std::vector &); + void onAddresses(const std::string& walletId, const std::vector &); void onAddressComments(const std::string &walletId , const std::map &); void onWalletBalance(const bs::sync::WalletBalanceData &); @@ -161,6 +161,7 @@ namespace bs { void needWalletData(const std::string& walletId); void needWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); + void createExtAddress(const std::string& walletId); void needExtAddresses(const std::string &walletId); void needIntAddresses(const std::string &walletId); void needUsedAddresses(const std::string &walletId); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index eef76af88..1a41ff4b4 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -531,8 +531,8 @@ bool QtGuiAdapter::processWallets(const Envelope &env) } catch (const std::exception &) {} } - QMetaObject::invokeMethod(mainWindow_, [this, addresses] { - mainWindow_->onAddresses(addresses); + QMetaObject::invokeMethod(mainWindow_, [this, addresses, walletId=msg.wallet_addresses().wallet_id()] { + mainWindow_->onAddresses(walletId, addresses); }); } break; @@ -730,6 +730,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needHDWalletDetails, this, &QtGuiAdapter::onNeedHDWalletDetails); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletData, this, &QtGuiAdapter::onNeedWalletData); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletBalances, this, &QtGuiAdapter::onNeedWalletBalances); + connect(mainWindow_, &bs::gui::qt::MainWindow::createExtAddress, this, &QtGuiAdapter::onCreateExtAddress); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needIntAddresses, this, &QtGuiAdapter::onNeedIntAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needUsedAddresses, this, &QtGuiAdapter::onNeedUsedAddresses); @@ -916,6 +917,14 @@ void QtGuiAdapter::onNeedWalletData(const std::string& walletId) } } +void QtGuiAdapter::onCreateExtAddress(const std::string& walletId) +{ + WalletsMessage msg; + msg.set_create_ext_address(walletId); + Envelope env{ 0, user_, userWallets_, {}, {}, msg.SerializeAsString(), true }; + pushFill(env); +} + void QtGuiAdapter::onNeedExtAddresses(const std::string &walletId) { logger_->debug("[{}] {}", __func__, walletId); @@ -1267,9 +1276,11 @@ void QtGuiAdapter::onNeedWalletDialog(bs::signer::ui::GeneralDialogType dlgType SignerMessage msg; auto msgReq = msg.mutable_dialog_request(); msgReq->set_dialog_type((int)dlgType); - auto msgData = msgReq->add_data(); - msgData->set_key("rootId"); - msgData->set_value(rootId); + if (!rootId.empty()) { + auto msgData = msgReq->add_data(); + msgData->set_key("rootId"); + msgData->set_value(rootId); + } Envelope env{ 0, user_, userSigner_, {}, {}, msg.SerializeAsString(), true }; pushFill(env); } diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 3504a9f2a..b9b21c818 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -167,6 +167,7 @@ private slots: void onNeedHDWalletDetails(const std::string &walletId); void onNeedWalletBalances(const std::string &walletId); void onNeedWalletData(const std::string& walletId); + void onCreateExtAddress(const std::string& walletId); void onNeedExtAddresses(const std::string &walletId); void onNeedIntAddresses(const std::string &walletId); void onNeedUsedAddresses(const std::string &walletId); diff --git a/common b/common index 3c69a4a16..a0a509251 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 3c69a4a163c2076ce008b2aa7bb6dbcc610d21f2 +Subproject commit a0a509251abe79fa43fa8cf97b27742ec1c4610c From b18299b4df91f164fc438c70d18ec08fa9d58d61 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 17 Dec 2020 16:47:44 +0300 Subject: [PATCH 076/146] Wallet scan and removal --- BlockSettleSigner/main.cpp | 2 +- BlockSettleUILib/WalletsViewModel.cpp | 25 +++++++++++++++++++++++++ BlockSettleUILib/WalletsViewModel.h | 3 +++ BlockSettleUILib/WalletsWidget.cpp | 17 ++++++----------- BlockSettleUILib/WalletsWidget.h | 1 + Core/SignerAdapter.cpp | 6 +----- GUI/QtWidgets/MainWindow.cpp | 5 +++++ GUI/QtWidgets/MainWindow.h | 1 + GUI/QtWidgets/QtGuiAdapter.cpp | 8 ++++++++ common | 2 +- 10 files changed, 52 insertions(+), 18 deletions(-) diff --git a/BlockSettleSigner/main.cpp b/BlockSettleSigner/main.cpp index 681f72dde..eaef01422 100644 --- a/BlockSettleSigner/main.cpp +++ b/BlockSettleSigner/main.cpp @@ -336,7 +336,7 @@ static int QMLApp(int argc, char **argv terminalConnectionTimer.setSingleShot(true); //BST-2786 - terminalConnectionTimer.setInterval(std::chrono::milliseconds{ /*5*/7000 }); // 5s is too little for some use cases + terminalConnectionTimer.setInterval(std::chrono::milliseconds{ /*5*/30000 }); // 5s is too little for some use cases QObject::connect(&adapter, &SignerAdapter::ready, [&timerStarted, &terminalConnectionTimer]() { diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index 9b667f0f3..944a94805 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -21,6 +21,21 @@ #include "Wallets/SyncWalletsManager.h" +void WalletNode::remove(WalletNode* child) +{ + bool found = false; + for (auto& c : children_) { + if (!found && (child->id() == c->id())) { + found = true; + continue; + } + if (found) { + c->incRow(-1); + } + } + children_.removeOne(child); +} + void WalletNode::replace(WalletNode *child) { for (auto &c : children_) { @@ -619,6 +634,16 @@ void WalletsViewModel::onHDWallet(const bs::sync::WalletInfo &wi) emit needHDWalletDetails(*wi.ids.cbegin()); } +void WalletsViewModel::onWalletDeleted(const bs::sync::WalletInfo& wi) +{ + const auto& wallet = rootNode_->findByWalletId(*wi.ids.cbegin()); + if (wallet) { + beginRemoveRows(QModelIndex(), wallet->row(), wallet->row()); + rootNode_->remove(wallet); + endRemoveRows(); + } +} + void WalletsViewModel::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { WalletNode *node{ nullptr }; diff --git a/BlockSettleUILib/WalletsViewModel.h b/BlockSettleUILib/WalletsViewModel.h index c4962b989..70103b5cc 100644 --- a/BlockSettleUILib/WalletsViewModel.h +++ b/BlockSettleUILib/WalletsViewModel.h @@ -62,6 +62,7 @@ class WalletNode virtual std::string id() const { return {}; } void add(WalletNode *child) { children_.append(child); } + void remove(WalletNode* child); void replace(WalletNode *child); void clear(); int nbChildren() const { return children_.count(); } @@ -69,6 +70,7 @@ class WalletNode WalletNode *parent() const { return parent_; } WalletNode *child(int index) const; int row() const { return row_; } + void incRow(int increment = 1) { row_ += increment; } const std::string &name() const { return name_; } Type type() const { return type_; } State state() const { return state_; } @@ -112,6 +114,7 @@ Q_OBJECT [[deprecated]] void LoadWallets(bool keepSelection = false); void onHDWallet(const bs::sync::WalletInfo &); + void onWalletDeleted(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData &); void onWalletBalances(const bs::sync::WalletBalanceData &); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 0e351c394..959df59f0 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -360,6 +360,12 @@ void WalletsWidget::onHDWallet(const bs::sync::WalletInfo &wi) walletsModel_->onHDWallet(wi); } +void WalletsWidget::onWalletDeleted(const bs::sync::WalletInfo& wi) +{ + walletsModel_->onWalletDeleted(wi); + wallets_.erase(wi); +} + void WalletsWidget::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { walletDetails_[hdWallet.id] = hdWallet; @@ -991,14 +997,3 @@ void WalletsWidget::onTXSigned(unsigned int id, BinaryData signedTX, bs::error:: BSMessageBox(BSMessageBox::critical, title, tr("Failed to send transaction to mempool")).exec(); } } - - // Not used -//void WalletsWidget::onDeleteWallet() -//{ -// const auto action = qobject_cast(sender()); -// const auto walletId = action ? action->data().toString() : QString(); -// if (walletId.isEmpty()) { -// BSMessageBox(BSMessageBox::critical, tr("Wallet Delete"), tr("Failed to delete wallet"), this).exec(); -// return; -// } -//} diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index 54cd47854..b6fbf1d00 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -86,6 +86,7 @@ Q_OBJECT void onNewBlock(unsigned int blockNum); void onHDWallet(const bs::sync::WalletInfo &); + void onWalletDeleted(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData &); void onGenerateAddress(bool isActive); void onAddresses(const std::string& walletId, const std::vector &); diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 7d2cd2d5e..2a889fecb 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -436,11 +436,7 @@ bool SignerAdapter::processSyncAddresses(const bs::message::Envelope &env }; std::set addrSet; for (const auto &addr : request.addresses()) { - try { - const auto &address = bs::Address::fromAddressString(addr); - addrSet.insert(address.prefixed()); - } - catch (const std::exception &) {} + addrSet.insert(BinaryData::fromString(addr)); } signer_->syncAddressBatch(request.wallet_id(), addrSet, cb); return true; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 941a459af..73f0eedb8 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -225,6 +225,11 @@ void MainWindow::onHDWallet(const bs::sync::WalletInfo &wi) ui_->widgetWallets->onHDWallet(wi); } +void bs::gui::qt::MainWindow::onWalletDeleted(const bs::sync::WalletInfo& wi) +{ + ui_->widgetWallets->onWalletDeleted(wi); +} + void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { ui_->widgetWallets->onHDWalletDetails(hdWallet); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 968f7785b..0c2667cc4 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -73,6 +73,7 @@ namespace bs { void onWalletsReady(); void onHDWallet(const bs::sync::WalletInfo &); + void onWalletDeleted(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData &); void onWalletsList(const std::string &id, const std::vector&); void onWalletData(const std::string &walletId, const bs::sync::WalletData&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 1a41ff4b4..12c1cd0d8 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -522,6 +522,14 @@ bool QtGuiAdapter::processWallets(const Envelope &env) } break; + case WalletsMessage::kWalletDeleted: { + const auto& wi = bs::sync::WalletInfo::fromCommonMsg(msg.wallet_deleted()); + QMetaObject::invokeMethod(mainWindow_, [this, wi]{ + mainWindow_->onWalletDeleted(wi); + }); + } + break; + case WalletsMessage::kWalletAddresses: { std::vector addresses; for (const auto &addr : msg.wallet_addresses().addresses()) { diff --git a/common b/common index a0a509251..2e7aad35e 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a0a509251abe79fa43fa8cf97b27742ec1c4610c +Subproject commit 2e7aad35e5cb07fc02e106bdfd7329c5c38e36bc From e5e1a0d27de433b6b50367287868c1f0a8a0fe8a Mon Sep 17 00:00:00 2001 From: Ation Date: Thu, 17 Dec 2020 18:32:37 +0200 Subject: [PATCH 077/146] Rename future --- BlockSettleUILib/ChartWidget.cpp | 2 +- BlockSettleUILib/OrderListModel.cpp | 4 ++-- BlockSettleUILib/Trading/FuturesTicket.cpp | 2 +- BlockSettleUILib/Trading/MarketDataModel.cpp | 3 +-- BlockSettleUILib/Trading/RFQDealerReply.cpp | 6 +++--- BlockSettleUILib/Trading/RFQDialog.cpp | 6 +++--- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 2 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 4 ++-- .../Trading/RequestingQuoteWidget.cpp | 2 +- BlockSettleUILib/Trading/WalletShieldBase.cpp | 6 +++--- BlockSettleUILib/UiUtils.cpp | 16 +++++++++------- BlockSettleUILib/UserScript.cpp | 2 +- common | 2 +- 13 files changed, 29 insertions(+), 28 deletions(-) diff --git a/BlockSettleUILib/ChartWidget.cpp b/BlockSettleUILib/ChartWidget.cpp index bea9c71c4..36a0648ca 100644 --- a/BlockSettleUILib/ChartWidget.cpp +++ b/BlockSettleUILib/ChartWidget.cpp @@ -189,7 +189,7 @@ void ChartWidget::SendEoDRequest() void ChartWidget::OnMdUpdated(bs::network::Asset::Type assetType, const QString& security, bs::network::MDFields mdFields) { - if (assetType == bs::network::Asset::Type::Futures) { + if (bs::network::Asset::isFuturesType(assetType)) { // ignore futures prices updates return; } diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 1e9591a3d..25bc785af 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -647,7 +647,7 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke beginInsertRows(createIndex(findMarket(sg, marketItem), 0, &marketItem->idx_), static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - if (getStatusGroup(order) == StatusGroup::UnSettled && order.assetType == bs::network::Asset::Type::Futures) { + if (getStatusGroup(order) == StatusGroup::UnSettled && order.assetType == bs::network::Asset::Type::DeliverableFutures) { marketItem->rows_.emplace_back(make_unique( QString::fromStdString(order.security), &marketItem->idx_)); } else { @@ -874,7 +874,7 @@ void OrderListModel::DisplayFuturesDeliveryRow(const Blocksettle::Communication: } beginInsertRows(createIndex(pendingFuturesSettlement_->row_, 0, &pendingFuturesSettlement_->idx_), static_cast(pendingFuturesSettlement_->rows_.size()), static_cast(pendingFuturesSettlement_->rows_.size())); - pendingFuturesSettlement_->rows_.emplace_back(make_unique(bs::network::Asset::Type::Futures, &pendingFuturesSettlement_->idx_)); + pendingFuturesSettlement_->rows_.emplace_back(make_unique(bs::network::Asset::Type::DeliverableFutures, &pendingFuturesSettlement_->idx_)); Market * marketItem = pendingFuturesSettlement_->rows_.back().get(); endInsertRows(); diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index e38036e47..8e6b6abb3 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -323,7 +323,7 @@ void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amou void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) { - if (type != bs::network::Asset::Futures && type != bs::network::Asset::CashSettledFutures) { + if (!bs::network::Asset::isFuturesType(type)) { return; } diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index 1203d5f62..18f3bfb5f 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -167,8 +167,7 @@ void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType, const QStr PriceMap fieldsMap; - if (assetType == bs::network::Asset::Futures || - assetType == bs::network::Asset::CashSettledFutures) { + if (bs::network::Asset::isFuturesType(assetType)) { FieldsToMap(bs::network::Asset::SpotXBT, mdFields, fieldsMap); } else { FieldsToMap(assetType, mdFields, fieldsMap); diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index ab26edfc8..9e9aee21d 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -291,7 +291,7 @@ void RFQDealerReply::updateQuoteReqNotification(const bs::network::QuoteReqNotif if (qrn.assetType == bs::network::Asset::SpotFX || qrn.assetType == bs::network::Asset::Undefined || - qrn.assetType == bs::network::Asset::Futures) { + qrn.assetType == bs::network::Asset::DeliverableFutures) { ui_->groupBoxSettlementInputs->hide(); } else { ui_->groupBoxSettlementInputs->show(); @@ -475,7 +475,7 @@ bool RFQDealerReply::checkBalance() const return false; } - if (currentQRN_.assetType == bs::network::Asset::Futures) { + if (currentQRN_.assetType == bs::network::Asset::DeliverableFutures) { return true; } @@ -638,7 +638,7 @@ void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn, d replyData->qn = bs::network::QuoteNotification(qrn, authKey_, price, ""); auto quoteAssetType = qrn.assetType; - if (quoteAssetType == bs::network::Asset::Futures) { + if (quoteAssetType == bs::network::Asset::DeliverableFutures) { quoteAssetType = bs::network::Asset::SpotFX; } diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 3b35703b1..3cc6d0c8a 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -106,7 +106,7 @@ void RFQDialog::onOrderFilled(const std::string "eId) } if (rfq_.assetType == bs::network::Asset::SpotFX - || rfq_.assetType == bs::network::Asset::Futures) { + || rfq_.assetType == bs::network::Asset::DeliverableFutures) { ui_->pageRequestingQuote->onOrderFilled(quoteId); } } @@ -118,7 +118,7 @@ void RFQDialog::onOrderFailed(const std::string& quoteId, const std::string& rea } if (rfq_.assetType == bs::network::Asset::SpotFX - || rfq_.assetType == bs::network::Asset::Futures) { + || rfq_.assetType == bs::network::Asset::DeliverableFutures) { ui_->pageRequestingQuote->onOrderFailed(quoteId, reason); } close(); @@ -130,7 +130,7 @@ void RFQDialog::onRFQResponseAccepted(const QString &reqId, const bs::network::Q quote_ = quote; if (rfq_.assetType == bs::network::Asset::SpotFX - || rfq_.assetType == bs::network::Asset::Futures) { + || rfq_.assetType == bs::network::Asset::DeliverableFutures) { quoteProvider_->AcceptQuoteFX(reqId, quote); } else { diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index dfd3c6419..1eb779242 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -321,7 +321,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) { const auto "eReqId = quoteProvider_->getQuoteReqId(order.quoteId); if (order.assetType == bs::network::Asset::SpotFX - || order.assetType == bs::network::Asset::Futures) { + || order.assetType == bs::network::Asset::DeliverableFutures) { if (order.status == bs::network::Order::Filled) { onSettlementComplete(quoteReqId); quoteProvider_->delQuoteReqId(quoteReqId); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 637b27dea..cec7b36a2 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -891,7 +891,7 @@ void RFQTicketXBT::submitButtonClicked() rfq->assetType = bs::network::Asset::PrivateMarket; break; case ProductGroupType::FuturesGroupType: - rfq->assetType = bs::network::Asset::Futures; + rfq->assetType = bs::network::Asset::DeliverableFutures; break; } @@ -1238,7 +1238,7 @@ void RFQTicketXBT::initProductGroupMap() , ProductGroupType::XBTGroupType); groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::SpotFX) , ProductGroupType::FXGroupType); - groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::Futures) + groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures) , ProductGroupType::FuturesGroupType); } diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index b325a1ac4..c9f2cd0d1 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -132,7 +132,7 @@ bool RequestingQuoteWidget::onQuoteReceived(const bs::network::Quote& quote) } if (quote.assetType == bs::network::Asset::SpotFX - || quote.assetType == bs::network::Asset::Futures) { + || quote.assetType == bs::network::Asset::DeliverableFutures) { ui_->pushButtonAccept->show(); setupTimer(Tradeable, quote.expirationTime.addMSecs(quote.timeSkewMs)); } else { diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index 70da22453..575bee913 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -155,10 +155,10 @@ WalletShieldBase::ProductType WalletShieldBase::getProductGroup(const QString &p else if (productGroup == QLatin1String("Spot FX")) { return ProductType::SpotFX; } - else if (productGroup == QLatin1String("1day Deliverable")) { - return ProductType::Futures; + else if (productGroup == QLatin1String("Deliverable 1-Day XBT")) { + return ProductType::DeliverableFutures; } - else if (productGroup == QLatin1String("1day Cash Settled")) { + else if (productGroup == QLatin1String("Non-Deliverable Perpetual XBT")) { return ProductType::CashSettledFutures; } #ifndef QT_NO_DEBUG diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index c067c6e22..3025f24d7 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -420,7 +420,7 @@ double UiUtils::truncatePriceForAsset(double price, bs::network::Asset::Type at) multiplier = 10000; break; case bs::network::Asset::SpotXBT: - case bs::network::Asset::Futures: + case bs::network::Asset::DeliverableFutures: multiplier = 100; break; case bs::network::Asset::PrivateMarket: @@ -440,7 +440,7 @@ QString UiUtils::displayPriceForAssetType(double price, bs::network::Asset::Type case bs::network::Asset::SpotFX: return UiUtils::displayPriceFX(price); case bs::network::Asset::SpotXBT: - case bs::network::Asset::Futures: + case bs::network::Asset::DeliverableFutures: case bs::network::Asset::CashSettledFutures: return UiUtils::displayPriceXBT(price); case bs::network::Asset::PrivateMarket: @@ -474,7 +474,8 @@ int UiUtils::GetPricePrecisionForAssetType(const bs::network::Asset::Type& asset case bs::network::Asset::SpotFX: return GetPricePrecisionFX(); case bs::network::Asset::SpotXBT: - case bs::network::Asset::Futures: + case bs::network::Asset::DeliverableFutures: + case bs::network::Asset::CashSettledFutures: return GetPricePrecisionXBT(); case bs::network::Asset::PrivateMarket: return GetPricePrecisionCC(); @@ -508,7 +509,8 @@ static void getPrecsFor(const std::string &security, const std::string &product, valuePrec = UiUtils::GetAmountPrecisionFX(); break; case bs::network::Asset::Type::SpotXBT: - case bs::network::Asset::Type::Futures: + case bs::network::Asset::Type::DeliverableFutures: + case bs::network::Asset::Type::CashSettledFutures: qtyPrec = UiUtils::GetAmountPrecisionXBT(); valuePrec = UiUtils::GetAmountPrecisionFX(); @@ -707,7 +709,7 @@ ApplicationSettings::Setting UiUtils::limitRfqSetting(bs::network::Asset::Type t case bs::network::Asset::PrivateMarket : return ApplicationSettings::PmRfqLimit; - case bs::network::Asset::Futures : + case bs::network::Asset::DeliverableFutures : return ApplicationSettings::FuturesLimit; default : @@ -725,7 +727,7 @@ ApplicationSettings::Setting UiUtils::limitRfqSetting(const QString &name) } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::PrivateMarket))) { return ApplicationSettings::PmRfqLimit; - } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::Futures))) { + } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures))) { return ApplicationSettings::FuturesLimit; } else { assert(false); @@ -746,7 +748,7 @@ QString UiUtils::marketNameForLimit(ApplicationSettings::Setting s) return QObject::tr(bs::network::Asset::toString(bs::network::Asset::PrivateMarket)); case ApplicationSettings::FuturesLimit : - return QObject::tr(bs::network::Asset::toString(bs::network::Asset::Futures)); + return QObject::tr(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures)); default : assert(false); diff --git a/BlockSettleUILib/UserScript.cpp b/BlockSettleUILib/UserScript.cpp index 4e5138da4..eb41f0002 100644 --- a/BlockSettleUILib/UserScript.cpp +++ b/BlockSettleUILib/UserScript.cpp @@ -168,7 +168,7 @@ double MarketData::ask(const QString &sec) const void MarketData::onMDUpdated(bs::network::Asset::Type assetType, const QString &security, bs::network::MDFields data) { - if (assetType == bs::network::Asset::Type::Futures) { + if (bs::network::Asset::isFuturesType(assetType)) { // ignore futures prices updates return; } diff --git a/common b/common index b740b24e7..9d27cb4ca 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit b740b24e71c4843b2979b9fee63f1ee1e1deb00e +Subproject commit 9d27cb4cac7d9360e594df25cc08642be045d866 From 0d809fdcdca030a84be35503a12e7a030ec17ac0 Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 18 Dec 2020 12:17:01 +0200 Subject: [PATCH 078/146] One more rename --- BlockSettleUILib/Trading/WalletShieldBase.cpp | 4 ++-- common | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp index 575bee913..d12f36a94 100644 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ b/BlockSettleUILib/Trading/WalletShieldBase.cpp @@ -155,10 +155,10 @@ WalletShieldBase::ProductType WalletShieldBase::getProductGroup(const QString &p else if (productGroup == QLatin1String("Spot FX")) { return ProductType::SpotFX; } - else if (productGroup == QLatin1String("Deliverable 1-Day XBT")) { + else if (productGroup == QLatin1String("XBT 1-day deliverable")) { return ProductType::DeliverableFutures; } - else if (productGroup == QLatin1String("Non-Deliverable Perpetual XBT")) { + else if (productGroup == QLatin1String("XBT 1-day rolling")) { return ProductType::CashSettledFutures; } #ifndef QT_NO_DEBUG diff --git a/common b/common index 9d27cb4ca..fd96edca2 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 9d27cb4cac7d9360e594df25cc08642be045d866 +Subproject commit fd96edca2e017747de990942c09cade2c95cfa6f From cf453ea3b21ce967bfcb5a295d8e3861ec30096e Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 18 Dec 2020 14:25:16 +0200 Subject: [PATCH 079/146] Update widget selection --- BlockSettleUILib/Trading/FuturesTicket.cpp | 3 +++ BlockSettleUILib/Trading/RFQRequestWidget.cpp | 10 +++++----- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 10 ---------- BlockSettleUILib/Trading/RFQTicketXBT.h | 3 +-- common | 2 +- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 8e6b6abb3..c14051a56 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -139,6 +139,9 @@ void FuturesTicket::init(const std::shared_ptr &logger void FuturesTicket::setType(bs::network::Asset::Type type) { type_ = type; + + ui_->pushButtonBuy->setEnabled(type == bs::network::Asset::CashSettledFutures); + ui_->pushButtonSell->setEnabled(type == bs::network::Asset::CashSettledFutures); } void FuturesTicket::SetCurrencyPair(const QString& currencyPair) diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 52995e063..39c233b2e 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -403,17 +403,17 @@ bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) using UserType = CelerClient::CelerUserType; const UserType userType = celerClient_->celerUserType(); - using GroupType = RFQShieldPage::ProductType; - const GroupType group = RFQShieldPage::getProductGroup(selectedInfo.productGroup_); + const auto group = RFQShieldPage::getProductGroup(selectedInfo.productGroup_); - if (group == GroupType::CashSettledFutures) { + if (group == WalletShieldBase::ProductType::CashSettledFutures + || group == WalletShieldBase::ProductType::DeliverableFutures) { showFuturesPage(group); return true; } switch (userType) { case UserType::Market: { - if (group == GroupType::SpotFX || group == GroupType::SpotXBT) { + if (group == WalletShieldBase::ProductType::SpotFX || group == WalletShieldBase::ProductType::SpotXBT) { ui_->shieldPage->showShieldReservedTradingParticipant(); popShield(); return false; @@ -424,7 +424,7 @@ bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) } case UserType::Dealing: case UserType::Trading: { - if ((group == GroupType::SpotXBT || group == GroupType::PrivateMarket) && + if ((group == WalletShieldBase::ProductType::SpotXBT || group == WalletShieldBase::ProductType::PrivateMarket) && checkWalletSettings(group, selectedInfo)) { return false; } diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index cec7b36a2..c428b04db 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -475,7 +475,6 @@ void RFQTicketXBT::SetProductGroup(const QString& productGroup) switch (currentGroupType_) { case ProductGroupType::FXGroupType: - case ProductGroupType::FuturesGroupType: ui_->groupBoxSettlementInputs->setVisible(false); break; case ProductGroupType::XBTGroupType: @@ -702,10 +701,6 @@ bs::Address RFQTicketXBT::recvXbtAddressIfSet() const bool RFQTicketXBT::checkBalance(double qty) const { - if (currentGroupType_ == ProductGroupType::FuturesGroupType) { - return true; - } - const auto balance = getBalanceInfo(); if (getSelectedSide() == bs::network::Side::Buy) { if (currentGroupType_ == ProductGroupType::CCGroupType) { @@ -890,9 +885,6 @@ void RFQTicketXBT::submitButtonClicked() case ProductGroupType::CCGroupType: rfq->assetType = bs::network::Asset::PrivateMarket; break; - case ProductGroupType::FuturesGroupType: - rfq->assetType = bs::network::Asset::DeliverableFutures; - break; } const auto &rfqId = CryptoPRNG::generateRandom(8).toHexStr(); @@ -1238,8 +1230,6 @@ void RFQTicketXBT::initProductGroupMap() , ProductGroupType::XBTGroupType); groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::SpotFX) , ProductGroupType::FXGroupType); - groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures) - , ProductGroupType::FuturesGroupType); } RFQTicketXBT::ProductGroupType RFQTicketXBT::getProductGroupType(const QString& productGroup) diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h index 04086a345..13cb8de66 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ b/BlockSettleUILib/Trading/RFQTicketXBT.h @@ -159,8 +159,7 @@ private slots: GroupNotSelected, FXGroupType, XBTGroupType, - CCGroupType, - FuturesGroupType + CCGroupType }; struct BalanceInfoContainer diff --git a/common b/common index fd96edca2..841a35c18 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit fd96edca2e017747de990942c09cade2c95cfa6f +Subproject commit 841a35c18baba8235fd964fc1f55c6cc14bd5c15 From 2b3083c18dbf03a5e5dac144fc23a54fe1cea081 Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 21 Dec 2020 09:26:34 +0200 Subject: [PATCH 080/146] Updte blotter --- BlockSettleUILib/OrderListModel.cpp | 2 +- common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 25bc785af..d08b3ea24 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -647,7 +647,7 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke beginInsertRows(createIndex(findMarket(sg, marketItem), 0, &marketItem->idx_), static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - if (getStatusGroup(order) == StatusGroup::UnSettled && order.assetType == bs::network::Asset::Type::DeliverableFutures) { + if (getStatusGroup(order) == StatusGroup::UnSettled && bs::network::Asset::isFuturesType(order.assetType)) { marketItem->rows_.emplace_back(make_unique( QString::fromStdString(order.security), &marketItem->idx_)); } else { diff --git a/common b/common index 841a35c18..4ae98232e 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 841a35c18baba8235fd964fc1f55c6cc14bd5c15 +Subproject commit 4ae98232ed1ae33d97d4890ee3e742bb896bb679 From 3aef7d804a4afcdef0b55501281a0e1112f89a50 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 23 Dec 2020 15:57:26 +0200 Subject: [PATCH 081/146] Minor fixes for 1day MD --- BlockSettleUILib/OrderListModel.cpp | 2 +- BlockSettleUILib/Trading/FuturesTicket.ui | 12 +++++- BlockSettleUILib/Trading/MarketDataModel.cpp | 44 ++++++++++++++++---- BlockSettleUILib/Trading/MarketDataWidget.h | 2 +- common | 2 +- 5 files changed, 50 insertions(+), 12 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index d08b3ea24..3432bc12b 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -647,7 +647,7 @@ void OrderListModel::createGroupsIfNeeded(const bs::network::Order &order, Marke beginInsertRows(createIndex(findMarket(sg, marketItem), 0, &marketItem->idx_), static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - if (getStatusGroup(order) == StatusGroup::UnSettled && bs::network::Asset::isFuturesType(order.assetType)) { + if (bs::network::Asset::isFuturesType(order.assetType)) { marketItem->rows_.emplace_back(make_unique( QString::fromStdString(order.security), &marketItem->idx_)); } else { diff --git a/BlockSettleUILib/Trading/FuturesTicket.ui b/BlockSettleUILib/Trading/FuturesTicket.ui index 5b62d3e6f..5ef1155ee 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.ui +++ b/BlockSettleUILib/Trading/FuturesTicket.ui @@ -1,4 +1,14 @@ + FuturesTicket @@ -77,7 +87,7 @@ - QUOTE REQUEST + REQUEST FOR STREAM 0 diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp index 18f3bfb5f..1ffcd1527 100644 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ b/BlockSettleUILib/Trading/MarketDataModel.cpp @@ -42,13 +42,13 @@ QString MarketDataModel::columnName(MarketDataColumns col) const { switch (col) { - case MarketDataColumns::Product: return tr("Security"); - case MarketDataColumns::BidPrice: return tr("Bid"); - case MarketDataColumns::OfferPrice: return tr("Ask"); - case MarketDataColumns::LastPrice: return tr("Last"); - case MarketDataColumns::DailyVol: return tr("24h Volume"); - case MarketDataColumns::EmptyColumn: return QString(); - default: return tr("Unknown"); + case MarketDataColumns::Product: return tr("Security"); + case MarketDataColumns::BidPrice: return tr("Bid"); + case MarketDataColumns::OfferPrice: return tr("Ask"); + case MarketDataColumns::LastPrice: return tr("Last"); + case MarketDataColumns::DailyVol: return tr("24h Volume"); + case MarketDataColumns::EmptyColumn: return QString(); + default: return tr("Unknown"); } } @@ -145,6 +145,34 @@ static void FieldsToMap(bs::network::Asset::Type at, const bs::network::MDFields } } +static void FutureFieldsToMap(bs::network::Asset::Type at, const bs::network::MDFields &fields, PriceMap &map) +{ + for (const auto &field : fields) { + switch (field.type) { + case bs::network::MDField::PriceBid: + if (field.isIndicativeForFutures()) { + map[MarketDataModel::MarketDataColumns::BidPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; + } + break; + case bs::network::MDField::PriceOffer: + if (field.isIndicativeForFutures()) { + map[MarketDataModel::MarketDataColumns::OfferPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; + } + break; + case bs::network::MDField::PriceLast: + map[MarketDataModel::MarketDataColumns::LastPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; + break; + case bs::network::MDField::DailyVolume: + map[MarketDataModel::MarketDataColumns::DailyVol] = { getVolumeString(field.value, at), field.value }; + break; + case bs::network::MDField::Reject: + map[MarketDataModel::MarketDataColumns::ColumnsCount] = { field.levelQuantity, 0 }; + break; + default: break; + } + } +} + bool MarketDataModel::isVisible(const QString &id) const { if (instrVisible_.empty()) { @@ -168,7 +196,7 @@ void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType, const QStr PriceMap fieldsMap; if (bs::network::Asset::isFuturesType(assetType)) { - FieldsToMap(bs::network::Asset::SpotXBT, mdFields, fieldsMap); + FutureFieldsToMap(bs::network::Asset::SpotXBT, mdFields, fieldsMap); } else { FieldsToMap(assetType, mdFields, fieldsMap); } diff --git a/BlockSettleUILib/Trading/MarketDataWidget.h b/BlockSettleUILib/Trading/MarketDataWidget.h index 6fc760c43..e75d2fcb1 100644 --- a/BlockSettleUILib/Trading/MarketDataWidget.h +++ b/BlockSettleUILib/Trading/MarketDataWidget.h @@ -111,7 +111,7 @@ class MDHeader : public QHeaderView QHeaderView::paintSection(painter, rect, logIndex); painter->restore(); - if (logIndex == 0) { + if (logIndex == 0) { QStyleOptionButton option; const QSize ch = checkboxSizeHint(); option.rect = QRect(2, (height() - ch.height()) / 2, ch.width(), ch.height()); diff --git a/common b/common index 4ae98232e..ed8e2b2f1 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 4ae98232ed1ae33d97d4890ee3e742bb896bb679 +Subproject commit ed8e2b2f102ef2e9e96b3b6715f34067ed47e898 From b447249d3b11d54dfe22093865b42f240088448a Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Wed, 23 Dec 2020 18:29:37 +0300 Subject: [PATCH 082/146] Enable deliverable futures --- BlockSettleUILib/Trading/FuturesTicket.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index c14051a56..9d9a79069 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -140,8 +140,8 @@ void FuturesTicket::setType(bs::network::Asset::Type type) { type_ = type; - ui_->pushButtonBuy->setEnabled(type == bs::network::Asset::CashSettledFutures); - ui_->pushButtonSell->setEnabled(type == bs::network::Asset::CashSettledFutures); + ui_->pushButtonBuy->setEnabled(bs::network::Asset::isFuturesType(type)); + ui_->pushButtonSell->setEnabled(bs::network::Asset::isFuturesType(type)); } void FuturesTicket::SetCurrencyPair(const QString& currencyPair) @@ -320,6 +320,7 @@ void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amou request.side = side; request.price = price; request.amount = amount; + request.type = type_; emit sendFutureRequestToPB(request); } From 39fdc91d10e4d4ee3c5cf8e7176144922710e399 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 23 Dec 2020 22:56:56 +0200 Subject: [PATCH 083/146] Update charting widget for new groups --- BlockSettleUILib/ChartWidget.cpp | 16 ++++++++++++---- common | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/BlockSettleUILib/ChartWidget.cpp b/BlockSettleUILib/ChartWidget.cpp index 36a0648ca..290293bc0 100644 --- a/BlockSettleUILib/ChartWidget.cpp +++ b/BlockSettleUILib/ChartWidget.cpp @@ -1071,10 +1071,18 @@ void ChartWidget::OnVolumeAxisRangeChanged(QCPRange newRange, QCPRange oldRange) QString ChartWidget::ProductTypeToString(TradeHistoryTradeType type) { switch (type) { - case FXTradeType: return QStringLiteral("FX"); - case XBTTradeType: return QStringLiteral("XBT"); - case PMTradeType: return QStringLiteral("PM"); - default: return QStringLiteral(""); + case FXTradeType: + return QStringLiteral("FX"); + case XBTTradeType: + return QStringLiteral("XBT"); + case PMTradeType: + return QStringLiteral("PM"); + case OneDayDeliverableTradeType: + return QString::fromStdString(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures)); + case OneDayCashSettledTradeType: + return QString::fromStdString(bs::network::Asset::toString(bs::network::Asset::CashSettledFutures)); + default: + return QStringLiteral(""); } } diff --git a/common b/common index ed8e2b2f1..9619abad1 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ed8e2b2f102ef2e9e96b3b6715f34067ed47e898 +Subproject commit 9619abad1602e0c474d545e3e7066e620d0237c1 From cca6d4021ab1b54857464ba8eb04c7a4b8e9d9d7 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 24 Dec 2020 15:48:25 +0300 Subject: [PATCH 084/146] Set focus to amount field when product selected --- BlockSettleUILib/Trading/FuturesTicket.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 9d9a79069..5d439856f 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -225,6 +225,10 @@ void FuturesTicket::productSelectionChanged() } updatePanel(); + + if (ui_->lineEditAmount->isEnabled()) { + ui_->lineEditAmount->setFocus(); + } } void FuturesTicket::updatePanel() From a24a46bc5ce1f4cd5044a5c9ede4f5bdc0351fb0 Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 28 Dec 2020 12:49:00 +0200 Subject: [PATCH 085/146] Update submodules --- Celer | 2 +- common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Celer b/Celer index 4106c127b..d94cdcdea 160000 --- a/Celer +++ b/Celer @@ -1 +1 @@ -Subproject commit 4106c127bef00b19e80adb575ac8fc1093a23582 +Subproject commit d94cdcdea94653ad581e4419fc74172dd0ab3b2f diff --git a/common b/common index 9619abad1..313a4cc1c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 9619abad1602e0c474d545e3e7066e620d0237c1 +Subproject commit 313a4cc1c3fbec13010618d3b4afcf89eec93a8e From cd0ea1834b8751d0dddacb1514fa5e11fd622d90 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 31 Dec 2020 17:43:05 +0300 Subject: [PATCH 086/146] Allow sending delivery addr --- BlockSettleUILib/BSTerminalMainWindow.cpp | 53 +++++++++++++++++++++++ BlockSettleUILib/BSTerminalMainWindow.h | 6 +++ BlockSettleUILib/BSTerminalMainWindow.ui | 6 +++ 3 files changed, 65 insertions(+) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 8fe593fdf..994631bf9 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -227,6 +228,17 @@ void BSTerminalMainWindow::onInitWalletDialogWasShown() initialWalletCreateDialogShown_ = true; } +void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &response) +{ + switch (response.data_case()) { + case ProxyTerminalPb::Response::kDeliveryAddress: + processSetDeliveryAddr(response.delivery_address()); + break; + default: + break; + } +} + void BSTerminalMainWindow::setWidgetsAuthorized(bool authorized) { // Update authorized state for some widgets @@ -1354,6 +1366,7 @@ void BSTerminalMainWindow::setupMenu() connect(ui_->actionCreateNewWallet, &QAction::triggered, this, [ww = ui_->widgetWallets]{ ww->onNewWallet(); }); connect(ui_->actionOpenURI, &QAction::triggered, this, [this]{ openURIDialog(); }); connect(ui_->actionAuthenticationAddresses, &QAction::triggered, this, &BSTerminalMainWindow::openAuthManagerDialog); + connect(ui_->actionSetDeliveryAddress, &QAction::triggered, this, &BSTerminalMainWindow::onSetDeliveryAddr); connect(ui_->actionSettings, &QAction::triggered, this, [=]() { openConfigDialog(); }); connect(ui_->actionAccountInformation, &QAction::triggered, this, &BSTerminalMainWindow::openAccountInfoDialog); connect(ui_->actionEnterColorCoinToken, &QAction::triggered, this, &BSTerminalMainWindow::openCCTokenDialog); @@ -1392,6 +1405,27 @@ void BSTerminalMainWindow::openAuthManagerDialog() authAddrDlg_->exec(); } +void BSTerminalMainWindow::onSetDeliveryAddr() +{ + auto addr = QInputDialog::getText(this, tr("Delivery Address"), tr("Address")).toStdString(); + if (addr.empty()) { + return; + } + try { + bs::Address::fromAddressString(addr); + } catch (const std::exception &e) { + BSMessageBox(BSMessageBox::critical, tr("Delivery Address") + , tr("Error: %1").arg(QString::fromStdString(e.what()))).exec(); + return; + } + if (!bsClient_) { + BSMessageBox(BSMessageBox::critical, tr("Delivery Address") + , tr("Connection closed")).exec(); + return; + } + bsClient_->setFuturesDeliveryAddr(addr); +} + void BSTerminalMainWindow::openConfigDialog(bool showInNetworkPage) { auto oldEnv = static_cast( @@ -1531,6 +1565,8 @@ void BSTerminalMainWindow::onUserLoggedIn() ui_->actionAccountInformation->setEnabled(true); ui_->actionAuthenticationAddresses->setEnabled(celerConnection_->celerUserType() != BaseCelerClient::CelerUserType::Market); + ui_->actionSetDeliveryAddress->setEnabled(celerConnection_->celerUserType() + != BaseCelerClient::CelerUserType::Market); ui_->actionOneTimePassword->setEnabled(true); ui_->actionEnterColorCoinToken->setEnabled(true); @@ -1556,6 +1592,7 @@ void BSTerminalMainWindow::onUserLoggedOut() { ui_->actionAccountInformation->setEnabled(false); ui_->actionAuthenticationAddresses->setEnabled(false); + ui_->actionSetDeliveryAddress->setEnabled(false); ui_->actionEnterColorCoinToken->setEnabled(false); ui_->actionOneTimePassword->setEnabled(false); @@ -2234,6 +2271,7 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli // connect to RFQ dialog connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); + connect(bsClient_.get(), &BsClient::processPbMessage, this, &BSTerminalMainWindow::onMessageFromPB); connect(bsClient_.get(), &BsClient::disconnected, ui_->widgetRFQ, &RFQRequestWidget::onUserDisconnected); connect(ui_->widgetRFQ, &RFQRequestWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClient::sendUnsignedPayin); connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayinToPB, bsClient_.get(), &BsClient::sendSignedPayin); @@ -2465,6 +2503,21 @@ void BSTerminalMainWindow::onBootstrapDataLoaded(const std::string& data) } } +void BSTerminalMainWindow::processSetDeliveryAddr(const ProxyTerminalPb::Response_DeliveryAddress &resp) +{ + if (!resp.success()) { + addDeferredDialog([this, errorMsg = resp.error_msg()] { + BSMessageBox(BSMessageBox::critical, tr("Delivery Address") + , tr("Error: %1").arg(QString::fromStdString(errorMsg)), this).exec(); + }); + return; + } + addDeferredDialog([this] { + BSMessageBox(BSMessageBox::info, tr("Delivery Address") + , tr("Address submitted"), this).exec(); + }); +} + void BSTerminalMainWindow::onAuthLeafCreated() { auto authWallet = walletsMgr_->getAuthWallet(); diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index f55622008..3c8f6f721 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -45,6 +45,7 @@ namespace Blocksettle { namespace Communication { namespace ProxyTerminalPb { class Response; + class Response_DeliveryAddress; } } } @@ -244,6 +245,7 @@ private slots: void onGenerateAddress(); void openAuthManagerDialog(); + void onSetDeliveryAddr(); void openConfigDialog(bool showInNetworkPage = false); void openAccountInfoDialog(); void openCCTokenDialog(); @@ -265,6 +267,8 @@ private slots: void onInitWalletDialogWasShown(); + void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); + protected: void closeEvent(QCloseEvent* event) override; void changeEvent(QEvent* e) override; @@ -306,6 +310,8 @@ private slots: void onBootstrapDataLoaded(const std::string& data); + void processSetDeliveryAddr(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryAddress &resp); + private: enum class ChatInitState { diff --git a/BlockSettleUILib/BSTerminalMainWindow.ui b/BlockSettleUILib/BSTerminalMainWindow.ui index 7638a8c3c..99acdfb26 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.ui +++ b/BlockSettleUILib/BSTerminalMainWindow.ui @@ -561,6 +561,7 @@ + @@ -922,6 +923,11 @@ Open bitcoin URI or payment request + + + Set Delivery Address + + From f1c4562fa2b77b690e59e077e591539a54ee03be Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 31 Dec 2020 17:43:24 +0300 Subject: [PATCH 087/146] Fix futures balances --- BlockSettleUILib/Trading/FuturesTicket.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 5d439856f..41ab2dc9b 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -233,10 +233,12 @@ void FuturesTicket::productSelectionChanged() void FuturesTicket::updatePanel() { + // currentProduct_ will be set to EURP or EURD, query EUR instead + auto currentProduct = "EUR"; const double balance = assetManager_ ? - assetManager_->getBalance(currentProduct_, false, nullptr) : 0.0; + assetManager_->getBalance(currentProduct, false, nullptr) : 0.0; auto amountString = UiUtils::displayCurrencyAmount(balance); - QString text = tr("%1 %2").arg(amountString).arg(QString::fromStdString(currentProduct_)); + QString text = tr("%1 %2").arg(amountString).arg(QString::fromStdString(currentProduct)); ui_->labelBalanceValue->setText(text); ui_->labelFutureBalanceValue->setText(UiUtils::displayAmount(assetManager_->netDeliverableBalanceXbt())); From 96763e1ace794300c59c3f1ec5328669d1843db4 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 7 Jan 2021 16:41:36 +0300 Subject: [PATCH 088/146] Merge from common/terminal_arch_refactor --- BlockSettleSigner/HeadlessApp.cpp | 1 + BlockSettleSigner/SignerAdapter.cpp | 6 +- BlockSettleSigner/SignerAdapter.h | 3 +- BlockSettleSigner/SignerAdapterContainer.cpp | 4 +- BlockSettleSigner/SignerAdapterContainer.h | 12 +- BlockSettleSigner/SignerAdapterListener.h | 10 +- BlockSettleSigner/SignerInterfaceListener.cpp | 8 +- BlockSettleSigner/main.cpp | 2 +- BlockSettleUILib/AuthAddressDialog.cpp | 10 +- BlockSettleUILib/AuthAddressDialog.h | 2 +- BlockSettleUILib/BSTerminalMainWindow.cpp | 127 ++++++++++-------- BlockSettleUILib/BSTerminalMainWindow.h | 14 +- BlockSettleUILib/CelerAccountInfoDialog.cpp | 2 +- BlockSettleUILib/CreateTransactionDialog.cpp | 11 +- BlockSettleUILib/CreateTransactionDialog.h | 6 +- .../CreateTransactionDialogAdvanced.cpp | 10 +- .../CreateTransactionDialogAdvanced.h | 9 +- .../CreateTransactionDialogSimple.cpp | 4 +- .../CreateTransactionDialogSimple.h | 4 +- BlockSettleUILib/LoginWindow.cpp | 6 +- BlockSettleUILib/LoginWindow.h | 8 +- .../RootWalletPropertiesDialog.cpp | 10 +- .../RootWalletPropertiesDialog.h | 6 +- BlockSettleUILib/PortfolioWidget.cpp | 2 +- BlockSettleUILib/PortfolioWidget.h | 4 +- BlockSettleUILib/StatusBarView.cpp | 23 ++-- BlockSettleUILib/StatusBarView.h | 9 +- .../Trading/AutoSignQuoteProvider.cpp | 32 ++--- .../Trading/AutoSignQuoteProvider.h | 24 ++-- .../Trading/DealerXBTSettlementContainer.cpp | 11 +- .../Trading/DealerXBTSettlementContainer.h | 8 +- BlockSettleUILib/Trading/OtcClient.cpp | 5 +- BlockSettleUILib/Trading/OtcClient.h | 2 +- .../Trading/QuoteRequestsModel.cpp | 18 +-- BlockSettleUILib/Trading/QuoteRequestsModel.h | 8 +- .../Trading/QuoteRequestsWidget.cpp | 2 +- .../Trading/QuoteRequestsWidget.h | 5 +- BlockSettleUILib/Trading/RFQDealerReply.cpp | 3 +- BlockSettleUILib/Trading/RFQDialog.cpp | 6 +- BlockSettleUILib/Trading/RFQDialog.h | 12 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 21 +-- BlockSettleUILib/Trading/RFQReplyWidget.h | 12 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 13 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 12 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 9 +- BlockSettleUILib/Trading/RFQTicketXBT.h | 6 +- .../Trading/ReqCCSettlementContainer.cpp | 9 +- .../Trading/ReqCCSettlementContainer.h | 6 +- .../Trading/ReqXBTSettlementContainer.cpp | 10 +- .../Trading/ReqXBTSettlementContainer.h | 6 +- .../Trading/RequestingQuoteWidget.cpp | 7 +- .../Trading/RequestingQuoteWidget.h | 6 +- BlockSettleUILib/TransactionsWidget.cpp | 2 +- BlockSettleUILib/TransactionsWidget.h | 4 +- .../TransactionsWidgetInterface.cpp | 8 +- .../TransactionsWidgetInterface.h | 6 +- BlockSettleUILib/UserScriptRunner.cpp | 2 +- BlockSettleUILib/WalletsViewModel.cpp | 15 ++- BlockSettleUILib/WalletsViewModel.h | 6 +- BlockSettleUILib/WalletsWidget.cpp | 9 +- BlockSettleUILib/WalletsWidget.h | 6 +- UnitTests/TestAuth.cpp | 4 +- UnitTests/TestAuth.h | 2 + UnitTests/TestCCoin.cpp | 5 +- UnitTests/TestCCoin.h | 2 + UnitTests/TestCCoinAsync.cpp | 5 +- UnitTests/TestCCoinAsync.h | 2 + UnitTests/TestCommon.cpp | 6 +- UnitTests/TestEnv.cpp | 6 +- UnitTests/TestEnv.h | 6 +- UnitTests/TestNetwork.cpp | 11 +- UnitTests/TestOtc.cpp | 7 +- UnitTests/TestSettlement.cpp | 4 +- UnitTests/TestSettlement.h | 2 + UnitTests/TestWallet.cpp | 26 +++- UnitTests/TestWalletArmory.cpp | 45 +++++-- common | 2 +- 77 files changed, 441 insertions(+), 318 deletions(-) diff --git a/BlockSettleSigner/HeadlessApp.cpp b/BlockSettleSigner/HeadlessApp.cpp index fa418623e..25f0382d1 100644 --- a/BlockSettleSigner/HeadlessApp.cpp +++ b/BlockSettleSigner/HeadlessApp.cpp @@ -36,6 +36,7 @@ #include "bs_signer.pb.h" using namespace bs::error; +using namespace Blocksettle::Communication; HeadlessAppObj::HeadlessAppObj(const std::shared_ptr &logger , const std::shared_ptr ¶ms, const std::shared_ptr &queue) diff --git a/BlockSettleSigner/SignerAdapter.cpp b/BlockSettleSigner/SignerAdapter.cpp index ba59f0f4f..61e3de145 100644 --- a/BlockSettleSigner/SignerAdapter.cpp +++ b/BlockSettleSigner/SignerAdapter.cpp @@ -44,9 +44,7 @@ SignerAdapter::SignerAdapter(const std::shared_ptr &logger , const std::shared_ptr &qmlBridge , const NetworkType netType, int signerPort , std::shared_ptr adapterConn) - : QObject(nullptr) - ,logger_(logger) - , netType_(netType) + : QtHCT(nullptr), logger_(logger), netType_(netType) , qmlBridge_(qmlBridge) { listener_ = std::make_shared(logger, qmlBridge_, adapterConn, this); @@ -54,7 +52,7 @@ SignerAdapter::SignerAdapter(const std::shared_ptr &logger , listener_.get())) { throw std::runtime_error("adapter connection failed"); } - signContainer_ = std::make_shared(logger_, listener_); + signContainer_ = std::make_shared(logger_, this, listener_); } std::shared_ptr SignerAdapter::instantiateAdapterConnection( diff --git a/BlockSettleSigner/SignerAdapter.h b/BlockSettleSigner/SignerAdapter.h index 157912e47..a09745bbc 100644 --- a/BlockSettleSigner/SignerAdapter.h +++ b/BlockSettleSigner/SignerAdapter.h @@ -17,6 +17,7 @@ #include "QmlBridge.h" #include "QmlFactory.h" #include "QPasswordData.h" +#include "HeadlessContainer.h" #include "SignerDefs.h" #include "bs_signer.pb.h" @@ -45,7 +46,7 @@ class SignAdapterContainer; class SignerInterfaceListener; class DataConnection; -class SignerAdapter : public QObject +class SignerAdapter : public QtHCT { Q_OBJECT friend class SignerInterfaceListener; diff --git a/BlockSettleSigner/SignerAdapterContainer.cpp b/BlockSettleSigner/SignerAdapterContainer.cpp index faa382a44..cba832169 100644 --- a/BlockSettleSigner/SignerAdapterContainer.cpp +++ b/BlockSettleSigner/SignerAdapterContainer.cpp @@ -13,7 +13,7 @@ #include #include #include -#include "CelerClientConnection.h" +#include "Celer/ClientConnection.h" #include "DataConnection.h" #include "DataConnectionListener.h" #include "HeadlessApp.h" @@ -48,7 +48,7 @@ void SignAdapterContainer::syncWalletInfo(const std::function &logger - , const std::shared_ptr &lsn) - : WalletSignerContainer(logger, OpMode::LocalInproc), listener_(lsn) + , SignerCallbackTarget *sct, const std::shared_ptr &lsn) + : WalletSignerContainer(logger, sct, OpMode::LocalInproc), listener_(lsn) {} ~SignAdapterContainer() noexcept override = default; @@ -34,6 +34,10 @@ class SignAdapterContainer : public WalletSignerContainer bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & , const SecureBinaryData &password); + void signTXRequest(const bs::core::wallet::TXSignRequest& + , const std::function& + , TXSignMode mode = TXSignMode::Full, bool keepDuplicatedRecipients = false) override {} bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & , TXSignMode = TXSignMode::Full, bool = false) override { return 0; } @@ -98,8 +102,8 @@ class SignAdapterContainer : public WalletSignerContainer const std::set&, std::function) override {} void extendAddressChain(const std::string&, unsigned, bool, const std::function> &)> &) override {} - void syncNewAddresses(const std::string &, const std::vector & - , const std::function> &)> &, bool = true) override {} + void syncNewAddresses(const std::string& walletId, const std::vector& + , const std::function>&)>&) override {} void getChatNode(const std::string &walletID, const std::function &) override {} void setSettlAuthAddr(const std::string &walletId, const BinaryData &, const bs::Address &addr) override {} void getSettlAuthAddr(const std::string &walletId, const BinaryData & diff --git a/BlockSettleSigner/SignerAdapterListener.h b/BlockSettleSigner/SignerAdapterListener.h index f13392dd3..b0076299e 100644 --- a/BlockSettleSigner/SignerAdapterListener.h +++ b/BlockSettleSigner/SignerAdapterListener.h @@ -34,6 +34,13 @@ namespace bs { class TransportBIP15xServer; } } +namespace Blocksettle { + namespace Communication { + namespace signer { + enum ControlPasswordStatus; + } + } +} class DispatchQueue; class HeadlessAppObj; class HeadlessContainerCallbacks; @@ -41,6 +48,7 @@ class HeadlessContainerCallbacksImpl; class HeadlessSettings; class ServerConnection; + class SignerAdapterListener : public ServerConnectionListener { public: @@ -54,7 +62,7 @@ class SignerAdapterListener : public ServerConnectionListener // Sent to GUI status update message void sendStatusUpdate(); - void sendControlPasswordStatusUpdate(signer::ControlPasswordStatus status); + void sendControlPasswordStatusUpdate(Blocksettle::Communication::signer::ControlPasswordStatus status); void resetConnection(); diff --git a/BlockSettleSigner/SignerInterfaceListener.cpp b/BlockSettleSigner/SignerInterfaceListener.cpp index 7e5046bd7..11b0fca3e 100644 --- a/BlockSettleSigner/SignerInterfaceListener.cpp +++ b/BlockSettleSigner/SignerInterfaceListener.cpp @@ -16,7 +16,7 @@ #include #include #include -#include "CelerClientConnection.h" +#include "Celer/ClientConnection.h" #include "DataConnection.h" #include "HeadlessApp.h" #include "Wallets/SyncWalletsManager.h" @@ -426,10 +426,10 @@ void SignerInterfaceListener::onSyncHDWallet(const std::string &data, bs::signer std::vector leaves; for (int j = 0; j < group.leaves_size(); ++j) { const auto leaf = group.leaves(j); - leaves.push_back({ leaf.id(), bs::hd::Path::fromString(leaf.path()) - , false, BinaryData::fromString(leaf.extra_data()) }); + leaves.push_back({ {leaf.id()}, bs::hd::Path::fromString(leaf.path()) + , "", "" , false, BinaryData::fromString(leaf.extra_data()) }); } - result.groups.push_back({ static_cast(group.type()), leaves }); + result.groups.push_back({ static_cast(group.type()), "", "", leaves }); } itCb->second(result); cbHDWalletData_.erase(itCb); diff --git a/BlockSettleSigner/main.cpp b/BlockSettleSigner/main.cpp index 02ed8cc54..d4ab5a8ab 100644 --- a/BlockSettleSigner/main.cpp +++ b/BlockSettleSigner/main.cpp @@ -336,7 +336,7 @@ static int QMLApp(int argc, char **argv terminalConnectionTimer.setSingleShot(true); //BST-2786 - terminalConnectionTimer.setInterval(std::chrono::milliseconds{ 5000 }); + terminalConnectionTimer.setInterval(std::chrono::milliseconds{ 25000 }); QObject::connect(&adapter, &SignerAdapter::ready, [&timerStarted, &terminalConnectionTimer]() { diff --git a/BlockSettleUILib/AuthAddressDialog.cpp b/BlockSettleUILib/AuthAddressDialog.cpp index b4ed294e0..b42070078 100644 --- a/BlockSettleUILib/AuthAddressDialog.cpp +++ b/BlockSettleUILib/AuthAddressDialog.cpp @@ -52,7 +52,8 @@ AuthAddressDialog::AuthAddressDialog(const std::shared_ptr &logg connect(model_, &AuthAdressControlProxyModel::modelReset, this, &AuthAddressDialog::onModelReset); connect(originModel, &AuthAddressViewModel::updateSelectionAfterReset, this, &AuthAddressDialog::onUpdateSelection); - connect(authAddressManager_.get(), &AuthAddressManager::AddrVerifiedOrRevoked, this, &AuthAddressDialog::onAddressStateChanged, Qt::QueuedConnection); + connect(authAddressManager_.get(), &AuthAddressManager::AddrVerifiedOrRevoked + , this, &AuthAddressDialog::onAddressStateChanged, Qt::QueuedConnection); connect(authAddressManager_.get(), &AuthAddressManager::Error, this, &AuthAddressDialog::onAuthMgrError, Qt::QueuedConnection); connect(authAddressManager_.get(), &AuthAddressManager::Info, this, &AuthAddressDialog::onAuthMgrInfo, Qt::QueuedConnection); @@ -234,14 +235,15 @@ void AuthAddressDialog::copySelectedToClipboard() } -void AuthAddressDialog::onAddressStateChanged(const QString &addr, const QString &state) +void AuthAddressDialog::onAddressStateChanged(const QString &addr, int st) { - if (state == QStringLiteral("Verified")) { + const auto& state = static_cast(st); + if (state == AuthCallbackTarget::AuthAddressState::Verified) { BSMessageBox(BSMessageBox::success, tr("Authentication Address") , tr("Authentication Address verified") , tr("You may now place orders in the Spot XBT product group.") ).exec(); - } else if (state == QStringLiteral("Revoked")) { + } else if (state == AuthCallbackTarget::AuthAddressState::Revoked) { BSMessageBox(BSMessageBox::warning, tr("Authentication Address") , tr("Authentication Address revoked") , tr("Authentication Address %1 was revoked and could not be used for Spot XBT trading.").arg(addr)).exec(); diff --git a/BlockSettleUILib/AuthAddressDialog.h b/BlockSettleUILib/AuthAddressDialog.h index 53d41a90f..c8f06f81b 100644 --- a/BlockSettleUILib/AuthAddressDialog.h +++ b/BlockSettleUILib/AuthAddressDialog.h @@ -65,7 +65,7 @@ private slots: void setDefaultAddress(); void onModelReset(); - void onAddressStateChanged(const QString &addr, const QString &state); + void onAddressStateChanged(const QString &addr, int state); void onAuthMgrError(const QString &details); void onAuthMgrInfo(const QString &text); diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 994631bf9..4fcec2481 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -87,6 +87,7 @@ #include "bs_proxy_terminal_pb.pb.h" +using namespace Blocksettle::Communication; namespace { const auto kAutoLoginTimer = std::chrono::seconds(10); } @@ -228,7 +229,7 @@ void BSTerminalMainWindow::onInitWalletDialogWasShown() initialWalletCreateDialogShown_ = true; } -void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response &response) +void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response& response) { switch (response.data_case()) { case ProxyTerminalPb::Response::kDeliveryAddress: @@ -451,9 +452,9 @@ void BSTerminalMainWindow::initConnections() connectionManager_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); celerConnection_ = std::make_shared(logMgr_->logger()); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectedToServer, this, &BSTerminalMainWindow::onCelerConnected); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectionClosed, this, &BSTerminalMainWindow::onCelerDisconnected); - connect(celerConnection_.get(), &BaseCelerClient::OnConnectionError, this, &BSTerminalMainWindow::onCelerConnectionError, Qt::QueuedConnection); + connect(celerConnection_.get(), &CelerClientQt::OnConnectedToServer, this, &BSTerminalMainWindow::onCelerConnected); + connect(celerConnection_.get(), &CelerClientQt::OnConnectionClosed, this, &BSTerminalMainWindow::onCelerDisconnected); + connect(celerConnection_.get(), &CelerClientQt::OnConnectionError, this, &BSTerminalMainWindow::onCelerConnectionError, Qt::QueuedConnection); mdCallbacks_ = std::make_shared(); mdProvider_ = std::make_shared(connectionManager_ @@ -518,8 +519,9 @@ void BSTerminalMainWindow::InitAuthManager() authManager_ = std::make_shared(logMgr_->logger(), armory_); authManager_->init(applicationSettings_, walletsMgr_, signContainer_); - connect(authManager_.get(), &AuthAddressManager::AddrVerifiedOrRevoked, this, [](const QString &addr, const QString &state) { - NotificationCenter::notify(bs::ui::NotifyType::AuthAddress, { addr, state }); + connect(authManager_.get(), &AuthAddressManager::AddrVerifiedOrRevoked, this + , [](const QString &addr, int state) { //FIXME: state should be converted to a real string + NotificationCenter::notify(bs::ui::NotifyType::AuthAddress, { addr, QString::number(state) }); }); connect(authManager_.get(), &AuthAddressManager::AuthWalletCreated, this, [this](const QString &walletId) { if (authAddrDlg_ && walletId.isEmpty()) { @@ -585,10 +587,11 @@ std::shared_ptr BSTerminalMainWindow::createRemoteSigner( addDeferredDialog(deferredDialog); }; + const auto hct = new QtHCT(this); QString resultHost = signerHost.address; const auto remoteSigner = std::make_shared(logMgr_->logger() , resultHost, resultPort, netType, connectionManager_ - , SignContainer::OpMode::Remote, false + , hct, SignContainer::OpMode::Remote, false , signersProvider_->remoteSignerKeysDir(), signersProvider_->remoteSignerKeysFile(), ourNewKeyCB); bs::network::BIP15xPeers peers; @@ -639,30 +642,36 @@ std::shared_ptr BSTerminalMainWindow::createLocalSigner() return nullptr; } + const auto hct = new QtHCT(this); const bool startLocalSignerProcess = true; return std::make_shared(logMgr_->logger() , applicationSettings_->GetHomeDir(), netType , localSignerPort, connectionManager_ - , startLocalSignerProcess, "", "" + , hct, startLocalSignerProcess, "", "" , applicationSettings_->get(ApplicationSettings::autoSignSpendLimit)); } bool BSTerminalMainWindow::InitSigningContainer() { - signContainer_ = createSigner(); - + signContainer_ = std::dynamic_pointer_cast(createSigner()); if (!signContainer_) { showError(tr("BlockSettle Signer"), tr("BlockSettle Signer creation failure")); return false; } - connect(signContainer_.get(), &SignContainer::connectionError, this, &BSTerminalMainWindow::onSignerConnError, Qt::QueuedConnection); - connect(signContainer_.get(), &SignContainer::disconnected, this, &BSTerminalMainWindow::updateControlEnabledState, Qt::QueuedConnection); + const auto hct = dynamic_cast(signContainer_->cbTarget()); + if (!hct) { + showError(tr("BlockSettle Signer"), tr("BlockSettle Signer improper creation")); + return false; + } + + connect(hct, &QtHCT::connectionError, this, &BSTerminalMainWindow::onSignerConnError, Qt::QueuedConnection); + connect(hct, &QtHCT::disconnected, this, &BSTerminalMainWindow::updateControlEnabledState, Qt::QueuedConnection); walletsMgr_->setSignContainer(signContainer_); - connect(signContainer_.get(), &WalletSignerContainer::ready, this, &BSTerminalMainWindow::SignerReady, Qt::QueuedConnection); - connect(signContainer_.get(), &WalletSignerContainer::needNewWalletPrompt, this, &BSTerminalMainWindow::onNeedNewWallet, Qt::QueuedConnection); - connect(signContainer_.get(), &WalletSignerContainer::walletsReadyToSync, this, &BSTerminalMainWindow::onSyncWallets, Qt::QueuedConnection); - connect(signContainer_.get(), &WalletSignerContainer::windowVisibilityChanged, this, &BSTerminalMainWindow::onSignerVisibleChanged, Qt::QueuedConnection); + connect(hct, &QtHCT::ready, this, &BSTerminalMainWindow::SignerReady, Qt::QueuedConnection); + connect(hct, &QtHCT::needNewWalletPrompt, this, &BSTerminalMainWindow::onNeedNewWallet, Qt::QueuedConnection); + connect(hct, &QtHCT::walletsReadyToSync, this, &BSTerminalMainWindow::onSyncWallets, Qt::QueuedConnection); + connect(hct, &QtHCT::windowVisibilityChanged, this, &BSTerminalMainWindow::onSignerVisibleChanged, Qt::QueuedConnection); return true; } @@ -2219,10 +2228,10 @@ void BSTerminalMainWindow::processDeferredDialogs() deferredDialogRunning_ = false; } -std::shared_ptr BSTerminalMainWindow::createClient() +std::shared_ptr BSTerminalMainWindow::createClient() { auto logger = logMgr_->logger("proxy"); - auto bsClient = std::make_shared(logger); + auto bsClient = std::make_shared(logger); bs::network::BIP15xParams params; params.ephemeralPeers = true; @@ -2240,13 +2249,13 @@ std::shared_ptr BSTerminalMainWindow::createClient() bsClient->setConnection(std::move(connection)); // Must be connected before loginDialog.exec call (balances could be received before loginDialog.exec returns)! - connect(bsClient.get(), &BsClient::balanceLoaded, assetManager_.get(), &AssetManager::fxBalanceLoaded); - connect(bsClient.get(), &BsClient::balanceUpdated, assetManager_.get(), &AssetManager::onAccountBalanceLoaded); + connect(bsClient.get(), &BsClientQt::balanceLoaded, assetManager_.get(), &AssetManager::fxBalanceLoaded); + connect(bsClient.get(), &BsClientQt::balanceUpdated, assetManager_.get(), &AssetManager::onAccountBalanceLoaded); return bsClient; } -void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsClient +void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsClient , const BsClientLoginResult &result, const std::string &email) { currentUserLogin_ = QString::fromStdString(email); @@ -2266,41 +2275,41 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli onBootstrapDataLoaded(result.bootstrapDataSigned); - connect(bsClient_.get(), &BsClient::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); - connect(bsClient_.get(), &BsClient::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); + connect(bsClient_.get(), &BsClientQt::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); + connect(bsClient_.get(), &BsClientQt::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); // connect to RFQ dialog - connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); - connect(bsClient_.get(), &BsClient::processPbMessage, this, &BSTerminalMainWindow::onMessageFromPB); - connect(bsClient_.get(), &BsClient::disconnected, ui_->widgetRFQ, &RFQRequestWidget::onUserDisconnected); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClient::sendUnsignedPayin); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayinToPB, bsClient_.get(), &BsClient::sendSignedPayin); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClient::sendSignedPayout); + connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); + connect(bsClient_.get(), &BsClientQt::processPbMessage, this, &BSTerminalMainWindow::onMessageFromPB); + connect(bsClient_.get(), &BsClientQt::disconnected, ui_->widgetRFQ, &RFQRequestWidget::onUserDisconnected); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClientQt::sendUnsignedPayin); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayinToPB, bsClient_.get(), &BsClientQt::sendSignedPayin); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClientQt::sendSignedPayout); - connect(ui_->widgetRFQ, &RFQRequestWidget::cancelXBTTrade, bsClient_.get(), &BsClient::sendCancelOnXBTTrade); - connect(ui_->widgetRFQ, &RFQRequestWidget::cancelCCTrade, bsClient_.get(), &BsClient::sendCancelOnCCTrade); + connect(ui_->widgetRFQ, &RFQRequestWidget::cancelXBTTrade, bsClient_.get(), &BsClientQt::sendCancelOnXBTTrade); + connect(ui_->widgetRFQ, &RFQRequestWidget::cancelCCTrade, bsClient_.get(), &BsClientQt::sendCancelOnCCTrade); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendFutureRequestToPB, bsClient_.get(), &BsClient::sendFutureRequest); + connect(ui_->widgetRFQ, &RFQRequestWidget::sendFutureRequestToPB, bsClient_.get(), &BsClientQt::sendFutureRequest); // connect to quote dialog - connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetRFQReply, &RFQReplyWidget::onMessageFromPB); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClient::sendUnsignedPayin); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayinToPB, bsClient_.get(), &BsClient::sendSignedPayin); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClient::sendSignedPayout); + connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetRFQReply, &RFQReplyWidget::onMessageFromPB); + connect(ui_->widgetRFQReply, &RFQReplyWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClientQt::sendUnsignedPayin); + connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayinToPB, bsClient_.get(), &BsClientQt::sendSignedPayin); + connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClientQt::sendSignedPayout); - connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelXBTTrade, bsClient_.get(), &BsClient::sendCancelOnXBTTrade); - connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelCCTrade, bsClient_.get(), &BsClient::sendCancelOnCCTrade); + connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelXBTTrade, bsClient_.get(), &BsClientQt::sendCancelOnXBTTrade); + connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelCCTrade, bsClient_.get(), &BsClientQt::sendCancelOnCCTrade); - connect(ui_->widgetChat, &ChatWidget::emailHashRequested, bsClient_.get(), &BsClient::findEmailHash); - connect(bsClient_.get(), &BsClient::emailHashReceived, ui_->widgetChat, &ChatWidget::onEmailHashReceived); + connect(ui_->widgetChat, &ChatWidget::emailHashRequested, bsClient_.get(), &BsClientQt::findEmailHash); + connect(bsClient_.get(), &BsClientQt::emailHashReceived, ui_->widgetChat, &ChatWidget::onEmailHashReceived); - connect(bsClient_.get(), &BsClient::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); - connect(bsClient_.get(), &BsClient::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); + connect(bsClient_.get(), &BsClientQt::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); + connect(bsClient_.get(), &BsClientQt::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); - connect(bsClient_.get(), &BsClient::processPbMessage, assetManager_.get(), &AssetManager::onMessageFromPB); + connect(bsClient_.get(), &BsClientQt::processPbMessage, assetManager_.get(), &AssetManager::onMessageFromPB); utxoReservationMgr_->setFeeRatePb(result.feeRatePb); - connect(bsClient_.get(), &BsClient::feeRateReceived, this, [this] (float feeRate) { + connect(bsClient_.get(), &BsClientQt::feeRateReceived, this, [this] (float feeRate) { utxoReservationMgr_->setFeeRatePb(feeRate); }); @@ -2318,20 +2327,20 @@ void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsCli // Market data, charts and chat should be available for all Auth eID logins mdProvider_->SubscribeToMD(); - connect(bsClient_.get(), &BsClient::processPbMessage, ui_->widgetChat, &ChatWidget::onProcessOtcPbMessage); - connect(ui_->widgetChat, &ChatWidget::sendOtcPbMessage, bsClient_.get(), &BsClient::sendPbMessage); + connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetChat, &ChatWidget::onProcessOtcPbMessage); + connect(ui_->widgetChat, &ChatWidget::sendOtcPbMessage, bsClient_.get(), &BsClientQt::sendPbMessage); - connect(bsClient_.get(), &BsClient::bootstrapDataUpdated, this, [this](const std::string& data) { + connect(bsClient_.get(), &BsClientQt::bootstrapDataUpdated, this, [this](const std::string& data) { onBootstrapDataLoaded(data); }); accountEnabled_ = true; onAccountTypeChanged(result.userType, result.enabled); - connect(bsClient_.get(), &BsClient::accountStateChanged, this, [this](bs::network::UserType userType, bool enabled) { + connect(bsClient_.get(), &BsClientQt::accountStateChanged, this, [this](bs::network::UserType userType, bool enabled) { onAccountTypeChanged(userType, enabled); }); - connect(bsClient_.get(), &BsClient::tradingStatusChanged, this, [](bool tradingEnabled) { + connect(bsClient_.get(), &BsClientQt::tradingStatusChanged, this, [](bool tradingEnabled) { NotificationCenter::notify(tradingEnabled ? bs::ui::NotifyType::TradingEnabledOnPB : bs::ui::NotifyType::TradingDisabledOnPB, {}); }); } @@ -2380,18 +2389,18 @@ void BSTerminalMainWindow::tryLoginUsingApiKey() } }; - connect(autoLoginClient_.get(), &BsClient::connected, this, [this, logger, apiKeyErrorCb] { - connect(autoLoginClient_.get(), &BsClient::authorizeDone, this, [this, logger, apiKeyErrorCb] - (BsClient::AuthorizeError error, const std::string &email) { - if (error != BsClient::AuthorizeError::NoError) { + connect(autoLoginClient_.get(), &BsClientQt::connected, this, [this, logger, apiKeyErrorCb] { + connect(autoLoginClient_.get(), &BsClientQt::authorizeDone, this, [this, logger, apiKeyErrorCb] + (BsClientQt::AuthorizeError error, const std::string &email) { + if (error != BsClientQt::AuthorizeError::NoError) { switch (error) { - case BsClient::AuthorizeError::UnknownIpAddr: + case BsClientQt::AuthorizeError::UnknownIpAddr: apiKeyErrorCb(AutoLoginState::Failed, tr("Unexpected IP address")); break; - case BsClient::AuthorizeError::UnknownApiKey: + case BsClientQt::AuthorizeError::UnknownApiKey: apiKeyErrorCb(AutoLoginState::Failed, tr("API key not found")); break; - case BsClient::AuthorizeError::Timeout: + case BsClientQt::AuthorizeError::Timeout: apiKeyErrorCb(AutoLoginState::Idle, tr("Request timeout")); break; default: @@ -2401,7 +2410,7 @@ void BSTerminalMainWindow::tryLoginUsingApiKey() return; } - connect(autoLoginClient_.get(), &BsClient::getLoginResultDone, this, [this, logger, email, apiKeyErrorCb] + connect(autoLoginClient_.get(), &BsClientQt::getLoginResultDone, this, [this, logger, email, apiKeyErrorCb] (const BsClientLoginResult &result) { if (result.status != AutheIDClient::NoError) { apiKeyErrorCb(AutoLoginState::Idle, tr("Login failed")); @@ -2433,10 +2442,10 @@ void BSTerminalMainWindow::tryLoginUsingApiKey() }); }); - connect(autoLoginClient_.get(), &BsClient::disconnected, this, [logger, apiKeyErrorCb] { + connect(autoLoginClient_.get(), &BsClientQt::disconnected, this, [logger, apiKeyErrorCb] { apiKeyErrorCb(AutoLoginState::Idle, tr("Proxy disconnected")); }); - connect(autoLoginClient_.get(), &BsClient::connectionFailed, this, [logger, apiKeyErrorCb] { + connect(autoLoginClient_.get(), &BsClientQt::connectionFailed, this, [logger, apiKeyErrorCb] { apiKeyErrorCb(AutoLoginState::Idle, tr("Proxy connection failed")); }); diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index 3c8f6f721..f611edffe 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -20,10 +20,9 @@ #include "ApplicationSettings.h" #include "ArmoryObject.h" #include "BsClient.h" -#include "CelerClientProxy.h" +#include "Celer/CelerClientProxy.h" #include "QWalletInfo.h" #include "SignContainer.h" -#include "WalletSignerContainer.h" #include "BIP15xHelpers.h" #include "ChatProtocol/ChatClientService.h" @@ -71,6 +70,7 @@ class CCPortfolioModel; class CcTrackerClient; class ConnectionManager; class CreateTransactionDialog; +class HeadlessContainer; class LoginWindow; class MDCallbacksQt; class OrderListModel; @@ -209,7 +209,7 @@ private slots: std::shared_ptr ccFileManager_; std::shared_ptr bootstrapDataManager_; std::shared_ptr authAddrDlg_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr autoSignQuoteProvider_; std::shared_ptr autoSignRFQProvider_; @@ -299,8 +299,8 @@ private slots: void restartTerminal(); void processDeferredDialogs(); - std::shared_ptr createClient(); - void activateClient(const std::shared_ptr &bsClient + std::shared_ptr createClient(); + void activateClient(const std::shared_ptr &bsClient , const BsClientLoginResult &result, const std::string &email); const std::string &loginApiKeyEncrypted() const; void initApiKeyLogins(); @@ -325,7 +325,7 @@ private slots: QString autoLoginLastErrorMsg_; std::string loginApiKeyEncrypted_; QTimer *loginTimer_{}; - std::shared_ptr autoLoginClient_; + std::shared_ptr autoLoginClient_; bool initialWalletCreateDialogShown_ = false; bool deferCCsync_ = false; @@ -363,7 +363,7 @@ private slots: }; std::unique_ptr act_; - std::shared_ptr bsClient_; + std::shared_ptr bsClient_; Chat::ChatClientServicePtr chatClientServicePtr_; diff --git a/BlockSettleUILib/CelerAccountInfoDialog.cpp b/BlockSettleUILib/CelerAccountInfoDialog.cpp index b2cf3ca86..878dd418e 100644 --- a/BlockSettleUILib/CelerAccountInfoDialog.cpp +++ b/BlockSettleUILib/CelerAccountInfoDialog.cpp @@ -11,7 +11,7 @@ #include "CelerAccountInfoDialog.h" #include "ui_CelerAccountInfoDialog.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" CelerAccountInfoDialog::CelerAccountInfoDialog(std::shared_ptr celerConnection, QWidget* parent) : QDialog(parent) diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index 3376273b5..419a0f972 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -56,7 +56,7 @@ const size_t kTransactionWeightLimit = 400000; CreateTransactionDialog::CreateTransactionDialog(const std::shared_ptr &armory , const std::shared_ptr& walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container, bool loadFeeSuggestions + , const std::shared_ptr &container, bool loadFeeSuggestions , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings , bs::UtxoReservationToken utxoReservation @@ -116,9 +116,12 @@ void CreateTransactionDialog::init() connect(comboBoxWallets(), SIGNAL(currentIndexChanged(int)), this, SLOT(selectedWalletChanged(int))); if (signContainer_) { - connect(signContainer_.get(), &SignContainer::TXSigned, this, &CreateTransactionDialog::onTXSigned); - connect(signContainer_.get(), &SignContainer::disconnected, this, &CreateTransactionDialog::updateCreateButtonText); - connect(signContainer_.get(), &SignContainer::authenticated, this, &CreateTransactionDialog::onSignerAuthenticated); + const auto hct = dynamic_cast(signContainer_->cbTarget()); + if (hct) { + connect(hct, &QtHCT::TXSigned, this, &CreateTransactionDialog::onTXSigned); + connect(hct, &QtHCT::disconnected, this, &CreateTransactionDialog::updateCreateButtonText); + connect(hct, &QtHCT::authenticated, this, &CreateTransactionDialog::onSignerAuthenticated); + } } updateCreateButtonText(); lineEditAddress()->setFocus(); diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index c94f757ea..a6496baa9 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -35,6 +35,7 @@ namespace bs { } class ApplicationSettings; class ArmoryConnection; +class HeadlessContainer; class QCheckBox; class QComboBox; class QLabel; @@ -42,7 +43,6 @@ class QLineEdit; class QPushButton; class QTextEdit; class RecipientWidget; -class SignContainer; class TransactionData; class UsedInputsModel; class XbtAmountValidator; @@ -60,7 +60,7 @@ Q_OBJECT CreateTransactionDialog(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , bool loadFeeSuggestions, const std::shared_ptr& logger , const std::shared_ptr &applicationSettings , bs::UtxoReservationToken utxoReservation @@ -144,7 +144,7 @@ protected slots: protected: std::shared_ptr armory_; std::shared_ptr walletsManager_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr transactionData_; std::shared_ptr logger_; std::shared_ptr applicationSettings_; diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index 6c8d4bf42..f2c715b46 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -17,9 +17,9 @@ #include "BSMessageBox.h" #include "CoinControlDialog.h" #include "CreateTransactionDialogSimple.h" +#include "HeadlessContainer.h" #include "SelectAddressDialog.h" #include "SelectedTransactionInputs.h" -#include "SignContainer.h" #include "TransactionData.h" #include "TransactionOutputsModel.h" #include "UiUtils.h" @@ -46,7 +46,7 @@ static const float kDustFeePerByte = 3.0; CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(const std::shared_ptr &armory , const std::shared_ptr& walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container + , const std::shared_ptr &container , bool loadFeeSuggestions , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings @@ -70,7 +70,7 @@ std::shared_ptr CreateTransactionDialogAdvanced const std::shared_ptr &armory , const std::shared_ptr& walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& container + , const std::shared_ptr& container , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings , const Tx &tx @@ -96,7 +96,7 @@ std::shared_ptr CreateTransactionDialogAdvanced const std::shared_ptr &armory , const std::shared_ptr& walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& container + , const std::shared_ptr& container , const std::shared_ptr& wallet , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings @@ -119,7 +119,7 @@ std::shared_ptr CreateTransactionDialogAdvanced::Create const std::shared_ptr &armory , const std::shared_ptr &walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& container + , const std::shared_ptr& container , const std::shared_ptr& logger , const std::shared_ptr & applicationSettings , const Bip21::PaymentRequestInfo& paymentInfo diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index 9a615d0e9..20ead67db 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -29,7 +29,6 @@ namespace bs { } class TransactionOutputsModel; - class QNetworkAccessManager; class CreateTransactionDialogAdvanced : public CreateTransactionDialog @@ -41,7 +40,7 @@ Q_OBJECT const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr& + , const std::shared_ptr& , const std::shared_ptr& , const std::shared_ptr & , const Tx & @@ -51,7 +50,7 @@ Q_OBJECT const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr & - , const std::shared_ptr& + , const std::shared_ptr& , const std::shared_ptr& , const std::shared_ptr& , const std::shared_ptr & @@ -62,7 +61,7 @@ Q_OBJECT const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr& + , const std::shared_ptr& , const std::shared_ptr& , const std::shared_ptr & , const Bip21::PaymentRequestInfo& paymentInfo @@ -72,7 +71,7 @@ Q_OBJECT CreateTransactionDialogAdvanced(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , bool loadFeeSuggestions , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.cpp b/BlockSettleUILib/CreateTransactionDialogSimple.cpp index 42aac57ae..8b9b4369a 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.cpp +++ b/BlockSettleUILib/CreateTransactionDialogSimple.cpp @@ -25,7 +25,7 @@ CreateTransactionDialogSimple::CreateTransactionDialogSimple(const std::shared_ptr &armory , const std::shared_ptr& walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings , QWidget* parent) @@ -300,7 +300,7 @@ void CreateTransactionDialogSimple::preSetValue(const bs::XBTAmount& value) std::shared_ptr CreateTransactionDialogSimple::CreateForPaymentRequest(const std::shared_ptr &armory , const std::shared_ptr& walletManager , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr& logger , const std::shared_ptr &applicationSettings , const Bip21::PaymentRequestInfo& paymentInfo diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.h b/BlockSettleUILib/CreateTransactionDialogSimple.h index a78e4c121..b3016f618 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.h +++ b/BlockSettleUILib/CreateTransactionDialogSimple.h @@ -29,7 +29,7 @@ Q_OBJECT const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr& + , const std::shared_ptr& , const std::shared_ptr& , const std::shared_ptr & , const Bip21::PaymentRequestInfo& paymentInfo @@ -39,7 +39,7 @@ Q_OBJECT CreateTransactionDialogSimple(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr &applicationSettings , QWidget* parent = nullptr); diff --git a/BlockSettleUILib/LoginWindow.cpp b/BlockSettleUILib/LoginWindow.cpp index f8d68fe3b..3538d3bd0 100644 --- a/BlockSettleUILib/LoginWindow.cpp +++ b/BlockSettleUILib/LoginWindow.cpp @@ -34,7 +34,7 @@ namespace { } LoginWindow::LoginWindow(const std::shared_ptr &logger - , const std::shared_ptr &bsClient + , const std::shared_ptr &bsClient , std::shared_ptr &settings , QWidget* parent) : QDialog(parent) @@ -92,8 +92,8 @@ LoginWindow::LoginWindow(const std::shared_ptr &logger updateState(); - connect(bsClient_.get(), &BsClient::startLoginDone, this, &LoginWindow::onStartLoginDone); - connect(bsClient_.get(), &BsClient::getLoginResultDone, this, &LoginWindow::onGetLoginResultDone); + connect(bsClient_.get(), &BsClientQt::startLoginDone, this, &LoginWindow::onStartLoginDone); + connect(bsClient_.get(), &BsClientQt::getLoginResultDone, this, &LoginWindow::onGetLoginResultDone); } LoginWindow::~LoginWindow() = default; diff --git a/BlockSettleUILib/LoginWindow.h b/BlockSettleUILib/LoginWindow.h index 0ca0d6411..2ed05a584 100644 --- a/BlockSettleUILib/LoginWindow.h +++ b/BlockSettleUILib/LoginWindow.h @@ -27,7 +27,7 @@ struct BsClientLoginResult; struct NetworkSettings; class ApplicationSettings; -class BsClient; +class BsClientQt; class LoginWindow : public QDialog { @@ -35,7 +35,7 @@ Q_OBJECT public: LoginWindow(const std::shared_ptr &logger - , const std::shared_ptr &bsClient + , const std::shared_ptr &bsClient , std::shared_ptr &settings , QWidget* parent = nullptr); ~LoginWindow() override; @@ -71,8 +71,8 @@ private slots: State state_{State::Idle}; QTimer timer_; float timeLeft_{}; - std::shared_ptr bsClient_; - std::unique_ptr result_; + std::shared_ptr bsClient_; + std::unique_ptr result_; }; #endif // __LOGIN_WINDOW_H__ diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 5dfdea522..1957c2064 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -20,7 +20,7 @@ #include "ApplicationSettings.h" #include "AssetManager.h" #include "BSMessageBox.h" -#include "SignContainer.h" +#include "HeadlessContainer.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -57,7 +57,7 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptr &wallet , const std::shared_ptr &walletsManager , const std::shared_ptr &armory - , const std::shared_ptr &container + , const std::shared_ptr &container , WalletsViewModel *walletsModel , const std::shared_ptr &appSettings , const std::shared_ptr &connectionManager @@ -109,8 +109,10 @@ RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptrbackupButton->setEnabled(false); ui_->manageEncryptionButton->setEnabled(false); } - connect(signingContainer_.get(), &SignContainer::QWalletInfo, this, &RootWalletPropertiesDialog::onHDWalletInfo); - + const auto hct = dynamic_cast(signingContainer_->cbTarget()); + if (hct) { + connect(hct, &QtHCT::QWalletInfo, this, &RootWalletPropertiesDialog::onHDWalletInfo); + } infoReqId_ = signingContainer_->GetInfo(wallet_->walletId()); } diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index ff7631785..842411fb5 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -33,7 +33,7 @@ class ArmoryConnection; class ApplicationSettings; class AssetManager; class CurrentWalletFilter; -class SignContainer; +class HeadlessContainer; class WalletsViewModel; class ConnectionManager; @@ -47,7 +47,7 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , WalletsViewModel *walletsModel , const std::shared_ptr & , const std::shared_ptr & @@ -72,7 +72,7 @@ private slots: std::shared_ptr wallet_; std::shared_ptr walletsManager_; bs::hd::WalletInfo walletInfo_; - std::shared_ptr signingContainer_; + std::shared_ptr signingContainer_; std::shared_ptr appSettings_; std::shared_ptr connectionManager_; std::shared_ptr assetMgr_; diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index c47961a93..e3b80a8e5 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -91,7 +91,7 @@ void PortfolioWidget::init(const std::shared_ptr &appSettin , const std::shared_ptr &mdProvider , const std::shared_ptr &mdCallbacks , const std::shared_ptr &model - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &utxoReservationManager , const std::shared_ptr &logger diff --git a/BlockSettleUILib/PortfolioWidget.h b/BlockSettleUILib/PortfolioWidget.h index da63903f7..38bb25328 100644 --- a/BlockSettleUILib/PortfolioWidget.h +++ b/BlockSettleUILib/PortfolioWidget.h @@ -35,9 +35,9 @@ class QAction; class ApplicationSettings; class ArmoryConnection; class CCPortfolioModel; +class HeadlessContainer; class MarketDataProvider; class MDCallbacksQt; -class WalletSignerContainer; class TransactionsViewModel; class UnconfirmedTransactionFilter; @@ -55,7 +55,7 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &utxoReservationManager , const std::shared_ptr & diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 224bf2753..7e72ae7b6 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -10,6 +10,7 @@ */ #include "StatusBarView.h" #include "AssetManager.h" +#include "HeadlessContainer.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -18,8 +19,9 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory , const std::shared_ptr &walletsManager - , std::shared_ptr assetManager, const std::shared_ptr &celerClient - , const std::shared_ptr &container, QStatusBar *parent) + , std::shared_ptr assetManager + , const std::shared_ptr &celerClient + , const std::shared_ptr &container, QStatusBar *parent) : QObject(nullptr) , statusBar_(parent) , iconSize_(16, 16) @@ -89,16 +91,19 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportFinished, this, &StatusBarView::onWalletImportFinished); connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &StatusBarView::updateBalances); - connect(celerClient.get(), &BaseCelerClient::OnConnectedToServer, this, &StatusBarView::onConnectedToServer); - connect(celerClient.get(), &BaseCelerClient::OnConnectionClosed, this, &StatusBarView::onConnectionClosed); - connect(celerClient.get(), &BaseCelerClient::OnConnectionError, this, &StatusBarView::onConnectionError); + connect(celerClient.get(), &CelerClientQt::OnConnectedToServer, this, &StatusBarView::onConnectedToServer); + connect(celerClient.get(), &CelerClientQt::OnConnectionClosed, this, &StatusBarView::onConnectionClosed); + connect(celerClient.get(), &CelerClientQt::OnConnectionError, this, &StatusBarView::onConnectionError); // container might be null if user rejects remote signer key if (container) { - // connected are not used here because we wait for authenticated signal instead - // disconnected are not used here because onContainerError should be always called - connect(container.get(), &SignContainer::authenticated, this, &StatusBarView::onContainerAuthorized); - connect(container.get(), &SignContainer::connectionError, this, &StatusBarView::onContainerError); + const auto hct = dynamic_cast(container->cbTarget()); + if (hct) { + // connected are not used here because we wait for authenticated signal instead + // disconnected are not used here because onContainerError should be always called + connect(hct, &QtHCT::authenticated, this, &StatusBarView::onContainerAuthorized); + connect(hct, &QtHCT::connectionError, this, &StatusBarView::onContainerError); + } } onArmoryStateChanged(armory_->state()); diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index f3676fc9f..e69250cc3 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -19,7 +19,7 @@ #include #include "ArmoryConnection.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "CircleProgressBar.h" #include "SignContainer.h" @@ -29,7 +29,7 @@ namespace bs { } } class AssetManager; -class SignContainer; +class HeadlessContainer; class StatusBarView : public QObject, public ArmoryCallbackTarget { @@ -37,8 +37,9 @@ class StatusBarView : public QObject, public ArmoryCallbackTarget public: StatusBarView(const std::shared_ptr & , const std::shared_ptr & - , std::shared_ptr assetManager, const std::shared_ptr & - , const std::shared_ptr &, QStatusBar *parent); + , std::shared_ptr assetManager + , const std::shared_ptr & + , const std::shared_ptr &, QStatusBar *parent); ~StatusBarView() noexcept override; StatusBarView(const StatusBarView&) = delete; diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp index 5771faa80..2221f7ff7 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp @@ -10,13 +10,13 @@ */ #include "AutoSignQuoteProvider.h" -#include "SignContainer.h" +#include "HeadlessContainer.h" #include "WalletManager.h" #include "Wallets/SyncWalletsManager.h" #include "Wallets/SyncHDWallet.h" #include "UserScriptRunner.h" -#include +#include #include #include #include @@ -25,8 +25,8 @@ AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptr &logger , UserScriptRunner *scriptRunner , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &celerClient + , const std::shared_ptr &container + , const std::shared_ptr &celerClient , QObject *parent) : QObject(parent), logger_(logger), scriptRunner_(scriptRunner) , appSettings_(appSettings) @@ -40,12 +40,12 @@ AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptr(signingContainer_->cbTarget()); + if (hct) { + connect(hct, &QtHCT::ready, this, &AutoSignScriptProvider::onSignerStateUpdated, Qt::QueuedConnection); + connect(hct, &QtHCT::disconnected, this, &AutoSignScriptProvider::onSignerStateUpdated, Qt::QueuedConnection); + connect(hct, &QtHCT::AutoSignStateChanged, this, &AutoSignScriptProvider::onAutoSignStateChanged); + } } connect(scriptRunner_, &UserScriptRunner::scriptLoaded, this, &AutoSignScriptProvider::onScriptLoaded); @@ -53,8 +53,8 @@ AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptr &logger , UserScriptRunner *scriptRunner , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &celerClient + , const std::shared_ptr &container + , const std::shared_ptr &celerClient , QObject *parent) : AutoSignScriptProvider(logger, scriptRunner, appSettings, container, celerClient, parent) { @@ -278,8 +278,8 @@ AutoSignAQProvider::AutoSignAQProvider(const std::shared_ptr &lo AutoSignRFQProvider::AutoSignRFQProvider(const std::shared_ptr &logger , UserScriptRunner *scriptRunner , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &celerClient + , const std::shared_ptr &container + , const std::shared_ptr &celerClient , QObject *parent) : AutoSignScriptProvider(logger, scriptRunner, appSettings, container, celerClient, parent) { diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h index ba0440f71..55001cdaa 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h @@ -17,8 +17,8 @@ #include #include "ApplicationSettings.h" -class BaseCelerClient; -class SignContainer; +class CelerClientQt; +class HeadlessContainer; class UserScriptRunner; class AssetManager; class QuoteProvider; @@ -42,8 +42,8 @@ class AutoSignScriptProvider : public QObject explicit AutoSignScriptProvider(const std::shared_ptr & , UserScriptRunner * , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & + , const std::shared_ptr & , QObject *parent = nullptr); bool isScriptLoaded() const { return scriptLoaded_; } @@ -93,10 +93,10 @@ public slots: protected: std::shared_ptr appSettings_; ApplicationSettings::Setting lastScript_{ ApplicationSettings::_last }; - std::shared_ptr logger_; - std::shared_ptr signingContainer_; - std::shared_ptr walletsManager_; - std::shared_ptr celerClient_; + std::shared_ptr logger_; + std::shared_ptr signingContainer_; + std::shared_ptr walletsManager_; + std::shared_ptr celerClient_; UserScriptRunner *scriptRunner_{}; bs::error::ErrorCode autoSignState_{ bs::error::ErrorCode::AutoSignDisabled }; @@ -113,8 +113,8 @@ class AutoSignAQProvider : public AutoSignScriptProvider explicit AutoSignAQProvider(const std::shared_ptr & , UserScriptRunner * , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & + , const std::shared_ptr & , QObject *parent = nullptr); }; @@ -125,8 +125,8 @@ class AutoSignRFQProvider : public AutoSignScriptProvider explicit AutoSignRFQProvider(const std::shared_ptr & , UserScriptRunner * , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & + , const std::shared_ptr & , QObject *parent = nullptr); }; diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp index 166897cd2..3a6f1a218 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp @@ -13,12 +13,12 @@ #include "AuthAddressManager.h" #include "CheckRecipSigner.h" #include "CurrencyPair.h" +#include "HeadlessContainer.h" #include "QuoteProvider.h" #include "TradesUtils.h" #include "TradesVerification.h" #include "UiUtils.h" #include "UtxoReservationManager.h" -#include "WalletSignerContainer.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -35,7 +35,7 @@ DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr , const std::shared_ptr &walletsMgr , const std::shared_ptr &xbtWallet , const std::shared_ptr "eProvider - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &authAddrMgr , const bs::Address &authAddr @@ -70,7 +70,7 @@ DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr throw std::runtime_error("no wallet"); } - auto qn = quoteProvider->getSubmittedXBTQuoteNotification(order.settlementId); + auto qn = quoteProvider->getSubmittedXBTQuoteNotification(order.settlementId.toHexStr()); if (qn.authKey.empty() || qn.reqAuthKey.empty() || qn.settlementId.empty()) { throw std::invalid_argument("failed to get submitted QN for " + order.quoteId); } @@ -98,7 +98,10 @@ DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr throw std::runtime_error("can't register settlement wallet in armory"); } - connect(signContainer_.get(), &SignContainer::TXSigned, this, &DealerXBTSettlementContainer::onTXSigned); + const auto hct = dynamic_cast(signContainer_ ? signContainer_->cbTarget() : nullptr); + if (hct) { + connect(hct, &QtHCT::TXSigned, this, &DealerXBTSettlementContainer::onTXSigned); + } } bool DealerXBTSettlementContainer::cancel() diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h index 364de5362..1e8995bca 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h @@ -37,8 +37,8 @@ namespace bs { } class ArmoryConnection; class AuthAddressManager; +class HeadlessContainer; class QuoteProvider; -class WalletSignerContainer; class DealerXBTSettlementContainer : public bs::SettlementContainer @@ -51,7 +51,7 @@ class DealerXBTSettlementContainer : public bs::SettlementContainer , const std::shared_ptr & , const std::shared_ptr &xbtWallet , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &authAddrMgr , const bs::Address &authAddr @@ -69,7 +69,7 @@ class DealerXBTSettlementContainer : public bs::SettlementContainer void activate() override; void deactivate() override; - std::string id() const override { return order_.settlementId; } + std::string id() const override { return order_.settlementId.toHexStr(); } bs::network::Asset::Type assetType() const override { return order_.assetType; } std::string security() const override { return order_.security; } std::string product() const override { return order_.product; } @@ -115,7 +115,7 @@ private slots: std::shared_ptr walletsMgr_; std::shared_ptr xbtWallet_; std::shared_ptr addrVerificator_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr authAddrMgr_; std::shared_ptr utxoReservationManager_; diff --git a/BlockSettleUILib/Trading/OtcClient.cpp b/BlockSettleUILib/Trading/OtcClient.cpp index 5a832f879..f3e106463 100644 --- a/BlockSettleUILib/Trading/OtcClient.cpp +++ b/BlockSettleUILib/Trading/OtcClient.cpp @@ -234,7 +234,10 @@ OtcClient::OtcClient(const std::shared_ptr &logger , applicationSettings_(applicationSettings) , params_(std::move(params)) { - connect(signContainer.get(), &SignContainer::TXSigned, this, &OtcClient::onTxSigned); + const auto hct = dynamic_cast(signContainer ? signContainer->cbTarget() : nullptr); + if (hct) { + connect(hct, &QtHCT::TXSigned, this, &OtcClient::onTxSigned); + } } OtcClient::~OtcClient() = default; diff --git a/BlockSettleUILib/Trading/OtcClient.h b/BlockSettleUILib/Trading/OtcClient.h index 35c7eb418..4237bcc49 100644 --- a/BlockSettleUILib/Trading/OtcClient.h +++ b/BlockSettleUILib/Trading/OtcClient.h @@ -200,7 +200,7 @@ private slots: std::shared_ptr logger_; std::shared_ptr walletsMgr_; - std::shared_ptr armory_; + std::shared_ptr armory_; std::shared_ptr signContainer_; std::shared_ptr authAddressManager_; std::shared_ptr utxoReservationManager_; diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp index 0f89d7cdd..d67ada126 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp @@ -12,8 +12,8 @@ #include "ApplicationSettings.h" #include "AssetManager.h" -#include "CelerClient.h" -#include "CelerSubmitQuoteNotifSequence.h" +#include "Celer/CelerClient.h" +#include "Celer/SubmitQuoteNotifSequence.h" #include "Colors.h" #include "CommonTypes.h" #include "CurrencyPair.h" @@ -26,7 +26,8 @@ QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptr &statsCollector - , std::shared_ptr celerClient, std::shared_ptr appSettings + , const std::shared_ptr &celerClient + , const std::shared_ptr &appSettings , QObject* parent) : QAbstractItemModel(parent) , secStatsCollector_(statsCollector) @@ -41,7 +42,7 @@ QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptrget(ApplicationSettings::PriceUpdateInterval)); - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &QuoteRequestsModel::clearModel); connect(this, &QuoteRequestsModel::deferredUpdate, this, &QuoteRequestsModel::onDeferredUpdate, Qt::QueuedConnection); @@ -612,7 +613,7 @@ void QuoteRequestsModel::ticker() { pendingDeleteIds_.clear(); for (auto qrn : notifications_) { - const auto timeDiff = timeNow.msecsTo(qrn.second.expirationTime.addMSecs(qrn.second.timeSkewMs)); + const auto timeDiff = timeNow.msecsTo(QDateTime::fromMSecsSinceEpoch(qrn.second.expirationTime + qrn.second.timeSkewMs)); if ((timeDiff < 0) || (qrn.second.status == bs::network::QuoteReqNotification::Withdrawn)) { forSpecificId(qrn.second.quoteRequestId, [this](Group *grp, int itemIndex) { const auto row = findGroup(&grp->idx_); @@ -795,13 +796,12 @@ void QuoteRequestsModel::onQuoteReqNotifReplied(const bs::network::QuoteNotifica g = group; const auto assetType = group->rfqs_[static_cast(i)]->assetType_; - const double quotedPrice = (qn.side == bs::network::Side::Buy) ? qn.bidPx : qn.offerPx; group->rfqs_[static_cast(i)]->quotedPriceString_ = - UiUtils::displayPriceForAssetType(quotedPrice, assetType); - group->rfqs_[static_cast(i)]->quotedPrice_ = quotedPrice; + UiUtils::displayPriceForAssetType(qn.price, assetType); + group->rfqs_[static_cast(i)]->quotedPrice_ = qn.price; group->rfqs_[static_cast(i)]->quotedPriceBrush_ = - colorForQuotedPrice(quotedPrice, group->rfqs_[static_cast(i)]->bestQuotedPx_); + colorForQuotedPrice(qn.price, group->rfqs_[static_cast(i)]->bestQuotedPx_); }); if (withdrawn) { diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h index 9afa94340..c11b616a4 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ b/BlockSettleUILib/Trading/QuoteRequestsModel.h @@ -34,8 +34,8 @@ namespace bs { class SettlementContainer; } class AssetManager; -class BaseCelerClient; class ApplicationSettings; +class CelerClientQt; class QuoteRequestsModel : public QAbstractItemModel { @@ -94,8 +94,8 @@ class QuoteRequestsModel : public QAbstractItemModel public: QuoteRequestsModel(const std::shared_ptr & - , std::shared_ptr celerClient - , std::shared_ptr appSettings + , const std::shared_ptr& + , const std::shared_ptr& , QObject* parent); ~QuoteRequestsModel() override; @@ -153,7 +153,7 @@ private slots: MDPrices mdPrices_; const QString groupNameSettlements_ = tr("Settlements"); std::shared_ptr secStatsCollector_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr appSettings_; std::unordered_set pendingDeleteIds_; int priceUpdateInterval_; diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp index 540dd855b..b24b77b4a 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp @@ -74,7 +74,7 @@ QuoteRequestsWidget::~QuoteRequestsWidget() = default; void QuoteRequestsWidget::init(std::shared_ptr logger, const std::shared_ptr "eProvider , const std::shared_ptr& assetManager, const std::shared_ptr &statsCollector - , const std::shared_ptr &appSettings, std::shared_ptr celerClient) + , const std::shared_ptr &appSettings, std::shared_ptr celerClient) { logger_ = logger; assetManager_ = assetManager; diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.h b/BlockSettleUILib/Trading/QuoteRequestsWidget.h index bf580d8e0..12ea00aa9 100644 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.h +++ b/BlockSettleUILib/Trading/QuoteRequestsWidget.h @@ -109,11 +109,10 @@ class RequestsProgressDelegate : public ProgressViewDelegateBase class AssetManager; -class BaseCelerClient; +class CelerClientQt; class QuoteRequestsModel; class QuoteReqSortModel; class RFQBlotterTreeView; -class BaseCelerClient; class QuoteRequestsWidget : public QWidget { @@ -126,7 +125,7 @@ Q_OBJECT void init(std::shared_ptr logger, const std::shared_ptr "eProvider , const std::shared_ptr& assetManager, const std::shared_ptr &statsCollector , const std::shared_ptr &appSettings - , std::shared_ptr celerClient); + , std::shared_ptr celerClient); void addSettlementContainer(const std::shared_ptr &); bool StartCCSignOnOrder(const QString& orderId, QDateTime timestamp); diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp index 9e9aee21d..ccd443dfb 100644 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ b/BlockSettleUILib/Trading/RFQDealerReply.cpp @@ -1124,7 +1124,8 @@ void bs::ui::RFQDealerReply::onUTXOReservationChanged(const std::string& walletI void bs::ui::RFQDealerReply::submit(double price, const std::shared_ptr& replyData) { - SPDLOG_LOGGER_DEBUG(logger_, "submitted quote reply on {}: {}/{}", replyData->qn.quoteRequestId, replyData->qn.bidPx, replyData->qn.offerPx); + SPDLOG_LOGGER_DEBUG(logger_, "submitted quote reply on {}: {}" + , replyData->qn.quoteRequestId, replyData->qn.price); sentNotifs_[replyData->qn.quoteRequestId] = price; submitQuoteNotifCb_(replyData); activeQuoteSubmits_.erase(replyData->qn.quoteRequestId); diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp index 3cc6d0c8a..65f227d06 100644 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ b/BlockSettleUILib/Trading/RFQDialog.cpp @@ -15,6 +15,7 @@ #include "AssetManager.h" #include "BSMessageBox.h" +#include "HeadlessContainer.h" #include "QuoteProvider.h" #include "RFQRequestWidget.h" #include "ReqCCSettlementContainer.h" @@ -22,7 +23,6 @@ #include "RfqStorage.h" #include "UiUtils.h" #include "UtxoReservationManager.h" -#include "WalletSignerContainer.h" #include "Wallets/SyncHDWallet.h" @@ -32,9 +32,9 @@ RFQDialog::RFQDialog(const std::shared_ptr &logger , const std::shared_ptr& authAddressManager , const std::shared_ptr& assetManager , const std::shared_ptr &walletsManager - , const std::shared_ptr &signContainer + , const std::shared_ptr &signContainer , const std::shared_ptr &armory - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , const std::shared_ptr &appSettings , const std::shared_ptr &rfqStorage , const std::shared_ptr &xbtWallet diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h index f1664d50f..02da21b8c 100644 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ b/BlockSettleUILib/Trading/RFQDialog.h @@ -41,14 +41,14 @@ class ApplicationSettings; class ArmoryConnection; class AssetManager; class AuthAddressManager; -class BaseCelerClient; +class CelerClientQt; class CCSettlementTransactionWidget; +class HeadlessContainer; class QuoteProvider; class RFQRequestWidget; class ReqCCSettlementContainer; class ReqXBTSettlementContainer; class RfqStorage; -class WalletSignerContainer; class XBTSettlementTransactionWidget; class RFQDialog : public QDialog @@ -62,9 +62,9 @@ Q_OBJECT , const std::shared_ptr& authAddressManager , const std::shared_ptr& assetManager , const std::shared_ptr &walletsManager - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr &celerClient + , const std::shared_ptr &celerClient , const std::shared_ptr &appSettings , const std::shared_ptr &rfqStorage , const std::shared_ptr &xbtWallet @@ -130,10 +130,10 @@ private slots: std::shared_ptr quoteProvider_; std::shared_ptr authAddressManager_; std::shared_ptr walletsManager_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr assetMgr_; std::shared_ptr armory_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr appSettings_; std::shared_ptr rfqStorage_; std::shared_ptr xbtWallet_; diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp index 1eb779242..963166110 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ b/BlockSettleUILib/Trading/RFQReplyWidget.cpp @@ -15,19 +15,19 @@ #include "AuthAddressManager.h" #include "AutoSignQuoteProvider.h" #include "BSMessageBox.h" -#include "CelerClient.h" -#include "CelerSubmitQuoteNotifSequence.h" +#include "Celer/CelerClient.h" +#include "Celer/SubmitQuoteNotifSequence.h" #include "CustomControls/CustomDoubleSpinBox.h" #include "DealerCCSettlementContainer.h" #include "DealerXBTSettlementContainer.h" #include "DialogManager.h" +#include "HeadlessContainer.h" #include "MDCallbacksQt.h" #include "OrderListModel.h" #include "OrdersView.h" #include "QuoteProvider.h" #include "RFQBlotterTreeView.h" #include "SelectedTransactionInputs.h" -#include "WalletSignerContainer.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" #include "UserScriptRunner.h" @@ -129,14 +129,14 @@ void RFQReplyWidget::shortcutActivated(ShortcutType s) } void RFQReplyWidget::init(const std::shared_ptr &logger - , const std::shared_ptr& celerClient + , const std::shared_ptr& celerClient , const std::shared_ptr &authAddressManager , const std::shared_ptr& quoteProvider , const std::shared_ptr& mdCallbacks , const std::shared_ptr& assetManager , const std::shared_ptr &appSettings , const std::shared_ptr &dialogManager - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &connectionManager , const std::shared_ptr &autoSignProvider @@ -230,9 +230,9 @@ void RFQReplyWidget::init(const std::shared_ptr &logger // ui_->treeViewOrders->setItemDelegateForColumn( // static_cast(OrderListModel::Header::Status), new PushButtonDelegate(ui_->treeViewOrders)); - connect(celerClient_.get(), &BaseCelerClient::OnConnectedToServer, this + connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this , &RFQReplyWidget::onConnectedToCeler); - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, this + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this , &RFQReplyWidget::onDisconnectedFromCeler); connect(ui_->widgetQuoteRequests->view(), &TreeViewWithEnterKey::enterKeyPressed @@ -381,10 +381,11 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) box.exec(); } } else { - const auto &it = sentXbtReplies_.find(order.settlementId); + const auto &it = sentXbtReplies_.find(order.settlementId.toHexStr()); if (it == sentXbtReplies_.end()) { // Looks like this is not error, not sure why we need this - SPDLOG_LOGGER_DEBUG(logger_, "haven't seen QuoteNotif with settlId={}", order.settlementId); + SPDLOG_LOGGER_DEBUG(logger_, "haven't seen QuoteNotif with settlId={}" + , order.settlementId.toHexStr()); return; } try { @@ -449,7 +450,7 @@ void RFQReplyWidget::onOrder(const bs::network::Order &order) sentCCReplies_.erase(quoteReqId); quoteProvider_->delQuoteReqId(quoteReqId); } - sentXbtReplies_.erase(order.settlementId); + sentXbtReplies_.erase(order.settlementId.toHexStr()); } } diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h index 4ae1d22d1..7d9967934 100644 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ b/BlockSettleUILib/Trading/RFQReplyWidget.h @@ -52,13 +52,13 @@ class ArmoryConnection; class AssetManager; class AuthAddressManager; class AutoSignScriptProvider; -class BaseCelerClient; +class CelerClientQt; class ConnectionManager; class DialogManager; +class HeadlessContainer; class MDCallbacksQt; class OrderListModel; class QuoteProvider; -class WalletSignerContainer; namespace Blocksettle { namespace Communication { @@ -84,14 +84,14 @@ Q_OBJECT ~RFQReplyWidget() override; void init(const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -182,13 +182,13 @@ private slots: private: std::unique_ptr ui_; std::shared_ptr logger_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr quoteProvider_; std::shared_ptr authAddressManager_; std::shared_ptr assetManager_; std::shared_ptr walletsManager_; std::shared_ptr dialogManager_; - std::shared_ptr signingContainer_; + std::shared_ptr signingContainer_; std::shared_ptr armory_; std::shared_ptr appSettings_; std::shared_ptr connectionManager_; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 39c233b2e..0e24344c6 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -17,9 +17,10 @@ #include "AuthAddressManager.h" #include "AutoSignQuoteProvider.h" #include "BSMessageBox.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "CurrencyPair.h" #include "DialogManager.h" +#include "HeadlessContainer.h" #include "MDCallbacksQt.h" #include "NotificationCenter.h" #include "OrderListModel.h" @@ -29,7 +30,6 @@ #include "RfqStorage.h" #include "UserScriptRunner.h" #include "UtxoReservationManager.h" -#include "WalletSignerContainer.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -37,6 +37,7 @@ #include "ui_RFQRequestWidget.h" +using namespace Blocksettle::Communication; namespace { enum class RFQPages : int { @@ -218,12 +219,12 @@ void RFQRequestWidget::initWidgets(const std::shared_ptr& md } void RFQRequestWidget::init(const std::shared_ptr &logger - , const std::shared_ptr& celerClient + , const std::shared_ptr& celerClient , const std::shared_ptr &authAddressManager , const std::shared_ptr "eProvider , const std::shared_ptr &assetManager , const std::shared_ptr &dialogManager - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &autoSignProvider , const std::shared_ptr &utxoReservationManager @@ -263,8 +264,8 @@ void RFQRequestWidget::init(const std::shared_ptr &logger , { false, QString::fromStdString(quoteId), QString::fromStdString(reason) }); }); - connect(celerClient_.get(), &BaseCelerClient::OnConnectedToServer, this, &RFQRequestWidget::onConnectedToCeler); - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, this, &RFQRequestWidget::onDisconnectedFromCeler); + connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this, &RFQRequestWidget::onConnectedToCeler); + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &RFQRequestWidget::onDisconnectedFromCeler); connect((RFQScriptRunner *)autoSignProvider_->scriptRunner(), &RFQScriptRunner::sendRFQ , ui_->pageRFQTicket, &RFQTicketXBT::onSendRFQ, Qt::QueuedConnection); diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index 635f70626..451ad8680 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -47,15 +47,15 @@ class ArmoryConnection; class AssetManager; class AuthAddressManager; class AutoSignScriptProvider; -class BaseCelerClient; +class CelerClientQt; class DialogManager; +class HeadlessContainer; class MarketDataProvider; class MDCallbacksQt; class OrderListModel; class QuoteProvider; class RFQDialog; class RfqStorage; -class WalletSignerContainer; class RFQRequestWidget : public TabWithShortcut { @@ -70,12 +70,12 @@ Q_OBJECT , const std::shared_ptr &); void init(const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -152,7 +152,7 @@ public slots: std::unique_ptr ui_; std::shared_ptr logger_; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; std::shared_ptr quoteProvider_; std::shared_ptr assetManager_; std::shared_ptr authAddressManager_; @@ -160,7 +160,7 @@ public slots: OrderListModel* orderListModel_; std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; + std::shared_ptr signingContainer_; std::shared_ptr armory_; std::shared_ptr appSettings_; std::shared_ptr autoSignProvider_; diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index c428b04db..10aa114ef 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -22,9 +22,9 @@ #include "CurrencyPair.h" #include "EncryptionUtils.h" #include "FXAmountValidator.h" +#include "HeadlessContainer.h" #include "QuoteProvider.h" #include "SelectedTransactionInputs.h" -#include "SignContainer.h" #include "TradeSettings.h" #include "TradesUtils.h" #include "TxClasses.h" @@ -125,7 +125,7 @@ void RFQTicketXBT::init(const std::shared_ptr &logger , const std::shared_ptr &authAddressManager , const std::shared_ptr& assetManager , const std::shared_ptr "eProvider - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &utxoReservationManager) { @@ -137,7 +137,10 @@ void RFQTicketXBT::init(const std::shared_ptr &logger utxoReservationManager_ = utxoReservationManager; if (signingContainer_) { - connect(signingContainer_.get(), &SignContainer::ready, this, &RFQTicketXBT::onSignerReady); + const auto hct = dynamic_cast(signingContainer_->cbTarget()); + if (hct) { + connect(hct, &QtHCT::ready, this, &RFQTicketXBT::onSignerReady); + } } connect(utxoReservationManager_.get(), &bs::UTXOReservationManager::availableUtxoChanged, this, &RFQTicketXBT::onUTXOReservationChanged); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h index 13cb8de66..c00a4de77 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ b/BlockSettleUILib/Trading/RFQTicketXBT.h @@ -52,9 +52,9 @@ class AssetManager; class AuthAddressManager; class CCAmountValidator; class FXAmountValidator; +class HeadlessContainer; class QuoteProvider; class SelectedTransactionInputs; -class SignContainer; class XbtAmountValidator; @@ -70,7 +70,7 @@ Q_OBJECT , const std::shared_ptr & , const std::shared_ptr &assetManager , const std::shared_ptr "eProvider - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &); void setWalletsManager(const std::shared_ptr &); @@ -232,7 +232,7 @@ private slots: std::shared_ptr authAddressManager_; std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; + std::shared_ptr signingContainer_; std::shared_ptr armory_; std::shared_ptr utxoReservationManager_; diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp index bc6114a84..2301106d7 100644 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp @@ -12,7 +12,7 @@ #include #include "AssetManager.h" #include "CheckRecipSigner.h" -#include "SignContainer.h" +#include "HeadlessContainer.h" #include "TradesUtils.h" #include "TransactionData.h" #include "Wallets/SyncHDWallet.h" @@ -25,7 +25,7 @@ using namespace bs::sync; ReqCCSettlementContainer::ReqCCSettlementContainer(const std::shared_ptr &logger - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &armory , const std::shared_ptr &assetMgr , const std::shared_ptr &walletsMgr @@ -76,7 +76,10 @@ ReqCCSettlementContainer::ReqCCSettlementContainer(const std::shared_ptr(signingContainer_ ? signingContainer_->cbTarget() : nullptr); + if (hct) { + connect(hct, &QtHCT::QWalletInfo, this, &ReqCCSettlementContainer::onWalletInfo); + } connect(this, &ReqCCSettlementContainer::genAddressVerified, this , &ReqCCSettlementContainer::onGenAddressVerified, Qt::QueuedConnection); diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.h b/BlockSettleUILib/Trading/ReqCCSettlementContainer.h index cb08c8c73..3a8ec3bae 100644 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.h +++ b/BlockSettleUILib/Trading/ReqCCSettlementContainer.h @@ -30,7 +30,7 @@ namespace bs { } class ArmoryConnection; class AssetManager; -class SignContainer; +class HeadlessContainer; class ReqCCSettlementContainer : public bs::SettlementContainer @@ -38,7 +38,7 @@ class ReqCCSettlementContainer : public bs::SettlementContainer Q_OBJECT public: ReqCCSettlementContainer(const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & @@ -100,7 +100,7 @@ private slots: private: std::shared_ptr logger_; - std::shared_ptr signingContainer_; + std::shared_ptr signingContainer_; std::shared_ptr xbtWallet_; std::vector> xbtLeaves_; std::shared_ptr ccWallet_; diff --git a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp index 3fdf6a8e1..0cdf2cc16 100644 --- a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp @@ -18,8 +18,8 @@ #include "AuthAddressManager.h" #include "CheckRecipSigner.h" #include "CurrencyPair.h" +#include "HeadlessContainer.h" #include "QuoteProvider.h" -#include "WalletSignerContainer.h" #include "TradesUtils.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" @@ -34,7 +34,7 @@ Q_DECLARE_METATYPE(AddressVerificationState) ReqXBTSettlementContainer::ReqXBTSettlementContainer(const std::shared_ptr &logger , const std::shared_ptr &authAddrMgr - , const std::shared_ptr &signContainer + , const std::shared_ptr &signContainer , const std::shared_ptr &armory , const std::shared_ptr &xbtWallet , const std::shared_ptr &walletsMgr @@ -67,8 +67,10 @@ ReqXBTSettlementContainer::ReqXBTSettlementContainer(const std::shared_ptr(); - connect(signContainer_.get(), &SignContainer::TXSigned, this, &ReqXBTSettlementContainer::onTXSigned); - + const auto hct = dynamic_cast(signContainer_ ? signContainer_->cbTarget() : nullptr); + if (hct) { + connect(hct, &QtHCT::TXSigned, this, &ReqXBTSettlementContainer::onTXSigned); + } connect(this, &ReqXBTSettlementContainer::timerExpired, this, &ReqXBTSettlementContainer::onTimerExpired); CurrencyPair cp(quote_.security); diff --git a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h index 8055f1173..b4d11db84 100644 --- a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h +++ b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h @@ -33,7 +33,7 @@ namespace bs { class AddressVerificator; class ArmoryConnection; class AuthAddressManager; -class WalletSignerContainer; +class HeadlessContainer; class QuoteProvider; @@ -43,7 +43,7 @@ class ReqXBTSettlementContainer : public bs::SettlementContainer public: ReqXBTSettlementContainer(const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &xbtWallet , const std::shared_ptr & @@ -107,7 +107,7 @@ private slots: std::shared_ptr logger_; std::shared_ptr authAddrMgr_; std::shared_ptr walletsMgr_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr armory_; std::shared_ptr xbtWallet_; std::shared_ptr utxoReservationManager_; diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp index c9f2cd0d1..bef38fa81 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp @@ -14,7 +14,7 @@ #include "AssetManager.h" #include "BlockDataManagerConfig.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "CurrencyPair.h" #include "UiUtils.h" #include "UtxoReservationManager.h" @@ -50,10 +50,11 @@ RequestingQuoteWidget::RequestingQuoteWidget(QWidget* parent) RequestingQuoteWidget::~RequestingQuoteWidget() = default; -void RequestingQuoteWidget::SetCelerClient(std::shared_ptr celerClient) { +void RequestingQuoteWidget::SetCelerClient(const std::shared_ptr &celerClient) +{ celerClient_ = celerClient; - connect(celerClient_.get(), &BaseCelerClient::OnConnectionClosed, + connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &RequestingQuoteWidget::onCelerDisconnected); } diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.h b/BlockSettleUILib/Trading/RequestingQuoteWidget.h index 4d883386a..048be651a 100644 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.h +++ b/BlockSettleUILib/Trading/RequestingQuoteWidget.h @@ -22,7 +22,7 @@ namespace Ui { class RequestingQuoteWidget; } class AssetManager; -class BaseCelerClient; +class CelerClientQt; class RequestingQuoteWidget : public QWidget { @@ -36,7 +36,7 @@ Q_OBJECT assetManager_ = assetManager; } - void SetCelerClient(std::shared_ptr celerClient); + void SetCelerClient(const std::shared_ptr &celerClient); void populateDetails(const bs::network::RFQ& rfq); @@ -79,7 +79,7 @@ public slots: bs::network::Quote quote_; std::shared_ptr assetManager_; bool balanceOk_ = true; - std::shared_ptr celerClient_; + std::shared_ptr celerClient_; private: void setupTimer(Status status, const QDateTime &expTime); diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index 136be45ba..08125d54b 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -280,7 +280,7 @@ TransactionsWidget::~TransactionsWidget() = default; void TransactionsWidget::init(const std::shared_ptr &walletsMgr , const std::shared_ptr &armory , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &signContainer + , const std::shared_ptr &signContainer , const std::shared_ptr &appSettings , const std::shared_ptr &logger) diff --git a/BlockSettleUILib/TransactionsWidget.h b/BlockSettleUILib/TransactionsWidget.h index d9bba4508..3e26f6931 100644 --- a/BlockSettleUILib/TransactionsWidget.h +++ b/BlockSettleUILib/TransactionsWidget.h @@ -33,10 +33,10 @@ namespace bs { } class ApplicationSettings; class ArmoryConnection; +class HeadlessContainer; class TransactionsProxy; class TransactionsViewModel; class TransactionsSortFilterModel; -class WalletSignerContainer; class TransactionsWidget : public TransactionsWidgetInterface @@ -50,7 +50,7 @@ Q_OBJECT void init(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr &); void SetTransactionsModel(const std::shared_ptr &); diff --git a/BlockSettleUILib/TransactionsWidgetInterface.cpp b/BlockSettleUILib/TransactionsWidgetInterface.cpp index 9b13b99d2..1323092d5 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.cpp +++ b/BlockSettleUILib/TransactionsWidgetInterface.cpp @@ -13,6 +13,7 @@ #include "ApplicationSettings.h" #include "BSMessageBox.h" #include "CreateTransactionDialogAdvanced.h" +#include "HeadlessContainer.h" #include "PasswordDialogDataWrapper.h" #include "TradesUtils.h" #include "TransactionsViewModel.h" @@ -52,7 +53,7 @@ TransactionsWidgetInterface::TransactionsWidgetInterface(QWidget *parent) void TransactionsWidgetInterface::init(const std::shared_ptr &walletsMgr , const std::shared_ptr &armory , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &signContainer + , const std::shared_ptr &signContainer , const std::shared_ptr &appSettings , const std::shared_ptr &logger) @@ -64,7 +65,10 @@ void TransactionsWidgetInterface::init(const std::shared_ptr(signContainer_ ? signContainer_->cbTarget() : nullptr); + if (hct) { + connect(hct, &QtHCT::TXSigned, this, &TransactionsWidgetInterface::onTXSigned); + } } void TransactionsWidgetInterface::onRevokeSettlement() diff --git a/BlockSettleUILib/TransactionsWidgetInterface.h b/BlockSettleUILib/TransactionsWidgetInterface.h index 18baa44b1..34e0ab74d 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.h +++ b/BlockSettleUILib/TransactionsWidgetInterface.h @@ -29,8 +29,8 @@ namespace bs { } class ApplicationSettings; class ArmoryConnection; +class HeadlessContainer; class TransactionsViewModel; -class WalletSignerContainer; class TransactionsWidgetInterface : public TabWithShortcut { Q_OBJECT @@ -41,7 +41,7 @@ class TransactionsWidgetInterface : public TabWithShortcut { void init(const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr& , const std::shared_ptr &); @@ -54,7 +54,7 @@ protected slots: protected: std::shared_ptr logger_; std::shared_ptr walletsManager_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr armory_; std::shared_ptr utxoReservationManager_; std::shared_ptr appSettings_; diff --git a/BlockSettleUILib/UserScriptRunner.cpp b/BlockSettleUILib/UserScriptRunner.cpp index 533b67e99..832084d20 100644 --- a/BlockSettleUILib/UserScriptRunner.cpp +++ b/BlockSettleUILib/UserScriptRunner.cpp @@ -339,7 +339,7 @@ void AQScriptHandler::aqTick() if (!qr) continue; auto itQRN = aqQuoteReqs_.find(qr->requestId().toStdString()); if (itQRN == aqQuoteReqs_.end()) continue; - const auto timeDiff = timeNow.msecsTo(itQRN->second.expirationTime.addMSecs(itQRN->second.timeSkewMs)); + const auto timeDiff = timeNow.msecsTo(QDateTime::fromMSecsSinceEpoch(itQRN->second.expirationTime + itQRN->second.timeSkewMs)); if (timeDiff <= 0) { expiredEntries << qr->requestId(); } diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index 710903dc4..282ed7859 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -14,7 +14,7 @@ #include #include -#include "SignContainer.h" +#include "HeadlessContainer.h" #include "UiUtils.h" #include "ValidityFlag.h" #include "Wallets/SyncHDWallet.h" @@ -290,7 +290,7 @@ void WalletRootNode::addGroups(const std::vector &walletsManager - , const std::string &defaultWalletId, const std::shared_ptr &container + , const std::string &defaultWalletId, const std::shared_ptr &container , QObject* parent, bool showOnlyRegular) : QAbstractItemModel(parent) , walletsManager_(walletsManager) @@ -308,10 +308,13 @@ WalletsViewModel::WalletsViewModel(const std::shared_ptr(signContainer_->cbTarget()); + if (hct) { + connect(hct, &QtHCT::QWalletInfo, this, &WalletsViewModel::onWalletInfo); + connect(hct, &QtHCT::Error, this, &WalletsViewModel::onHDWalletError); + connect(hct, &QtHCT::authenticated, this, &WalletsViewModel::onSignerAuthenticated); + connect(hct, &QtHCT::ready, this, &WalletsViewModel::onWalletChanged); + } } } diff --git a/BlockSettleUILib/WalletsViewModel.h b/BlockSettleUILib/WalletsViewModel.h index aa4b1a48a..3e7062fbb 100644 --- a/BlockSettleUILib/WalletsViewModel.h +++ b/BlockSettleUILib/WalletsViewModel.h @@ -29,7 +29,7 @@ namespace bs { class WalletsManager; } } -class SignContainer; +class HeadlessContainer; class WalletsViewModel; @@ -94,7 +94,7 @@ class WalletsViewModel : public QAbstractItemModel Q_OBJECT public: WalletsViewModel(const std::shared_ptr& walletsManager, const std::string &defaultWalletId - , const std::shared_ptr &sc = nullptr, QObject *parent = nullptr, bool showOnlyRegular = false); + , const std::shared_ptr &sc = nullptr, QObject *parent = nullptr, bool showOnlyRegular = false); ~WalletsViewModel() noexcept override = default; WalletsViewModel(const WalletsViewModel&) = delete; @@ -161,7 +161,7 @@ private slots: private: std::shared_ptr walletsManager_; - std::shared_ptr signContainer_; + std::shared_ptr signContainer_; std::shared_ptr selectedWallet_; std::shared_ptr rootNode_; std::string defaultWalletId_; diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 205b16a53..d9a5debf6 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -27,9 +27,9 @@ #include "ApplicationSettings.h" #include "AssetManager.h" #include "BSMessageBox.h" +#include "HeadlessContainer.h" #include "NewWalletDialog.h" #include "SelectAddressDialog.h" -#include "SignContainer.h" #include "WalletsViewModel.h" #include "WalletWarningDialog.h" #include "Wallets/SyncHDWallet.h" @@ -178,7 +178,7 @@ WalletsWidget::~WalletsWidget() = default; void WalletsWidget::init(const std::shared_ptr &logger , const std::shared_ptr &manager - , const std::shared_ptr &container + , const std::shared_ptr &container , const std::shared_ptr &applicationSettings , const std::shared_ptr &connectionManager , const std::shared_ptr &assetMgr @@ -196,7 +196,10 @@ void WalletsWidget::init(const std::shared_ptr &logger // signingContainer_ might be null if user rejects remote signer key if (signingContainer_) { - connect(signingContainer_.get(), &SignContainer::TXSigned, this, &WalletsWidget::onTXSigned); + const auto hct = dynamic_cast(signingContainer_->cbTarget()); + if (hct) { + connect(hct, &QtHCT::TXSigned, this, &WalletsWidget::onTXSigned); + } } const auto &defWallet = walletsManager_->getDefaultWallet(); diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index fa03be787..5b4c21a2b 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -43,9 +43,9 @@ class ArmoryConnection; class AssetManager; class AuthAddressManager; class ConnectionManager; +class HeadlessContainer; class QAction; class QMenu; -class SignContainer; class WalletNode; class WalletsViewModel; @@ -59,7 +59,7 @@ Q_OBJECT void init(const std::shared_ptr &logger , const std::shared_ptr & - , const std::shared_ptr & + , const std::shared_ptr & , const std::shared_ptr & , const std::shared_ptr &connectionManager , const std::shared_ptr & @@ -124,7 +124,7 @@ private slots: std::shared_ptr logger_; std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; + std::shared_ptr signingContainer_; std::shared_ptr appSettings_; std::shared_ptr connectionManager_; std::shared_ptr assetManager_; diff --git a/UnitTests/TestAuth.cpp b/UnitTests/TestAuth.cpp index 73dd7f7cc..8d5d4b87f 100644 --- a/UnitTests/TestAuth.cpp +++ b/UnitTests/TestAuth.cpp @@ -58,6 +58,7 @@ check unflagged return #include "CheckRecipSigner.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "Wallets/SyncHDLeaf.h" #include "Wallets/SyncHDWallet.h" @@ -250,7 +251,8 @@ void TestAuth::SetUp() } //setup sync manager - auto inprocSigner = std::make_shared(priWallet_, envPtr_->logger()); + hct_ = std::make_shared(nullptr); + auto inprocSigner = std::make_shared(priWallet_, hct_.get(), envPtr_->logger()); inprocSigner->Start(); syncMgr_ = std::make_shared(envPtr_->logger(), envPtr_->appSettings(), envPtr_->armoryConnection()); diff --git a/UnitTests/TestAuth.h b/UnitTests/TestAuth.h index c7f717805..3bdf4ba02 100644 --- a/UnitTests/TestAuth.h +++ b/UnitTests/TestAuth.h @@ -36,6 +36,7 @@ namespace bs { class Wallet; } } +class QtHCT; //////////////////////////////////////////////////////////////////////////////// class TestValidationACT : public ValidationAddressACT @@ -124,6 +125,7 @@ class TestAuth : public ::testing::Test std::shared_ptr xbtSignWallet_; std::shared_ptr xbtWallet_; std::shared_ptr syncMgr_; + std::shared_ptr hct_; bs::Address recvAddr_; std::shared_ptr envPtr_; diff --git a/UnitTests/TestCCoin.cpp b/UnitTests/TestCCoin.cpp index b6d0531e5..010ed2ef6 100644 --- a/UnitTests/TestCCoin.cpp +++ b/UnitTests/TestCCoin.cpp @@ -15,6 +15,7 @@ #include "CheckRecipSigner.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "Wallets/SyncHDLeaf.h" #include "Wallets/SyncHDWallet.h" @@ -89,7 +90,9 @@ void TestCCoin::SetUp() } } - auto inprocSigner = std::make_shared(envPtr_->walletsMgr(), envPtr_->logger(), "", NetworkType::TestNet); + hct_ = std::make_shared(nullptr); + auto inprocSigner = std::make_shared(envPtr_->walletsMgr() + , envPtr_->logger(), hct_.get(), "", NetworkType::TestNet); inprocSigner->Start(); syncMgr_ = std::make_shared(envPtr_->logger(), envPtr_->appSettings(), envPtr_->armoryConnection()); syncMgr_->setSignContainer(inprocSigner); diff --git a/UnitTests/TestCCoin.h b/UnitTests/TestCCoin.h index f130bc978..0ab6a651e 100644 --- a/UnitTests/TestCCoin.h +++ b/UnitTests/TestCCoin.h @@ -40,6 +40,7 @@ namespace bs { class WalletsManager; } } +class QtHCT; struct CCoinSpender { @@ -165,6 +166,7 @@ class TestCCoin : public ::testing::Test std::shared_ptr syncMgr_; std::vector localACTs_; + std::shared_ptr hct_; bs::Address genesisAddr_; bs::Address revocationAddr_; diff --git a/UnitTests/TestCCoinAsync.cpp b/UnitTests/TestCCoinAsync.cpp index a13b2c9fe..b4378720d 100644 --- a/UnitTests/TestCCoinAsync.cpp +++ b/UnitTests/TestCCoinAsync.cpp @@ -15,6 +15,7 @@ #include "CheckRecipSigner.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "Wallets/SyncHDLeaf.h" #include "Wallets/SyncHDWallet.h" @@ -73,7 +74,9 @@ void TestCCoinAsync::SetUp() } } - auto inprocSigner = std::make_shared(envPtr_->walletsMgr(), envPtr_->logger(), "", NetworkType::TestNet); + hct_ = std::make_shared(nullptr); + auto inprocSigner = std::make_shared(envPtr_->walletsMgr() + , envPtr_->logger(), hct_.get(), "", NetworkType::TestNet); inprocSigner->Start(); syncMgr_ = std::make_shared(envPtr_->logger(), envPtr_->appSettings(), envPtr_->armoryConnection()); syncMgr_->setSignContainer(inprocSigner); diff --git a/UnitTests/TestCCoinAsync.h b/UnitTests/TestCCoinAsync.h index 4d362c2b7..ae7a85365 100644 --- a/UnitTests/TestCCoinAsync.h +++ b/UnitTests/TestCCoinAsync.h @@ -40,6 +40,7 @@ namespace bs { class WalletsManager; } } +class QtHCT; class AsyncCCT : public ColoredCoinTrackerAsync @@ -83,6 +84,7 @@ class TestCCoinAsync : public ::testing::Test std::vector> userWallets_; std::shared_ptr syncMgr_; + std::shared_ptr hct_; bs::Address genesisAddr_; bs::Address revocationAddr_; diff --git a/UnitTests/TestCommon.cpp b/UnitTests/TestCommon.cpp index f7290bd61..dfa787127 100644 --- a/UnitTests/TestCommon.cpp +++ b/UnitTests/TestCommon.cpp @@ -28,6 +28,7 @@ #include "CacheFile.h" #include "CurrencyPair.h" #include "EasyCoDec.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "MarketDataProvider.h" #include "MDCallbacksQt.h" @@ -96,7 +97,9 @@ TEST(TestCommon, AssetManager) TestEnv env(StaticLogger::loggerPtr); env.requireAssets(); - auto inprocSigner = std::make_shared(env.walletsMgr(), StaticLogger::loggerPtr, "", NetworkType::TestNet); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(env.walletsMgr() + , StaticLogger::loggerPtr, hct, "", NetworkType::TestNet); inprocSigner->Start(); auto syncMgr = std::make_shared(StaticLogger::loggerPtr , env.appSettings(), env.armoryConnection()); @@ -134,6 +137,7 @@ TEST(TestCommon, AssetManager) assetMgr.onMDUpdate(bs::network::Asset::PrivateMarket, QLatin1String("BLK/XBT") , { bs::network::MDField{bs::network::MDField::PriceLast, 0.023} }); EXPECT_EQ(assetMgr.getPrice("BLK"), 0.023); + delete hct; } TEST(TestCommon, UtxoReservation) diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index 6cebe7b4e..d609b9d57 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -15,7 +15,7 @@ #include "ArmoryObject.h" #include "ArmorySettings.h" #include "AuthAddressManager.h" -#include "CelerClient.h" +#include "Celer/CelerClient.h" #include "ConnectionManager.h" #include "CoreWalletsManager.h" #include "MarketDataProvider.h" @@ -121,12 +121,12 @@ void TestEnv::requireArmory() qDebug() << "Waiting for ArmoryDB connection..."; while (armoryConnection_->state() != ArmoryState::Connected) { - QThread::msleep(1); + std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); } qDebug() << "Armory connected - waiting for ready state..."; armoryConnection_->goOnline(); while (armoryConnection_->state() != ArmoryState::Ready) { - QThread::msleep(1); + std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); } logger_->debug("Armory is ready - continue execution"); } diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index d7ce4cc64..b41e1d407 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -46,7 +46,7 @@ class ApplicationSettings; class ArmoryConnection; class AuthAddressManager; class BlockchainMonitor; -class CelerClient; +class CelerClientQt; class ConnectionManager; class MarketDataProvider; class MDCallbacksQt; @@ -462,7 +462,7 @@ class TestEnv std::shared_ptr assetMgr() { return assetMgr_; } std::shared_ptr blockMonitor() { return blockMonitor_; } std::shared_ptr connectionMgr() { return connMgr_; } - std::shared_ptr celerConnection() { return celerConn_; } + std::shared_ptr celerConnection() { return celerConn_; } std::shared_ptr logger() { return logger_; } std::shared_ptr walletsMgr() { return walletsMgr_; } std::shared_ptr mdProvider() { return mdProvider_; } @@ -477,7 +477,7 @@ class TestEnv std::shared_ptr appSettings_; std::shared_ptr assetMgr_; std::shared_ptr blockMonitor_; - std::shared_ptr celerConn_; + std::shared_ptr celerConn_; std::shared_ptr connMgr_; std::shared_ptr mdCallbacks_; std::shared_ptr mdProvider_; diff --git a/UnitTests/TestNetwork.cpp b/UnitTests/TestNetwork.cpp index ab8cfe8c1..447b0024f 100644 --- a/UnitTests/TestNetwork.cpp +++ b/UnitTests/TestNetwork.cpp @@ -13,7 +13,8 @@ #include "Bip15xDataConnection.h" #include "Bip15xServerConnection.h" -#include "CelerMessageMapper.h" +#include "Celer/MessageMapper.h" +#include "Celer/CommonUtils.h" #include "CommonTypes.h" #include "IdStringGenerator.h" #include "QuoteProvider.h" @@ -76,14 +77,14 @@ using namespace bs::network; TEST(TestNetwork, Types) { for (const auto t : {Side::Buy, Side::Sell}) { - const auto celerType = Side::toCeler(t); - EXPECT_EQ(Side::fromCeler(celerType), t) << "Side " << Side::toString(t); + const auto celerType = bs::celer::toCeler(t); + EXPECT_EQ(bs::celer::fromCeler(celerType), t) << "Side " << Side::toString(t); } for (int i = bs::network::Asset::first; i < bs::network::Asset::last; i++) { const auto t = static_cast(i); - const auto celerProdType = bs::network::Asset::toCelerProductType(t); - EXPECT_EQ(bs::network::Asset::fromCelerProductType(celerProdType), t) << "Asset type " << bs::network::Asset::toString(t); + const auto celerProdType = bs::celer::toCelerProductType(t); + EXPECT_EQ(bs::celer::fromCelerProductType(celerProdType), t) << "Asset type " << bs::network::Asset::toString(t); } } diff --git a/UnitTests/TestOtc.cpp b/UnitTests/TestOtc.cpp index fe05f0f67..95349d036 100644 --- a/UnitTests/TestOtc.cpp +++ b/UnitTests/TestOtc.cpp @@ -12,6 +12,7 @@ #include "CoreHDWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "SettableField.h" #include "StringUtils.h" @@ -34,6 +35,7 @@ namespace { const auto kSettlementId = std::string("dc26c004d7b24f71cd5b348a254c292777586f5d9d00f60ac65dd7d5b06d0c2b"); } // namespace +class QtHCT; class TestPeer { @@ -79,7 +81,9 @@ class TestPeer walletsMgr_->addWallet(wallet_); - signer_ = std::make_shared(walletsMgr_, env.logger(), "", NetworkType::TestNet); + hct_ = std::make_shared(nullptr); + signer_ = std::make_shared(walletsMgr_, env.logger() + , hct_.get(), "", NetworkType::TestNet); signer_->Start(); syncWalletMgr_ = std::make_shared(env.logger() @@ -111,6 +115,7 @@ class TestPeer std::shared_ptr walletsMgr_; std::shared_ptr syncWalletMgr_; std::shared_ptr signer_; + std::shared_ptr hct_; std::shared_ptr otc_; bs::Address authAddress_; bs::Address nativeAddr_; diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index bc45cf12b..553c9f390 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -18,6 +18,7 @@ #include "ApplicationSettings.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "TestEnv.h" #include "TransactionData.h" @@ -138,8 +139,9 @@ void TestSettlement::SetUp() hdWallet_.push_back(hdWallet); } + hct_ = std::make_shared(nullptr); auto inprocSigner = std::make_shared( - walletsMgr_, logger, "", NetworkType::TestNet); + walletsMgr_, logger, hct_.get(), "", NetworkType::TestNet); inprocSigner->Start(); syncMgr_ = std::make_shared(logger , envPtr_->appSettings(), envPtr_->armoryConnection()); diff --git a/UnitTests/TestSettlement.h b/UnitTests/TestSettlement.h index 0d2b8d6a2..44869aa0b 100644 --- a/UnitTests/TestSettlement.h +++ b/UnitTests/TestSettlement.h @@ -41,6 +41,7 @@ namespace bs { class WalletsManager; } } +class QtHCT; class TestSettlement : public ::testing::Test { @@ -68,6 +69,7 @@ class TestSettlement : public ::testing::Test std::vector> hdWallet_; std::vector> authWallet_; std::shared_ptr walletsMgr_; + std::shared_ptr hct_; std::shared_ptr syncMgr_; std::vector> xbtWallet_; std::vector authAddrs_; diff --git a/UnitTests/TestWallet.cpp b/UnitTests/TestWallet.cpp index 1e0f710e5..a85abfa5a 100644 --- a/UnitTests/TestWallet.cpp +++ b/UnitTests/TestWallet.cpp @@ -19,6 +19,7 @@ #include "CoreHDWallet.h" #include "CoreWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "SystemFileUtils.h" #include "TestEnv.h" @@ -189,8 +190,9 @@ TEST_F(TestWallet, BIP84_primary) EXPECT_EQ(leafCC->name(), "84'/16979'/7568'"); //16979 == 0x4253 } + auto hct = new QtHCT(nullptr); auto inprocSigner = std::make_shared( - envPtr_->walletsMgr(), envPtr_->logger(), "", NetworkType::TestNet); + envPtr_->walletsMgr(), envPtr_->logger(), hct, "", NetworkType::TestNet); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -202,6 +204,7 @@ TEST_F(TestWallet, BIP84_primary) EXPECT_TRUE(envPtr_->walletsMgr()->deleteWalletFile(wallet)); EXPECT_EQ(envPtr_->walletsMgr()->getPrimaryWallet(), nullptr); + delete hct; } TEST_F(TestWallet, BIP84_address) @@ -602,7 +605,8 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) } //create sync manager - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -697,6 +701,7 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) //shut it all down filename = walletPtr->getFileName(); + delete hct; } { @@ -704,7 +709,8 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) auto walletPtr = std::make_shared( filename, NetworkType::TestNet, "", ctrlPass, envPtr_->logger()); - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -796,6 +802,7 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) filename = WOcopy->getFileName(); //scope out to clean up prior to WO testing + delete hct; } //load WO, perform checks one last time @@ -807,7 +814,8 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) EXPECT_EQ(walletPtr->isWatchingOnly(), true); //create sync manager - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -843,6 +851,7 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) EXPECT_EQ(originalSet.size(), 12); EXPECT_EQ(originalSet.size(), loadedSet.size()); EXPECT_EQ(originalSet, loadedSet); + delete hct; } } @@ -1337,7 +1346,8 @@ TEST_F(TestWallet, SyncWallet_TriggerPoolExtension) } //create sync manager - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1477,6 +1487,7 @@ TEST_F(TestWallet, SyncWallet_TriggerPoolExtension) auto addr_hash = BtcUtils::getHash160(addr_node.getPublicKey()); EXPECT_EQ(addr_hash, intAddrVec[i].unprefixed()); } + delete hct; } } @@ -1801,7 +1812,8 @@ TEST_F(TestWallet, TxIdNestedSegwit) envPtr_->requireArmory(); ASSERT_NE(envPtr_->armoryConnection(), nullptr); - auto inprocSigner = std::make_shared(coreWallet, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(coreWallet, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1873,6 +1885,7 @@ TEST_F(TestWallet, TxIdNestedSegwit) catch (const std::exception &e) { ASSERT_FALSE(true) << e.what(); } + delete hct; } TEST_F(TestWallet, ChangePassword) @@ -1936,7 +1949,6 @@ TEST_F(TestWallet, ChangeControlPassword) const bs::core::wallet::Seed seed{ SecureBinaryData::fromString("Sample test seed") , NetworkType::TestNet }; - { auto wallet = std::make_shared( walletName, walletDescr, seed, pd1, walletFolder_, envPtr_->logger()); diff --git a/UnitTests/TestWalletArmory.cpp b/UnitTests/TestWalletArmory.cpp index f22b3da15..7601f2d83 100644 --- a/UnitTests/TestWalletArmory.cpp +++ b/UnitTests/TestWalletArmory.cpp @@ -19,6 +19,7 @@ #include "CoreHDWallet.h" #include "CoreWallet.h" #include "CoreWalletsManager.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "SystemFileUtils.h" #include "TestEnv.h" @@ -69,7 +70,8 @@ class TestWalletWithArmory : public ::testing::Test TEST_F(TestWalletWithArmory, AddressChainExtension) { - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -238,6 +240,7 @@ TEST_F(TestWalletWithArmory, AddressChainExtension) //check balance balances = syncLeaf->getAddrBalance(addrVec[11]); EXPECT_EQ(balances[0], 25 * COIN); + delete hct; } TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) @@ -247,7 +250,8 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) std::vector intVec; { - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -434,6 +438,7 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) leafPtr_.reset(); walletPtr_->eraseFile(); walletPtr_.reset(); + delete hct; } std::string filename; @@ -453,7 +458,8 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) } //sync with db - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -561,6 +567,7 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) EXPECT_EQ(syncLeaf->getIntAddressCount(), 47); filename = walletPtr_->getFileName(); + delete hct; } /* @@ -584,7 +591,8 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) filename, NetworkType::TestNet); //resync address chain use, it should not disrupt current state - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); syncMgr->setSignContainer(inprocSigner); @@ -659,6 +667,7 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) //check address list matches EXPECT_EQ(extAddrList, extVec); + delete hct; } } @@ -673,7 +682,8 @@ TEST_F(TestWalletWithArmory, Comments) auto changeAddr = leafPtr_->getNewChangeAddress(); ASSERT_FALSE(changeAddr.empty()); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -728,6 +738,7 @@ TEST_F(TestWalletWithArmory, Comments) Tx tx(txData); EXPECT_TRUE(tx.isInitialized()); EXPECT_EQ(leafPtr_->getTransactionComment(tx.getThisHash()), txComment); + delete hct; } TEST_F(TestWalletWithArmory, ZCBalance) @@ -737,7 +748,8 @@ TEST_F(TestWalletWithArmory, ZCBalance) const auto changeAddr = leafPtr_->getNewChangeAddress(); EXPECT_EQ(leafPtr_->getUsedAddressCount(), 3); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -920,6 +932,7 @@ TEST_F(TestWalletWithArmory, ZCBalance) EXPECT_EQ(syncLeaf->getSpendableBalance(), double(300 * COIN - amount - fee) / BTCNumericTypes::BalanceDivider - syncLeaf->getUnconfirmedBalance()); EXPECT_EQ(syncLeaf->getUnconfirmedBalance(), 0); + delete hct; } TEST_F(TestWalletWithArmory, SimpleTX_bech32) @@ -930,7 +943,8 @@ TEST_F(TestWalletWithArmory, SimpleTX_bech32) const auto changeAddr = leafPtr_->getNewChangeAddress(); EXPECT_EQ(leafPtr_->getUsedAddressCount(), 4); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1035,6 +1049,7 @@ TEST_F(TestWalletWithArmory, SimpleTX_bech32) auto&& zcVec2 = UnitTestWalletACT::waitOnZC(); EXPECT_EQ(zcVec2[0].txHash, txObj2.getThisHash()); + delete hct; } TEST_F(TestWalletWithArmory, SignSettlement) @@ -1074,7 +1089,8 @@ TEST_F(TestWalletWithArmory, SignSettlement) } /*sync the wallet and connect to db*/ - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1182,11 +1198,13 @@ TEST_F(TestWalletWithArmory, SignSettlement) break; } } + delete hct; } TEST_F(TestWalletWithArmory, GlobalDelegateConf) { - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1368,6 +1386,7 @@ TEST_F(TestWalletWithArmory, GlobalDelegateConf) ASSERT_FALSE(ledgerEntries->empty()); EXPECT_EQ(ledgerEntries->size(), 9); // we have one additional TX on addr at mining EXPECT_EQ(envPtr_->armoryConnection()->getConfirmationsNumber((*ledgerEntries)[1].getBlockNum()), 1); + delete hct; } TEST_F(TestWalletWithArmory, CallbackReturnTxCrash) @@ -1375,7 +1394,8 @@ TEST_F(TestWalletWithArmory, CallbackReturnTxCrash) auto addr = leafPtr_->getNewExtAddress(); ASSERT_FALSE(addr.empty()); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1400,6 +1420,7 @@ TEST_F(TestWalletWithArmory, CallbackReturnTxCrash) promSync->set_value(true); }, true); futSync.wait(); + delete hct; } TEST_F(TestWalletWithArmory, PushZC_retry) @@ -1415,7 +1436,8 @@ TEST_F(TestWalletWithArmory, PushZC_retry) prefixed.append(CryptoPRNG::generateRandom(20)); auto otherAddr = bs::Address::fromHash(prefixed); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(walletPtr_, hct, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1488,4 +1510,5 @@ TEST_F(TestWalletWithArmory, PushZC_retry) const auto &zcId = envPtr_->armoryConnection()->pushZC(txSigned); EXPECT_EQ(UnitTestWalletACT::waitOnBroadcastError(zcId), -27); // Already-in-chain + delete hct; } diff --git a/common b/common index 313a4cc1c..e834a58f0 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 313a4cc1c3fbec13010618d3b4afcf89eec93a8e +Subproject commit e834a58f0fdea7e754f114f35a1c0139d56f27ab From 38ac4ef826226a96ca1d2c2738be8e2c910d5068 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 8 Jan 2021 12:03:30 +0300 Subject: [PATCH 089/146] Switch to C++17 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ef28f934..8836987c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ # CMAKE_MINIMUM_REQUIRED( VERSION 3.3 ) -SET(CMAKE_CXX_STANDARD 14) +SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_CXX_EXTENSIONS OFF) SET(QT_USE_QTDBUS ON) From 7ef7f41e128b8993966fca0fadfa2de571a59b7c Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 8 Jan 2021 11:48:14 +0200 Subject: [PATCH 090/146] Fix build --- BlockSettleUILib/CMakeLists.txt | 2 +- CommonUI/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/CMakeLists.txt b/BlockSettleUILib/CMakeLists.txt index 5dc2ffc05..e20d31e93 100644 --- a/BlockSettleUILib/CMakeLists.txt +++ b/BlockSettleUILib/CMakeLists.txt @@ -10,7 +10,7 @@ # CMAKE_MINIMUM_REQUIRED( VERSION 3.3 ) -SET(CMAKE_CXX_STANDARD 14) +SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_CXX_EXTENSIONS OFF) diff --git a/CommonUI/CMakeLists.txt b/CommonUI/CMakeLists.txt index fa00e6200..dc8525cfc 100644 --- a/CommonUI/CMakeLists.txt +++ b/CommonUI/CMakeLists.txt @@ -10,7 +10,7 @@ # CMAKE_MINIMUM_REQUIRED( VERSION 3.3 ) -SET(CMAKE_CXX_STANDARD 14) +SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD_REQUIRED ON) SET(CMAKE_CXX_EXTENSIONS OFF) From 265784e23e74513ff5b9a626fc0dacedf5fd8247 Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 8 Jan 2021 12:05:59 +0200 Subject: [PATCH 091/146] Fix osx build --- BlockSettleSigner/SignerAdapterListener.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlockSettleSigner/SignerAdapterListener.h b/BlockSettleSigner/SignerAdapterListener.h index b0076299e..5097efd3f 100644 --- a/BlockSettleSigner/SignerAdapterListener.h +++ b/BlockSettleSigner/SignerAdapterListener.h @@ -37,7 +37,7 @@ namespace bs { namespace Blocksettle { namespace Communication { namespace signer { - enum ControlPasswordStatus; + enum ControlPasswordStatus : int; } } } From 1184982408aff9d26d259d9a93367c3b71122f5a Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 8 Jan 2021 16:12:21 +0300 Subject: [PATCH 092/146] Start of deprecated code removal --- BlockSettleApp/main.cpp | 158 +- BlockSettleSigner/SignerAdapterContainer.h | 4 +- BlockSettleUILib/BSTerminalMainWindow.cpp | 2484 ----------------- BlockSettleUILib/BSTerminalMainWindow.h | 373 --- BlockSettleUILib/CreateTransactionDialog.cpp | 26 - BlockSettleUILib/CreateTransactionDialog.h | 8 - .../CreateTransactionDialogAdvanced.cpp | 74 +- .../CreateTransactionDialogAdvanced.h | 40 +- .../CreateTransactionDialogSimple.cpp | 36 +- .../CreateTransactionDialogSimple.h | 18 +- .../RootWalletPropertiesDialog.cpp | 64 - .../RootWalletPropertiesDialog.h | 9 - BlockSettleUILib/PortfolioWidget.cpp | 2 +- BlockSettleUILib/PubKeyLoader.cpp | 6 +- BlockSettleUILib/SelectWalletDialog.cpp | 29 - BlockSettleUILib/SelectWalletDialog.h | 2 - BlockSettleUILib/StatusBarView.cpp | 4 +- .../Trading/AutoSignQuoteProvider.cpp | 4 +- .../Trading/DealerXBTSettlementContainer.cpp | 2 +- BlockSettleUILib/Trading/OtcClient.cpp | 2 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 86 - BlockSettleUILib/Trading/RFQRequestWidget.h | 18 - BlockSettleUILib/Trading/RFQTicketXBT.cpp | 55 - BlockSettleUILib/Trading/RFQTicketXBT.h | 9 - .../Trading/ReqCCSettlementContainer.cpp | 2 +- .../Trading/ReqXBTSettlementContainer.cpp | 2 +- BlockSettleUILib/TransactionsWidget.cpp | 16 - BlockSettleUILib/TransactionsWidget.h | 6 - .../TransactionsWidgetInterface.cpp | 32 +- .../TransactionsWidgetInterface.h | 6 - BlockSettleUILib/WalletsViewModel.cpp | 26 - BlockSettleUILib/WalletsViewModel.h | 2 - BlockSettleUILib/WalletsWidget.cpp | 77 +- BlockSettleUILib/WalletsWidget.h | 9 - Celer | 2 +- Core/SignerAdapter.h | 2 +- UnitTests/TestAuth.cpp | 2 +- UnitTests/TestAuth.h | 3 +- UnitTests/TestCCoin.cpp | 3 +- UnitTests/TestCCoin.h | 5 +- UnitTests/TestCCoinAsync.cpp | 3 +- UnitTests/TestCCoinAsync.h | 5 +- UnitTests/TestCommon.cpp | 6 +- UnitTests/TestOtc.cpp | 5 +- UnitTests/TestSettlement.cpp | 2 +- UnitTests/TestSettlement.h | 3 +- UnitTests/TestWallet.cpp | 14 +- UnitTests/TestWalletArmory.cpp | 24 +- common | 2 +- 49 files changed, 112 insertions(+), 3660 deletions(-) delete mode 100644 BlockSettleUILib/BSTerminalMainWindow.cpp delete mode 100644 BlockSettleUILib/BSTerminalMainWindow.h diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 4cdd361fb..f2c72300b 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -9,24 +9,16 @@ */ #include -#include #include -#include -#include -#include -#include -#include +#include +#include #include #include -#include #include - #include - #include "ApplicationSettings.h" #include "BSErrorCode.h" #include "BSMessageBox.h" -#include "BSTerminalMainWindow.h" #include "BSTerminalSplashScreen.h" #include "EncryptionUtils.h" @@ -154,150 +146,6 @@ static QScreen *getDisplay(QPoint position) return QGuiApplication::primaryScreen(); } -static int runUnchecked(QApplication *app, const std::shared_ptr &settings - , BSTerminalSplashScreen &splashScreen, QLockFile &lockFile) -{ - BSTerminalMainWindow mainWindow(settings, splashScreen, lockFile); - -#if defined (Q_OS_MAC) - MacOsApp *macApp = (MacOsApp*)(app); - - QObject::connect(macApp, &MacOsApp::reactivateTerminal, &mainWindow, &BSTerminalMainWindow::onReactivate); -#endif - - if (!settings->get(ApplicationSettings::launchToTray)) { - mainWindow.loadPositionAndShow(); - } - - mainWindow.postSplashscreenActions(); - -// bs::disableAppNap(); - - return app->exec(); -} - -static int runChecked(QApplication *app, const std::shared_ptr &settings - , BSTerminalSplashScreen &splashScreen, QLockFile &lockFile) -{ - try { - return runUnchecked(app, settings, splashScreen, lockFile); - } - catch (const std::exception &e) { - std::cerr << "Failed to start BlockSettle Terminal: " << e.what() << std::endl; - BSMessageBox(BSMessageBox::critical, app->tr("BlockSettle Terminal") - , app->tr("Unhandled exception detected: %1").arg(QLatin1String(e.what()))).exec(); - return EXIT_FAILURE; - } -} - -static int GuiApp(int &argc, char** argv) -{ - Q_INIT_RESOURCE(armory); - Q_INIT_RESOURCE(tradinghelp); - Q_INIT_RESOURCE(wallethelp); - - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); - QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - -#if defined (Q_OS_MAC) - MacOsApp app(argc, argv); -#else - QApplication app(argc, argv); -#endif - - QApplication::setQuitOnLastWindowClosed(false); - - QFileInfo localStyleSheetFile(QLatin1String("stylesheet.css")); - - QFile stylesheetFile(localStyleSheetFile.exists() - ? localStyleSheetFile.fileName() - : QLatin1String(":/STYLESHEET")); - - if (stylesheetFile.open(QFile::ReadOnly)) { - app.setStyleSheet(QString::fromLatin1(stylesheetFile.readAll())); - QPalette p = QApplication::palette(); - p.setColor(QPalette::Disabled, QPalette::Light, QColor(10,22,25)); - QApplication::setPalette(p); - } - -#ifndef NDEBUG - // Start monitoring to update stylesheet live when file is changed on the disk - QTimer timer; - QObject::connect(&timer, &QTimer::timeout, &app, [&app] { - checkStyleSheet(app); - }); - timer.start(100); -#endif - - QDirIterator it(QLatin1String(":/resources/Raleway/")); - while (it.hasNext()) { - QFontDatabase::addApplicationFont(it.next()); - } - - QString location = QStandardPaths::writableLocation(QStandardPaths::TempLocation); -#ifndef NDEBUG - QString userName = QDir::home().dirName(); - QString lockFilePath = location + QLatin1String("/blocksettle-") + userName + QLatin1String(".lock"); -#else - QString lockFilePath = location + QLatin1String("/blocksettle.lock"); -#endif - QLockFile lockFile(lockFilePath); - lockFile.setStaleLockTime(0); - - if (!lockFile.tryLock()) { - BSMessageBox box(BSMessageBox::info, app.tr("BlockSettle Terminal") - , app.tr("BlockSettle Terminal is already running") - , app.tr("Stop the other BlockSettle Terminal instance. If no other " \ - "instance is running, delete the lockfile (%1).").arg(lockFilePath)); - return box.exec(); - } - - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType>(); - qRegisterMetaType(); - qRegisterMetaType>>(); - qRegisterMetaType(); - qRegisterMetaType>(); - qRegisterMetaType>(); - qRegisterMetaType(); - - // load settings - auto settings = std::make_shared(); - if (!settings->LoadApplicationSettings(app.arguments())) { - BSMessageBox errorMessage(BSMessageBox::critical, app.tr("Error") - , app.tr("Failed to parse command line arguments") - , settings->ErrorText()); - errorMessage.exec(); - return EXIT_FAILURE; - } - - QString logoIcon; - logoIcon = QLatin1String(":/SPLASH_LOGO"); - - QPixmap splashLogo(logoIcon); - const int splashScreenWidth = 400; - BSTerminalSplashScreen splashScreen(splashLogo.scaledToWidth(splashScreenWidth, Qt::SmoothTransformation)); - - auto mainGeometry = settings->get(ApplicationSettings::GUI_main_geometry); - auto currentDisplay = getDisplay(mainGeometry.center()); - auto splashGeometry = splashScreen.geometry(); - splashGeometry.moveCenter(currentDisplay->geometry().center()); - splashScreen.setGeometry(splashGeometry); - - app.processEvents(); - -#ifdef NDEBUG - return runChecked(&app, settings, splashScreen, lockFile); -#else - return runUnchecked(&app, settings, splashScreen, lockFile); -#endif -} - int main(int argc, char** argv) { srand(std::time(nullptr)); @@ -365,8 +213,6 @@ int main(int argc, char** argv) return EXIT_FAILURE; } #endif //NDEBUG - -// return GuiApp(argc, argv); } #include "main.moc" diff --git a/BlockSettleSigner/SignerAdapterContainer.h b/BlockSettleSigner/SignerAdapterContainer.h index ebfd83ed8..3eb2bfd2d 100644 --- a/BlockSettleSigner/SignerAdapterContainer.h +++ b/BlockSettleSigner/SignerAdapterContainer.h @@ -21,12 +21,12 @@ namespace spdlog { class logger; } -class SignAdapterContainer : public WalletSignerContainer +class SignAdapterContainer : public WalletSignerContainer, public SignerCallbackTarget { public: SignAdapterContainer(const std::shared_ptr &logger , const std::shared_ptr &lsn) - : WalletSignerContainer(logger, OpMode::LocalInproc), listener_(lsn) + : WalletSignerContainer(logger, this, OpMode::LocalInproc), listener_(lsn) {} ~SignAdapterContainer() noexcept override = default; diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp deleted file mode 100644 index c1c27707c..000000000 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ /dev/null @@ -1,2484 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "BSTerminalMainWindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ArmoryServersProvider.h" -#include "AssetManager.h" -#include "AuthAddressDialog.h" -#include "AuthAddressManager.h" -#include "AutheIDClient.h" -#include "AutoSignQuoteProvider.h" -#include "Bip15xDataConnection.h" -#include "BootstrapDataManager.h" -#include "BSMarketDataProvider.h" -#include "BSMessageBox.h" -#include "BSTerminalSplashScreen.h" -#include "CCFileManager.h" -#include "CCPortfolioModel.h" -#include "CCTokenEntryDialog.h" -#include "CelerAccountInfoDialog.h" -#include "ColoredCoinServer.h" -#include "ConnectionManager.h" -#include "CreatePrimaryWalletPrompt.h" -#include "CreateTransactionDialogAdvanced.h" -#include "CreateTransactionDialogSimple.h" -#include "DialogManager.h" -#include "FutureValue.h" -#include "HeadlessContainer.h" -#include "ImportKeyBox.h" -#include "InfoDialogs/AboutDialog.h" -#include "InfoDialogs/MDAgreementDialog.h" -#include "InfoDialogs/StartupDialog.h" -#include "InfoDialogs/SupportDialog.h" -#include "LoginWindow.h" -#include "MarketDataProvider.h" -#include "MDCallbacksQt.h" -#include "NewAddressDialog.h" -#include "NewWalletDialog.h" -#include "NotificationCenter.h" -#include "OpenURIDialog.h" -#include "OrderListModel.h" -#include "PubKeyLoader.h" -#include "QuoteProvider.h" -#include "RequestReplyCommand.h" -#include "RetryingDataConnection.h" -#include "SelectWalletDialog.h" -#include "Settings/ConfigDialog.h" -#include "SignersProvider.h" -#include "SslCaBundle.h" -#include "SslDataConnection.h" -#include "StatusBarView.h" -#include "StringUtils.h" -#include "SystemFileUtils.h" -#include "TabWithShortcut.h" -#include "TransactionsViewModel.h" -#include "TransactionsWidget.h" -#include "TransportBIP15x.h" -#include "UiUtils.h" -#include "UserScriptRunner.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "WsDataConnection.h" - -#include "ui_BSTerminalMainWindow.h" - -namespace { - const auto kAutoLoginTimer = std::chrono::seconds(10); -} - -BSTerminalMainWindow::BSTerminalMainWindow(const std::shared_ptr& settings - , BSTerminalSplashScreen& splashScreen, QLockFile &lockFile, QWidget* parent) - : QMainWindow(parent) - , ui_(new Ui::BSTerminalMainWindow()) - , applicationSettings_(settings) - , lockFile_(lockFile) -{ - UiUtils::SetupLocale(); - - ui_->setupUi(this); - - setupShortcuts(); - setupInfoWidget(); - - loginButtonText_ = tr("Login"); - - logMgr_ = std::make_shared(); - logMgr_->add(applicationSettings_->GetLogsConfig()); - logMgr_->logger()->debug("Settings loaded from {}", applicationSettings_->GetSettingsPath().toStdString()); - - bool licenseAccepted = showStartupDialog(); - if (!licenseAccepted) { - QMetaObject::invokeMethod(this, []() { - qApp->exit(EXIT_FAILURE); - }, Qt::QueuedConnection); - return; - } - - initBootstrapDataManager(); - - nextArmoryReconnectAttempt_ = std::chrono::steady_clock::now(); - signersProvider_= std::make_shared(applicationSettings_); - armoryServersProvider_ = std::make_shared(applicationSettings_, bootstrapDataManager_); - - if (applicationSettings_->get(ApplicationSettings::armoryDbName).isEmpty()) { - const auto env = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration)); - switch(env) { - case ApplicationSettings::EnvConfiguration::Production: - armoryServersProvider_->setupServer(armoryServersProvider_->getIndexOfMainNetServer(), false); - break; - case ApplicationSettings::EnvConfiguration::Test: -#ifndef PRODUCTION_BUILD - case ApplicationSettings::EnvConfiguration::Staging: -#endif - armoryServersProvider_->setupServer(armoryServersProvider_->getIndexOfTestNetServer(), false); - break; - } - } - - splashScreen.show(); - - connect(ui_->actionQuit, &QAction::triggered, qApp, &QCoreApplication::quit); - - bs::UtxoReservation::init(logMgr_->logger()); - - setupIcon(); - UiUtils::setupIconFont(this); - NotificationCenter::createInstance(logMgr_->logger(), applicationSettings_, ui_.get(), sysTrayIcon_, this); - - initConnections(); - initArmory(); - initCcClient(); - - walletsMgr_ = std::make_shared(logMgr_->logger(), applicationSettings_, armory_, trackerClient_); - - if (!applicationSettings_->get(ApplicationSettings::initialized)) { - applicationSettings_->SetDefaultSettings(true); - } - - InitAssets(); - InitSigningContainer(); - InitAuthManager(); - initUtxoReservationManager(); - - cbApproveChat_ = PubKeyLoader::getApprovingCallback(PubKeyLoader::KeyType::Chat - , this, applicationSettings_, bootstrapDataManager_); - cbApproveProxy_ = PubKeyLoader::getApprovingCallback(PubKeyLoader::KeyType::Proxy - , this, applicationSettings_, bootstrapDataManager_); - cbApproveCcServer_ = PubKeyLoader::getApprovingCallback(PubKeyLoader::KeyType::CcServer - , this, applicationSettings_, bootstrapDataManager_); - cbApproveExtConn_ = PubKeyLoader::getApprovingCallback(PubKeyLoader::KeyType::ExtConnector - , this, applicationSettings_, bootstrapDataManager_); - - statusBarView_ = std::make_shared(armory_, walletsMgr_, assetManager_, celerConnection_ - , signContainer_, ui_->statusbar); - - splashScreen.SetProgress(100); - splashScreen.close(); - QApplication::processEvents(); - - setupToolbar(); - setupMenu(); - - ui_->widgetTransactions->setEnabled(false); - - connectSigner(); - connectArmory(); - connectCcClient(); - - InitChartsView(); - - ui_->tabWidget->setCurrentIndex(settings->get(ApplicationSettings::GUI_main_tab)); - - UpdateMainWindowAppearence(); - setWidgetsAuthorized(false); - - updateControlEnabledState(); - - InitWidgets(); - - loginApiKeyEncrypted_ = applicationSettings_->get(ApplicationSettings::LoginApiKey); - -#ifdef PRODUCTION_BUILD - const bool showEnvSelector = false; -#else - const bool showEnvSelector = true; -#endif - ui_->prodEnvSettings->setVisible(showEnvSelector); - ui_->testEnvSettings->setVisible(showEnvSelector); -} - -void BSTerminalMainWindow::onBsConnectionDisconnected() -{ - onCelerDisconnected(); -} - -void BSTerminalMainWindow::onBsConnectionFailed() -{ - SPDLOG_LOGGER_ERROR(logMgr_->logger(), "BsClient disconnected unexpectedly"); - showError(tr("Network error"), tr("Connection to BlockSettle server failed")); -} - -void BSTerminalMainWindow::onInitWalletDialogWasShown() -{ - initialWalletCreateDialogShown_ = true; -} - -void BSTerminalMainWindow::setWidgetsAuthorized(bool authorized) -{ - // Update authorized state for some widgets - ui_->widgetPortfolio->setAuthorized(authorized); - ui_->widgetRFQ->setAuthorized(authorized); - ui_->widgetChart->setAuthorized(authorized); -} - -void BSTerminalMainWindow::postSplashscreenActions() -{ - if (applicationSettings_->get(ApplicationSettings::SubscribeToMDOnStart)) { - mdProvider_->SubscribeToMD(); - } -} - -void BSTerminalMainWindow::loadPositionAndShow() -{ - auto geom = applicationSettings_->get(ApplicationSettings::GUI_main_geometry); - if (geom.isEmpty()) { - show(); - return; - } - setGeometry(geom); // This call is required for screenNumber() method to work properly - -#ifdef Q_OS_WINDOWS - int screenNo = QApplication::desktop()->screenNumber(this); - if (screenNo < 0) { - screenNo = 0; - } - const auto screenGeom = QApplication::desktop()->screenGeometry(screenNo); - if (!screenGeom.contains(geom)) { - const int screenWidth = screenGeom.width() * 0.9; - const int screenHeight = screenGeom.height() * 0.9; - geom.setWidth(std::min(geom.width(), screenWidth)); - geom.setHeight(std::min(geom.height(), screenHeight)); - geom.moveCenter(screenGeom.center()); - } - const auto screen = qApp->screens()[screenNo]; - const float pixelRatio = screen->devicePixelRatio(); - if (pixelRatio > 1.0) { - const float coeff = (float)0.9999; // some coefficient that prevents oversizing of main window on HiRes display on Windows - geom.setWidth(geom.width() * coeff); - geom.setHeight(geom.height() * coeff); - } - setGeometry(geom); -#else - if (QApplication::desktop()->screenNumber(this) == -1) { - auto currentScreenRect = QApplication::desktop()->screenGeometry(QCursor::pos()); - // Do not delete 0.9 multiplier, since in some system window size is applying without system native toolbar - geom.setWidth(std::min(geom.width(), static_cast(currentScreenRect.width() * 0.9))); - geom.setHeight(std::min(geom.height(), static_cast(currentScreenRect.height() * 0.9))); - geom.moveCenter(currentScreenRect.center()); - setGeometry(geom); -} -#endif // not Windows - - show(); -} - -bool BSTerminalMainWindow::event(QEvent *event) -{ - if (event->type() == QEvent::WindowActivate) { - auto tabChangedSignal = QMetaMethod::fromSignal(&QTabWidget::currentChanged); - int currentIndex = ui_->tabWidget->currentIndex(); - tabChangedSignal.invoke(ui_->tabWidget, Q_ARG(int, currentIndex)); - } - return QMainWindow::event(event); -} - -BSTerminalMainWindow::~BSTerminalMainWindow() -{ - // Check UTXO reservation state before any other destructors call! - if (bs::UtxoReservation::instance()) { - bs::UtxoReservation::instance()->shutdownCheck(); - } - - applicationSettings_->set(ApplicationSettings::GUI_main_geometry, geometry()); - applicationSettings_->set(ApplicationSettings::GUI_main_tab, ui_->tabWidget->currentIndex()); - applicationSettings_->SaveSettings(); - - NotificationCenter::destroyInstance(); - if (signContainer_) { - signContainer_->Stop(); - signContainer_.reset(); - } - walletsMgr_.reset(); - assetManager_.reset(); -} - -void BSTerminalMainWindow::setupToolbar() -{ - action_send_ = new QAction(tr("Send Bitcoin"), this); - connect(action_send_, &QAction::triggered, this, &BSTerminalMainWindow::onSend); - - action_generate_address_ = new QAction(tr("Generate &Address"), this); - connect(action_generate_address_, &QAction::triggered, this, &BSTerminalMainWindow::onGenerateAddress); - - action_login_ = new QAction(tr("Login to BlockSettle"), this); - connect(action_login_, &QAction::triggered, this, &BSTerminalMainWindow::onLogin); - - action_logout_ = new QAction(tr("Logout from BlockSettle"), this); - connect(action_logout_, &QAction::triggered, this, &BSTerminalMainWindow::onLogout); - - setupTopRightWidget(); - - action_logout_->setVisible(false); - - connect(ui_->pushButtonUser, &QPushButton::clicked, this, &BSTerminalMainWindow::onButtonUserClicked); - - QMenu* trayMenu = new QMenu(this); - QAction* trayShowAction = trayMenu->addAction(tr("&Open Terminal")); - connect(trayShowAction, &QAction::triggered, this, &QMainWindow::show); - trayMenu->addSeparator(); - - trayMenu->addAction(action_send_); - trayMenu->addAction(action_generate_address_); - trayMenu->addAction(ui_->actionSettings); - - trayMenu->addSeparator(); - trayMenu->addAction(ui_->actionQuit); - sysTrayIcon_->setContextMenu(trayMenu); -} - -void BSTerminalMainWindow::setupTopRightWidget() -{ - auto toolBar = new QToolBar(this); - toolBar->setObjectName(QLatin1String("mainToolBar")); - toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - ui_->tabWidget->setCornerWidget(toolBar, Qt::TopRightCorner); - - toolBar->addAction(action_send_); - toolBar->addAction(action_generate_address_); - - for (int i = 0; i < toolBar->children().size(); ++i) { - auto *toolButton = qobject_cast(toolBar->children().at(i)); - if (toolButton && (toolButton->defaultAction() == action_send_ - || toolButton->defaultAction() == action_generate_address_)) { - toolButton->setObjectName(QLatin1String("mainToolBarActions")); - } - } - -#if defined(Q_OS_WIN) - ui_->tabWidget->setProperty("onWindows", QVariant(true)); -#elif defined(Q_OS_LINUX) - ui_->tabWidget->setProperty("onLinux", QVariant(true)); -#else - ui_->tabWidget->setProperty("onMacos", QVariant(true)); -#endif - - auto *prevStyle = ui_->tabWidget->style(); - ui_->tabWidget->setStyle(nullptr); - ui_->tabWidget->setStyle(prevStyle); -} - -void BSTerminalMainWindow::setupIcon() -{ - QIcon icon; - QString iconFormatString = QString::fromStdString(":/ICON_BS_%1"); - - for (const int s : {16, 24, 32}) { - icon.addFile(iconFormatString.arg(s), QSize(s, s)); - } - - setWindowIcon(icon); - - sysTrayIcon_ = std::make_shared(icon, this); - sysTrayIcon_->setToolTip(windowTitle()); - sysTrayIcon_->show(); - - connect(sysTrayIcon_.get(), &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason reason) { - if (reason == QSystemTrayIcon::Context) { - // Right click, this is handled by the menu, so we don't do anything here. - return; - } - - setWindowState(windowState() & ~Qt::WindowMinimized); - show(); - raise(); - activateWindow(); - }); - - connect(qApp, &QCoreApplication::aboutToQuit, sysTrayIcon_.get(), &QSystemTrayIcon::hide); - connect(qApp, SIGNAL(lastWindowClosed()), sysTrayIcon_.get(), SLOT(hide())); -} - -void BSTerminalMainWindow::setupInfoWidget() -{ - const bool show = applicationSettings_->get(ApplicationSettings::ShowInfoWidget); - ui_->infoWidget->setVisible(show); - - if (!show) { - return; - } - - connect(ui_->introductionBtn, &QPushButton::clicked, this, []() { - QDesktopServices::openUrl(QUrl(QLatin1String("https://www.youtube.com/watch?v=mUqKq9GKjmI"))); - }); - connect(ui_->tutorialsButton, &QPushButton::clicked, this, []() { - QDesktopServices::openUrl(QUrl(QLatin1String("https://blocksettle.com/tutorials"))); - }); - connect(ui_->closeBtn, &QPushButton::clicked, this, [this]() { - ui_->infoWidget->setVisible(false); - applicationSettings_->set(ApplicationSettings::ShowInfoWidget, false); - }); -} - -void BSTerminalMainWindow::initConnections() -{ - connectionManager_ = std::make_shared(logMgr_->logger("message")); - connectionManager_->setCaBundle(bs::caBundlePtr(), bs::caBundleSize()); - - celerConnection_ = std::make_shared(logMgr_->logger()); - connect(celerConnection_.get(), &CelerClientQt::OnConnectedToServer, this, &BSTerminalMainWindow::onCelerConnected); - connect(celerConnection_.get(), &CelerClientQt::OnConnectionClosed, this, &BSTerminalMainWindow::onCelerDisconnected); - connect(celerConnection_.get(), &CelerClientQt::OnConnectionError, this, &BSTerminalMainWindow::onCelerConnectionError, Qt::QueuedConnection); - - mdCallbacks_ = std::make_shared(); - mdProvider_ = std::make_shared(connectionManager_ - , logMgr_->logger("message"), mdCallbacks_.get(), true, false); - connect(mdCallbacks_.get(), &MDCallbacksQt::UserWantToConnectToMD, this, &BSTerminalMainWindow::acceptMDAgreement); - connect(mdCallbacks_.get(), &MDCallbacksQt::WaitingForConnectionDetails, this, [this] { - auto env = static_cast( - applicationSettings_->get(ApplicationSettings::envConfiguration)); - mdProvider_->SetConnectionSettings(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::MdServer, env) - , PubKeyLoader::serverHttpsPort()); - }); -} - -void BSTerminalMainWindow::LoadWallets() -{ - logMgr_->logger()->debug("Loading wallets"); - - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletsReady, this, [this] { - ui_->widgetRFQ->setWalletsManager(walletsMgr_); - ui_->widgetRFQReply->setWalletsManager(walletsMgr_); - autoSignQuoteProvider_->setWalletsManager(walletsMgr_); - autoSignRFQProvider_->setWalletsManager(walletsMgr_); - }); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletsSynchronized, this, [this] { - walletsSynched_ = true; - updateControlEnabledState(); - CompleteDBConnection(); - act_->onRefresh({}, true); - tryGetChatKeys(); - }); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::info, this, &BSTerminalMainWindow::showInfo); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::error, this, &BSTerminalMainWindow::showError); - - // Enable/disable send action when first wallet created/last wallet removed - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletChanged, this - , &BSTerminalMainWindow::updateControlEnabledState); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletDeleted, this, [this] { - updateControlEnabledState(); - resetChatKeys(); - }); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletAdded, this, [this] { - updateControlEnabledState(); - tryGetChatKeys(); - }); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::newWalletAdded, this - , &BSTerminalMainWindow::updateControlEnabledState); - - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, [this](const std::string &walletId) { - auto wallet = dynamic_cast(walletsMgr_->getWalletById(walletId).get()); - if (wallet && wallet->purpose() == bs::hd::Purpose::NonSegWit && wallet->getTotalBalance() > 0) { - showLegacyWarningIfNeeded(); - } - }); - - connect(walletsMgr_.get(), &bs::sync::WalletsManager::AuthLeafCreated, this, &BSTerminalMainWindow::onAuthLeafCreated); - - onSyncWallets(); -} - -void BSTerminalMainWindow::InitAuthManager() -{ - authManager_ = std::make_shared(logMgr_->logger(), armory_); - authManager_->init(applicationSettings_, walletsMgr_, signContainer_); - - connect(authManager_.get(), &AuthAddressManager::AddrVerifiedOrRevoked, this, [](const QString &addr, int state) { - NotificationCenter::notify(bs::ui::NotifyType::AuthAddress, { addr, state }); - }); - connect(authManager_.get(), &AuthAddressManager::AuthWalletCreated, this, [this](const QString &walletId) { - if (authAddrDlg_ && walletId.isEmpty()) { - openAuthManagerDialog(); - } - }); - - authManager_->SetLoadedValidationAddressList(bootstrapDataManager_->GetAuthValidationList()); -} - -std::shared_ptr BSTerminalMainWindow::createSigner() -{ - if (signersProvider_->currentSignerIsLocal()) { - return createLocalSigner(); - } else { - return createRemoteSigner(); - } -} - -std::shared_ptr BSTerminalMainWindow::createRemoteSigner(bool restoreHeadless) -{ - SignerHost signerHost = signersProvider_->getCurrentSigner(); - QString resultPort = QString::number(signerHost.port); - NetworkType netType = applicationSettings_->get(ApplicationSettings::netType); - - // Define the callback that will be used to determine if the signer's BIP - // 150 identity key, if it has changed, will be accepted. It needs strings - // for the old and new keys, and a promise to set once the user decides. - const auto &ourNewKeyCB = [this](const std::string& oldKey, const std::string& newKey - , const std::string& srvAddrPort - , const std::shared_ptr> &newKeyProm) { - logMgr_->logger()->debug("[BSTerminalMainWindow::createSigner::callback] received" - " new key {} [{}], old key {} [{}] for {} ({})", newKey, newKey.size(), oldKey - , oldKey.size(), srvAddrPort, signersProvider_->getCurrentSigner().serverId()); - std::string oldKeyHex = oldKey; - if (oldKeyHex.empty() && (signersProvider_->getCurrentSigner().serverId() == srvAddrPort)) { - oldKeyHex = signersProvider_->getCurrentSigner().key.toStdString(); - } - - const auto &deferredDialog = [this, oldKeyHex, newKey, newKeyProm, srvAddrPort]{ - ImportKeyBox box(BSMessageBox::question - , tr("Import Signer ID Key?") - , this); - - box.setNewKey(newKey); - box.setOldKey(QString::fromStdString(oldKeyHex)); - box.setAddrPort(srvAddrPort); - - const bool answer = (box.exec() == QDialog::Accepted); - - if (answer) { - signersProvider_->addKey(srvAddrPort, newKey); - } - - bool result = newKeyProm->setValue(answer); - if (!result) { - SPDLOG_LOGGER_DEBUG(logMgr_->logger() - , "can't set result for signer key prompt for {}, perhaps connection was already closed" - , srvAddrPort); - } - }; - - addDeferredDialog(deferredDialog); - }; - - QString resultHost = signerHost.address; - const auto remoteSigner = std::make_shared(logMgr_->logger() - , resultHost, resultPort, netType, connectionManager_, nullptr - , SignContainer::OpMode::Remote, false - , signersProvider_->remoteSignerKeysDir(), signersProvider_->remoteSignerKeysFile(), ourNewKeyCB); - - bs::network::BIP15xPeers peers; - for (const auto &signer : signersProvider_->signers()) { - try { - const BinaryData signerKey = BinaryData::CreateFromHex(signer.key.toStdString()); - peers.push_back(bs::network::BIP15xPeer(signer.serverId(), signerKey)); - } - catch (const std::exception &e) { - logMgr_->logger()->warn("[{}] invalid signer key: {}", __func__, e.what()); - } - } - remoteSigner->updatePeerKeys(peers); - - if (restoreHeadless) { - // setup headleass signer back (it was changed when createLocalSigner called signersProvider_->switchToLocalFullGUI) - signersProvider_->setupSigner(0, true); - } - - return remoteSigner; -} - -std::shared_ptr BSTerminalMainWindow::createLocalSigner() -{ - QLatin1String localSignerHost("127.0.0.1"); - QString localSignerPort; - NetworkType netType = applicationSettings_->get(ApplicationSettings::netType); - - for (int attempts = 0; attempts < 10; ++attempts) { - // https://tools.ietf.org/html/rfc6335 - // the Dynamic Ports, also known as the Private or Ephemeral Ports, - // from 49152-65535 (never assigned) - auto port = 49152 + rand() % 16000; - - auto portToTest = QString::number(port); - - if (!SignerConnectionExists(localSignerHost, portToTest)) { - localSignerPort = portToTest; - break; - } else { - logMgr_->logger()->error("[BSTerminalMainWindow::createLocalSigner] attempt {} : port {} used" - , port); - } - } - - if (localSignerPort.isEmpty()) { - logMgr_->logger()->error("[BSTerminalMainWindow::createLocalSigner] failed to find not busy port"); - return nullptr; - } - - const bool startLocalSignerProcess = true; - return std::make_shared(logMgr_->logger() - , applicationSettings_->GetHomeDir(), netType - , localSignerPort, connectionManager_, nullptr - , startLocalSignerProcess, "", "" - , applicationSettings_->get(ApplicationSettings::autoSignSpendLimit)); -} - -bool BSTerminalMainWindow::InitSigningContainer() -{ - signContainer_ = createSigner(); - - if (!signContainer_) { - showError(tr("BlockSettle Signer"), tr("BlockSettle Signer creation failure")); - return false; - } - connect(signContainer_.get(), &SignContainer::connectionError, this, &BSTerminalMainWindow::onSignerConnError, Qt::QueuedConnection); - connect(signContainer_.get(), &SignContainer::disconnected, this, &BSTerminalMainWindow::updateControlEnabledState, Qt::QueuedConnection); - - walletsMgr_->setSignContainer(signContainer_); - connect(signContainer_.get(), &WalletSignerContainer::ready, this, &BSTerminalMainWindow::SignerReady, Qt::QueuedConnection); - connect(signContainer_.get(), &WalletSignerContainer::needNewWalletPrompt, this, &BSTerminalMainWindow::onNeedNewWallet, Qt::QueuedConnection); - connect(signContainer_.get(), &WalletSignerContainer::walletsReadyToSync, this, &BSTerminalMainWindow::onSyncWallets, Qt::QueuedConnection); - connect(signContainer_.get(), &WalletSignerContainer::windowVisibilityChanged, this, &BSTerminalMainWindow::onSignerVisibleChanged, Qt::QueuedConnection); - - return true; -} - -void BSTerminalMainWindow::SignerReady() -{ - updateControlEnabledState(); - - LoadWallets(); - - walletsMgr_->setUserId(BinaryData::CreateFromHex(celerConnection_->userId())); - - if (deferCCsync_) { - signContainer_->syncCCNames(walletsMgr_->ccResolver()->securities()); - deferCCsync_ = false; - } - - lastSignerError_ = SignContainer::NoError; -} - -void BSTerminalMainWindow::onNeedNewWallet() -{ - if (!initialWalletCreateDialogShown_) { - initialWalletCreateDialogShown_ = true; - const auto &deferredDialog = [this]{ - ui_->widgetWallets->onNewWallet(); - }; - addDeferredDialog(deferredDialog); - } -} - -void BSTerminalMainWindow::acceptMDAgreement() -{ - const auto &deferredDailog = [this]{ - if (!isMDLicenseAccepted()) { - MDAgreementDialog dlg{this}; - if (dlg.exec() != QDialog::Accepted) { - return; - } - - saveUserAcceptedMDLicense(); - } - - mdProvider_->MDLicenseAccepted(); - }; - - addDeferredDialog(deferredDailog); -} - -void BSTerminalMainWindow::updateControlEnabledState() -{ - if (action_send_) { - const bool txCreationEnabled = !walletsMgr_->hdWallets().empty() - && armory_->isOnline() && signContainer_ && signContainer_->isReady(); - - action_send_->setEnabled(txCreationEnabled); - ui_->actionOpenURI->setEnabled(txCreationEnabled); - } - // Do not allow login until wallets synced (we need to check if user has primary wallet or not). - // Should be OK for both local and remote signer. - bool loginAllowed = walletsSynched_ && loginApiKeyEncrypted().empty(); - ui_->pushButtonUser->setEnabled(loginAllowed); - action_login_->setEnabled(true); - - action_login_->setVisible(!celerConnection_->IsConnected()); - action_login_->setEnabled(loginAllowed); - action_logout_->setVisible(celerConnection_->IsConnected()); -} - -bool BSTerminalMainWindow::isMDLicenseAccepted() const -{ - return applicationSettings_->get(ApplicationSettings::MDLicenseAccepted); -} - -void BSTerminalMainWindow::saveUserAcceptedMDLicense() -{ - applicationSettings_->set(ApplicationSettings::MDLicenseAccepted, true); -} - -bool BSTerminalMainWindow::showStartupDialog() -{ - bool wasInitialized = applicationSettings_->get(ApplicationSettings::initialized); - if (wasInitialized) { - return true; - } - - #ifdef _WIN32 - // Read registry value in case it was set with installer. Could be used only on Windows for now. - QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Software\\blocksettle\\blocksettle"), QSettings::NativeFormat); - bool showLicense = !settings.value(QLatin1String("license_accepted"), false).toBool(); - #else - bool showLicense = true; - #endif // _WIN32 - - StartupDialog startupDialog(showLicense); - startupDialog.init(applicationSettings_); - int result = startupDialog.exec(); - - if (result == QDialog::Rejected) { - hide(); - return false; - } - - // Need update armory settings if case user selects TestNet - startupDialog.applySelectedConnectivity(); - - return true; -} - -void BSTerminalMainWindow::InitAssets() -{ - ccFileManager_ = std::make_shared(logMgr_->logger(), applicationSettings_); - assetManager_ = std::make_shared(logMgr_->logger(), walletsMgr_ - , mdCallbacks_, celerConnection_); - assetManager_->init(); - - orderListModel_ = std::make_unique(assetManager_); - - connect(ccFileManager_.get(), &CCFileManager::CCSecurityDef, assetManager_.get(), &AssetManager::onCCSecurityReceived); - connect(ccFileManager_.get(), &CCFileManager::CCSecurityInfo, walletsMgr_.get(), &bs::sync::WalletsManager::onCCSecurityInfo); - connect(ccFileManager_.get(), &CCFileManager::Loaded, this, &BSTerminalMainWindow::onCCLoaded); - - connect(mdCallbacks_.get(), &MDCallbacksQt::MDUpdate, assetManager_.get(), &AssetManager::onMDUpdate); - - ccFileManager_->SetLoadedDefinitions(bootstrapDataManager_->GetCCDefinitions()); -} - -void BSTerminalMainWindow::InitPortfolioView() -{ - portfolioModel_ = std::make_shared(walletsMgr_, assetManager_, this); - ui_->widgetPortfolio->init(applicationSettings_, mdProvider_, mdCallbacks_ - , portfolioModel_, signContainer_, armory_, utxoReservationMgr_, logMgr_->logger("ui"), walletsMgr_); -} - -void BSTerminalMainWindow::InitWalletsView() -{ - ui_->widgetWallets->init(logMgr_->logger("ui"), walletsMgr_, signContainer_ - , applicationSettings_, connectionManager_, assetManager_, authManager_, armory_); - connect(ui_->widgetWallets, &WalletsWidget::newWalletCreationRequest, this, &BSTerminalMainWindow::onInitWalletDialogWasShown); -} - -void BSTerminalMainWindow::tryInitChatView() -{ - // Chat initialization is a bit convoluted. - // First we need to create and initialize chatClientServicePtr_ (which lives in background thread and so is async). - // For this it needs to know chat server address where to connect and chat keys used for chat messages encryption. - // Only after that we could init ui_->widgetChat and try to login after that. - if (chatInitState_ != ChatInitState::NoStarted || !gotChatKeys_) { - return; - } - chatInitState_ = ChatInitState::InProgress; - - chatClientServicePtr_ = std::make_shared(); - - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::initDone, this, [this]() { - const bool isProd = applicationSettings_->get(ApplicationSettings::envConfiguration) == - static_cast(ApplicationSettings::EnvConfiguration::Production); - const auto env = isProd ? bs::network::otc::Env::Prod : bs::network::otc::Env::Test; - - ui_->widgetChat->init(connectionManager_, env, chatClientServicePtr_, - logMgr_->logger("chat"), walletsMgr_, authManager_, armory_, signContainer_, - mdCallbacks_, assetManager_, utxoReservationMgr_, applicationSettings_); - - connect(chatClientServicePtr_->getClientPartyModelPtr().get(), &Chat::ClientPartyModel::userPublicKeyChanged, - this, [this](const Chat::UserPublicKeyInfoList& userPublicKeyInfoList) { - addDeferredDialog([this, userPublicKeyList = userPublicKeyInfoList]() { - ui_->widgetChat->onUserPublicKeyChanged(userPublicKeyList); - }); - }, Qt::QueuedConnection); - - chatInitState_ = ChatInitState::Done; - tryLoginIntoChat(); - }); - - auto env = static_cast( - applicationSettings_->get(ApplicationSettings::envConfiguration)); - - Chat::ChatSettings chatSettings; - chatSettings.connectionManager = connectionManager_; - chatSettings.chatPrivKey = chatPrivKey_; - chatSettings.chatPubKey = chatPubKey_; - chatSettings.chatServerHost = PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Chat, env); - chatSettings.chatServerPort = PubKeyLoader::serverHttpPort(); - chatSettings.chatDbFile = applicationSettings_->get(ApplicationSettings::chatDbFile); - - chatClientServicePtr_->Init(logMgr_->logger("chat"), chatSettings); - - connect(ui_->tabWidget, &QTabWidget::currentChanged, this, &BSTerminalMainWindow::onTabWidgetCurrentChanged); - connect(ui_->widgetChat, &ChatWidget::requestPrimaryWalletCreation, this, &BSTerminalMainWindow::onCreatePrimaryWalletRequest); - - if (NotificationCenter::instance() != nullptr) { - connect(NotificationCenter::instance(), &NotificationCenter::newChatMessageClick, ui_->widgetChat, &ChatWidget::onNewChatMessageTrayNotificationClicked); - } -} - -void BSTerminalMainWindow::tryLoginIntoChat() -{ - if (chatInitState_ != ChatInitState::Done || chatTokenData_.empty() || chatTokenSign_.empty()) { - return; - } - - chatClientServicePtr_->LoginToServer(chatTokenData_, chatTokenSign_, cbApproveChat_); - - chatTokenData_.clear(); - chatTokenSign_.clear(); -} - -void BSTerminalMainWindow::resetChatKeys() -{ - gotChatKeys_ = false; - chatPubKey_.clear(); - chatPrivKey_.clear(); - tryGetChatKeys(); -} - -void BSTerminalMainWindow::tryGetChatKeys() -{ - if (gotChatKeys_) { - return; - } - const auto &primaryWallet = walletsMgr_->getPrimaryWallet(); - if (!primaryWallet) { - // Reset API key if it was stored (as it won't be possible to decrypt it) - applicationSettings_->reset(ApplicationSettings::LoginApiKey); - loginApiKeyEncrypted_.clear(); - return; - } - signContainer_->getChatNode(primaryWallet->walletId(), [this](const BIP32_Node &node) { - if (node.getPublicKey().empty() || node.getPrivateKey().empty()) { - SPDLOG_LOGGER_ERROR(logMgr_->logger(), "chat keys is empty"); - return; - } - chatPubKey_ = node.getPublicKey(); - chatPrivKey_ = node.getPrivateKey(); - gotChatKeys_ = true; - tryInitChatView(); - initApiKeyLogins(); - }); -} - -void BSTerminalMainWindow::InitChartsView() -{ - ui_->widgetChart->init(applicationSettings_, mdProvider_, mdCallbacks_ - , connectionManager_, logMgr_->logger("ui")); -} - -// Initialize widgets related to transactions. -void BSTerminalMainWindow::InitTransactionsView() -{ - ui_->widgetExplorer->init(armory_, logMgr_->logger(), walletsMgr_, ccFileManager_, authManager_); - ui_->widgetTransactions->init(walletsMgr_, armory_, utxoReservationMgr_, signContainer_, applicationSettings_ - , logMgr_->logger("ui")); - ui_->widgetTransactions->setEnabled(true); - - ui_->widgetTransactions->SetTransactionsModel(transactionsModel_); - ui_->widgetPortfolio->SetTransactionsModel(transactionsModel_); -} - -void BSTerminalMainWindow::MainWinACT::onStateChanged(ArmoryState state) -{ - switch (state) { - case ArmoryState::Ready: - QMetaObject::invokeMethod(parent_, [this] { - parent_->isArmoryReady_ = true; - parent_->armoryReconnectDelay_ = 0; - parent_->CompleteDBConnection(); - parent_->CompleteUIOnlineView(); - parent_->walletsMgr_->goOnline(); - - parent_->armory_->getNodeStatus([this] (const std::shared_ptr &status){ - QMetaObject::invokeMethod(parent_, [this, status] { - if (status) { - parent_->onNodeStatus(status->status(), status->isSegWitEnabled(), status->rpcStatus()); - } - }); - }); - }); - break; - case ArmoryState::Connected: - QMetaObject::invokeMethod(parent_, [this] { - parent_->armoryRestartCount_ = 0; - parent_->wasWalletsRegistered_ = false; - parent_->armory_->goOnline(); - }); - break; - case ArmoryState::Offline: - QMetaObject::invokeMethod(parent_, &BSTerminalMainWindow::ArmoryIsOffline); - break; - default: - break; - } -} - -void BSTerminalMainWindow::CompleteUIOnlineView() -{ - if (!transactionsModel_) { - transactionsModel_ = std::make_shared(armory_ - , walletsMgr_, logMgr_->logger("ui"), this); - - InitTransactionsView(); - transactionsModel_->loadAllWallets(); - } - updateControlEnabledState(); -} - -void BSTerminalMainWindow::CompleteDBConnection() -{ - if (!wasWalletsRegistered_ && walletsSynched_ && isArmoryReady_) { - // Fix race with BDMAction_Refresh and BDMAction_Ready: register wallets AFTER armory becames ready. - // Otherwise BDMAction_Refresh might come before BDMAction_Ready causing a lot of problems. - walletsMgr_->registerWallets(); - wasWalletsRegistered_ = true; - } -} - -void BSTerminalMainWindow::onReactivate() -{ - show(); -} - -void BSTerminalMainWindow::raiseWindow() -{ - if (isMinimized()) { - showNormal(); - } else if (isHidden()) { - show(); - } - raise(); - activateWindow(); - setFocus(); -#ifdef Q_OS_WIN - auto hwnd = reinterpret_cast(winId()); - auto flags = static_cast(SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - auto currentProcessId = ::GetCurrentProcessId(); - auto currentThreadId = ::GetCurrentThreadId(); - auto windowThreadId = ::GetWindowThreadProcessId(hwnd, nullptr); - if (currentThreadId != windowThreadId) { - ::AttachThreadInput(windowThreadId, currentThreadId, TRUE); - } - ::AllowSetForegroundWindow(currentProcessId); - ::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, flags); - ::SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, flags); - ::SetForegroundWindow(hwnd); - ::SetFocus(hwnd); - ::SetActiveWindow(hwnd); - if (currentThreadId != windowThreadId) { - ::AttachThreadInput(windowThreadId, currentThreadId, FALSE); - } -#endif // Q_OS_WIN -} - -void BSTerminalMainWindow::UpdateMainWindowAppearence() -{ - if (!applicationSettings_->get(ApplicationSettings::closeToTray) && isHidden()) { - setWindowState(windowState() & ~Qt::WindowMinimized); - show(); - raise(); - activateWindow(); - } - - setWindowTitle(tr("BlockSettle Terminal")); - -// const auto bsTitle = tr("BlockSettle Terminal [%1]"); -// switch (applicationSettings_->get(ApplicationSettings::netType)) { -// case NetworkType::TestNet: -// setWindowTitle(bsTitle.arg(tr("TESTNET"))); -// break; - -// case NetworkType::RegTest: -// setWindowTitle(bsTitle.arg(tr("REGTEST"))); -// break; - -// default: -// setWindowTitle(tr("BlockSettle Terminal")); -// break; -// } -} - -bool BSTerminalMainWindow::isUserLoggedIn() const -{ - return (celerConnection_ && celerConnection_->IsConnected()); -} - -bool BSTerminalMainWindow::isArmoryConnected() const -{ - return armory_->state() == ArmoryState::Ready; -} - -void BSTerminalMainWindow::ArmoryIsOffline() -{ - logMgr_->logger("ui")->debug("BSTerminalMainWindow::ArmoryIsOffline"); - - auto now = std::chrono::steady_clock::now(); - std::chrono::milliseconds nextConnDelay(0); - if (now < nextArmoryReconnectAttempt_) { - nextConnDelay = std::chrono::duration_cast( - nextArmoryReconnectAttempt_ - now); - } - if (nextConnDelay != std::chrono::milliseconds::zero()) { - - auto delaySec = std::chrono::duration_cast(nextConnDelay); - SPDLOG_LOGGER_DEBUG(logMgr_->logger("ui") - , "restart armory connection in {} second", nextConnDelay.count()); - - QTimer::singleShot(nextConnDelay, this, &BSTerminalMainWindow::ArmoryIsOffline); - return; - } - - if (walletsMgr_) { - walletsMgr_->unregisterWallets(); - } - updateControlEnabledState(); - - //increase reconnect delay - armoryReconnectDelay_ = armoryReconnectDelay_ % 2 ? - armoryReconnectDelay_ * 2 : armoryReconnectDelay_ + 1; - armoryReconnectDelay_ = std::max(armoryReconnectDelay_, unsigned(60)); - nextArmoryReconnectAttempt_ = - std::chrono::steady_clock::now() + std::chrono::seconds(armoryReconnectDelay_); - - connectArmory(); - - // XXX: disabled until armory connection is stable in terminal - // updateLoginActionState(); -} - -void BSTerminalMainWindow::initArmory() -{ - armory_ = std::make_shared(logMgr_->logger() - , applicationSettings_->get(ApplicationSettings::txCacheFileName), true); - act_ = make_unique(this); - act_->init(armory_.get()); -} - -void BSTerminalMainWindow::initCcClient() -{ - bool isDefaultArmory = armoryServersProvider_->isDefault(armoryServersProvider_->indexOfCurrent()); - if (isDefaultArmory) { - trackerClient_ = std::make_shared(logMgr_->logger()); - } -} - -void BSTerminalMainWindow::initUtxoReservationManager() -{ - utxoReservationMgr_ = std::make_shared( - walletsMgr_, armory_, logMgr_->logger()); -} - -void BSTerminalMainWindow::initBootstrapDataManager() -{ - bootstrapDataManager_ = std::make_shared(logMgr_->logger(), applicationSettings_); - if (bootstrapDataManager_->hasLocalFile()) { - bootstrapDataManager_->loadFromLocalFile(); - } else { - // load from resources - const QString filePathInResources = applicationSettings_->bootstrapResourceFileName(); - - QFile file; - file.setFileName(filePathInResources); - if (file.open(QIODevice::ReadOnly)) { - const std::string bootstrapData = file.readAll().toStdString(); - - bootstrapDataManager_->setReceivedData(bootstrapData); - } else { - logMgr_->logger()->error("[BSTerminalMainWindow::initBootstrapDataManager] failed to locate bootstrap file in resources: {}" - , filePathInResources.toStdString()); - } - - } -} - -void BSTerminalMainWindow::MainWinACT::onTxBroadcastError(const std::string& requestId, const BinaryData &txHash, int errCode - , const std::string &errMsg) -{ - NotificationCenter::notify(bs::ui::NotifyType::BroadcastError, - { QString::fromStdString(txHash.toHexStr(true)), QString::fromStdString(errMsg) }); -} - -void BSTerminalMainWindow::MainWinACT::onNodeStatus(NodeStatus nodeStatus, bool isSegWitEnabled, RpcStatus rpcStatus) -{ - QMetaObject::invokeMethod(parent_, [parent = parent_, nodeStatus, isSegWitEnabled, rpcStatus] { - parent->onNodeStatus(nodeStatus, isSegWitEnabled, rpcStatus); - }); -} - -void BSTerminalMainWindow::MainWinACT::onZCReceived(const std::string& requestId, const std::vector& zcs) -{ - QMetaObject::invokeMethod(parent_, [this, zcs] { parent_->onZCreceived(zcs); }); -} - -void BSTerminalMainWindow::connectArmory() -{ - ArmorySettings currentArmorySettings = armoryServersProvider_->getArmorySettings(); - armoryServersProvider_->setConnectedArmorySettings(currentArmorySettings); - armory_->setupConnection(currentArmorySettings, [this](const BinaryData& srvPubKey, const std::string& srvIPPort) { - auto promiseObj = std::make_shared>(); - std::future futureObj = promiseObj->get_future(); - QMetaObject::invokeMethod(this, [this, srvPubKey, srvIPPort, promiseObj] { - showArmoryServerPrompt(srvPubKey, srvIPPort, promiseObj); - }); - - bool result = futureObj.get(); - // stop armory connection loop if server key was rejected - if (!result) { - armory_->needsBreakConnectionLoop_.store(true); - armory_->setState(ArmoryState::Cancelled); - } - return result; - }); -} - -void BSTerminalMainWindow::connectCcClient() -{ - if (trackerClient_) { - auto env = static_cast( - applicationSettings_->get(ApplicationSettings::envConfiguration)); - auto trackerHostName = PubKeyLoader::serverHostName(PubKeyLoader::KeyType::CcServer, env); - trackerClient_->openConnection(trackerHostName, PubKeyLoader::serverHttpPort(), cbApproveCcServer_); - } -} - -void BSTerminalMainWindow::connectSigner() -{ - if (!signContainer_) { - return; - } - - signContainer_->Start(); -} - -bool BSTerminalMainWindow::createPrimaryWallet() -{ - auto primaryWallet = walletsMgr_->getPrimaryWallet(); - if (primaryWallet) { - return true; - } - - - for (const auto &wallet : walletsMgr_->hdWallets()) { - if (!wallet->isOffline() && !wallet->isHardwareWallet()) { - BSMessageBox qry(BSMessageBox::question, tr("Promote to primary wallet"), tr("Promote to primary wallet?") - , tr("To trade through BlockSettle, you are required to have a wallet which" - " supports the sub-wallets required to interact with the system. Each Terminal" - " may only have one Primary Wallet. Do you wish to promote '%1'?") - .arg(QString::fromStdString(wallet->name())), this); - if (qry.exec() == QDialog::Accepted) { - walletsMgr_->PromoteWalletToPrimary(wallet->walletId()); - return true; - } - } - } - - CreatePrimaryWalletPrompt dlg; - int rc = dlg.exec(); - if (rc == CreatePrimaryWalletPrompt::CreateWallet) { - ui_->widgetWallets->CreateNewWallet(); - } else if (rc == CreatePrimaryWalletPrompt::ImportWallet) { - ui_->widgetWallets->ImportNewWallet(); - } - - return true; -} - -void BSTerminalMainWindow::onCreatePrimaryWalletRequest() -{ - bool result = createPrimaryWallet(); - - if (!result) { - // Need to inform UI about rejection - ui_->widgetRFQ->forceCheckCondition(); - ui_->widgetRFQReply->forceCheckCondition(); - ui_->widgetChat->onUpdateOTCShield(); - } -} - -void BSTerminalMainWindow::showInfo(const QString &title, const QString &text) -{ - BSMessageBox(BSMessageBox::info, title, text).exec(); -} - -void BSTerminalMainWindow::showError(const QString &title, const QString &text) -{ - QMetaObject::invokeMethod(this, [this, title, text] { - BSMessageBox(BSMessageBox::critical, title, text, this).exec(); - }); -} - -void BSTerminalMainWindow::onSignerConnError(SignContainer::ConnectionError error, const QString &details) -{ - updateControlEnabledState(); - - // Prevent showing multiple signer error dialogs (for example network mismatch) - if (error == lastSignerError_) { - return; - } - lastSignerError_ = error; - - if (error != SignContainer::ConnectionTimeout || signContainer_->isLocal()) { - showError(tr("Signer Connection Error"), details); - } -} - -void BSTerminalMainWindow::onGenerateAddress() -{ - if (walletsMgr_->hdWallets().empty()) { - createPrimaryWallet(); - return; - } - - const auto defWallet = walletsMgr_->getDefaultWallet(); - std::string selWalletId = defWallet ? defWallet->walletId() : std::string{}; - - if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { - auto wallets = ui_->widgetWallets->getSelectedWallets(); - if (!wallets.empty()) { - selWalletId = wallets[0].ids[0]; - } else { - wallets = ui_->widgetWallets->getFirstWallets(); - - if (!wallets.empty()) { - selWalletId = wallets[0].ids[0]; - } - } - } - SelectWalletDialog selectWalletDialog(walletsMgr_, selWalletId, this); - selectWalletDialog.exec(); - - if (selectWalletDialog.result() == QDialog::Rejected) { - return; - } - - const auto &wallet = walletsMgr_->getWalletById(selectWalletDialog.getSelectedWallet()); - NewAddressDialog newAddressDialog(wallet, this); - newAddressDialog.exec(); -} - -void BSTerminalMainWindow::onSend() -{ - std::string selectedWalletId; - - if (ui_->tabWidget->currentWidget() == ui_->widgetWallets) { - auto wallet = ui_->widgetWallets->getSelectedHdWallet(); - if (wallet.ids.empty()) { - const auto &priWallet = walletsMgr_->getPrimaryWallet(); - if (priWallet) { - wallet.ids.push_back(priWallet->walletId()); - } - } - if (!wallet.ids.empty()) { - selectedWalletId = wallet.ids[0]; - } - } else { - selectedWalletId = applicationSettings_->getDefaultWalletId(); - } - - std::shared_ptr dlg; - - if ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) - || applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { - dlg = std::make_shared(armory_, walletsMgr_, utxoReservationMgr_ - , signContainer_, true, logMgr_->logger("ui"), applicationSettings_, nullptr, bs::UtxoReservationToken{}, this ); - } else { - dlg = std::make_shared(armory_, walletsMgr_, utxoReservationMgr_, signContainer_ - , logMgr_->logger("ui"), applicationSettings_, this); - } - - if (!selectedWalletId.empty()) { - dlg->SelectWallet(selectedWalletId, UiUtils::WalletsTypes::None); - } - - DisplayCreateTransactionDialog(dlg); -} - -void BSTerminalMainWindow::setupMenu() -{ - // menu role erquired for OSX only, to place it to first menu item - action_login_->setMenuRole(QAction::ApplicationSpecificRole); - action_logout_->setMenuRole(QAction::ApplicationSpecificRole); - - - ui_->menuFile->insertAction(ui_->actionSettings, action_login_); - ui_->menuFile->insertAction(ui_->actionSettings, action_logout_); - - ui_->menuFile->insertSeparator(action_login_); - ui_->menuFile->insertSeparator(ui_->actionSettings); - - AboutDialog *aboutDlg = new AboutDialog(applicationSettings_->get(ApplicationSettings::ChangeLog_Base_Url), this); - auto aboutDlgCb = [aboutDlg] (int tab) { - return [aboutDlg, tab]() { - aboutDlg->setTab(tab); - aboutDlg->show(); - }; - }; - - SupportDialog *supportDlg = new SupportDialog(this); - auto supportDlgCb = [supportDlg] (int tab, QString title) { - return [supportDlg, tab, title]() { - supportDlg->setTab(tab); - supportDlg->setWindowTitle(title); - supportDlg->show(); - }; - }; - - connect(ui_->actionCreateNewWallet, &QAction::triggered, this, [ww = ui_->widgetWallets]{ ww->onNewWallet(); }); - connect(ui_->actionOpenURI, &QAction::triggered, this, [this]{ openURIDialog(); }); - connect(ui_->actionAuthenticationAddresses, &QAction::triggered, this, &BSTerminalMainWindow::openAuthManagerDialog); - connect(ui_->actionSettings, &QAction::triggered, this, [=]() { openConfigDialog(); }); - connect(ui_->actionAccountInformation, &QAction::triggered, this, &BSTerminalMainWindow::openAccountInfoDialog); - connect(ui_->actionEnterColorCoinToken, &QAction::triggered, this, &BSTerminalMainWindow::openCCTokenDialog); - connect(ui_->actionAbout, &QAction::triggered, aboutDlgCb(0)); - connect(ui_->actionVersion, &QAction::triggered, aboutDlgCb(3)); - connect(ui_->actionGuides, &QAction::triggered, supportDlgCb(0, QObject::tr("Guides"))); - connect(ui_->actionVideoTutorials, &QAction::triggered, supportDlgCb(1, QObject::tr("Video Tutorials"))); - connect(ui_->actionContact, &QAction::triggered, supportDlgCb(2, QObject::tr("Support"))); - - onUserLoggedOut(); - -#ifndef Q_OS_MAC - ui_->horizontalFrame->hide(); - ui_->menubar->setCornerWidget(ui_->loginGroupWidget); -#endif - -#ifndef PRODUCTION_BUILD - auto envType = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration).toInt()); - bool isProd = envType == ApplicationSettings::EnvConfiguration::Production; - ui_->prodEnvSettings->setEnabled(!isProd); - ui_->testEnvSettings->setEnabled(isProd); - connect(ui_->prodEnvSettings, &QPushButton::clicked, this, [this] { - promptSwitchEnv(true); - }); - connect(ui_->testEnvSettings, &QPushButton::clicked, this, [this] { - promptSwitchEnv(false); - }); -#else - ui_->prodEnvSettings->setVisible(false); - ui_->testEnvSettings->setVisible(false); -#endif // !PRODUCTION_BUILD -} - -void BSTerminalMainWindow::openAuthManagerDialog() -{ - authAddrDlg_->exec(); -} - -void BSTerminalMainWindow::openConfigDialog(bool showInNetworkPage) -{ - auto oldEnv = static_cast( - applicationSettings_->get(ApplicationSettings::envConfiguration)); - - ConfigDialog configDialog(applicationSettings_, armoryServersProvider_, signersProvider_, signContainer_, walletsMgr_, this); - connect(&configDialog, &ConfigDialog::reconnectArmory, this, &BSTerminalMainWindow::onArmoryNeedsReconnect); - - if (showInNetworkPage) { - configDialog.popupNetworkSettings(); - } - - int rc = configDialog.exec(); - - UpdateMainWindowAppearence(); - - auto newEnv = static_cast( - applicationSettings_->get(ApplicationSettings::envConfiguration)); - if (rc == QDialog::Accepted && newEnv != oldEnv) { - bool prod = newEnv == ApplicationSettings::EnvConfiguration::Production; - BSMessageBox mbox(BSMessageBox::question - , tr("Environment selection") - , tr("Switch Environment") - , tr("Do you wish to change to the %1 environment now?").arg(prod ? tr("Production") : tr("Test")) - , this); - mbox.setConfirmButtonText(tr("Yes")); - int rc = mbox.exec(); - if (rc == QDialog::Accepted) { - restartTerminal(); - } - } -} - -void BSTerminalMainWindow::openAccountInfoDialog() -{ - CelerAccountInfoDialog dialog(celerConnection_, this); - dialog.exec(); -} - -void BSTerminalMainWindow::openCCTokenDialog() -{ - const auto lbdCCTokenDlg = [this] { - QMetaObject::invokeMethod(this, [this] { - CCTokenEntryDialog(walletsMgr_, ccFileManager_, applicationSettings_, this).exec(); - }); - }; - // Do not use deferredDialogs_ here as it will deadblock PuB public key processing - if (walletsMgr_->hasPrimaryWallet()) { - lbdCCTokenDlg(); - } -} - -void BSTerminalMainWindow::onLogin() -{ - if (!action_login_->isEnabled()) { - return; - } - auto envType = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration).toInt()); - - if (walletsSynched_ && !walletsMgr_->getPrimaryWallet()) { - addDeferredDialog([this] { - createPrimaryWallet(); - }); - return; - } - - auto bsClient = createClient(); - - auto logger = logMgr_->logger("proxy"); - LoginWindow loginDialog(logger, bsClient, applicationSettings_, this); - - int rc = loginDialog.exec(); - if (rc != QDialog::Accepted && !loginDialog.result()) { - return; - } - - bool isRegistered = (loginDialog.result()->userType == bs::network::UserType::Market - || loginDialog.result()->userType == bs::network::UserType::Trading - || loginDialog.result()->userType == bs::network::UserType::Dealing); - - if (!isRegistered && envType == ApplicationSettings::EnvConfiguration::Test) { - auto createTestAccountUrl = applicationSettings_->get(ApplicationSettings::GetAccount_UrlTest); - BSMessageBox dlg(BSMessageBox::info, tr("Create Test Account") - , tr("Create a BlockSettle test account") - , tr("

Login requires a test account - create one in minutes on test.blocksettle.com

" - "

Once you have registered, return to login in the Terminal.

" - "Create Test Account Now") - .arg(createTestAccountUrl).arg(BSMessageBox::kUrlColor), this); - dlg.setOkVisible(false); - dlg.setCancelVisible(true); - dlg.enableRichText(); - dlg.exec(); - return; - } - - if (!isRegistered && envType == ApplicationSettings::EnvConfiguration::Production) { - auto createAccountUrl = applicationSettings_->get(ApplicationSettings::GetAccount_UrlProd); - BSMessageBox dlg(BSMessageBox::info, tr("Create Account") - , tr("Create a BlockSettle account") - , tr("

Login requires an account - create one in minutes on blocksettle.com

" - "

Once you have registered, return to login in the Terminal.

" - "Create Account Now") - .arg(createAccountUrl).arg(BSMessageBox::kUrlColor), this); - dlg.setOkVisible(false); - dlg.setCancelVisible(true); - dlg.enableRichText(); - dlg.exec(); - return; - } - - activateClient(bsClient, *loginDialog.result(), loginDialog.email().toStdString()); -} - -void BSTerminalMainWindow::onLogout() -{ - ui_->widgetWallets->setUsername(QString()); - if (chatClientServicePtr_) { - chatClientServicePtr_->LogoutFromServer(); - } - ui_->widgetChart->disconnect(); - - if (celerConnection_->IsConnected()) { - celerConnection_->CloseConnection(); - } - - mdProvider_->UnsubscribeFromMD(); - - setLoginButtonText(loginButtonText_); - - setWidgetsAuthorized(false); - - bsClient_.reset(); -} - -void BSTerminalMainWindow::onUserLoggedIn() -{ - ui_->actionAccountInformation->setEnabled(true); - ui_->actionAuthenticationAddresses->setEnabled(celerConnection_->celerUserType() - != BaseCelerClient::CelerUserType::Market); - ui_->actionOneTimePassword->setEnabled(true); - ui_->actionEnterColorCoinToken->setEnabled(true); - - ui_->actionDeposits->setEnabled(true); - ui_->actionWithdrawalRequest->setEnabled(true); - ui_->actionLinkAdditionalBankAccount->setEnabled(true); - - ccFileManager_->ConnectToCelerClient(celerConnection_); - ui_->widgetRFQ->onUserConnected(userType_); - ui_->widgetRFQReply->onUserConnected(userType_); - - const auto userId = BinaryData::CreateFromHex(celerConnection_->userId()); - const auto &deferredDialog = [this, userId] { - walletsMgr_->setUserId(userId); - enableTradingIfNeeded(); - }; - addDeferredDialog(deferredDialog); - - setLoginButtonText(currentUserLogin_); -} - -void BSTerminalMainWindow::onUserLoggedOut() -{ - ui_->actionAccountInformation->setEnabled(false); - ui_->actionAuthenticationAddresses->setEnabled(false); - ui_->actionEnterColorCoinToken->setEnabled(false); - ui_->actionOneTimePassword->setEnabled(false); - - ui_->actionDeposits->setEnabled(false); - ui_->actionWithdrawalRequest->setEnabled(false); - ui_->actionLinkAdditionalBankAccount->setEnabled(false); - - if (walletsMgr_) { - walletsMgr_->setUserId(BinaryData{}); - } - if (authManager_) { - authManager_->OnDisconnectedFromCeler(); - } - - setLoginButtonText(loginButtonText_); -} - -void BSTerminalMainWindow::onAccountTypeChanged(bs::network::UserType userType, bool enabled) -{ - userType_ = userType; - - if (enabled != accountEnabled_ && userType != bs::network::UserType::Chat) { - accountEnabled_ = enabled; - NotificationCenter::notify(enabled ? bs::ui::NotifyType::AccountEnabled : bs::ui::NotifyType::AccountDisabled, {}); - } - - authManager_->setUserType(userType); - - ui_->widgetChat->setUserType(enabled ? userType : bs::network::UserType::Chat); -} - -void BSTerminalMainWindow::onCelerConnected() -{ - onUserLoggedIn(); - updateControlEnabledState(); -} - -void BSTerminalMainWindow::onCelerDisconnected() -{ - onUserLoggedOut(); - celerConnection_->CloseConnection(); - updateControlEnabledState(); -} - -void BSTerminalMainWindow::onCelerConnectionError(int errorCode) -{ - switch(errorCode) - { - case BaseCelerClient::LoginError: - logMgr_->logger("ui")->debug("[BSTerminalMainWindow::onCelerConnectionError] login failed. Probably user do not have BS matching account"); - break; - } -} - -struct BSTerminalMainWindow::TxInfo { - Tx tx; - uint32_t txTime{}; - int64_t value{}; - std::shared_ptr wallet; - bs::sync::Transaction::Direction direction{}; - QString mainAddress; -}; - -void BSTerminalMainWindow::onZCreceived(const std::vector &entries) -{ - if (entries.empty()) { - return; - } - for (const auto &entry : walletsMgr_->mergeEntries(entries)) { - const auto &cbTx = [this, entry] (const Tx &tx) - { - std::shared_ptr wallet; - for (const auto &walletId : entry.walletIds) { - wallet = walletsMgr_->getWalletById(walletId); - if (wallet) { - break; - } - } - if (!wallet) { - return; - } - - auto txInfo = std::make_shared(); - txInfo->tx = tx; - txInfo->txTime = entry.txTime; - txInfo->value = entry.value; - txInfo->wallet = wallet; - - const auto &cbDir = [this, txInfo] (bs::sync::Transaction::Direction dir, const std::vector &) { - txInfo->direction = dir; - if (!txInfo->mainAddress.isEmpty()) { - showZcNotification(*txInfo); - } - }; - - const auto &cbMainAddr = [this, txInfo] (const QString &mainAddr, int addrCount) { - txInfo->mainAddress = mainAddr; - if ((txInfo->direction != bs::sync::Transaction::Direction::Unknown)) { - showZcNotification(*txInfo); - } - }; - - walletsMgr_->getTransactionDirection(tx, wallet->walletId(), cbDir); - walletsMgr_->getTransactionMainAddress(tx, wallet->walletId(), (entry.value > 0), cbMainAddr); - }; - armory_->getTxByHash(entry.txHash, cbTx, true); - } -} - -void BSTerminalMainWindow::showZcNotification(const TxInfo &txInfo) -{ - QStringList lines; - lines << tr("Date: %1").arg(UiUtils::displayDateTime(txInfo.txTime)); - lines << tr("TX: %1 %2 %3").arg(tr(bs::sync::Transaction::toString(txInfo.direction))) - .arg(txInfo.wallet->displayTxValue(txInfo.value)).arg(txInfo.wallet->displaySymbol()); - lines << tr("Wallet: %1").arg(QString::fromStdString(txInfo.wallet->name())); - lines << (txInfo.tx.isRBF() ? tr("RBF Enabled") : tr("RBF Disabled")); - lines << txInfo.mainAddress; - - const auto &title = tr("New blockchain transaction"); - NotificationCenter::notify(bs::ui::NotifyType::BlockchainTX, { title, lines.join(tr("\n")) }); -} - -void BSTerminalMainWindow::onNodeStatus(NodeStatus nodeStatus, bool isSegWitEnabled, RpcStatus rpcStatus) -{ - // Do not use rpcStatus for node status check, it works unreliable for some reasons - bool isBitcoinCoreOnline = (nodeStatus == NodeStatus_Online); - if (isBitcoinCoreOnline != isBitcoinCoreOnline_) { - isBitcoinCoreOnline_ = isBitcoinCoreOnline; - if (isBitcoinCoreOnline) { - SPDLOG_LOGGER_INFO(logMgr_->logger(), "BlockSettleDB connected to Bitcoin Core RPC"); - NotificationCenter::notify(bs::ui::NotifyType::BitcoinCoreOnline, {}); - } else { - SPDLOG_LOGGER_ERROR(logMgr_->logger(), "BlockSettleDB disconnected from Bitcoin Core RPC"); - NotificationCenter::notify(bs::ui::NotifyType::BitcoinCoreOffline, {}); - } - } -} - -void BSTerminalMainWindow::showRunInBackgroundMessage() -{ - sysTrayIcon_->showMessage(tr("BlockSettle is running"), tr("BlockSettle Terminal is running in the backgroud. Click the tray icon to open the main window."), QIcon(QLatin1String(":/resources/login-logo.png"))); -} - -void BSTerminalMainWindow::closeEvent(QCloseEvent* event) -{ - if (applicationSettings_->get(ApplicationSettings::closeToTray)) { - hide(); - event->ignore(); - } - else { - if (chatClientServicePtr_) { - chatClientServicePtr_->LogoutFromServer(); - } - - QMainWindow::closeEvent(event); - QApplication::exit(); - } -} - -void BSTerminalMainWindow::changeEvent(QEvent* e) -{ - switch (e->type()) - { - case QEvent::WindowStateChange: - { - if (this->windowState() & Qt::WindowMinimized) - { - if (applicationSettings_->get(ApplicationSettings::minimizeToTray)) - { - QTimer::singleShot(0, this, &QMainWindow::hide); - } - } - - break; - } - default: - break; - } - - QMainWindow::changeEvent(e); -} - -void BSTerminalMainWindow::setLoginButtonText(const QString& text) -{ - auto *button = ui_->pushButtonUser; - button->setText(text); - button->setProperty("usernameButton", QVariant(text == loginButtonText_)); - button->setProperty("usernameButtonLoggedIn", QVariant(text != loginButtonText_)); - button->style()->unpolish(button); - button->style()->polish(button); - button->update(); - -#ifndef Q_OS_MAC - ui_->menubar->adjustSize(); -#endif -} - -void BSTerminalMainWindow::onCCLoaded() -{ - walletsMgr_->onCCInfoLoaded(); - - const auto ccResolver = walletsMgr_->ccResolver(); - if (ccResolver && signContainer_) { - deferCCsync_ = false; - signContainer_->syncCCNames(ccResolver->securities()); - } - else { - deferCCsync_ = true; - } -} - -void BSTerminalMainWindow::setupShortcuts() -{ - auto overviewTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+1")), this); - overviewTabShortcut->setContext(Qt::WindowShortcut); - connect(overviewTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(0);}); - - auto tradingTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+2")), this); - tradingTabShortcut->setContext(Qt::WindowShortcut); - connect(tradingTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(1);}); - - auto dealingTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+3")), this); - dealingTabShortcut->setContext(Qt::WindowShortcut); - connect(dealingTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(2);}); - - auto walletsTabShortcutt = new QShortcut(QKeySequence(QStringLiteral("Ctrl+4")), this); - walletsTabShortcutt->setContext(Qt::WindowShortcut); - connect(walletsTabShortcutt, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(3);}); - - auto transactionsTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+5")), this); - transactionsTabShortcut->setContext(Qt::WindowShortcut); - connect(transactionsTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(4);}); - - auto explorerTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+6")), this); - explorerTabShortcut->setContext(Qt::WindowShortcut); - connect(explorerTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(5);}); - - auto chartsTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+7")), this); - chartsTabShortcut->setContext(Qt::WindowShortcut); - connect(chartsTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(6);}); - - auto chatTabShortcut = new QShortcut(QKeySequence(QStringLiteral("Ctrl+8")), this); - chatTabShortcut->setContext(Qt::WindowShortcut); - connect(chatTabShortcut, &QShortcut::activated, [this](){ ui_->tabWidget->setCurrentIndex(7);}); - - // TODO: Switch ChatWidget to TabWithShortcut if needed (it will ignore shortcuts right now) - - auto addShotcut = [this](const char *keySequence, TabWithShortcut::ShortcutType type) { - auto shortcut = new QShortcut(QKeySequence(QLatin1String(keySequence)), this); - shortcut->setContext(Qt::WindowShortcut); - connect(shortcut, &QShortcut::activated, [this, type]() { - auto widget = dynamic_cast(ui_->tabWidget->currentWidget()); - if (widget) { - widget->shortcutActivated(type); - } - }); - }; - - addShotcut("Alt+1", TabWithShortcut::ShortcutType::Alt_1); - addShotcut("Alt+2", TabWithShortcut::ShortcutType::Alt_2); - addShotcut("Alt+3", TabWithShortcut::ShortcutType::Alt_3); - addShotcut("Ctrl+S", TabWithShortcut::ShortcutType::Ctrl_S); - addShotcut("Ctrl+P", TabWithShortcut::ShortcutType::Ctrl_P); - addShotcut("Ctrl+Q", TabWithShortcut::ShortcutType::Ctrl_Q); - addShotcut("Alt+S", TabWithShortcut::ShortcutType::Alt_S); - addShotcut("Alt+B", TabWithShortcut::ShortcutType::Alt_B); - addShotcut("Alt+P", TabWithShortcut::ShortcutType::Alt_P); -} - -void BSTerminalMainWindow::onButtonUserClicked() { - if (ui_->pushButtonUser->text() == loginButtonText_) { - onLogin(); - } else { - if (BSMessageBox(BSMessageBox::question, tr("User Logout"), tr("You are about to logout") - , tr("Do you want to continue?")).exec() == QDialog::Accepted) - onLogout(); - } -} - -void BSTerminalMainWindow::showArmoryServerPrompt(const BinaryData &srvPubKey, const std::string &srvIPPort, std::shared_ptr> promiseObj) -{ - QList servers = armoryServersProvider_->servers(); - int serverIndex = armoryServersProvider_->indexOfIpPort(srvIPPort); - if (serverIndex >= 0) { - ArmoryServer server = servers.at(serverIndex); - - const auto &deferredDialog = [this, server, promiseObj, srvPubKey, srvIPPort]{ - if (server.armoryDBKey.isEmpty()) { - ImportKeyBox box(BSMessageBox::question - , tr("Import BlockSettleDB ID Key?") - , this); - - box.setNewKeyFromBinary(srvPubKey); - box.setAddrPort(srvIPPort); - - bool answer = (box.exec() == QDialog::Accepted); - - if (answer) { - armoryServersProvider_->addKey(srvIPPort, srvPubKey); - } - - promiseObj->set_value(true); - } - else if (server.armoryDBKey.toStdString() != srvPubKey.toHexStr()) { - ImportKeyBox box(BSMessageBox::question - , tr("Import BlockSettleDB ID Key?") - , this); - - box.setNewKeyFromBinary(srvPubKey); - box.setOldKey(server.armoryDBKey); - box.setAddrPort(srvIPPort); - box.setCancelVisible(true); - - bool answer = (box.exec() == QDialog::Accepted); - - if (answer) { - armoryServersProvider_->addKey(srvIPPort, srvPubKey); - } - - promiseObj->set_value(answer); - } - else { - promiseObj->set_value(true); - } - }; - - addDeferredDialog(deferredDialog); - } - else { - // server not in the list - added directly to ini config - promiseObj->set_value(true); - } -} - -void BSTerminalMainWindow::onArmoryNeedsReconnect() -{ - disconnect(statusBarView_.get(), nullptr, nullptr, nullptr); - statusBarView_->deleteLater(); - QApplication::processEvents(); - - initArmory(); - LoadWallets(); - - QApplication::processEvents(); - - statusBarView_ = std::make_shared(armory_, walletsMgr_, assetManager_, celerConnection_ - , signContainer_, ui_->statusbar); - - InitWalletsView(); - - - InitSigningContainer(); - InitAuthManager(); - - connectSigner(); - connectArmory(); -} - -void BSTerminalMainWindow::onTabWidgetCurrentChanged(const int &index) -{ - const int chatIndex = ui_->tabWidget->indexOf(ui_->widgetChat); - const bool isChatTab = index == chatIndex; - //ui_->widgetChat->updateChat(isChatTab); -} - -void BSTerminalMainWindow::onSyncWallets() -{ - if (walletsMgr_->isSynchronising()) { - return; - } - - wasWalletsRegistered_ = false; - walletsSynched_ = false; - const auto &progressDelegate = [this](int cur, int total) { - logMgr_->logger()->debug("Loaded wallet {} of {}", cur, total); - }; - - walletsMgr_->reset(); - walletsMgr_->syncWallets(progressDelegate); - updateControlEnabledState(); -} - -void BSTerminalMainWindow::onSignerVisibleChanged() -{ - processDeferredDialogs(); -} - -void BSTerminalMainWindow::InitWidgets() -{ - authAddrDlg_ = std::make_shared(logMgr_->logger(), authManager_ - , assetManager_, applicationSettings_, this); - - InitWalletsView(); - InitPortfolioView(); - - ui_->widgetRFQ->initWidgets(mdProvider_, mdCallbacks_, applicationSettings_); - - auto quoteProvider = std::make_shared(assetManager_ - , logMgr_->logger("message")); - quoteProvider->ConnectToCelerClient(celerConnection_); - - const auto &logger = logMgr_->logger(); - const auto aqScriptRunner = new AQScriptRunner(quoteProvider, signContainer_ - , mdCallbacks_, assetManager_, logger); - if (!applicationSettings_->get(ApplicationSettings::ExtConnName).empty() - && !applicationSettings_->get(ApplicationSettings::ExtConnHost).empty() - && !applicationSettings_->get(ApplicationSettings::ExtConnPort).empty() - && !applicationSettings_->get(ApplicationSettings::ExtConnPubKey).empty()) { - ExtConnections extConns; - logger->debug("Setting up ext connection"); - - const auto &clientKeyPath = SystemFilePaths::appDataLocation() + "/extConnKey"; - bs::network::ws::PrivateKey privKeyClient; - std::ifstream privKeyReader(clientKeyPath, std::ios::binary); - if (privKeyReader.is_open()) { - std::string str; - str.assign(std::istreambuf_iterator(privKeyReader) - , std::istreambuf_iterator()); - privKeyClient.reserve(str.size()); - std::for_each(str.cbegin(), str.cend(), [&privKeyClient](char c) { - privKeyClient.push_back(c); - }); - } - if (privKeyClient.empty()) { - logger->debug("Creating new ext connection key"); - privKeyClient = bs::network::ws::generatePrivKey(); - std::ofstream privKeyWriter(clientKeyPath, std::ios::out|std::ios::binary); - privKeyWriter.write((char *)&privKeyClient[0], privKeyClient.size()); - const auto &pubKeyClient = bs::network::ws::publicKey(privKeyClient); - applicationSettings_->set(ApplicationSettings::ExtConnOwnPubKey - , QString::fromStdString(bs::toHex(pubKeyClient))); - } - const auto &certClient = bs::network::ws::generateSelfSignedCert(privKeyClient); - const auto &srvPubKey = applicationSettings_->get(ApplicationSettings::ExtConnPubKey); - SslDataConnectionParams clientParams; - clientParams.useSsl = true; - clientParams.cert = certClient; - clientParams.privKey = privKeyClient; - clientParams.allowSelfSigned = true; - clientParams.skipHostNameChecks = true; - clientParams.verifyCallback = [srvPubKey, this](const std::string &pubKey) -> bool { - if (BinaryData::CreateFromHex(srvPubKey).toBinStr() == pubKey) { - return true; - } - QMetaObject::invokeMethod(this, [pubKey] { - BSMessageBox(BSMessageBox::warning, tr("External Connection error") - , tr("Invalid server key: %1").arg(QString::fromStdString(bs::toHex(pubKey)))).exec(); - }); - return false; - }; - - RetryingDataConnectionParams retryingParams; - retryingParams.connection = std::make_unique(logger, clientParams); - auto connection = std::make_shared(logger, std::move(retryingParams)); - - if (connection->openConnection(applicationSettings_->get(ApplicationSettings::ExtConnHost) - , applicationSettings_->get(ApplicationSettings::ExtConnPort) - , aqScriptRunner->getExtConnListener().get())) { - extConns[applicationSettings_->get(ApplicationSettings::ExtConnName)] = connection; - } - aqScriptRunner->setExtConnections(extConns); - } - - autoSignQuoteProvider_ = std::make_shared(logger - , aqScriptRunner, applicationSettings_, signContainer_, celerConnection_); - - const auto rfqScriptRunner = new RFQScriptRunner(mdCallbacks_, logger, nullptr); - autoSignRFQProvider_ = std::make_shared(logger - , rfqScriptRunner, applicationSettings_, signContainer_, celerConnection_); - - auto dialogManager = std::make_shared(this); - - ui_->widgetRFQ->init(logger, celerConnection_, authManager_, quoteProvider - , assetManager_, dialogManager, signContainer_, armory_, autoSignRFQProvider_ - , utxoReservationMgr_, orderListModel_.get()); - ui_->widgetRFQReply->init(logger, celerConnection_, authManager_ - , quoteProvider, mdCallbacks_, assetManager_, applicationSettings_ - , signContainer_, armory_, connectionManager_, autoSignQuoteProvider_ - , utxoReservationMgr_, orderListModel_.get()); - - connect(ui_->widgetRFQ, &RFQRequestWidget::requestPrimaryWalletCreation, this - , &BSTerminalMainWindow::onCreatePrimaryWalletRequest); - connect(ui_->widgetRFQReply, &RFQReplyWidget::requestPrimaryWalletCreation, this - , &BSTerminalMainWindow::onCreatePrimaryWalletRequest); - connect(ui_->widgetRFQ, &RFQRequestWidget::loginRequested, this - , &BSTerminalMainWindow::onLogin); - - connect(ui_->tabWidget, &QTabWidget::tabBarClicked, this, - [requestRFQ = QPointer(ui_->widgetRFQ) - , replyRFQ = QPointer(ui_->widgetRFQReply) - , tabWidget = QPointer(ui_->tabWidget)] (int index) - { - if (!tabWidget) { - return; - } - if (requestRFQ && requestRFQ == tabWidget->widget(index)) { - requestRFQ->forceCheckCondition(); - } - if (replyRFQ && replyRFQ == tabWidget->widget(index)) { - replyRFQ->forceCheckCondition(); - } - }); -} - -void BSTerminalMainWindow::enableTradingIfNeeded() -{ - // Can't proceed without userId - if (!walletsMgr_->isUserIdSet()) { - return; - } - - auto enableTradingFunc = [this](const std::shared_ptr &wallet) { - addDeferredDialog([this, wallet] { - BSMessageBox qry(BSMessageBox::question, tr("Upgrade Wallet"), tr("Enable Trading") - , tr("BlockSettle requires you to hold sub-wallets with Authentication Addresses to interact with our trading system.

" - "You will be able to trade up to %1 bitcoin per trade once your Authentication Address has been submitted.

" - "After %2 trades your Authentication Address will be verified and your trading limit removed.

" - "Do you wish to enable XBT trading?").arg(bs::XBTAmount(tradeSettings_->xbtTier1Limit).GetValueBitcoin()).arg(tradeSettings_->authRequiredSettledTrades) - , this); - qry.enableRichText(); - if (qry.exec() == QDialog::Accepted) { - walletsMgr_->EnableXBTTradingInWallet(wallet->walletId(), [this](bs::error::ErrorCode result) { - if (result == bs::error::ErrorCode::NoError) { - // If wallet was promoted to primary we could try to get chat keys now - tryGetChatKeys(); - walletsMgr_->setUserId(BinaryData::CreateFromHex(celerConnection_->userId())); - } - }); - } - }); - }; - - auto primaryWallet = walletsMgr_->getPrimaryWallet(); - if (primaryWallet) { - if (!primaryWallet->tradingEnabled()) { - enableTradingFunc(primaryWallet); - } - } -} - -void BSTerminalMainWindow::showLegacyWarningIfNeeded() -{ - if (applicationSettings_->get(ApplicationSettings::HideLegacyWalletWarning)) { - return; - } - applicationSettings_->set(ApplicationSettings::HideLegacyWalletWarning, true); - addDeferredDialog([this] { - int forcedWidth = 640; - BSMessageBox mbox(BSMessageBox::info - , tr("Legacy Wallets") - , tr("Legacy Address Balances") - , tr("The BlockSettle Terminal has detected the use of legacy addresses in your wallet.\n\n" - "The BlockSettle Terminal supports viewing and spending from legacy addresses, but will not support the following actions related to these addresses:\n\n" - "- GUI support for legacy address generation\n" - "- Trading and settlement using legacy inputs\n\n" - "BlockSettle strongly recommends that you move your legacy address balances to native SegWit addresses.") - , {} - , forcedWidth - , this); - mbox.exec(); - }); -} - -void BSTerminalMainWindow::promptSwitchEnv(bool prod) -{ - BSMessageBox mbox(BSMessageBox::question - , tr("Environment selection") - , tr("Switch Environment") - , tr("Do you wish to change to the %1 environment now?").arg(prod ? tr("Production") : tr("Test")) - , this); - mbox.setConfirmButtonText(tr("Yes")); - int rc = mbox.exec(); - if (rc == QDialog::Accepted) { - if (prod) { - switchToProdEnv(); - } else { - switchToTestEnv(); - } - restartTerminal(); - } -} - -void BSTerminalMainWindow::switchToTestEnv() -{ - applicationSettings_->set(ApplicationSettings::envConfiguration - , static_cast(ApplicationSettings::EnvConfiguration::Test)); - armoryServersProvider_->setupServer(armoryServersProvider_->getIndexOfTestNetServer()); -} - -void BSTerminalMainWindow::switchToProdEnv() -{ - applicationSettings_->set(ApplicationSettings::envConfiguration - , static_cast(ApplicationSettings::EnvConfiguration::Production)); - armoryServersProvider_->setupServer(armoryServersProvider_->getIndexOfMainNetServer()); -} - -void BSTerminalMainWindow::restartTerminal() -{ - lockFile_.unlock(); - QProcess::startDetached(qApp->arguments()[0], qApp->arguments()); - qApp->quit(); -} - -void BSTerminalMainWindow::processDeferredDialogs() -{ - if(deferredDialogRunning_) { - return; - } - if (signContainer_ && signContainer_->isLocal() && signContainer_->isWindowVisible()) { - return; - } - - deferredDialogRunning_ = true; - while (!deferredDialogs_.empty()) { - deferredDialogs_.front()(); // run stored lambda - deferredDialogs_.pop(); - } - deferredDialogRunning_ = false; -} - -std::shared_ptr BSTerminalMainWindow::createClient() -{ - auto logger = logMgr_->logger("proxy"); - auto bsClient = std::make_shared(logger); - - bs::network::BIP15xParams params; - params.ephemeralPeers = true; - params.authMode = bs::network::BIP15xAuthMode::OneWay; - const auto &bip15xTransport = std::make_shared(logger, params); - bip15xTransport->setKeyCb(cbApproveProxy_); - - auto wsConnection = std::make_unique(logger, WsDataConnectionParams{}); - auto connection = std::make_unique(logger, std::move(wsConnection), bip15xTransport); - auto env = static_cast( - applicationSettings_->get(ApplicationSettings::envConfiguration)); - bool result = connection->openConnection(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Proxy, env) - , PubKeyLoader::serverHttpPort(), bsClient.get()); - assert(result); - bsClient->setConnection(std::move(connection)); - - // Must be connected before loginDialog.exec call (balances could be received before loginDialog.exec returns)! - connect(bsClient.get(), &BsClientQt::balanceLoaded, assetManager_.get(), &AssetManager::fxBalanceLoaded); - connect(bsClient.get(), &BsClientQt::balanceUpdated, assetManager_.get(), &AssetManager::onAccountBalanceLoaded); - - return bsClient; -} - -void BSTerminalMainWindow::activateClient(const std::shared_ptr &bsClient - , const BsClientLoginResult &result, const std::string &email) -{ - currentUserLogin_ = QString::fromStdString(email); - - chatTokenData_ = result.chatTokenData; - chatTokenSign_ = result.chatTokenSign; - tryLoginIntoChat(); - - bsClient_ = bsClient; - ccFileManager_->setBsClient(bsClient); - authAddrDlg_->setBsClient(bsClient); - - tradeSettings_ = std::make_shared(result.tradeSettings); - applicationSettings_->set(ApplicationSettings::SubmittedAddressXbtLimit, static_cast(tradeSettings_->xbtTier1Limit)); - - authManager_->initLogin(celerConnection_, tradeSettings_); - - onBootstrapDataLoaded(result.bootstrapDataSigned); - - connect(bsClient_.get(), &BsClientQt::disconnected, orderListModel_.get(), &OrderListModel::onDisconnected); - connect(bsClient_.get(), &BsClientQt::disconnected, this, &BSTerminalMainWindow::onBsConnectionDisconnected); - connect(bsClient_.get(), &BsClientQt::connectionFailed, this, &BSTerminalMainWindow::onBsConnectionFailed); - - // connect to RFQ dialog - connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetRFQ, &RFQRequestWidget::onMessageFromPB); - connect(bsClient_.get(), &BsClientQt::disconnected, ui_->widgetRFQ, &RFQRequestWidget::onUserDisconnected); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClientQt::sendUnsignedPayin); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayinToPB, bsClient_.get(), &BsClientQt::sendSignedPayin); - connect(ui_->widgetRFQ, &RFQRequestWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClientQt::sendSignedPayout); - - connect(ui_->widgetRFQ, &RFQRequestWidget::cancelXBTTrade, bsClient_.get(), &BsClientQt::sendCancelOnXBTTrade); - connect(ui_->widgetRFQ, &RFQRequestWidget::cancelCCTrade, bsClient_.get(), &BsClientQt::sendCancelOnCCTrade); - - // connect to quote dialog - connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetRFQReply, &RFQReplyWidget::onMessageFromPB); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendUnsignedPayinToPB, bsClient_.get(), &BsClientQt::sendUnsignedPayin); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayinToPB, bsClient_.get(), &BsClientQt::sendSignedPayin); - connect(ui_->widgetRFQReply, &RFQReplyWidget::sendSignedPayoutToPB, bsClient_.get(), &BsClientQt::sendSignedPayout); - - connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelXBTTrade, bsClient_.get(), &BsClientQt::sendCancelOnXBTTrade); - connect(ui_->widgetRFQReply, &RFQReplyWidget::cancelCCTrade, bsClient_.get(), &BsClientQt::sendCancelOnCCTrade); - - connect(ui_->widgetChat, &ChatWidget::emailHashRequested, bsClient_.get(), &BsClientQt::findEmailHash); - connect(bsClient_.get(), &BsClientQt::emailHashReceived, ui_->widgetChat, &ChatWidget::onEmailHashReceived); - - connect(bsClient_.get(), &BsClientQt::processPbMessage, orderListModel_.get(), &OrderListModel::onMessageFromPB); - - utxoReservationMgr_->setFeeRatePb(result.feeRatePb); - connect(bsClient_.get(), &BsClientQt::feeRateReceived, this, [this] (float feeRate) { - utxoReservationMgr_->setFeeRatePb(feeRate); - }); - - setLoginButtonText(currentUserLogin_); - setWidgetsAuthorized(true); - - // We don't use password here, BsProxy will manage authentication - SPDLOG_LOGGER_DEBUG(logMgr_->logger(), "got celer login: {}", result.celerLogin); - celerConnection_->LoginToServer(bsClient_.get(), result.celerLogin, email); - - ui_->widgetWallets->setUsername(currentUserLogin_); - action_logout_->setVisible(false); - action_login_->setEnabled(false); - - // Market data, charts and chat should be available for all Auth eID logins - mdProvider_->SubscribeToMD(); - - connect(bsClient_.get(), &BsClientQt::processPbMessage, ui_->widgetChat, &ChatWidget::onProcessOtcPbMessage); - connect(ui_->widgetChat, &ChatWidget::sendOtcPbMessage, bsClient_.get(), &BsClientQt::sendPbMessage); - - connect(bsClient_.get(), &BsClientQt::bootstrapDataUpdated, this, [this](const std::string& data) { - onBootstrapDataLoaded(data); - }); - - accountEnabled_ = true; - onAccountTypeChanged(result.userType, result.enabled); - connect(bsClient_.get(), &BsClientQt::accountStateChanged, this, [this](bs::network::UserType userType, bool enabled) { - onAccountTypeChanged(userType, enabled); - }); - - connect(bsClient_.get(), &BsClientQt::tradingStatusChanged, this, [](bool tradingEnabled) { - NotificationCenter::notify(tradingEnabled ? bs::ui::NotifyType::TradingEnabledOnPB : bs::ui::NotifyType::TradingDisabledOnPB, {}); - }); -} - -const std::string &BSTerminalMainWindow::loginApiKeyEncrypted() const -{ - return loginApiKeyEncrypted_; -} - -void BSTerminalMainWindow::initApiKeyLogins() -{ - if (loginTimer_ || loginApiKeyEncrypted().empty() || !gotChatKeys_) { - return; - } - loginTimer_ = new QTimer(this); - connect(loginTimer_, &QTimer::timeout, this, [this] { - tryLoginUsingApiKey(); - }); - tryLoginUsingApiKey(); - loginTimer_->start(kAutoLoginTimer); -} - -void BSTerminalMainWindow::tryLoginUsingApiKey() -{ - if (loginApiKeyEncrypted().empty() || autoLoginState_ != AutoLoginState::Idle) { - return; - } - - auto logger = logMgr_->logger("proxy"); - autoLoginClient_ = createClient(); - - auto apiKeyErrorCb = [this, logger](AutoLoginState newState, const QString &errorMsg) { - // Do not show related errors multiple times - if (autoLoginState_ == AutoLoginState::Idle || autoLoginState_ == AutoLoginState::Failed) { - return; - } - SPDLOG_LOGGER_ERROR(logger, "authorization failed: {}", errorMsg.toStdString()); - autoLoginState_ = newState; - autoLoginClient_ = nullptr; - if (autoLoginLastErrorMsg_ != errorMsg) { - autoLoginLastErrorMsg_ = errorMsg; - BSMessageBox(BSMessageBox::critical, tr("API key login") - , tr("Login failed") - , errorMsg - , this).exec(); - } - }; - - connect(autoLoginClient_.get(), &BsClientQt::connected, this, [this, logger, apiKeyErrorCb] { - connect(autoLoginClient_.get(), &BsClientQt::authorizeDone, this, [this, logger, apiKeyErrorCb] - (BsClientCallbackTarget::AuthorizeError error, const std::string &email) { - if (error != BsClientCallbackTarget::AuthorizeError::NoError) { - switch (error) { - case BsClientCallbackTarget::AuthorizeError::UnknownIpAddr: - apiKeyErrorCb(AutoLoginState::Failed, tr("Unexpected IP address")); - break; - case BsClientCallbackTarget::AuthorizeError::UnknownApiKey: - apiKeyErrorCb(AutoLoginState::Failed, tr("API key not found")); - break; - case BsClientCallbackTarget::AuthorizeError::Timeout: - apiKeyErrorCb(AutoLoginState::Idle, tr("Request timeout")); - break; - default: - apiKeyErrorCb(AutoLoginState::Idle, tr("Unknown server error")); - break; - } - return; - } - - connect(autoLoginClient_.get(), &BsClientQt::getLoginResultDone, this, [this, logger, email, apiKeyErrorCb] - (const BsClientLoginResult &result) { - if (result.status != AutheIDClient::NoError) { - apiKeyErrorCb(AutoLoginState::Idle, tr("Login failed")); - return; - } - activateClient(autoLoginClient_, result, email); - autoLoginState_ = AutoLoginState::Connected; - autoLoginClient_ = nullptr; - autoLoginLastErrorMsg_.clear(); - }); - autoLoginClient_->getLoginResult(); - }); - - SecureBinaryData apiKeyEncCopy; - try { - apiKeyEncCopy = SecureBinaryData::CreateFromHex(loginApiKeyEncrypted()); - } catch (...) { - apiKeyErrorCb(AutoLoginState::Failed, tr("Encrypted API key invalid")); - return; - } - - ConfigDialog::decryptData(walletsMgr_, signContainer_, apiKeyEncCopy, [this, apiKeyErrorCb] - (ConfigDialog::EncryptError error, const SecureBinaryData &data) { - if (error != ConfigDialog::EncryptError::NoError) { - apiKeyErrorCb(AutoLoginState::Failed, ConfigDialog::encryptErrorStr(error)); - return; - } - autoLoginClient_->authorize(data.toBinStr()); - }); - }); - - connect(autoLoginClient_.get(), &BsClientQt::disconnected, this, [logger, apiKeyErrorCb] { - apiKeyErrorCb(AutoLoginState::Idle, tr("Proxy disconnected")); - }); - connect(autoLoginClient_.get(), &BsClientQt::connectionFailed, this, [logger, apiKeyErrorCb] { - apiKeyErrorCb(AutoLoginState::Idle, tr("Proxy connection failed")); - }); - - autoLoginState_ = AutoLoginState::Connecting; -} - -void BSTerminalMainWindow::addDeferredDialog(const std::function &deferredDialog) -{ - // multi thread scope, it's safe to call this function from different threads - QMetaObject::invokeMethod(this, [this, deferredDialog] { - // single thread scope (main thread), it's safe to push to deferredDialogs_ - // and check deferredDialogRunning_ variable - deferredDialogs_.push(deferredDialog); - processDeferredDialogs(); - }, Qt::QueuedConnection); -} - -void BSTerminalMainWindow::openURIDialog() -{ - const bool testnetNetwork = applicationSettings_->get(ApplicationSettings::netType) == NetworkType::TestNet; - - auto uiLogger = logMgr_->logger("ui"); - - OpenURIDialog dlg{connectionManager_->GetNAM(), testnetNetwork, uiLogger, this}; - if (dlg.exec() == QDialog::Accepted) { - // open create transaction dialog - - const auto requestInfo = dlg.getRequestInfo(); - std::shared_ptr cerateTxDlg; - - if (applicationSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault)) { - cerateTxDlg = CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsMgr_ - , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ - , requestInfo, this); - } else { - cerateTxDlg = CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsMgr_ - , utxoReservationMgr_, signContainer_, uiLogger, applicationSettings_ - , requestInfo, this); - } - - DisplayCreateTransactionDialog(cerateTxDlg); - } -} - -void BSTerminalMainWindow::DisplayCreateTransactionDialog(std::shared_ptr dlg) -{ - while(true) { - dlg->exec(); - - if ((dlg->result() != QDialog::Accepted) || !dlg->switchModeRequested()) { - break; - } - - auto nextDialog = dlg->SwitchMode(); - dlg = nextDialog; - } -} - -void BSTerminalMainWindow::onBootstrapDataLoaded(const std::string& data) -{ - if (bootstrapDataManager_->setReceivedData(data)) { - authManager_->SetLoadedValidationAddressList(bootstrapDataManager_->GetAuthValidationList()); - ccFileManager_->SetLoadedDefinitions(bootstrapDataManager_->GetCCDefinitions()); - } -} - -void BSTerminalMainWindow::onAuthLeafCreated() -{ - auto authWallet = walletsMgr_->getAuthWallet(); - if (authWallet != nullptr) { - // check that current wallet has auth address that was submitted at some point - // if there is no such address - display auth address dialog, so user could submit - auto submittedAddresses = celerConnection_->GetSubmittedAuthAddressSet(); - auto existingAddresses = authWallet->getUsedAddressList(); - - bool haveSubmittedAddress = false; - for ( const auto& address : existingAddresses) { - if (submittedAddresses.find(address.display()) != submittedAddresses.end()) { - haveSubmittedAddress = true; - break; - } - } - - if (!haveSubmittedAddress) { - addDeferredDialog([this]() - { - openAuthManagerDialog(); - }); - } - } -} diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h deleted file mode 100644 index 609dabb8a..000000000 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ /dev/null @@ -1,373 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __BS_TERMINAL_MAIN_WINDOW_H__ -#define __BS_TERMINAL_MAIN_WINDOW_H__ - -#include -#include - -#include -#include - -#include "ApplicationSettings.h" -#include "ArmoryObject.h" -#include "BsClient.h" -#include "Celer/CelerClientProxy.h" -#include "QWalletInfo.h" -#include "SignContainer.h" -#include "WalletSignerContainer.h" -#include "BIP15xHelpers.h" - -#include "ChatProtocol/ChatClientService.h" - -namespace Ui { - class BSTerminalMainWindow; -} -namespace bs { - class LogManager; - class UTXOReservationManager; - struct TradeSettings; - namespace sync { - class Wallet; - class WalletsManager; - } -} - -class QLockFile; - -struct BsClientLoginResult; -struct NetworkSettings; - -class AboutDialog; -class ArmoryServersProvider; -class AssetManager; -class AuthAddressDialog; -class AuthAddressManager; -class AutheIDClient; -class AutoSignScriptProvider; -class BSMarketDataProvider; -class BSTerminalSplashScreen; -class BaseCelerClient; -class BootstrapDataManager; -class CCFileManager; -class CCPortfolioModel; -class CcTrackerClient; -class ConnectionManager; -class CreateTransactionDialog; -class LoginWindow; -class MDCallbacksQt; -class OrderListModel; -class QSystemTrayIcon; -class RequestReplyCommand; -class SignersProvider; -class StatusBarView; -class StatusViewBlockListener; -class TransactionsViewModel; -class WalletManagementWizard; - -enum class BootstrapFileError: int; - -class BSTerminalMainWindow : public QMainWindow -{ -Q_OBJECT - -public: - BSTerminalMainWindow(const std::shared_ptr& settings - , BSTerminalSplashScreen& splashScreen, QLockFile &lockFile, QWidget* parent = nullptr); - ~BSTerminalMainWindow() override; - - void postSplashscreenActions(); - void loadPositionAndShow(); - - bool event(QEvent *event) override; - void addDeferredDialog(const std::function &deferredDialog); - -private: - void setupToolbar(); - void setupTopRightWidget(); - void setupMenu(); - void setupIcon(); - - void setupWalletsView(); - void setupTransactionsView(); - void setupInfoWidget(); - - void initConnections(); - void initArmory(); - void initCcClient(); - void initUtxoReservationManager(); - void initBootstrapDataManager(); - void connectArmory(); - void connectCcClient(); - void connectSigner(); - std::shared_ptr createSigner(); - std::shared_ptr createRemoteSigner(bool restoreHeadless = false); - std::shared_ptr createLocalSigner(); - - void setTabStyle(); - - void LoadWallets(); - void InitAuthManager(); - bool InitSigningContainer(); - void InitAssets(); - - void InitPortfolioView(); - void InitWalletsView(); - void InitChartsView(); - - void tryInitChatView(); - void tryLoginIntoChat(); - void resetChatKeys(); - void tryGetChatKeys(); - - void UpdateMainWindowAppearence(); - - bool isMDLicenseAccepted() const; - void saveUserAcceptedMDLicense(); - - bool showStartupDialog(); - void setWidgetsAuthorized(bool authorized); - - void openURIDialog(); - -signals: - void armoryServerPromptResultReady(); - -private slots: - void InitTransactionsView(); - void ArmoryIsOffline(); - void SignerReady(); - void onNeedNewWallet(); - void showInfo(const QString &title, const QString &text); - void showError(const QString &title, const QString &text); - void onSignerConnError(SignContainer::ConnectionError error, const QString &details); - - void CompleteUIOnlineView(); - void CompleteDBConnection(); - - bool createPrimaryWallet(); - void onCreatePrimaryWalletRequest(); - - void acceptMDAgreement(); - void updateControlEnabledState(); - void onButtonUserClicked(); - void showArmoryServerPrompt(const BinaryData& srvPubKey, const std::string& srvIPPort, std::shared_ptr > promiseObj); - - void onArmoryNeedsReconnect(); - void onCCLoaded(); - - void onTabWidgetCurrentChanged(const int &index); - void onSyncWallets(); - void onSignerVisibleChanged(); - - void onAuthLeafCreated(); - -private: - std::unique_ptr ui_; - QAction *action_send_ = nullptr; - QAction *action_generate_address_ = nullptr; - QAction *action_login_ = nullptr; - QAction *action_logout_ = nullptr; - - std::shared_ptr logMgr_; - std::shared_ptr applicationSettings_; - std::shared_ptr walletsMgr_; - std::shared_ptr armoryServersProvider_; - std::shared_ptr signersProvider_; - std::shared_ptr authManager_; - std::shared_ptr armory_; - std::shared_ptr trackerClient_; - - std::shared_ptr statusBarView_; - std::shared_ptr sysTrayIcon_; - std::shared_ptr transactionsModel_; - std::shared_ptr portfolioModel_; - std::shared_ptr connectionManager_; - std::shared_ptr celerConnection_; - std::shared_ptr mdProvider_; - std::shared_ptr mdCallbacks_; - std::shared_ptr assetManager_; - std::shared_ptr ccFileManager_; - std::shared_ptr bootstrapDataManager_; - std::shared_ptr authAddrDlg_; - std::shared_ptr signContainer_; - std::shared_ptr autoSignQuoteProvider_; - std::shared_ptr autoSignRFQProvider_; - - std::shared_ptr orderListModel_; - - std::shared_ptr walletsWizard_; - std::shared_ptr utxoReservationMgr_{}; - - QString currentUserLogin_; - - - unsigned armoryReconnectDelay_ = 0; - std::chrono::time_point nextArmoryReconnectAttempt_; - -public slots: - void onReactivate(); - void raiseWindow(); - -private: - struct TxInfo; - - enum class AutoLoginState - { - Idle, - Connecting, - Connected, - Failed, - }; - -private slots: - - void onSend(); - void onGenerateAddress(); - - void openAuthManagerDialog(); - void openConfigDialog(bool showInNetworkPage = false); - void openAccountInfoDialog(); - void openCCTokenDialog(); - - void onZCreceived(const std::vector &); - void showZcNotification(const TxInfo &); - void onNodeStatus(NodeStatus, bool isSegWitEnabled, RpcStatus); - - void onLogin(); - void onLogout(); - - void onCelerConnected(); - void onCelerDisconnected(); - void onCelerConnectionError(int errorCode); - void showRunInBackgroundMessage(); - - void onBsConnectionDisconnected(); - void onBsConnectionFailed(); - - void onInitWalletDialogWasShown(); - -protected: - void closeEvent(QCloseEvent* event) override; - void changeEvent(QEvent* e) override; - -private: - void onUserLoggedIn(); - void onUserLoggedOut(); - - void onAccountTypeChanged(bs::network::UserType userType, bool enabled); - - void setLoginButtonText(const QString& text); - - void setupShortcuts(); - - bool isUserLoggedIn() const; - bool isArmoryConnected() const; - - void InitWidgets(); - - void enableTradingIfNeeded(); - - void showLegacyWarningIfNeeded(); - - void promptSwitchEnv(bool prod); - void switchToTestEnv(); - void switchToProdEnv(); - - void restartTerminal(); - void processDeferredDialogs(); - - std::shared_ptr createClient(); - void activateClient(const std::shared_ptr &bsClient - , const BsClientLoginResult &result, const std::string &email); - const std::string &loginApiKeyEncrypted() const; - void initApiKeyLogins(); - void tryLoginUsingApiKey(); - - void DisplayCreateTransactionDialog(std::shared_ptr dlg); - - void onBootstrapDataLoaded(const std::string& data); - -private: - enum class ChatInitState - { - NoStarted, - InProgress, - Done, - }; - - QString loginButtonText_; - AutoLoginState autoLoginState_{AutoLoginState::Idle}; - QString autoLoginLastErrorMsg_; - std::string loginApiKeyEncrypted_; - QTimer *loginTimer_{}; - std::shared_ptr autoLoginClient_; - - bool initialWalletCreateDialogShown_ = false; - bool deferCCsync_ = false; - - bool wasWalletsRegistered_ = false; - bool walletsSynched_ = false; - bool isArmoryReady_ = false; - - SignContainer::ConnectionError lastSignerError_{}; - - bs::network::BIP15xNewKeyCb cbApproveChat_{ nullptr }; - bs::network::BIP15xNewKeyCb cbApproveProxy_{ nullptr }; - bs::network::BIP15xNewKeyCb cbApproveCcServer_{ nullptr }; - bs::network::BIP15xNewKeyCb cbApproveExtConn_{ nullptr }; - - std::queue> deferredDialogs_; - bool deferredDialogRunning_ = false; - - uint32_t armoryRestartCount_{}; - - class MainWinACT : public ArmoryCallbackTarget - { - public: - MainWinACT(BSTerminalMainWindow *wnd) - : parent_(wnd) {} - ~MainWinACT() override { cleanup(); } - void onZCReceived(const std::string& requestId, const std::vector&) override; - void onStateChanged(ArmoryState) override; - void onTxBroadcastError(const std::string& requestId, const BinaryData &txHash, int errCode - , const std::string &errMsg) override; - void onNodeStatus(NodeStatus, bool isSegWitEnabled, RpcStatus) override; - - private: - BSTerminalMainWindow *parent_; - }; - std::unique_ptr act_; - - std::shared_ptr bsClient_; - - Chat::ChatClientServicePtr chatClientServicePtr_; - - ChatInitState chatInitState_{ChatInitState::NoStarted}; - bool gotChatKeys_{false}; - BinaryData chatTokenData_; - SecureBinaryData chatTokenSign_; - BinaryData chatPubKey_; - SecureBinaryData chatPrivKey_; - - // Default is online to not show online notification after terminal startup - bool isBitcoinCoreOnline_{true}; - - bool accountEnabled_{true}; - - QLockFile &lockFile_; - - bs::network::UserType userType_{}; - - std::shared_ptr tradeSettings_; -}; - -#endif // __BS_TERMINAL_MAIN_WINDOW_H__ diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index 6e86e8d9b..1b5f4d07d 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -54,27 +54,6 @@ const std::map kFeeLevels = { }; const size_t kTransactionWeightLimit = 400000; -CreateTransactionDialog::CreateTransactionDialog(const std::shared_ptr &armory - , const std::shared_ptr& walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container, bool loadFeeSuggestions - , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , bs::UtxoReservationToken utxoReservation - , QWidget* parent) - : QDialog(parent) - , armory_(armory) - , walletsManager_(walletManager) - , signContainer_(container) - , logger_(logger) - , applicationSettings_(applicationSettings) - , utxoReservationManager_(utxoReservationManager) - , utxoRes_(std::move(utxoReservation)) - , loadFeeSuggestions_(loadFeeSuggestions) -{ - qRegisterMetaType>(); -} - CreateTransactionDialog::CreateTransactionDialog(bool loadFeeSuggestions , uint32_t topBlock, const std::shared_ptr& logger , QWidget* parent) @@ -127,11 +106,6 @@ void CreateTransactionDialog::init() connect(comboBoxFeeSuggestions(), SIGNAL(activated(int)), this, SLOT(feeSelectionChanged(int))); connect(comboBoxWallets(), SIGNAL(currentIndexChanged(int)), this, SLOT(selectedWalletChanged(int))); - if (signContainer_) { - connect(signContainer_.get(), &SignContainer::TXSigned, this, &CreateTransactionDialog::onTXSigned); - connect(signContainer_.get(), &SignContainer::disconnected, this, &CreateTransactionDialog::updateCreateButtonText); - connect(signContainer_.get(), &SignContainer::authenticated, this, &CreateTransactionDialog::onSignerAuthenticated); - } updateCreateButtonText(); lineEditAddress()->setFocus(); } diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index 0636de562..016af1bb6 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -59,14 +59,6 @@ class CreateTransactionDialog : public QDialog Q_OBJECT public: - [[deprecated]] CreateTransactionDialog(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , bool loadFeeSuggestions, const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , bs::UtxoReservationToken utxoReservation - , QWidget* parent); CreateTransactionDialog(bool loadFeeSuggestions, uint32_t topBlock , const std::shared_ptr&, QWidget* parent); ~CreateTransactionDialog() noexcept override; diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index d0080caf6..142edfe9d 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -43,27 +43,6 @@ static const float kDustFeePerByte = 3.0; -CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(const std::shared_ptr &armory - , const std::shared_ptr& walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container - , bool loadFeeSuggestions - , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , const std::shared_ptr &txData - , bs::UtxoReservationToken utxoReservation - , QWidget* parent) - : CreateTransactionDialog(armory, walletManager, utxoReservationManager, container, loadFeeSuggestions, - logger, applicationSettings, std::move(utxoReservation), parent) - , ui_(new Ui::CreateTransactionDialogAdvanced) -{ - transactionData_ = txData; - selectedChangeAddress_ = bs::Address{}; - - ui_->setupUi(this); - initUI(); -} - CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(bool loadFeeSuggestions , uint32_t topBlock, const std::shared_ptr& logger , const std::shared_ptr& txData, bs::UtxoReservationToken utxoReservation @@ -80,17 +59,11 @@ CreateTransactionDialogAdvanced::CreateTransactionDialogAdvanced(bool loadFeeSug CreateTransactionDialogAdvanced::~CreateTransactionDialogAdvanced() = default; std::shared_ptr CreateTransactionDialogAdvanced::CreateForRBF( - const std::shared_ptr &armory - , const std::shared_ptr& walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& container - , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , const Tx &tx - , QWidget* parent) + uint32_t topBlock, const std::shared_ptr& logger + , const Tx &tx, QWidget* parent) { - auto dlg = std::make_shared(armory - , walletManager, utxoReservationManager, container, false, logger, applicationSettings, nullptr, bs::UtxoReservationToken(), parent); + auto dlg = std::make_shared(topBlock, false + , logger, nullptr, bs::UtxoReservationToken(), parent); dlg->setWindowTitle(tr("Replace-By-Fee")); @@ -106,40 +79,28 @@ std::shared_ptr CreateTransactionDialogAdvanced } std::shared_ptr CreateTransactionDialogAdvanced::CreateForCPFP( - const std::shared_ptr &armory - , const std::shared_ptr& walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& container - , const std::shared_ptr& wallet + uint32_t topBlock, const std::string& walletId , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , const Tx &tx - , QWidget* parent) + , const Tx &tx, QWidget* parent) { - auto dlg = std::make_shared(armory - , walletManager, utxoReservationManager, container, false, logger, applicationSettings, nullptr, bs::UtxoReservationToken(), parent); + auto dlg = std::make_shared(topBlock, false + , logger, nullptr, bs::UtxoReservationToken(), parent); dlg->setWindowTitle(tr("Child-Pays-For-Parent")); dlg->ui_->pushButtonImport->setEnabled(false); dlg->ui_->pushButtonShowSimple->setEnabled(false); - dlg->setCPFPinputs(tx, wallet); + //FIXME: dlg->setCPFPinputs(tx, wallet); dlg->isCPFP_ = true; return dlg; } std::shared_ptr CreateTransactionDialogAdvanced::CreateForPaymentRequest( - const std::shared_ptr &armory - , const std::shared_ptr &walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& container - , const std::shared_ptr& logger - , const std::shared_ptr & applicationSettings - , const Bip21::PaymentRequestInfo& paymentInfo - , QWidget* parent) + uint32_t topBlock, const std::shared_ptr& logger + , const Bip21::PaymentRequestInfo& paymentInfo, QWidget* parent) { - auto dlg = std::make_shared(armory - , walletManager, utxoReservationManager, container, false, logger, applicationSettings, nullptr, bs::UtxoReservationToken(), parent); + auto dlg = std::make_shared(topBlock, false + , logger, nullptr, bs::UtxoReservationToken(), parent); dlg->ui_->pushButtonImport->setEnabled(false); dlg->ui_->pushButtonShowSimple->setEnabled(CreateTransactionDialog::canUseSimpleMode(paymentInfo)); @@ -1858,13 +1819,12 @@ bool CreateTransactionDialogAdvanced::switchModeRequested() const std::shared_ptr CreateTransactionDialogAdvanced::SwitchMode() { if (!paymentInfo_.address.isEmpty()) { - return CreateTransactionDialogSimple::CreateForPaymentRequest(armory_, walletsManager_ - , utxoReservationManager_, signContainer_, logger_, applicationSettings_, paymentInfo_, parentWidget()); + return CreateTransactionDialogSimple::CreateForPaymentRequest(topBlock_ + , logger_, paymentInfo_, parentWidget()); } - auto simpleDialog = std::make_shared(armory_ - , walletsManager_, utxoReservationManager_, signContainer_ - , logger_, applicationSettings_, parentWidget()); + auto simpleDialog = std::make_shared(topBlock_ + , logger_, parentWidget()); simpleDialog->SelectWallet(UiUtils::getSelectedWalletId(ui_->comboBoxWallets), UiUtils::getSelectedWalletType(ui_->comboBoxWallets)); diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index bd90ec84f..2f2b8ee2b 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -34,48 +34,20 @@ class CreateTransactionDialogAdvanced : public CreateTransactionDialog Q_OBJECT public: - static std::shared_ptr CreateForRBF( - const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr& - , const std::shared_ptr & - , const Tx & + static std::shared_ptr CreateForRBF(uint32_t topBlock + , const std::shared_ptr&, const Tx & , QWidget* parent = nullptr); - static std::shared_ptr CreateForCPFP( - const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr& - , const std::shared_ptr& - , const std::shared_ptr & - , const Tx & - , QWidget* parent = nullptr); + static std::shared_ptr CreateForCPFP(uint32_t topBlock + , const std::string& walletId, const std::shared_ptr& + , const Tx &, QWidget* parent = nullptr); - static std::shared_ptr CreateForPaymentRequest( - const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr& + static std::shared_ptr CreateForPaymentRequest(uint32_t topBlock , const std::shared_ptr& - , const std::shared_ptr & , const Bip21::PaymentRequestInfo& paymentInfo , QWidget* parent = nullptr); public: - [[deprecated]] CreateTransactionDialogAdvanced(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , bool loadFeeSuggestions - , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , const std::shared_ptr & - , bs::UtxoReservationToken utxoReservation - , QWidget* parent = nullptr); CreateTransactionDialogAdvanced(bool loadFeeSuggestions, uint32_t topBlock , const std::shared_ptr& , const std::shared_ptr& diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.cpp b/BlockSettleUILib/CreateTransactionDialogSimple.cpp index d0d73565b..d50b91193 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.cpp +++ b/BlockSettleUILib/CreateTransactionDialogSimple.cpp @@ -22,20 +22,6 @@ #include "Wallets/SyncWalletsManager.h" #include "XbtAmountValidator.h" -CreateTransactionDialogSimple::CreateTransactionDialogSimple(const std::shared_ptr &armory - , const std::shared_ptr& walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container - , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings - , QWidget* parent) - : CreateTransactionDialog(armory, walletManager, utxoReservationManager, container, true, logger, applicationSettings, {}, parent) - , ui_(new Ui::CreateTransactionDialogSimple) -{ - ui_->setupUi(this); - initUI(); -} - CreateTransactionDialogSimple::CreateTransactionDialogSimple(uint32_t topBlock , const std::shared_ptr& logger, QWidget* parent) : CreateTransactionDialog(true, topBlock, logger, parent) @@ -239,12 +225,12 @@ bool CreateTransactionDialogSimple::switchModeRequested() const std::shared_ptr CreateTransactionDialogSimple::SwitchMode() { if (!paymentInfo_.address.isEmpty()) { - return CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory_, walletsManager_ - , utxoReservationManager_, signContainer_, logger_, applicationSettings_, paymentInfo_, parentWidget()); + return CreateTransactionDialogAdvanced::CreateForPaymentRequest(topBlock_ + , logger_, paymentInfo_, parentWidget()); } - auto advancedDialog = std::make_shared(armory_, walletsManager_ - , utxoReservationManager_, signContainer_, true, logger_, applicationSettings_, transactionData_, std::move(utxoRes_), parentWidget()); + auto advancedDialog = std::make_shared(topBlock_ + , true, logger_, transactionData_, std::move(utxoRes_), parentWidget()); if (!offlineTransactions_.empty()) { advancedDialog->SetImportedTransactions(offlineTransactions_); @@ -305,22 +291,18 @@ void CreateTransactionDialogSimple::preSetValue(const bs::XBTAmount& value) ui_->lineEditAmount->setText(UiUtils::displayAmount(value)); } -std::shared_ptr CreateTransactionDialogSimple::CreateForPaymentRequest(const std::shared_ptr &armory - , const std::shared_ptr& walletManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &container +std::shared_ptr CreateTransactionDialogSimple::CreateForPaymentRequest(uint32_t topBlock , const std::shared_ptr& logger - , const std::shared_ptr &applicationSettings , const Bip21::PaymentRequestInfo& paymentInfo , QWidget* parent) { if (!canUseSimpleMode(paymentInfo)) { - return CreateTransactionDialogAdvanced::CreateForPaymentRequest(armory, walletManager - , utxoReservationManager, container, logger, applicationSettings, paymentInfo, parent); + return CreateTransactionDialogAdvanced::CreateForPaymentRequest(topBlock + , logger, paymentInfo, parent); } - auto dlg = std::make_shared(armory, walletManager - , utxoReservationManager, container, logger, applicationSettings, parent); + auto dlg = std::make_shared(topBlock, logger + , parent); // set address and lock dlg->preSetAddress(paymentInfo.address); diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.h b/BlockSettleUILib/CreateTransactionDialogSimple.h index 19b9b21df..84636a54b 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.h +++ b/BlockSettleUILib/CreateTransactionDialogSimple.h @@ -25,28 +25,12 @@ class CreateTransactionDialogSimple : public CreateTransactionDialog Q_OBJECT public: - [[deprecated]] static std::shared_ptr CreateForPaymentRequest( - const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr& - , const std::shared_ptr & - , const Bip21::PaymentRequestInfo& paymentInfo - , QWidget* parent = nullptr); static std::shared_ptr CreateForPaymentRequest( - const std::shared_ptr& + uint32_t topBlock, const std::shared_ptr& , const Bip21::PaymentRequestInfo& paymentInfo , QWidget* parent = nullptr); public: - [[deprecated]] CreateTransactionDialogSimple(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr &applicationSettings - , QWidget* parent = nullptr); CreateTransactionDialogSimple(uint32_t topBlock, const std::shared_ptr& , QWidget* parent = nullptr); ~CreateTransactionDialogSimple() override; diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 37395d851..c3e33cc06 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -52,70 +52,6 @@ class CurrentWalletFilter : public QSortFilterProxyModel std::string walletId_; }; -RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptr &logger - , const bs::sync::WalletInfo &wallet - , const std::shared_ptr &walletsManager - , const std::shared_ptr &armory - , const std::shared_ptr &container - , WalletsViewModel *walletsModel - , const std::shared_ptr &appSettings - , const std::shared_ptr &connectionManager - , const std::shared_ptr &assetMgr - , QWidget* parent) - : QDialog(parent) - , ui_(new Ui::WalletPropertiesDialog()) - , wallet_(wallet) - , walletsManager_(walletsManager) - , walletInfo_(wallet) - , signingContainer_(container) - , appSettings_(appSettings) - , connectionManager_(connectionManager) - , assetMgr_(assetMgr) - , logger_(logger) -{ - ui_->setupUi(this); - - ui_->labelEncRank->clear(); - - walletFilter_ = new CurrentWalletFilter(*wallet.ids.cbegin(), this); - walletFilter_->setSourceModel(walletsModel); - ui_->treeViewWallets->setModel(walletFilter_); - - connect(walletsModel, &WalletsViewModel::modelReset, - this, &RootWalletPropertiesDialog::onModelReset); - - ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnDescription)); - ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnState)); - ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnSpendableBalance)); - ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnUnconfirmedBalance)); - ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnNbAddresses)); - ui_->treeViewWallets->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - - connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged, this, &RootWalletPropertiesDialog::onWalletSelected); - - connect(ui_->deleteButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onDeleteWallet); - connect(ui_->backupButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onBackupWallet); - connect(ui_->manageEncryptionButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onChangePassword); - connect(ui_->rescanButton, &QPushButton::clicked, this, &RootWalletPropertiesDialog::onRescanBlockchain); - - updateWalletDetails(wallet_); - - ui_->rescanButton->setEnabled(armory->state() == ArmoryState::Ready); - ui_->manageEncryptionButton->setEnabled(false); - - if (signingContainer_) { - if (signingContainer_->isOffline()) { - ui_->backupButton->setEnabled(false); - ui_->manageEncryptionButton->setEnabled(false); - } - connect(signingContainer_.get(), &SignContainer::QWalletInfo, this, &RootWalletPropertiesDialog::onHDWalletInfo); - - infoReqId_ = signingContainer_->GetInfo(*wallet_.ids.cbegin()); - } - - ui_->treeViewWallets->expandAll(); -} - RootWalletPropertiesDialog::RootWalletPropertiesDialog(const std::shared_ptr &logger , const bs::sync::WalletInfo &wallet , WalletsViewModel *walletsModel diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 6ba570aa0..402275ec0 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -45,15 +45,6 @@ class RootWalletPropertiesDialog : public QDialog Q_OBJECT public: - [[deprecated]] RootWalletPropertiesDialog(const std::shared_ptr &logger - , const bs::sync::WalletInfo & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , WalletsViewModel *walletsModel - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &, QWidget* parent = nullptr); RootWalletPropertiesDialog(const std::shared_ptr &logger , const bs::sync::WalletInfo &, WalletsViewModel *walletsModel , QWidget* parent = nullptr); diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index 642e22939..d13502956 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -97,7 +97,7 @@ void PortfolioWidget::init(const std::shared_ptr &appSettin , const std::shared_ptr &logger , const std::shared_ptr &walletsMgr) { - TransactionsWidgetInterface::init(walletsMgr, armory, utxoReservationManager, container, appSettings, logger); + //FIXME: TransactionsWidgetInterface::init(walletsMgr, armory, utxoReservationManager, container, appSettings, logger); ui_->widgetMarketData->init(appSettings, ApplicationSettings::Filter_MD_RFQ_Portfolio , mdProvider, mdCallbacks); diff --git a/BlockSettleUILib/PubKeyLoader.cpp b/BlockSettleUILib/PubKeyLoader.cpp index d83808956..379a8b218 100644 --- a/BlockSettleUILib/PubKeyLoader.cpp +++ b/BlockSettleUILib/PubKeyLoader.cpp @@ -10,9 +10,9 @@ */ #include "PubKeyLoader.h" #include +#include #include "BSMessageBox.h" #include "ImportKeyBox.h" -#include "BSTerminalMainWindow.h" #include "FutureValue.h" PubKeyLoader::PubKeyLoader(const std::shared_ptr &appSettings @@ -88,10 +88,10 @@ bs::network::BIP15xNewKeyCb PubKeyLoader::getApprovingCallback(const KeyType kt }); }; - BSTerminalMainWindow *mw = qobject_cast(bsMainWindow); +/* BSTerminalMainWindow *mw = qobject_cast(bsMainWindow); if (mw) { mw->addDeferredDialog(deferredDialog); - } + }*/ }; } diff --git a/BlockSettleUILib/SelectWalletDialog.cpp b/BlockSettleUILib/SelectWalletDialog.cpp index 08a1684a7..a35f007d4 100644 --- a/BlockSettleUILib/SelectWalletDialog.cpp +++ b/BlockSettleUILib/SelectWalletDialog.cpp @@ -16,35 +16,6 @@ #include "Wallets/SyncWalletsManager.h" -SelectWalletDialog::SelectWalletDialog(const std::shared_ptr &walletsManager - , const std::string &selWalletId, QWidget* parent) - : QDialog(parent) - , ui_(new Ui::SelectWalletDialog) -{ - ui_->setupUi(this); - - ui_->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Select")); - - walletsModel_ = new WalletsViewModel(walletsManager, selWalletId, nullptr, ui_->treeViewWallets, true); - walletsModel_->setBitcoinLeafSelectionMode(); - ui_->treeViewWallets->setModel(walletsModel_); - ui_->treeViewWallets->setItemsExpandable(true); - ui_->treeViewWallets->setRootIsDecorated(true); - walletsModel_->LoadWallets(); - ui_->treeViewWallets->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - - connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged - , this, &SelectWalletDialog::onSelectionChanged); - connect(ui_->treeViewWallets, &QTreeView::doubleClicked - , this, &SelectWalletDialog::onDoubleClicked); - - connect(ui_->buttonBox, &QDialogButtonBox::accepted, this, &SelectWalletDialog::accept); - connect(ui_->buttonBox, &QDialogButtonBox::rejected, this, &SelectWalletDialog::reject); - - onSelectionChanged(); - ui_->treeViewWallets->expandAll(); -} - SelectWalletDialog::SelectWalletDialog(const std::string& selWalletId, QWidget* parent) : QDialog(parent) , ui_(new Ui::SelectWalletDialog) diff --git a/BlockSettleUILib/SelectWalletDialog.h b/BlockSettleUILib/SelectWalletDialog.h index 91873bf0d..75feb33f4 100644 --- a/BlockSettleUILib/SelectWalletDialog.h +++ b/BlockSettleUILib/SelectWalletDialog.h @@ -33,8 +33,6 @@ class SelectWalletDialog : public QDialog Q_OBJECT public: - [[deprecated]] SelectWalletDialog(const std::shared_ptr & - , const std::string &selWalletId, QWidget* parent = nullptr); SelectWalletDialog(const std::string& selWalletId, QWidget* parent = nullptr); ~SelectWalletDialog() override; diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index e7590545e..c4eab0bbd 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -95,12 +95,12 @@ StatusBarView::StatusBarView(const std::shared_ptr &armory connect(celerClient.get(), &CelerClientQt::OnConnectionError, this, &StatusBarView::onMatchingConnError); // container might be null if user rejects remote signer key - if (container) { +/* if (container) { // connected are not used here because we wait for authenticated signal instead // disconnected are not used here because onContainerError should be always called connect(container.get(), &SignContainer::authenticated, this, &StatusBarView::onContainerAuthorized); connect(container.get(), &SignContainer::connectionError, this, &StatusBarView::onSignerStatusChanged); - } + }*/ onArmoryStateChanged(armory_->state(), armory_->topBlock()); onDisconnectedFromMatching(); diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp index a868467a6..cfcd9bd1e 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp +++ b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp @@ -39,14 +39,14 @@ AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptrsetWalletsManager(walletsManager_); } - if (signingContainer_) { +/* if (signingContainer_) { connect(signingContainer_.get(), &SignContainer::ready, this , &AutoSignScriptProvider::onSignerStateUpdated, Qt::QueuedConnection); connect(signingContainer_.get(), &SignContainer::disconnected, this , &AutoSignScriptProvider::onSignerStateUpdated, Qt::QueuedConnection); connect(signingContainer_.get(), &SignContainer::AutoSignStateChanged, this , &AutoSignScriptProvider::onAutoSignStateChanged); - } + }*/ connect(scriptRunner_, &UserScriptRunner::scriptLoaded, this, &AutoSignScriptProvider::onScriptLoaded); connect(scriptRunner_, &UserScriptRunner::failedToLoad, this, &AutoSignScriptProvider::onScriptFailed); diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp index 2373356df..a8a480d32 100644 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp @@ -98,7 +98,7 @@ DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr throw std::runtime_error("can't register settlement wallet in armory"); } - connect(signContainer_.get(), &SignContainer::TXSigned, this, &DealerXBTSettlementContainer::onTXSigned); +// connect(signContainer_.get(), &SignContainer::TXSigned, this, &DealerXBTSettlementContainer::onTXSigned); } bool DealerXBTSettlementContainer::cancel() diff --git a/BlockSettleUILib/Trading/OtcClient.cpp b/BlockSettleUILib/Trading/OtcClient.cpp index 5a832f879..51ae40b7b 100644 --- a/BlockSettleUILib/Trading/OtcClient.cpp +++ b/BlockSettleUILib/Trading/OtcClient.cpp @@ -234,7 +234,7 @@ OtcClient::OtcClient(const std::shared_ptr &logger , applicationSettings_(applicationSettings) , params_(std::move(params)) { - connect(signContainer.get(), &SignContainer::TXSigned, this, &OtcClient::onTxSigned); +// connect(signContainer.get(), &SignContainer::TXSigned, this, &OtcClient::onTxSigned); } OtcClient::~OtcClient() = default; diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 82bd8c534..f59a40267 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -78,27 +78,6 @@ RFQRequestWidget::RFQRequestWidget(QWidget* parent) RFQRequestWidget::~RFQRequestWidget() = default; -void RFQRequestWidget::setWalletsManager(const std::shared_ptr &walletsManager) -{ - if (walletsManager_ == nullptr) { - walletsManager_ = walletsManager; - ui_->pageRFQTicket->setWalletsManager(walletsManager); - ui_->shieldPage->init(walletsManager, authAddressManager_, appSettings_); - - if (autoSignProvider_) { - autoSignProvider_->scriptRunner()->setWalletsManager(walletsManager_); - } - - // Do not listen for walletChanged (too verbose and resets UI too often) and walletsReady (to late and resets UI after startup unexpectedly) - connect(walletsManager_.get(), &bs::sync::WalletsManager::CCLeafCreated, this, &RFQRequestWidget::forceCheckCondition); - connect(walletsManager_.get(), &bs::sync::WalletsManager::AuthLeafCreated, this, &RFQRequestWidget::forceCheckCondition); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletDeleted, this, &RFQRequestWidget::forceCheckCondition); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletAdded, this, &RFQRequestWidget::forceCheckCondition); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletsSynchronized, this, &RFQRequestWidget::forceCheckCondition); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletPromotedToPrimary, this, &RFQRequestWidget::forceCheckCondition); - } -} - void RFQRequestWidget::shortcutActivated(ShortcutType s) { switch (s) { @@ -334,71 +313,6 @@ void RFQRequestWidget::popShield() ui_->widgetMarketData->view()->setFocus(); } -void RFQRequestWidget::initWidgets(const std::shared_ptr& mdProvider - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr &appSettings) -{ - appSettings_ = appSettings; - ui_->widgetMarketData->init(appSettings, ApplicationSettings::Filter_MD_RFQ - , mdProvider, mdCallbacks); - - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, ui_->pageRFQTicket, &RFQTicketXBT::onMDUpdate); -} - -void RFQRequestWidget::init(const std::shared_ptr &logger - , const std::shared_ptr& celerClient - , const std::shared_ptr &authAddressManager - , const std::shared_ptr "eProvider - , const std::shared_ptr &assetManager - , const std::shared_ptr &dialogManager - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &autoSignProvider - , const std::shared_ptr &utxoReservationManager - , OrderListModel *orderListModel) -{ - logger_ = logger; - celerClient_ = celerClient; - authAddressManager_ = authAddressManager; - quoteProvider_ = quoteProvider; - assetManager_ = assetManager; - dialogManager_ = dialogManager; - signingContainer_ = container; - armory_ = armory; - autoSignProvider_ = autoSignProvider; - utxoReservationManager_ = utxoReservationManager; - - if (walletsManager_) { - autoSignProvider_->scriptRunner()->setWalletsManager(walletsManager_); - } - - ui_->pageRFQTicket->init(logger, authAddressManager, assetManager, - quoteProvider, container, armory, utxoReservationManager); - - ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->treeViewOrders->setModel(orderListModel); - ui_->treeViewOrders->initWithModel(orderListModel); - connect(quoteProvider_.get(), &QuoteProvider::quoteOrderFilled, [](const std::string "eId) { - NotificationCenter::notify(bs::ui::NotifyType::CelerOrder, {true, QString::fromStdString(quoteId)}); - }); - connect(quoteProvider_.get(), &QuoteProvider::orderFailed, [](const std::string "eId, const std::string &reason) { - NotificationCenter::notify(bs::ui::NotifyType::CelerOrder - , { false, QString::fromStdString(quoteId), QString::fromStdString(reason) }); - }); - - connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this, &RFQRequestWidget::onConnectedToCeler); - connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &RFQRequestWidget::onDisconnectedFromCeler); - - connect((RFQScriptRunner *)autoSignProvider_->scriptRunner(), &RFQScriptRunner::sendRFQ - , ui_->pageRFQTicket, &RFQTicketXBT::onSendRFQ, Qt::QueuedConnection); - connect((RFQScriptRunner *)autoSignProvider_->scriptRunner(), &RFQScriptRunner::cancelRFQ - , ui_->pageRFQTicket, &RFQTicketXBT::onCancelRFQ, Qt::QueuedConnection); - - ui_->pageRFQTicket->disablePanel(); - - connect(authAddressManager_.get(), &AuthAddressManager::AddressListUpdated, this, &RFQRequestWidget::forceCheckCondition); -} - void RFQRequestWidget::init(const std::shared_ptr&logger , const std::shared_ptr& dialogMgr, OrderListModel* orderListModel) { diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h index d9b452faf..cd23d0a96 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ b/BlockSettleUILib/Trading/RFQRequestWidget.h @@ -66,28 +66,10 @@ Q_OBJECT RFQRequestWidget(QWidget* parent = nullptr); ~RFQRequestWidget() override; - [[deprecated]] void initWidgets(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); - - [[deprecated]] void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , OrderListModel *orderListModel); - void init(const std::shared_ptr& , const std::shared_ptr& , OrderListModel* orderListModel); - void setWalletsManager(const std::shared_ptr &); - void shortcutActivated(ShortcutType s) override; void setAuthorized(bool authorized); diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp index 1cfcb13fe..685b663e9 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ b/BlockSettleUILib/Trading/RFQTicketXBT.cpp @@ -120,31 +120,6 @@ bs::FixedXbtInputs RFQTicketXBT::fixedXbtInputs() return std::move(fixedXbtInputs_); } -void RFQTicketXBT::init(const std::shared_ptr &logger - , const std::shared_ptr &authAddressManager - , const std::shared_ptr& assetManager - , const std::shared_ptr "eProvider - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &utxoReservationManager) -{ - logger_ = logger; - authAddressManager_ = authAddressManager; - assetManager_ = assetManager; - signingContainer_ = container; - armory_ = armory; - utxoReservationManager_ = utxoReservationManager; - tradeSettings_ = authAddressManager_->tradeSettings(); - - if (signingContainer_) { - connect(signingContainer_.get(), &SignContainer::ready, this, &RFQTicketXBT::onSignerReady); - } - connect(utxoReservationManager_.get(), &bs::UTXOReservationManager::availableUtxoChanged, - this, &RFQTicketXBT::onUTXOReservationChanged); - - updateSubmitButton(); -} - std::shared_ptr RFQTicketXBT::getCCWallet(const std::string &cc) const { if (walletsManager_) { @@ -267,36 +242,6 @@ QString RFQTicketXBT::getProduct() const return currentProduct_; } -void RFQTicketXBT::setWalletsManager(const std::shared_ptr &walletsManager) -{ - walletsManager_ = walletsManager; - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletsSynchronized, this, &RFQTicketXBT::walletsLoaded); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletAdded, this, &RFQTicketXBT::walletsLoaded); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletDeleted, this, &RFQTicketXBT::walletsLoaded); - connect(walletsManager_.get(), &bs::sync::WalletsManager::settlementLeavesLoaded, this, &RFQTicketXBT::onSettlLeavesLoaded); - - connect(walletsManager_.get(), &bs::sync::WalletsManager::CCLeafCreated, this, &RFQTicketXBT::onHDLeafCreated); - connect(walletsManager_.get(), &bs::sync::WalletsManager::CCLeafCreateFailed, this, &RFQTicketXBT::onCreateHDWalletError); - - walletsLoaded(); - - auto updateAuthAddresses = [this] { - UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, authAddressManager_); - onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); - authAddr_ = authAddressManager_->getDefault(); - if (authKey_.empty()) { - onSettlLeavesLoaded(0); - } - }; - updateAuthAddresses(); - connect(authAddressManager_.get(), &AuthAddressManager::AddressListUpdated, this, updateAuthAddresses); - - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, [this] { - // This will update balance after receiving ZC - updatePanel(); - }); -} - void RFQTicketXBT::init(const std::shared_ptr& logger) { logger_ = logger; diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h index d90f9c3d3..34903a136 100644 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ b/BlockSettleUILib/Trading/RFQTicketXBT.h @@ -67,15 +67,6 @@ Q_OBJECT RFQTicketXBT(QWidget* parent = nullptr); ~RFQTicketXBT() override; - [[deprecated]] void init(const std::shared_ptr &logger - , const std::shared_ptr & - , const std::shared_ptr &assetManager - , const std::shared_ptr "eProvider - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); - [[deprecated]]void setWalletsManager(const std::shared_ptr &); - void init(const std::shared_ptr&); void resetTicket(); diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp index 02fe5b324..a5bc98e33 100644 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp +++ b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp @@ -76,7 +76,7 @@ ReqCCSettlementContainer::ReqCCSettlementContainer(const std::shared_ptr(); - connect(signContainer_.get(), &SignContainer::TXSigned, this, &ReqXBTSettlementContainer::onTXSigned); +// connect(signContainer_.get(), &SignContainer::TXSigned, this, &ReqXBTSettlementContainer::onTXSigned); connect(this, &ReqXBTSettlementContainer::timerExpired, this, &ReqXBTSettlementContainer::onTimerExpired); diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index 6f7c3e4bd..08207ba31 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -277,22 +277,6 @@ TransactionsWidget::TransactionsWidget(QWidget* parent) TransactionsWidget::~TransactionsWidget() = default; -void TransactionsWidget::init(const std::shared_ptr &walletsMgr - , const std::shared_ptr &armory - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &signContainer - , const std::shared_ptr &appSettings - , const std::shared_ptr &logger) - -{ - TransactionsWidgetInterface::init(walletsMgr, armory, utxoReservationManager, signContainer, appSettings, logger); - - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletChanged, this, &TransactionsWidget::walletsChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletDeleted, [this](std::string) { walletsChanged(); }); - - scheduleDateFilterCheck(); -} - void TransactionsWidget::init(const std::shared_ptr &logger , const std::shared_ptr &model) { diff --git a/BlockSettleUILib/TransactionsWidget.h b/BlockSettleUILib/TransactionsWidget.h index 82020a861..3594aca27 100644 --- a/BlockSettleUILib/TransactionsWidget.h +++ b/BlockSettleUILib/TransactionsWidget.h @@ -47,12 +47,6 @@ Q_OBJECT TransactionsWidget(QWidget* parent = nullptr ); ~TransactionsWidget() override; - [[deprecated]] void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr &); void init(const std::shared_ptr & , const std::shared_ptr &); [[deprecated]] void SetTransactionsModel(const std::shared_ptr &); diff --git a/BlockSettleUILib/TransactionsWidgetInterface.cpp b/BlockSettleUILib/TransactionsWidgetInterface.cpp index 22c96250d..66ab2808c 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.cpp +++ b/BlockSettleUILib/TransactionsWidgetInterface.cpp @@ -49,24 +49,6 @@ TransactionsWidgetInterface::TransactionsWidgetInterface(QWidget *parent) connect(actionCPFP_, &QAction::triggered, this, &TransactionsWidgetInterface::onCreateCPFPDialog); } -void TransactionsWidgetInterface::init(const std::shared_ptr &walletsMgr - , const std::shared_ptr &armory - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &signContainer - , const std::shared_ptr &appSettings - , const std::shared_ptr &logger) - -{ - walletsManager_ = walletsMgr; - armory_ = armory; - utxoReservationManager_ = utxoReservationManager; - signContainer_ = signContainer; - appSettings_ = appSettings; - logger_ = logger; - - connect(signContainer_.get(), &SignContainer::TXSigned, this, &TransactionsWidgetInterface::onTXSigned); -} - void TransactionsWidgetInterface::init(const std::shared_ptr &logger) { logger_ = logger; @@ -211,10 +193,9 @@ void TransactionsWidgetInterface::onCreateRBFDialog() const auto &cbDialog = [this](const TransactionPtr &txItem) { try { - auto dlg = CreateTransactionDialogAdvanced::CreateForRBF(armory_ - , walletsManager_, utxoReservationManager_, signContainer_, logger_, appSettings_, txItem->tx - , this); - dlg->exec(); + //FIXME: auto dlg = CreateTransactionDialogAdvanced::CreateForRBF(topBlock_ + // , logger_, txItem->tx, this); + //dlg->exec(); } catch (const std::exception &e) { BSMessageBox(BSMessageBox::critical, tr("RBF Transaction"), tr("Failed to create RBF transaction") @@ -247,10 +228,9 @@ void TransactionsWidgetInterface::onCreateCPFPDialog() break; } } - auto dlg = CreateTransactionDialogAdvanced::CreateForCPFP(armory_ - , walletsManager_, utxoReservationManager_, signContainer_, wallet, logger_, appSettings_ - , txItem->tx, this); - dlg->exec(); + //FIXME: auto dlg = CreateTransactionDialogAdvanced::CreateForCPFP(topBlock_ + // , logger_, , txItem->tx, this); + //dlg->exec(); } catch (const std::exception &e) { BSMessageBox(BSMessageBox::critical, tr("CPFP Transaction"), tr("Failed to create CPFP transaction") diff --git a/BlockSettleUILib/TransactionsWidgetInterface.h b/BlockSettleUILib/TransactionsWidgetInterface.h index 2fc4f9e40..6535e98f3 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.h +++ b/BlockSettleUILib/TransactionsWidgetInterface.h @@ -38,12 +38,6 @@ class TransactionsWidgetInterface : public TabWithShortcut { explicit TransactionsWidgetInterface(QWidget *parent = nullptr); ~TransactionsWidgetInterface() noexcept override = default; - [[deprecated]] void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr &); void init(const std::shared_ptr &); protected slots: diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index 944a94805..48d4f86d6 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -348,32 +348,6 @@ WalletGroupNode *WalletRootNode::addGroup(bs::core::wallet::Type type } -WalletsViewModel::WalletsViewModel(const std::shared_ptr &walletsManager - , const std::string &defaultWalletId, const std::shared_ptr &container - , QObject* parent, bool showOnlyRegular) - : QAbstractItemModel(parent) - , walletsManager_(walletsManager) - , signContainer_(container) - , defaultWalletId_(defaultWalletId) - , showRegularWallets_(showOnlyRegular) -{ - rootNode_ = std::make_shared(this, WalletNode::Type::Root); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletsReady, this, &WalletsViewModel::onWalletChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletChanged, this, &WalletsViewModel::onWalletChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletDeleted, this, &WalletsViewModel::onWalletChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::blockchainEvent, this, &WalletsViewModel::onWalletChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::invalidatedZCs, this, &WalletsViewModel::onWalletChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &WalletsViewModel::onWalletChanged); - connect(walletsManager_.get(), &bs::sync::WalletsManager::newWalletAdded, this, &WalletsViewModel::onNewWalletAdded); - - if (signContainer_) { - connect(signContainer_.get(), &SignContainer::QWalletInfo, this, &WalletsViewModel::onWalletInfo); - connect(signContainer_.get(), &SignContainer::Error, this, &WalletsViewModel::onHDWalletError); - connect(signContainer_.get(), &SignContainer::authenticated, this, &WalletsViewModel::onSignerAuthenticated); - connect(signContainer_.get(), &SignContainer::ready, this, &WalletsViewModel::onWalletChanged); - } -} - WalletsViewModel::WalletsViewModel(const std::string &defaultWalletId , QObject* parent, bool showOnlyRegular) : QAbstractItemModel(parent) diff --git a/BlockSettleUILib/WalletsViewModel.h b/BlockSettleUILib/WalletsViewModel.h index 70103b5cc..2220252af 100644 --- a/BlockSettleUILib/WalletsViewModel.h +++ b/BlockSettleUILib/WalletsViewModel.h @@ -93,8 +93,6 @@ class WalletsViewModel : public QAbstractItemModel { Q_OBJECT public: - [[deprecated]] WalletsViewModel(const std::shared_ptr& walletsManager, const std::string &defaultWalletId - , const std::shared_ptr &sc = nullptr, QObject *parent = nullptr, bool showOnlyRegular = false); WalletsViewModel(const std::string &defaultWalletId, QObject *parent = nullptr, bool showOnlyRegular = false); ~WalletsViewModel() noexcept override = default; diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 959df59f0..d2d25a04b 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -177,50 +177,6 @@ WalletsWidget::WalletsWidget(QWidget* parent) WalletsWidget::~WalletsWidget() = default; -void WalletsWidget::init(const std::shared_ptr &logger - , const std::shared_ptr &manager - , const std::shared_ptr &container - , const std::shared_ptr &applicationSettings - , const std::shared_ptr &connectionManager - , const std::shared_ptr &assetMgr - , const std::shared_ptr &authMgr - , const std::shared_ptr &armory) -{ - logger_ = logger; - walletsManager_ = manager; - signingContainer_ = container; - appSettings_ = applicationSettings; - assetManager_ = assetMgr; - authMgr_ = authMgr; - armory_ = armory; - connectionManager_ = connectionManager; - - // signingContainer_ might be null if user rejects remote signer key - if (signingContainer_) { - connect(signingContainer_.get(), &SignContainer::TXSigned, this, &WalletsWidget::onTXSigned); - } - - const auto &defWallet = walletsManager_->getDefaultWallet(); - InitWalletsView(defWallet ? defWallet->walletId() : std::string{}); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportFinished, [this] { walletsModel_->LoadWallets(); }); - - auto filter = appSettings_->get(ApplicationSettings::WalletFiltering); - - ui_->pushButtonEmpty->setChecked(filter & AddressSortFilterModel::HideEmpty); - ui_->pushButtonInternal->setChecked(filter & AddressSortFilterModel::HideInternal); - ui_->pushButtonExternal->setChecked(filter & AddressSortFilterModel::HideExternal); - ui_->pushButtonUsed->setChecked(filter & AddressSortFilterModel::HideUsedEmpty); - - updateAddressFilters(filter); - - for (auto button : {ui_->pushButtonEmpty, ui_->pushButtonInternal, - ui_->pushButtonExternal, ui_->pushButtonUsed}) { - connect(button, &QPushButton::toggled, this, &WalletsWidget::onFilterSettingsChanged); - } - - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletsSynchronized, this, &WalletsWidget::onWalletsSynchronized, Qt::QueuedConnection); -} - void WalletsWidget::init(const std::shared_ptr &logger) { logger_ = logger; @@ -252,12 +208,7 @@ void WalletsWidget::setUsername(const QString& username) void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) { - if (walletsManager_ && signingContainer_) { - walletsModel_ = new WalletsViewModel(walletsManager_, defaultWalletId, signingContainer_, ui_->treeViewWallets); - } - else { - walletsModel_ = new WalletsViewModel(defaultWalletId, ui_->treeViewWallets); - } + walletsModel_ = new WalletsViewModel(defaultWalletId, ui_->treeViewWallets); connect(walletsModel_, &WalletsViewModel::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); connect(walletsModel_, &WalletsViewModel::needWalletBalances, this, &WalletsWidget::needWalletBalances); @@ -519,22 +470,16 @@ void WalletsWidget::showWalletProperties(const QModelIndex& index) const auto &hdWallet = node->hdWallet(); if (!(*hdWallet.ids.cbegin()).empty()) { - if (walletsManager_ && armory_ && signingContainer_ && appSettings_ && connectionManager_ && assetManager_) { - RootWalletPropertiesDialog(logger_, hdWallet, walletsManager_, armory_, signingContainer_ - , walletsModel_, appSettings_, connectionManager_, assetManager_, this).exec(); - } - else { - rootDlg_ = new RootWalletPropertiesDialog(logger_, hdWallet, walletsModel_, this); - connect(rootDlg_, &RootWalletPropertiesDialog::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); - connect(rootDlg_, &RootWalletPropertiesDialog::needWalletBalances, this, &WalletsWidget::needWalletBalances); - connect(rootDlg_, &RootWalletPropertiesDialog::needUTXOs, this, &WalletsWidget::needUTXOs); - connect(rootDlg_, &RootWalletPropertiesDialog::needWalletDialog, this, &WalletsWidget::needWalletDialog); - connect(rootDlg_, &QDialog::finished, [this](int) { - rootDlg_->deleteLater(); - rootDlg_ = nullptr; - }); - rootDlg_->exec(); - } + rootDlg_ = new RootWalletPropertiesDialog(logger_, hdWallet, walletsModel_, this); + connect(rootDlg_, &RootWalletPropertiesDialog::needHDWalletDetails, this, &WalletsWidget::needHDWalletDetails); + connect(rootDlg_, &RootWalletPropertiesDialog::needWalletBalances, this, &WalletsWidget::needWalletBalances); + connect(rootDlg_, &RootWalletPropertiesDialog::needUTXOs, this, &WalletsWidget::needUTXOs); + connect(rootDlg_, &RootWalletPropertiesDialog::needWalletDialog, this, &WalletsWidget::needWalletDialog); + connect(rootDlg_, &QDialog::finished, [this](int) { + rootDlg_->deleteLater(); + rootDlg_ = nullptr; + }); + rootDlg_->exec(); } } diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index b6fbf1d00..fa50ad745 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -62,15 +62,6 @@ Q_OBJECT WalletsWidget(QWidget* parent = nullptr ); ~WalletsWidget() override; - [[deprecated]] void init(const std::shared_ptr &logger - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &connectionManager - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); - void init(const std::shared_ptr &logger); void setUsername(const QString& username); diff --git a/Celer b/Celer index 4106c127b..d94cdcdea 160000 --- a/Celer +++ b/Celer @@ -1 +1 @@ -Subproject commit 4106c127bef00b19e80adb575ac8fc1093a23582 +Subproject commit d94cdcdea94653ad581e4419fc74172dd0ab3b2f diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 0c986e432..1608ea9a2 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -40,7 +40,7 @@ namespace BlockSettle { class SignerClient; class WalletSignerContainer; -class SignerAdapter : public bs::message::Adapter, public HeadlessCallbackTarget +class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget { public: SignerAdapter(const std::shared_ptr & diff --git a/UnitTests/TestAuth.cpp b/UnitTests/TestAuth.cpp index 73dd7f7cc..cf5f69fa6 100644 --- a/UnitTests/TestAuth.cpp +++ b/UnitTests/TestAuth.cpp @@ -250,7 +250,7 @@ void TestAuth::SetUp() } //setup sync manager - auto inprocSigner = std::make_shared(priWallet_, envPtr_->logger()); + auto inprocSigner = std::make_shared(priWallet_, this, envPtr_->logger()); inprocSigner->Start(); syncMgr_ = std::make_shared(envPtr_->logger(), envPtr_->appSettings(), envPtr_->armoryConnection()); diff --git a/UnitTests/TestAuth.h b/UnitTests/TestAuth.h index c7f717805..a6cf88642 100644 --- a/UnitTests/TestAuth.h +++ b/UnitTests/TestAuth.h @@ -19,6 +19,7 @@ #include #include "Address.h" #include "BlockchainMonitor.h" +#include "SignContainer.h" #include "TestEnv.h" #include "AuthAddressLogic.h" @@ -104,7 +105,7 @@ class TestValidationACT : public ValidationAddressACT }; //////////////////////////////////////////////////////////////////////////////// -class TestAuth : public ::testing::Test +class TestAuth : public ::testing::Test, public SignerCallbackTarget { protected: void SetUp() override; diff --git a/UnitTests/TestCCoin.cpp b/UnitTests/TestCCoin.cpp index b6d0531e5..96dc7ac30 100644 --- a/UnitTests/TestCCoin.cpp +++ b/UnitTests/TestCCoin.cpp @@ -89,7 +89,8 @@ void TestCCoin::SetUp() } } - auto inprocSigner = std::make_shared(envPtr_->walletsMgr(), envPtr_->logger(), "", NetworkType::TestNet); + auto inprocSigner = std::make_shared(envPtr_->walletsMgr() + , envPtr_->logger(), this, "", NetworkType::TestNet); inprocSigner->Start(); syncMgr_ = std::make_shared(envPtr_->logger(), envPtr_->appSettings(), envPtr_->armoryConnection()); syncMgr_->setSignContainer(inprocSigner); diff --git a/UnitTests/TestCCoin.h b/UnitTests/TestCCoin.h index f130bc978..62b3e87f3 100644 --- a/UnitTests/TestCCoin.h +++ b/UnitTests/TestCCoin.h @@ -18,8 +18,9 @@ #include "Address.h" #include "BlockchainMonitor.h" -#include "TestEnv.h" #include "ColoredCoinLogic.h" +#include "SignContainer.h" +#include "TestEnv.h" namespace bs { namespace core { @@ -149,7 +150,7 @@ class ColoredCoinTrackerClient_UT : public ColoredCoinTrackerClient } }; -class TestCCoin : public ::testing::Test +class TestCCoin : public ::testing::Test, public SignerCallbackTarget { public: using UTXOs = std::vector; diff --git a/UnitTests/TestCCoinAsync.cpp b/UnitTests/TestCCoinAsync.cpp index a13b2c9fe..061d7f84d 100644 --- a/UnitTests/TestCCoinAsync.cpp +++ b/UnitTests/TestCCoinAsync.cpp @@ -73,7 +73,8 @@ void TestCCoinAsync::SetUp() } } - auto inprocSigner = std::make_shared(envPtr_->walletsMgr(), envPtr_->logger(), "", NetworkType::TestNet); + auto inprocSigner = std::make_shared(envPtr_->walletsMgr() + , envPtr_->logger(), this, "", NetworkType::TestNet); inprocSigner->Start(); syncMgr_ = std::make_shared(envPtr_->logger(), envPtr_->appSettings(), envPtr_->armoryConnection()); syncMgr_->setSignContainer(inprocSigner); diff --git a/UnitTests/TestCCoinAsync.h b/UnitTests/TestCCoinAsync.h index 4d362c2b7..a679cbb59 100644 --- a/UnitTests/TestCCoinAsync.h +++ b/UnitTests/TestCCoinAsync.h @@ -18,8 +18,9 @@ #include "Address.h" #include "BlockchainMonitor.h" -#include "TestEnv.h" #include "CCLogicAsync.h" +#include "SignContainer.h" +#include "TestEnv.h" namespace bs { namespace core { @@ -68,7 +69,7 @@ class AsyncCCT : public ColoredCoinTrackerAsync }; -class TestCCoinAsync : public ::testing::Test +class TestCCoinAsync : public ::testing::Test, public SignerCallbackTarget { public: using UTXOs = std::vector; diff --git a/UnitTests/TestCommon.cpp b/UnitTests/TestCommon.cpp index f7290bd61..dfa787127 100644 --- a/UnitTests/TestCommon.cpp +++ b/UnitTests/TestCommon.cpp @@ -28,6 +28,7 @@ #include "CacheFile.h" #include "CurrencyPair.h" #include "EasyCoDec.h" +#include "HeadlessContainer.h" #include "InprocSigner.h" #include "MarketDataProvider.h" #include "MDCallbacksQt.h" @@ -96,7 +97,9 @@ TEST(TestCommon, AssetManager) TestEnv env(StaticLogger::loggerPtr); env.requireAssets(); - auto inprocSigner = std::make_shared(env.walletsMgr(), StaticLogger::loggerPtr, "", NetworkType::TestNet); + auto hct = new QtHCT(nullptr); + auto inprocSigner = std::make_shared(env.walletsMgr() + , StaticLogger::loggerPtr, hct, "", NetworkType::TestNet); inprocSigner->Start(); auto syncMgr = std::make_shared(StaticLogger::loggerPtr , env.appSettings(), env.armoryConnection()); @@ -134,6 +137,7 @@ TEST(TestCommon, AssetManager) assetMgr.onMDUpdate(bs::network::Asset::PrivateMarket, QLatin1String("BLK/XBT") , { bs::network::MDField{bs::network::MDField::PriceLast, 0.023} }); EXPECT_EQ(assetMgr.getPrice("BLK"), 0.023); + delete hct; } TEST(TestCommon, UtxoReservation) diff --git a/UnitTests/TestOtc.cpp b/UnitTests/TestOtc.cpp index fe05f0f67..720a3558d 100644 --- a/UnitTests/TestOtc.cpp +++ b/UnitTests/TestOtc.cpp @@ -35,7 +35,7 @@ namespace { } // namespace -class TestPeer +class TestPeer : public SignerCallbackTarget { public: void init(TestEnv &env, const std::string &name) @@ -79,7 +79,8 @@ class TestPeer walletsMgr_->addWallet(wallet_); - signer_ = std::make_shared(walletsMgr_, env.logger(), "", NetworkType::TestNet); + signer_ = std::make_shared(walletsMgr_, env.logger(), this + , "", NetworkType::TestNet); signer_->Start(); syncWalletMgr_ = std::make_shared(env.logger() diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index e6eeee680..fccc70db2 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -142,7 +142,7 @@ void TestSettlement::SetUp() hdWallet_.push_back(hdWallet); inprocSigner_.push_back(std::make_shared( - walletsMgr_.at(i), logger, "", NetworkType::TestNet + walletsMgr_.at(i), logger, this, "", NetworkType::TestNet , [this, hdWallet](const std::string&) { return std::make_unique(hdWallet, passphrase_); })); diff --git a/UnitTests/TestSettlement.h b/UnitTests/TestSettlement.h index 45ec4551c..16c0a8100 100644 --- a/UnitTests/TestSettlement.h +++ b/UnitTests/TestSettlement.h @@ -19,6 +19,7 @@ #include #include "Address.h" #include "BlockchainMonitor.h" +#include "SignContainer.h" #include "TestEnv.h" @@ -42,7 +43,7 @@ namespace bs { } } -class TestSettlement : public ::testing::Test +class TestSettlement : public ::testing::Test, public SignerCallbackTarget { protected: TestSettlement(); diff --git a/UnitTests/TestWallet.cpp b/UnitTests/TestWallet.cpp index 1e0f710e5..f7c838ea6 100644 --- a/UnitTests/TestWallet.cpp +++ b/UnitTests/TestWallet.cpp @@ -47,7 +47,7 @@ unit tests to add: ***/ //////////////////////////////////////////////////////////////////////////////// -class TestWallet : public ::testing::Test +class TestWallet : public ::testing::Test, public SignerCallbackTarget { void SetUp() { @@ -190,7 +190,7 @@ TEST_F(TestWallet, BIP84_primary) } auto inprocSigner = std::make_shared( - envPtr_->walletsMgr(), envPtr_->logger(), "", NetworkType::TestNet); + envPtr_->walletsMgr(), envPtr_->logger(), this, "", NetworkType::TestNet); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -602,7 +602,7 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) } //create sync manager - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -704,7 +704,7 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) auto walletPtr = std::make_shared( filename, NetworkType::TestNet, "", ctrlPass, envPtr_->logger()); - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -807,7 +807,7 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) EXPECT_EQ(walletPtr->isWatchingOnly(), true); //create sync manager - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1337,7 +1337,7 @@ TEST_F(TestWallet, SyncWallet_TriggerPoolExtension) } //create sync manager - auto inprocSigner = std::make_shared(walletPtr, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1801,7 +1801,7 @@ TEST_F(TestWallet, TxIdNestedSegwit) envPtr_->requireArmory(); ASSERT_NE(envPtr_->armoryConnection(), nullptr); - auto inprocSigner = std::make_shared(coreWallet, envPtr_->logger()); + auto inprocSigner = std::make_shared(coreWallet, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); diff --git a/UnitTests/TestWalletArmory.cpp b/UnitTests/TestWalletArmory.cpp index f22b3da15..1016381a8 100644 --- a/UnitTests/TestWalletArmory.cpp +++ b/UnitTests/TestWalletArmory.cpp @@ -28,7 +28,7 @@ #include "Wallets/SyncWalletsManager.h" -class TestWalletWithArmory : public ::testing::Test +class TestWalletWithArmory : public ::testing::Test, public SignerCallbackTarget { protected: void SetUp() @@ -69,7 +69,7 @@ class TestWalletWithArmory : public ::testing::Test TEST_F(TestWalletWithArmory, AddressChainExtension) { - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -247,7 +247,7 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) std::vector intVec; { - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -453,7 +453,7 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) } //sync with db - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -584,7 +584,7 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) filename, NetworkType::TestNet); //resync address chain use, it should not disrupt current state - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); syncMgr->setSignContainer(inprocSigner); @@ -673,7 +673,7 @@ TEST_F(TestWalletWithArmory, Comments) auto changeAddr = leafPtr_->getNewChangeAddress(); ASSERT_FALSE(changeAddr.empty()); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -737,7 +737,7 @@ TEST_F(TestWalletWithArmory, ZCBalance) const auto changeAddr = leafPtr_->getNewChangeAddress(); EXPECT_EQ(leafPtr_->getUsedAddressCount(), 3); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -930,7 +930,7 @@ TEST_F(TestWalletWithArmory, SimpleTX_bech32) const auto changeAddr = leafPtr_->getNewChangeAddress(); EXPECT_EQ(leafPtr_->getUsedAddressCount(), 4); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1074,7 +1074,7 @@ TEST_F(TestWalletWithArmory, SignSettlement) } /*sync the wallet and connect to db*/ - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1186,7 +1186,7 @@ TEST_F(TestWalletWithArmory, SignSettlement) TEST_F(TestWalletWithArmory, GlobalDelegateConf) { - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1375,7 +1375,7 @@ TEST_F(TestWalletWithArmory, CallbackReturnTxCrash) auto addr = leafPtr_->getNewExtAddress(); ASSERT_FALSE(addr.empty()); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); @@ -1415,7 +1415,7 @@ TEST_F(TestWalletWithArmory, PushZC_retry) prefixed.append(CryptoPRNG::generateRandom(20)); auto otherAddr = bs::Address::fromHash(prefixed); - auto inprocSigner = std::make_shared(walletPtr_, envPtr_->logger()); + auto inprocSigner = std::make_shared(walletPtr_, this, envPtr_->logger()); inprocSigner->Start(); auto syncMgr = std::make_shared(envPtr_->logger() , envPtr_->appSettings(), envPtr_->armoryConnection()); diff --git a/common b/common index 9b3a3076a..21db9f027 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 9b3a3076a6f5eef8f1c0803b1df8a3180fb27447 +Subproject commit 21db9f027e001fdadc73e6d02617210a1b37b018 From b867184b7c74dbcc62bc607a825a0ed6dd94c2ac Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 8 Jan 2021 17:03:11 +0300 Subject: [PATCH 093/146] Updated common --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index e834a58f0..8ed86a140 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit e834a58f0fdea7e754f114f35a1c0139d56f27ab +Subproject commit 8ed86a1405c26e2175666e28a02ef4e5c32e8765 From 7473c41c43feec89c710d400543df956c12ca816 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 8 Jan 2021 21:24:03 +0300 Subject: [PATCH 094/146] Fix for settlement tests --- UnitTests/TestSettlement.cpp | 2 +- common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index fccc70db2..cf609679d 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -185,7 +185,7 @@ TEST_F(TestSettlement, Initial_balances) { return [walletId](const bs::message::Envelope& env) { - if (env.request || env.receiver || + if (env.request || (env.sender->value() != TerminalUsers::Wallets)) { return false; } diff --git a/common b/common index 21db9f027..62f253099 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 21db9f027e001fdadc73e6d02617210a1b37b018 +Subproject commit 62f25309936f05fb8d10d7990f4966efe8c28505 From d81bc6d2356b437d63cfd6d019acd27717b670cb Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 11 Jan 2021 18:02:33 +0300 Subject: [PATCH 095/146] Merged common/bs_dev --- BlockSettleUILib/Settings/GeneralSettingsPage.cpp | 10 +++++++--- common | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/BlockSettleUILib/Settings/GeneralSettingsPage.cpp b/BlockSettleUILib/Settings/GeneralSettingsPage.cpp index 8c0d67b8b..0c157c8fa 100644 --- a/BlockSettleUILib/Settings/GeneralSettingsPage.cpp +++ b/BlockSettleUILib/Settings/GeneralSettingsPage.cpp @@ -153,6 +153,8 @@ static inline QString logLevel(int level) void GeneralSettingsPage::apply() { + const auto walletId = UiUtils::getSelectedWalletId(ui_->comboBox_defaultWallet); + if (appSettings_) { appSettings_->set(ApplicationSettings::launchToTray, ui_->checkBoxLaunchToTray->isChecked()); appSettings_->set(ApplicationSettings::minimizeToTray, ui_->checkBoxMinimizeToTray->isChecked()); @@ -198,6 +200,7 @@ void GeneralSettingsPage::apply() appSettings_->set(ApplicationSettings::logMessages, logSettings); } + appSettings_->setDefaultWalletId(walletId); } else { // don't update local settings_ yet - the update will arrive explicitly emit putSetting(ApplicationSettings::launchToTray, ui_->checkBoxLaunchToTray->isChecked()); @@ -209,6 +212,10 @@ void GeneralSettingsPage::apply() emit putSetting(ApplicationSettings::DetailedSettlementTxDialogByDefault , ui_->detailedSettlementTxDialogByDefaultCheckBox->isChecked()); + const auto netType = static_cast(settings_.at(ApplicationSettings::netType).toInt()); + emit putSetting((netType == NetworkType::TestNet) ? ApplicationSettings::DefaultXBTTradeWalletIdTestnet + : ApplicationSettings::DefaultXBTTradeWalletIdMainnet, QString::fromStdString(walletId)); + const auto cfgLog = ApplicationSettings::parseLogConfig(settings_.at( ApplicationSettings::logDefault).toStringList()); { @@ -241,9 +248,6 @@ void GeneralSettingsPage::apply() emit putSetting(ApplicationSettings::logMessages, logSettings); } } - - const auto walletId = UiUtils::getSelectedWalletId(ui_->comboBox_defaultWallet); - appSettings_->setDefaultWalletId(walletId); } void GeneralSettingsPage::onSelectLogFile() diff --git a/common b/common index 62f253099..cc3609009 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 62f25309936f05fb8d10d7990f4966efe8c28505 +Subproject commit cc36090098a01953e6217819532c6c895806da04 From 1e665b65580e14ae26ba857d43f171e9a258e806 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 12 Jan 2021 16:38:48 +0300 Subject: [PATCH 096/146] Fix crash --- BlockSettleUILib/CreateTransactionDialog.cpp | 12 ++++++++---- common | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index 419a0f972..b85b6480b 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -438,15 +438,19 @@ void CreateTransactionDialog::onTXSigned(unsigned int id, BinaryData signedTX, b void CreateTransactionDialog::startBroadcasting() { broadcasting_ = true; - pushButtonCreate()->setEnabled(false); - pushButtonCreate()->setText(tr("Waiting for TX signing...")); + QMetaObject::invokeMethod(this, [this] { + pushButtonCreate()->setEnabled(false); + pushButtonCreate()->setText(tr("Waiting for TX signing...")); + }); } void CreateTransactionDialog::stopBroadcasting() { broadcasting_ = false; - pushButtonCreate()->setEnabled(true); - updateCreateButtonText(); + QMetaObject::invokeMethod(this, [this] { + pushButtonCreate()->setEnabled(true); + updateCreateButtonText(); + }); } bool CreateTransactionDialog::BroadcastImportedTx() diff --git a/common b/common index 8ed86a140..b494c32a3 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 8ed86a1405c26e2175666e28a02ef4e5c32e8765 +Subproject commit b494c32a3ebc28e23fdbb5e85c1bdf49e7ec00fe From a4a4a0b62ce1062daeb55bfccb72c611f7de7bab Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Tue, 12 Jan 2021 17:56:50 +0300 Subject: [PATCH 097/146] Add profit/loss calculation --- BlockSettleUILib/Trading/FuturesTicket.cpp | 29 +++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 41ab2dc9b..710d1c88f 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -233,15 +233,32 @@ void FuturesTicket::productSelectionChanged() void FuturesTicket::updatePanel() { - // currentProduct_ will be set to EURP or EURD, query EUR instead - auto currentProduct = "EUR"; + // currentProduct_ will be set to EURP or EURD const double balance = assetManager_ ? - assetManager_->getBalance(currentProduct, false, nullptr) : 0.0; + assetManager_->getBalance(currentProduct_, false, nullptr) : 0.0; + auto currentProduct = "EUR"; auto amountString = UiUtils::displayCurrencyAmount(balance); QString text = tr("%1 %2").arg(amountString).arg(QString::fromStdString(currentProduct)); ui_->labelBalanceValue->setText(text); - ui_->labelFutureBalanceValue->setText(UiUtils::displayAmount(assetManager_->netDeliverableBalanceXbt())); + int64_t futuresXbtAmount = currentProduct_ == "EURD" ? + assetManager_->futuresXbtAmountDeliverable() : assetManager_->futuresXbtAmountCashSettled(); + ui_->labelFutureBalanceValue->setText(UiUtils::displayAmount(futuresXbtAmount)); + double futuresBalance = currentProduct_ == "EURD" ? + assetManager_->futuresBalanceDeliverable() : assetManager_->futuresBalanceCashSettled(); + const auto &mdInfos = mdInfo_[type_][security_.toStdString()]; + // TODO: Use current price from the server + double currentPrice = 0; + for (const auto &item : mdInfos) { + if (item.second.askPrice != 0 && item.second.bidPrice != 0) { + currentPrice = (item.second.askPrice + item.second.bidPrice) / 2; + break; + } + }; + double profitLoss = assetManager_->profitLoss(futuresXbtAmount, futuresBalance, currentPrice); + auto profitLossString = UiUtils::displayCurrencyAmount(profitLoss); + QString profitLossText = tr("%1 %2").arg(profitLossString).arg(QString::fromStdString(currentProduct)); + ui_->labelProfitLoss->setText(profitLossText); double qty = getQuantity(); size_t selectedLine = getSelectedLine(qty); @@ -265,7 +282,8 @@ void FuturesTicket::updatePanel() void FuturesTicket::onCloseAll() { - auto netBalance = assetManager_->netDeliverableBalanceXbt(); + int64_t netBalance = currentProduct_ == "EURD" ? + assetManager_->futuresXbtAmountDeliverable() : assetManager_->futuresXbtAmountCashSettled(); auto amount = bs::XBTAmount(static_cast(std::abs(netBalance))); auto side = netBalance < 0 ? bs::network::Side::Buy : bs::network::Side::Sell; sendRequest(side, amount); @@ -344,7 +362,6 @@ void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &sec if (field.levelQuantity == kInfValueStr) { amount = kInfValueAmount; } else { - double dValue = field.levelQuantity.toDouble(); amount = bs::XBTAmount(field.levelQuantity.toDouble()); } auto &mdInfo = mdInfos[amount]; From 17fdaa869c31fe3494ab95e82ac143c75367736a Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Wed, 13 Jan 2021 15:46:48 +0300 Subject: [PATCH 098/146] Update futures confirm/error message box text --- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 0e24344c6..d7550ef0f 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -387,13 +387,18 @@ void RFQRequestWidget::processFutureResponse(const ProxyTerminalPb::Response_Fut { QMetaObject::invokeMethod(this, [this, msg] { if (!msg.success()) { - BSMessageBox errorMessage(BSMessageBox::critical, tr("Error") - , tr("Request failed: %1").arg(QString::fromStdString(msg.error_msg())), this); + BSMessageBox errorMessage(BSMessageBox::critical, tr("Order message") + , tr("Trade rejected"), QString::fromStdString(msg.error_msg()), this); errorMessage.exec(); return; } - auto details = tr("Price: %1").arg(UiUtils::displayPriceXBT(msg.price())); - BSMessageBox errorMessage(BSMessageBox::info, tr("Success"), tr("Order succeed"), details, this); + auto productStr = QString::fromStdString(msg.product()); + auto sideStr = msg.side() == bs::types::Side::SIDE_SELL ? tr("Sell") : tr("Buy"); + auto amountStr = UiUtils::displayAmount(msg.amount()); + auto priceStr = UiUtils::displayPriceXBT(msg.price()); + auto details = tr("Product: %1\nSide: Buy / Sell\nVolume: Amount\nPrice: Price") + .arg(productStr).arg(sideStr).arg(amountStr).arg(priceStr); + BSMessageBox errorMessage(BSMessageBox::info, tr("Order message"), tr("Order confirmation"), details, this); errorMessage.exec(); }); } From 8213895a2f1f0a70374813043f782e4fe1370f68 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Wed, 13 Jan 2021 17:59:31 +0300 Subject: [PATCH 099/146] Add delivery notification message --- BlockSettleUILib/BSTerminalMainWindow.cpp | 25 +++++++++++++++++++ BlockSettleUILib/BSTerminalMainWindow.h | 1 + BlockSettleUILib/Trading/RFQRequestWidget.cpp | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 4fcec2481..1eab142b4 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -235,6 +235,9 @@ void BSTerminalMainWindow::onMessageFromPB(const ProxyTerminalPb::Response& resp case ProxyTerminalPb::Response::kDeliveryAddress: processSetDeliveryAddr(response.delivery_address()); break; + case ProxyTerminalPb::Response::kDeliveryObligationUpdate: + processDeliveryObligationUpdate(response.delivery_obligation_update()); + break; default: break; } @@ -2527,6 +2530,28 @@ void BSTerminalMainWindow::processSetDeliveryAddr(const ProxyTerminalPb::Respons }); } +void BSTerminalMainWindow::processDeliveryObligationUpdate(const ProxyTerminalPb::Response_DeliveryObligationsRequest &resp) +{ + auto amountStr = UiUtils::displayAmount(resp.to_deliver()); + switch (resp.status()) { + case ProxyTerminalPb::Response_DeliveryObligationsRequest::PENDING: + addDeferredDialog([this, amountStr] { + auto details = tr("Volume: %1").arg(amountStr); + BSMessageBox errorMessage(BSMessageBox::info, tr("Delivery"), tr("Delivery requested"), details, this); + }); + break; + case ProxyTerminalPb::Response_DeliveryObligationsRequest::DELIVERED: + addDeferredDialog([this, amountStr] { + auto details = tr("Volume: %1").arg(amountStr); + BSMessageBox errorMessage(BSMessageBox::info, tr("Delivery"), tr("Delivery detected"), details, this); + }); + break; + default: + SPDLOG_LOGGER_ERROR(logMgr_->logger(), "unexpected DeliveryObligationsRequest status"); + break; + } +} + void BSTerminalMainWindow::onAuthLeafCreated() { auto authWallet = walletsMgr_->getAuthWallet(); diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index f611edffe..30546282e 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -311,6 +311,7 @@ private slots: void onBootstrapDataLoaded(const std::string& data); void processSetDeliveryAddr(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryAddress &resp); + void processDeliveryObligationUpdate(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &resp); private: enum class ChatInitState diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index d7550ef0f..21d7e0d9a 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -396,7 +396,7 @@ void RFQRequestWidget::processFutureResponse(const ProxyTerminalPb::Response_Fut auto sideStr = msg.side() == bs::types::Side::SIDE_SELL ? tr("Sell") : tr("Buy"); auto amountStr = UiUtils::displayAmount(msg.amount()); auto priceStr = UiUtils::displayPriceXBT(msg.price()); - auto details = tr("Product: %1\nSide: Buy / Sell\nVolume: Amount\nPrice: Price") + auto details = tr("Product: %1\nSide: %2\nVolume: %3\nPrice: %4") .arg(productStr).arg(sideStr).arg(amountStr).arg(priceStr); BSMessageBox errorMessage(BSMessageBox::info, tr("Order message"), tr("Order confirmation"), details, this); errorMessage.exec(); From 8c9d2b4330497aba7e59f82cdbb67dc2152d5e06 Mon Sep 17 00:00:00 2001 From: Ation Date: Thu, 14 Jan 2021 13:42:14 +0200 Subject: [PATCH 100/146] Move settlement in blotter --- BlockSettleUILib/OrderListModel.cpp | 6 +++--- BlockSettleUILib/OrderListModel.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 3432bc12b..24e10d5d2 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -664,9 +664,9 @@ void OrderListModel::reset() { beginResetModel(); groups_.clear(); - unsettled_ = std::make_unique(StatusGroup::toString(StatusGroup::UnSettled), 0); - settled_ = std::make_unique(StatusGroup::toString(StatusGroup::Settled), 1); - pendingFuturesSettlement_ = std::make_unique(StatusGroup::toString(StatusGroup::PendingSettlements), 2); + pendingFuturesSettlement_ = std::make_unique(StatusGroup::toString(StatusGroup::PendingSettlements), 0); + unsettled_ = std::make_unique(StatusGroup::toString(StatusGroup::UnSettled), 1); + settled_ = std::make_unique(StatusGroup::toString(StatusGroup::Settled), 2); endResetModel(); } diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 6842a4e74..00b3a01d1 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -238,9 +238,9 @@ public slots: enum Type { first = 0, - UnSettled = first, + PendingSettlements = first, + UnSettled, Settled, - PendingSettlements, Undefined }; From 670a3a90043cc0dee798df0a6b8704da3580868b Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 15 Jan 2021 16:22:43 +0300 Subject: [PATCH 101/146] Tiny UI fix for the future delivery confirmation --- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp index 21d7e0d9a..581820dcf 100644 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ b/BlockSettleUILib/Trading/RFQRequestWidget.cpp @@ -392,11 +392,11 @@ void RFQRequestWidget::processFutureResponse(const ProxyTerminalPb::Response_Fut errorMessage.exec(); return; } - auto productStr = QString::fromStdString(msg.product()); + auto productStr = QString::fromStdString(msg.product().empty() ? "" : msg.product()); auto sideStr = msg.side() == bs::types::Side::SIDE_SELL ? tr("Sell") : tr("Buy"); auto amountStr = UiUtils::displayAmount(msg.amount()); auto priceStr = UiUtils::displayPriceXBT(msg.price()); - auto details = tr("Product: %1\nSide: %2\nVolume: %3\nPrice: %4") + auto details = tr("Product:\t%1\nSide:\t%2\nVolume:\t%3\nPrice:\t%4") .arg(productStr).arg(sideStr).arg(amountStr).arg(priceStr); BSMessageBox errorMessage(BSMessageBox::info, tr("Order message"), tr("Order confirmation"), details, this); errorMessage.exec(); From 61ea42e238b690636ece8ac75181e40be188e183 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 15 Jan 2021 16:52:34 +0300 Subject: [PATCH 102/146] Fix DeliveryObligation message box --- BlockSettleUILib/BSTerminalMainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 1eab142b4..79fd1a0c3 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2537,13 +2537,13 @@ void BSTerminalMainWindow::processDeliveryObligationUpdate(const ProxyTerminalPb case ProxyTerminalPb::Response_DeliveryObligationsRequest::PENDING: addDeferredDialog([this, amountStr] { auto details = tr("Volume: %1").arg(amountStr); - BSMessageBox errorMessage(BSMessageBox::info, tr("Delivery"), tr("Delivery requested"), details, this); + BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery requested"), details, this).exec(); }); break; case ProxyTerminalPb::Response_DeliveryObligationsRequest::DELIVERED: addDeferredDialog([this, amountStr] { auto details = tr("Volume: %1").arg(amountStr); - BSMessageBox errorMessage(BSMessageBox::info, tr("Delivery"), tr("Delivery detected"), details, this); + BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery detected"), details, this).exec(); }); break; default: From 1b86f8d3066a8b43d51db6f386ae5fe8cb969a7d Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 15 Jan 2021 17:33:01 +0300 Subject: [PATCH 103/146] Fix create delivery tx --- BlockSettleUILib/OrderListModel.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 24e10d5d2..42a2d57d9 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -971,11 +971,7 @@ bool OrderListModel::DeliveryRequired(const QModelIndex &index) bool OrderListModel::isFutureDeliveryIndex(const QModelIndex &index) const { - if (!index.isValid()) { - return false; - } - - if (index.column() != Header::Status) { + if (!index.isValid() || index.column() != Header::Status || !pendingFuturesSettlement_) { return false; } @@ -987,7 +983,7 @@ bool OrderListModel::isFutureDeliveryIndex(const QModelIndex &index) const ++depth; } - if (statusGroupIndex.row() != 2 || depth != 2) { + if (statusGroupIndex.row() != pendingFuturesSettlement_->row_ || depth != 2) { return false; } } From c98b3127c08c01b9447fdf267629cfded02521cd Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 18 Jan 2021 19:39:02 +0300 Subject: [PATCH 104/146] Removed more unused code --- BlockSettleUILib/AddressDetailDialog.cpp | 125 --- BlockSettleUILib/AddressDetailDialog.h | 11 - BlockSettleUILib/AddressListModel.cpp | 3 + BlockSettleUILib/PortfolioWidget.cpp | 6 +- BlockSettleUILib/TransactionDetailDialog.cpp | 218 ----- BlockSettleUILib/TransactionDetailDialog.h | 2 - BlockSettleUILib/TransactionsViewModel.cpp | 746 +----------------- BlockSettleUILib/TransactionsViewModel.h | 35 +- BlockSettleUILib/TransactionsWidget.cpp | 18 +- .../TransactionsWidgetInterface.cpp | 9 - BlockSettleUILib/WalletsWidget.cpp | 4 +- GUI/QtWidgets/QtGuiAdapter.cpp | 1 + common | 2 +- 13 files changed, 19 insertions(+), 1161 deletions(-) diff --git a/BlockSettleUILib/AddressDetailDialog.cpp b/BlockSettleUILib/AddressDetailDialog.cpp index bb3cced2f..dde6f4979 100644 --- a/BlockSettleUILib/AddressDetailDialog.cpp +++ b/BlockSettleUILib/AddressDetailDialog.cpp @@ -87,96 +87,6 @@ class AddressTransactionFilter : public QSortFilterProxyModel }; -AddressDetailDialog::AddressDetailDialog(const bs::Address& address - , const std::shared_ptr &wallet - , const std::shared_ptr& walletsManager - , const std::shared_ptr &armory - , const std::shared_ptr &logger, QWidget* parent) - : QDialog(parent) - , ui_(new Ui::AddressDetailDialog()) - , logger_(logger) - , address_(address) - , walletsManager_(walletsManager) - , armory_(armory) - , wallet_(wallet) -{ - ui_->setupUi(this); - ui_->labelError->hide(); - - auto balanceVec = wallet_->getAddrBalance(address); - onAddrBalanceReceived(balanceVec); - - onAddrTxNReceived(wallet_->getAddrTxN(address)); - - auto copyButton = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); - connect(copyButton, &QPushButton::clicked, this, &AddressDetailDialog::onCopyClicked); - - ui_->labelWalletName->setText(QString::fromStdString(wallet_->name())); - - const auto addressString = QString::fromStdString(address.display()); - ui_->labelAddress->setText(addressString); - - const auto addrIndex = wallet_->getAddressIndex(address); - const auto path = bs::hd::Path::fromString(addrIndex); - QString index; - if (path.length() != 2) { - index = QString::fromStdString(addrIndex); - } - else { - const auto lastIndex = QString::number(path.get(-1)); - switch (path.get(-2)) { - case 0: - index = tr("External/%1").arg(lastIndex); - break; - case 1: - index = tr("Internal/%1").arg(lastIndex); - break; - default: - index = tr("Unknown/%1").arg(lastIndex); - } - } - if (index.length() > 64) { - index = index.left(64); - } - ui_->labelAddrIndex->setText(index); - - const auto comment = wallet_->getAddressComment(address); - ui_->labelComment->setText(QString::fromStdString(comment)); - - ui_->inputAddressesWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->outputAddressesWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - - if (armory_->state() != ArmoryState::Ready) { - ui_->labelError->setText(tr("BlockSettleDB is not connected")); - onError(); - } - else { - QPointer thisPtr = this; - const auto &cbLedgerDelegate = [thisPtr](const std::shared_ptr &delegate) { - QMetaObject::invokeMethod(qApp, [thisPtr, delegate]{ - if (thisPtr) { - thisPtr->initModels(delegate); - } - }); - }; - if (!wallet_->getLedgerDelegateForAddress(address_, cbLedgerDelegate)) { - ui_->labelError->setText(tr("Error loading address info")); - onError(); - } - } - - const QString addrURI = QLatin1String("bitcoin:") + addressString; - ui_->labelQR->setPixmap(UiUtils::getQRCode(addrURI, 128)); - - ui_->inputAddressesWidget->setContextMenuPolicy(Qt::CustomContextMenu); - ui_->outputAddressesWidget->setContextMenuPolicy(Qt::CustomContextMenu); - - connect(ui_->inputAddressesWidget, &QTreeView::customContextMenuRequested, - this, &AddressDetailDialog::onInputAddrContextMenu); - connect(ui_->outputAddressesWidget, &QTreeView::customContextMenuRequested, - this, &AddressDetailDialog::onOutputAddrContextMenu); -} - AddressDetailDialog::AddressDetailDialog(const bs::Address& address , const std::shared_ptr &logger, bs::core::wallet::Type wltType , uint64_t balance, uint32_t txn, const QString &walletName @@ -274,41 +184,6 @@ void AddressDetailDialog::onTXDetails(const std::vectoronTXDetails(details); } -void AddressDetailDialog::initModels(const std::shared_ptr &delegate) -{ - TransactionsViewModel* model = new TransactionsViewModel(armory_ - , walletsManager_ - , delegate - , logger_ - , wallet_ - , address_ - , this); - - IncomingTransactionFilter* incomingFilter = new IncomingTransactionFilter(this); - incomingFilter->setSourceModel(model); - AddressTransactionFilter* inFilter = new AddressTransactionFilter(this); - inFilter->setSourceModel(incomingFilter); - ui_->inputAddressesWidget->setModel(inFilter); - ui_->inputAddressesWidget->sortByColumn(static_cast(TransactionsViewModel::Columns::Date), Qt::DescendingOrder); - - OutgoingTransactionFilter* outgoingFilter = new OutgoingTransactionFilter(this); - outgoingFilter->setSourceModel(model); - AddressTransactionFilter* outFilter = new AddressTransactionFilter(this); - outFilter->setSourceModel(outgoingFilter); - ui_->outputAddressesWidget->setModel(outFilter); - ui_->outputAddressesWidget->sortByColumn(static_cast(TransactionsViewModel::Columns::Date), Qt::DescendingOrder); -} - -void AddressDetailDialog::onAddrBalanceReceived(const std::vector &balance) -{ - if (balance.empty()) { - ui_->labelBalance->setText(QString::number(0)); - return; - } - ui_->labelBalance->setText((wallet_->type() == bs::core::wallet::Type::ColorCoin) - ? UiUtils::displayCCAmount(balance[0]) : UiUtils::displayAmount(balance[0])); -} - void AddressDetailDialog::setBalance(uint64_t balance, bs::core::wallet::Type wltType) { ui_->labelBalance->setText((wltType == bs::core::wallet::Type::ColorCoin) diff --git a/BlockSettleUILib/AddressDetailDialog.h b/BlockSettleUILib/AddressDetailDialog.h index 101f3049e..644b47953 100644 --- a/BlockSettleUILib/AddressDetailDialog.h +++ b/BlockSettleUILib/AddressDetailDialog.h @@ -38,12 +38,6 @@ class AddressDetailDialog : public QDialog Q_OBJECT public: - [[deprecated]] AddressDetailDialog(const bs::Address &address - , const std::shared_ptr &wallet - , const std::shared_ptr& walletsManager - , const std::shared_ptr &armory - , const std::shared_ptr &logger - , QWidget* parent = nullptr ); AddressDetailDialog(const bs::Address &, const std::shared_ptr & , bs::core::wallet::Type, uint64_t balance, uint32_t txn , const QString &walletName, const std::string &addrIndex @@ -60,7 +54,6 @@ Q_OBJECT private slots: void onCopyClicked() const; - void onAddrBalanceReceived(const std::vector &); //deprecated void onAddrTxNReceived(uint32_t); void onInputAddrContextMenu(const QPoint &pos); void onOutputAddrContextMenu(const QPoint &pos); @@ -68,15 +61,11 @@ private slots: private: void setBalance(uint64_t, bs::core::wallet::Type); void onError(); - [[deprecated]] void initModels(const std::shared_ptr &); private: std::unique_ptr ui_; const bs::Address address_; std::shared_ptr logger_; - std::shared_ptr walletsManager_; - std::shared_ptr armory_; - std::shared_ptr wallet_; TransactionsViewModel *model_{ nullptr }; }; diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index 979926f65..865ffd57a 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -228,6 +228,9 @@ void AddressListModel::onAddresses(const std::string & } return false; }); + if (itWallet == wallets_.cend()) { + return; + } auto row = createRow(addr.address, *itWallet); row.index = QString::fromStdString(addr.index); row.addrIndex = addressRows_.size(); diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index d13502956..787117480 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -130,8 +130,8 @@ void PortfolioWidget::showTransactionDetails(const QModelIndex& index) return; } - TransactionDetailDialog transactionDetailDialog(txItem, walletsManager_, armory_, this); - transactionDetailDialog.exec(); +//FIXME: TransactionDetailDialog transactionDetailDialog(txItem, walletsManager_, armory_, this); +// transactionDetailDialog.exec(); } } @@ -167,7 +167,7 @@ void PortfolioWidget::showContextMenu(const QPoint &point) if (txNode->item()->isPayin()) { actionRevoke_->setData(sourceIndex); - actionRevoke_->setEnabled(model_->isTxRevocable(txNode->item()->tx)); +// actionRevoke_->setEnabled(model_->isTxRevocable(txNode->item()->tx)); contextMenu_.addAction(actionRevoke_); } else { diff --git a/BlockSettleUILib/TransactionDetailDialog.cpp b/BlockSettleUILib/TransactionDetailDialog.cpp index e114c01e0..41dbd04cb 100644 --- a/BlockSettleUILib/TransactionDetailDialog.cpp +++ b/BlockSettleUILib/TransactionDetailDialog.cpp @@ -28,224 +28,6 @@ #include -TransactionDetailDialog::TransactionDetailDialog(const TransactionPtr &tvi - , const std::shared_ptr &walletsManager - , const std::shared_ptr &armory, QWidget* parent) - : QDialog(parent) - , ui_(new Ui::TransactionDetailDialog()) - , walletsManager_(walletsManager) -{ - ui_->setupUi(this); - itemSender_ = new QTreeWidgetItem(QStringList(tr("Sender"))); - itemReceiver_ = new QTreeWidgetItem(QStringList(tr("Receiver"))); - - const auto &cbInit = [this, armory, handle = validityFlag_.handle()](const TransactionPtr &item) mutable { - ValidityGuard guard(handle); - if (!handle.isValid()) { - return; - } - ui_->labelAmount->setText(item->amountStr); - ui_->labelDirection->setText(tr(bs::sync::Transaction::toString(item->direction))); - ui_->labelAddress->setText(item->mainAddress); - - if (item->confirmations > 0) { - ui_->labelHeight->setText(QString::number(item->txEntry.blockNum)); - } - else { - if (item->txEntry.isRBF) { - ui_->labelFlag->setText(tr("RBF eligible")); - } else if (item->isCPFP) { - ui_->labelFlag->setText(tr("CPFP eligible")); - } - } - - if (item->tx.isInitialized()) { - ui_->labelSize->setText(QString::number(item->tx.getTxWeight())); - - std::set txHashSet; - std::map> txOutIndices; - - for (size_t i = 0; i < item->tx.getNumTxIn(); ++i) { - TxIn in = item->tx.getTxInCopy(i); - OutPoint op = in.getOutPoint(); - txHashSet.insert(op.getTxHash()); - txOutIndices[op.getTxHash()].insert(op.getTxOutIndex()); - } - - auto cbTXs = [this, item, txOutIndices, handle] - (const AsyncClient::TxBatchResult &txs, std::exception_ptr exPtr) mutable - { - ValidityGuard guard(handle); - if (!handle.isValid()) { - return; - } - - if (exPtr != nullptr) { - ui_->labelComment->setText(tr("Failed to get TX details")); - } - - ui_->treeAddresses->addTopLevelItem(itemSender_); - ui_->treeAddresses->addTopLevelItem(itemReceiver_); - - for (const auto &wallet : item->wallets) { - if (wallet->type() == bs::core::wallet::Type::ColorCoin) { - ccLeaf_ = std::dynamic_pointer_cast(wallet); - break; - } - } - - if (!ccLeaf_) { - for (size_t i = 0; i < item->tx.getNumTxOut(); ++i) { - const TxOut out = item->tx.getTxOutCopy(i); - const auto txType = out.getScriptType(); - if (txType == TXOUT_SCRIPT_OPRETURN || txType == TXOUT_SCRIPT_NONSTANDARD) { - continue; - } - const auto addr = bs::Address::fromTxOut(out); - const auto addressWallet = walletsManager_->getWalletByAddress(addr); - if (addressWallet && (addressWallet->type() == bs::core::wallet::Type::ColorCoin)) { - ccLeaf_ = std::dynamic_pointer_cast(addressWallet); - break; - } - } - } - if (ccLeaf_) { - ui_->labelFlag->setText(tr("CC: %1").arg(ccLeaf_->displaySymbol())); - } - - uint64_t value = 0; - bool initialized = true; - - std::set inputWallets; - - const bool isInternalTx = item->direction == bs::sync::Transaction::Internal; - for (const auto &prevTx : txs) { - if (!prevTx.second || !prevTx.second->isInitialized()) { - continue; - } - const auto &itTxOut = txOutIndices.find(prevTx.first); - if (itTxOut == txOutIndices.end()) { - continue; - } - for (const auto &txOutIdx : itTxOut->second) { - TxOut prevOut = prevTx.second->getTxOutCopy(txOutIdx); - value += prevOut.getValue(); - const auto addr = bs::Address::fromTxOut(prevOut); - const auto addressWallet = walletsManager_->getWalletByAddress(addr); - if (addressWallet) { - const auto &rootWallet = walletsManager_->getHDRootForLeaf(addressWallet->walletId()); - if (rootWallet) { - auto xbtGroup = rootWallet->getGroup(rootWallet->getXBTGroupType()); - bool isXbtLeaf = false; - - if (xbtGroup) { - const auto &xbtLeaves = xbtGroup->getLeaves(); - for (const auto &leaf : xbtLeaves) { - if (*leaf == *addressWallet) { - isXbtLeaf = true; - break; - } - } - - if (isXbtLeaf) { - inputWallets.insert(xbtLeaves.cbegin(), xbtLeaves.cend()); - } - } - - if (!isXbtLeaf) { - inputWallets.insert(addressWallet); - } - } - else { - inputWallets.insert(addressWallet); - } - if (!ccLeaf_ && (addressWallet->type() == bs::core::wallet::Type::ColorCoin)) { - ccLeaf_ = std::dynamic_pointer_cast(addressWallet); - } - } - addAddress(prevOut, false, prevTx.first, {}); - } - } - - std::vector allOutputs; - for (size_t i = 0; i < item->tx.getNumTxOut(); ++i) { - const TxOut out = item->tx.getTxOutCopy(i); - value -= out.getValue(); - allOutputs.push_back(out); - } - - for (size_t i = 0; i < item->tx.getNumTxOut(); ++i) { - const TxOut out = item->tx.getTxOutCopy(i); - addAddress(out, true, item->tx.getThisHash(), inputWallets - , allOutputs); - } - - if (!item->wallets.empty()) { - std::string comment; - for (const auto &wallet : item->wallets) { - comment = wallet->getTransactionComment(item->tx.getThisHash()); - if (!comment.empty()) { - break; - } - } - ui_->labelComment->setText(QString::fromStdString(comment)); - } - - if (initialized) { - ui_->labelFee->setText(UiUtils::displayAmount(value)); - ui_->labelSb->setText( - QString::number((float)value / (float)item->tx.getTxWeight())); - } - - ui_->treeAddresses->expandItem(itemSender_); - ui_->treeAddresses->expandItem(itemReceiver_); - - for (int i = 0; i < ui_->treeAddresses->columnCount(); ++i) { - ui_->treeAddresses->resizeColumnToContents(i); - ui_->treeAddresses->setColumnWidth(i, - ui_->treeAddresses->columnWidth(i) + extraTreeWidgetColumnMargin); - } - adjustSize(); - }; - if (txHashSet.empty()) { - cbTXs({}, nullptr); - } - else { - armory->getTXsByHash(txHashSet, cbTXs, true); - } - } - - ui_->labelConfirmations->setText(QString::number(item->confirmations)); - }; - TransactionsViewItem::initialize(tvi, armory.get(), walletsManager, cbInit); - - bool bigEndianHash = true; - ui_->labelHash->setText(QString::fromStdString(tvi->txEntry.txHash.toHexStr(bigEndianHash))); - ui_->labelTime->setText(UiUtils::displayDateTime(QDateTime::fromTime_t(tvi->txEntry.txTime))); - - ui_->labelWalletName->setText(tvi->walletName.isEmpty() ? tr("Unknown") : tvi->walletName); - - /* disabled the context menu for copy to clipboard functionality, it can be removed later - ui_->treeAddresses->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui_->treeAddresses, &QTreeView::customContextMenuRequested, [=](const QPoint& p) { - const auto address = ui_->treeAddresses->itemAt(p)->data(0, Qt::UserRole).toString(); - - if (!address.isEmpty()) { - QMenu* menu = new QMenu(this); - QAction* copyAction = menu->addAction(tr("&Copy Address")); - connect(copyAction, &QAction::triggered, [=]() { - qApp->clipboard()->setText(address); - }); - menu->popup(ui_->treeAddresses->mapToGlobal(p)); - } - });*/ - // allow address column to be copied to clipboard with right click - ui_->treeAddresses->copyToClipboardColumns_.append(2); - - setMinimumHeight(minHeightAtRendering); - resize(minimumSize()); -} - TransactionDetailDialog::TransactionDetailDialog(const TransactionPtr &txi , QWidget* parent) : QDialog(parent) diff --git a/BlockSettleUILib/TransactionDetailDialog.h b/BlockSettleUILib/TransactionDetailDialog.h index f3c4957cd..e9c91a7fa 100644 --- a/BlockSettleUILib/TransactionDetailDialog.h +++ b/BlockSettleUILib/TransactionDetailDialog.h @@ -42,8 +42,6 @@ class TransactionDetailDialog : public QDialog Q_OBJECT public: - [[deprecated]] TransactionDetailDialog(const TransactionPtr &tvi, const std::shared_ptr & - , const std::shared_ptr &, QWidget* parent = nullptr); TransactionDetailDialog(const TransactionPtr &, QWidget* parent = nullptr); ~TransactionDetailDialog() override; virtual QSize minimumSizeHint() const override; diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index e2ae826ca..41b3aa3aa 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -269,39 +269,6 @@ unsigned int TXNode::level() const } -TransactionsViewModel::TransactionsViewModel(const std::shared_ptr &armory - , const std::shared_ptr &walletsManager - , const std::shared_ptr &ledgerDelegate - , const std::shared_ptr &logger - , const std::shared_ptr &defWlt - , const bs::Address &filterAddress - , QObject* parent) - : QAbstractItemModel(parent) - , logger_(logger) - , ledgerDelegate_(ledgerDelegate) - , walletsManager_(walletsManager) - , defaultWallet_(defWlt) - , allWallets_(false) - , filterAddress_(filterAddress) -{ - init(); - ArmoryCallbackTarget::init(armory.get()); - loadLedgerEntries(); -} - -TransactionsViewModel::TransactionsViewModel(const std::shared_ptr &armory - , const std::shared_ptr &walletsManager - , const std::shared_ptr &logger - , QObject* parent) - : QAbstractItemModel(parent) - , logger_(logger) - , walletsManager_(walletsManager) - , allWallets_(true) -{ - ArmoryCallbackTarget::init(armory.get()); - init(); -} - TransactionsViewModel::TransactionsViewModel(const std::shared_ptr &logger , QObject* parent) : QAbstractItemModel(parent), logger_(logger) @@ -311,67 +278,11 @@ TransactionsViewModel::TransactionsViewModel(const std::shared_ptr(false); - qRegisterMetaType(); - qRegisterMetaType(); - - rootNode_.reset(new TXNode); - - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletChanged, this, &TransactionsViewModel::refresh, Qt::QueuedConnection); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletDeleted, this, &TransactionsViewModel::onWalletDeleted, Qt::QueuedConnection); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportFinished, this, &TransactionsViewModel::refresh, Qt::QueuedConnection); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletsReady, this, &TransactionsViewModel::updatePage, Qt::QueuedConnection); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &TransactionsViewModel::onRefreshTxValidity, Qt::QueuedConnection); - - // Need this to be able mark invalid CC TXs in red - connect(walletsManager_.get(), &bs::sync::WalletsManager::ccTrackerReady, this, &TransactionsViewModel::onRefreshTxValidity, Qt::QueuedConnection); -} - TransactionsViewModel::~TransactionsViewModel() noexcept { - cleanup(); *stopped_ = true; } -void TransactionsViewModel::onNewBlock(unsigned int, unsigned int) -{ - QMetaObject::invokeMethod(this, [this] { - if (allWallets_) { - loadAllWallets(true); - } - }); -} - -void TransactionsViewModel::loadAllWallets(bool onNewBlock) -{ - const auto &cbWalletsLD = [this, onNewBlock](const std::shared_ptr &delegate) { - if (!initialLoadCompleted_) { - if (onNewBlock && logger_) { - logger_->debug("[TransactionsViewModel::loadAllWallets] previous loading is not complete, yet"); - } - return; - } - ledgerDelegate_ = delegate; - if (onNewBlock && logger_) { - logger_->debug("[TransactionsViewModel::loadAllWallets] ledger delegate is updated"); - } - loadLedgerEntries(onNewBlock); - }; - if (initialLoadCompleted_) { - if (ledgerDelegate_) { - loadLedgerEntries(onNewBlock); - } - else { - SPDLOG_LOGGER_DEBUG(logger_, "create new ledger delegate"); - armory_->getWalletsLedgerDelegate(cbWalletsLD); - } - } else { - SPDLOG_LOGGER_WARN(logger_, "initial load is not completed yet"); - } -} - int TransactionsViewModel::columnCount(const QModelIndex &) const { return static_cast(Columns::last) + 1; @@ -385,64 +296,6 @@ TXNode *TransactionsViewModel::getNode(const QModelIndex &index) const return static_cast(index.internalPointer()); } -bool TransactionsViewModel::isTxRevocable(const Tx& tx) -{ - auto iRevokable = revocableTxs_.find(tx.getThisHash()); - if (iRevokable != revocableTxs_.cend()) { - return iRevokable->second; - } - - // Output to settlement address must be first - const auto settlementOutIndex = 0; - - try { - const auto &txOut = tx.getTxOutCopy(settlementOutIndex); - const auto &addr = bs::Address::fromTxOut(txOut); - if (addr.getType() != AddressEntryType_P2WSH) { - return false; - } - } catch (...) { - SPDLOG_LOGGER_ERROR(logger_, "can't get address"); - return false; - } - - const std::map> spentnessToTrack = { { tx.getThisHash(), { static_cast(settlementOutIndex) } } }; - - auto cbStoreRevoke = [this, caller = QPointer(this), txHash = tx.getThisHash()](const std::map> &results, std::exception_ptr exPtr) { - QMetaObject::invokeMethod(qApp, [this, caller, txHash, results, exPtr] { - if (!caller) { - return; - } - - if (exPtr != nullptr) { - SPDLOG_LOGGER_ERROR(logger_, "request failed"); - return; - } - - auto iResult = results.find(txHash); - if (iResult == results.cend()) { - SPDLOG_LOGGER_ERROR(logger_, "TX not found"); - return; - } - - for (auto const &result : iResult->second) { - if (result.second.state_ == OutputSpentnessState::Unspent) { - revocableTxs_[txHash] = true; - return; - } - } - }); - }; - - armory_->getSpentnessForOutputs(spentnessToTrack, cbStoreRevoke); - armory_->getSpentnessForZcOutputs(spentnessToTrack, cbStoreRevoke); - - // We want user to have this as false while result is not returning back - revocableTxs_[tx.getThisHash()] = false; - return false; -} - int TransactionsViewModel::rowCount(const QModelIndex &parent) const { const auto &node = getNode(parent); @@ -549,7 +402,7 @@ void TransactionsViewModel::onWalletDeleted(std::string) void TransactionsViewModel::updatePage() { if (allWallets_) { - loadAllWallets(); +// loadAllWallets(); } } @@ -566,49 +419,6 @@ void TransactionsViewModel::clear() *stopped_ = false; } -void TransactionsViewModel::onStateChanged(ArmoryState state) -{ - QMetaObject::invokeMethod(this, [this, state] { - if (state == ArmoryState::Offline) { - ledgerDelegate_.reset(); - clear(); - } else if ((state == ArmoryState::Ready) && !rootNode_->hasChildren()) { - loadAllWallets(); - } - }); -} - -std::shared_ptr TransactionsViewModel::itemFromTransaction(const bs::TXEntry &entry) -{ - auto item = std::make_shared(); - item->txEntry = entry; - item->displayDateTime = UiUtils::displayDateTime(entry.txTime); - for (const auto &walletId : entry.walletIds) { - const auto wallet = walletsManager_->getWalletById(walletId); - if (wallet) { - item->wallets.push_back(wallet); - } - } - item->filterAddress = filterAddress_; - if (item->wallets.empty() && defaultWallet_) { - item->wallets.push_back(defaultWallet_); - } - if (!item->wallets.empty()) { - item->walletID = QString::fromStdString(item->wallets[0]->walletId()); - } - else { - item->walletID = QString::fromStdString(*entry.walletIds.cbegin()); - } - - item->confirmations = armory_->getConfirmationsNumber(entry.blockNum); - if (!item->wallets.empty()) { - item->walletName = QString::fromStdString(item->wallets[0]->name()); - } - const auto validWallet = item->wallets.empty() ? nullptr : item->wallets[0]; - item->isValid = validWallet ? validWallet->isTxValid(entry.txHash) : bs::sync::TxValidity::Invalid; - return item; -} - std::shared_ptr TransactionsViewModel::createTxItem(const bs::TXEntry &entry) { auto item = std::make_shared(); @@ -626,288 +436,6 @@ std::shared_ptr TransactionsViewModel::createTxItem(const return item; } -void TransactionsViewModel::onZCReceived(const std::string& requestId, const std::vector& entries) -{ - QMetaObject::invokeMethod(this, [this, entries] { updateTransactionsPage(entries); }); -} - -void TransactionsViewModel::onZCInvalidated(const std::set &ids) -{ - std::vector delRows; -#ifdef TX_MODEL_NESTED_NODES - std::vector children; -#endif - { - QMutexLocker locker(&updateMutex_); - for (const auto &txHash : ids) { - const auto invNodes = rootNode_->nodesByTxHash(txHash); - for (const auto &node : invNodes) { - delRows.push_back(node->row()); - } -#ifdef TX_MODEL_NESTED_NODES // nested nodes are not supported for now - const auto node = rootNode_->find(entry); - if (node && (node->parent() == rootNode_.get()) && !node->item()->confirmations) { - delRows.push_back(node->row()); - if (node->hasChildren()) { // handle race condition when node being deleted has confirmed children - for (const auto &child : node->children()) { - if (child->item()->confirmations) { - children.push_back(child->item()->txEntry); - } - } - } - } -#endif //TX_MODEL_NESTED_NODES - } - } - if (!delRows.empty()) { - QMetaObject::invokeMethod(this, [this, delRows] { - onDelRows(delRows); - }); - } - -#ifdef TX_MODEL_NESTED_NODES - if (!children.empty()) { - logger_->debug("[{}] {} children to update", __func__, children.size()); - updateTransactionsPage(children); - } -#endif -} - -#ifdef TX_MODEL_NESTED_NODES -static bool isChildOf(TransactionPtr child, TransactionPtr parent) -{ - if (!child->initialized || !parent->initialized) { - return false; - } - if (!parent->parentId.empty() && !child->groupId.empty()) { - if (child->groupId == parent->parentId) { - return true; - } - } - if ((!child->confirmations && child->txEntry.isRBF && !parent->confirmations && parent->txEntry.isRBF) - && (child->txEntry.txHash != parent->txEntry.txHash) - && (child->txEntry.walletIds == parent->txEntry.walletIds)) { - std::set childInputs, parentInputs; - for (int i = 0; i < int(child->tx.getNumTxIn()); i++) { - childInputs.insert(child->tx.getTxInCopy(i).serialize()); - } - for (int i = 0; i < int(parent->tx.getNumTxIn()); i++) { - parentInputs.insert(parent->tx.getTxInCopy(i).serialize()); - } - if (childInputs == parentInputs) { - return true; - } - } - return false; -} -#endif //TX_MODEL_NESTED_NODES - -std::pair TransactionsViewModel::updateTransactionsPage(const std::vector &page) -{ - struct ItemKey { - BinaryData txHash; - std::set walletIds; - bool operator<(const ItemKey &ik) const { - if (txHash != ik.txHash) { - return (txHash < ik.txHash); - } - return (walletIds < ik.walletIds); - } - }; - auto newItems = std::make_shared>(); - auto updatedItems = std::make_shared>(); - auto newTxKeys = std::make_shared>(); - - const auto mergedPage = allWallets_ ? walletsManager_->mergeEntries(page) : page; - - const auto lbdAddNew = [this, newItems, newTxKeys](const TransactionPtr &item) - { - if (!oldestItem_ || (oldestItem_->txEntry.txTime >= item->txEntry.txTime)) { - oldestItem_ = item; - } - newTxKeys->insert({ item->txEntry.txHash, item->txEntry.walletIds }); - newItems->push_back(new TXNode(item)); - }; - - const auto mergeItem = [this, updatedItems](const TransactionPtr &item) -> bool - { - for (const auto &node : rootNode_->children()) { - if (!node) { - continue; - } - if (walletsManager_->mergeableEntries(node->item()->txEntry, item->txEntry)) { - item->txEntry.merge(node->item()->txEntry); - updatedItems->push_back(item); - return true; - } - } - return false; - }; - - for (const auto &entry : mergedPage) { - const auto item = itemFromTransaction(entry); - if (item->wallets.empty()) { - continue; - } - - TXNode *node = nullptr; - { - QMutexLocker locker(&updateMutex_); - node = rootNode_->find(item->txEntry); - } - if (node) { - updatedItems->push_back(item); - } - else { - if (allWallets_) { - if (!mergeItem(item)) { - lbdAddNew(item); - } - } - else { - lbdAddNew(item); - } - } - } - - const auto &cbInited = [this, newItems, updatedItems, newTxKeys] - (const TransactionPtr &itemPtr) - { - if (!itemPtr || !itemPtr->initialized) { - logger_->error("item is not inited"); - return; - } - if (newTxKeys->empty()) { - logger_->warn("TX keys already empty"); - return; - } - newTxKeys->erase({ itemPtr->txEntry.txHash, itemPtr->txEntry.walletIds }); - if (newTxKeys->empty()) { -#ifdef TX_MODEL_NESTED_NODES - std::unordered_set deletedItems; - if (rootNode_->hasChildren()) { - std::vector delRows; - const auto &cbEachExisting = [newItems, &delRows](TXNode *txNode) { - for (auto &newItem : *newItems) { - if (newItem.second.second->find(txNode->item()->id()) - || txNode->find(newItem.first)) { // avoid looped graphs - continue; - } - if (isChildOf(txNode->item(), newItem.second.first)) { - delRows.push_back(txNode->row()); - auto &&children = txNode->children(); - newItem.second.second->add(txNode); - for (auto &child : children) { - newItem.second.second->add(child); - } - txNode->clear(false); - } - else if (isChildOf(newItem.second.first, txNode->item())) { - // do nothing, yet - } - } - }; - { - QMutexLocker locker(&updateMutex_); - for (auto &child : rootNode_->children()) { - cbEachExisting(child); - } - } - if (!delRows.empty()) { - onDelRows(delRows); - } - } - const auto newItemsCopy = *newItems; - for (auto &parentItem : *newItems) { - if (deletedItems.find(parentItem.first) != deletedItems.end()) { // don't treat child-parent transitively - continue; - } - for (auto &childItem : newItemsCopy) { - if (parentItem.first == childItem.first) { // don't compare with self - continue; - } - if (deletedItems.find(childItem.first) != deletedItems.end()) { - continue; - } - if (isChildOf(childItem.second.first, parentItem.second.first)) { - parentItem.second.second->add(childItem.second.second); - deletedItems.insert(childItem.second.first->id()); - } - } - } - for (const auto &delId : deletedItems) { - newItems->erase(delId); - } -#endif //TX_MODEL_NESTED_NODES - if (!newItems->empty()) { - onNewItems(*newItems); - if (signalOnEndLoading_) { - signalOnEndLoading_ = false; - emit dataLoaded(int(newItems->size())); - } - } - if (!updatedItems->empty()) { - updateBlockHeight(*updatedItems); - } - } - }; - - const auto newItemsCopy = *newItems; - if (!newItemsCopy.empty()) { - for (const auto &node : newItemsCopy) { - updateTransactionDetails(node->item(), cbInited); - } - } - else { - if (!updatedItems->empty()) { - updateBlockHeight(*updatedItems); - } - emit dataLoaded(0); - } - - return { newItemsCopy.size(), updatedItems->size() }; -} - -void TransactionsViewModel::updateBlockHeight(const std::vector> &updItems) -{ - if (!rootNode_->hasChildren()) { - logger_->debug("[{}] root node doesn't have children", __func__); - return; - } - - for (const auto &updItem : updItems) { - TXNode *node = nullptr; - { - QMutexLocker locker(&updateMutex_); - node = rootNode_->find(updItem->txEntry); - } - if (!node) { - continue; - } - const auto &item = node->item(); - if (!updItem->wallets.empty()) { - item->isValid = updItem->wallets[0]->isTxValid(updItem->txEntry.txHash); - } - if (item->txEntry.value != updItem->txEntry.value) { - item->wallets = updItem->wallets; - item->walletID = updItem->walletID; - item->txEntry = updItem->txEntry; - item->amountStr.clear(); - item->calcAmount(walletsManager_); - } - const auto newBlockNum = updItem->txEntry.blockNum; - if (newBlockNum != UINT32_MAX) { - const auto confNum = armory_->getConfirmationsNumber(newBlockNum); - item->confirmations = confNum; - item->txEntry.blockNum = newBlockNum; - onItemConfirmed(item); - } - } - - emit dataChanged(index(0, static_cast(Columns::Amount)) - , index(rootNode_->nbChildren() - 1, static_cast(Columns::Status))); -} - void TransactionsViewModel::onItemConfirmed(const TransactionPtr item) { if (item->txEntry.isRBF && (item->confirmations == 1)) { @@ -934,108 +462,13 @@ void TransactionsViewModel::onRefreshTxValidity() if (item->isValid != newState) { item->isValid = newState; // Update balance in case lotSize_ is received after CC gen file loaded - item->calcAmount(walletsManager_); +// item->calcAmount(walletsManager_); emit dataChanged(index(i, static_cast(Columns::first)) , index(i, static_cast(Columns::last))); } } } -void TransactionsViewModel::loadLedgerEntries(bool onNewBlock) -{ - if (!initialLoadCompleted_ || !ledgerDelegate_) { - if (onNewBlock && logger_) { - logger_->debug("[TransactionsViewModel::loadLedgerEntries] previous loading is not complete/started"); - } - return; - } - initialLoadCompleted_ = false; - - QPointer thisPtr = this; - auto rawData = std::make_shared>>(); - auto rawDataMutex = std::make_shared(); - - const auto &cbPageCount = [thisPtr, onNewBlock, stopped = stopped_, logger = logger_, rawData, rawDataMutex, ledgerDelegate = ledgerDelegate_] - (ReturnMessage pageCnt) - { - try { - int inPageCnt = int(pageCnt.get()); - - if (inPageCnt == 0) { - SPDLOG_LOGGER_ERROR(logger, "page count is 0"); - thisPtr->initialLoadCompleted_ = true; - return; - } - - QMetaObject::invokeMethod(qApp, [thisPtr, inPageCnt] { - if (thisPtr) { - emit thisPtr->initProgress(0, int(inPageCnt * 2)); - } - }); - - for (int pageId = 0; pageId < inPageCnt; ++pageId) { - if (*stopped) { - logger->debug("[TransactionsViewModel::loadLedgerEntries] stopped"); - break; - } - - const auto &cbLedger = [thisPtr, onNewBlock, pageId, inPageCnt, rawData, logger, rawDataMutex] - (ReturnMessage> entries)->void { - try { - auto le = entries.get(); - - std::lock_guard lock(*rawDataMutex); - - (*rawData)[pageId] = bs::TXEntry::fromLedgerEntries(le); - if (onNewBlock && logger) { - logger->debug("[TransactionsViewModel::loadLedgerEntries] loaded {} entries for page {} (of {})" - , le.size(), pageId, inPageCnt); - } - - if (int(rawData->size()) >= inPageCnt) { - QMetaObject::invokeMethod(qApp, [thisPtr, rawData, onNewBlock] { - if (thisPtr) { - thisPtr->ledgerToTxData(*rawData, onNewBlock); - } - }); - } - } - catch (std::exception& e) { - logger->error("[TransactionsViewModel::loadLedgerEntries::cbLedger] " \ - "return data error: {}", e.what()); - } - - QMetaObject::invokeMethod(qApp, [thisPtr, pageId] { - if (thisPtr) { - emit thisPtr->updateProgress(pageId); - } - }); - }; - ledgerDelegate->getHistoryPage(uint32_t(pageId), cbLedger); - } - } - catch (const std::exception &e) { - logger->error("[TransactionsViewModel::loadLedgerEntries::cbPageCount] return " \ - "data error: {}", e.what()); - } - }; - - ledgerDelegate_->getPageCount(cbPageCount); -} - -void TransactionsViewModel::ledgerToTxData(const std::map> &rawData - , bool onNewBlock) -{ - int pageCnt = 0; - - signalOnEndLoading_ = true; - for (const auto &le : rawData) { - updateTransactionsPage(le.second); - emit updateProgress(int(rawData.size()) + pageCnt++); - } - initialLoadCompleted_ = true; -} - void TransactionsViewModel::onNewItems(const std::vector &newItems) { const int curLastIdx = rootNode_->nbChildren(); @@ -1103,17 +536,6 @@ TransactionPtr TransactionsViewModel::getItem(const QModelIndex &index) const return node->item(); } -void TransactionsViewModel::updateTransactionDetails(const TransactionPtr &item - , const std::function &cb) -{ - const auto &cbInited = [cb](const TransactionPtr &item) { - if (cb) { - cb(item); - } - }; - TransactionsViewItem::initialize(item, armory_, walletsManager_, cbInited); -} - void TransactionsViewModel::onNewBlock(unsigned int curBlock) { if (curBlock_ == curBlock) { @@ -1282,170 +704,6 @@ void TransactionsViewModel::onTXDetails(const std::vector &walletsMgr - , std::function userCB) -{ - const auto cbCheckIfInitializationCompleted = [item, userCB] { - if (item->initialized) { - return; - } - if (!item->dirStr.isEmpty() && !item->mainAddress.isEmpty() && !item->amountStr.isEmpty()) { - item->initialized = true; - userCB(item); - } - }; - - const auto cbMainAddr = [item, cbCheckIfInitializationCompleted](QString mainAddr, int addrCount) { - item->mainAddress = mainAddr; - item->addressCount = addrCount; - cbCheckIfInitializationCompleted(); - }; - - const auto cbInit = [item, walletsMgr, cbMainAddr, cbCheckIfInitializationCompleted, userCB] { - if (item->amountStr.isEmpty() && item->txHashesReceived) { - item->calcAmount(walletsMgr); - } - if (item->mainAddress.isEmpty()) { - if (!walletsMgr->getTransactionMainAddress(item->tx, item->walletID.toStdString(), (item->amount > 0), cbMainAddr)) { - userCB(nullptr); - } - } - else { - cbCheckIfInitializationCompleted(); - } - }; - - const auto cbTXs = [item, cbInit, userCB] - (const AsyncClient::TxBatchResult &txs, std::exception_ptr exPtr) - { - if (exPtr != nullptr) { - userCB(nullptr); - return; - } - item->txIns.insert(txs.cbegin(), txs.cend()); - item->txHashesReceived = true; - cbInit(); - }; - const auto &cbDir = [item, cbInit](bs::sync::Transaction::Direction dir, std::vector inAddrs) { - item->direction = dir; - item->dirStr = QObject::tr(bs::sync::Transaction::toStringDir(dir)); - if (dir == bs::sync::Transaction::Direction::Received) { - if (inAddrs.size() == 1) { // likely a settlement address - switch (inAddrs[0].getType()) { - case AddressEntryType_P2WSH: - case AddressEntryType_P2SH: - case AddressEntryType_Multisig: - item->parentId = inAddrs[0]; - break; - default: break; - } - } - } - else if (dir == bs::sync::Transaction::Direction::Sent) { - for (int i = 0; i < item->tx.getNumTxOut(); ++i) { - TxOut out = item->tx.getTxOutCopy((int)i); - auto addr = bs::Address::fromHash(out.getScrAddressStr()); - switch (addr.getType()) { - case AddressEntryType_P2WSH: // likely a settlement address - case AddressEntryType_P2SH: - case AddressEntryType_Multisig: - item->parentId = addr; - break; - default: break; - } - if (!item->parentId.empty()) { - break; - } - } - } - else if (dir == bs::sync::Transaction::Direction::PayIn) { - for (int i = 0; i < item->tx.getNumTxOut(); ++i) { - TxOut out = item->tx.getTxOutCopy((int)i); - auto addr = bs::Address::fromHash(out.getScrAddressStr()); - switch (addr.getType()) { - case AddressEntryType_P2WSH: - case AddressEntryType_P2SH: - case AddressEntryType_Multisig: - item->groupId = addr; - break; - default: break; - } - if (!item->groupId.empty()) { - break; - } - } - } - else if (dir == bs::sync::Transaction::Direction::PayOut) { - if (inAddrs.size() == 1) { - item->groupId = inAddrs[0]; - } - } - cbInit(); - }; - - const auto cbTX = [item, armory, walletsMgr, cbTXs, cbInit, cbDir, cbMainAddr, userCB](const Tx &newTx) { - if (!newTx.isInitialized()) { - userCB(nullptr); - return; - } - if (item->comment.isEmpty()) { - item->comment = item->wallets.empty() ? QString() - : QString::fromStdString(item->wallets[0]->getTransactionComment(item->txEntry.txHash)); - const auto endLineIndex = item->comment.indexOf(QLatin1Char('\n')); - if (endLineIndex != -1) { - item->comment = item->comment.left(endLineIndex) + QLatin1String("..."); - } - } - - if (!item->tx.isInitialized()) { - item->tx = std::move(newTx); - std::set txHashSet; - for (size_t i = 0; i < item->tx.getNumTxIn(); i++) { - TxIn in = item->tx.getTxInCopy(i); - OutPoint op = in.getOutPoint(); - if (item->txIns.find(op.getTxHash()) == item->txIns.end()) { - txHashSet.insert(op.getTxHash()); - } - } - if (txHashSet.empty()) { - item->txHashesReceived = true; - } - else { - if (!armory->getTXsByHash(txHashSet, cbTXs, true)) { - userCB(nullptr); - } - } - } - else { - item->txHashesReceived = true; - } - - if (item->dirStr.isEmpty()) { - if (!walletsMgr->getTransactionDirection(item->tx, item->walletID.toStdString(), cbDir)) { - userCB(nullptr); - } - } - else { - if (item->txHashesReceived) { - cbInit(); - } - } - }; - - if (item->initialized) { - userCB(item); - } else { - if (item->tx.isInitialized()) { - cbTX(item->tx); - } else { - if (!armory->getTxByHash(item->txEntry.txHash, cbTX, true)) { - userCB(nullptr); - } - } - } -} - static bool isSpecialWallet(const std::shared_ptr &wallet) { if (!wallet) { diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index 328642a81..414147f6b 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -20,7 +20,6 @@ #include #include #include -#include "ArmoryConnection.h" #include "AsyncClient.h" #include "SignerDefs.h" @@ -69,9 +68,6 @@ struct TransactionsViewItem BinaryData groupId; bool isSet() const { return (!txEntry.txHash.empty() && !walletID.isEmpty()); } - static void initialize(const TransactionPtr &item, ArmoryConnection * - , const std::shared_ptr & - , std::function); void calcAmount(const std::shared_ptr &); bool containsInputsFrom(const Tx &tx) const; @@ -133,21 +129,10 @@ Q_DECLARE_METATYPE(TransactionsViewItem) Q_DECLARE_METATYPE(TransactionItems) -class TransactionsViewModel : public QAbstractItemModel, public ArmoryCallbackTarget +class TransactionsViewModel : public QAbstractItemModel { Q_OBJECT public: - [[deprecated]] TransactionsViewModel(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &defWlt - , const bs::Address &filterAddress = bs::Address() - , QObject* parent = nullptr); - [[deprecated]] TransactionsViewModel(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , QObject* parent = nullptr); TransactionsViewModel(const std::shared_ptr &, QObject* parent = nullptr); ~TransactionsViewModel() noexcept override; @@ -156,7 +141,6 @@ Q_OBJECT TransactionsViewModel(TransactionsViewModel&&) = delete; TransactionsViewModel& operator = (TransactionsViewModel&&) = delete; - void loadAllWallets(bool onNewBlock=false); size_t itemsCount() const { return rootNode_->nbChildren(); } int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -171,7 +155,6 @@ Q_OBJECT TransactionPtr getItem(const QModelIndex &) const; TransactionPtr getOldestItem() const { return oldestItem_; } TXNode *getNode(const QModelIndex &) const; - bool isTxRevocable(const Tx& tx); void onLedgerEntries(const std::string &filter, uint32_t totalPages , uint32_t curPage, uint32_t curBlock, const std::vector &); @@ -192,21 +175,7 @@ private slots: void onRefreshTxValidity(); private: - [[deprecated]] void onNewBlock(unsigned int height, unsigned int branchHgt) override; - [[deprecated]] void onStateChanged(ArmoryState) override; - [[deprecated]] void onZCReceived(const std::string& requestId, const std::vector &) override; - [[deprecated]] void onZCInvalidated(const std::set &ids) override; - - [[deprecated]] void init(); void clear(); - [[deprecated]] void loadLedgerEntries(bool onNewBlock=false); - [[deprecated]] void ledgerToTxData(const std::map> &rawData - , bool onNewBlock=false); - std::pair updateTransactionsPage(const std::vector &); - void updateBlockHeight(const std::vector> &); - void updateTransactionDetails(const TransactionPtr &item - , const std::function &cb); - [[deprecated]] std::shared_ptr itemFromTransaction(const bs::TXEntry &); std::shared_ptr createTxItem(const bs::TXEntry &); signals: @@ -242,8 +211,6 @@ private slots: std::map invalidatedNodes_; TransactionPtr oldestItem_; std::shared_ptr logger_; - std::shared_ptr ledgerDelegate_; - std::shared_ptr walletsManager_; mutable QMutex updateMutex_; std::shared_ptr defaultWallet_; std::atomic_bool signalOnEndLoading_{ false }; diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index 08207ba31..de97fa7c7 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -225,7 +225,7 @@ TransactionsWidget::TransactionsWidget(QWidget* parent) if (txNode->item()->isPayin()) { contextMenu_.addAction(actionRevoke_); actionRevoke_->setData(sourceIndex); - actionRevoke_->setEnabled(model_->isTxRevocable(txNode->item()->tx)); +// actionRevoke_->setEnabled(model_->isTxRevocable(txNode->item()->tx)); } else { actionRevoke_->setData(-1); @@ -520,17 +520,11 @@ void TransactionsWidget::showTransactionDetails(const QModelIndex& index) return; } - if (walletsManager_ && armory_) { - TransactionDetailDialog transactionDetailDialog(txItem, walletsManager_, armory_, this); - transactionDetailDialog.exec(); - } - else { - auto txDetailDialog = new TransactionDetailDialog(txItem, this); - connect(txDetailDialog, &QDialog::finished, [txDetailDialog](int) { - txDetailDialog->deleteLater(); - }); - txDetailDialog->show(); - } + auto txDetailDialog = new TransactionDetailDialog(txItem, this); + connect(txDetailDialog, &QDialog::finished, [txDetailDialog](int) { + txDetailDialog->deleteLater(); + }); + txDetailDialog->show(); } void TransactionsWidget::updateResultCount() diff --git a/BlockSettleUILib/TransactionsWidgetInterface.cpp b/BlockSettleUILib/TransactionsWidgetInterface.cpp index 66ab2808c..c7c16ccc5 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.cpp +++ b/BlockSettleUILib/TransactionsWidgetInterface.cpp @@ -178,9 +178,6 @@ void TransactionsWidgetInterface::onRevokeSettlement() if (txItem->initialized) { cbDialog(txItem); } - else { - TransactionsViewItem::initialize(txItem, armory_.get(), walletsManager_, cbDialog); - } } void TransactionsWidgetInterface::onCreateRBFDialog() @@ -206,9 +203,6 @@ void TransactionsWidgetInterface::onCreateRBFDialog() if (txItem->initialized) { cbDialog(txItem); } - else { - TransactionsViewItem::initialize(txItem, armory_.get(), walletsManager_, cbDialog); - } } void TransactionsWidgetInterface::onCreateCPFPDialog() @@ -241,9 +235,6 @@ void TransactionsWidgetInterface::onCreateCPFPDialog() if (txItem->initialized) { cbDialog(txItem); } - else { - TransactionsViewItem::initialize(txItem, armory_.get(), walletsManager_, cbDialog); - } } void TransactionsWidgetInterface::onTXSigned(unsigned int id, BinaryData signedTX diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index d2d25a04b..0d0e1fe8f 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -498,8 +498,8 @@ void WalletsWidget::showAddressProperties(const QModelIndex& index) const auto address = (addrIndex < addresses.size()) ? addresses[addrIndex] : bs::Address(); wallet->onBalanceAvailable([this, address, wallet] { - auto dialog = new AddressDetailDialog(address, wallet, walletsManager_, armory_, logger_, this); - QMetaObject::invokeMethod(this, [dialog] { dialog->exec(); }); +//FIXME: auto dialog = new AddressDetailDialog(address, wallet, walletsManager_, armory_, logger_, this); +// QMetaObject::invokeMethod(this, [dialog] { dialog->exec(); }); }); } else { diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 12c1cd0d8..1c4183f34 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -449,6 +449,7 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) mainWindow_->onWalletsReady(); }); } + break; case ArmoryMessage::kLedgerEntries: return processLedgerEntries(msg.ledger_entries()); case ArmoryMessage::kAddressHistory: diff --git a/common b/common index cc3609009..6acdd3b5e 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit cc36090098a01953e6217819532c6c895806da04 +Subproject commit 6acdd3b5ed2624014f7c2aea719e8cf701fbeeed From 50a07fb99b5a8c662e98d8ef22730a6ee62b3200 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Tue, 19 Jan 2021 17:59:47 +0300 Subject: [PATCH 105/146] Add PriceAmount tests --- UnitTests/TestCommon.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/UnitTests/TestCommon.cpp b/UnitTests/TestCommon.cpp index dfa787127..3bf7ca383 100644 --- a/UnitTests/TestCommon.cpp +++ b/UnitTests/TestCommon.cpp @@ -30,8 +30,9 @@ #include "EasyCoDec.h" #include "HeadlessContainer.h" #include "InprocSigner.h" -#include "MarketDataProvider.h" #include "MDCallbacksQt.h" +#include "MarketDataProvider.h" +#include "PriceAmount.h" #include "TestEnv.h" #include "WalletUtils.h" #include "Wallets/SyncWalletsManager.h" @@ -398,3 +399,27 @@ TEST(TestCommon, XBTAmount) auto diff1 = bs::XBTAmount((xbt1 + bs::XBTAmount(uint64_t(1))).GetValueBitcoin()) - xbt1; EXPECT_EQ(diff1, 1); } + +TEST(TestCommon, PriceAmount) +{ + EXPECT_EQ(bs::CentAmount(0.0).to_string(), "0.00"); + EXPECT_EQ(bs::CentAmount(0.1).to_string(), "0.10"); + EXPECT_EQ(bs::CentAmount(-0.1).to_string(), "-0.10"); + EXPECT_EQ(bs::CentAmount(0.19).to_string(), "0.19"); + EXPECT_EQ(bs::CentAmount(-0.19).to_string(), "-0.19"); + + EXPECT_EQ(bs::CentAmount(0.129).to_string(), "0.12"); + EXPECT_EQ(bs::CentAmount(0.1299999).to_string(), "0.12"); + EXPECT_EQ(bs::CentAmount(0.13).to_string(), "0.13"); + EXPECT_EQ(bs::CentAmount(-0.129).to_string(), "-0.12"); + EXPECT_EQ(bs::CentAmount(-0.1299999).to_string(), "-0.12"); + EXPECT_EQ(bs::CentAmount(-0.13).to_string(), "-0.13"); + + EXPECT_EQ(bs::CentAmount(-0.0001).to_string(), "0.00"); + EXPECT_EQ(bs::CentAmount(0.0001).to_string(), "0.00"); + + EXPECT_EQ(bs::CentAmount(12345.0001).to_string(), "12345.00"); + EXPECT_EQ(bs::CentAmount(-12345.0001).to_string(), "-12345.00"); + EXPECT_EQ(bs::CentAmount(0.12345).to_string(), "0.12"); + EXPECT_EQ(bs::CentAmount(-0.12345).to_string(), "0.12"); +} From 506c9c20d0c6a5d4868b8e6aab1c452b9f769e55 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 19 Jan 2021 19:38:59 +0300 Subject: [PATCH 106/146] Portfolio tab is now functional --- BlockSettleUILib/CCPortfolioModel.cpp | 125 +++++++++++++++++++++++++- BlockSettleUILib/CCPortfolioModel.h | 41 ++++++--- BlockSettleUILib/CCWidget.cpp | 75 +++++++++++++++- BlockSettleUILib/CCWidget.h | 16 +++- BlockSettleUILib/CCWidget.ui | 114 ++++++++++++++++++++--- BlockSettleUILib/PortfolioWidget.cpp | 104 ++++++++++++++++----- BlockSettleUILib/PortfolioWidget.h | 19 ++-- GUI/QtWidgets/MainWindow.cpp | 8 +- GUI/QtWidgets/MainWindow.h | 2 - 9 files changed, 434 insertions(+), 70 deletions(-) diff --git a/BlockSettleUILib/CCPortfolioModel.cpp b/BlockSettleUILib/CCPortfolioModel.cpp index 843788957..f87942443 100644 --- a/BlockSettleUILib/CCPortfolioModel.cpp +++ b/BlockSettleUILib/CCPortfolioModel.cpp @@ -417,8 +417,8 @@ class XBTAssetGroupNode : public AssetGroupNode class CCAssetGroupNode : public AssetGroupNode { public: - CCAssetGroupNode(const QString& xbtGroupName, AssetNode* parent) - : AssetGroupNode(xbtGroupName, parent) + CCAssetGroupNode(const QString& groupName, AssetNode* parent) + : AssetGroupNode(groupName, parent) {} ~CCAssetGroupNode() noexcept override = default; @@ -452,8 +452,8 @@ class CCAssetGroupNode : public AssetGroupNode class FXAssetGroupNode : public AssetGroupNode { public: - FXAssetGroupNode(const QString& xbtGroupName, AssetNode* parent) - : AssetGroupNode(xbtGroupName, parent) + FXAssetGroupNode(const QString& groupName, AssetNode* parent) + : AssetGroupNode(groupName, parent) {} ~FXAssetGroupNode() noexcept override = default; @@ -604,6 +604,12 @@ CCPortfolioModel::CCPortfolioModel(const std::shared_ptr(tr("XBT"), tr("Private Shares"), tr("Cash")); +} + int CCPortfolioModel::columnCount(const QModelIndex & parent) const { return PortfolioColumns::PortfolioColumnsCount; @@ -738,6 +744,89 @@ std::shared_ptr CCPortfolioModel::walletsManager() return walletsManager_; } +void CCPortfolioModel::onHDWallet(const bs::sync::WalletInfo& wi) +{ + if (!root_->HaveXBTGroup()) { + beginInsertRows({}, rowCount({}), rowCount({})); + root_->GetXBTGroup(); + endInsertRows(); + } + const auto& xbtGroup = root_->GetXBTGroup(); + const auto &displayedWallets = xbtGroup->GetWalletIds(); + const auto& walletId = *wi.ids.cbegin(); + if (displayedWallets.find(walletId) != displayedWallets.end()) { + return; + } + beginInsertRows(createIndex(xbtGroup->getRow(), 0, static_cast(xbtGroup)) + , displayedWallets.size(), displayedWallets.size()); + xbtGroup->AddAsset(QString::fromStdString(wi.name), QString::fromStdString(walletId)); + endInsertRows(); +} + +void CCPortfolioModel::onHDWalletDetails(const bs::sync::HDWalletData& wd) +{ + for (const auto& group : wd.groups) { + if ((group.type != bs::hd::CoinType::Bitcoin_main) && (group.type != bs::hd::CoinType::Bitcoin_test)) { + continue; + } + for (const auto& leaf : group.leaves) { + for (const auto& id : leaf.ids) { + leafIdToRootId_[id] = wd.id; + } + } + } +} + +void CCPortfolioModel::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + leafBalances_[wbd.id] = wbd.balTotal; + const auto& itId = leafIdToRootId_.find(wbd.id); + if (itId != leafIdToRootId_.end()) { + updateWalletBalance(itId->second); + } +} + +void CCPortfolioModel::onBalance(const std::string& currency, double balance) +{ + const auto& fxGroup = root_->GetFXGroup(); + auto fxNode = fxGroup->GetFXNode(currency); + if (!fxNode) { // insert under root + beginInsertRows(QModelIndex{}, fxGroup->getRow(), fxGroup->getRow()); + fxGroup->AddAsset(QString::fromStdString(currency)); + fxNode = fxGroup->GetFXNode(currency); + fxNode->SetFXAmount(balance); + endInsertRows(); + } + else { + fxNode->SetFXAmount(balance); + dataChanged(index(fxGroup->getRow(), PortfolioColumns::XBTValueColumn) + , index(fxGroup->getRow(), PortfolioColumns::XBTValueColumn) + , { Qt::DisplayRole }); + + const auto &parentIndex = createIndex(fxGroup->getRow(), 0, static_cast(fxGroup)); + dataChanged(index(fxNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) + , index(fxNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) + , { Qt::DisplayRole }); + } +} + +void CCPortfolioModel::onPriceChanged(const std::string& currency, double price) +{ + const auto& fxGroup = root_->GetFXGroup(); + const auto &fxNode = fxGroup->GetFXNode(currency); + + if (fxNode && fxNode->SetPrice(price)) { + dataChanged(index(fxGroup->getRow(), PortfolioColumns::XBTValueColumn) + , index(fxGroup->getRow(), PortfolioColumns::XBTValueColumn) + , { Qt::DisplayRole }); + + const auto& parentIndex = createIndex(fxGroup->getRow(), 0, static_cast(fxGroup)); + dataChanged(index(fxNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) + , index(fxNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) + , { Qt::DisplayRole }); + } +} + bool CCPortfolioModel::hasChildren(const QModelIndex& parentIndex) const { return getNodeByIndex(parentIndex)->HasChildren(); @@ -1022,3 +1111,31 @@ void CCPortfolioModel::updateCCBalance() } } } + +void CCPortfolioModel::updateWalletBalance(const std::string& walletId) +{ + const auto& xbtGroup = root_->GetXBTGroup(); + const auto& parentIndex = createIndex(xbtGroup->getRow(), 0, static_cast(xbtGroup)); + + const auto xbtNode = xbtGroup->GetXBTNode(walletId); + if (!xbtNode) { + return; + } + double balTotal = 0; + for (const auto& idMap : leafIdToRootId_) { + if (idMap.second == walletId) { + try { + balTotal += leafBalances_.at(idMap.first); + } + catch (const std::exception&) {} + } + } + if (xbtNode->SetXBTAmount(balTotal)) { + dataChanged(index(xbtNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) + , index(xbtNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) + , { Qt::DisplayRole }); + dataChanged(index(xbtGroup->getRow(), PortfolioColumns::XBTValueColumn) + , index(xbtGroup->getRow(), PortfolioColumns::XBTValueColumn) + , { Qt::DisplayRole }); + } +} diff --git a/BlockSettleUILib/CCPortfolioModel.h b/BlockSettleUILib/CCPortfolioModel.h index d2ec30985..c85b57b71 100644 --- a/BlockSettleUILib/CCPortfolioModel.h +++ b/BlockSettleUILib/CCPortfolioModel.h @@ -13,6 +13,7 @@ #include #include +#include "SignerDefs.h" namespace bs { namespace sync { @@ -27,9 +28,10 @@ class RootAssetGroupNode; class CCPortfolioModel : public QAbstractItemModel { public: - CCPortfolioModel(const std::shared_ptr & + [[deprecated]] CCPortfolioModel(const std::shared_ptr & , const std::shared_ptr& assetManager , QObject *parent = nullptr); + CCPortfolioModel(QObject* parent = nullptr); ~CCPortfolioModel() noexcept override = default; CCPortfolioModel(const CCPortfolioModel&) = delete; @@ -38,8 +40,14 @@ class CCPortfolioModel : public QAbstractItemModel CCPortfolioModel(CCPortfolioModel&&) = delete; CCPortfolioModel& operator = (CCPortfolioModel&&) = delete; - std::shared_ptr assetManager(); - std::shared_ptr walletsManager(); + [[deprecated]] std::shared_ptr assetManager(); + [[deprecated]] std::shared_ptr walletsManager(); + + void onHDWallet(const bs::sync::WalletInfo&); + void onHDWalletDetails(const bs::sync::HDWalletData&); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onBalance(const std::string& currency, double balance); + void onPriceChanged(const std::string& currency, double price); private: enum PortfolioColumns @@ -65,25 +73,30 @@ class CCPortfolioModel : public QAbstractItemModel bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; private slots: - void onFXBalanceLoaded(); - void onFXBalanceCleared(); + void onFXBalanceLoaded(); // deprecated + void onFXBalanceCleared(); // deprecated + + void onFXBalanceChanged(const std::string& currency); // deprecated - void onFXBalanceChanged(const std::string& currency); + void onXBTPriceChanged(const std::string& currency); //deprecated + void onCCPriceChanged(const std::string& currency); // deprecated - void onXBTPriceChanged(const std::string& currency); - void onCCPriceChanged(const std::string& currency); + void reloadXBTWalletsList(); // deprecated + void updateXBTBalance(); // deprecated - void reloadXBTWalletsList(); - void updateXBTBalance(); + void reloadCCWallets(); // deprecated + void updateCCBalance(); // deprecated - void reloadCCWallets(); - void updateCCBalance(); +private: + void updateWalletBalance(const std::string& walletId); private: - std::shared_ptr assetManager_; - std::shared_ptr walletsManager_; + [[deprecated]] std::shared_ptr assetManager_; + [[deprecated]] std::shared_ptr walletsManager_; std::shared_ptr root_ = nullptr; + std::map leafIdToRootId_; + std::map leafBalances_; }; #endif // __CC_PORTFOLIO_MODEL__ \ No newline at end of file diff --git a/BlockSettleUILib/CCWidget.cpp b/BlockSettleUILib/CCWidget.cpp index bf68b3cf3..f6e174d95 100644 --- a/BlockSettleUILib/CCWidget.cpp +++ b/BlockSettleUILib/CCWidget.cpp @@ -22,6 +22,7 @@ CCWidget::CCWidget(QWidget* parent) , ui_(new Ui::CCWidget()) { ui_->setupUi(this); + ui_->frameXbtValue->hide(); } CCWidget::~CCWidget() = default; @@ -36,9 +37,45 @@ void CCWidget::SetPortfolioModel(const std::shared_ptr& model) connect(model.get(), &CCPortfolioModel::rowsInserted, this, &CCWidget::onRowsInserted); connect(model.get(), &CCPortfolioModel::modelReset, this, [this]() { ui_->treeViewCC->expandAll(); }); - connect(assetManager_.get(), &AssetManager::totalChanged, this, &CCWidget::updateTotalAssets); - connect(walletsManager.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &CCWidget::updateTotalAssets); - updateTotalAssets(); + if (assetManager_) { + connect(assetManager_.get(), &AssetManager::totalChanged, this, &CCWidget::updateTotalAssets); + } + if (walletsManager) { + connect(walletsManager.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &CCWidget::updateTotalAssets); + updateTotalAssets(); + } +} + +void CCWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + walletBalance_[wbd.id] = wbd.balTotal; + updateTotalBalances(); +} + +void CCWidget::onPriceChanged(const std::string& currency, double price) +{ + auto& itPrice = fxPrices_[currency]; + if (itPrice != price) { + itPrice = price; + updateTotalBalances(); + } +} + +void CCWidget::onBasePriceChanged(const std::string ¤cy, double price) +{ + baseCur_ = currency; + if (basePrice_ != price) { + basePrice_ = price; + updateTotalBalances(); + } +} + +void CCWidget::onBalance(const std::string& currency, double balance) +{ + if (balance > 0) { + fxBalance_[currency] = balance; + updateTotalBalances(); + } } void CCWidget::updateTotalAssets() @@ -58,3 +95,35 @@ void CCWidget::onRowsInserted(const QModelIndex &parent, int first, int last) Q_UNUSED(last) ui_->treeViewCC->expandAll(); } + +void CCWidget::updateTotalBalances() +{ + if (walletBalance_.empty() && fxBalance_.empty()) { + ui_->labelTotalValue->setText(tr("%1").arg(tr("Loading..."))); + } + else { + double xbtBalance = 0; + for (const auto& bal : walletBalance_) { + xbtBalance += bal.second; + } + for (const auto& bal : fxBalance_) { + try { + xbtBalance += bal.second * fxPrices_.at(bal.first); + } + catch (const std::exception&) {} + } + ui_->labelTotalValue->setText(tr("%1").arg(UiUtils::displayAmount(xbtBalance))); + } + + if (!walletBalance_.empty() && !baseCur_.empty()) { + ui_->frameXbtValue->show(); + + double xbtBalance = 0; + for (const auto& bal : walletBalance_) { + xbtBalance += bal.second; + } + xbtBalance *= basePrice_; + ui_->labelXbtValue->setText(UiUtils::UnifyValueString(tr("%1").arg(QString::number(xbtBalance, 'f', 2)))); + ui_->labelBasePrice->setText(UiUtils::UnifyValueString(QString::number(basePrice_, 'f', 2))); + } +} diff --git a/BlockSettleUILib/CCWidget.h b/BlockSettleUILib/CCWidget.h index 83d1a3d63..b6ec230df 100644 --- a/BlockSettleUILib/CCWidget.h +++ b/BlockSettleUILib/CCWidget.h @@ -13,6 +13,7 @@ #include #include +#include "SignerDefs.h" namespace Ui { class CCWidget; @@ -31,13 +32,26 @@ Q_OBJECT void SetPortfolioModel(const std::shared_ptr& model); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onPriceChanged(const std::string& currency, double price); + void onBasePriceChanged(const std::string ¤cy, double price); + void onBalance(const std::string& currency, double balance); + private slots: void updateTotalAssets(); void onRowsInserted(const QModelIndex &parent, int first, int last); private: - std::shared_ptr assetManager_; + void updateTotalBalances(); + +private: + [[deprecated]] std::shared_ptr assetManager_; std::unique_ptr ui_; + std::map walletBalance_; + std::map fxBalance_; + std::map fxPrices_; + std::string baseCur_; + double basePrice_{ 0 }; }; #endif // __CC_WIDGET_H__ diff --git a/BlockSettleUILib/CCWidget.ui b/BlockSettleUILib/CCWidget.ui index 9fc2e9e0e..7ceb0f153 100644 --- a/BlockSettleUILib/CCWidget.ui +++ b/BlockSettleUILib/CCWidget.ui @@ -1,14 +1,4 @@ - CCWidget @@ -20,6 +10,12 @@ 300 + + + 0 + 0 + + CCWidget @@ -122,6 +118,104 @@
+ + + + true + + + + 5 + + + 10 + + + 0 + + + 10 + + + 0 + + + + + + + + 75 + true + + + + XBT value in + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 75 + true + + + + EUR + + + 0 + + + + EUR + + + + + + + + + 75 + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + at + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index 787117480..f87658cc0 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -15,8 +15,10 @@ #include #include #include "ApplicationSettings.h" -#include "CreateTransactionDialogAdvanced.h" #include "BSMessageBox.h" +#include "CCPortfolioModel.h" +#include "CreateTransactionDialogAdvanced.h" +#include "CurrencyPair.h" #include "TransactionDetailDialog.h" #include "TransactionsViewModel.h" #include "Wallets/SyncWalletsManager.h" @@ -87,27 +89,15 @@ void PortfolioWidget::SetTransactionsModel(const std::shared_ptr(TransactionsViewModel::Columns::TxHash)); } -void PortfolioWidget::init(const std::shared_ptr &appSettings - , const std::shared_ptr &mdProvider - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr &model - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr &logger - , const std::shared_ptr &walletsMgr) +void PortfolioWidget::init(const std::shared_ptr& logger) { - //FIXME: TransactionsWidgetInterface::init(walletsMgr, armory, utxoReservationManager, container, appSettings, logger); - - ui_->widgetMarketData->init(appSettings, ApplicationSettings::Filter_MD_RFQ_Portfolio - , mdProvider, mdCallbacks); - ui_->widgetCCProtfolio->SetPortfolioModel(model); + logger_ = logger; + portfolioModel_ = std::make_shared(this); + ui_->widgetCCProtfolio->SetPortfolioModel(portfolioModel_); } void PortfolioWidget::shortcutActivated(ShortcutType s) -{ - -} +{} void PortfolioWidget::setAuthorized(bool authorized) { @@ -118,6 +108,75 @@ void PortfolioWidget::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields& fields) { ui_->widgetMarketData->onMDUpdated(assetType, security, fields); + + if ((assetType == bs::network::Asset::Undefined) || security.isEmpty()) { + return; + } + double lastPx = 0; + double bidPrice = 0; + + double productPrice = 0; + CurrencyPair cp(security.toStdString()); + std::string ccy; + + switch (assetType) { + case bs::network::Asset::PrivateMarket: + ccy = cp.NumCurrency(); + break; + case bs::network::Asset::SpotXBT: + ccy = cp.DenomCurrency(); + break; + default: + return; + } + + if (ccy.empty()) { + return; + } + + for (const auto& field : fields) { + if (field.type == bs::network::MDField::PriceLast) { + lastPx = field.value; + break; + } else if (field.type == bs::network::MDField::PriceBid) { + bidPrice = field.value; + } + } + + productPrice = (lastPx > 0) ? lastPx : bidPrice; + + if (productPrice > 0) { + if (ccy == cp.DenomCurrency()) { + productPrice = 1 / productPrice; + } + portfolioModel_->onPriceChanged(ccy, productPrice); + ui_->widgetCCProtfolio->onPriceChanged(ccy, productPrice); + if (ccy == "EUR") { + ui_->widgetCCProtfolio->onBasePriceChanged(ccy, 1/productPrice); + } + } +} + +void PortfolioWidget::onHDWallet(const bs::sync::WalletInfo& wi) +{ + portfolioModel_->onHDWallet(wi); +} + +void PortfolioWidget::onHDWalletDetails(const bs::sync::HDWalletData& wd) +{ + portfolioModel_->onHDWalletDetails(wd); +} + +void PortfolioWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) +{ + portfolioModel_->onWalletBalance(wbd); + ui_->widgetCCProtfolio->onWalletBalance(wbd); +} + +void PortfolioWidget::onBalance(const std::string& currency, double balance) +{ + portfolioModel_->onBalance(currency, balance); + ui_->widgetCCProtfolio->onBalance(currency, balance); } void PortfolioWidget::showTransactionDetails(const QModelIndex& index) @@ -130,8 +189,11 @@ void PortfolioWidget::showTransactionDetails(const QModelIndex& index) return; } -//FIXME: TransactionDetailDialog transactionDetailDialog(txItem, walletsManager_, armory_, this); -// transactionDetailDialog.exec(); + auto txDetailDialog = new TransactionDetailDialog(txItem, this); + connect(txDetailDialog, &QDialog::finished, [txDetailDialog](int) { + txDetailDialog->deleteLater(); + }); + txDetailDialog->show(); } } @@ -188,6 +250,4 @@ void PortfolioWidget::showContextMenu(const QPoint &point) } } - - #include "PortfolioWidget.moc" diff --git a/BlockSettleUILib/PortfolioWidget.h b/BlockSettleUILib/PortfolioWidget.h index 3dc2c5b98..a8837cc6c 100644 --- a/BlockSettleUILib/PortfolioWidget.h +++ b/BlockSettleUILib/PortfolioWidget.h @@ -15,6 +15,7 @@ #include #include #include "CommonTypes.h" +#include "SignerDefs.h" #include "TransactionsWidgetInterface.h" namespace spdlog { @@ -49,23 +50,18 @@ Q_OBJECT PortfolioWidget(QWidget* parent = nullptr ); ~PortfolioWidget() override; + void init(const std::shared_ptr&); void SetTransactionsModel(const std::shared_ptr& model); - void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr & - , const std::shared_ptr &); - void shortcutActivated(ShortcutType s) override; - void setAuthorized(bool authorized); + void onMDUpdated(bs::network::Asset::Type, const QString& security , const bs::network::MDFields&); + void onHDWallet(const bs::sync::WalletInfo&); + void onHDWalletDetails(const bs::sync::HDWalletData&); + void onWalletBalance(const bs::sync::WalletBalanceData&); + void onBalance(const std::string& currency, double balance); private slots: void showTransactionDetails(const QModelIndex& index); @@ -74,6 +70,7 @@ private slots: private: std::unique_ptr ui_; UnconfirmedTransactionFilter* filter_; + std::shared_ptr portfolioModel_; }; #endif // __PORFOLIO_WIDGET_H__ diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 73f0eedb8..184498420 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -223,6 +223,7 @@ void MainWindow::onSignerStateChanged(int state, const std::string &details) void MainWindow::onHDWallet(const bs::sync::WalletInfo &wi) { ui_->widgetWallets->onHDWallet(wi); + ui_->widgetPortfolio->onHDWallet(wi); } void bs::gui::qt::MainWindow::onWalletDeleted(const bs::sync::WalletInfo& wi) @@ -233,6 +234,7 @@ void bs::gui::qt::MainWindow::onWalletDeleted(const bs::sync::WalletInfo& wi) void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { ui_->widgetWallets->onHDWalletDetails(hdWallet); + ui_->widgetPortfolio->onHDWalletDetails(hdWallet); ui_->widgetRFQ->onHDWallet(hdWallet); ui_->widgetRFQReply->onHDWallet(hdWallet); } @@ -278,6 +280,7 @@ void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) txDlg_->onAddressBalances(wbd.id, wbd.addrBalances); } ui_->widgetWallets->onWalletBalance(wbd); + ui_->widgetPortfolio->onWalletBalance(wbd); ui_->widgetRFQ->onWalletBalance(wbd); ui_->widgetRFQReply->onWalletBalance(wbd); statusBarView_->onXbtBalance(wbd); @@ -992,6 +995,7 @@ void MainWindow::onMDUpdated(bs::network::Asset::Type assetType void bs::gui::qt::MainWindow::onBalance(const std::string& currency, double balance) { statusBarView_->onBalanceUpdated(currency, balance); + ui_->widgetPortfolio->onBalance(currency, balance); ui_->widgetRFQ->onBalance(currency, balance); ui_->widgetRFQReply->onBalance(currency, balance); } @@ -1238,9 +1242,7 @@ void MainWindow::initWidgets() initTransactionsView(); - /* portfolioModel_ = std::make_shared(walletsMgr_, assetManager_, this); - ui_->widgetPortfolio->init(applicationSettings_, mdProvider_, mdCallbacks_ - , portfolioModel_, signContainer_, armory_, utxoReservationMgr_, logMgr_->logger("ui"), walletsMgr_);*/ + ui_->widgetPortfolio->init(logger_); orderListModel_ = std::make_shared(this); dialogMgr_ = std::make_shared(this); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 0c2667cc4..af5979f61 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -275,8 +275,6 @@ namespace bs { std::shared_ptr statusBarView_; std::shared_ptr sysTrayIcon_; std::shared_ptr notifCenter_; - // std::shared_ptr portfolioModel_; - std::shared_ptr txModel_; std::shared_ptr orderListModel_; From 01daaf2d49a440966f4a75c79757efe86b514670 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Wed, 20 Jan 2021 15:21:54 +0300 Subject: [PATCH 107/146] IM balance fixes --- BlockSettleUILib/StatusBarView.cpp | 8 +++++--- BlockSettleUILib/Stylesheet/stylesheet.css | 6 ++++++ BlockSettleUILib/Trading/FuturesTicket.cpp | 21 ++++++++++++++++----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 7e72ae7b6..9200a5f8c 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -289,9 +289,11 @@ void StatusBarView::setBalances() QString text = tr(" XBT: %1 ").arg(xbt); for (const auto& currency : assetManager_->currencies()) { - text += tr("| %1: %2 ") - .arg(QString::fromStdString(currency)) - .arg(UiUtils::displayCurrencyAmount(assetManager_->getBalance(currency, false, nullptr))); + if (currency != "EURP" && currency != "EURD") { + text += tr("| %1: %2 ") + .arg(QString::fromStdString(currency)) + .arg(UiUtils::displayCurrencyAmount(assetManager_->getBalance(currency, false, nullptr))); + } } balanceLabel_->setText(text); diff --git a/BlockSettleUILib/Stylesheet/stylesheet.css b/BlockSettleUILib/Stylesheet/stylesheet.css index 962ce682b..1511eca59 100644 --- a/BlockSettleUILib/Stylesheet/stylesheet.css +++ b/BlockSettleUILib/Stylesheet/stylesheet.css @@ -1700,3 +1700,9 @@ QDialog#LoginWindow QWidget#widgetSignup[testEnv="true"] { #labelFuturePrice[selectedLine="true"] { color: #ffffff; } +#labelFutureBalanceValue[positiveColor="true"] { + color: #22C064; +} +#labelFutureBalanceValue[negativeColor="true"] { + color: #EC0A35; +} diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp index 710d1c88f..ee60ea9c4 100644 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ b/BlockSettleUILib/Trading/FuturesTicket.cpp @@ -234,12 +234,17 @@ void FuturesTicket::productSelectionChanged() void FuturesTicket::updatePanel() { // currentProduct_ will be set to EURP or EURD - const double balance = assetManager_ ? + const double balanceIm = assetManager_ ? assetManager_->getBalance(currentProduct_, false, nullptr) : 0.0; auto currentProduct = "EUR"; - auto amountString = UiUtils::displayCurrencyAmount(balance); - QString text = tr("%1 %2").arg(amountString).arg(QString::fromStdString(currentProduct)); - ui_->labelBalanceValue->setText(text); + const double balanceReal = assetManager_ ? + assetManager_->getBalance(currentProduct, false, nullptr) : 0.0; + auto amountStringIm = UiUtils::displayCurrencyAmount(balanceIm); + auto amountStringReal = UiUtils::displayCurrencyAmount(balanceReal); + QString textIm = tr("%1 %2").arg(amountStringIm).arg(QString::fromStdString(currentProduct)); + QString textReal = tr("%1 %2").arg(amountStringReal).arg(QString::fromStdString(currentProduct)); + ui_->labelInitialMargin->setText(textIm); + ui_->labelBalanceValue->setText(textReal); int64_t futuresXbtAmount = currentProduct_ == "EURD" ? assetManager_->futuresXbtAmountDeliverable() : assetManager_->futuresXbtAmountCashSettled(); @@ -254,7 +259,13 @@ void FuturesTicket::updatePanel() currentPrice = (item.second.askPrice + item.second.bidPrice) / 2; break; } - }; + } + ui_->labelFutureBalanceValue->setProperty("positiveColor", futuresXbtAmount > 0); + ui_->labelFutureBalanceValue->setProperty("negativeColor", futuresXbtAmount < 0); + ui_->labelFutureBalanceValue->style()->unpolish(ui_->labelFutureBalanceValue); + ui_->labelFutureBalanceValue->style()->polish(ui_->labelFutureBalanceValue); + ui_->labelFutureBalanceValue->update(); + double profitLoss = assetManager_->profitLoss(futuresXbtAmount, futuresBalance, currentPrice); auto profitLossString = UiUtils::displayCurrencyAmount(profitLoss); QString profitLossText = tr("%1 %2").arg(profitLossString).arg(QString::fromStdString(currentProduct)); From 3994b09def1db23cd470e564500fbdbdb462ad63 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 21 Jan 2021 10:47:04 +0300 Subject: [PATCH 108/146] Auto sign obligation delivery --- BlockSettleUILib/BSTerminalMainWindow.cpp | 80 ++++++++++++++++++++--- BlockSettleUILib/BSTerminalMainWindow.h | 1 + common | 2 +- 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index 79fd1a0c3..c2144b392 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2535,23 +2535,82 @@ void BSTerminalMainWindow::processDeliveryObligationUpdate(const ProxyTerminalPb auto amountStr = UiUtils::displayAmount(resp.to_deliver()); switch (resp.status()) { case ProxyTerminalPb::Response_DeliveryObligationsRequest::PENDING: - addDeferredDialog([this, amountStr] { - auto details = tr("Volume: %1").arg(amountStr); - BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery requested"), details, this).exec(); - }); + if (autoSignQuoteProvider_->autoSignState() == bs::error::ErrorCode::NoError) { // auto-sign enabled + autoSignDeliveryObligation(resp); + } + else { + addDeferredDialog([this, amountStr] { + auto details = tr("Volume: %1").arg(amountStr); + BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery requested"), details, this).exec(); + }); + } break; case ProxyTerminalPb::Response_DeliveryObligationsRequest::DELIVERED: - addDeferredDialog([this, amountStr] { - auto details = tr("Volume: %1").arg(amountStr); - BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery detected"), details, this).exec(); - }); + logMgr_->logger("")->debug("[{}] obligation {} delivered", __func__, resp.id()); + if (autoSignQuoteProvider_->autoSignState() != bs::error::ErrorCode::NoError) { // auto-sign disabled + addDeferredDialog([this, amountStr] { + auto details = tr("Volume: %1").arg(amountStr); + BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery detected"), details, this).exec(); + }); + } break; default: - SPDLOG_LOGGER_ERROR(logMgr_->logger(), "unexpected DeliveryObligationsRequest status"); + SPDLOG_LOGGER_ERROR(logMgr_->logger(), "unexpected DeliveryObligationsRequest status {}", resp.status()); break; } } +#include "TransactionData.h" +void BSTerminalMainWindow::autoSignDeliveryObligation(const ProxyTerminalPb::Response_DeliveryObligationsRequest& request) +{ + const auto& cbInputs = [this, request](bs::FixedXbtInputs&& inputs) + { + std::vector utxos; + for (const auto& input : inputs.inputs) { + utxos.push_back(input.first); + } + + const auto& bsAddress = bs::Address::fromAddressString(request.bs_address()); + const auto& recip = bsAddress.getRecipient(bs::XBTAmount{ (uint64_t)std::abs(request.to_deliver()) }); + + const auto& defWallet = walletsMgr_->getDefaultWallet(); + if (!defWallet) { + logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation] no default wallet"); + return; + } + const int idx = std::rand() % defWallet->getIntAddressCount(); + const auto changeAddr = defWallet->getIntAddressList()[idx]; + uint64_t fee = 321 * utxoReservationMgr_->feeRatePb(); + auto txReq = defWallet->createTXRequest(utxos, { recip }, true, fee, false, changeAddr); + if (!txReq.isValid()) { + logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation] invalid TX request"); + return; + } + fee = txReq.estimateTxVirtSize() * utxoReservationMgr_->feeRatePb(); + if (!fee) { + logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation] invalid fee"); + return; + } + txReq = defWallet->createTXRequest(utxos, { recip }, true, fee, false, changeAddr); + + const auto& cbSigned = [this](const BinaryData& signedTX, bs::error::ErrorCode + , const std::string& errorReason) + { + const auto &pushId = armory_->pushZC(signedTX); + if (pushId.empty()) { + logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation] failed to push ZC"); + } + else { + logMgr_->logger()->debug("[BSTerminalMainWindow::autoSignDeliveryObligation] ZC pushed, id={}", pushId); + } + }; + signContainer_->signTXRequest(txReq, cbSigned, SignContainer::TXSignMode::AutoSign); + }; + utxoReservationMgr_->reserveBestXbtUtxoSet(walletsMgr_->getPrimaryWallet()->walletId() + , std::abs(request.to_deliver()) + (uint64_t)(utxoReservationMgr_->feeRatePb() * 321) + , false, cbInputs, false, bs::UTXOReservationManager::CheckAmount::Enabled, true); +} + void BSTerminalMainWindow::onAuthLeafCreated() { auto authWallet = walletsMgr_->getAuthWallet(); @@ -2580,6 +2639,9 @@ void BSTerminalMainWindow::onAuthLeafCreated() void BSTerminalMainWindow::onDeliverFutureObligations(const QModelIndex& index) { + if (autoSignQuoteProvider_->autoSignState() == bs::error::ErrorCode::NoError) { + return; // don't invoke manual create TX dialog if auto-sign is enabled + } auto deliveryObligationData = orderListModel_->getDeliveryObligationData(index); if (!deliveryObligationData.isValid()) { return; diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index 30546282e..e5fd49b13 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -312,6 +312,7 @@ private slots: void processSetDeliveryAddr(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryAddress &resp); void processDeliveryObligationUpdate(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &resp); + void autoSignDeliveryObligation(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest&); private: enum class ChatInitState diff --git a/common b/common index b494c32a3..d4671e5bd 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit b494c32a3ebc28e23fdbb5e85c1bdf49e7ec00fe +Subproject commit d4671e5bdef580317b4531f7975a2efbc83ece3d From bdc491e9106d719b57f1d839054d6ba0d2791260 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 21 Jan 2021 14:37:20 +0300 Subject: [PATCH 109/146] Auto send delivery obligation with Auth eID wallet --- BlockSettleUILib/BSTerminalMainWindow.cpp | 50 +++++++++++++++++++---- BlockSettleUILib/BSTerminalMainWindow.h | 2 + 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index c2144b392..e3cd63d0d 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2535,7 +2535,7 @@ void BSTerminalMainWindow::processDeliveryObligationUpdate(const ProxyTerminalPb auto amountStr = UiUtils::displayAmount(resp.to_deliver()); switch (resp.status()) { case ProxyTerminalPb::Response_DeliveryObligationsRequest::PENDING: - if (autoSignQuoteProvider_->autoSignState() == bs::error::ErrorCode::NoError) { // auto-sign enabled + if (canSendObligationTX()) { autoSignDeliveryObligation(resp); } else { @@ -2547,7 +2547,7 @@ void BSTerminalMainWindow::processDeliveryObligationUpdate(const ProxyTerminalPb break; case ProxyTerminalPb::Response_DeliveryObligationsRequest::DELIVERED: logMgr_->logger("")->debug("[{}] obligation {} delivered", __func__, resp.id()); - if (autoSignQuoteProvider_->autoSignState() != bs::error::ErrorCode::NoError) { // auto-sign disabled + if (!canSendObligationTX()) { addDeferredDialog([this, amountStr] { auto details = tr("Volume: %1").arg(amountStr); BSMessageBox(BSMessageBox::info, tr("Delivery"), tr("Delivery detected"), details, this).exec(); @@ -2560,7 +2560,6 @@ void BSTerminalMainWindow::processDeliveryObligationUpdate(const ProxyTerminalPb } } -#include "TransactionData.h" void BSTerminalMainWindow::autoSignDeliveryObligation(const ProxyTerminalPb::Response_DeliveryObligationsRequest& request) { const auto& cbInputs = [this, request](bs::FixedXbtInputs&& inputs) @@ -2593,9 +2592,26 @@ void BSTerminalMainWindow::autoSignDeliveryObligation(const ProxyTerminalPb::Res } txReq = defWallet->createTXRequest(utxos, { recip }, true, fee, false, changeAddr); - const auto& cbSigned = [this](const BinaryData& signedTX, bs::error::ErrorCode - , const std::string& errorReason) + const auto& cbSigned = [this, defWallet](const BinaryData& signedTX + , bs::error::ErrorCode errCode, const std::string& errorReason) { + if (errCode == bs::error::ErrorCode::NoError) { + try { + const Tx tx(signedTX); + signContainer_->syncTxComment(defWallet->walletId(), tx.getThisHash() + , "EURD1 delivery"); //TODO: change to non-hardcoded comment if needed + } + catch (const std::exception &e) { + logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation]" + " failed to deser TX: {}", e.what()); + return; + } + } + else { + logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation]" + " failed to sign: {}", errorReason); + return; + } const auto &pushId = armory_->pushZC(signedTX); if (pushId.empty()) { logMgr_->logger()->error("[BSTerminalMainWindow::autoSignDeliveryObligation] failed to push ZC"); @@ -2604,13 +2620,33 @@ void BSTerminalMainWindow::autoSignDeliveryObligation(const ProxyTerminalPb::Res logMgr_->logger()->debug("[BSTerminalMainWindow::autoSignDeliveryObligation] ZC pushed, id={}", pushId); } }; - signContainer_->signTXRequest(txReq, cbSigned, SignContainer::TXSignMode::AutoSign); + signContainer_->signTXRequest(txReq, cbSigned + , (autoSignQuoteProvider_->autoSignState() == bs::error::ErrorCode::NoError) + ? SignContainer::TXSignMode::AutoSign : SignContainer::TXSignMode::Full); }; utxoReservationMgr_->reserveBestXbtUtxoSet(walletsMgr_->getPrimaryWallet()->walletId() , std::abs(request.to_deliver()) + (uint64_t)(utxoReservationMgr_->feeRatePb() * 321) , false, cbInputs, false, bs::UTXOReservationManager::CheckAmount::Enabled, true); } +bool BSTerminalMainWindow::canSendObligationTX() const +{ + if (autoSignQuoteProvider_->autoSignState() == bs::error::ErrorCode::NoError) { + return true; + } + const auto& priWallet = walletsMgr_->getPrimaryWallet(); + if (!priWallet) { + logMgr_->logger()->debug("[{}] no primary wallet", __func__); + return false; + } + const auto& encTypes = priWallet->encryptionTypes(); + if (/*(priWallet->encryptionRank().n == 1) &&*/ !encTypes.empty() + && (encTypes[0] == bs::wallet::EncryptionType::Auth)) { + return true; // also auto-send from 1-of-1 Auth eID encrypted wallet + } + return false; +} + void BSTerminalMainWindow::onAuthLeafCreated() { auto authWallet = walletsMgr_->getAuthWallet(); @@ -2639,7 +2675,7 @@ void BSTerminalMainWindow::onAuthLeafCreated() void BSTerminalMainWindow::onDeliverFutureObligations(const QModelIndex& index) { - if (autoSignQuoteProvider_->autoSignState() == bs::error::ErrorCode::NoError) { + if (canSendObligationTX()) { return; // don't invoke manual create TX dialog if auto-sign is enabled } auto deliveryObligationData = orderListModel_->getDeliveryObligationData(index); diff --git a/BlockSettleUILib/BSTerminalMainWindow.h b/BlockSettleUILib/BSTerminalMainWindow.h index e5fd49b13..a67a852c3 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.h +++ b/BlockSettleUILib/BSTerminalMainWindow.h @@ -147,6 +147,8 @@ Q_OBJECT void openURIDialog(); + bool canSendObligationTX() const; + signals: void armoryServerPromptResultReady(); From 02c305463362971f8c9f9b36b3d41ce87bd6ec3d Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 21 Jan 2021 21:06:24 +0300 Subject: [PATCH 110/146] Avoid crashing on clean start --- BlockSettleUILib/PortfolioWidget.cpp | 18 +++++++++ BlockSettleUILib/PortfolioWidget.h | 8 ++++ .../Settings/NetworkSettingsPage.cpp | 4 +- BlockSettleUILib/Trading/MarketDataWidget.cpp | 38 +++++++++++++++++-- BlockSettleUILib/Trading/MarketDataWidget.h | 10 ++++- GUI/QtWidgets/MainWindow.cpp | 13 +++++++ GUI/QtWidgets/MainWindow.h | 3 ++ GUI/QtWidgets/QtGuiAdapter.cpp | 11 ++++-- common | 2 +- 9 files changed, 95 insertions(+), 12 deletions(-) diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index f87658cc0..4597a7aef 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -70,6 +70,9 @@ PortfolioWidget::PortfolioWidget(QWidget* parent) connect(ui_->treeViewUnconfirmedTransactions, &QTreeView::customContextMenuRequested, this, &PortfolioWidget::showContextMenu); connect(ui_->treeViewUnconfirmedTransactions, &QTreeView::activated, this, &PortfolioWidget::showTransactionDetails); ui_->treeViewUnconfirmedTransactions->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + connect(ui_->widgetMarketData, &MarketDataWidget::needMdConnection, this, &PortfolioWidget::needMdConnection); + connect(ui_->widgetMarketData, &MarketDataWidget::needMdDisconnect, this, &PortfolioWidget::needMdDisconnect); } PortfolioWidget::~PortfolioWidget() = default; @@ -104,6 +107,16 @@ void PortfolioWidget::setAuthorized(bool authorized) ui_->widgetMarketData->setAuthorized(authorized); } +void PortfolioWidget::onMDConnected() +{ + ui_->widgetMarketData->onMDConnected(); +} + +void PortfolioWidget::onMDDisconnected() +{ + ui_->widgetMarketData->onMDDisconnected(); +} + void PortfolioWidget::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields& fields) { @@ -179,6 +192,11 @@ void PortfolioWidget::onBalance(const std::string& currency, double balance) ui_->widgetCCProtfolio->onBalance(currency, balance); } +void PortfolioWidget::onEnvConfig(int value) +{ + ui_->widgetMarketData->onEnvConfig(value); +} + void PortfolioWidget::showTransactionDetails(const QModelIndex& index) { if (filter_) { diff --git a/BlockSettleUILib/PortfolioWidget.h b/BlockSettleUILib/PortfolioWidget.h index a8837cc6c..b1a0d542a 100644 --- a/BlockSettleUILib/PortfolioWidget.h +++ b/BlockSettleUILib/PortfolioWidget.h @@ -14,6 +14,7 @@ #include #include #include +#include "ApplicationSettings.h" #include "CommonTypes.h" #include "SignerDefs.h" #include "TransactionsWidgetInterface.h" @@ -56,12 +57,19 @@ Q_OBJECT void shortcutActivated(ShortcutType s) override; void setAuthorized(bool authorized); + void onMDConnected(); + void onMDDisconnected(); void onMDUpdated(bs::network::Asset::Type, const QString& security , const bs::network::MDFields&); void onHDWallet(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData&); void onWalletBalance(const bs::sync::WalletBalanceData&); void onBalance(const std::string& currency, double balance); + void onEnvConfig(int); + +signals: + void needMdConnection(ApplicationSettings::EnvConfiguration); + void needMdDisconnect(); private slots: void showTransactionDetails(const QModelIndex& index); diff --git a/BlockSettleUILib/Settings/NetworkSettingsPage.cpp b/BlockSettleUILib/Settings/NetworkSettingsPage.cpp index eebc6aa02..39730e995 100644 --- a/BlockSettleUILib/Settings/NetworkSettingsPage.cpp +++ b/BlockSettleUILib/Settings/NetworkSettingsPage.cpp @@ -182,12 +182,12 @@ void NetworkSettingsPage::displayArmorySettings() } else { selectedServerIndex = armorySrvCurrent_; - if (selectedServerIndex >= armoryServers_.size()) { + if ((selectedServerIndex >= armoryServers_.size()) || (selectedServerIndex < 0)) { return; } selectedServer = armoryServers_.at(selectedServerIndex); connectedServerIndex = armorySrvConnected_; - if (connectedServerIndex >= armoryServers_.size()) { + if ((connectedServerIndex >= armoryServers_.size()) || (connectedServerIndex < 0)) { return; } connectedServerSettings = armoryServers_.at(connectedServerIndex); diff --git a/BlockSettleUILib/Trading/MarketDataWidget.cpp b/BlockSettleUILib/Trading/MarketDataWidget.cpp index 9842edd06..4bf622b04 100644 --- a/BlockSettleUILib/Trading/MarketDataWidget.cpp +++ b/BlockSettleUILib/Trading/MarketDataWidget.cpp @@ -170,10 +170,23 @@ void MarketDataWidget::OnMDDisconnected() void MarketDataWidget::ChangeMDSubscriptionState() { - if (mdProvider_->IsConnectionActive()) { - mdProvider_->DisconnectFromMDSource(); - } else { - mdProvider_->SubscribeToMD(); + if (mdProvider_) { + if (mdProvider_->IsConnectionActive()) { + mdProvider_->DisconnectFromMDSource(); + } else { + mdProvider_->SubscribeToMD(); + } + } + else { + if (envConf_ == ApplicationSettings::EnvConfiguration::Unknown) { + return; // pop up error? + } + if (connected_) { + emit needMdDisconnect(); + } + else { + emit needMdConnection(envConf_); + } } } @@ -217,12 +230,29 @@ MarketSelectedInfo MarketDataWidget::getCurrentlySelectedInfo() const return getRowInfo(index); } +void MarketDataWidget::onMDConnected() +{ + connected_ = true; + OnMDConnected(); +} + +void MarketDataWidget::onMDDisconnected() +{ + connected_ = false; + OnMDDisconnected(); +} + void MarketDataWidget::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields& fields) { marketDataModel_->onMDUpdated(assetType, security, fields); } +void MarketDataWidget::onEnvConfig(int value) +{ + envConf_ = static_cast(value); +} + void MarketDataWidget::onMDRejected(const std::string &security, const std::string &reason) { if (security.empty()) { diff --git a/BlockSettleUILib/Trading/MarketDataWidget.h b/BlockSettleUILib/Trading/MarketDataWidget.h index 3ef166914..5cf989d03 100644 --- a/BlockSettleUILib/Trading/MarketDataWidget.h +++ b/BlockSettleUILib/Trading/MarketDataWidget.h @@ -22,7 +22,6 @@ namespace Ui { class MarketDataWidget; }; -class ApplicationSettings; class MarketDataModel; class MarketDataProvider; class MDCallbacksQt; @@ -57,8 +56,11 @@ Q_OBJECT void setAuthorized(bool authorized); MarketSelectedInfo getCurrentlySelectedInfo() const; + void onMDConnected(); + void onMDDisconnected(); void onMDUpdated(bs::network::Asset::Type, const QString& security , const bs::network::MDFields&); + void onEnvConfig(int); signals: void CurrencySelected(const MarketSelectedInfo& selectedInfo); @@ -66,6 +68,8 @@ Q_OBJECT void BidClicked(const MarketSelectedInfo& selectedInfo); void MDHeaderClicked(); void clicked(); + void needMdConnection(ApplicationSettings::EnvConfiguration); + void needMdDisconnect(); private slots: void resizeAndExpand(); @@ -95,7 +99,9 @@ private slots: std::shared_ptr mdHeader_; bool filteredView_ = true; std::shared_ptr mdProvider_; - bool authorized_{ false }; + bool authorized_{ false }; + bool connected_{ false }; + ApplicationSettings::EnvConfiguration envConf_{ ApplicationSettings::EnvConfiguration::Unknown }; }; #include diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 184498420..149de612d 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -160,6 +160,7 @@ void MainWindow::onSetting(int setting, const QVariant &value) envConfig_ = newEnvCfg; //TODO: maybe initiate relog and Celer/proxy reconnect } + ui_->widgetPortfolio->onEnvConfig(value.toInt()); } break; case ApplicationSettings::rememberLoginUserName: @@ -979,6 +980,16 @@ void MainWindow::onMatchingLogout() ui_->widgetRFQReply->onMatchingLogout(); } +void bs::gui::qt::MainWindow::onMDConnected() +{ + ui_->widgetPortfolio->onMDConnected(); +} + +void bs::gui::qt::MainWindow::onMDDisconnected() +{ + ui_->widgetPortfolio->onMDDisconnected(); +} + void bs::gui::qt::MainWindow::onNewSecurity(const std::string& name, bs::network::Asset::Type at) { ui_->widgetRFQ->onNewSecurity(name, at); @@ -1243,6 +1254,8 @@ void MainWindow::initWidgets() initTransactionsView(); ui_->widgetPortfolio->init(logger_); + connect(ui_->widgetPortfolio, &PortfolioWidget::needMdConnection, this, &MainWindow::needMdConnection); + connect(ui_->widgetPortfolio, &PortfolioWidget::needMdDisconnect, this, &MainWindow::needMdDisconnect); orderListModel_ = std::make_shared(this); dialogMgr_ = std::make_shared(this); diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index af5979f61..88e625cdd 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -102,6 +102,8 @@ namespace bs { , const std::string &userId); void onMatchingLogout(); + void onMDConnected(); + void onMDDisconnected(); void onNewSecurity(const std::string& name, bs::network::Asset::Type); void onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &); @@ -190,6 +192,7 @@ namespace bs { void needCancelLogin(); void needMatchingLogout(); void needMdConnection(ApplicationSettings::EnvConfiguration); + void needMdDisconnect(); void needNewAuthAddress(); void needSubmitAuthAddress(const bs::Address&); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 1c4183f34..1f31e86be 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -1668,9 +1668,11 @@ bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) return true; } switch (msg.data_case()) { + case MktDataMessage::kConnected: + return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMDConnected(); }); case MktDataMessage::kDisconnected: mdInstrumentsReceived_ = false; - break; + return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMDDisconnected(); }); case MktDataMessage::kNewSecurity: return processSecurity(msg.new_security().name(), msg.new_security().asset_type()); case MktDataMessage::kAllInstrumentsReceived: @@ -1685,8 +1687,11 @@ bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) bool QtGuiAdapter::processSecurity(const std::string& name, int assetType) { - assetTypes_[name] = static_cast(assetType); - return true; + const auto &at = static_cast(assetType); + assetTypes_[name] = at; + return QMetaObject::invokeMethod(mainWindow_, [this, name, at] { + mainWindow_->onNewSecurity(name, at); + }); } bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) diff --git a/common b/common index 6acdd3b5e..62fbb267d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 6acdd3b5ed2624014f7c2aea719e8cf701fbeeed +Subproject commit 62fbb267d406b7a966f373b9ba2c4881f5abe066 From 0886b986160bfd0ebba7841e2da45c89a39e033a Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 22 Jan 2021 11:36:00 +0300 Subject: [PATCH 111/146] uncomment rank check --- BlockSettleUILib/BSTerminalMainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BlockSettleUILib/BSTerminalMainWindow.cpp b/BlockSettleUILib/BSTerminalMainWindow.cpp index e3cd63d0d..dfdd82b5e 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.cpp +++ b/BlockSettleUILib/BSTerminalMainWindow.cpp @@ -2640,7 +2640,7 @@ bool BSTerminalMainWindow::canSendObligationTX() const return false; } const auto& encTypes = priWallet->encryptionTypes(); - if (/*(priWallet->encryptionRank().n == 1) &&*/ !encTypes.empty() + if ((priWallet->encryptionRank().n == 1) && !encTypes.empty() && (encTypes[0] == bs::wallet::EncryptionType::Auth)) { return true; // also auto-send from 1-of-1 Auth eID encrypted wallet } From 878e49d0aee1cb61ea368136680a97f7eb74abe6 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 22 Jan 2021 12:03:15 +0300 Subject: [PATCH 112/146] Fix data races in SignerAdapter --- Core/SignerAdapter.cpp | 79 +++++++++++++++++++----------------------- Core/SignerAdapter.h | 3 +- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 2a889fecb..4f9dd66b3 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -376,12 +376,12 @@ bool SignerAdapter::processNewKeyResponse(bool acceptNewKey) bool SignerAdapter::processStartWalletSync(const bs::message::Envelope &env) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cbWallets = [this, msgId=env.id] (const std::vector &wi) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -406,9 +406,8 @@ bool SignerAdapter::processStartWalletSync(const bs::message::Envelope &env) keyRank->set_m(entry.encryptionRank.m); keyRank->set_n(entry.encryptionRank.n); } - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; signer_->syncWalletInfo(cbWallets); return true; @@ -417,12 +416,12 @@ bool SignerAdapter::processStartWalletSync(const bs::message::Envelope &env) bool SignerAdapter::processSyncAddresses(const bs::message::Envelope &env , const SignerMessage_SyncAddresses &request) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cb = [this, msgId = env.id, walletId = request.wallet_id()] (bs::sync::SyncState st) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -430,9 +429,8 @@ bool SignerAdapter::processSyncAddresses(const bs::message::Envelope &env msgResp->set_wallet_id(walletId); msgResp->set_status(static_cast(st)); - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; std::set addrSet; for (const auto &addr : request.addresses()) { @@ -445,13 +443,13 @@ bool SignerAdapter::processSyncAddresses(const bs::message::Envelope &env bool SignerAdapter::processSyncNewAddresses(const bs::message::Envelope &env , const SignerMessage_SyncNewAddresses &request) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); if (request.single()) { const auto &cb = [this, msgId = env.id, walletId = request.wallet_id()] (const bs::Address &addr) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -459,9 +457,8 @@ bool SignerAdapter::processSyncNewAddresses(const bs::message::Envelope &env msgResp->set_wallet_id(walletId); msgResp->add_addresses()->set_address(addr.display()); - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; if (request.indices_size() != 1) { logger_->error("[{}] not a single new address request", __func__); @@ -473,8 +470,8 @@ bool SignerAdapter::processSyncNewAddresses(const bs::message::Envelope &env const auto &cb = [this, msgId=env.id, walletId = request.wallet_id()] (const std::vector> &addrIdxPairs) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -486,9 +483,8 @@ bool SignerAdapter::processSyncNewAddresses(const bs::message::Envelope &env msgPair->set_index(aiPair.second); } - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; std::vector indices; indices.reserve(request.indices_size()); @@ -503,12 +499,12 @@ bool SignerAdapter::processSyncNewAddresses(const bs::message::Envelope &env bool SignerAdapter::processExtendAddrChain(const bs::message::Envelope &env , const SignerMessage_ExtendAddrChain &request) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cb = [this, msgId = env.id, walletId = request.wallet_id()] (const std::vector> &addrIdxPairs) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -520,9 +516,8 @@ bool SignerAdapter::processExtendAddrChain(const bs::message::Envelope &env msgPair->set_index(aiPair.second); } - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; signer_->extendAddressChain(request.wallet_id(), request.count(), request.ext_int(), cb); return true; @@ -531,12 +526,12 @@ bool SignerAdapter::processExtendAddrChain(const bs::message::Envelope &env bool SignerAdapter::processSyncWallet(const bs::message::Envelope &env , const std::string &walletId) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cb = [this, msgId=env.id, walletId] (bs::sync::WalletData data) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -563,9 +558,8 @@ bool SignerAdapter::processSyncWallet(const bs::message::Envelope &env msgTxCom->set_comment(txCom.comment); } - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; signer_->syncWallet(walletId, cb); return true; @@ -574,12 +568,12 @@ bool SignerAdapter::processSyncWallet(const bs::message::Envelope &env bool SignerAdapter::processSyncHdWallet(const bs::message::Envelope &env , const std::string &walletId) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cb = [this, msgId = env.id, walletId] (bs::sync::HDWalletData data) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -587,9 +581,8 @@ bool SignerAdapter::processSyncHdWallet(const bs::message::Envelope &env *msgResp = data.toCommonMessage(); msgResp->set_wallet_id(walletId); - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; signer_->syncHDWallet(walletId, cb); return true; @@ -615,18 +608,17 @@ bool SignerAdapter::processSyncTxComment(const SignerMessage_SyncTxComment &requ bool SignerAdapter::processSetSettlId(const bs::message::Envelope &env , const SignerMessage_SetSettlementId &request) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cb = [this, msgId=env.id](bool result) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; msg.set_settl_id_set(result); - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; signer_->setSettlementID(request.wallet_id() , BinaryData::fromString(request.settlement_id()), cb); @@ -667,12 +659,12 @@ bool SignerAdapter::processSignSettlementTx(const bs::message::Envelope& env bool SignerAdapter::processGetRootPubKey(const bs::message::Envelope &env , const std::string &walletId) { - requests_[env.id] = env.sender; + requests_.put(env.id, env.sender); const auto &cb = [this, msgId=env.id, walletId] (bool result, const SecureBinaryData &key) { - const auto &itReq = requests_.find(msgId); - if (itReq == requests_.end()) { + auto sender = requests_.take(msgId); + if (!sender) { return; } SignerMessage msg; @@ -681,9 +673,8 @@ bool SignerAdapter::processGetRootPubKey(const bs::message::Envelope &env msgResp->set_pub_key(key.toBinStr()); msgResp->set_success(result); - Envelope envResp{ msgId, user_, itReq->second, {}, {}, msg.SerializeAsString() }; + Envelope envResp{ msgId, user_, sender, {}, {}, msg.SerializeAsString() }; pushFill(envResp); - requests_.erase(itReq); }; signer_->getRootPubkey(walletId, cb); return true; diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 1608ea9a2..410084f41 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -14,6 +14,7 @@ #include "FutureValue.h" #include "HeadlessContainer.h" #include "Message/Adapter.h" +#include "ThreadSafeContainers.h" namespace spdlog { class logger; @@ -119,7 +120,7 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget std::string curServerId_; std::string connKey_; - std::map> requests_; + bs::ThreadSafeMap> requests_; std::unordered_map autoSignRequests_; }; From 6e02d5acd0dcd6ed0528d08f799727c01a10ca0a Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 22 Jan 2021 12:03:52 +0300 Subject: [PATCH 113/146] Fix minor startup TSAN reports --- GUI/QtWidgets/QtGuiAdapter.cpp | 12 +++++++++--- GUI/QtWidgets/QtGuiAdapter.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 1c4183f34..ffbdb878a 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -207,8 +207,11 @@ void QtGuiAdapter::run(int &argc, char **argv) QPixmap splashLogo(logoIcon); const int splashScreenWidth = 400; - splashScreen_ = new BSTerminalSplashScreen(splashLogo.scaledToWidth(splashScreenWidth - , Qt::SmoothTransformation)); + { + std::lock_guard lock(mutex_); + splashScreen_ = new BSTerminalSplashScreen(splashLogo.scaledToWidth(splashScreenWidth + , Qt::SmoothTransformation)); + } updateSplashProgress(); splashScreen_->show(); @@ -400,9 +403,11 @@ bool QtGuiAdapter::processAdminMessage(const Envelope &env) break; } break; - case AdministrativeMessage::kComponentLoading: + case AdministrativeMessage::kComponentLoading: { + std::lock_guard lock(mutex_); loadingComponents_.insert(msg.component_loading()); break; + } default: break; } updateSplashProgress(); @@ -639,6 +644,7 @@ void QtGuiAdapter::updateStates() void QtGuiAdapter::updateSplashProgress() { + std::lock_guard lock(mutex_); if (!splashScreen_ || createdComponents_.empty()) { return; } diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index b9b21c818..baa659418 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -216,6 +216,7 @@ private slots: bs::gui::qt::MainWindow * mainWindow_{ nullptr }; BSTerminalSplashScreen * splashScreen_{ nullptr }; + std::recursive_mutex mutex_; std::set createdComponents_; std::set loadingComponents_; int armoryState_{ -1 }; From 8bd5f20ce0d2bbaa8c307978621da7ef1bb2626c Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 22 Jan 2021 13:13:57 +0300 Subject: [PATCH 114/146] Fix crash in UtxoReservationManager --- BlockSettleUILib/UtxoReservationManager.cpp | 2 +- Core/BsServerAdapter.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/BlockSettleUILib/UtxoReservationManager.cpp b/BlockSettleUILib/UtxoReservationManager.cpp index fb26f2822..16552e6be 100644 --- a/BlockSettleUILib/UtxoReservationManager.cpp +++ b/BlockSettleUILib/UtxoReservationManager.cpp @@ -234,7 +234,7 @@ void bs::UTXOReservationManager::getBestXbtUtxoSet(const HDWalletId& walletId, b BTCNumericTypes::balance_type bs::UTXOReservationManager::getAvailableCCUtxoSum(const CCProductName& CCProduct) const { - const auto& ccWallet = walletsManager_->getCCWallet(CCProduct); + const auto& ccWallet = walletsManager_ ? walletsManager_->getCCWallet(CCProduct) : nullptr; if (!ccWallet) { return {}; } diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 28492a0d8..ac7e51b77 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -154,6 +154,7 @@ bool BsServerAdapter::processPuBKeyResponse(bool allowed) } futPuBkey_->setValue(allowed); futPuBkey_.reset(); + return true; } bool BsServerAdapter::processTimeout(const std::string& id) @@ -165,6 +166,7 @@ bool BsServerAdapter::processTimeout(const std::string& id) } itTO->second(); timeouts_.erase(itTO); + return true; } bool BsServerAdapter::processOpenConnection() From c505527c062d6c9cb3e0e919ec9d1ed9413a2f24 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 22 Jan 2021 14:02:46 +0300 Subject: [PATCH 115/146] Prevent chart tab from crashing the terminal --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 62fbb267d..ae6c88259 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 62fbb267d406b7a966f373b9ba2c4881f5abe066 +Subproject commit ae6c88259044f1bf1c12115db7408910ead47054 From 558df85afc5739292554ba435abe31d085cf8ae0 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 22 Jan 2021 14:02:58 +0300 Subject: [PATCH 116/146] Prevent chart tab from crashing the terminal --- BlockSettleUILib/ChartWidget.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/BlockSettleUILib/ChartWidget.cpp b/BlockSettleUILib/ChartWidget.cpp index 084ce91cd..3088c05f8 100644 --- a/BlockSettleUILib/ChartWidget.cpp +++ b/BlockSettleUILib/ChartWidget.cpp @@ -364,6 +364,9 @@ void ChartWidget::ProcessOhlcHistoryResponse(const std::string& data) logger_->error("Empty data received from mdhs."); return; } + if (!candlesticksChart_ || !volumeChart_) { + return; + } OhlcResponse response; if (!response.ParseFromString(data)) { @@ -459,6 +462,9 @@ void ChartWidget::ProcessOhlcHistoryResponse(const std::string& data) void ChartWidget::ProcessEodResponse(const std::string& data) { + if (!candlesticksChart_ || !volumeChart_) { + return; + } eodRequestSent_ = false; EodPrice eodPrice; eodPrice.ParseFromString(data); @@ -489,6 +495,9 @@ double ChartWidget::CountOffsetFromRightBorder() void ChartWidget::CheckToAddNewCandle(qint64 stamp) { + if (!candlesticksChart_ || !volumeChart_) { + return; + } if (stamp <= newestCandleTimestamp_ + IntervalWidth(dateRange_.checkedId()) || !volumeChart_->data()->size()) { return; } @@ -526,6 +535,9 @@ void ChartWidget::DrawCrossfire(QMouseEvent* event) void ChartWidget::UpdatePrintFlag() { + if (!candlesticksChart_ || !volumeChart_) { + return; + } if (candlesticksChart_->data()->isEmpty()) { lastPrintFlag_->setVisible(false); return; @@ -567,6 +579,9 @@ bool ChartWidget::needLoadNewData(const QCPRange& range, const QSharedPointerdata(); if (needLoadNewData(range, data)) { if (qFuzzyCompare(prevRequestStamp, data->constBegin()->key)) { @@ -716,6 +731,9 @@ QString ChartWidget::GetFormattedStamp(double timestamp) void ChartWidget::UpdateOHLCInfo(double width, double timestamp) { + if (!candlesticksChart_ || !volumeChart_) { + return; + } auto ohlcValue = *candlesticksChart_->data()->findBegin(timestamp + width / 2); auto volumeValue = *volumeChart_->data()->findBegin(timestamp + width / 2); //ohlcValue.close >= ohlcValue.open ? c_greenColor : c_redColor From 61e7376ef7c12533e24cd8a9eded42400881f48d Mon Sep 17 00:00:00 2001 From: Ation Date: Fri, 22 Jan 2021 13:04:59 +0200 Subject: [PATCH 117/146] Display delivery status --- BlockSettleUILib/OrderListModel.cpp | 13 ++++++++++--- BlockSettleUILib/OrderListModel.h | 7 ++++++- common | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 42a2d57d9..5230d557f 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -882,14 +882,16 @@ void OrderListModel::DisplayFuturesDeliveryRow(const Blocksettle::Communication: static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); marketItem->rows_.emplace_back(make_unique(QStringLiteral("XBT/EUR") - , &marketItem->idx_, obligation.to_deliver(), obligation.price(), obligation.bs_address())); + , &marketItem->idx_, obligation.to_deliver(), obligation.price(), obligation.bs_address() + , obligation.status() == Blocksettle::Communication::ProxyTerminalPb::Response::DeliveryObligationsRequest::DELIVERED)); endInsertRows(); } OrderListModel::FuturesDeliveryGroup::FuturesDeliveryGroup(const QString &sec, IndexHelper *parent , int64_t quantity, double price - , const std::string& bsAddress) + , const std::string& bsAddress + , const bool delivered) : Group(sec, parent) { quantity_ = static_cast(quantity) / BTCNumericTypes::BalanceDivider; @@ -901,6 +903,8 @@ OrderListModel::FuturesDeliveryGroup::FuturesDeliveryGroup(const QString &sec, I if (quantity < 0) { toDeliver_ = bs::XBTAmount{ static_cast(-quantity) }; } + + delivered_ = delivered; } QVariant OrderListModel::FuturesDeliveryGroup::getQuantity() const @@ -939,6 +943,9 @@ QVariant OrderListModel::FuturesDeliveryGroup::getPrice() const QVariant OrderListModel::FuturesDeliveryGroup::getStatus() const { if (quantity_ < 0) { + if (delivered_) { + return tr("Delivered"); + } return tr("Create TX"); } @@ -947,7 +954,7 @@ QVariant OrderListModel::FuturesDeliveryGroup::getStatus() const bool OrderListModel::FuturesDeliveryGroup::deliveryRequired() const { - return toDeliver_.GetValue() != 0; + return toDeliver_.GetValue() != 0 && !delivered_; } OrderListModel::DeliveryObligationData OrderListModel::FuturesDeliveryGroup::getDeliveryObligationData() const diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 00b3a01d1..153de9fb6 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -186,7 +186,10 @@ public slots: struct FuturesDeliveryGroup : public Group { - FuturesDeliveryGroup(const QString &sec, IndexHelper *parent, int64_t quantity, double price, const std::string& bsAddress); + FuturesDeliveryGroup(const QString &sec, IndexHelper *parent + , int64_t quantity, double price + , const std::string& bsAddress + , const bool delivered); ~FuturesDeliveryGroup() override = default; @@ -210,6 +213,8 @@ public slots: bs::XBTAmount toDeliver_; std::string bsAddress_; + + bool delivered_; }; diff --git a/common b/common index d4671e5bd..16125bf11 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit d4671e5bdef580317b4531f7975a2efbc83ece3d +Subproject commit 16125bf11b5f36ac48ae579f73c5c08a8beeb58a From 049b07685147307a736f2638ad81fde321adde48 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Tue, 26 Jan 2021 12:00:11 +0300 Subject: [PATCH 118/146] Per-order blotter updates --- BlockSettleUILib/OrderListModel.cpp | 26 ++++++++++++++++---------- BlockSettleUILib/OrderListModel.h | 2 ++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index 5230d557f..b724c5625 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -672,14 +672,21 @@ void OrderListModel::reset() void OrderListModel::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message) { - // Save latest selected index first - resetLatestChangedStatus(message); - // OrderListModel supposed to work correctly when orders states updated one by one. - // We don't use this anymore (server sends all active orders every time) so just clear old caches. - // Remove this if old behavior is needed - reset(); - // Use some fake orderId so old code works correctly - int orderId = 0; + std::set newIds; + for (const auto &data : message.orders()) { + newIds.insert(data.id()); + } + bool orderRemoved = false; + for (const auto &knownId : knownIds_) { + if (newIds.find(knownId) == newIds.end()) { + orderRemoved = true; + break; + } + } + knownIds_ = std::move(newIds); + if (orderRemoved) { + reset(); + } for (const auto &data : message.orders()) { bs::network::Order order; @@ -700,8 +707,7 @@ void OrderListModel::processUpdateOrders(const Blocksettle::Communication::Proxy order.assetType = static_cast(data.trade_type()); - orderId += 1; - order.exchOrderId = QString::number(orderId); + order.exchOrderId = QString::fromStdString(data.id()); order.side = bs::network::Side::Type(data.side()); order.pendingStatus = data.status_text(); order.dateTime = QDateTime::fromMSecsSinceEpoch(data.timestamp_ms()); diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 153de9fb6..67479d745 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -291,6 +291,8 @@ public slots: std::vector> sortedPeviousOrderStatuses_{}; QDateTime latestChangedTimestamp_; + std::set knownIds_; + bool connected_{}; }; From a047f61f200d6df34d940180939de6106b9b1d58 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Fri, 5 Feb 2021 17:19:00 +0300 Subject: [PATCH 119/146] Use per-order blotter updates --- BlockSettleUILib/OrderListModel.cpp | 97 ++++++++++++++++------------- BlockSettleUILib/OrderListModel.h | 4 +- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index b724c5625..aaae5b5ef 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -24,6 +24,37 @@ namespace { const auto kSettledColor = QColor{0x22, 0xC0, 0x64}; const auto kFailedColor = QColor{0xEC, 0x0A, 0x35}; + bs::network::Order convertOrder(const bs::types::Order &data) { + bs::network::Order order; + + switch (data.status()) { + case bs::types::ORDER_STATUS_PENDING: + order.status = bs::network::Order::Pending; + break; + case bs::types::ORDER_STATUS_FILLED: + order.status = bs::network::Order::Filled; + break; + case bs::types::ORDER_STATUS_VOID: + order.status = bs::network::Order::Failed; + break; + default: + break; + } + + order.assetType = static_cast(data.trade_type()); + + order.exchOrderId = QString::fromStdString(data.id()); + order.side = bs::network::Side::Type(data.side()); + order.pendingStatus = data.status_text(); + order.dateTime = QDateTime::fromMSecsSinceEpoch(data.timestamp_ms()); + order.product = data.product(); + order.quantity = data.quantity(); + order.security = data.product() + "/" + data.product_against(); + order.price = data.price(); + + return order; + } + } // namespace double getOrderValue(const bs::network::Order& order) @@ -401,6 +432,9 @@ void OrderListModel::onMessageFromPB(const Blocksettle::Communication::ProxyTerm case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrdersObligations: processUpdateOrders(response.update_orders_obligations()); break; + case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrder: + processUpdateOrder(response.update_order()); + break; default: break; } @@ -672,56 +706,35 @@ void OrderListModel::reset() void OrderListModel::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message) { - std::set newIds; - for (const auto &data : message.orders()) { - newIds.insert(data.id()); - } - bool orderRemoved = false; - for (const auto &knownId : knownIds_) { - if (newIds.find(knownId) == newIds.end()) { - orderRemoved = true; - break; - } - } - knownIds_ = std::move(newIds); - if (orderRemoved) { - reset(); - } + reset(); for (const auto &data : message.orders()) { - bs::network::Order order; - - switch (data.status()) { - case bs::types::ORDER_STATUS_PENDING: - order.status = bs::network::Order::Pending; - break; - case bs::types::ORDER_STATUS_FILLED: - order.status = bs::network::Order::Filled; - break; - case bs::types::ORDER_STATUS_VOID: - order.status = bs::network::Order::Failed; - break; - default: - break; - } - - order.assetType = static_cast(data.trade_type()); - - order.exchOrderId = QString::fromStdString(data.id()); - order.side = bs::network::Side::Type(data.side()); - order.pendingStatus = data.status_text(); - order.dateTime = QDateTime::fromMSecsSinceEpoch(data.timestamp_ms()); - order.product = data.product(); - order.quantity = data.quantity(); - order.security = data.product() + "/" + data.product_against(); - order.price = data.price(); - + auto order = convertOrder(data); onOrderUpdated(order); } DisplayFuturesDeliveryRow(message.obligation()); } +void OrderListModel::processUpdateOrder(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrder &msg) +{ + auto order = convertOrder(msg.order()); + switch (msg.action()) { + case bs::types::ACTION_CREATED: + case bs::types::ACTION_UPDATED: { + onOrderUpdated(order); + break; + } + case bs::types::ACTION_REMOVED: { + auto found = findItem(order); + removeRowIfContainerChanged(order, found.second); + break; + } + default: + break; + } +} + void OrderListModel::resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message) { latestChangedTimestamp_ = {}; diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 67479d745..299b562d9 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -27,6 +27,7 @@ namespace Blocksettle { class Response; class Response_DeliveryObligationsRequest; class Response_UpdateOrdersAndObligations; + class Response_UpdateOrder; } } } @@ -273,6 +274,7 @@ public slots: void reset(); void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &msg); + void processUpdateOrder(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrder &msg); void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message); void DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation); @@ -291,8 +293,6 @@ public slots: std::vector> sortedPeviousOrderStatuses_{}; QDateTime latestChangedTimestamp_; - std::set knownIds_; - bool connected_{}; }; From aa16d4fa176f9d53374019df1bafd1ee25faeed8 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 10 Feb 2021 18:48:40 +0300 Subject: [PATCH 120/146] Updated common --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index ae6c88259..8c426fc6c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ae6c88259044f1bf1c12115db7408910ead47054 +Subproject commit 8c426fc6c1033a3e4737c13bdb36a8c9a4863f35 From 8c497a305c47a1106fe78e07274dc867f99f74e8 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 10 Feb 2021 19:38:09 +0300 Subject: [PATCH 121/146] reduce amount of .ui files included in GUI/QtWidgets --- BlockSettleUILib/ButtonMenu.h | 2 +- GUI/QtWidgets/CMakeLists.txt | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/BlockSettleUILib/ButtonMenu.h b/BlockSettleUILib/ButtonMenu.h index 4fbe00bcb..85179c519 100644 --- a/BlockSettleUILib/ButtonMenu.h +++ b/BlockSettleUILib/ButtonMenu.h @@ -33,4 +33,4 @@ Q_OBJECT QPushButton *parentButton_; }; -#endif // __PUSH_BUTTON_MENU_H__ \ No newline at end of file +#endif // __PUSH_BUTTON_MENU_H__ diff --git a/GUI/QtWidgets/CMakeLists.txt b/GUI/QtWidgets/CMakeLists.txt index fe324cdbe..8f3d36451 100644 --- a/GUI/QtWidgets/CMakeLists.txt +++ b/GUI/QtWidgets/CMakeLists.txt @@ -10,14 +10,14 @@ FILE(GLOB HEADERS ) FILE(GLOB UI_FILES - ${BLOCKSETTLE_UI_INCLUDE_DIR}/*.ui - ${BLOCKSETTLE_UI_INCLUDE_DIR}/CustomControls/*.ui + ${BLOCKSETTLE_UI_INCLUDE_DIR}/BSTerminalMainWindow.ui +# ${BLOCKSETTLE_UI_INCLUDE_DIR}/CustomControls/*.ui ${BLOCKSETTLE_UI_INCLUDE_DIR}/InfoDialogs/*.ui - ${BLOCKSETTLE_UI_INCLUDE_DIR}/ManageEncryption/*.ui - ${BLOCKSETTLE_UI_INCLUDE_DIR}/Settings/*.ui - ${BLOCKSETTLE_UI_INCLUDE_DIR}/Trading/*.ui +# ${BLOCKSETTLE_UI_INCLUDE_DIR}/ManageEncryption/*.ui +# ${BLOCKSETTLE_UI_INCLUDE_DIR}/Settings/*.ui +# ${BLOCKSETTLE_UI_INCLUDE_DIR}/Trading/*.ui ${BLOCKSETTLE_UI_INCLUDE_DIR}/ChatUI/*.ui - ${BLOCKSETTLE_UI_INCLUDE_DIR}/ChatUI/OTCShieldWidgets/*.ui +# ${BLOCKSETTLE_UI_INCLUDE_DIR}/ChatUI/OTCShieldWidgets/*.ui ) INCLUDE_DIRECTORIES(../../BlockSettleUILib) From 434bb3c08581548d067e0d5ba1e3ff5ff6b09ca6 Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 10 Feb 2021 19:14:34 +0200 Subject: [PATCH 122/146] fix order blotter expanding on insert --- BlockSettleUILib/OrdersView.cpp | 18 +++++++++++------- common | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/BlockSettleUILib/OrdersView.cpp b/BlockSettleUILib/OrdersView.cpp index 54e078b4a..26273cada 100644 --- a/BlockSettleUILib/OrdersView.cpp +++ b/BlockSettleUILib/OrdersView.cpp @@ -73,18 +73,22 @@ void OrdersView::onSelectRow(const QPersistentModelIndex &row) scrollTo(row, QAbstractItemView::EnsureVisible); } -void OrdersView::onRowsInserted(const QModelIndex &parent, int first, int) +void OrdersView::onRowsInserted(const QModelIndex &parent, int first, int last) { if (!parent.isValid()) { return; } - if (!collapsed_.contains(UiUtils::modelPath(parent, model_))) { - expand(parent); - } - else { - setHasNewItemFlag(parent, true); - } + if (!collapsed_.contains(UiUtils::modelPath(parent, model_))) { + auto topLevelIndex = parent; + while (topLevelIndex.isValid()) { + expand(topLevelIndex); + topLevelIndex = topLevelIndex.parent(); + } + } + else { + setHasNewItemFlag(parent, true); + } if (selectionModel()->hasSelection()) { scrollTo(selectionModel()->selectedIndexes().at(0), QAbstractItemView::EnsureVisible); diff --git a/common b/common index 16125bf11..1c54561cf 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 16125bf11b5f36ac48ae579f73c5c08a8beeb58a +Subproject commit 1c54561cf7aeb1ae672d1a6b718d481887a36ad3 From 43a3849a1b870646e7c18b77ec735661a3b09f38 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 16 Feb 2021 20:22:51 +0300 Subject: [PATCH 123/146] dedicated message clock --- Core/ApiJson.cpp | 2 +- Core/BsServerAdapter.cpp | 2 +- Core/SettlementAdapter.cpp | 4 ++-- UnitTests/TestAdapters.cpp | 2 +- common | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index ba812215f..42857f8e5 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -618,7 +618,7 @@ bool ApiJsonAdapter::sendReplyToClient(uint64_t msgId void ApiJsonAdapter::sendGCtimeout() { - const auto& timeNow = std::chrono::system_clock::now(); + const auto& timeNow = bs::message::bus_clock::now(); Envelope env{ 0, user_, user_, timeNow, timeNow + kRequestTimeout, "GC" }; pushFill(env); } diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index ac7e51b77..75f33a3a4 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -368,7 +368,7 @@ void BsServerAdapter::startTimer(std::chrono::milliseconds timeout const auto& toKey = CryptoPRNG::generateRandom(4).toHexStr(); timeouts_[toKey] = cb; msg.set_timeout(toKey); - const auto& timeNow = std::chrono::system_clock::now(); + const auto& timeNow = bs::message::bus_clock::now(); Envelope env{ 0, user_, user_, timeNow, timeNow + timeout, msg.SerializeAsString(), true }; pushFill(env); } diff --git a/Core/SettlementAdapter.cpp b/Core/SettlementAdapter.cpp index 2392184e1..a15dab06c 100644 --- a/Core/SettlementAdapter.cpp +++ b/Core/SettlementAdapter.cpp @@ -282,7 +282,7 @@ bool SettlementAdapter::processMatchingInRFQ(const IncomingRFQ& qrn) settlByRfqId_[rfq.requestId] = settlement; msg.set_quote_req_timeout(rfq.requestId); - const auto& timeNow = std::chrono::system_clock::now(); + const auto& timeNow = bs::message::bus_clock::now(); const auto expTime = std::chrono::milliseconds(qrn.expiration_ms()) - timeNow.time_since_epoch(); if (expTime.count() < 0) { logger_->error("[{}] outdated expiry {} for {}", __func__, expTime.count(), rfq.requestId); @@ -893,7 +893,7 @@ bool SettlementAdapter::startXbtSettlement(const bs::network::Quote& quote) return false; } - const auto& timeNow = std::chrono::system_clock::now(); + const auto& timeNow = bs::message::bus_clock::now(); SettlementMessage msg; msg.set_handshake_timeout(settlementId.toBinStr()); Envelope env{ 0, user_, user_, timeNow, timeNow + kHandshakeTimeout diff --git a/UnitTests/TestAdapters.cpp b/UnitTests/TestAdapters.cpp index a6a98c6f9..e3f32a399 100644 --- a/UnitTests/TestAdapters.cpp +++ b/UnitTests/TestAdapters.cpp @@ -156,7 +156,7 @@ bool MatchingMock::process(const bs::message::Envelope& env) for (const auto& sibling : siblings_) { sibling->inject(msgCopy, email_); } - const auto& timeNow = std::chrono::system_clock::now(); + const auto& timeNow = bs::message::bus_clock::now(); Envelope envTO{ 0, user_, user_, timeNow, timeNow + kExpirationTimeout , quote.quote_id() }; //FIXME: put actual quote's expirationTime pushFill(envTO); diff --git a/common b/common index 8c426fc6c..bc3a84cef 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 8c426fc6c1033a3e4737c13bdb36a8c9a4863f35 +Subproject commit bc3a84cef5482539dc3961874d87feb294d7f2b5 From 572cdbcff4bedb8e1b92f232123af0906b531b75 Mon Sep 17 00:00:00 2001 From: Pavel Kokolemin Date: Thu, 18 Feb 2021 17:19:36 +0300 Subject: [PATCH 124/146] Try to fix blotter updates --- BlockSettleUILib/OrderListModel.cpp | 11 ++++------- BlockSettleUILib/OrderListModel.h | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/OrderListModel.cpp index aaae5b5ef..4ce7995ad 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/OrderListModel.cpp @@ -584,12 +584,12 @@ std::pair OrderListModel::findItem(const bs::networ } void OrderListModel::removeRowIfContainerChanged(const bs::network::Order &order, - int &oldOrderRow) + int &oldOrderRow, bool force) { // Remove row if container (settled/unsettled) changed. auto git = groups_.find(order.exchOrderId.toStdString()); - if (git != groups_.end() && git->second != getStatusGroup(order) && oldOrderRow >= 0) { + if (git != groups_.end() && (git->second != getStatusGroup(order) || force) && oldOrderRow >= 0) { StatusGroup *tmpsg = (git->second == StatusGroup::UnSettled ? unsettled_.get() : settled_.get()); @@ -727,7 +727,7 @@ void OrderListModel::processUpdateOrder(const Blocksettle::Communication::ProxyT } case bs::types::ACTION_REMOVED: { auto found = findItem(order); - removeRowIfContainerChanged(order, found.second); + removeRowIfContainerChanged(order, found.second, true); break; } default: @@ -772,10 +772,7 @@ void OrderListModel::onOrderUpdated(const bs::network::Order& order) Group *groupItem = nullptr; Market *marketItem = nullptr; - // NOTE: because of batch orders update removeRowIfContainerChanged do nothing. - // Once batch update removed, that method should also be changed in order to display - // futures trades properly. ( Mostly futures group, since it display accumulated value ) - removeRowIfContainerChanged(order, found.second); + removeRowIfContainerChanged(order, found.second, false); findMarketAndGroup(order, marketItem, groupItem); diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h index 299b562d9..5ad511fe4 100644 --- a/BlockSettleUILib/OrderListModel.h +++ b/BlockSettleUILib/OrderListModel.h @@ -268,7 +268,7 @@ public slots: std::pair findItem(const bs::network::Order &order); void setOrderStatus(Group *group, int index, const bs::network::Order& order, bool emitUpdate = false); - void removeRowIfContainerChanged(const bs::network::Order &order, int &oldOrderRow); + void removeRowIfContainerChanged(const bs::network::Order &order, int &oldOrderRow, bool force); void findMarketAndGroup(const bs::network::Order &order, Market *&market, Group *&group); void createGroupsIfNeeded(const bs::network::Order &order, Market *&market, Group *&group); From d688c66f35318e6533a4133c771933d6865b49af Mon Sep 17 00:00:00 2001 From: Ation Date: Wed, 3 Mar 2021 10:40:57 +0200 Subject: [PATCH 125/146] Add json lib --- CMakeLists.txt | 4 ++++ common | 2 +- generate.py | 38 ++++++++++++++++++++------------------ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8836987c8..c78e706ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,6 +335,10 @@ INCLUDE_DIRECTORIES( ${QRENCODE_INCLUDE_DIR} ) SET(SPDLOG_INCLUDE_DIR ${THIRD_PARTY_COMMON_DIR}/SPDLog/include) INCLUDE_DIRECTORIES( ${SPDLOG_INCLUDE_DIR} ) +# add JSON +SET(JSON_INCLUDE_DIR ${THIRD_PARTY_COMMON_DIR}/JSON/single_include) +INCLUDE_DIRECTORIES( ${JSON_INCLUDE_DIR} ) + # setup libbtc SET(LIBBTC_PACKAGE_ROOT ${THIRD_PARTY_COMMON_DIR}/libbtc) SET(LIBBTC_LIB_DIR ${LIBBTC_PACKAGE_ROOT}/lib) diff --git a/common b/common index 1c54561cf..d622faaf6 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1c54561cf7aeb1ae672d1a6b718d481887a36ad3 +Subproject commit d622faaf670720cfb8f99014d126d9d6db64ed2c diff --git a/generate.py b/generate.py index aadab7315..ed8d7c4b1 100644 --- a/generate.py +++ b/generate.py @@ -22,24 +22,25 @@ sys.path.insert(0, os.path.join('common')) sys.path.insert(0, os.path.join('common', 'build_scripts')) -from build_scripts.settings import Settings -from build_scripts.protobuf_settings import ProtobufSettings -from build_scripts.gtest_settings import GtestSettings -from build_scripts.jom_settings import JomSettings -from build_scripts.qt_settings import QtSettings -from build_scripts.spdlog_settings import SpdlogSettings -from build_scripts.zeromq_settings import ZeroMQSettings -from build_scripts.libqrencode_settings import LibQREncode -from build_scripts.mpir_settings import MPIRSettings -from build_scripts.libbtc_settings import LibBTC -from build_scripts.openssl_settings import OpenSslSettings -from build_scripts.websockets_settings import WebsocketsSettings +from build_scripts.bip_protocols_settings import BipProtocolsSettings +from build_scripts.botan_settings import BotanSettings +from build_scripts.gtest_settings import GtestSettings +from build_scripts.hidapi_settings import HidapiSettings +from build_scripts.jom_settings import JomSettings +from build_scripts.libbtc_settings import LibBTC from build_scripts.libchacha20poly1305_settings import LibChaCha20Poly1305Settings -from build_scripts.botan_settings import BotanSettings -from build_scripts.hidapi_settings import HidapiSettings -from build_scripts.libusb_settings import LibusbSettings -from build_scripts.trezor_common_settings import TrezorCommonSettings -from build_scripts.bip_protocols_settings import BipProtocolsSettings +from build_scripts.libqrencode_settings import LibQREncode +from build_scripts.libusb_settings import LibusbSettings +from build_scripts.mpir_settings import MPIRSettings +from build_scripts.nlohmann_json_settings import NLohmanJson +from build_scripts.openssl_settings import OpenSslSettings +from build_scripts.protobuf_settings import ProtobufSettings +from build_scripts.qt_settings import QtSettings +from build_scripts.settings import Settings +from build_scripts.spdlog_settings import SpdlogSettings +from build_scripts.trezor_common_settings import TrezorCommonSettings +from build_scripts.websockets_settings import WebsocketsSettings +from build_scripts.zeromq_settings import ZeroMQSettings def generate_project(build_mode, link_mode, build_production, hide_warnings, cmake_flags, build_tests, build_tracker): project_settings = Settings(build_mode, link_mode) @@ -70,7 +71,8 @@ def generate_project(build_mode, link_mode, build_production, hide_warnings, cma HidapiSettings(project_settings), LibusbSettings(project_settings), TrezorCommonSettings(project_settings), - BipProtocolsSettings(project_settings) + BipProtocolsSettings(project_settings), + NLohmanJson(project_settings) ] if build_tests: From 7deeae5b2f75c94823171f5542adeb488b610abb Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 24 Mar 2021 09:56:55 +0300 Subject: [PATCH 126/146] Disable RBF for simple payment TX --- BlockSettleUILib/CreateTransactionDialogSimple.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.cpp b/BlockSettleUILib/CreateTransactionDialogSimple.cpp index 8b9b4369a..1b67befe2 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.cpp +++ b/BlockSettleUILib/CreateTransactionDialogSimple.cpp @@ -332,6 +332,10 @@ std::shared_ptr CreateTransactionDialogSimple::CreateFo dlg->ui_->textEditComment->setText(paymentInfo.label); } + dlg->ui_->checkBoxRBF->setChecked(false); + dlg->ui_->checkBoxRBF->setEnabled(false); + dlg->ui_->checkBoxRBF->setToolTip(tr("RBF disabled for payment request")); + dlg->paymentInfo_ = paymentInfo; return dlg; From 6ef7a416e15c88da88eba538ae2613b559f6fc4b Mon Sep 17 00:00:00 2001 From: goatpig Date: Mon, 29 Mar 2021 10:16:42 +0200 Subject: [PATCH 127/146] fix wallet unit test, update common --- UnitTests/TestWallet.cpp | 4 ++-- common | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UnitTests/TestWallet.cpp b/UnitTests/TestWallet.cpp index f7c838ea6..0a2bfd1a8 100644 --- a/UnitTests/TestWallet.cpp +++ b/UnitTests/TestWallet.cpp @@ -82,7 +82,7 @@ TEST_F(TestWallet, BIP84_derivation) { const bs::core::WalletPasswordScoped lock(wallet, passphrase); - wallet->createStructure(); + wallet->createStructure(false); ASSERT_NE(wallet->getGroup(wallet->getXBTGroupType()), nullptr); } @@ -1333,7 +1333,7 @@ TEST_F(TestWallet, SyncWallet_TriggerPoolExtension) { const bs::core::WalletPasswordScoped lock(walletPtr, passphrase); - walletPtr->createStructure(10); + walletPtr->createStructure(false, 10); } //create sync manager diff --git a/common b/common index bc3a84cef..363a39e19 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit bc3a84cef5482539dc3961874d87feb294d7f2b5 +Subproject commit 363a39e19380d6acb1ba696cefafd8215320c930 From 988b6dce481a2160843f6b7fb2a5fb65c8968234 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 1 Apr 2021 11:16:12 +0300 Subject: [PATCH 128/146] renamed TxN to TxCount where appropriate --- GUI/QtWidgets/QtGuiAdapter.cpp | 2 +- common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 328bc7261..188b8b470 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -1339,7 +1339,7 @@ bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env wbd.nbAddresses = response.nb_addresses(); for (const auto &addrBal : response.address_balances()) { wbd.addrBalances.push_back({ BinaryData::fromString(addrBal.address()) - , addrBal.txn(), addrBal.total_balance(), addrBal.spendable_balance() + , addrBal.tx_count(), addrBal.total_balance(), addrBal.spendable_balance() , addrBal.unconfirmed_balance() }); } return QMetaObject::invokeMethod(mainWindow_, [this, wbd] { diff --git a/common b/common index 363a39e19..954f4789a 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 363a39e19380d6acb1ba696cefafd8215320c930 +Subproject commit 954f4789ab7341b229d31a82d5639c54cf3d83b6 From ff4bae9361f2727d369bfcccbada395fe7bf9e39 Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 5 Apr 2021 18:29:52 +0300 Subject: [PATCH 129/146] Update copy years according to commit history --- BlockSettleApp/main.cpp | 2 +- BlockSettleHW/CMakeLists.txt | 2 +- BlockSettleHW/hwcommonstructure.cpp | 2 +- BlockSettleHW/hwcommonstructure.h | 2 +- BlockSettleHW/hwdeviceinterface.h | 2 +- BlockSettleHW/hwdevicemanager.cpp | 2 +- BlockSettleHW/hwdevicemanager.h | 2 +- BlockSettleHW/hwdevicemodel.cpp | 2 +- BlockSettleHW/hwdevicemodel.h | 2 +- BlockSettleHW/ledger/ledgerClient.cpp | 2 +- BlockSettleHW/ledger/ledgerClient.h | 2 +- BlockSettleHW/ledger/ledgerDevice.cpp | 2 +- BlockSettleHW/ledger/ledgerDevice.h | 2 +- BlockSettleHW/ledger/ledgerStructure.cpp | 2 +- BlockSettleHW/ledger/ledgerStructure.h | 2 +- BlockSettleHW/trezor/trezorClient.cpp | 2 +- BlockSettleHW/trezor/trezorClient.h | 2 +- BlockSettleHW/trezor/trezorDevice.cpp | 2 +- BlockSettleHW/trezor/trezorDevice.h | 2 +- BlockSettleHW/trezor/trezorStructure.h | 2 +- BlockSettleSigner/Bip39EntryValidator.cpp | 2 +- BlockSettleSigner/Bip39EntryValidator.h | 2 +- BlockSettleSigner/EasyEncValidator.cpp | 2 +- BlockSettleSigner/EasyEncValidator.h | 2 +- BlockSettleSigner/HeadlessApp.cpp | 2 +- BlockSettleSigner/QmlBridge.cpp | 2 +- BlockSettleSigner/QmlBridge.h | 2 +- BlockSettleSigner/QmlCallbackImpl.h | 2 +- BlockSettleSigner/SignerAdapter.cpp | 2 +- BlockSettleSigner/SignerAdapter.h | 2 +- BlockSettleSigner/SignerAdapterContainer.cpp | 2 +- BlockSettleSigner/SignerAdapterContainer.h | 2 +- BlockSettleSigner/SignerAdapterListener.cpp | 2 +- BlockSettleSigner/SignerAdapterListener.h | 2 +- BlockSettleSigner/SignerInterfaceListener.cpp | 2 +- BlockSettleSigner/SignerInterfaceListener.h | 2 +- BlockSettleSigner/WalletsProxy.cpp | 2 +- BlockSettleSigner/main.cpp | 2 +- BlockSettleTracker/CMakeLists.txt | 2 +- BlockSettleTracker/main.cpp | 2 +- BlockSettleUILib/AddressDetailDialog.cpp | 2 +- BlockSettleUILib/AddressDetailDialog.h | 2 +- BlockSettleUILib/AddressListModel.cpp | 2 +- BlockSettleUILib/ApiKeyEntryDialog.cpp | 2 +- BlockSettleUILib/ApiKeyEntryDialog.h | 2 +- BlockSettleUILib/AuthAddressConfirmDialog.cpp | 2 +- BlockSettleUILib/AuthAddressConfirmDialog.h | 2 +- BlockSettleUILib/AuthAddressConfirmDialog.ui | 2 +- BlockSettleUILib/AuthAddressDialog.cpp | 2 +- BlockSettleUILib/AuthAddressDialog.h | 2 +- BlockSettleUILib/ButtonMenu.h | 2 +- BlockSettleUILib/CCPortfolioModel.cpp | 2 +- BlockSettleUILib/CCPortfolioModel.h | 2 +- BlockSettleUILib/CCWidget.cpp | 2 +- BlockSettleUILib/CCWidget.h | 2 +- BlockSettleUILib/CMakeLists.txt | 2 +- BlockSettleUILib/CelerAccountInfoDialog.cpp | 2 +- BlockSettleUILib/ChartWidget.cpp | 2 +- BlockSettleUILib/CreatePrimaryWalletPrompt.cpp | 2 +- BlockSettleUILib/CreatePrimaryWalletPrompt.h | 2 +- BlockSettleUILib/CreatePrimaryWalletPrompt.ui | 2 +- BlockSettleUILib/CreateTransactionDialog.cpp | 2 +- BlockSettleUILib/CreateTransactionDialog.h | 2 +- BlockSettleUILib/CreateTransactionDialogAdvanced.cpp | 2 +- BlockSettleUILib/CreateTransactionDialogAdvanced.h | 2 +- BlockSettleUILib/CreateTransactionDialogSimple.cpp | 2 +- BlockSettleUILib/CreateTransactionDialogSimple.h | 2 +- BlockSettleUILib/EditContactDialog.cpp | 2 +- BlockSettleUILib/EditContactDialog.h | 2 +- BlockSettleUILib/EditContactDialog.ui | 2 +- BlockSettleUILib/ImportKeyBox.cpp | 2 +- BlockSettleUILib/ImportKeyBox.h | 2 +- BlockSettleUILib/ImportKeyBox.ui | 2 +- .../ManageEncryption/RootWalletPropertiesDialog.cpp | 2 +- BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h | 2 +- BlockSettleUILib/OrderListModel.cpp | 2 +- BlockSettleUILib/OrderListModel.h | 2 +- BlockSettleUILib/OrdersView.cpp | 2 +- BlockSettleUILib/PortfolioWidget.cpp | 2 +- BlockSettleUILib/PortfolioWidget.h | 2 +- BlockSettleUILib/ProgressViewDelegateBase.cpp | 2 +- BlockSettleUILib/ProgressViewDelegateBase.h | 2 +- BlockSettleUILib/PubKeyLoader.cpp | 2 +- BlockSettleUILib/PubKeyLoader.h | 2 +- BlockSettleUILib/RangeSlider.cpp | 2 +- BlockSettleUILib/RangeSlider.h | 2 +- BlockSettleUILib/RangeWidget.cpp | 2 +- BlockSettleUILib/RangeWidget.h | 2 +- BlockSettleUILib/RangeWidget.ui | 2 +- BlockSettleUILib/SelectWalletDialog.cpp | 2 +- BlockSettleUILib/SelectWalletDialog.h | 2 +- BlockSettleUILib/Settings/APISettingsPage.cpp | 2 +- BlockSettleUILib/Settings/APISettingsPage.h | 2 +- BlockSettleUILib/Settings/GeneralSettingsPage.cpp | 2 +- BlockSettleUILib/Settings/HeadlessSettings.cpp | 2 +- BlockSettleUILib/Settings/HeadlessSettings.h | 2 +- BlockSettleUILib/Settings/NetworkSettingsPage.cpp | 2 +- BlockSettleUILib/Settings/SignerSettings.cpp | 2 +- BlockSettleUILib/Settings/SignerSettings.h | 2 +- BlockSettleUILib/StatusBarView.cpp | 2 +- BlockSettleUILib/StatusBarView.h | 2 +- BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp | 2 +- BlockSettleUILib/Trading/AutoSignQuoteProvider.h | 2 +- BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp | 2 +- BlockSettleUILib/Trading/DealerXBTSettlementContainer.h | 2 +- BlockSettleUILib/Trading/FuturesTicket.cpp | 2 +- BlockSettleUILib/Trading/FuturesTicket.h | 2 +- BlockSettleUILib/Trading/MarketDataModel.cpp | 2 +- BlockSettleUILib/Trading/MarketDataModel.h | 2 +- BlockSettleUILib/Trading/MarketDataWidget.cpp | 2 +- BlockSettleUILib/Trading/MarketDataWidget.h | 2 +- BlockSettleUILib/Trading/OtcClient.cpp | 2 +- BlockSettleUILib/Trading/OtcClient.h | 2 +- BlockSettleUILib/Trading/QuoteRequestsModel.cpp | 2 +- BlockSettleUILib/Trading/QuoteRequestsModel.h | 2 +- BlockSettleUILib/Trading/QuoteRequestsWidget.cpp | 2 +- BlockSettleUILib/Trading/QuoteRequestsWidget.h | 2 +- BlockSettleUILib/Trading/RFQDealerReply.cpp | 2 +- BlockSettleUILib/Trading/RFQDialog.cpp | 2 +- BlockSettleUILib/Trading/RFQDialog.h | 2 +- BlockSettleUILib/Trading/RFQReplyWidget.cpp | 2 +- BlockSettleUILib/Trading/RFQReplyWidget.h | 2 +- BlockSettleUILib/Trading/RFQRequestWidget.cpp | 2 +- BlockSettleUILib/Trading/RFQRequestWidget.h | 2 +- BlockSettleUILib/Trading/RFQTicketXBT.cpp | 2 +- BlockSettleUILib/Trading/RFQTicketXBT.h | 2 +- BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp | 2 +- BlockSettleUILib/Trading/ReqCCSettlementContainer.h | 2 +- BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp | 2 +- BlockSettleUILib/Trading/ReqXBTSettlementContainer.h | 2 +- BlockSettleUILib/Trading/RequestingQuoteWidget.cpp | 2 +- BlockSettleUILib/Trading/RequestingQuoteWidget.h | 2 +- BlockSettleUILib/Trading/WalletShieldBase.cpp | 2 +- BlockSettleUILib/TransactionDetailDialog.cpp | 2 +- BlockSettleUILib/TransactionDetailDialog.h | 2 +- BlockSettleUILib/TransactionsViewModel.cpp | 2 +- BlockSettleUILib/TransactionsViewModel.h | 2 +- BlockSettleUILib/TransactionsWidget.cpp | 2 +- BlockSettleUILib/TransactionsWidget.h | 2 +- BlockSettleUILib/TransactionsWidgetInterface.cpp | 2 +- BlockSettleUILib/TransactionsWidgetInterface.h | 2 +- BlockSettleUILib/UiUtils.cpp | 2 +- BlockSettleUILib/UiUtils.h | 2 +- BlockSettleUILib/UtxoModelInterface.cpp | 2 +- BlockSettleUILib/UtxoModelInterface.h | 2 +- BlockSettleUILib/UtxoReservationManager.cpp | 2 +- BlockSettleUILib/UtxoReservationManager.h | 2 +- BlockSettleUILib/UtxoReservationToken.cpp | 2 +- BlockSettleUILib/UtxoReservationToken.h | 2 +- BlockSettleUILib/WalletsViewModel.cpp | 2 +- BlockSettleUILib/WalletsViewModel.h | 2 +- BlockSettleUILib/WalletsWidget.cpp | 2 +- BlockSettleUILib/WalletsWidget.h | 2 +- CMakeLists.txt | 2 +- CommonUI/CMakeLists.txt | 2 +- CommonUI/SslCaBundle.cpp | 2 +- CommonUI/SslCaBundle.h | 2 +- Core/ApiJson.cpp | 2 +- Core/ApiJson.h | 2 +- Core/BsServerAdapter.cpp | 2 +- Core/MatchingAdapter.cpp | 2 +- Core/SettlementAdapter.cpp | 2 +- Core/SignerAdapter.cpp | 2 +- Core/SignerAdapter.h | 2 +- GUI/QtWidgets/MainWindow.cpp | 2 +- GUI/QtWidgets/MainWindow.h | 2 +- GUI/QtWidgets/QtGuiAdapter.cpp | 2 +- GUI/QtWidgets/QtGuiAdapter.h | 2 +- UnitTests/MockTerminal.cpp | 2 +- UnitTests/MockTerminal.h | 2 +- UnitTests/TestAuth.cpp | 2 +- UnitTests/TestAuth.h | 2 +- UnitTests/TestCCoin.cpp | 2 +- UnitTests/TestCCoin.h | 2 +- UnitTests/TestCCoinAsync.cpp | 2 +- UnitTests/TestCCoinAsync.h | 2 +- UnitTests/TestCommon.cpp | 2 +- UnitTests/TestEnv.h | 2 +- UnitTests/TestOtc.cpp | 2 +- UnitTests/TestSettlement.cpp | 2 +- UnitTests/TestSettlement.h | 2 +- UnitTests/TestWallet.cpp | 2 +- UnitTests/TestWalletArmory.cpp | 2 +- UnitTests/TestWebSockets.cpp | 2 +- common | 2 +- generate.py | 2 +- prepeare_release.py | 2 +- 187 files changed, 187 insertions(+), 187 deletions(-) diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index f2c72300b..8580e79f4 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/CMakeLists.txt b/BlockSettleHW/CMakeLists.txt index 3a4c39b82..f8874f12b 100644 --- a/BlockSettleHW/CMakeLists.txt +++ b/BlockSettleHW/CMakeLists.txt @@ -1,7 +1,7 @@ # # # *********************************************************************************** -# * Copyright (C) 2020 - 2020, BlockSettle AB +# * Copyright (C) 2020, BlockSettle AB # * Distributed under the GNU Affero General Public License (AGPL v3) # * See LICENSE or http://www.gnu.org/licenses/agpl.html # * diff --git a/BlockSettleHW/hwcommonstructure.cpp b/BlockSettleHW/hwcommonstructure.cpp index d4682bee0..497e7de6d 100644 --- a/BlockSettleHW/hwcommonstructure.cpp +++ b/BlockSettleHW/hwcommonstructure.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwcommonstructure.h b/BlockSettleHW/hwcommonstructure.h index 30bfb3ac4..638c2fbf2 100644 --- a/BlockSettleHW/hwcommonstructure.h +++ b/BlockSettleHW/hwcommonstructure.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdeviceinterface.h b/BlockSettleHW/hwdeviceinterface.h index 8e2ae2825..9f41bffbb 100644 --- a/BlockSettleHW/hwdeviceinterface.h +++ b/BlockSettleHW/hwdeviceinterface.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemanager.cpp b/BlockSettleHW/hwdevicemanager.cpp index 1b69ab0c6..b401b413a 100644 --- a/BlockSettleHW/hwdevicemanager.cpp +++ b/BlockSettleHW/hwdevicemanager.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemanager.h b/BlockSettleHW/hwdevicemanager.h index 42432c875..64be1d292 100644 --- a/BlockSettleHW/hwdevicemanager.h +++ b/BlockSettleHW/hwdevicemanager.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemodel.cpp b/BlockSettleHW/hwdevicemodel.cpp index 5ec783a84..c5e3c2467 100644 --- a/BlockSettleHW/hwdevicemodel.cpp +++ b/BlockSettleHW/hwdevicemodel.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemodel.h b/BlockSettleHW/hwdevicemodel.h index fcbcfbf74..b0e53e7d5 100644 --- a/BlockSettleHW/hwdevicemodel.h +++ b/BlockSettleHW/hwdevicemodel.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerClient.cpp b/BlockSettleHW/ledger/ledgerClient.cpp index a91812666..a9766a64a 100644 --- a/BlockSettleHW/ledger/ledgerClient.cpp +++ b/BlockSettleHW/ledger/ledgerClient.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerClient.h b/BlockSettleHW/ledger/ledgerClient.h index ec97d61eb..36eef40f6 100644 --- a/BlockSettleHW/ledger/ledgerClient.h +++ b/BlockSettleHW/ledger/ledgerClient.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerDevice.cpp b/BlockSettleHW/ledger/ledgerDevice.cpp index 9f6912caf..5c694391b 100644 --- a/BlockSettleHW/ledger/ledgerDevice.cpp +++ b/BlockSettleHW/ledger/ledgerDevice.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerDevice.h b/BlockSettleHW/ledger/ledgerDevice.h index d8316fd18..da99e5012 100644 --- a/BlockSettleHW/ledger/ledgerDevice.h +++ b/BlockSettleHW/ledger/ledgerDevice.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerStructure.cpp b/BlockSettleHW/ledger/ledgerStructure.cpp index f30bc8303..acb29ffcb 100644 --- a/BlockSettleHW/ledger/ledgerStructure.cpp +++ b/BlockSettleHW/ledger/ledgerStructure.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerStructure.h b/BlockSettleHW/ledger/ledgerStructure.h index de3e27e71..4b653715b 100644 --- a/BlockSettleHW/ledger/ledgerStructure.h +++ b/BlockSettleHW/ledger/ledgerStructure.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorClient.cpp b/BlockSettleHW/trezor/trezorClient.cpp index cd7a4dfbb..2d85036d6 100644 --- a/BlockSettleHW/trezor/trezorClient.cpp +++ b/BlockSettleHW/trezor/trezorClient.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorClient.h b/BlockSettleHW/trezor/trezorClient.h index 0cda3130b..fd3ff3794 100644 --- a/BlockSettleHW/trezor/trezorClient.h +++ b/BlockSettleHW/trezor/trezorClient.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorDevice.cpp b/BlockSettleHW/trezor/trezorDevice.cpp index 9edd7a458..c821abf89 100644 --- a/BlockSettleHW/trezor/trezorDevice.cpp +++ b/BlockSettleHW/trezor/trezorDevice.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorDevice.h b/BlockSettleHW/trezor/trezorDevice.h index d4c630b1c..d601698dc 100644 --- a/BlockSettleHW/trezor/trezorDevice.h +++ b/BlockSettleHW/trezor/trezorDevice.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorStructure.h b/BlockSettleHW/trezor/trezorStructure.h index 1d5b887ac..da4197fd1 100644 --- a/BlockSettleHW/trezor/trezorStructure.h +++ b/BlockSettleHW/trezor/trezorStructure.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/Bip39EntryValidator.cpp b/BlockSettleSigner/Bip39EntryValidator.cpp index de594a44b..7dc7b0c31 100644 --- a/BlockSettleSigner/Bip39EntryValidator.cpp +++ b/BlockSettleSigner/Bip39EntryValidator.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/Bip39EntryValidator.h b/BlockSettleSigner/Bip39EntryValidator.h index 0d056fd1c..00e07c081 100644 --- a/BlockSettleSigner/Bip39EntryValidator.h +++ b/BlockSettleSigner/Bip39EntryValidator.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/EasyEncValidator.cpp b/BlockSettleSigner/EasyEncValidator.cpp index b4ab173cb..d47ee8a72 100644 --- a/BlockSettleSigner/EasyEncValidator.cpp +++ b/BlockSettleSigner/EasyEncValidator.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/EasyEncValidator.h b/BlockSettleSigner/EasyEncValidator.h index dd768caa7..05cfdd1ae 100644 --- a/BlockSettleSigner/EasyEncValidator.h +++ b/BlockSettleSigner/EasyEncValidator.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/HeadlessApp.cpp b/BlockSettleSigner/HeadlessApp.cpp index 25f0382d1..9dec4072f 100644 --- a/BlockSettleSigner/HeadlessApp.cpp +++ b/BlockSettleSigner/HeadlessApp.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/QmlBridge.cpp b/BlockSettleSigner/QmlBridge.cpp index db1a1b07c..b64509479 100644 --- a/BlockSettleSigner/QmlBridge.cpp +++ b/BlockSettleSigner/QmlBridge.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/QmlBridge.h b/BlockSettleSigner/QmlBridge.h index ae6fbb04c..04589e5fd 100644 --- a/BlockSettleSigner/QmlBridge.h +++ b/BlockSettleSigner/QmlBridge.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/QmlCallbackImpl.h b/BlockSettleSigner/QmlCallbackImpl.h index be32bf9eb..4455a5eb3 100644 --- a/BlockSettleSigner/QmlCallbackImpl.h +++ b/BlockSettleSigner/QmlCallbackImpl.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapter.cpp b/BlockSettleSigner/SignerAdapter.cpp index 7319b42d7..a20a5c6d6 100644 --- a/BlockSettleSigner/SignerAdapter.cpp +++ b/BlockSettleSigner/SignerAdapter.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapter.h b/BlockSettleSigner/SignerAdapter.h index a09745bbc..72505e8e8 100644 --- a/BlockSettleSigner/SignerAdapter.h +++ b/BlockSettleSigner/SignerAdapter.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapterContainer.cpp b/BlockSettleSigner/SignerAdapterContainer.cpp index cba832169..f8a7aa212 100644 --- a/BlockSettleSigner/SignerAdapterContainer.cpp +++ b/BlockSettleSigner/SignerAdapterContainer.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapterContainer.h b/BlockSettleSigner/SignerAdapterContainer.h index 3eb2bfd2d..8870668e8 100644 --- a/BlockSettleSigner/SignerAdapterContainer.h +++ b/BlockSettleSigner/SignerAdapterContainer.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapterListener.cpp b/BlockSettleSigner/SignerAdapterListener.cpp index 32e043997..bcdf8071c 100644 --- a/BlockSettleSigner/SignerAdapterListener.cpp +++ b/BlockSettleSigner/SignerAdapterListener.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapterListener.h b/BlockSettleSigner/SignerAdapterListener.h index 4817b5dc6..9e6ef8129 100644 --- a/BlockSettleSigner/SignerAdapterListener.h +++ b/BlockSettleSigner/SignerAdapterListener.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerInterfaceListener.cpp b/BlockSettleSigner/SignerInterfaceListener.cpp index 6e16cad70..08e7a91ac 100644 --- a/BlockSettleSigner/SignerInterfaceListener.cpp +++ b/BlockSettleSigner/SignerInterfaceListener.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerInterfaceListener.h b/BlockSettleSigner/SignerInterfaceListener.h index 61452b92d..1853742b5 100644 --- a/BlockSettleSigner/SignerInterfaceListener.h +++ b/BlockSettleSigner/SignerInterfaceListener.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/WalletsProxy.cpp b/BlockSettleSigner/WalletsProxy.cpp index 844cd88dc..b6efdf10e 100644 --- a/BlockSettleSigner/WalletsProxy.cpp +++ b/BlockSettleSigner/WalletsProxy.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/main.cpp b/BlockSettleSigner/main.cpp index d4ab5a8ab..7acbced82 100644 --- a/BlockSettleSigner/main.cpp +++ b/BlockSettleSigner/main.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleTracker/CMakeLists.txt b/BlockSettleTracker/CMakeLists.txt index 22a0ab6c8..f466d6257 100644 --- a/BlockSettleTracker/CMakeLists.txt +++ b/BlockSettleTracker/CMakeLists.txt @@ -1,7 +1,7 @@ # # # *********************************************************************************** -# * Copyright (C) 2020 - 2020, BlockSettle AB +# * Copyright (C) 2020, BlockSettle AB # * Distributed under the GNU Affero General Public License (AGPL v3) # * See LICENSE or http://www.gnu.org/licenses/agpl.html # * diff --git a/BlockSettleTracker/main.cpp b/BlockSettleTracker/main.cpp index ef654dcee..b9b630dd5 100644 --- a/BlockSettleTracker/main.cpp +++ b/BlockSettleTracker/main.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AddressDetailDialog.cpp b/BlockSettleUILib/AddressDetailDialog.cpp index dde6f4979..de437c613 100644 --- a/BlockSettleUILib/AddressDetailDialog.cpp +++ b/BlockSettleUILib/AddressDetailDialog.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AddressDetailDialog.h b/BlockSettleUILib/AddressDetailDialog.h index 644b47953..4e62cddc9 100644 --- a/BlockSettleUILib/AddressDetailDialog.h +++ b/BlockSettleUILib/AddressDetailDialog.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index 865ffd57a..e0758f3a3 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2018 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/ApiKeyEntryDialog.cpp b/BlockSettleUILib/ApiKeyEntryDialog.cpp index 57a9dd6b5..855621e79 100644 --- a/BlockSettleUILib/ApiKeyEntryDialog.cpp +++ b/BlockSettleUILib/ApiKeyEntryDialog.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/ApiKeyEntryDialog.h b/BlockSettleUILib/ApiKeyEntryDialog.h index cab36fa40..f55bf9835 100644 --- a/BlockSettleUILib/ApiKeyEntryDialog.h +++ b/BlockSettleUILib/ApiKeyEntryDialog.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020 - 2020, BlockSettle AB +* Copyright (C) 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AuthAddressConfirmDialog.cpp b/BlockSettleUILib/AuthAddressConfirmDialog.cpp index 5d9ae9e0b..9f6c75f82 100644 --- a/BlockSettleUILib/AuthAddressConfirmDialog.cpp +++ b/BlockSettleUILib/AuthAddressConfirmDialog.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AuthAddressConfirmDialog.h b/BlockSettleUILib/AuthAddressConfirmDialog.h index 1d62b9add..c8ed2e156 100644 --- a/BlockSettleUILib/AuthAddressConfirmDialog.h +++ b/BlockSettleUILib/AuthAddressConfirmDialog.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2020, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AuthAddressConfirmDialog.ui b/BlockSettleUILib/AuthAddressConfirmDialog.ui index 86b4626c9..a877e192b 100644 --- a/BlockSettleUILib/AuthAddressConfirmDialog.ui +++ b/BlockSettleUILib/AuthAddressConfirmDialog.ui @@ -2,7 +2,7 @@ AddressDetailDialog diff --git a/BlockSettleUILib/ApiKeyEntryDialog.ui b/BlockSettleUILib/ApiKeyEntryDialog.ui index 56c8b71ac..e2980cef3 100644 --- a/BlockSettleUILib/ApiKeyEntryDialog.ui +++ b/BlockSettleUILib/ApiKeyEntryDialog.ui @@ -1,4 +1,14 @@ + ApiKeyEntryDialog diff --git a/BlockSettleUILib/CCWidget.ui b/BlockSettleUILib/CCWidget.ui index 7ceb0f153..aeab83f04 100644 --- a/BlockSettleUILib/CCWidget.ui +++ b/BlockSettleUILib/CCWidget.ui @@ -1,4 +1,14 @@ + CCWidget diff --git a/BlockSettleUILib/Settings/APISettingsPage.ui b/BlockSettleUILib/Settings/APISettingsPage.ui index cd2fdca3b..d8c188bb0 100644 --- a/BlockSettleUILib/Settings/APISettingsPage.ui +++ b/BlockSettleUILib/Settings/APISettingsPage.ui @@ -1,4 +1,14 @@ + APISettingsPage diff --git a/BlockSettleUILib/Settings/ConfigDialog.ui b/BlockSettleUILib/Settings/ConfigDialog.ui index e11962c21..8f6bdeec2 100644 --- a/BlockSettleUILib/Settings/ConfigDialog.ui +++ b/BlockSettleUILib/Settings/ConfigDialog.ui @@ -1,4 +1,14 @@ + ConfigDialog diff --git a/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui b/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui index 4041a22f5..a1b53cbe2 100644 --- a/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui +++ b/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui @@ -1,4 +1,14 @@ + AutoSignQuoteWidget diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 9411aa814..2bd9414ef 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -1,3 +1,14 @@ +# +# +# *********************************************************************************** +# * Copyright (C) 2020 - 2021, BlockSettle AB +# * Distributed under the GNU Affero General Public License (AGPL v3) +# * See LICENSE or http://www.gnu.org/licenses/agpl.html +# * +# ********************************************************************************** +# +# + CMAKE_MINIMUM_REQUIRED(VERSION 3.3) PROJECT(${TERMINAL_CORE_NAME}) diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 55f62efd2..769469d40 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -1 +1,12 @@ +# +# +# *********************************************************************************** +# * Copyright (C) 2020 - 2021, BlockSettle AB +# * Distributed under the GNU Affero General Public License (AGPL v3) +# * See LICENSE or http://www.gnu.org/licenses/agpl.html +# * +# ********************************************************************************** +# +# + ADD_SUBDIRECTORY(QtWidgets) diff --git a/GUI/QtWidgets/CMakeLists.txt b/GUI/QtWidgets/CMakeLists.txt index 8f3d36451..0b8eb9ba0 100644 --- a/GUI/QtWidgets/CMakeLists.txt +++ b/GUI/QtWidgets/CMakeLists.txt @@ -1,3 +1,14 @@ +# +# +# *********************************************************************************** +# * Copyright (C) 2020 - 2021, BlockSettle AB +# * Distributed under the GNU Affero General Public License (AGPL v3) +# * See LICENSE or http://www.gnu.org/licenses/agpl.html +# * +# ********************************************************************************** +# +# + CMAKE_MINIMUM_REQUIRED(VERSION 3.3) PROJECT(${TERMINAL_GUI_QT_NAME}) diff --git a/UnitTests/TestAdapters.cpp b/UnitTests/TestAdapters.cpp index e3f32a399..c1e360cb1 100644 --- a/UnitTests/TestAdapters.cpp +++ b/UnitTests/TestAdapters.cpp @@ -1,3 +1,13 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020 - 2021, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ #include "TestAdapters.h" #include #include diff --git a/UnitTests/TestAdapters.h b/UnitTests/TestAdapters.h index c2cdd7e8a..16641e928 100644 --- a/UnitTests/TestAdapters.h +++ b/UnitTests/TestAdapters.h @@ -1,3 +1,13 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020 - 2021, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ #ifndef TEST_ADAPTERS_H #define TEST_ADAPTERS_H From 26f60056a966d6e67bc4575c1da4f091104bbcaa Mon Sep 17 00:00:00 2001 From: Ation Date: Mon, 5 Apr 2021 19:31:00 +0300 Subject: [PATCH 131/146] fix year --- BlockSettleHW/CMakeLists.txt | 2 +- BlockSettleHW/hwcommonstructure.cpp | 2 +- BlockSettleHW/hwcommonstructure.h | 2 +- BlockSettleHW/hwdeviceinterface.h | 2 +- BlockSettleHW/hwdevicemanager.cpp | 2 +- BlockSettleHW/hwdevicemanager.h | 2 +- BlockSettleHW/hwdevicemodel.cpp | 2 +- BlockSettleHW/hwdevicemodel.h | 2 +- BlockSettleHW/ledger/ledgerClient.cpp | 2 +- BlockSettleHW/ledger/ledgerClient.h | 2 +- BlockSettleHW/ledger/ledgerDevice.cpp | 2 +- BlockSettleHW/ledger/ledgerDevice.h | 2 +- BlockSettleHW/ledger/ledgerStructure.cpp | 2 +- BlockSettleHW/ledger/ledgerStructure.h | 2 +- BlockSettleHW/trezor/trezorClient.cpp | 2 +- BlockSettleHW/trezor/trezorClient.h | 2 +- BlockSettleHW/trezor/trezorDevice.cpp | 2 +- BlockSettleHW/trezor/trezorDevice.h | 2 +- BlockSettleHW/trezor/trezorStructure.h | 2 +- BlockSettleSigner/Bip39EntryValidator.cpp | 2 +- BlockSettleSigner/Bip39EntryValidator.h | 2 +- BlockSettleSigner/EasyEncValidator.cpp | 2 +- BlockSettleSigner/QmlBridge.cpp | 2 +- BlockSettleSigner/QmlBridge.h | 2 +- BlockSettleSigner/QmlCallbackImpl.h | 2 +- BlockSettleSigner/SignerAdapterListener.cpp | 2 +- BlockSettleSigner/SignerInterfaceListener.h | 2 +- BlockSettleTracker/CMakeLists.txt | 2 +- BlockSettleTracker/main.cpp | 2 +- BlockSettleUILib/ApiKeyEntryDialog.cpp | 2 +- BlockSettleUILib/ApiKeyEntryDialog.h | 2 +- BlockSettleUILib/AuthAddressConfirmDialog.cpp | 2 +- BlockSettleUILib/AuthAddressConfirmDialog.h | 2 +- BlockSettleUILib/AuthAddressConfirmDialog.ui | 2 +- BlockSettleUILib/CreatePrimaryWalletPrompt.cpp | 2 +- BlockSettleUILib/CreatePrimaryWalletPrompt.h | 2 +- BlockSettleUILib/CreatePrimaryWalletPrompt.ui | 2 +- BlockSettleUILib/EditContactDialog.cpp | 2 +- BlockSettleUILib/EditContactDialog.h | 2 +- BlockSettleUILib/EditContactDialog.ui | 2 +- BlockSettleUILib/ImportKeyBox.cpp | 2 +- BlockSettleUILib/ImportKeyBox.h | 2 +- BlockSettleUILib/ImportKeyBox.ui | 2 +- BlockSettleUILib/ProgressViewDelegateBase.cpp | 2 +- BlockSettleUILib/ProgressViewDelegateBase.h | 2 +- BlockSettleUILib/PubKeyLoader.h | 2 +- BlockSettleUILib/RangeSlider.cpp | 2 +- BlockSettleUILib/RangeSlider.h | 2 +- BlockSettleUILib/RangeWidget.cpp | 2 +- BlockSettleUILib/RangeWidget.h | 2 +- BlockSettleUILib/RangeWidget.ui | 2 +- BlockSettleUILib/Settings/APISettingsPage.cpp | 2 +- BlockSettleUILib/Settings/APISettingsPage.h | 2 +- BlockSettleUILib/Settings/HeadlessSettings.cpp | 2 +- BlockSettleUILib/Settings/HeadlessSettings.h | 2 +- BlockSettleUILib/Settings/SignerSettings.cpp | 2 +- BlockSettleUILib/Settings/SignerSettings.h | 2 +- BlockSettleUILib/Trading/FuturesTicket.h | 2 +- BlockSettleUILib/UtxoModelInterface.cpp | 2 +- BlockSettleUILib/UtxoModelInterface.h | 2 +- BlockSettleUILib/UtxoReservationManager.h | 2 +- BlockSettleUILib/UtxoReservationToken.cpp | 2 +- BlockSettleUILib/UtxoReservationToken.h | 2 +- CommonUI/SslCaBundle.cpp | 2 +- CommonUI/SslCaBundle.h | 2 +- Core/ApiJson.h | 2 +- UnitTests/MockTerminal.cpp | 2 +- UnitTests/MockTerminal.h | 2 +- UnitTests/TestWebSockets.cpp | 2 +- common | 2 +- prepeare_release.py | 2 +- 71 files changed, 71 insertions(+), 71 deletions(-) diff --git a/BlockSettleHW/CMakeLists.txt b/BlockSettleHW/CMakeLists.txt index f8874f12b..f6efd92cb 100644 --- a/BlockSettleHW/CMakeLists.txt +++ b/BlockSettleHW/CMakeLists.txt @@ -1,7 +1,7 @@ # # # *********************************************************************************** -# * Copyright (C) 2020, BlockSettle AB +# * Copyright (C) 2020 - 2021, BlockSettle AB # * Distributed under the GNU Affero General Public License (AGPL v3) # * See LICENSE or http://www.gnu.org/licenses/agpl.html # * diff --git a/BlockSettleHW/hwcommonstructure.cpp b/BlockSettleHW/hwcommonstructure.cpp index 497e7de6d..5243a6dfd 100644 --- a/BlockSettleHW/hwcommonstructure.cpp +++ b/BlockSettleHW/hwcommonstructure.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwcommonstructure.h b/BlockSettleHW/hwcommonstructure.h index 638c2fbf2..6448d7ed4 100644 --- a/BlockSettleHW/hwcommonstructure.h +++ b/BlockSettleHW/hwcommonstructure.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdeviceinterface.h b/BlockSettleHW/hwdeviceinterface.h index 9f41bffbb..5191b9c09 100644 --- a/BlockSettleHW/hwdeviceinterface.h +++ b/BlockSettleHW/hwdeviceinterface.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemanager.cpp b/BlockSettleHW/hwdevicemanager.cpp index b401b413a..7acb4cad0 100644 --- a/BlockSettleHW/hwdevicemanager.cpp +++ b/BlockSettleHW/hwdevicemanager.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemanager.h b/BlockSettleHW/hwdevicemanager.h index 64be1d292..dc20a23a0 100644 --- a/BlockSettleHW/hwdevicemanager.h +++ b/BlockSettleHW/hwdevicemanager.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemodel.cpp b/BlockSettleHW/hwdevicemodel.cpp index c5e3c2467..f465caad0 100644 --- a/BlockSettleHW/hwdevicemodel.cpp +++ b/BlockSettleHW/hwdevicemodel.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/hwdevicemodel.h b/BlockSettleHW/hwdevicemodel.h index b0e53e7d5..d33510d9a 100644 --- a/BlockSettleHW/hwdevicemodel.h +++ b/BlockSettleHW/hwdevicemodel.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerClient.cpp b/BlockSettleHW/ledger/ledgerClient.cpp index a9766a64a..6ba0ddebc 100644 --- a/BlockSettleHW/ledger/ledgerClient.cpp +++ b/BlockSettleHW/ledger/ledgerClient.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerClient.h b/BlockSettleHW/ledger/ledgerClient.h index 36eef40f6..3b14238f2 100644 --- a/BlockSettleHW/ledger/ledgerClient.h +++ b/BlockSettleHW/ledger/ledgerClient.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerDevice.cpp b/BlockSettleHW/ledger/ledgerDevice.cpp index 5c694391b..1001c81b1 100644 --- a/BlockSettleHW/ledger/ledgerDevice.cpp +++ b/BlockSettleHW/ledger/ledgerDevice.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerDevice.h b/BlockSettleHW/ledger/ledgerDevice.h index da99e5012..914238402 100644 --- a/BlockSettleHW/ledger/ledgerDevice.h +++ b/BlockSettleHW/ledger/ledgerDevice.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerStructure.cpp b/BlockSettleHW/ledger/ledgerStructure.cpp index acb29ffcb..ca1963ba2 100644 --- a/BlockSettleHW/ledger/ledgerStructure.cpp +++ b/BlockSettleHW/ledger/ledgerStructure.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/ledger/ledgerStructure.h b/BlockSettleHW/ledger/ledgerStructure.h index 4b653715b..acaafac3c 100644 --- a/BlockSettleHW/ledger/ledgerStructure.h +++ b/BlockSettleHW/ledger/ledgerStructure.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorClient.cpp b/BlockSettleHW/trezor/trezorClient.cpp index 2d85036d6..e1c8bd840 100644 --- a/BlockSettleHW/trezor/trezorClient.cpp +++ b/BlockSettleHW/trezor/trezorClient.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorClient.h b/BlockSettleHW/trezor/trezorClient.h index fd3ff3794..5f3fac1f6 100644 --- a/BlockSettleHW/trezor/trezorClient.h +++ b/BlockSettleHW/trezor/trezorClient.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorDevice.cpp b/BlockSettleHW/trezor/trezorDevice.cpp index c821abf89..56e09f03e 100644 --- a/BlockSettleHW/trezor/trezorDevice.cpp +++ b/BlockSettleHW/trezor/trezorDevice.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorDevice.h b/BlockSettleHW/trezor/trezorDevice.h index d601698dc..3dcc6d838 100644 --- a/BlockSettleHW/trezor/trezorDevice.h +++ b/BlockSettleHW/trezor/trezorDevice.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleHW/trezor/trezorStructure.h b/BlockSettleHW/trezor/trezorStructure.h index da4197fd1..0b71a300d 100644 --- a/BlockSettleHW/trezor/trezorStructure.h +++ b/BlockSettleHW/trezor/trezorStructure.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/Bip39EntryValidator.cpp b/BlockSettleSigner/Bip39EntryValidator.cpp index 7dc7b0c31..b0b1b0c6f 100644 --- a/BlockSettleSigner/Bip39EntryValidator.cpp +++ b/BlockSettleSigner/Bip39EntryValidator.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/Bip39EntryValidator.h b/BlockSettleSigner/Bip39EntryValidator.h index 00e07c081..1a837018a 100644 --- a/BlockSettleSigner/Bip39EntryValidator.h +++ b/BlockSettleSigner/Bip39EntryValidator.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/EasyEncValidator.cpp b/BlockSettleSigner/EasyEncValidator.cpp index d47ee8a72..3e3907cdf 100644 --- a/BlockSettleSigner/EasyEncValidator.cpp +++ b/BlockSettleSigner/EasyEncValidator.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/QmlBridge.cpp b/BlockSettleSigner/QmlBridge.cpp index b64509479..61e6e49bc 100644 --- a/BlockSettleSigner/QmlBridge.cpp +++ b/BlockSettleSigner/QmlBridge.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/QmlBridge.h b/BlockSettleSigner/QmlBridge.h index 04589e5fd..57a0efd40 100644 --- a/BlockSettleSigner/QmlBridge.h +++ b/BlockSettleSigner/QmlBridge.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/QmlCallbackImpl.h b/BlockSettleSigner/QmlCallbackImpl.h index 4455a5eb3..50b3751ec 100644 --- a/BlockSettleSigner/QmlCallbackImpl.h +++ b/BlockSettleSigner/QmlCallbackImpl.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerAdapterListener.cpp b/BlockSettleSigner/SignerAdapterListener.cpp index bcdf8071c..2350c0133 100644 --- a/BlockSettleSigner/SignerAdapterListener.cpp +++ b/BlockSettleSigner/SignerAdapterListener.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleSigner/SignerInterfaceListener.h b/BlockSettleSigner/SignerInterfaceListener.h index 1853742b5..c563cff45 100644 --- a/BlockSettleSigner/SignerInterfaceListener.h +++ b/BlockSettleSigner/SignerInterfaceListener.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleTracker/CMakeLists.txt b/BlockSettleTracker/CMakeLists.txt index f466d6257..3cbb43848 100644 --- a/BlockSettleTracker/CMakeLists.txt +++ b/BlockSettleTracker/CMakeLists.txt @@ -1,7 +1,7 @@ # # # *********************************************************************************** -# * Copyright (C) 2020, BlockSettle AB +# * Copyright (C) 2020 - 2021, BlockSettle AB # * Distributed under the GNU Affero General Public License (AGPL v3) # * See LICENSE or http://www.gnu.org/licenses/agpl.html # * diff --git a/BlockSettleTracker/main.cpp b/BlockSettleTracker/main.cpp index b9b630dd5..bdd5b2a8d 100644 --- a/BlockSettleTracker/main.cpp +++ b/BlockSettleTracker/main.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/ApiKeyEntryDialog.cpp b/BlockSettleUILib/ApiKeyEntryDialog.cpp index 855621e79..0e29e8872 100644 --- a/BlockSettleUILib/ApiKeyEntryDialog.cpp +++ b/BlockSettleUILib/ApiKeyEntryDialog.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/ApiKeyEntryDialog.h b/BlockSettleUILib/ApiKeyEntryDialog.h index f55bf9835..6f974c246 100644 --- a/BlockSettleUILib/ApiKeyEntryDialog.h +++ b/BlockSettleUILib/ApiKeyEntryDialog.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2020, BlockSettle AB +* Copyright (C) 2020 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AuthAddressConfirmDialog.cpp b/BlockSettleUILib/AuthAddressConfirmDialog.cpp index 9f6c75f82..53c21b147 100644 --- a/BlockSettleUILib/AuthAddressConfirmDialog.cpp +++ b/BlockSettleUILib/AuthAddressConfirmDialog.cpp @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AuthAddressConfirmDialog.h b/BlockSettleUILib/AuthAddressConfirmDialog.h index c8ed2e156..88533dbe0 100644 --- a/BlockSettleUILib/AuthAddressConfirmDialog.h +++ b/BlockSettleUILib/AuthAddressConfirmDialog.h @@ -1,7 +1,7 @@ /* *********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB +* Copyright (C) 2019 - 2021, BlockSettle AB * Distributed under the GNU Affero General Public License (AGPL v3) * See LICENSE or http://www.gnu.org/licenses/agpl.html * diff --git a/BlockSettleUILib/AuthAddressConfirmDialog.ui b/BlockSettleUILib/AuthAddressConfirmDialog.ui index a877e192b..c6b914bea 100644 --- a/BlockSettleUILib/AuthAddressConfirmDialog.ui +++ b/BlockSettleUILib/AuthAddressConfirmDialog.ui @@ -2,7 +2,7 @@ - - AuthAddressConfirmDialog - - - - 0 - 0 - 376 - 175 - - - - - 0 - 0 - - - - Dialog - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 15 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - SUBMIT AUTHENTICATION ADDRESS - - - - 6 - - - 0 - - - 2 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Address - - - true - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 0 - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - SIGN WITH AUTH EID - - - - 6 - - - 0 - - - 2 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 6 - - - - - 16777215 - 6 - - - - false - - - - - - - - 0 - 0 - - - - - - - Qt::AlignCenter - - - - - - - - - - - - - - 0 - 0 - - - - true - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 120 - 0 - - - - Cancel - - - true - - - true - - - - - - - - - - - diff --git a/BlockSettleUILib/AuthAddressDialog.cpp b/BlockSettleUILib/AuthAddressDialog.cpp deleted file mode 100644 index 80405074f..000000000 --- a/BlockSettleUILib/AuthAddressDialog.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "AuthAddressDialog.h" -#include "ui_AuthAddressDialog.h" - -#include -#include -#include -#include -#include -#include - -#include "ApplicationSettings.h" -#include "AssetManager.h" -#include "AuthAddressConfirmDialog.h" -#include "AuthAddressManager.h" -#include "AuthAddressViewModel.h" -#include "BSMessageBox.h" -#include "UiUtils.h" - - -AuthAddressDialog::AuthAddressDialog(const std::shared_ptr &logger - , const std::shared_ptr &authAddressManager - , const std::shared_ptr &assetMgr - , const std::shared_ptr &settings, QWidget* parent) - : QDialog(parent) - , ui_(new Ui::AuthAddressDialog()) - , logger_(logger) - , authAddressManager_(authAddressManager) - , assetManager_(assetMgr) - , settings_(settings) -{ - ui_->setupUi(this); - - authModel_ = new AuthAddressViewModel(authAddressManager_, ui_->treeViewAuthAdress); - model_ = new AuthAdressControlProxyModel(authModel_, this); - model_->setVisibleRowsCount(settings_->get(ApplicationSettings::numberOfAuthAddressVisible)); - ui_->treeViewAuthAdress->setModel(model_); - ui_->treeViewAuthAdress->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->treeViewAuthAdress->installEventFilter(this); - - connect(ui_->treeViewAuthAdress->selectionModel(), &QItemSelectionModel::selectionChanged - , this, &AuthAddressDialog::adressSelected); - connect(model_, &AuthAdressControlProxyModel::modelReset, this, &AuthAddressDialog::onModelReset); - connect(authModel_, &AuthAddressViewModel::updateSelectionAfterReset, this, &AuthAddressDialog::onUpdateSelection); - - connect(authAddressManager_.get(), &AuthAddressManager::AddrVerifiedOrRevoked - , this, &AuthAddressDialog::onAddressStateChanged, Qt::QueuedConnection); - connect(authAddressManager_.get(), &AuthAddressManager::Error, this, &AuthAddressDialog::onAuthMgrError, Qt::QueuedConnection); - connect(authAddressManager_.get(), &AuthAddressManager::Info, this, &AuthAddressDialog::onAuthMgrInfo, Qt::QueuedConnection); - - connect(ui_->pushButtonCreate, &QPushButton::clicked, this, &AuthAddressDialog::createAddress); - connect(ui_->pushButtonRevoke, &QPushButton::clicked, this, &AuthAddressDialog::revokeSelectedAddress); - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &AuthAddressDialog::submitSelectedAddress); - connect(ui_->pushButtonDefault, &QPushButton::clicked, this, &AuthAddressDialog::setDefaultAddress); -} - -AuthAddressDialog::AuthAddressDialog(const std::shared_ptr& logger - , QWidget* parent) - : QDialog(parent) - , ui_(new Ui::AuthAddressDialog()) - , logger_(logger) -{ - ui_->setupUi(this); - - authModel_ = new AuthAddressViewModel(ui_->treeViewAuthAdress); - model_ = new AuthAdressControlProxyModel(authModel_, this); - model_->setVisibleRowsCount(10); //FIXME -// model_->setVisibleRowsCount(settings_->get(ApplicationSettings::numberOfAuthAddressVisible)); - ui_->treeViewAuthAdress->setModel(model_); - ui_->treeViewAuthAdress->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->treeViewAuthAdress->installEventFilter(this); - - connect(ui_->treeViewAuthAdress->selectionModel(), &QItemSelectionModel::selectionChanged - , this, &AuthAddressDialog::adressSelected); - connect(model_, &AuthAdressControlProxyModel::modelReset, this, &AuthAddressDialog::onModelReset); - connect(authModel_, &AuthAddressViewModel::updateSelectionAfterReset, this, &AuthAddressDialog::onUpdateSelection); - - connect(ui_->pushButtonCreate, &QPushButton::clicked, this, &AuthAddressDialog::createAddress); - connect(ui_->pushButtonRevoke, &QPushButton::clicked, this, &AuthAddressDialog::revokeSelectedAddress); - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &AuthAddressDialog::submitSelectedAddress); - connect(ui_->pushButtonDefault, &QPushButton::clicked, this, &AuthAddressDialog::setDefaultAddress); -} - -AuthAddressDialog::~AuthAddressDialog() = default; - -bool AuthAddressDialog::eventFilter(QObject* sender, QEvent* event) -{ - if (sender == ui_->treeViewAuthAdress) { - if (QEvent::KeyPress == event->type()) { - QKeyEvent* keyEvent = static_cast(event); - - if (keyEvent->matches(QKeySequence::Copy)) { - copySelectedToClipboard(); - return true; - } - } - else if (QEvent::ContextMenu == event->type()) { - QContextMenuEvent* contextMenuEvent = static_cast(event); - - QPoint pos = contextMenuEvent->pos(); - pos.setY(pos.y() - ui_->treeViewAuthAdress->header()->height()); - const auto index = ui_->treeViewAuthAdress->indexAt(pos); - if (index.isValid()) { - if (ui_->treeViewAuthAdress->selectionModel()->selectedIndexes()[0] != index) { - ui_->treeViewAuthAdress->selectionModel()->select(index, - QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - } - - QMenu menu; - menu.addAction(tr("&Copy Authentication Address"), [this]() { - copySelectedToClipboard(); - }); - menu.exec(contextMenuEvent->globalPos()); - return true; - } - } - } - - return QWidget::eventFilter(sender, event); -} - -void AuthAddressDialog::showEvent(QShowEvent *evt) -{ - if (defaultAddr_.empty()) { - if (authAddressManager_) { - defaultAddr_ = authAddressManager_->getDefault(); - if (defaultAddr_.empty() && authAddressManager_->GetAddressCount()) { - defaultAddr_ = authAddressManager_->GetAddress(0); - } - } - authModel_->setDefaultAddr(defaultAddr_); - } - - updateUnsubmittedState(); - - ui_->treeViewAuthAdress->selectionModel()->clearSelection(); - - QModelIndex index = model_->getFirstUnsubmitted(); - if (!index.isValid() && !model_->isEmpty()) { - // get first if none unsubmitted - index = model_->index(0, 0); - } - - if (index.isValid()) { - ui_->treeViewAuthAdress->selectionModel()->select( - index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - } - - ui_->labelHint->clear(); - - resizeTreeViewColumns(); - - QDialog::showEvent(evt); -} - -bool AuthAddressDialog::unsubmittedExist() const -{ - return unconfirmedExists_; -} - -void AuthAddressDialog::updateUnsubmittedState() -{ - if (authAddressManager_) { - for (size_t i = 0; i < authAddressManager_->GetAddressCount(); i++) { - const auto authAddr = authAddressManager_->GetAddress(i); - if (!authAddressManager_->hasSettlementLeaf(authAddr)) { - authAddressManager_->createSettlementLeaf(authAddr, [] {}); - unconfirmedExists_ = true; - return; - } - switch (authAddressManager_->GetState(authAddr)) { - case AuthAddressManager::AuthAddressState::Verifying: - unconfirmedExists_ = true; - break; - default: - unconfirmedExists_ = false; - break; - } - } - } -} - -void AuthAddressDialog::onAuthMgrError(const QString &details) -{ - showError(tr("Authentication Address error"), details); -} - -void AuthAddressDialog::onAuthMgrInfo(const QString &text) -{ - showInfo(tr("Authentication Address"), text); -} - -void AuthAddressDialog::showError(const QString &text, const QString &details) -{ - BSMessageBox errorMessage(BSMessageBox::critical, tr("Error"), text, details, this); - errorMessage.exec(); -} - -void AuthAddressDialog::showInfo(const QString &title, const QString &text) -{ - BSMessageBox(BSMessageBox::info, title, text).exec(); -} - -void AuthAddressDialog::setAddressToVerify(const QString &addr) -{ - if (addr.isEmpty()) { - setWindowTitle(tr("Authentication Addresses")); - } else { - setWindowTitle(tr("Address %1 requires verification").arg(addr)); - for (int i = 0; i < model_->rowCount(); i++) { - if (addr == model_->data(model_->index(i, 0))) { - const auto &index = model_->index(i, 0); - ui_->treeViewAuthAdress->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - ui_->treeViewAuthAdress->scrollTo(index); - break; - } - } - - ui_->labelHint->setText(tr("Your Authentication Address can now be Verified. Please press Verify and enter your password to execute the address verification.")); - ui_->treeViewAuthAdress->setFocus(); - } -} - -void AuthAddressDialog::setBsClient(const std::weak_ptr& bsClient) -{ - bsClient_ = bsClient; -} - -void AuthAddressDialog::onAuthAddresses(const std::vector& addrs - , const std::map& states) -{ - authModel_->onAuthAddresses(addrs, states); -} - -void AuthAddressDialog::onSubmittedAuthAddresses(const std::vector& addrs) -{ - authModel_->onSubmittedAuthAddresses(addrs); -} - -void AuthAddressDialog::onAuthVerifyTxSent() -{ - BSMessageBox(BSMessageBox::info, tr("Authentication Address") - , tr("Verification Transaction successfully sent.") - , tr("Once the validation transaction is mined six (6) blocks deep, your Authentication Address will be" - " accepted as valid by the network and you can enter orders in the Spot XBT product group.")).exec(); - accept(); -} - -void AuthAddressDialog::onUpdateSelection(int row) -{ - if (row < 0 || row >= model_->rowCount()) { - return; - } - - ui_->treeViewAuthAdress->selectionModel()->select( - model_->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); -} - -void AuthAddressDialog::copySelectedToClipboard() -{ - auto *selectionModel = ui_->treeViewAuthAdress->selectionModel(); - if (!selectionModel->hasSelection()) { - return; - } - - auto const address = model_->getAddress(selectionModel->selectedIndexes()[0]); - qApp->clipboard()->setText(QString::fromStdString(address.display())); -} - - -void AuthAddressDialog::onAddressStateChanged(const QString &addr, int st) -{ - const auto state = static_cast(st); - if (state == AuthCallbackTarget::AuthAddressState::Verified) { - BSMessageBox(BSMessageBox::success, tr("Authentication Address") - , tr("Authentication Address verified") - , tr("You may now place orders in the Spot XBT product group.") - ).exec(); - } else if (state == AuthCallbackTarget::AuthAddressState::Revoked) { - BSMessageBox(BSMessageBox::warning, tr("Authentication Address") - , tr("Authentication Address revoked") - , tr("Authentication Address %1 was revoked and could not be used for Spot XBT trading.").arg(addr)).exec(); - } - updateEnabledStates(); -} - -void AuthAddressDialog::resizeTreeViewColumns() -{ - if (!ui_->treeViewAuthAdress->model()) { - return; - } - - for (int i = ui_->treeViewAuthAdress->model()->columnCount() - 1; i >= 0; --i) { - ui_->treeViewAuthAdress->resizeColumnToContents(i); - } -} - -void AuthAddressDialog::adressSelected() -{ - updateEnabledStates(); -} - -bs::Address AuthAddressDialog::GetSelectedAddress() const -{ - auto selectedRows = ui_->treeViewAuthAdress->selectionModel()->selectedRows(); - if (!selectedRows.isEmpty()) { - return model_->getAddress(selectedRows[0]); - } - - return bs::Address(); -} - -void AuthAddressDialog::createAddress() -{ - if (authAddressManager_) { - if (authAddressManager_->GetAddressCount() > model_->getVisibleRowsCount()) { - // We already have address but they is no visible in view - model_->increaseVisibleRowsCountByOne(); - saveAddressesNumber(); - onModelReset(); - return; - } - - if (!authAddressManager_->CreateNewAuthAddress()) { - showError(tr("Failed to create new address"), tr("Auth wallet error")); - } else { - ui_->pushButtonCreate->setEnabled(false); - model_->increaseVisibleRowsCountByOne(); - saveAddressesNumber(); - } - } - else { -/* if (nbAddresses > model_->getVisibleRowsCount()) { - model_->increaseVisibleRowsCountByOne(); - saveAddressesNumber(); - onModelReset(); - return; - }*/ - emit needNewAuthAddress(); - ui_->pushButtonCreate->setEnabled(false); - model_->increaseVisibleRowsCountByOne(); - saveAddressesNumber(); - } -} - -void AuthAddressDialog::revokeSelectedAddress() -{ - auto selectedAddress = GetSelectedAddress(); - if (selectedAddress.empty()) { - return; - } - - BSMessageBox revokeQ(BSMessageBox::question, tr("Authentication Address") - , tr("Revoke Authentication Address") - , tr("Your Authentication Address\n" - "%1\n" - "will be revoked. Are you sure you wish to continue?") - .arg(QString::fromStdString(selectedAddress.display())) - , this); - if (revokeQ.exec() == QDialog::Accepted) { - authAddressManager_->RevokeAddress(selectedAddress); - } -} - -void AuthAddressDialog::submitSelectedAddress() -{ - ui_->pushButtonSubmit->setEnabled(false); - ui_->labelHint->clear(); - - const auto selectedAddress = GetSelectedAddress(); - if (selectedAddress.empty()) { - return; - } - - if (authAddressManager_) { - const auto state = authAddressManager_->GetState(selectedAddress); - if (state != AuthAddressManager::AuthAddressState::NotSubmitted) { - SPDLOG_LOGGER_ERROR(logger_, "refuse to submit address in state: {}", (int)state); - return; - } - - auto bsClient = bsClient_.lock(); - - if (!bsClient) { - SPDLOG_LOGGER_ERROR(logger_, "bsClient_ in not set"); - return; - } - - setLastSubmittedAddress(selectedAddress); - - AuthAddressConfirmDialog confirmDlg{ bsClient_, lastSubmittedAddress_, authAddressManager_, settings_, this }; - auto result = confirmDlg.exec(); - - setLastSubmittedAddress({}); - - if (result == QDialog::Accepted) { - accept(); - } - } - else { - auto confirmDlg = new AuthAddressConfirmDialog(selectedAddress, this); - if (confirmDlg->exec() == QDialog::Accepted) { - emit needSubmitAuthAddress(selectedAddress); - } - } -} - -void AuthAddressDialog::setDefaultAddress() -{ - auto selectedAddress = GetSelectedAddress(); - if (!selectedAddress.empty()) { - defaultAddr_ = selectedAddress; - authModel_->setDefaultAddr(defaultAddr_); - authAddressManager_->setDefault(defaultAddr_); - resizeTreeViewColumns(); - } -} - -void AuthAddressDialog::onModelReset() -{ - updateEnabledStates(); - saveAddressesNumber(); - adressSelected(); -} - -void AuthAddressDialog::saveAddressesNumber() -{ - const int newNumber = std::max(1, model_->rowCount()); - const int oldNumber = settings_ ? settings_->get(ApplicationSettings::numberOfAuthAddressVisible) : 10; //FIXME - if (newNumber == oldNumber) { - return; // nothing to save - } - else if (newNumber > oldNumber) { - // we have added address - emit onUpdateSelection(model_->rowCount() - 1); - } - - if (settings_) { - settings_->set(ApplicationSettings::numberOfAuthAddressVisible, newNumber); - settings_->SaveSettings(); - } - else { - emit putSetting(ApplicationSettings::numberOfAuthAddressVisible, newNumber); - } - - if (model_->isEmpty()) { - model_->setVisibleRowsCount(1); - } -} - -void AuthAddressDialog::setLastSubmittedAddress(const bs::Address &address) -{ - if (lastSubmittedAddress_ != address) { - lastSubmittedAddress_ = address; - updateEnabledStates(); - } -} - -void AuthAddressDialog::updateEnabledStates() -{ - const auto selectionModel = ui_->treeViewAuthAdress->selectionModel(); - if (selectionModel->hasSelection()) { - const auto address = model_->getAddress(selectionModel->selectedRows()[0]); - - const bool allowSubmit = authAddressManager_ ? authAddressManager_->UserCanSubmitAuthAddress() : true; - - if (authAddressManager_) { - switch (authAddressManager_->GetState(address)) { - case AuthAddressManager::AuthAddressState::NotSubmitted: - ui_->pushButtonRevoke->setEnabled(false); - ui_->pushButtonSubmit->setEnabled(lastSubmittedAddress_.empty() && allowSubmit); - ui_->pushButtonDefault->setEnabled(false); - break; - case AuthAddressManager::AuthAddressState::Submitted: - case AuthAddressManager::AuthAddressState::Tainted: - case AuthAddressManager::AuthAddressState::Verifying: - case AuthAddressManager::AuthAddressState::Revoked: - case AuthAddressManager::AuthAddressState::RevokedByBS: - case AuthAddressManager::AuthAddressState::Invalid: - ui_->pushButtonRevoke->setEnabled(false); - ui_->pushButtonSubmit->setEnabled(false); - ui_->pushButtonDefault->setEnabled(false); - break; - case AuthAddressManager::AuthAddressState::Verified: - ui_->pushButtonRevoke->setEnabled(authAddressManager_->readyError() == AuthAddressManager::ReadyError::NoError); - ui_->pushButtonSubmit->setEnabled(false); - ui_->pushButtonDefault->setEnabled(address != defaultAddr_); - break; - default: - break; - } - } - else { - switch (authModel_->getState(address)) { - case AddressVerificationState::Verified: - ui_->pushButtonRevoke->setEnabled(true); - ui_->pushButtonSubmit->setEnabled(false); - ui_->pushButtonDefault->setEnabled(address != defaultAddr_); - break; - default: - ui_->pushButtonRevoke->setEnabled(false); - ui_->pushButtonSubmit->setEnabled(authModel_->canSubmit(address)); - ui_->pushButtonDefault->setEnabled(false); - break; - } - } - } - else { - ui_->pushButtonRevoke->setEnabled(false); - ui_->pushButtonSubmit->setEnabled(false); - ui_->pushButtonDefault->setEnabled(false); - } - - ui_->pushButtonCreate->setEnabled(lastSubmittedAddress_.empty() && - model_ && !model_->isUnsubmittedAddressVisible()); -} diff --git a/BlockSettleUILib/AuthAddressDialog.h b/BlockSettleUILib/AuthAddressDialog.h deleted file mode 100644 index 671e80932..000000000 --- a/BlockSettleUILib/AuthAddressDialog.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __AUTH_ADDRESS_DIALOG_H__ -#define __AUTH_ADDRESS_DIALOG_H__ - -#include "ApplicationSettings.h" -#include "AuthAddressManager.h" -#include "BinaryData.h" -#include "BsClient.h" -#include "ValidityFlag.h" - -#include - -#include -#include - -class AssetManager; -class AuthAdressControlProxyModel; -class QItemSelection; - -namespace Ui { - class AuthAddressDialog; -} -namespace bs { - struct TradeSettings; -} -namespace spdlog { - class logger; -} -class AuthAddressViewModel; -class AuthAddressConfirmDialog; - - -class AuthAddressDialog : public QDialog -{ -Q_OBJECT - -public: - [[deprecated]] AuthAddressDialog(const std::shared_ptr &logger - , const std::shared_ptr& authAddressManager - , const std::shared_ptr & - , const std::shared_ptr &, QWidget* parent = nullptr); - AuthAddressDialog(const std::shared_ptr&, QWidget* parent = nullptr); - ~AuthAddressDialog() override; - - void setAddressToVerify(const QString &addr); - void setBsClient(const std::weak_ptr& bsClient); - - void onAuthAddresses(const std::vector& - , const std::map&); - void onSubmittedAuthAddresses(const std::vector&); - -signals: - void askForConfirmation(const QString &address, double txAmount); - void needNewAuthAddress(); - void needSubmitAuthAddress(const bs::Address&); - void putSetting(ApplicationSettings::Setting, const QVariant&); - -private slots: - void resizeTreeViewColumns(); - void adressSelected(); - - void createAddress(); - void revokeSelectedAddress(); - void submitSelectedAddress(); - void setDefaultAddress(); - - void onModelReset(); - void onAddressStateChanged(const QString &addr, int state); - - void onAuthMgrError(const QString &details); - void onAuthMgrInfo(const QString &text); - - void onAuthVerifyTxSent(); - void onUpdateSelection(int row); - void copySelectedToClipboard(); - -protected: - void showEvent(QShowEvent *) override; - void showError(const QString &text, const QString &details = {}); - void showInfo(const QString &title, const QString &text); - bool eventFilter(QObject* sender, QEvent* event) override; - -private: - bs::Address GetSelectedAddress() const; - bool unsubmittedExist() const; - void updateUnsubmittedState(); - void saveAddressesNumber(); - - void setLastSubmittedAddress(const bs::Address &address); - void updateEnabledStates(); - -private: - std::unique_ptr ui_; - std::shared_ptr logger_; - std::shared_ptr authAddressManager_; - std::shared_ptr assetManager_; - std::shared_ptr settings_; - AuthAddressViewModel* authModel_{ nullptr }; - QPointer model_; - bs::Address defaultAddr_; - std::weak_ptr bsClient_; - ValidityFlag validityFlag_; - - bs::Address lastSubmittedAddress_{}; - - bool unconfirmedExists_ = false; -}; - -#endif // __AUTH_ADDRESS_DIALOG_H__ diff --git a/BlockSettleUILib/AuthAddressDialog.ui b/BlockSettleUILib/AuthAddressDialog.ui deleted file mode 100644 index 7bfb05e59..000000000 --- a/BlockSettleUILib/AuthAddressDialog.ui +++ /dev/null @@ -1,313 +0,0 @@ - - - - AuthAddressDialog - - - - 0 - 0 - 556 - 320 - - - - - 0 - 0 - - - - - 556 - 320 - - - - - 556 - 320 - - - - Authentication Addresses - - - - :/resources/brand-logo.png:/resources/brand-logo.png - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Select Address - - - - 6 - - - QLayout::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Submit an Authentication Address for access to bitcoin trading. - - - Qt::PlainText - - - true - - - - - - - - 0 - 0 - - - - Qt::NoContextMenu - - - true - - - false - - - false - - - - - - - - - - Qt::PlainText - - - true - - - - - - - - - - - - - true - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Qt::Horizontal - - - - 400 - 20 - - - - - - - - false - - - - 0 - 0 - - - - - 110 - 35 - - - - Set as Default - - - false - - - - - - - false - - - - 0 - 0 - - - - - 110 - 35 - - - - Revoke - - - false - - - - - - - - 0 - 0 - - - - - 110 - 35 - - - - Create Address - - - false - - - true - - - - - - - false - - - - 0 - 0 - - - - - 110 - 35 - - - - Submit - - - false - - - true - - - - - - - - - - - - - - diff --git a/BlockSettleUILib/AuthAddressViewModel.cpp b/BlockSettleUILib/AuthAddressViewModel.cpp deleted file mode 100644 index aac4823a3..000000000 --- a/BlockSettleUILib/AuthAddressViewModel.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include -#include -#include "AuthAddressViewModel.h" -#include "EncryptionUtils.h" - - -AuthAddressViewModel::AuthAddressViewModel(const std::shared_ptr& authManager, QObject *parent) - : QAbstractItemModel(parent) - , authManager_(authManager) - , defaultAddr_(authManager->getDefault()) -{ - connect(authManager_.get(), &AuthAddressManager::AddressListUpdated, this, &AuthAddressViewModel::onAddressListUpdated, Qt::QueuedConnection); - connect(authManager_.get(), &AuthAddressManager::AuthWalletChanged, this, &AuthAddressViewModel::onAddressListUpdated, Qt::QueuedConnection); -} - -AuthAddressViewModel::AuthAddressViewModel(QObject* parent) - : QAbstractItemModel(parent) -{} - -AuthAddressViewModel::~AuthAddressViewModel() noexcept = default; - -int AuthAddressViewModel::columnCount(const QModelIndex&) const -{ - return static_cast(AuthAddressViewColumns::ColumnsCount); -} - -int AuthAddressViewModel::rowCount(const QModelIndex&) const -{ - return addresses_.size(); -} - -QVariant AuthAddressViewModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() < 0 || index.row() >= addresses_.size()) { - return {}; - } - - const auto address = addresses_[index.row()]; - - if (role == Qt::DisplayRole) { - switch(static_cast(index.column())) { - case AuthAddressViewColumns::ColumnName: - return QString::fromStdString(address.display()); - case AuthAddressViewColumns::ColumnState: - if (authManager_) { - switch (authManager_->GetState(address)) { - case AuthAddressManager::AuthAddressState::Unknown: - return tr("Loading state..."); - case AuthAddressManager::AuthAddressState::NotSubmitted: - return tr("Not Submitted"); - case AuthAddressManager::AuthAddressState::Submitted: - return tr("Submitted"); - case AuthAddressManager::AuthAddressState::Tainted: - return tr("Not Submitted"); - case AuthAddressManager::AuthAddressState::Verifying: - return tr("Verification pending"); - case AuthAddressManager::AuthAddressState::Verified: - return tr("Verified"); - case AuthAddressManager::AuthAddressState::Revoked: - return tr("Revoked"); - case AuthAddressManager::AuthAddressState::RevokedByBS: - return tr("Invalidated by BS"); - case AuthAddressManager::AuthAddressState::Invalid: - return tr("State loading failed"); - } - } - else { - const auto& itState = states_.find(address); - if (itState == states_.end()) { - return tr("Loading state..."); - } - switch (itState->second) { - case AddressVerificationState::Verified: - return tr("Verified"); - case AddressVerificationState::Revoked: - return tr("Revoked"); - case AddressVerificationState::Virgin: - if (submittedAddresses_.find(address) != submittedAddresses_.end()) { - return tr("Submitted"); - } - else { - return tr("Not submitted"); - } - case AddressVerificationState::Verifying: - return tr("Verifying"); - default: return tr("Unknown %1").arg((int)itState->second); - } - } - default: - return {}; - } - } - else if (role == Qt::FontRole) { - if (!defaultAddr_.empty() && (address.prefixed() == defaultAddr_.prefixed())) { - QFont font; - font.setBold(true); - return font; - } - } - - return {}; -} - -QVariant AuthAddressViewModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != Qt::Horizontal) { - return QVariant(); - } - - if (role == Qt::DisplayRole) { - switch(static_cast(section)) { - case AuthAddressViewColumns::ColumnName: - return tr("Address"); - case AuthAddressViewColumns::ColumnState: - return tr("Status"); - default: - return QVariant(); - } - } - - return QVariant(); -} - -QModelIndex AuthAddressViewModel::index(int row, int column, const QModelIndex&) const -{ - if ((row < 0) || (row >= rowCount()) || (column < 0) || (column >= columnCount())) { - return QModelIndex(); - } - - return createIndex(row, column); -} - -QModelIndex AuthAddressViewModel::parent(const QModelIndex&) const -{ - return {}; -} - -bs::Address AuthAddressViewModel::getAddress(const QModelIndex& index) const -{ - if (!index.isValid() || index.row() < 0 || index.row() >= addresses_.size()) { - return {}; - } - - return addresses_[index.row()]; -} - -bool AuthAddressViewModel::isAddressNotSubmitted(int row) const -{ - if (row < 0 || row >= addresses_.size()) { - return false; - } - - const auto& address = addresses_[row]; - if (authManager_) { - const auto addrState = authManager_->GetState(address); - return addrState == AuthAddressManager::AuthAddressState::NotSubmitted || - addrState == AuthAddressManager::AuthAddressState::Tainted; - } - else { - return (submittedAddresses_.find(address) == submittedAddresses_.end()); - } -} - -void AuthAddressViewModel::setDefaultAddr(const bs::Address &addr) -{ - defaultAddr_ = addr; - for (int i = 0; i < addresses_.size(); ++i) { - if (addresses_[i].prefixed() == defaultAddr_.prefixed()) { - emit dataChanged(index(i, 0), index(i, 0), { Qt::FontRole }); - return; - } - } -} - -void AuthAddressViewModel::onAuthAddresses(const std::vector&addrs - , const std::map& states) -{ - if (!addrs.empty()) { - beginResetModel(); - addresses_ = addrs; - endResetModel(); - } - if (!states.empty()) { - for (const auto& state : states) { - states_[state.first] = state.second; - } - emit dataChanged(index(0, 0), index(addresses_.size() - 1, 0)); - } -} - -void AuthAddressViewModel::onSubmittedAuthAddresses(const std::vector& addrs) -{ - submittedAddresses_.insert(addrs.cbegin(), addrs.cend()); -} - -bool AuthAddressViewModel::canSubmit(const bs::Address& addr) const -{ - const auto& itState = states_.find(addr); - if ((itState == states_.end()) || (itState->second != AddressVerificationState::Virgin)) { - return false; - } - return (submittedAddresses_.find(addr) == submittedAddresses_.end()); -} - -AddressVerificationState AuthAddressViewModel::getState(const bs::Address& addr) const -{ - try { - return states_.at(addr); - } - catch (const std::exception&) { - return AddressVerificationState::VerificationFailed; - } -} - -void AuthAddressViewModel::onAddressListUpdated() -{ - // store selection - const auto treeView = qobject_cast(QObject::parent()); - std::pair selectedRowToName; - if (treeView && treeView->selectionModel() && treeView->selectionModel()->hasSelection()) { - selectedRowToName.first = treeView->selectionModel()->selectedRows()[0].row(); - selectedRowToName.second = getAddress(index(selectedRowToName.first, - static_cast(AuthAddressViewColumns::ColumnName))).display(); - } - - // do actual update - const int sizeBeforeReset = addresses_.size(); - emit beginResetModel(); - addresses_.clear(); - const int total = authManager_->GetAddressCount(); - addresses_.reserve(total); - for (int i = 0; i < total; ++i) { - addresses_.push_back(authManager_->GetAddress(i)); - } - emit endResetModel(); - - // restore selection if needed - if (sizeBeforeReset >= addresses_.size() - && selectedRowToName.first < addresses_.size() - && selectedRowToName.second == addresses_[selectedRowToName.first].display()) { - emit updateSelectionAfterReset(selectedRowToName.first); - } -} - -AuthAdressControlProxyModel::AuthAdressControlProxyModel(AuthAddressViewModel *sourceModel, QWidget *parent) - : QSortFilterProxyModel(parent) - , sourceModel_(sourceModel) -{ - setDynamicSortFilter(true); - setSourceModel(sourceModel_); -} - -AuthAdressControlProxyModel::~AuthAdressControlProxyModel() = default; - -void AuthAdressControlProxyModel::setVisibleRowsCount(int rows) -{ - visibleRowsCount_ = rows; - invalidate(); -} - -void AuthAdressControlProxyModel::increaseVisibleRowsCountByOne() -{ - ++visibleRowsCount_; - invalidate(); -} - -int AuthAdressControlProxyModel::getVisibleRowsCount() const -{ - return visibleRowsCount_; -} - -bs::Address AuthAdressControlProxyModel::getAddress(const QModelIndex& index) const -{ - if (!index.isValid()) { - return {}; - } - - const auto& sourceIndex = mapToSource(index); - return sourceModel_->getAddress(sourceIndex); -} - -bool AuthAdressControlProxyModel::isEmpty() const -{ - return rowCount() == 0; -} - -QModelIndex AuthAdressControlProxyModel::getFirstUnsubmitted() const -{ - if (isEmpty()) { - return {}; - } - - for (int i = 0; i < rowCount(); ++i) { - if (sourceModel_->isAddressNotSubmitted(i)) { - return index(i, 0); - } - } - - return {}; -} - -bool AuthAdressControlProxyModel::isUnsubmittedAddressVisible() const -{ - if (isEmpty()) { - return false; - } - - for (int i = 0; i < visibleRowsCount_; ++i) { - if (sourceModel_->isAddressNotSubmitted(i)) { - return true; - } - } - - return false; -} - -bool AuthAdressControlProxyModel::filterAcceptsRow(int row, const QModelIndex&) const -{ - return visibleRowsCount_ > row; -} diff --git a/BlockSettleUILib/AuthAddressViewModel.h b/BlockSettleUILib/AuthAddressViewModel.h deleted file mode 100644 index 7fcc54854..000000000 --- a/BlockSettleUILib/AuthAddressViewModel.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __AUTH_ADDRESS_MODEL_H__ -#define __AUTH_ADDRESS_MODEL_H__ - -#include -#include -#include - -#include - -#include "AuthAddress.h" -#include "AuthAddressManager.h" -#include "BinaryData.h" - -class AuthAddressViewModel : public QAbstractItemModel -{ - Q_OBJECT -public: - [[deprecated]] AuthAddressViewModel(const std::shared_ptr& authManager, QObject *parent = nullptr); - AuthAddressViewModel(QObject* parent = nullptr); - ~AuthAddressViewModel() noexcept override; - - AuthAddressViewModel(const AuthAddressViewModel&) = delete; - AuthAddressViewModel& operator = (const AuthAddressViewModel&) = delete; - - AuthAddressViewModel(AuthAddressViewModel&&) = delete; - AuthAddressViewModel& operator = (AuthAddressViewModel&&) = delete; - - bs::Address getAddress(const QModelIndex& index) const; - bool isAddressNotSubmitted(int row) const; - void setDefaultAddr(const bs::Address &addr); - - void onAuthAddresses(const std::vector& - , const std::map&); - void onSubmittedAuthAddresses(const std::vector&); - - bool canSubmit(const bs::Address&) const; - AddressVerificationState getState(const bs::Address&) const; - -public: - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &child) const override; - -private slots : - void onAddressListUpdated(); - -signals: - void updateSelectionAfterReset(int row); - -private: - std::shared_ptr authManager_; - -private: - enum AuthAddressViewColumns : int - { - ColumnName, - ColumnState, - ColumnsCount - }; - bs::Address defaultAddr_; - std::vector addresses_; - std::set submittedAddresses_; - std::map states_; -}; - -class AuthAdressControlProxyModel : public QSortFilterProxyModel { -public: - explicit AuthAdressControlProxyModel(AuthAddressViewModel *sourceModel, QWidget *parent); - ~AuthAdressControlProxyModel() override; - - void setVisibleRowsCount(int rows); - void increaseVisibleRowsCountByOne(); - int getVisibleRowsCount() const; - - bs::Address getAddress(const QModelIndex& index) const; - bool isEmpty() const; - - QModelIndex getFirstUnsubmitted() const; - bool isUnsubmittedAddressVisible() const; - -protected: - bool filterAcceptsRow(int row, const QModelIndex& parent) const override; - -private: - int visibleRowsCount_{}; - QPointer sourceModel_ = nullptr; -}; - -#endif // __AUTH_ADDRESS_MODEL_H__ diff --git a/BlockSettleUILib/BSTerminalMainWindow.ui b/BlockSettleUILib/BSTerminalMainWindow.ui index 99acdfb26..f91af01b9 100644 --- a/BlockSettleUILib/BSTerminalMainWindow.ui +++ b/BlockSettleUILib/BSTerminalMainWindow.ui @@ -488,16 +488,6 @@ OVERVIEW - - - TRADE - - - - - DEALING - - WALLETS @@ -519,16 +509,6 @@ EXPLORER - - - CHARTS - - - - - OTC CHAT - - @@ -948,40 +928,15 @@
PortfolioWidget.h
1 - - RFQRequestWidget - QWidget -
Trading/RFQRequestWidget.h
- 1 -
- - RFQReplyWidget - QWidget -
Trading/RFQReplyWidget.h
- 1 -
ExplorerWidget QWidget
ExplorerWidget.h
1
- - ChartWidget - QWidget -
ChartWidget.h
- 1 -
- - ChatWidget - QWidget -
ChatUI/ChatWidget.h
- 1 -
-
diff --git a/BlockSettleUILib/CCAmountValidator.cpp b/BlockSettleUILib/CCAmountValidator.cpp deleted file mode 100644 index 3fec14e8d..000000000 --- a/BlockSettleUILib/CCAmountValidator.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "CCAmountValidator.h" - -#include "UiUtils.h" - -CCAmountValidator::CCAmountValidator(QObject* parent) - : CustomDoubleValidator(parent) -{ - setBottom(0.0); - setDecimals(UiUtils::GetAmountPrecisionCC()); -} \ No newline at end of file diff --git a/BlockSettleUILib/CCAmountValidator.h b/BlockSettleUILib/CCAmountValidator.h deleted file mode 100644 index 154bb193f..000000000 --- a/BlockSettleUILib/CCAmountValidator.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __CC_AMOUNT_VALIDATOR_H__ -#define __CC_AMOUNT_VALIDATOR_H__ - -#include "CustomControls/CustomDoubleValidator.h" - -class CCAmountValidator : public CustomDoubleValidator -{ -Q_OBJECT - -public: - CCAmountValidator(QObject* parent); - ~CCAmountValidator() noexcept override = default; -}; - -#endif // __CC_AMOUNT_VALIDATOR_H__ diff --git a/BlockSettleUILib/CCPortfolioModel.cpp b/BlockSettleUILib/CCPortfolioModel.cpp index d29afe9ef..bff9569b4 100644 --- a/BlockSettleUILib/CCPortfolioModel.cpp +++ b/BlockSettleUILib/CCPortfolioModel.cpp @@ -734,16 +734,6 @@ int CCPortfolioModel::rowCount(const QModelIndex & parentIndex) const return getNodeByIndex(parentIndex)->childrenCount(); } -std::shared_ptr CCPortfolioModel::assetManager() -{ - return assetManager_; -} - -std::shared_ptr CCPortfolioModel::walletsManager() -{ - return walletsManager_; -} - void CCPortfolioModel::onHDWallet(const bs::sync::WalletInfo& wi) { if (!root_->HaveXBTGroup()) { @@ -810,23 +800,6 @@ void CCPortfolioModel::onBalance(const std::string& currency, double balance) } } -void CCPortfolioModel::onPriceChanged(const std::string& currency, double price) -{ - const auto& fxGroup = root_->GetFXGroup(); - const auto &fxNode = fxGroup->GetFXNode(currency); - - if (fxNode && fxNode->SetPrice(price)) { - dataChanged(index(fxGroup->getRow(), PortfolioColumns::XBTValueColumn) - , index(fxGroup->getRow(), PortfolioColumns::XBTValueColumn) - , { Qt::DisplayRole }); - - const auto& parentIndex = createIndex(fxGroup->getRow(), 0, static_cast(fxGroup)); - dataChanged(index(fxNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) - , index(fxNode->getRow(), PortfolioColumns::XBTValueColumn, parentIndex) - , { Qt::DisplayRole }); - } -} - bool CCPortfolioModel::hasChildren(const QModelIndex& parentIndex) const { return getNodeByIndex(parentIndex)->HasChildren(); diff --git a/BlockSettleUILib/CCPortfolioModel.h b/BlockSettleUILib/CCPortfolioModel.h index 043b13370..a625e6568 100644 --- a/BlockSettleUILib/CCPortfolioModel.h +++ b/BlockSettleUILib/CCPortfolioModel.h @@ -40,14 +40,10 @@ class CCPortfolioModel : public QAbstractItemModel CCPortfolioModel(CCPortfolioModel&&) = delete; CCPortfolioModel& operator = (CCPortfolioModel&&) = delete; - [[deprecated]] std::shared_ptr assetManager(); - [[deprecated]] std::shared_ptr walletsManager(); - void onHDWallet(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData&); void onWalletBalance(const bs::sync::WalletBalanceData&); void onBalance(const std::string& currency, double balance); - void onPriceChanged(const std::string& currency, double price); private: enum PortfolioColumns diff --git a/BlockSettleUILib/CCTokenEntryDialog.cpp b/BlockSettleUILib/CCTokenEntryDialog.cpp deleted file mode 100644 index 457433774..000000000 --- a/BlockSettleUILib/CCTokenEntryDialog.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "CCTokenEntryDialog.h" -#include "ui_CCTokenEntryDialog.h" - -#include "bs_communication.pb.h" - -#include -#include -#include - -#include "BSErrorCodeStrings.h" -#include "BSMessageBox.h" -#include "CCFileManager.h" -#include "SignContainer.h" -#include "Wallets/SyncHDLeaf.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "BSErrorCodeStrings.h" -#include "ApplicationSettings.h" - -namespace { - const auto kAutheIdTimeout = int(BsClient::autheidCcAddressTimeout() / std::chrono::seconds(1)); -} - -CCTokenEntryDialog::CCTokenEntryDialog(const std::shared_ptr &walletsMgr - , const std::shared_ptr &ccFileMgr - , const std::shared_ptr &settings - , QWidget *parent) - : QDialog(parent) - , ui_(new Ui::CCTokenEntryDialog()) - , ccFileMgr_(ccFileMgr) - , walletsMgr_(walletsMgr) - , settings_(settings) -{ - ui_->setupUi(this); - - connect(ui_->pushButtonOk, &QPushButton::clicked, this, &CCTokenEntryDialog::accept); - connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &CCTokenEntryDialog::onCancel); - connect(ui_->lineEditToken, &QLineEdit::textEdited, this, &CCTokenEntryDialog::tokenChanged); - connect(ui_->lineEditToken, &QLineEdit::returnPressed, this, &CCTokenEntryDialog::accept, Qt::QueuedConnection); - - connect(ccFileMgr_.get(), &CCFileManager::CCAddressSubmitted, this, &CCTokenEntryDialog::onCCAddrSubmitted, Qt::QueuedConnection); - connect(ccFileMgr_.get(), &CCFileManager::CCInitialSubmitted, this, &CCTokenEntryDialog::onCCInitialSubmitted, Qt::QueuedConnection); - connect(ccFileMgr_.get(), &CCFileManager::CCSubmitFailed, this, &CCTokenEntryDialog::onCCSubmitFailed, Qt::QueuedConnection); - - updateOkState(); - - ui_->progressBar->setMaximum(kAutheIdTimeout * 10); - - timer_.setInterval(100); - connect(&timer_, &QTimer::timeout, this, &CCTokenEntryDialog::onTimer); - - ui_->stackedWidgetAuth->setCurrentIndex(0); -} - -CCTokenEntryDialog::~CCTokenEntryDialog() = default; - -void CCTokenEntryDialog::tokenChanged() -{ - ui_->labelTokenHint->clear(); - ui_->pushButtonOk->setEnabled(false); - strToken_ = ui_->lineEditToken->text().toStdString(); - if (strToken_.empty()) { - return; - } - - try { - const auto decoded = BtcUtils::base58toScrAddr(strToken_); - Blocksettle::Communication::CCSeedResponse response; - if (!response.ParseFromArray(decoded.getPtr(), decoded.getSize())) { - throw std::invalid_argument("invalid internal token structure"); - } - seed_ = response.bsseed(); - ccProduct_ = response.ccproduct(); - - ccWallet_ = walletsMgr_->getCCWallet(ccProduct_); - if (!ccWallet_) { - ui_->labelTokenHint->setText(tr("The Terminal will prompt you to create the relevant subwallet (%1)," - " if required").arg(QString::fromStdString(ccProduct_))); - - MessageBoxCCWalletQuestion qry(QString::fromStdString(ccProduct_), this); - if (qry.exec() == QDialog::Accepted) { - const auto createCCLeafCb = [this](bs::error::ErrorCode result){ - if (result == bs::error::ErrorCode::TxCancelled) { - reject(); - } - else if (result == bs::error::ErrorCode::NoError) { - ccWallet_ = walletsMgr_->getCCWallet(ccProduct_); - if (ccWallet_) { - ui_->labelTokenHint->setText(tr("Private Market subwallet for %1 created!").arg(QString::fromStdString(ccProduct_))); - } else { - ui_->labelTokenHint->setText(tr("Failed to create CC subwallet %1").arg(QString::fromStdString(ccProduct_))); - } - updateOkState(); - } - else { - ui_->labelTokenHint->setText(tr("Failed to create CC subwallet %1, reason:\n%2") - .arg(QString::fromStdString(ccProduct_)) - .arg(bs::error::ErrorCodeToString(result))); - } - }; - - walletsMgr_->CreateCCLeaf(ccProduct_, createCCLeafCb); - - } else { - reject(); - } - } - } - catch (const std::exception &e) { - ui_->labelTokenHint->setText(tr("Token you entered is not valid: %1").arg(QLatin1String(e.what()))); - } - updateOkState(); -} - -void CCTokenEntryDialog::updateOkState() -{ - ui_->pushButtonOk->setEnabled(ccWallet_ != nullptr); -} - -void CCTokenEntryDialog::accept() -{ - if (!ccWallet_) { - reject(); - return; - } - const auto &cbAddr = [this](const bs::Address &address) { - if (ccFileMgr_->submitAddress(address, seed_, ccProduct_)) { - ui_->pushButtonOk->setEnabled(false); - } else { - onCCSubmitFailed(QString::fromStdString(address.display()) - , tr("Submission to PB failed")); - } - }; - ccWallet_->getNewExtAddress(cbAddr); - - ui_->progressBar->setValue(kAutheIdTimeout * 10); - timeLeft_ = kAutheIdTimeout; - timer_.start(); - ui_->stackedWidgetAuth->setCurrentWidget(ui_->pageAuth); - ui_->labelToken->setText(ui_->lineEditToken->text()); - ui_->labelProduct->setText(QString::fromStdString(ccProduct_)); -} - -void CCTokenEntryDialog::reject() -{ - ccFileMgr_->cancelActiveSign(); - QDialog::reject(); -} - -void CCTokenEntryDialog::onCCAddrSubmitted(const QString addr) -{ - QDialog::accept(); - - const bool isProd = settings_->get(ApplicationSettings::envConfiguration) == - static_cast(ApplicationSettings::EnvConfiguration::Production); - - auto body = tr("BlockSettle will issue your tokens within the next %1.").arg(isProd ? tr("24 hours") : tr("15 minutes")); - if (!isProd) { - body += tr("\n\nOnce mined one block, the tokens can be traded."); - } - - BSMessageBox(BSMessageBox::success, tr("Submission Successful") - , tr("Equity Token Submitted") - , body).exec(); -} - -void CCTokenEntryDialog::onCCInitialSubmitted(const QString addr) -{ - ui_->labelTokenHint->setText(tr("Request was sent")); -} - -void CCTokenEntryDialog::onCCSubmitFailed(const QString, const QString &err) -{ - reject(); - BSMessageBox(BSMessageBox::critical, tr("CC Token submit failure") - , tr("Failed to submit Private Market token to BlockSettle"), err, this).exec(); -} - -void CCTokenEntryDialog::onTimer() -{ - timeLeft_ -= timer_.interval() / 1000.0; - - if (timeLeft_ < 0) { - return; - } - - ui_->progressBar->setValue(int(timeLeft_ * 10)); - ui_->labelTimeLeft->setText(tr("%1 seconds left").arg(int(timeLeft_))); -} - -void CCTokenEntryDialog::onCancel() -{ - setDisabled(true); - timeLeft_ = 0; - timer_.stop(); - reject(); -} diff --git a/BlockSettleUILib/CCTokenEntryDialog.h b/BlockSettleUILib/CCTokenEntryDialog.h deleted file mode 100644 index 28a81b459..000000000 --- a/BlockSettleUILib/CCTokenEntryDialog.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __CC_TOKEN_ENTRY_DIALOG_H__ -#define __CC_TOKEN_ENTRY_DIALOG_H__ - -#include -#include -#include - -#include "BinaryData.h" -#include "BSErrorCode.h" -#include "EncryptionUtils.h" -#include "ValidityFlag.h" - -namespace Ui { - class CCTokenEntryDialog; -} -namespace bs { - namespace sync { - class Wallet; - class WalletsManager; - namespace hd { - class Leaf; - } - } -} -class CCFileManager; -class ApplicationSettings; - -class CCTokenEntryDialog : public QDialog -{ -Q_OBJECT - -public: - CCTokenEntryDialog( - const std::shared_ptr &, - const std::shared_ptr &, - const std::shared_ptr &, - QWidget *parent); - ~CCTokenEntryDialog() override; - -protected: - void accept() override; - void reject() override; - -private slots: - void tokenChanged(); - void updateOkState(); - void onCCAddrSubmitted(const QString addr); - void onCCInitialSubmitted(const QString addr); - void onCCSubmitFailed(const QString addr, const QString &err); - - void onTimer(); - void onCancel(); -private: - std::unique_ptr ui_; - - std::shared_ptr ccFileMgr_; - std::shared_ptr walletsMgr_; - std::shared_ptr ccWallet_; - std::shared_ptr settings_; - - std::string ccProduct_; - std::string strToken_; - uint32_t seed_ = 0; - - QTimer timer_; - double timeLeft_{}; - - ValidityFlag validityFlag_; -}; - -#endif // __CC_TOKEN_ENTRY_DIALOG_H__ diff --git a/BlockSettleUILib/CCTokenEntryDialog.ui b/BlockSettleUILib/CCTokenEntryDialog.ui deleted file mode 100644 index 0441c0529..000000000 --- a/BlockSettleUILib/CCTokenEntryDialog.ui +++ /dev/null @@ -1,548 +0,0 @@ - - - - CCTokenEntryDialog - - - - 0 - 0 - 417 - 175 - - - - - 0 - 0 - - - - Equity Token Issuance - - - false - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 13 - 40 - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - TOKEN ENTRY - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - 0 - 30 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 80 - 0 - - - - - 80 - 16777215 - - - - Equity token - - - true - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Enter token you received from BlockSettle - - - true - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - - - - - - - - - - - 0 - 0 - - - - true - - - - 10 - - - 20 - - - 5 - - - 20 - - - 5 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 120 - 0 - - - - Ok - - - true - - - true - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 20 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - EQUITY TOKEN ISSUANCE - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 80 - 0 - - - - - 80 - 16777215 - - - - Token - - - true - - - - - - - TextLabel - - - - - - - Product - - - - - - - TextLabel - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - SIGN WITH AUTH EID - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 6 - - - - - 16777215 - 6 - - - - false - - - - - - - - - - Qt::AlignCenter - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - 0 - 0 - - - - true - - - - 10 - - - 20 - - - 5 - - - 20 - - - 5 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 120 - 0 - - - - Cancel - - - true - - - true - - - - - - - - - - - - - - pushButtonOk - - - - diff --git a/BlockSettleUILib/CCWidget.cpp b/BlockSettleUILib/CCWidget.cpp index d014924be..16460d4aa 100644 --- a/BlockSettleUILib/CCWidget.cpp +++ b/BlockSettleUILib/CCWidget.cpp @@ -29,21 +29,11 @@ CCWidget::~CCWidget() = default; void CCWidget::SetPortfolioModel(const std::shared_ptr& model) { - assetManager_ = model->assetManager(); - const auto &walletsManager = model->walletsManager(); - ui_->treeViewCC->setModel(model.get()); ui_->treeViewCC->header()->setSectionResizeMode(QHeaderView::Stretch); connect(model.get(), &CCPortfolioModel::rowsInserted, this, &CCWidget::onRowsInserted); connect(model.get(), &CCPortfolioModel::modelReset, this, [this]() { ui_->treeViewCC->expandAll(); }); - if (assetManager_) { - connect(assetManager_.get(), &AssetManager::totalChanged, this, &CCWidget::updateTotalAssets); - } - if (walletsManager) { - connect(walletsManager.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &CCWidget::updateTotalAssets); - updateTotalAssets(); - } } void CCWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) @@ -80,7 +70,7 @@ void CCWidget::onBalance(const std::string& currency, double balance) void CCWidget::updateTotalAssets() { - auto assets = assetManager_->getTotalAssets(); + int assets = 0; //FIXME if (assets < 0) { ui_->labelTotalValue->setText(tr("%1").arg(tr("Loading..."))); } diff --git a/BlockSettleUILib/CCWidget.h b/BlockSettleUILib/CCWidget.h index f7cbc8cef..d1008a8ef 100644 --- a/BlockSettleUILib/CCWidget.h +++ b/BlockSettleUILib/CCWidget.h @@ -45,7 +45,6 @@ private slots: void updateTotalBalances(); private: - [[deprecated]] std::shared_ptr assetManager_; std::unique_ptr ui_; std::map walletBalance_; std::map fxBalance_; diff --git a/BlockSettleUILib/CelerAccountInfoDialog.cpp b/BlockSettleUILib/CelerAccountInfoDialog.cpp deleted file mode 100644 index 92d3fa4a3..000000000 --- a/BlockSettleUILib/CelerAccountInfoDialog.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "CelerAccountInfoDialog.h" -#include "ui_CelerAccountInfoDialog.h" - -#include "Celer/CelerClient.h" - -CelerAccountInfoDialog::CelerAccountInfoDialog(std::shared_ptr celerConnection, QWidget* parent) - : QDialog(parent) - , ui_(new Ui::CelerAccountInfoDialog()) -{ - ui_->setupUi(this); - ui_->labelEmailAddress->setText(QString::fromStdString(celerConnection->email())); - ui_->labelUserType->setText(celerConnection->userType()); - connect(ui_->buttonBox, &QDialogButtonBox::rejected, this, &CelerAccountInfoDialog::reject); -} - -CelerAccountInfoDialog::~CelerAccountInfoDialog() = default; diff --git a/BlockSettleUILib/CelerAccountInfoDialog.h b/BlockSettleUILib/CelerAccountInfoDialog.h deleted file mode 100644 index 2044d90f1..000000000 --- a/BlockSettleUILib/CelerAccountInfoDialog.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __CELER_ACCOUNT_INFO_DIALOG_H__ -#define __CELER_ACCOUNT_INFO_DIALOG_H__ - -#include -#include - -namespace Ui { - class CelerAccountInfoDialog; -}; -class BaseCelerClient; - -class CelerAccountInfoDialog : public QDialog -{ -Q_OBJECT - -public: - CelerAccountInfoDialog(std::shared_ptr celerConnection, QWidget* parent = nullptr ); - ~CelerAccountInfoDialog() override; - -private: - std::unique_ptr ui_; -}; - -#endif // __CELER_ACCOUNT_INFO_DIALOG_H__ diff --git a/BlockSettleUILib/CelerAccountInfoDialog.ui b/BlockSettleUILib/CelerAccountInfoDialog.ui deleted file mode 100644 index ad7b210f4..000000000 --- a/BlockSettleUILib/CelerAccountInfoDialog.ui +++ /dev/null @@ -1,314 +0,0 @@ - - - - CelerAccountInfoDialog - - - - 0 - 0 - 400 - 300 - - - - Account Information - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - true - - - - 10 - - - 0 - - - 10 - - - 0 - - - 10 - - - - - - 80 - 80 - - - - - 80 - 80 - - - - - - - :/resources/username-white.png - - - false - - - Qt::AlignCenter - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 5 - - - 10 - - - 0 - - - 10 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Username - - - - - - - font-weight: bold; - - - TextLabel - - - true - - - - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 5 - - - 10 - - - 0 - - - 10 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Type - - - - - - - - 75 - true - - - - TextLabel - - - true - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - true - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - - - - - - - diff --git a/BlockSettleUILib/ChartWidget.cpp b/BlockSettleUILib/ChartWidget.cpp deleted file mode 100644 index 2549421f4..000000000 --- a/BlockSettleUILib/ChartWidget.cpp +++ /dev/null @@ -1,1350 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChartWidget.h" - -#include - -#include "ApplicationSettings.h" -#include "Colors.h" -#include "MDCallbacksQt.h" -#include "MarketDataProvider.h" -#include "MdhsClient.h" -#include "PubKeyLoader.h" -#include "market_data_history.pb.h" - -#include "trade_history.pb.h" -#include "ui_ChartWidget.h" - -const QColor BACKGROUND_COLOR = QColor(28, 40, 53); -const QColor FOREGROUND_COLOR = QColor(Qt::white); -const QColor VOLUME_COLOR = QColor(32, 159, 223); - -using namespace Blocksettle::Communication::TradeHistory; - -ComboBoxDelegate::ComboBoxDelegate(QObject* parent) - : QItemDelegate(parent) -{ -} - -void ComboBoxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - if (index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator")) { - painter->setPen(Qt::gray); - painter->drawLine(option.rect.left(), option.rect.center().y(), option.rect.right(), option.rect.center().y()); - } - else if (index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("parent")) { - QStyleOptionViewItem parentOption = option; - parentOption.state |= QStyle::State_Enabled; - QItemDelegate::paint(painter, parentOption, index); - } - else if (index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("child")) { - QStyleOptionViewItem childOption = option; - int indent = option.fontMetrics.width(QString(4, QChar::fromLatin1(' '))); - childOption.rect.adjust(indent, 0, 0, 0); - childOption.textElideMode = Qt::ElideNone; - QItemDelegate::paint(painter, childOption, index); - } - else { - QItemDelegate::paint(painter, option, index); - } -} - -QSize ComboBoxDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - QString type = index.data(Qt::AccessibleDescriptionRole).toString(); - if (type == QLatin1String("separator")) - return QSize(0, 10); - return QItemDelegate::sizeHint(option, index); -} - -ChartWidget::ChartWidget(QWidget* pParent) - : QWidget(pParent) - , ui_(new Ui::ChartWidget) - , candlesticksChart_(nullptr) - , volumeChart_(nullptr) - , volumeAxisRect_(nullptr) - , lastHigh_(0.0) - , lastLow_(0.0) - , lastClose_(0.0) - , currentTimestamp_(0) - , lastInterval_(-1) - , dragY_(0) - , isDraggingYAxis_(false) -{ - ui_->setupUi(this); - horLine = new QCPItemLine(ui_->customPlot); - vertLine = new QCPItemLine(ui_->customPlot); - setAutoScaleBtnColor(); - connect(ui_->autoScaleBtn, &QPushButton::clicked, this, &ChartWidget::OnAutoScaleBtnClick); - - setMouseTracking(true); - connect(ui_->resetBtn, &QPushButton::clicked, this, &ChartWidget::OnResetBtnClick); - // setting up date range radio button group - dateRange_.addButton(ui_->btn1h, Interval::OneHour); - dateRange_.addButton(ui_->btn6h, Interval::SixHours); - dateRange_.addButton(ui_->btn12h, Interval::TwelveHours); - dateRange_.addButton(ui_->btn24h, Interval::TwentyFourHours); - dateRange_.addButton(ui_->btn1w, Interval::OneWeek); - dateRange_.addButton(ui_->btn1m, Interval::OneMonth); - dateRange_.addButton(ui_->btn6m, Interval::SixMonths); - dateRange_.addButton(ui_->btn1y, Interval::OneYear); - connect(&dateRange_, qOverload(&QButtonGroup::buttonClicked), - this, &ChartWidget::OnDateRangeChanged); - - cboModel_ = new QStandardItemModel(this); - ui_->cboInstruments->setItemDelegate(new ComboBoxDelegate); - ui_->cboInstruments->setModel(cboModel_); - - //uncomment when there will we enought data - ui_->btn1y->hide(); - ui_->btn6m->hide(); -} - -void ChartWidget::init(const std::shared_ptr& appSettings - , const std::shared_ptr& mdProvider - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr& connectionManager - , const std::shared_ptr& logger) -{ - auto env = static_cast( - appSettings->get(ApplicationSettings::envConfiguration)); - const auto &mdhsHost = PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Mdhs, env); - const auto &mdhsPort = PubKeyLoader::serverHttpsPort(); - - appSettings_ = appSettings; - mdProvider_ = mdProvider; - mdhsClient_ = std::make_shared(connectionManager, logger, mdhsHost, mdhsPort); - logger_ = logger; - - connect(mdhsClient_.get(), &MdhsClient::DataReceived, this, &ChartWidget::OnDataReceived); - - connect(ui_->pushButtonMDConnection, &QPushButton::clicked, this, &ChartWidget::ChangeMDSubscriptionState); - - connect(ui_->cboInstruments, &QComboBox::currentTextChanged, this, &ChartWidget::OnInstrumentChanged); - ui_->cboInstruments->setEnabled(false); - - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, this, &ChartWidget::OnMdUpdated); - connect(mdCallbacks.get(), &MDCallbacksQt::OnNewFXTrade, this, &ChartWidget::OnNewXBTorFXTrade); - connect(mdCallbacks.get(), &MDCallbacksQt::OnNewPMTrade, this, &ChartWidget::OnNewPMTrade); - connect(mdCallbacks.get(), &MDCallbacksQt::OnNewXBTTrade, this, &ChartWidget::OnNewXBTorFXTrade); - connect(mdCallbacks.get(), &MDCallbacksQt::WaitingForConnectionDetails, this, &ChartWidget::OnLoadingNetworkSettings); - connect(mdCallbacks.get(), &MDCallbacksQt::StartConnecting, this, &ChartWidget::OnMDConnecting); - connect(mdCallbacks.get(), &MDCallbacksQt::Connected, this, &ChartWidget::OnMDConnected); - connect(mdCallbacks.get(), &MDCallbacksQt::Disconnecting, this, &ChartWidget::OnMDDisconnecting); - connect(mdCallbacks.get(), &MDCallbacksQt::Disconnected, this, &ChartWidget::OnMDDisconnected); - - // initialize charts - InitializeCustomPlot(); - - auto timeframe = appSettings_->get(ApplicationSettings::ChartTimeframe).toInt(); - const auto btns = dateRange_.buttons(); - for (auto it : btns) { - if (dateRange_.id(it) == timeframe) { - it->setChecked(true); - break; - } - } -} - -void ChartWidget::setAuthorized(bool authorized) -{ - ui_->pushButtonMDConnection->setEnabled(!authorized); - authorized_ = authorized; -} - -void ChartWidget::disconnect() -{ - OnMDDisconnecting(); -} - -ChartWidget::~ChartWidget() -{ - delete ui_; -} - -void ChartWidget::SendEoDRequest() -{ - OhlcRequest ohlcRequest; - ohlcRequest.set_product(getCurrentProductName().toStdString()); - ohlcRequest.set_interval(static_cast(dateRange_.checkedId())); - ohlcRequest.set_count(1); - ohlcRequest.set_lesser_than(-1); - - MarketDataHistoryRequest request; - request.set_request_type(MarketDataHistoryMessageType::EoDPriceType); - request.set_request(ohlcRequest.SerializeAsString()); - mdhsClient_->SendRequest(request); - eodRequestSent_ = true; -} - -// Populate combo box with existing instruments comeing from mdProvider -void ChartWidget::OnMdUpdated(bs::network::Asset::Type assetType, const QString& security, - bs::network::MDFields mdFields) -{ - if (bs::network::Asset::isFuturesType(assetType)) { - // ignore futures prices updates - return; - } - - if ((assetType == bs::network::Asset::Undefined) && security.isEmpty()) // Celer disconnected - { - isProductListInitialized_ = false; - cboModel_->clear(); - ui_->cboInstruments->setEnabled(false); - return; - } - if (!isProductListInitialized_) { - isProductListInitialized_ = true; - MarketDataHistoryRequest request; - request.set_request_type(MarketDataHistoryMessageType::ProductsListType); - mdhsClient_->SendRequest(request); - return; - } - - if (assetType == bs::network::Asset::Type::PrivateMarket) { - const std::string& productName = security.toStdString(); - - if (pmProducts_.find(productName) == pmProducts_.end()) { - pmProducts_.emplace(productName); - QTimer::singleShot(5000, [this]() - { - MarketDataHistoryRequest request; - request.set_request_type(MarketDataHistoryMessageType::ProductsListType); - mdhsClient_->SendRequest(request); - }); - } - } - - for (const auto& field : mdFields) { - if (field.type == bs::network::MDField::MDTimestamp) { - if (currentTimestamp_ >= field.value) { - break; - } - - currentTimestamp_ = field.value; - CheckToAddNewCandle(currentTimestamp_); - auto date = QDateTime::fromMSecsSinceEpoch(currentTimestamp_, Qt::TimeSpec::UTC).time(); - if (!eodUpdated_ - && !eodRequestSent_ - && date.hour() == 0 - && date.minute() == 0 - && date.second() > 5 - ) { - SendEoDRequest(); - QTimer::singleShot(5000, [this]() - { - if (!eodUpdated_) { - SendEoDRequest(); - } - }); - } - if (date.hour() != 0) { - eodUpdated_ = false; - eodRequestSent_ = false; - } - - break; - } - } -} - -void ChartWidget::UpdateChart(const int& interval) -{ - eodUpdated_ = false; - eodRequestSent_ = false; - auto product = getCurrentProductName(); - if (product.isEmpty()) - return; - if (!candlesticksChart_ || !volumeChart_) { - return; - } - candlesticksChart_->data()->clear(); - volumeChart_->data()->clear(); - qreal width = 0.8 * IntervalWidth(interval) / 1000; - candlesticksChart_->setWidth(width); - volumeChart_->setWidth(width); - OhlcRequest ohlcRequest; - ohlcRequest.set_product(product.toStdString()); - ohlcRequest.set_interval(static_cast(interval)); - ohlcRequest.set_count(requestLimit); - ohlcRequest.set_lesser_than(-1); - - MarketDataHistoryRequest request; - request.set_request_type(MarketDataHistoryMessageType::OhlcHistoryType); - request.set_request(ohlcRequest.SerializeAsString()); - mdhsClient_->SendRequest(request); -} - -void ChartWidget::OnDataReceived(const std::string& data) -{ - if (data.empty()) { - logger_->error("Empty data received from mdhs."); - return; - } - - MarketDataHistoryResponse response; - if (!response.ParseFromString(data)) { - logger_->error("can't parse response from mdhs: {}", data); - return; - } - - switch (response.response_type()) { - case MarketDataHistoryMessageType::ProductsListType: - ProcessProductsListResponse(response.response()); - break; - case MarketDataHistoryMessageType::OhlcHistoryType: - ProcessOhlcHistoryResponse(response.response()); - break; - case MarketDataHistoryMessageType::EoDPriceType: - { - ProcessEodResponse(response.response()); - } - break; - default: - logger_->error("[ApiServerConnectionListener::OnDataReceived] undefined message type"); - break; - } -} - -void ChartWidget::ProcessProductsListResponse(const std::string& data) -{ - if (data.empty()) { - logger_->error("Empty data received from mdhs."); - return; - } - - ProductsListResponse response; - if (!response.ParseFromString(data)) { - logger_->error("can't parse response from mdhs: {}", data); - return; - } - - cboModel_->clear(); - - std::map, std::greater<>> tempMap; - for (const auto& product : response.products()) { - tempMap[product.type()].push_back(product.product()); - productTypesMapper[product.product()] = product.type(); - - if (product.type() == TradeHistoryTradeType::PMTradeType) { - pmProducts_.emplace(product.product()); - } - } - for (const auto& mapElement : tempMap) { - AddParentItem(cboModel_, ProductTypeToString(mapElement.first)); - for (const auto& name : mapElement.second) { - AddChildItem(cboModel_, QString::fromStdString(name)); - } - } - - auto savedProduct = appSettings_->get(ApplicationSettings::ChartProduct).toString(); - bool found = false; - for (int i = 0; i < ui_->cboInstruments->count(); i++) { - if (ui_->cboInstruments->itemText(i).contains(savedProduct)) { - ui_->cboInstruments->setCurrentIndex(i); - found = true; - break; - } - } - if (!found) { - ui_->cboInstruments->setCurrentIndex(1); //to prevent automatic selection of parent item - } - - zoomDiff_ = 0.0; - - ui_->cboInstruments->setEnabled(true); -} - -void ChartWidget::ProcessOhlcHistoryResponse(const std::string& data) -{ - if (data.empty()) { - logger_->error("Empty data received from mdhs."); - return; - } - if (!candlesticksChart_ || !volumeChart_) { - return; - } - - OhlcResponse response; - if (!response.ParseFromString(data)) { - logger_->error("can't parse response from mdhs: {}", data); - return; - } - - bool firstPortion = candlesticksChart_->data()->size() == 0; - - auto product = getCurrentProductName(); - auto interval = dateRange_.checkedId(); - - if (product != QString::fromStdString(response.product()) || interval != response.interval()) - return; - - quint64 maxTimestamp = 0; - - for (int i = 0; i < response.candles_size(); i++) { - auto candle = response.candles(i); - maxTimestamp = qMax(maxTimestamp, static_cast(candle.timestamp())); - - bool isLast = (i == 0); - if (candle.timestamp() >= lastCandle_.timestamp() - || lastCandle_.timestamp() - candle.timestamp() < IntervalWidth( interval, 1, QDateTime::fromMSecsSinceEpoch(candle.timestamp(), Qt::TimeSpec::UTC))) { - if (lastCandle_.timestamp() != 0) { - logger_->error("Invalid distance between candles from mdhs. The last timestamp: {} new timestamp: {}", - lastCandle_.timestamp(), candle.timestamp()); - } - } - else { - if (lastCandle_.timestamp() - candle.timestamp() != IntervalWidth( - interval, 1, QDateTime::fromMSecsSinceEpoch(candle.timestamp(), Qt::TimeSpec::UTC)) && candlesticksChart_->data()->size()) { - for (int j = 0; j < (lastCandle_.timestamp() - candle.timestamp()) / IntervalWidth( - interval, 1, QDateTime::fromMSecsSinceEpoch(candle.timestamp(), Qt::TimeSpec::UTC)) - 1; j++) { - AddDataPoint(candle.close(), candle.close(), candle.close(), candle.close(), - lastCandle_.timestamp() - IntervalWidth(interval) * (j + 1), 0); - } - } - } - - lastCandle_ = candle; - - AddDataPoint(candle.open(), candle.high(), candle.low(), candle.close(), candle.timestamp(), candle.volume()); -#if 0 - qDebug("Added: %s, open: %f, high: %f, low: %f, close: %f, volume: %f" - , QDateTime::fromMSecsSinceEpoch(candle.timestamp(), Qt::TimeSpec::UTC) - .toUTC().toString(Qt::ISODateWithMs).toStdString().c_str() - , candle.open() - , candle.high() - , candle.low() - , candle.close() - , candle.volume()); -#endif - if (firstPortion && isLast) { - lastHigh_ = candle.high(); - lastLow_ = candle.low(); - lastClose_ = candle.close(); - } - } - - if (firstPortion) { - if (!qFuzzyIsNull(currentTimestamp_)) { - newestCandleTimestamp_ = GetCandleTimestamp(currentTimestamp_, static_cast(interval)); - } - else { - logger_->warn("Data from mdhs came before MD update, or MD send wrong current timestamp"); - newestCandleTimestamp_ = GetCandleTimestamp(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(), - static_cast(interval)); - } - if (!response.candles_size()) { - AddDataPoint(0, 0, 0, 0, newestCandleTimestamp_, 0); - maxTimestamp = newestCandleTimestamp_; - } - else { - if (newestCandleTimestamp_ > maxTimestamp) { - auto lastCandle = *(candlesticksChart_->data()->at(candlesticksChart_->data()->size() - 1)); - for (quint64 i = 0; i < (newestCandleTimestamp_ - maxTimestamp) / IntervalWidth(interval); i++) { - AddDataPoint(lastCandle.close, lastCandle.close, lastCandle.close, lastCandle.close, - newestCandleTimestamp_ - IntervalWidth(interval) * i, 0); - } - maxTimestamp = newestCandleTimestamp_; - } - } - firstTimestampInDb_ = response.first_stamp_in_db() / 1000; - UpdatePlot(interval, maxTimestamp); - } - else { - LoadAdditionalPoints(volumeAxisRect_->axis(QCPAxis::atBottom)->range()); - rescalePlot(); - ui_->customPlot->replot(); - } -} - -void ChartWidget::ProcessEodResponse(const std::string& data) -{ - if (!candlesticksChart_ || !volumeChart_) { - return; - } - eodRequestSent_ = false; - EodPrice eodPrice; - eodPrice.ParseFromString(data); - if (getCurrentProductName().toStdString() != eodPrice.product()) { - return; - } - if (candlesticksChart_->data()->size() < 2) { - return; - } - auto delta = dateRange_.checkedId() >= Interval::TwentyFourHours ? 2 : 1; //should we update last or pre-last candle - auto lastCandle = candlesticksChart_->data()->end() - delta; - lastCandle->high = qMax(lastCandle->high, eodPrice.price()); - lastCandle->low = qMin(lastCandle->low, eodPrice.price()); - if (!qFuzzyCompare(lastCandle->close, eodPrice.price())) { - lastCandle->close = eodPrice.price(); - UpdateOHLCInfo(IntervalWidth(dateRange_.checkedId()) / 1000, - ui_->customPlot->xAxis->pixelToCoord(ui_->customPlot->mapFromGlobal(QCursor::pos()).x())); - rescalePlot(); - ui_->customPlot->replot(); - } - eodUpdated_ = true; -} - -double ChartWidget::CountOffsetFromRightBorder() -{ - return ui_->customPlot->xAxis->pixelToCoord(6) - ui_->customPlot->xAxis->pixelToCoord(0); -} - -void ChartWidget::CheckToAddNewCandle(qint64 stamp) -{ - if (!candlesticksChart_ || !volumeChart_) { - return; - } - if (stamp <= newestCandleTimestamp_ + IntervalWidth(dateRange_.checkedId()) || !volumeChart_->data()->size()) { - return; - } - auto candleStamp = GetCandleTimestamp(stamp, static_cast(dateRange_.checkedId())); - auto lastCandle = *(candlesticksChart_->data()->at(candlesticksChart_->data()->size() - 1)); - for (quint64 i = 0; i < (candleStamp - newestCandleTimestamp_) / IntervalWidth(dateRange_.checkedId()); i++) { - AddDataPoint(lastCandle.close, lastCandle.close, lastCandle.close, lastCandle.close, - candleStamp - IntervalWidth(dateRange_.checkedId()) * i, 0); - } - newestCandleTimestamp_ = candleStamp; - auto upper = ui_->customPlot->xAxis->range().upper; - if (upper + IntervalWidth(dateRange_.checkedId()) / 1000 > newestCandleTimestamp_ / 1000) { - ui_->customPlot->xAxis->moveRange(IntervalWidth(dateRange_.checkedId()) / 1000); - } - AddDataPoint(lastClose_, lastClose_, lastClose_, lastClose_, newestCandleTimestamp_, 0); - ui_->customPlot->replot(); -} - -void ChartWidget::setAutoScaleBtnColor() const -{ - QString color = QStringLiteral("background-color: transparent; border: none; color: %1"). - arg(autoScaling_ ? QStringLiteral("rgb(36,124,172)") : QStringLiteral("rgb(255, 255, 255)")); - ui_->autoScaleBtn->setStyleSheet(color); -} - -void ChartWidget::DrawCrossfire(QMouseEvent* event) -{ - vertLine->start->setCoords(qMin(event->pos().x(), volumeAxisRect_->right() + 1), 0); - vertLine->end->setCoords(qMin(event->pos().x(), volumeAxisRect_->right() + 1), volumeAxisRect_->bottom()); - horLine->start->setCoords(0, qMin(event->pos().y(), volumeAxisRect_->bottom())); - horLine->end->setCoords(volumeAxisRect_->right(), qMin(event->pos().y(), volumeAxisRect_->bottom())); - vertLine->setVisible(true); - horLine->setVisible(true); -} - -void ChartWidget::UpdatePrintFlag() -{ - if (!candlesticksChart_ || !volumeChart_) { - return; - } - if (candlesticksChart_->data()->isEmpty()) { - lastPrintFlag_->setVisible(false); - return; - } - lastPrintFlag_->setVisible(true); - if (isHigh_) { - lastPrintFlag_->setBrush(QBrush(c_greenColor)); - } else { - lastPrintFlag_->setBrush(QBrush(c_redColor)); - } - auto prec = FractionSizeForProduct(productTypesMapper[getCurrentProductName().toStdString()]); - lastPrintFlag_->setText(QStringLiteral("- ") + QString::number(lastClose_, 'f', prec)); - lastPrintFlag_->position->setCoords(ui_->customPlot->yAxis2->axisRect()->rect().right() + 2, ui_->customPlot->yAxis2->coordToPixel(lastClose_)); - ui_->customPlot->replot(); -} - -void ChartWidget::UpdatePlot(const int& interval, const qint64& timestamp) -{ - qreal upper = timestamp / 1000 + IntervalWidth(interval) / 1000 / 2; - qreal lower = upper - - IntervalWidth(dateRange_.checkedId(), appSettings_->get(ApplicationSettings::ChartCandleCount).toInt()) / 1000 - - IntervalWidth(interval) / 1000 / 2; - ui_->customPlot->xAxis->setRange(lower, upper); - auto margin = IntervalWidth(dateRange_.checkedId()) / 1000 * 0.5; - ui_->customPlot->xAxis->setRange(lower - margin, upper + margin); - rescaleCandlesYAxis(); - ui_->customPlot->yAxis2->setNumberPrecision( - FractionSizeForProduct(productTypesMapper[getCurrentProductName().toStdString()])); - ui_->customPlot->replot(); - UpdatePrintFlag(); -} - -bool ChartWidget::needLoadNewData(const QCPRange& range, const QSharedPointer data) const -{ - return data->size() && - (range.lower - data->constBegin()->key < IntervalWidth(dateRange_.checkedId()) / 1000 * loadDistance) - && firstTimestampInDb_ + IntervalWidth(OneHour) < data->constBegin()->key; -} - -void ChartWidget::LoadAdditionalPoints(const QCPRange& range) -{ - if (!candlesticksChart_ || !volumeChart_) { - return; - } - const auto data = candlesticksChart_->data(); - if (needLoadNewData(range, data)) { - if (qFuzzyCompare(prevRequestStamp, data->constBegin()->key)) { - return; - } - OhlcRequest ohlcRequest; - auto product = getCurrentProductName(); - ohlcRequest.set_product(product.toStdString()); - ohlcRequest.set_interval(static_cast(dateRange_.checkedId())); - ohlcRequest.set_count(requestLimit); - ohlcRequest.set_lesser_than(data->constBegin()->key * 1000); - - prevRequestStamp = data->constBegin()->key; - - MarketDataHistoryRequest request; - request.set_request_type(MarketDataHistoryMessageType::OhlcHistoryType); - request.set_request(ohlcRequest.SerializeAsString()); - mdhsClient_->SendRequest(request); - } -} - -void ChartWidget::pickTicketDateFormat(const QCPRange& range) const -{ - const float rangeCoeff = 0.8; - if (range.size() < 3 * 24 * 60 * 60 * rangeCoeff) { - dateTimeTicker->setDateTimeFormat(QStringLiteral("dd MMM\nHH:mm")); - } - else if (range.size() < 365 * 24 * 60 * 60 * rangeCoeff) { - dateTimeTicker->setDateTimeFormat(QStringLiteral("dd MMM\n")); - } - else { - dateTimeTicker->setDateTimeFormat(QStringLiteral("MMM yyyy\n")); - } -} - -QString ChartWidget::getCurrentProductName() const -{ - return ui_->cboInstruments->currentText().simplified().replace(QStringLiteral(" "), QStringLiteral("")); -} - -void ChartWidget::AddParentItem(QStandardItemModel* model, const QString& text) -{ - QStandardItem* item = new QStandardItem(text); - item->setFlags(item->flags() & ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable)); - item->setData(QStringLiteral("parent"), Qt::AccessibleDescriptionRole); - QFont font = item->font(); - font.setBold(true); - item->setFont(font); - model->appendRow(item); -} - -void ChartWidget::AddChildItem(QStandardItemModel* model, const QString& text) -{ - QStandardItem* item = new QStandardItem(text + QString(4, QChar::fromLatin1(' '))); - item->setData(QStringLiteral("child"), Qt::AccessibleDescriptionRole); - model->appendRow(item); -} - -void ChartWidget::AddDataPoint(const qreal& open, const qreal& high, const qreal& low, const qreal& close, - const qreal& timestamp, const qreal& volume) const -{ - if (candlesticksChart_) { - candlesticksChart_->data()->add(QCPFinancialData(timestamp / 1000, open, high, low, close)); - } - if (volumeChart_) { - volumeChart_->data()->add(QCPBarsData(timestamp / 1000, volume)); - } -} - -quint64 ChartWidget::IntervalWidth(int interval, int count, const QDateTime& specialDate) const -{ - if (interval == -1) { - return 1; - } - qreal hour = 3600000; - switch (static_cast(interval)) { - case Interval::OneYear: - return hour * (specialDate.isValid() ? specialDate.date().daysInYear() * 24 : 8760) * count; - case Interval::SixMonths: - return hour * (specialDate.isValid() ? 24 * specialDate.date().daysInMonth() * 6 : 4320) * count; - case Interval::OneMonth: - return hour * (specialDate.isValid() ? 24 * specialDate.date().daysInMonth() : 720) * count; - case Interval::OneWeek: - return hour * 168 * count; - case Interval::TwentyFourHours: - return hour * 24 * count; - case Interval::TwelveHours: - return hour * 12 * count; - case Interval::SixHours: - return hour * 6 * count; - case Interval::OneHour: - return hour * count; - default: - return hour * count; - } -} - -int ChartWidget::FractionSizeForProduct(TradeHistoryTradeType type) -{ - switch (type) { - case FXTradeType: - return 4; - case XBTTradeType: - return 2; - case PMTradeType: - return 6; - default: - return -1; - } -} - -// Handles changes of date range. -void ChartWidget::OnDateRangeChanged(int interval) -{ - if (lastInterval_ != interval) { - appSettings_->set(ApplicationSettings::ChartTimeframe, interval); - lastInterval_ = interval; - zoomDiff_ = 0.0; - UpdateChart(interval); - } -} - -void ChartWidget::OnInstrumentChanged(const QString& text) -{ - if (text != getCurrentProductName()) { - appSettings_->set(ApplicationSettings::ChartProduct, getCurrentProductName()); - zoomDiff_ = volumeAxisRect_->axis(QCPAxis::atBottom)->range().size(); - isHigh_ = true; - UpdateChart(dateRange_.checkedId()); - } -} - -QString ChartWidget::GetFormattedStamp(double timestamp) -{ - QString resultFormat; - switch (static_cast(dateRange_.checkedId())) { - case TwelveHours: - case SixHours: - case OneHour: - resultFormat = QStringLiteral("dd MMM yy hh:mm"); - break; - default: - resultFormat = QStringLiteral("dd MMM yy"); - } - return QDateTime::fromSecsSinceEpoch(qint64(timestamp)).toUTC().toString(resultFormat); -} - -void ChartWidget::UpdateOHLCInfo(double width, double timestamp) -{ - if (!candlesticksChart_ || !volumeChart_) { - return; - } - auto ohlcValue = *candlesticksChart_->data()->findBegin(timestamp + width / 2); - auto volumeValue = *volumeChart_->data()->findBegin(timestamp + width / 2); - //ohlcValue.close >= ohlcValue.open ? c_greenColor : c_redColor - const auto& color = VOLUME_COLOR.name(); - auto prec = FractionSizeForProduct(productTypesMapper[getCurrentProductName().toStdString()]); - QString partForm = QStringLiteral("%1"); - QString format = QStringLiteral( -"  %1     %2 %3   %4 %5   %6 %7   %8 %9   %10 %11" -) - .arg(partForm.arg(GetFormattedStamp(ohlcValue.key)).arg(FOREGROUND_COLOR.name())) - .arg(partForm.arg(QStringLiteral("O:")).arg(FOREGROUND_COLOR.name())) - .arg(partForm.arg(ohlcValue.open, 0, 'f', prec).arg(color)) - .arg(partForm.arg(QStringLiteral("H:")).arg(FOREGROUND_COLOR.name())) - .arg(partForm.arg(ohlcValue.high, 0, 'f', prec).arg(color)) - .arg(partForm.arg(QStringLiteral("L:")).arg(FOREGROUND_COLOR.name())) - .arg(partForm.arg(ohlcValue.low, 0, 'f', prec).arg(color)) - .arg(partForm.arg(QStringLiteral("C:")).arg(FOREGROUND_COLOR.name())) - .arg(partForm.arg(ohlcValue.close, 0, 'f', prec).arg(color)) - .arg(partForm.arg(QStringLiteral("Volume:")).arg(FOREGROUND_COLOR.name())) - .arg(partForm.arg(volumeValue.value).arg(color)); - ui_->ohlcLbl->setText(format); -} - -void ChartWidget::OnPlotMouseMove(QMouseEvent* event) -{ - DrawCrossfire(event); - - double x = event->localPos().x(); - double width = IntervalWidth(dateRange_.checkedId()) / 1000; - double timestamp = ui_->customPlot->xAxis->pixelToCoord(x); - if (!candlesticksChart_->data()->size() || - timestamp > candlesticksChart_->data()->at(candlesticksChart_->data()->size() - 1)->key + width / 2 || - timestamp < candlesticksChart_->data()->at(0)->key - width / 2) { - ui_->ohlcLbl->setText({}); - } - else { - UpdateOHLCInfo(width, timestamp); - } - - if (isDraggingYAxis_) { - auto rightAxis = ui_->customPlot->yAxis2; - auto currentYPos = event->pos().y(); - auto lower_bound = rightAxis->range().lower; - auto upper_bound = rightAxis->range().upper; - auto diff = upper_bound - lower_bound; - auto directionCoeff = (currentYPos - lastDragCoord_.y() > 0) ? -1 : 1; - lastDragCoord_.setY(currentYPos); - double tempCoeff = 28.0; //change this to impact on xAxis scale speed, the lower coeff the faster scaling - upper_bound -= diff / tempCoeff * directionCoeff; - lower_bound += diff / tempCoeff * directionCoeff; - rightAxis->setRange(lower_bound, upper_bound); - } - if (isDraggingXAxis_) { - auto bottomAxis = volumeAxisRect_->axis(QCPAxis::atBottom); - auto currentXPos = event->pos().x(); - auto lower_bound = volumeAxisRect_->axis(QCPAxis::atBottom)->range().lower; - auto upper_bound = volumeAxisRect_->axis(QCPAxis::atBottom)->range().upper; - auto diff = upper_bound - lower_bound; - auto directionCoeff = (currentXPos - lastDragCoord_.x() > 0) ? -1 : 1; - //double scalingCoeff = qAbs(currentXPos - startDragCoordX_) / ui_->customPlot->size().width(); - lastDragCoord_.setX(currentXPos); - double tempCoeff = 10.0; //change this to impact on xAxis scale speed, the lower coeff the faster scaling - lower_bound += diff / tempCoeff * /*scalingCoeff * */ directionCoeff; - auto lower_limit = candlesticksChart_->data()->constBegin()->key - (upper_bound - lower_bound) * 0.2; - if (lower_bound < lower_limit && directionCoeff == -1) { - return; - } - bottomAxis->setRange(lower_bound, upper_bound); - } - if (isDraggingMainPlot_) { - auto axis = ui_->customPlot->xAxis; - const double startPixel = dragStartPos_.x(); - const double currentPixel = event->pos().x(); - const double diff = axis->pixelToCoord(startPixel) - axis->pixelToCoord(currentPixel); - auto size = candlesticksChart_->data()->size(); - double upper_bound = size ? candlesticksChart_->data()->at(size - 1)->key : QDateTime::currentSecsSinceEpoch(); - upper_bound += IntervalWidth(dateRange_.checkedId()) / 1000 / 2 + CountOffsetFromRightBorder(); - double lower_bound = QDateTime(QDate(2009, 1, 3)).toSecsSinceEpoch(); - if (dragStartRangeX_.upper + diff > upper_bound && diff > 0) { - dragStartPos_.setX(event->pos().x()); - dragStartRangeX_ = axis->range(); - } - else if (dragStartRangeX_.lower + diff < lower_bound && diff < 0) { - dragStartPos_.setX(event->pos().x()); - dragStartRangeX_ = axis->range(); - } - else { - axis->setRange(dragStartRangeX_.lower + diff, dragStartRangeX_.upper + diff); - } - if (!autoScaling_) { - auto axisY = ui_->customPlot->yAxis2; - const double startPixelY = dragStartPos_.y(); - const double currentPixelY = event->pos().y(); - const double diffY = axisY->pixelToCoord(startPixelY) - axisY->pixelToCoord(currentPixelY); - axisY->setRange(dragStartRangeY_.lower + diffY, dragStartRangeY_.upper + diffY); - } - - } - ui_->customPlot->replot(); -} - -void ChartWidget::leaveEvent(QEvent* event) -{ - vertLine->setVisible(false); - horLine->setVisible(false); - ui_->customPlot->replot(); -} - -void ChartWidget::rescaleCandlesYAxis() -{ - bool foundRange = false; - auto keyRange = candlesticksChart_->keyAxis()->range(); - keyRange.upper += IntervalWidth(dateRange_.checkedId()) / 1000 / 2; - keyRange.lower -= IntervalWidth(dateRange_.checkedId()) / 1000 / 2; - auto newRange = candlesticksChart_->getValueRange(foundRange, QCP::sdBoth, keyRange); - if (foundRange) { - const double margin = 0.15; - if (!QCPRange::validRange(newRange)) // likely due to range being zero - { - double center = (newRange.lower + newRange.upper) * 0.5; - // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason - newRange.lower = center - candlesticksChart_->valueAxis()->range().size() * margin / 2.0; - newRange.upper = center + candlesticksChart_->valueAxis()->range().size() * margin / 2.0; - } - else { - auto old = candlesticksChart_->valueAxis()->range(); - if (old != newRange) { - newRange.lower -= newRange.size() * margin; - newRange.upper += newRange.size() * margin; - } - } - candlesticksChart_->valueAxis()->setRange(newRange); - } -} - -void ChartWidget::rescaleVolumesYAxis() const -{ - if (!volumeChart_->data()->size()) { - return; - } - auto lower_bound = volumeAxisRect_->axis(QCPAxis::atBottom)->range().lower; - auto upper_bound = volumeAxisRect_->axis(QCPAxis::atBottom)->range().upper; - double maxVolume = volumeChart_->data()->constBegin()->value; - for (const auto& it : *volumeChart_->data()) { - if (it.key >= lower_bound && it.key <= upper_bound) { - maxVolume = qMax(maxVolume, it.value); - } - } - if (!qFuzzyCompare(maxVolume, volumeAxisRect_->axis(QCPAxis::atBottom)->range().upper)) { - volumeAxisRect_->axis(QCPAxis::atRight)->setRange(0, maxVolume); - ui_->customPlot->replot(); - } -} - -void ChartWidget::rescalePlot() -{ - if (autoScaling_) { - rescaleCandlesYAxis(); - } - rescaleVolumesYAxis(); -} - -void ChartWidget::OnMousePressed(QMouseEvent* event) -{ - auto select = ui_->customPlot->yAxis2->selectTest(event->pos(), false); - isDraggingYAxis_ = select != -1.0; - if (isDraggingYAxis_) { - if (autoScaling_) { - ui_->autoScaleBtn->animateClick(); - } - } - - auto selectXPoint = volumeAxisRect_->axis(QCPAxis::atBottom)->selectTest(event->pos(), false); - isDraggingXAxis_ = selectXPoint != -1.0; - if (isDraggingXAxis_) { - volumeAxisRect_->axis(QCPAxis::atBottom)->axisRect()->setRangeDrag( - volumeAxisRect_->axis(QCPAxis::atBottom)->orientation()); - startDragCoordX_ = event->pos().x(); - } - - if (ui_->customPlot->axisRect()->rect().contains(event->pos()) || volumeAxisRect_->rect().contains(event->pos())) { - dragStartRangeX_ = ui_->customPlot->xAxis->range(); - dragStartRangeY_ = ui_->customPlot->yAxis2->range(); - dragStartPos_ = event->pos(); - isDraggingMainPlot_ = true; - } - - if (isDraggingXAxis_ || isDraggingYAxis_) { - lastDragCoord_ = event->pos(); - isDraggingMainPlot_ = false; - } -} - -void ChartWidget::OnMouseReleased(QMouseEvent* event) -{ - isDraggingYAxis_ = false; - isDraggingXAxis_ = false; - isDraggingMainPlot_ = false; - //ui_->customPlot->setInteraction(QCP::iRangeDrag, true); -} - -void ChartWidget::OnWheelScroll(QWheelEvent* event) -{ - auto bottomAxis = volumeAxisRect_->axis(QCPAxis::atBottom); - auto lower_bound = volumeAxisRect_->axis(QCPAxis::atBottom)->range().lower; - auto upper_bound = volumeAxisRect_->axis(QCPAxis::atBottom)->range().upper; - auto diff = upper_bound - lower_bound; - auto directionCoeff = event->angleDelta().y() < 0 ? -1 : 1; - double tempCoeff = 120.0 / qAbs(event->angleDelta().y()) * 10; - //change this to impact on xAxis scale speed, the lower coeff the faster scaling - lower_bound += diff / tempCoeff * directionCoeff; - auto lower_limit = candlesticksChart_->data()->constBegin()->key - (upper_bound - lower_bound) * 0.2 ; - if (lower_bound < lower_limit && directionCoeff == -1) { - return; - } - bottomAxis->setRange(lower_bound, upper_bound); - ui_->customPlot->replot(); -} - -void ChartWidget::OnAutoScaleBtnClick() -{ - autoScaling_ = !autoScaling_; - if (autoScaling_) { - rescalePlot(); - } - setAutoScaleBtnColor(); -} - -void ChartWidget::OnResetBtnClick() -{ - if (candlesticksChart_->data()->size()) { - auto new_upper = candlesticksChart_->data()->at(candlesticksChart_->data()->size() - 1)->key + IntervalWidth( - dateRange_.checkedId()) / 1000 / 2; - QCPRange defaultRange(new_upper - IntervalWidth(dateRange_.checkedId(), requestLimit) / 1000, new_upper); - volumeAxisRect_->axis(QCPAxis::atBottom)->setRange(defaultRange); - volumeAxisRect_->axis(QCPAxis::atBottom)->setRange(defaultRange.lower - CountOffsetFromRightBorder(), - defaultRange.upper + CountOffsetFromRightBorder()); - } - if (!autoScaling_) { - autoScaling_ = true; - rescalePlot(); - setAutoScaleBtnColor(); - } -} - -void ChartWidget::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - QMetaObject::invokeMethod(this, &ChartWidget::UpdatePrintFlag, Qt::QueuedConnection);//UpdatePrintFlag should be called after chart have resized, so we put this method to event loop's queue -} - -quint64 ChartWidget::GetCandleTimestamp(const uint64_t& timestamp, const Interval& interval) const -{ - QDateTime now = QDateTime::fromMSecsSinceEpoch(timestamp).toUTC(); - QDateTime result = now; - switch (interval) { - case Interval::OneYear: - { - result.setTime(QTime(0, 0)); - result.setDate(QDate(now.date().year(), 1, 1)); - break; - } - case Interval::SixMonths: - { - int month = now.date().month(); // 1 - January, 12 - December - int mod = month % 6; - result.setTime(QTime(0, 0)); - result.setDate(QDate(now.date().year(), month - mod + 1, 1)); - break; - } - case Interval::OneMonth: - { - result.setTime(QTime(0, 0)); - result.setDate(QDate(now.date().year(), now.date().month(), 1)); - break; - } - case Interval::OneWeek: - { - auto date = now.date(); - auto start = date.addDays(1 - date.dayOfWeek()); //1 - Monday, 7 - Sunday - result.setTime(QTime(0, 0)); - result.setDate(start); - break; - } - case Interval::TwentyFourHours: - result.setTime(QTime(0, 0)); - break; - case Interval::TwelveHours: - { - int hour = now.time().hour(); - int mod = hour % 12; - result.setTime(QTime(hour - mod, 0)); - break; - } - case Interval::SixHours: - { - int hour = now.time().hour(); - int mod = hour % 6; - result.setTime(QTime(hour - mod, 0)); - break; - } - case Interval::OneHour: - result.setTime(QTime(now.time().hour(), 0)); - break; - default: - break; - } - return result.toMSecsSinceEpoch(); -} - -bool ChartWidget::isBeyondUpperLimit(QCPRange newRange, int interval) -{ - return (newRange.size() > IntervalWidth(interval, candleCountOnScreenLimit) / 1000) && !isDraggingMainPlot_; -} - -bool ChartWidget::isBeyondLowerLimit(QCPRange newRange, int interval) -{ - return newRange.size() < IntervalWidth(interval, candleViewLimit) / 1000; -} - -void ChartWidget::OnVolumeAxisRangeChanged(QCPRange newRange, QCPRange oldRange) -{ - auto interval = dateRange_.checkedId() == -1 ? 0 : dateRange_.checkedId(); - - if (isBeyondUpperLimit(newRange, interval) && oldRange.lower >= 0) { - volumeAxisRect_->axis(QCPAxis::atBottom)->setRange( - oldRange.upper - IntervalWidth(interval, candleCountOnScreenLimit) / 1000, oldRange.upper); - ui_->customPlot->xAxis->setRange(volumeAxisRect_->axis(QCPAxis::atBottom)->range()); - } - else { - if (isBeyondLowerLimit(newRange, interval)) { - volumeAxisRect_->axis(QCPAxis::atBottom)->setRange( - oldRange.upper - IntervalWidth(interval, candleViewLimit) / 1000 - 1.0, oldRange.upper); - ui_->customPlot->xAxis->setRange(volumeAxisRect_->axis(QCPAxis::atBottom)->range()); - } - else { - ui_->customPlot->xAxis->setRange(newRange); - } - } - if (!std::isinf(ui_->customPlot->xAxis->range().size() / (IntervalWidth(dateRange_.checkedId()) / 1000))) { - appSettings_->set(ApplicationSettings::ChartCandleCount, int(ui_->customPlot->xAxis->range().size() / (IntervalWidth(dateRange_.checkedId()) / 1000))); - } - LoadAdditionalPoints(newRange); - pickTicketDateFormat(newRange); - rescalePlot(); -} - -QString ChartWidget::ProductTypeToString(TradeHistoryTradeType type) -{ - switch (type) { - case FXTradeType: - return QStringLiteral("FX"); - case XBTTradeType: - return QStringLiteral("XBT"); - case PMTradeType: - return QStringLiteral("PM"); - case OneDayDeliverableTradeType: - return QString::fromStdString(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures)); - case OneDayCashSettledTradeType: - return QString::fromStdString(bs::network::Asset::toString(bs::network::Asset::CashSettledFutures)); - default: - return QStringLiteral(""); - } -} - -void ChartWidget::SetupCrossfire() -{ - QPen pen(Qt::white, 1, Qt::PenStyle::DashLine); - QVector dashes; - qreal space = 8; - dashes << 4 << space; - pen.setDashPattern(dashes); - - vertLine->setLayer(QStringLiteral("axes")); - horLine->setLayer(QStringLiteral("axes")); - - vertLine->start->setType(QCPItemPosition::ptAbsolute); - vertLine->end->setType(QCPItemPosition::ptAbsolute); - vertLine->setPen(pen); - vertLine->setClipToAxisRect(false); - - horLine->start->setType(QCPItemPosition::ptAbsolute); - horLine->end->setType(QCPItemPosition::ptAbsolute); - horLine->setPen(pen); - horLine->setClipToAxisRect(false); - - if (!ui_->customPlot->rect().contains(mapFromGlobal(QCursor::pos()))) { - vertLine->setVisible(false); - horLine->setVisible(false); - } -} - -void ChartWidget::SetupLastPrintFlag() -{ - lastPrintFlag_ = new QCPItemText(ui_->customPlot); - lastPrintFlag_->setVisible(false); - lastPrintFlag_->setPen(Qt::NoPen); - lastPrintFlag_->setColor(Qt::white); - lastPrintFlag_->setBrush(QBrush(c_greenColor)); - auto font = ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->labelFont(); - lastPrintFlag_->setFont(font); - lastPrintFlag_->position->setType(QCPItemPosition::ptAbsolute); - lastPrintFlag_->position->setAxisRect(ui_->customPlot->yAxis2->axisRect()); - lastPrintFlag_->setPositionAlignment(Qt::AlignLeft | Qt::AlignVCenter); - lastPrintFlag_->setLayer(QStringLiteral("axes")); - lastPrintFlag_->setClipAxisRect(ui_->customPlot->yAxis2->axisRect()); - lastPrintFlag_->setClipToAxisRect(false); -} - -void ChartWidget::InitializeCustomPlot() -{ - SetupCrossfire(); - - QBrush bgBrush(BACKGROUND_COLOR); - ui_->customPlot->setBackground(bgBrush); - - ui_->ohlcLbl->setFont(QFont(QStringLiteral("sans"), 10)); - - // create candlestick chart: - candlesticksChart_ = new QCPFinancial(ui_->customPlot->xAxis, ui_->customPlot->yAxis2); - candlesticksChart_->setName(tr("Candlestick")); - candlesticksChart_->setChartStyle(QCPFinancial::csCandlestick); - candlesticksChart_->setTwoColored(true); - candlesticksChart_->setBrushPositive(c_greenColor); - candlesticksChart_->setBrushNegative(c_redColor); - candlesticksChart_->setPenPositive(QPen(c_greenColor)); - candlesticksChart_->setPenNegative(QPen(c_redColor)); - - ui_->customPlot->axisRect()->axis(QCPAxis::atLeft)->setVisible(false); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setVisible(true); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setBasePen(QPen(FOREGROUND_COLOR)); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setTickPen(QPen(FOREGROUND_COLOR)); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setSubTickPen(QPen(FOREGROUND_COLOR)); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setTickLabelColor(FOREGROUND_COLOR); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setTickLength(0, 8); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setSubTickLength(0, 4); - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->setNumberFormat(QStringLiteral("f")); - ui_->customPlot->axisRect()->axis(QCPAxis::atBottom)->grid()->setPen(Qt::NoPen); - - // create bottom axis rect for volume bar chart: - volumeAxisRect_ = new QCPAxisRect(ui_->customPlot); - ui_->customPlot->plotLayout()->addElement(1, 0, volumeAxisRect_); - volumeAxisRect_->setMaximumSize(QSize(QWIDGETSIZE_MAX, 100)); - volumeAxisRect_->axis(QCPAxis::atBottom)->setLayer(QStringLiteral("axes")); - volumeAxisRect_->axis(QCPAxis::atBottom)->grid()->setLayer(QStringLiteral("grid")); - // bring bottom and main axis rect closer together: - ui_->customPlot->plotLayout()->setRowSpacing(0); - volumeAxisRect_->setAutoMargins(QCP::msLeft | QCP::msRight | QCP::msBottom); - volumeAxisRect_->setMargins(QMargins(0, 0, 0, 0)); - // create two bar plottables, for positive (green) and negative (red) volume bars: - ui_->customPlot->setAutoAddPlottableToLegend(false); - - volumeChart_ = new QCPBars(volumeAxisRect_->axis(QCPAxis::atBottom), volumeAxisRect_->axis(QCPAxis::atRight)); - volumeChart_->setPen(QPen(VOLUME_COLOR)); - volumeChart_->setBrush(VOLUME_COLOR); - - volumeAxisRect_->axis(QCPAxis::atLeft)->setVisible(false); - volumeAxisRect_->axis(QCPAxis::atRight)->setVisible(true); - volumeAxisRect_->axis(QCPAxis::atRight)->setBasePen(QPen(FOREGROUND_COLOR)); - volumeAxisRect_->axis(QCPAxis::atRight)->setTickPen(QPen(FOREGROUND_COLOR)); - volumeAxisRect_->axis(QCPAxis::atRight)->setSubTickPen(QPen(FOREGROUND_COLOR)); - volumeAxisRect_->axis(QCPAxis::atRight)->setTickLabelColor(FOREGROUND_COLOR); - volumeAxisRect_->axis(QCPAxis::atRight)->setTickLength(0, 8); - volumeAxisRect_->axis(QCPAxis::atRight)->setSubTickLength(0, 4); - volumeAxisRect_->axis(QCPAxis::atRight)->ticker()->setTickCount(2); - volumeAxisRect_->axis(QCPAxis::atRight)->setTickLabelFont( - ui_->customPlot->axisRect()->axis(QCPAxis::atRight)->labelFont()); - - volumeAxisRect_->axis(QCPAxis::atBottom)->setBasePen(QPen(FOREGROUND_COLOR)); - volumeAxisRect_->axis(QCPAxis::atBottom)->setTickPen(QPen(FOREGROUND_COLOR)); - volumeAxisRect_->axis(QCPAxis::atBottom)->setSubTickPen(QPen(FOREGROUND_COLOR)); - volumeAxisRect_->axis(QCPAxis::atBottom)->setTickLength(0, 8); - volumeAxisRect_->axis(QCPAxis::atBottom)->setSubTickLength(0, 4); - volumeAxisRect_->axis(QCPAxis::atBottom)->setTickLabelColor(FOREGROUND_COLOR); - volumeAxisRect_->axis(QCPAxis::atBottom)->grid()->setPen(Qt::NoPen); - - // interconnect x axis ranges of main and bottom axis rects: - connect(ui_->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)) - , volumeAxisRect_->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange))); - - connect(volumeAxisRect_->axis(QCPAxis::atBottom), - qOverload(&QCPAxis::rangeChanged), - this, - &ChartWidget::OnVolumeAxisRangeChanged); - - SetupLastPrintFlag(); - - connect(ui_->customPlot->yAxis2, - qOverload(&QCPAxis::rangeChanged), - this, - [this]() {UpdatePrintFlag(); }); - - // configure axes of both main and bottom axis rect: - dateTimeTicker->setDateTimeSpec(Qt::UTC); - dateTimeTicker->setDateTimeFormat(QStringLiteral("dd/MM/yy\nHH:mm")); - dateTimeTicker->setTickCount(17); - volumeAxisRect_->axis(QCPAxis::atBottom)->setTicker(dateTimeTicker); - //volumeAxisRect_->axis(QCPAxis::atBottom)->setTickLabelRotation(10); - volumeAxisRect_->axis(QCPAxis::atBottom)->setTickLabelFont(QFont(QStringLiteral("Arial"), 9)); - ui_->customPlot->xAxis->setBasePen(Qt::NoPen); - ui_->customPlot->xAxis->setTickLabels(false); - ui_->customPlot->xAxis->setTicks(false); - // only want vertical grid in main axis rect, so hide xAxis backbone, ticks, and labels - ui_->customPlot->xAxis->setTicker(dateTimeTicker); - ui_->customPlot->rescaleAxes(); - ui_->customPlot->xAxis->scaleRange(1.025, ui_->customPlot->xAxis->range().center()); - ui_->customPlot->yAxis->scaleRange(1.1, ui_->customPlot->yAxis->range().center()); - - // make axis rects' left side line up: - QCPMarginGroup* group = new QCPMarginGroup(ui_->customPlot); - ui_->customPlot->axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, group); - volumeAxisRect_->setMarginGroup(QCP::msLeft | QCP::msRight, group); - - connect(ui_->customPlot, &QCustomPlot::mouseMove, this, &ChartWidget::OnPlotMouseMove); - connect(ui_->customPlot, &QCustomPlot::mousePress, this, &ChartWidget::OnMousePressed); - connect(ui_->customPlot, &QCustomPlot::mouseRelease, this, &ChartWidget::OnMouseReleased); - connect(ui_->customPlot, &QCustomPlot::mouseWheel, this, &ChartWidget::OnWheelScroll); - volumeAxisRect_->axis(QCPAxis::atRight)->setRange(0, 1000); -} - -void ChartWidget::OnLoadingNetworkSettings() -{ - ui_->pushButtonMDConnection->setText(tr("Connecting")); - ui_->pushButtonMDConnection->setEnabled(false); - ui_->pushButtonMDConnection->setToolTip(tr("Waiting for connection details")); -} - -void ChartWidget::OnMDConnecting() -{ - ui_->pushButtonMDConnection->setText(tr("Connecting")); - ui_->pushButtonMDConnection->setEnabled(false); - ui_->pushButtonMDConnection->setToolTip(QString{}); -} - -void ChartWidget::OnMDConnected() -{ - ui_->pushButtonMDConnection->setText(tr("Disconnect")); - ui_->pushButtonMDConnection->setEnabled(!authorized_); -} - -void ChartWidget::OnMDDisconnecting() -{ - ui_->pushButtonMDConnection->setText(tr("Disconnecting")); - ui_->pushButtonMDConnection->setEnabled(false); - - if (candlesticksChart_ != nullptr) - candlesticksChart_->data()->clear(); - - if (volumeChart_ != nullptr) - volumeChart_->data()->clear(); - - ui_->ohlcLbl->setText({}); - ui_->customPlot->replot(); - - mdProvider_->UnsubscribeFromMD(); - mdProvider_->DisconnectFromMDSource(); -} - -void ChartWidget::OnMDDisconnected() -{ - ui_->pushButtonMDConnection->setText(tr("Subscribe")); - ui_->pushButtonMDConnection->setEnabled(!authorized_); -} - -void ChartWidget::ChangeMDSubscriptionState() -{ - if (mdProvider_->IsConnectionActive()) { - mdProvider_->DisconnectFromMDSource(); - } - else { - mdProvider_->SubscribeToMD(); - } -} - -void ChartWidget::OnNewTrade(const std::string& productName, uint64_t timestamp, double price, double amount) -{ - if (productName != getCurrentProductName().toStdString() || - !candlesticksChart_->data()->size() || - !volumeChart_->data()->size()) { - return; - } - - auto lastVolume = volumeChart_->data()->end() - 1; - lastVolume->value += amount; - auto lastCandle = candlesticksChart_->data()->end() - 1; - lastCandle->high = qMax(lastCandle->high, price); - lastCandle->low = qMin(lastCandle->low, price); - if (!qFuzzyCompare(lastCandle->close, price) || !qFuzzyIsNull(amount)) { - isHigh_ = price > lastClose_; - lastClose_ = price; - UpdatePrintFlag(); - lastCandle->close = price; - UpdateOHLCInfo(IntervalWidth(dateRange_.checkedId()) / 1000, - ui_->customPlot->xAxis->pixelToCoord(ui_->customPlot->mapFromGlobal(QCursor::pos()).x())); - rescalePlot(); - ui_->customPlot->replot(); - } - CheckToAddNewCandle(timestamp); -} - -void ChartWidget::OnNewXBTorFXTrade(const bs::network::NewTrade& trade) -{ - OnNewTrade(trade.product, trade.timestamp, trade.price, trade.amount); -} - -void ChartWidget::OnNewPMTrade(const bs::network::NewPMTrade& trade) -{ - OnNewTrade(trade.product, trade.timestamp, trade.price, trade.amount); -} diff --git a/BlockSettleUILib/ChartWidget.h b/BlockSettleUILib/ChartWidget.h deleted file mode 100644 index ffd1f689b..000000000 --- a/BlockSettleUILib/ChartWidget.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHARTWIDGET_H -#define CHARTWIDGET_H - -#include -#include -#include "CommonTypes.h" -#include "CustomControls/qcustomplot.h" -#include "market_data_history.pb.h" - -QT_BEGIN_NAMESPACE -namespace Ui { class ChartWidget; } -class QStandardItemModel; -class QCPTextElement; -class QCPFinancial; -class QCPBars; -class QCPAxisRect; -QT_END_NAMESPACE - -class ApplicationSettings; -class MarketDataProvider; -class MDCallbacksQt; -class ConnectionManager; -namespace spdlog { class logger; } -class MdhsClient; - -#include -#include - -class ComboBoxDelegate : public QItemDelegate -{ - Q_OBJECT -public: - explicit ComboBoxDelegate(QObject *parent = nullptr); -protected: - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; -}; - -class ChartWidget : public QWidget -{ - Q_OBJECT - -public: - explicit ChartWidget(QWidget* pParent = nullptr); - ~ChartWidget() override; - void SendEoDRequest(); - void init(const std::shared_ptr& - , const std::shared_ptr& - , const std::shared_ptr & - , const std::shared_ptr& - , const std::shared_ptr&); - - void setAuthorized(bool authorized); - void disconnect(); - -protected slots: - void OnDataReceived(const std::string& data); - void OnDateRangeChanged(int id); - void OnMdUpdated(bs::network::Asset::Type, const QString &security, bs::network::MDFields); - void OnInstrumentChanged(const QString &text); - QString GetFormattedStamp(double timestamp); - void UpdateOHLCInfo(double width, double timestamp); - void OnPlotMouseMove(QMouseEvent* event); - void leaveEvent(QEvent* event) override; - void rescaleCandlesYAxis(); - void rescaleVolumesYAxis() const; - void rescalePlot(); - void OnMousePressed(QMouseEvent* event); - void OnMouseReleased(QMouseEvent* event); - void OnWheelScroll(QWheelEvent* event); - void OnAutoScaleBtnClick(); - void OnResetBtnClick(); - void resizeEvent(QResizeEvent* event) override; - bool isBeyondUpperLimit(QCPRange newRange, int interval); - bool isBeyondLowerLimit(QCPRange newRange, int interval); - void OnVolumeAxisRangeChanged(QCPRange newRange, QCPRange oldRange); - static QString ProductTypeToString(Blocksettle::Communication::TradeHistory::TradeHistoryTradeType type); - void SetupCrossfire(); - void SetupLastPrintFlag(); - - void OnLoadingNetworkSettings(); - void OnMDConnecting(); - void OnMDConnected(); - void OnMDDisconnecting(); - void OnMDDisconnected(); - void ChangeMDSubscriptionState(); - - void OnNewTrade(const std::string& productName, uint64_t timestamp, double price, double amount); - void OnNewXBTorFXTrade(const bs::network::NewTrade& trade); - void OnNewPMTrade(const bs::network::NewPMTrade& trade); - -protected: - quint64 GetCandleTimestamp(const uint64_t& timestamp, - const Blocksettle::Communication::MarketDataHistory::Interval& interval) const; - void AddDataPoint(const qreal& open, const qreal& high, const qreal& low, const qreal& close, const qreal& timestamp, const qreal& volume) const; - void UpdateChart(const int& interval); - void InitializeCustomPlot(); - quint64 IntervalWidth(int interval = -1, int count = 1, const QDateTime& specialDate = {}) const; - static int FractionSizeForProduct(Blocksettle::Communication::TradeHistory::TradeHistoryTradeType type); - void ProcessProductsListResponse(const std::string& data); - void ProcessOhlcHistoryResponse(const std::string& data); - void ProcessEodResponse(const std::string& data); - double CountOffsetFromRightBorder(); - - void CheckToAddNewCandle(qint64 stamp); - - void setAutoScaleBtnColor() const; - - void DrawCrossfire(QMouseEvent* event); - - void UpdatePrintFlag(); - - void UpdatePlot(const int& interval, const qint64& timestamp); - - bool needLoadNewData(const QCPRange& range, QSharedPointer data) const; - - void LoadAdditionalPoints(const QCPRange& range); - - void pickTicketDateFormat(const QCPRange& range) const; -private: - QString getCurrentProductName() const; - void AddParentItem(QStandardItemModel * model, const QString& text); - void AddChildItem(QStandardItemModel* model, const QString& text); - -private: - std::shared_ptr appSettings_; - std::shared_ptr mdProvider_; - std::shared_ptr mdhsClient_; - std::shared_ptr logger_; - - bool isProductListInitialized_{ false }; - std::map productTypesMapper; - - QSharedPointer dateTimeTicker{ new QCPAxisTickerDateTime }; - - const int loadDistance{ 15 }; - - bool eodUpdated_{ false }; - bool eodRequestSent_{ false }; - - constexpr static int requestLimit{ 200 }; - constexpr static int candleViewLimit{ 150 }; - constexpr static qint64 candleCountOnScreenLimit{ 1500 }; - - Blocksettle::Communication::MarketDataHistory::OhlcCandle lastCandle_; - - double prevRequestStamp{ 0.0 }; - - double zoomDiff_{ 0.0 }; - - Ui::ChartWidget *ui_; - QButtonGroup dateRange_; - QStandardItemModel *cboModel_; - QCPFinancial *candlesticksChart_; - QCPBars *volumeChart_; - QCPAxisRect *volumeAxisRect_; - - QCPItemText * lastPrintFlag_{ nullptr }; - bool isHigh_ { true }; - - QCPItemLine* horLine; - QCPItemLine* vertLine; - - double lastHigh_; - double lastLow_; - double lastClose_; - double currentTimestamp_; - quint64 newestCandleTimestamp_{}; - - bool autoScaling_{ true }; - - qreal currentMinPrice_{ 0 }; - qreal currentMaxPrice_{ 0 }; - - int lastInterval_; - int dragY_; - - bool isDraggingYAxis_; - bool isDraggingXAxis_{ false }; - bool isDraggingMainPlot_{ false }; - QCPRange dragStartRangeX_; - QCPRange dragStartRangeY_; - QPointF dragStartPos_; - - QPoint lastDragCoord_; - qreal startDragCoordX_{ 0.0 }; - - quint64 firstTimestampInDb_{ 0 }; - bool authorized_{ false }; - - std::set pmProducts_; -}; - -#endif // CHARTWIDGET_H diff --git a/BlockSettleUILib/ChartWidget.ui b/BlockSettleUILib/ChartWidget.ui deleted file mode 100644 index 1f6370f9a..000000000 --- a/BlockSettleUILib/ChartWidget.ui +++ /dev/null @@ -1,291 +0,0 @@ - - - - ChartWidget - - - - 0 - 0 - 1137 - 711 - - - - Form - - - - 0 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - false - - - true - - - - 0 - - - 4 - - - 4 - - - 4 - - - 0 - - - - - - 120 - 0 - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Subscribe - - - - - - - auto scaling - - - - - - - Reset - - - - - - - - 40 - 16777215 - - - - 1y - - - true - - - - - - - - 40 - 16777215 - - - - 6m - - - true - - - - - - - - 40 - 16777215 - - - - 1m - - - true - - - - - - - - 40 - 16777215 - - - - 1w - - - true - - - - - - - - 40 - 16777215 - - - - 24h - - - true - - - - - - - - 40 - 16777215 - - - - 12h - - - true - - - - - - - - 40 - 16777215 - - - - 6h - - - true - - - - - - - - 40 - 16777215 - - - - 1h - - - true - - - - - - - - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - - - - - - - - - - QCustomPlot - QWidget -
CustomControls/qcustomplot.h
- 1 -
-
- - -
diff --git a/BlockSettleUILib/ChatUI/BSChatInput.cpp b/BlockSettleUILib/ChatUI/BSChatInput.cpp deleted file mode 100644 index d37cf2a7d..000000000 --- a/BlockSettleUILib/ChatUI/BSChatInput.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "BSChatInput.h" - -#include - -BSChatInput::BSChatInput(QWidget *parent) - : QTextBrowser(parent) -{ - -} -BSChatInput::BSChatInput(const QString &text, QWidget *parent) - : QTextBrowser(parent) -{ - setText(text); -} - -BSChatInput::~BSChatInput() = default; - -void BSChatInput::keyPressEvent(QKeyEvent * e) -{ - //Qt::Key_Return - Main Enter key - //Qt::Key_Enter = Numpad Enter key - if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (e->modifiers().testFlag(Qt::ShiftModifier)) { - this->insertPlainText(QStringLiteral("\n")); - - } else { - emit sendMessage(); - } - return e->ignore(); - } - else if (e->modifiers().testFlag(Qt::ControlModifier)) { - if (Qt::Key_C == e->key()) { - // If there no selection than could be that we going to copy text from other element - // which cannot have focus. - if (!textCursor().hasSelection()) { - e->setAccepted(false); - return; - } - } - else if (Qt::Key_V == e->key()) { - QTextBrowser::keyPressEvent(e); - auto cursor = textCursor(); - cursor.setCharFormat({}); - setTextCursor(cursor); - return; - } - } - - return QTextBrowser::keyPressEvent(e); -} diff --git a/BlockSettleUILib/ChatUI/BSChatInput.h b/BlockSettleUILib/ChatUI/BSChatInput.h deleted file mode 100644 index d5222c6f6..000000000 --- a/BlockSettleUILib/ChatUI/BSChatInput.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef BSCHATINPUT_H -#define BSCHATINPUT_H - -#include - -class BSChatInput : public QTextBrowser { - Q_OBJECT -public: - BSChatInput(QWidget *parent = nullptr); - BSChatInput(const QString &text, QWidget *parent = nullptr); - ~BSChatInput() override; - -signals: - void sendMessage(); - -public: - void keyPressEvent(QKeyEvent * e) override; -}; - -#endif // BSCHATINPUT_H diff --git a/BlockSettleUILib/ChatUI/ChatClientUsersViewItemDelegate.cpp b/BlockSettleUILib/ChatUI/ChatClientUsersViewItemDelegate.cpp deleted file mode 100644 index dc1da292c..000000000 --- a/BlockSettleUILib/ChatUI/ChatClientUsersViewItemDelegate.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatClientUsersViewItemDelegate.h" -#include -#include - -namespace { - const int kDotSize = 8; - const QString kDotPathname = QLatin1String{ ":/ICON_DOT" }; -} - -using namespace bs; - -ChatClientUsersViewItemDelegate::ChatClientUsersViewItemDelegate(ChatPartiesSortProxyModelPtr proxyModel, QObject *parent) - : QStyledItemDelegate (parent) - , proxyModel_(proxyModel) -{ -} - -void ChatClientUsersViewItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (!index.isValid()) { - return; - } - - const QModelIndex& sourceIndex = proxyModel_ ? proxyModel_->mapToSource(index) : index; - if (!sourceIndex.isValid()) { - return; - } - - PartyTreeItem* internalData = static_cast(sourceIndex.internalPointer()); - if (internalData->modelType() == UI::ElementType::Container) { - paintPartyContainer(painter, option, sourceIndex); - } - else if (internalData->modelType() == UI::ElementType::Party) { - paintParty(painter, option, sourceIndex); - } -} - -void ChatClientUsersViewItemDelegate::paintPartyContainer(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem itemOption(option); - - if (itemOption.state & QStyle::State_Selected) { - painter->save(); - painter->fillRect(itemOption.rect, itemStyle_.colorHighlightBackground()); - painter->restore(); - } - - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorCategoryItem()); - PartyTreeItem* internalData = static_cast(index.internalPointer()); - Q_ASSERT(internalData && internalData->data().canConvert()); - itemOption.text = internalData->data().toString(); - - QStyledItemDelegate::paint(painter, itemOption, index); -} - -void ChatClientUsersViewItemDelegate::paintParty(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QStyleOptionViewItem itemOption(option); - if (itemOption.state & QStyle::State_Selected) { - painter->save(); - painter->fillRect(itemOption.rect, itemStyle_.colorHighlightBackground()); - painter->restore(); - } - - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorRoom()); - PartyTreeItem* party = static_cast(index.internalPointer()); - Chat::ClientPartyPtr clientPartyPtr = party->data().value(); - if (!clientPartyPtr) { - return; - } - - itemOption.text = QString::fromStdString(clientPartyPtr->displayName()); - if (clientPartyPtr->isPrivateStandard() || clientPartyPtr->isPrivateOTC()) { - if (Chat::PartyState::INITIALIZED == clientPartyPtr->partyState()) { - paintInitParty(party, painter, itemOption); - } - else { - paintRequestParty(clientPartyPtr, painter, itemOption); - } - } - - QStyledItemDelegate::paint(painter, itemOption, index); - - if (party->hasNewMessages() && Chat::PartyState::REQUESTED != clientPartyPtr->partyState()) { - painter->save(); - QFontMetrics fm(itemOption.font, painter->device()); - auto textRect = fm.boundingRect(itemOption.rect, 0, itemOption.text); - const QPixmap pixmap(kDotPathname); - const QRect r(itemOption.rect.left() + textRect.width() + kDotSize, - itemOption.rect.top() + itemOption.rect.height() / 2 - kDotSize / 2 + 1, - kDotSize, kDotSize); - painter->drawPixmap(r, pixmap, pixmap.rect()); - painter->restore(); - } -} - -void ChatClientUsersViewItemDelegate::paintInitParty(PartyTreeItem* partyTreeItem, QPainter* painter, - QStyleOptionViewItem& itemOption) const -{ - Chat::ClientPartyPtr clientPartyPtr = partyTreeItem->data().value(); - // This should be always true as far as we checked it in previous flow function - assert(clientPartyPtr); - - switch (clientPartyPtr->clientStatus()) - { - case Chat::ClientStatus::ONLINE: - { - QColor palleteColor = itemStyle_.colorContactOnline(); - if (partyTreeItem->isOTCTogglingMode() && !partyTreeItem->activeOTCToggleState()) { - palleteColor = itemStyle_.colorContactOffline(); - } - - itemOption.palette.setColor(QPalette::Text, palleteColor); - } - break; - - case Chat::ClientStatus::OFFLINE: - { - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorContactOffline()); - if (itemOption.state & QStyle::State_Selected) { - painter->save(); - painter->fillRect(itemOption.rect, itemStyle_.colorContactOffline()); - painter->restore(); - } - } - break; - - default: - { - // You should specify rules for new ClientStatus explicitly - Q_ASSERT(false); - } - break; - } -} - -void ChatClientUsersViewItemDelegate::paintRequestParty(Chat::ClientPartyPtr& clientPartyPtr, QPainter* painter, - QStyleOptionViewItem& itemOption) const -{ - switch (clientPartyPtr->partyState()) { - case Chat::PartyState::UNINITIALIZED: - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorContactOutgoing()); - break; - case Chat::PartyState::REQUESTED: - if (clientPartyPtr->partyCreatorHash() == proxyModel_->currentUser()) { - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorContactOutgoing()); - } - else { - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorContactIncoming()); - } - break; - default: - break; - } -} - -QWidget *ChatClientUsersViewItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - QWidget * editor = QStyledItemDelegate::createEditor(parent, option, index); - editor->setProperty("contact_editor", true); - return editor; -} diff --git a/BlockSettleUILib/ChatUI/ChatClientUsersViewItemDelegate.h b/BlockSettleUILib/ChatUI/ChatClientUsersViewItemDelegate.h deleted file mode 100644 index 6eec056e7..000000000 --- a/BlockSettleUILib/ChatUI/ChatClientUsersViewItemDelegate.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATCLIENTUSERSVIEWITEMDELEGATE_H -#define CHATCLIENTUSERSVIEWITEMDELEGATE_H - -#include -#include "ChatUsersViewItemStyle.h" - -#include "ChatPartiesSortProxyModel.h" - -class ChatClientUsersViewItemDelegate : public QStyledItemDelegate -{ - Q_OBJECT -public: - explicit ChatClientUsersViewItemDelegate(ChatPartiesSortProxyModelPtr proxyModel, QObject *parent = nullptr); - -public: - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - -protected: - void paintPartyContainer(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - void paintParty(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - - void paintInitParty(PartyTreeItem* partyTreeItem, QPainter* painter, - QStyleOptionViewItem& itemOption) const; - void paintRequestParty(Chat::ClientPartyPtr& clientPartyPtr, QPainter* painter, - QStyleOptionViewItem& itemOption) const; - -private: - ChatUsersViewItemStyle itemStyle_; - ChatPartiesSortProxyModelPtr proxyModel_; -}; -#endif // CHATCLIENTUSERSVIEWITEMDELEGATE_H diff --git a/BlockSettleUILib/ChatUI/ChatMessagesTextEdit.cpp b/BlockSettleUILib/ChatUI/ChatMessagesTextEdit.cpp deleted file mode 100644 index 96744f902..000000000 --- a/BlockSettleUILib/ChatUI/ChatMessagesTextEdit.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatMessagesTextEdit.h" - -#include "OtcUtils.h" -#include "RequestPartyBox.h" -#include "chat.pb.h" - -#include -#include -#include -#include -#include - -#include - -namespace { - // Translation - const QString ownSenderUserName = QObject::tr("you"); - const QString contextMenuCopy = QObject::tr("Copy"); - const QString contextMenuCopyLink = QObject::tr("Copy Link Location"); - const QString contextMenuSelectAll= QObject::tr("Select All"); - const QString contextMenuAddUserMenu = QObject::tr("Add to contacts"); - const QString contextMenuAddUserMenuStatusTip = QObject::tr("Click to add user to contact list"); - const QString contextMenuRemoveUserMenu = QObject::tr("Remove from contacts"); - const QString contextMenuRemoveUserMenuStatusTip = QObject::tr("Click to remove user from contact list"); -} - -ChatMessagesTextEdit::ChatMessagesTextEdit(QWidget* parent) - : QTextBrowser(parent) - , internalStyle_(this) -{ - tableFormat_.setBorder(0); - tableFormat_.setCellPadding(0); - tableFormat_.setCellSpacing(0); - - setupHighlightPalette(); - - connect(this, &QTextBrowser::anchorClicked, this, &ChatMessagesTextEdit::onUrlActivated); - connect(this, &QTextBrowser::textChanged, this, &ChatMessagesTextEdit::onTextChanged); -} - -void ChatMessagesTextEdit::setupHighlightPalette() -{ - auto highlightPalette = palette(); - highlightPalette.setColor(QPalette::Inactive, QPalette::Highlight, highlightPalette.color(QPalette::Active, QPalette::Highlight)); - highlightPalette.setColor(QPalette::Inactive, QPalette::HighlightedText, highlightPalette.color(QPalette::Active, QPalette::HighlightedText)); - setPalette(highlightPalette); -} - -int ChatMessagesTextEdit::messagesCount(const std::string& partyId) const -{ - return messages_[partyId].count(); -} - -Chat::MessagePtr ChatMessagesTextEdit::getMessage(const std::string& partyId, const std::string& messageId) const -{ - const auto it = std::find_if(messages_[partyId].begin(), messages_[partyId].end(), [messageId](const Chat::MessagePtr& msg)->bool { - return msg->messageId() == messageId; - }); - - if (it != messages_[partyId].end()) { - return *it; - } - - return nullptr; -} - -QString ChatMessagesTextEdit::data(const std::string& partyId, const std::string& messageId, const Column &column) -{ - if (messages_[partyId].empty()) { - return QString(); - } - - return dataMessage(partyId, messageId, column); -} - -QString ChatMessagesTextEdit::dataMessage(const std::string& partyId, const std::string& messageId, const ChatMessagesTextEdit::Column &column) const -{ - const auto message = getMessage(partyId, messageId); - - if (!message) { - return QString(); - } - - Chat::ClientPartyPtr previousClientPartyPtr = nullptr; - - switch (column) { - case Column::Time: - { - const auto dateTime = message->timestamp().toLocalTime(); - return toHtmlText(dateTime.toString(QString::fromUtf8("MM/dd/yy hh:mm:ss"))); - } - - case Column::User: - { - const auto& senderHash = message->senderHash(); - - if (senderHash == ownUserId_) { - return ownSenderUserName; - } - - if (!previousClientPartyPtr || previousClientPartyPtr->id() != partyId) { - previousClientPartyPtr = partyModel_->getClientPartyById(message->partyId()); - } - - if (!previousClientPartyPtr->isGlobal()) { - return elideUserName(previousClientPartyPtr->displayName()); - } - - const auto clientPartyPtr = partyModel_->getClientPartyById(partyId); - - if (clientPartyPtr && clientPartyPtr->isPrivate()) { - return toHtmlUsername(clientPartyPtr->displayName(), clientPartyPtr->userHash()); - } - return toHtmlUsername(senderHash, senderHash); - } - case Column::Status:{ - return QString(); - } - case Column::Message: { - //QString text = QLatin1String("[%1] %2"); - //text = text.arg(QString::fromStdString(message->messageText())); - - //if (ChatUtils::messageFlagRead(message, Chat::Data_Message_State_INVALID)) { - // return toHtmlInvalid(text.arg(QLatin1String("INVALID MESSAGE!"))); - //} else if (message.encryption() == Chat::Data_Message_Encryption_IES) { - // return toHtmlInvalid(text.arg(QLatin1String("IES ENCRYPTED!"))); - //} else if ( message.encryption() == Chat::Data_Message_Encryption_AEAD) { - // return toHtmlInvalid(text.arg(QLatin1String("AEAD ENCRYPTED!"))); - //} - return toHtmlText(QString::fromStdString(message->messageText())); - } - default: - break; - } - - return QString(); -} - -QImage ChatMessagesTextEdit::statusImage(const std::string& partyId, const std::string& messageId) const -{ - const auto message = getMessage(partyId, messageId); - - if (!message) { - return statusImageGreyUnsent_; - } - - if (message->senderHash() != ownUserId_) { - return QImage(); - } - - auto statusImage = statusImageGreyUnsent_; - - const auto clientPartyPtr = partyModel_->getClientPartyById(message->partyId()); - - if (!clientPartyPtr) { - return QImage(); - } - - if (clientPartyPtr->isGlobalStandard()) { - if ((message->partyMessageState() != Chat::PartyMessageState::UNSENT)) { - statusImage = statusImageBlueSeen_; - } - return statusImage; - } - - if (message->partyMessageState() == Chat::PartyMessageState::UNSENT) { - statusImage = statusImageGreyUnsent_; - } else if (message->partyMessageState() == Chat::PartyMessageState::SENT) { - statusImage = statusImageYellowSent_; - } else if (message->partyMessageState() == Chat::PartyMessageState::RECEIVED) { - statusImage = statusImageGreenReceived_; - } else if (message->partyMessageState() == Chat::PartyMessageState::SEEN) { - statusImage = statusImageBlueSeen_; - } - - return statusImage; -} - -void ChatMessagesTextEdit::contextMenuEvent(QContextMenuEvent *e) -{ - textCursor_ = cursorForPosition(e->pos()); - - // keep selection - if (textCursor().hasSelection()) { - textCursor_.setPosition(textCursor().selectionStart(), QTextCursor::MoveAnchor); - textCursor_.setPosition(textCursor().selectionEnd(), QTextCursor::KeepAnchor); - } - - setTextCursor(textCursor_); - QString text = textCursor_.block().text(); - - //show contact context menu when username is right clicked in User column - if ((textCursor_.block().blockNumber() - 1) % 5 == static_cast(Column::User)) { - if (text != ownSenderUserName) { - const QUrl link = anchorAt(e->pos()); - if (!link.isEmpty()) { - text = link.path(); - } - std::unique_ptr userMenuPtr = initUserContextMenu(text); - userMenuPtr->exec(QCursor::pos()); - return; - } - } - - // show default text context menu - if (text.length() > 0 || textCursor_.hasSelection()) { - QMenu contextMenu(this); - - QAction copyAction(contextMenuCopy, this); - QAction copyLinkLocationAction(contextMenuCopyLink, this); - QAction selectAllAction(contextMenuSelectAll, this); - - connect(©Action, &QAction::triggered, this, &ChatMessagesTextEdit::onCopyActionTriggered); - connect(©LinkLocationAction, &QAction::triggered, this, &ChatMessagesTextEdit::onCopyLinkLocationActionTriggered); - connect(&selectAllAction, &QAction::triggered, this, &ChatMessagesTextEdit::onSelectAllActionTriggered); - - contextMenu.addAction(©Action); - - // show Copy Link Location only when it needed - anchor_ = this->anchorAt(e->pos()); - if (!anchor_.isEmpty()) { - contextMenu.addAction(©LinkLocationAction); - } - - contextMenu.addSeparator(); - contextMenu.addAction(&selectAllAction); - - contextMenu.exec(e->globalPos()); - } -} - -void ChatMessagesTextEdit::onCopyActionTriggered() const -{ - if (textCursor_.hasSelection()) { - QApplication::clipboard()->setText(getFormattedTextFromSelection()); - } - else { - QTextDocument doc; - doc.setHtml(textCursor_.block().text()); - QApplication::clipboard()->setText(doc.toPlainText()); - } -} - -void ChatMessagesTextEdit::onCopyLinkLocationActionTriggered() const -{ - QApplication::clipboard()->setText(anchor_); -} - -void ChatMessagesTextEdit::onSelectAllActionTriggered() -{ - this->selectAll(); -} - -void ChatMessagesTextEdit::onTextChanged() const -{ - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); -} - -void ChatMessagesTextEdit::onUserUrlOpened(const QUrl &url) -{ - const std::string userId = url.path().toStdString(); - const Chat::ClientPartyPtr clientPartyPtr = partyModel_->getStandardPartyForUsers(ownUserId_, userId); - - if (!clientPartyPtr) { - onShowRequestPartyBox(userId); - return; - } - - if (Chat::PartyState::REJECTED == clientPartyPtr->partyState()) { - onShowRequestPartyBox(clientPartyPtr->userHash()); - return; - } - - if (clientPartyPtr->id() == currentPartyId_) { - return; - } - - emit switchPartyRequest(QString::fromStdString(clientPartyPtr->id())); -} - -void ChatMessagesTextEdit::onShowRequestPartyBox(const std::string& userHash) -{ - std::string requestUserHash = userHash; - - const Chat::ClientPartyPtr clientPartyPtr = partyModel_->getStandardPartyForUsers(ownUserId_, requestUserHash); - - const QString requestNote = tr("You can enter initial message below:"); - const QString requestTitle = tr("Do you want to send friend request to %1 ?").arg(QString::fromStdString(requestUserHash)); - - RequestPartyBox rpBox(requestTitle, requestNote); - if (rpBox.exec() == QDialog::Accepted) { - if (clientPartyPtr && Chat::PartyState::REJECTED == clientPartyPtr->partyState()) { - emit removePartyRequest(clientPartyPtr->id()); - requestUserHash = clientPartyPtr->userHash(); - } - - emit newPartyRequest(requestUserHash, rpBox.getCustomMessage().toStdString()); - } -} - -void ChatMessagesTextEdit::onSwitchToChat(const std::string& partyId) -{ - currentPartyId_ = partyId; - clear(); - if (!currentPartyId_.empty()) { - showMessages(partyId); - onTextChanged(); - auto clientMessagesHistory = messages_[partyId]; - - if (clientMessagesHistory.empty()) { - return; - } - - auto riter = clientMessagesHistory.rbegin(); - for (; riter != clientMessagesHistory.rend(); ++riter) - { - const auto messagePtr = (*riter); - - if (messagePtr->partyMessageState() == Chat::PartyMessageState::SEEN) { - continue; - } - - if (messagePtr->senderHash() == ownUserId_) { - continue; - } - - emit messageRead(messagePtr->partyId(), messagePtr->messageId()); - } - } -} - -void ChatMessagesTextEdit::onLogout() -{ - onSwitchToChat({}); -} - -Chat::MessagePtr ChatMessagesTextEdit::onMessageStatusChanged(const std::string& partyId, const std::string& message_id, - const int party_message_state) -{ - Chat::MessagePtr message = findMessage(partyId, message_id); - - if (message) { - message->setPartyMessageState(static_cast(party_message_state)); - notifyMessageChanged(message); - } - - return message; -} - -void ChatMessagesTextEdit::onSetColumnsWidth(int time, int icon, int user, int message) -{ - QVector col_widths; - col_widths << QTextLength(QTextLength::FixedLength, time); - col_widths << QTextLength(QTextLength::FixedLength, icon); - col_widths << QTextLength(QTextLength::FixedLength, user); - col_widths << QTextLength(QTextLength::VariableLength, message); - tableFormat_.setColumnWidthConstraints(col_widths); - userColumnWidth_ = user; -} - -void ChatMessagesTextEdit::onSetClientPartyModel(const Chat::ClientPartyModelPtr& partyModel) -{ - partyModel_ = partyModel; -} - -QString ChatMessagesTextEdit::getFormattedTextFromSelection() const -{ - QString text; - QTextDocument textDocument; - - // get selected text in html format - textDocument.setHtml(createMimeDataFromSelection()->html()); - QTextBlock currentBlock = textDocument.begin(); - int blockCount = 0; - - // each column is presented as a block - while (currentBlock.isValid()) { - blockCount++; - if (!currentBlock.text().isEmpty()) { - - // format columns splits to tabulation - if (!text.isEmpty()) { - text += QChar::Tabulation; - - // new row (when few rows are selected) - if ((blockCount - 2) % 5 == 0) { - text += QChar::LineFeed; - } - } - // replace some special characters, because they can display incorrect - text += currentBlock.text().replace(QChar::LineSeparator, QChar::LineFeed); - } - currentBlock = currentBlock.next(); - } - return text; -} - -void ChatMessagesTextEdit::onUrlActivated(const QUrl &link) { - if (link.scheme() != QLatin1Literal("user")) { - QDesktopServices::openUrl(link); - } - else { - onUserUrlOpened(link); - } -} - -void ChatMessagesTextEdit::insertMessage(const Chat::MessagePtr& messagePtr) -{ - // push new message if it doesn't exist in current chat - auto& messagesList = messages_[messagePtr->partyId()]; - const auto messageIt = - std::find_if(messagesList.begin(), messagesList.end(), [messagePtr](const Chat::MessagePtr& m)->bool - { - return m->messageId() == messagePtr->messageId(); - }); - - // remove duplicates by give message_id - if (messageIt != messagesList.cend()) - { - deleteMessage(static_cast(std::distance(messagesList.begin(), messageIt))); - messagesList.erase(messageIt); - } - - messagesList.push_back(messagePtr); - if (messagePtr->partyId() == currentPartyId_) { - showMessage(messagePtr->partyId(), messagePtr->messageId()); - } - - if (messagePtr->partyMessageState() != Chat::PartyMessageState::SEEN - && messagePtr->senderHash() != ownUserId_ - && messagePtr->partyId() == currentPartyId_ - && isVisible()) { - emit messageRead(messagePtr->partyId(), messagePtr->messageId()); - } -} - -void ChatMessagesTextEdit::insertMessageInDoc(QTextCursor& cursor, const std::string& partyId, const std::string& messageId) -{ - cursor.beginEditBlock(); - auto* table = cursor.insertTable(1, 4, tableFormat_); - - const auto time = data(partyId, messageId, Column::Time); - table->cellAt(0, 0).firstCursorPosition().insertHtml(time); - - const auto image = statusImage(partyId, messageId); - if (!image.isNull()) { - table->cellAt(0, 1).firstCursorPosition().insertImage(image); - } - - const auto user = data(partyId, messageId, Column::User); - table->cellAt(0, 2).firstCursorPosition().insertHtml(user); - - const auto message = data(partyId, messageId, Column::Message); - table->cellAt(0, 3).firstCursorPosition().insertHtml(message); - cursor.endEditBlock(); -} - -void ChatMessagesTextEdit::updateMessage(const std::string& partyId, const std::string& messageId) -{ - ClientMessagesHistory messagesList = messages_[partyId]; - const ClientMessagesHistory::iterator it = - std::find_if(messagesList.begin(), messagesList.end(), [messageId](const Chat::MessagePtr& m)->bool - { - return m->messageId() == messageId; - }); - - if (it != messagesList.end()) - { - const int distance = std::distance(messagesList.begin(), it); - auto cursor = deleteMessage(distance); - insertMessageInDoc(cursor, partyId, messageId); - } -} - -QTextCursor ChatMessagesTextEdit::deleteMessage(const int index) const -{ - QTextCursor cursor = textCursor(); - cursor.movePosition(QTextCursor::Start); - cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, index * 2); - cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, 1); - cursor.removeSelectedText(); - - return cursor; -} - -QString ChatMessagesTextEdit::elideUserName(const std::string& displayName) const -{ - return fontMetrics().elidedText(QString::fromStdString(displayName), Qt::ElideRight, userColumnWidth_); -} - -void ChatMessagesTextEdit::showMessage(const std::string& partyId, const std::string& messageId) -{ - /* add text */ - QTextCursor cursor = textCursor(); - cursor.movePosition(QTextCursor::End); - - insertMessageInDoc(cursor, partyId, messageId); -} - -void ChatMessagesTextEdit::showMessages(const std::string &partyId) -{ - for (const auto& message : messages_[partyId]) { - showMessage(partyId, message->messageId()); - } -} - -std::unique_ptr ChatMessagesTextEdit::initUserContextMenu(const QString& userName) -{ - std::unique_ptr userMenuPtr = std::make_unique(this); - - Chat::ClientPartyPtr clientPartyPtr = partyModel_->getStandardPartyForUsers(ownUserId_, userName.toStdString()); - if (!clientPartyPtr) { - QAction* addAction = userMenuPtr->addAction(contextMenuAddUserMenu); - addAction->setStatusTip(contextMenuAddUserMenuStatusTip); - connect(addAction, &QAction::triggered, this, [this, userName_ = userName]() { - onShowRequestPartyBox(userName_.toStdString()); - }); - } - else { - QAction* removeAction = userMenuPtr->addAction(contextMenuRemoveUserMenu); - removeAction->setStatusTip(contextMenuRemoveUserMenuStatusTip); - connect(removeAction, &QAction::triggered, this, [this, clientPartyPtr]() { - emit removePartyRequest(clientPartyPtr->id()); - }); - } - - return userMenuPtr; -} - -void ChatMessagesTextEdit::onMessageUpdate(const Chat::MessagePtrList& messagePtrList) -{ -#ifndef QT_NO_DEBUG - const std::string& partyId = !messagePtrList.empty() ? messagePtrList[0]->partyId() : ""; -#endif - Chat::MessagePtrList messagePtrListSorted = messagePtrList; - std::sort(messagePtrListSorted.begin(), messagePtrListSorted.end(), [](const auto& left, const auto& right) -> bool { - return left->timestamp() < right->timestamp(); - }); - for (const auto& messagePtr : messagePtrListSorted) { -#ifndef QT_NO_DEBUG - Q_ASSERT(partyId == messagePtr->partyId()); -#endif - insertMessage(messagePtr); - } -} - -void ChatMessagesTextEdit::onUpdatePartyName(const std::string& partyId) -{ - const auto messageHistory = messages_[currentPartyId_]; - - for (const auto& messagePtr : messageHistory) - { - if (messagePtr->partyId() != partyId) { - continue; - } - - updateMessage(partyId, messagePtr->messageId()); - } -} - -Chat::MessagePtr ChatMessagesTextEdit::findMessage(const std::string& partyId, const std::string& messageId) -{ - if (messages_.contains(partyId)) { - const auto it = std::find_if(messages_[partyId].begin(), messages_[partyId].end(), [messageId](const Chat::MessagePtr& message) { - return message->messageId() == messageId; - }); - - if (it != messages_[partyId].end()) { - return *it; - } - } - - return {}; -} - -void ChatMessagesTextEdit::notifyMessageChanged(const Chat::MessagePtr& message) -{ - const std::string& partyId = message->partyId(); - if (partyId != currentPartyId_) { - // Do not need to update view - return; - } - - if (messages_.contains(partyId)) { - const std::string& id = message->messageId(); - const auto it = std::find_if(messages_[partyId].begin(), messages_[partyId].end(), [id](const Chat::MessagePtr& iteration) { - return iteration->messageId() == id; - }); - - if (it != messages_[partyId].end()) { - updateMessage(partyId, (*it)->messageId()); - } - } -} - -QString ChatMessagesTextEdit::toHtmlUsername(const std::string& username, const std::string& userId) const -{ - return QStringLiteral("%3") - .arg(QString::fromStdString(userId)) - .arg(internalStyle_.colorHyperlink().name()) - .arg(elideUserName(username)); -} - -QString ChatMessagesTextEdit::toHtmlInvalid(const QString &text) const -{ - QString changedText = QStringLiteral("%2").arg(internalStyle_.colorRed().name()).arg(text); - return changedText; -} - -QString ChatMessagesTextEdit::toHtmlText(const QString &text) const -{ - const auto otcText = OtcUtils::toReadableString(text); - if (!otcText.isEmpty()) { - // No further processing is needed - return QStringLiteral("*** %2 ***").arg(internalStyle_.colorOtc().name()).arg(otcText); - } - - QString changedText = text.toHtmlEscaped(); - - // make linkable - int index = 0; - int startIndex; - - while ((startIndex = changedText.indexOf(QLatin1Literal("https://"), index, Qt::CaseInsensitive)) != -1 - || (startIndex = changedText.indexOf(QLatin1Literal("http://"), index, Qt::CaseInsensitive)) != -1) { - - int endIndex = changedText.indexOf(QLatin1Literal(" "), startIndex); - if (endIndex == -1) { - endIndex = changedText.indexOf(QLatin1Literal("\n"), startIndex); - } - if (endIndex == -1) { - endIndex = changedText.length(); - } - - QString linkText = changedText.mid(startIndex, endIndex - startIndex); - QString hyperlinkText = QStringLiteral("%1").arg(linkText).arg(internalStyle_.colorHyperlink().name()); - - changedText = changedText.replace(startIndex, endIndex - startIndex, hyperlinkText); - - index = startIndex + hyperlinkText.length(); - } - - // replace linefeed with
- changedText.replace(QLatin1Literal("\n"), QLatin1Literal("
")); - - // set text color as white - changedText = QStringLiteral("%2").arg(internalStyle_.colorWhite().name()).arg(changedText); - - return changedText; -} diff --git a/BlockSettleUILib/ChatUI/ChatMessagesTextEdit.h b/BlockSettleUILib/ChatUI/ChatMessagesTextEdit.h deleted file mode 100644 index 0f6319bac..000000000 --- a/BlockSettleUILib/ChatUI/ChatMessagesTextEdit.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATMESSAGESTEXTEDIT_H -#define CHATMESSAGESTEXTEDIT_H - -#include "ChatProtocol/Message.h" -#include "ChatProtocol/ClientPartyModel.h" - -#include -#include -#include -#include -#include - -#include - -namespace Chat { - class MessageData; -} - -class ChatMessagesTextEditStyle : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(QColor color_hyperlink READ colorHyperlink - WRITE setColorHyperlink) - Q_PROPERTY(QColor color_white READ colorWhite - WRITE setColorWhite) - Q_PROPERTY(QColor color_red READ colorRed - WRITE setColorRed) - Q_PROPERTY(QColor color_otc READ colorOtc - WRITE setColorOtc) - -public: - explicit ChatMessagesTextEditStyle(QWidget *parent) - : QWidget(parent), colorHyperlink_(Qt::blue), colorWhite_(Qt::white), colorRed_(Qt::red), colorOtc_(Qt::lightGray) - { - QWidget::setVisible(false); - } - - QColor colorHyperlink() const { return colorHyperlink_; } - void setColorHyperlink(const QColor &colorHyperlink) { - colorHyperlink_ = colorHyperlink; - } - - QColor colorWhite() const { return colorWhite_; } - void setColorWhite(const QColor &colorWhite) { - colorWhite_ = colorWhite; - } - - QColor colorRed() const { return colorRed_; } - void setColorRed(QColor val) { colorRed_ = val; } - - QColor colorOtc() const { return colorOtc_; } - void setColorOtc(const QColor &colorOtc) { - colorOtc_ = colorOtc; - } - -private: - QColor colorHyperlink_; - QColor colorWhite_; - QColor colorRed_; - QColor colorOtc_; -}; - -class ChatMessagesTextEdit : public QTextBrowser -{ - Q_OBJECT - -public: - ChatMessagesTextEdit(QWidget* parent = nullptr); - ~ChatMessagesTextEdit() noexcept override = default; - - QString getFormattedTextFromSelection() const; - int messagesCount(const std::string& partyId) const; - -public slots: - void onSetColumnsWidth(int time, int icon, int user, int message); - void onSetOwnUserId(const std::string &userId) { ownUserId_ = userId; } - void onSetClientPartyModel(const Chat::ClientPartyModelPtr& partyModel); - void onSwitchToChat(const std::string& partyId); - void onLogout(); - Chat::MessagePtr onMessageStatusChanged(const std::string& partyId, const std::string& message_id, - const int party_message_state); - void onMessageUpdate(const Chat::MessagePtrList& messagePtrList); - void onUpdatePartyName(const std::string& partyId); - - void onShowRequestPartyBox(const std::string& userHash); - -signals: - void messageRead(const std::string& partyId, const std::string& messageId); - void newPartyRequest(const std::string& userName, const std::string& initialMessage); - void removePartyRequest(const std::string& partyId); - void switchPartyRequest(const QString& partyId); - -protected: - enum class Column { - Time, - Status, - User, - Message, - last - }; - - QString data(const std::string& partyId, const std::string& messageId, const Column &column); - QString dataMessage(const std::string& partyId, const std::string& messageId, const Column &column) const; - QImage statusImage(const std::string& partyId, const std::string& messageId) const; - - void contextMenuEvent(QContextMenuEvent* e) override; - -private slots: - void onUrlActivated(const QUrl &link); - void onCopyActionTriggered() const; - void onCopyLinkLocationActionTriggered() const; - void onSelectAllActionTriggered(); - void onTextChanged() const; - void onUserUrlOpened(const QUrl &url); - -private: - Chat::MessagePtr getMessage(const std::string& partyId, const std::string& messageId) const; - void setupHighlightPalette(); - std::unique_ptr initUserContextMenu(const QString& userName); - - // #new_logic - QString toHtmlUsername(const std::string& username, const std::string& userId) const; - QString toHtmlText(const QString &text) const; - QString toHtmlInvalid(const QString &text) const; - - void insertMessage(const Chat::MessagePtr& messagePtr); - void showMessage(const std::string& partyId, const std::string& messageId); - void showMessages(const std::string& partyId); - Chat::MessagePtr findMessage(const std::string& partyId, const std::string& messageId); - void notifyMessageChanged(const Chat::MessagePtr& message); - void insertMessageInDoc(QTextCursor& cursor, const std::string& partyId, const std::string& messageId); - void updateMessage(const std::string& partyId, const std::string& messageId); - QTextCursor deleteMessage(int index) const; - QString elideUserName(const std::string& displayName) const; - - Chat::ClientPartyModelPtr partyModel_; - - std::string currentPartyId_; - std::string ownUserId_; - - using ClientMessagesHistory = QVector; - QMap messages_; - - QImage statusImageGreyUnsent_ = QImage({ QLatin1Literal(":/ICON_MSG_STATUS_OFFLINE") }, "PNG"); - QImage statusImageYellowSent_ = QImage({ QLatin1Literal(":/ICON_MSG_STATUS_CONNECTING") }, "PNG"); - QImage statusImageGreenReceived_ = QImage({ QLatin1Literal(":/ICON_MSG_STATUS_ONLINE") }, "PNG"); - QImage statusImageBlueSeen_ = QImage({ QLatin1Literal(":/ICON_MSG_STATUS_READ") }, "PNG"); - - QTextTableFormat tableFormat_; - ChatMessagesTextEditStyle internalStyle_; - - QTextCursor textCursor_; - QString anchor_; - int userColumnWidth_ = 0; -}; - -#endif // CHATMESSAGESTEXTEDIT_H diff --git a/BlockSettleUILib/ChatUI/ChatOTCHelper.cpp b/BlockSettleUILib/ChatUI/ChatOTCHelper.cpp deleted file mode 100644 index 221d04157..000000000 --- a/BlockSettleUILib/ChatUI/ChatOTCHelper.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatOTCHelper.h" - -#include - -#include "ArmoryConnection.h" -#include "ChatProtocol/ClientParty.h" -#include "OtcClient.h" -#include "OtcUtils.h" -#include "SignContainer.h" -#include "chat.pb.h" -#include "UtxoReservationManager.h" - -ChatOTCHelper::ChatOTCHelper(QObject* parent /*= nullptr*/) - : QObject(parent) -{ -} - -void ChatOTCHelper::init(bs::network::otc::Env env - , const std::shared_ptr& loggerPtr - , const std::shared_ptr& walletsMgr - , const std::shared_ptr& armory - , const std::shared_ptr& signContainer - , const std::shared_ptr &authAddressManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& applicationSettings) -{ - loggerPtr_ = loggerPtr; - - OtcClientParams params; - params.env = env; - otcClient_ = new OtcClient(loggerPtr, walletsMgr, armory, signContainer, authAddressManager, - utxoReservationManager, applicationSettings, std::move(params), this); -} - -OtcClient* ChatOTCHelper::client() const -{ - return otcClient_; -} - -void ChatOTCHelper::setCurrentUserId(const std::string& ownUserId) -{ - otcClient_->setOwnContactId(ownUserId); -} - -void ChatOTCHelper::setGlobalOTCEntryTimeStamp(QDateTime timeStamp) -{ - selectedGlobalEntryTimeStamp_ = timeStamp; -} - -QDateTime ChatOTCHelper::selectedGlobalOTCEntryTimeStamp() const -{ - return selectedGlobalEntryTimeStamp_; -} - -void ChatOTCHelper::onLogout() -{ - for (const auto &contactId : connectedContacts_) { - otcClient_->contactDisconnected(contactId); - } - connectedContacts_.clear(); -} - -void ChatOTCHelper::onProcessOtcPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response) -{ - otcClient_->processPbMessage(response); -} - -void ChatOTCHelper::onOtcRequestSubmit(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer& offer) -{ - if (!peer) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "peer not found"); - return; - } - - bool result = otcClient_->sendOffer(peer, offer); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "send offer failed"); - return; - } -} - -void ChatOTCHelper::onOtcPullOrReject(const bs::network::otc::PeerPtr &peer) -{ - if (!peer) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "peer not found"); - return; - } - - bool result = otcClient_->pullOrReject(peer); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "pull or reject failed"); - return; - } -} - -void ChatOTCHelper::onOtcResponseAccept(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer& offer) -{ - if (!peer) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "peer not found"); - return; - } - - bool result = otcClient_->acceptOffer(peer, offer); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "accept offer failed"); - return; - } -} - -void ChatOTCHelper::onOtcResponseUpdate(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer& offer) -{ - if (!peer) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "peer not found"); - return; - } - - bool result = otcClient_->updateOffer(peer, offer); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "update offer failed"); - return; - } -} - -void ChatOTCHelper::onOtcResponseReject(const bs::network::otc::PeerPtr &peer) -{ - if (!peer) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "peer not found"); - return; - } - - bool result = otcClient_->pullOrReject(peer); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "reject offer failed"); - return; - } -} - -void ChatOTCHelper::onOtcQuoteRequestSubmit(const bs::network::otc::QuoteRequest &request) -{ - bool result = otcClient_->sendQuoteRequest(request); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "sending quote request failed"); - return; - } -} - -void ChatOTCHelper::onOtcQuoteResponseSubmit(const bs::network::otc::PeerPtr &peer, const bs::network::otc::QuoteResponse &response) -{ - if (!peer) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "peer not found"); - return; - } - - bool result = otcClient_->sendQuoteResponse(peer, response); - if (!result) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "sending response failed"); - return; - } -} - -void ChatOTCHelper::onMessageArrived(const Chat::MessagePtrList& messagePtr) -{ - for (const auto &msg : messagePtr) { - if (msg->partyId() == Chat::OtcRoomName) { - auto data = OtcUtils::deserializePublicMessage(msg->messageText()); - if (!data.empty()) { - otcClient_->processPublicMessage(msg->timestamp(), msg->senderHash(), data); - } - } else if (msg->partyMessageState() == Chat::SENT && msg->senderHash() != otcClient_->ownContactId()) { - auto connIt = connectedContacts_.find(msg->senderHash()); - if (connIt == connectedContacts_.end()) { - continue; - } - - auto data = OtcUtils::deserializeMessage(msg->messageText()); - if (!data.empty()) { - otcClient_->processContactMessage(msg->senderHash(), data); - } - } - } -} - -void ChatOTCHelper::onPartyStateChanged(const Chat::ClientPartyPtr& clientPartyPtr) -{ - const std::string& contactId = clientPartyPtr->userHash(); - auto connIt = connectedContacts_.find(contactId); - if (clientPartyPtr->clientStatus() == Chat::ONLINE && connIt == connectedContacts_.end()) { - otcClient_->contactConnected(contactId); - connectedContacts_.insert(contactId); - } else if (clientPartyPtr->clientStatus() == Chat::OFFLINE && connIt != connectedContacts_.end()) { - otcClient_->contactDisconnected(contactId); - connectedContacts_.erase(connIt); - } -} diff --git a/BlockSettleUILib/ChatUI/ChatOTCHelper.h b/BlockSettleUILib/ChatUI/ChatOTCHelper.h deleted file mode 100644 index accae997a..000000000 --- a/BlockSettleUILib/ChatUI/ChatOTCHelper.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATOTCHELPER_H -#define CHATOTCHELPER_H - -#include -#include -#include "OtcTypes.h" -#include "ChatProtocol/Message.h" -#include "ChatProtocol/ClientParty.h" - -namespace spdlog { - class logger; -} - -namespace bs { - namespace sync { - class WalletsManager; - } - namespace network { - namespace otc { - enum class Env : int; - struct Offer; - struct Peer; - struct PeerId; - struct QuoteRequest; - struct QuoteResponse; - } - } - class UTXOReservationManager; -} - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - } - } -} - -class ApplicationSettings; -class ArmoryConnection; -class AuthAddressManager; -class OtcClient; -class WalletSignerContainer; - -class ChatOTCHelper : public QObject { - Q_OBJECT -public: - ChatOTCHelper(QObject* parent = nullptr); - ~ChatOTCHelper() override = default; - - void init(bs::network::otc::Env env - , const std::shared_ptr& loggerPtr - , const std::shared_ptr& walletsMgr - , const std::shared_ptr& armory - , const std::shared_ptr& signContainer - , const std::shared_ptr &authAddressManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& applicationSettings); - - OtcClient* client() const; - - void setCurrentUserId(const std::string& ownUserId); - - void setGlobalOTCEntryTimeStamp(QDateTime timeStamp); - QDateTime selectedGlobalOTCEntryTimeStamp() const; - -public slots: - void onLogout(); - void onProcessOtcPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response); - - void onOtcRequestSubmit(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer& offer); - void onOtcPullOrReject(const bs::network::otc::PeerPtr &peer); - void onOtcResponseAccept(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer& offer); - void onOtcResponseUpdate(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer& offer); - void onOtcResponseReject(const bs::network::otc::PeerPtr &peer); - - void onOtcQuoteRequestSubmit(const bs::network::otc::QuoteRequest &request); - void onOtcQuoteResponseSubmit(const bs::network::otc::PeerPtr &peer, const bs::network::otc::QuoteResponse &response); - - void onMessageArrived(const Chat::MessagePtrList& messagePtr); - void onPartyStateChanged(const Chat::ClientPartyPtr& clientPartyPtr); - -private: - OtcClient* otcClient_{}; - std::set connectedContacts_; - std::shared_ptr loggerPtr_; - QDateTime selectedGlobalEntryTimeStamp_{}; -}; - -#endif // CHATOTCHELPER_H diff --git a/BlockSettleUILib/ChatUI/ChatPartiesSortProxyModel.cpp b/BlockSettleUILib/ChatUI/ChatPartiesSortProxyModel.cpp deleted file mode 100644 index a71708d47..000000000 --- a/BlockSettleUILib/ChatUI/ChatPartiesSortProxyModel.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatPartiesSortProxyModel.h" - -using namespace bs; - -ChatPartiesSortProxyModel::ChatPartiesSortProxyModel(ChatPartiesTreeModelPtr sourceModel, QObject *parent /*= nullptr*/) - : QSortFilterProxyModel(parent) - , sourceModel_(std::move(sourceModel)) -{ - setDynamicSortFilter(true); - setSourceModel(sourceModel_.get()); -} - -PartyTreeItem* ChatPartiesSortProxyModel::getInternalData(const QModelIndex& index) const -{ - if (!index.isValid()) { - return {}; - } - - const auto& sourceIndex = mapToSource(index); - return static_cast(sourceIndex.internalPointer()); -} - -const std::string& ChatPartiesSortProxyModel::currentUser() const -{ - return sourceModel_->currentUser(); -} - -Qt::ItemFlags ChatPartiesSortProxyModel::flags(const QModelIndex& index) const -{ - if (!index.isValid()) { - return Qt::NoItemFlags; - } - - PartyTreeItem* treeItem = getInternalData(index); - if (!treeItem) { - return Qt::NoItemFlags; - } - - if (UI::ElementType::Container != treeItem->modelType()) { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } - - return Qt::NoItemFlags; -} - -QModelIndex ChatPartiesSortProxyModel::getProxyIndexById(const std::string& partyId) const -{ - const QModelIndex sourceIndex = sourceModel_->getPartyIndexById(partyId); - return mapFromSource(sourceIndex); -} - -QModelIndex ChatPartiesSortProxyModel::getOTCGlobalRoot() const -{ - const QModelIndex sourceOtcIndex = sourceModel_->getOTCGlobalRoot(); - return mapFromSource(sourceOtcIndex); -} - -bool ChatPartiesSortProxyModel::filterAcceptsRow(int row, const QModelIndex& parent) const -{ - Q_ASSERT(sourceModel_); - - auto index = sourceModel_->index(row, 0, parent); - if (!index.isValid()) { - return false; - } - - PartyTreeItem* item = static_cast(index.internalPointer()); - if (!item) { - return false; - } - - // return true if you want to display tree item - switch (item->modelType()) { - case UI::ElementType::Party: - return true; - case UI::ElementType::Container: { - if (item->childCount() == 0 && item->data().toString() == ChatModelNames::ContainerTabOTCIdentifier) { - return false; - } - return true; - } - default: - return false; - } -} - -bool ChatPartiesSortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - if (!left.isValid() || !right.isValid()) { - return QSortFilterProxyModel::lessThan(left, right); - } - - PartyTreeItem* itemLeft = static_cast(left.internalPointer()); - PartyTreeItem* itemRight = static_cast(right.internalPointer()); - - if (!itemLeft || !itemRight) { - return QSortFilterProxyModel::lessThan(left, right); - } - - if (itemLeft->modelType() == itemRight->modelType()) { - if (itemLeft->modelType() == UI::ElementType::Party) { - Chat::ClientPartyPtr leftParty = itemLeft->data().value(); - Chat::ClientPartyPtr rightParty = itemRight->data().value(); - return leftParty->displayName() < rightParty->displayName(); - } - - if (itemLeft->modelType() == UI::ElementType::Container) { - return itemLeft->childNumber() < itemRight->childNumber(); - } - } - - return QSortFilterProxyModel::lessThan(left, right); -} diff --git a/BlockSettleUILib/ChatUI/ChatPartiesSortProxyModel.h b/BlockSettleUILib/ChatUI/ChatPartiesSortProxyModel.h deleted file mode 100644 index 5db53f4f8..000000000 --- a/BlockSettleUILib/ChatUI/ChatPartiesSortProxyModel.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATPARTYSORTPROXYMODEL_H -#define CHATPARTYSORTPROXYMODEL_H - -#include "ChatPartiesTreeModel.h" -#include - -class ChatPartiesSortProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - explicit ChatPartiesSortProxyModel(ChatPartiesTreeModelPtr sourceModel, QObject *parent = nullptr); - - PartyTreeItem* getInternalData(const QModelIndex& index) const; - - const std::string& currentUser() const; - Qt::ItemFlags flags(const QModelIndex& index) const override; - - QModelIndex getProxyIndexById(const std::string& partyId) const; - QModelIndex getOTCGlobalRoot() const; - -protected: - - bool filterAcceptsRow(int row, const QModelIndex& parent) const override; - bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; - -private: - ChatPartiesTreeModelPtr sourceModel_; -}; - -using ChatPartiesSortProxyModelPtr = std::shared_ptr; - -#endif // CHATPARTYSORTPROXYMODEL_H diff --git a/BlockSettleUILib/ChatUI/ChatPartiesTreeModel.cpp b/BlockSettleUILib/ChatUI/ChatPartiesTreeModel.cpp deleted file mode 100644 index f4df3d831..000000000 --- a/BlockSettleUILib/ChatUI/ChatPartiesTreeModel.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatPartiesTreeModel.h" - -#include "OtcClient.h" - -using namespace bs; -using namespace bs::network; - -namespace { - const auto kTogglingIntervalMs = std::chrono::milliseconds(250); -} - -ChatPartiesTreeModel::ChatPartiesTreeModel(const Chat::ChatClientServicePtr& chatClientServicePtr, OtcClient *otcClient, QObject* parent) - : QAbstractItemModel(parent) - , chatClientServicePtr_(chatClientServicePtr) - , otcClient_(otcClient) -{ - rootItem_ = new PartyTreeItem({}, UI::ElementType::Root); - - // #flickeringOTC Flickering otc awaiting messages - disabled for now - //otcWatchToggling_.setInterval(kTogglingIntervalMs); - //connect(&otcWatchToggling_, &QTimer::timeout, this, &ChatPartiesTreeModel::onUpdateOTCAwaitingColor); - //otcWatchToggling_.start(); -} - -ChatPartiesTreeModel::~ChatPartiesTreeModel() = default; - -void ChatPartiesTreeModel::onPartyModelChanged() -{ - Chat::ClientPartyModelPtr clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - - // Save unseen state first - const auto reusableItemData = collectReusableData(rootItem_); - - beginResetModel(); - - rootItem_->removeAll(); - otcWatchIndx_.clear(); - - std::unique_ptr globalSection = std::make_unique(ChatModelNames::ContainerTabGlobal, UI::ElementType::Container, rootItem_); - std::unique_ptr otcGlobalSection = std::make_unique(ChatModelNames::ContainerTabOTCIdentifier, UI::ElementType::Container, rootItem_); - std::unique_ptr privateSection = std::make_unique(ChatModelNames::ContainerTabPrivate, UI::ElementType::Container, rootItem_); - std::unique_ptr requestSection = std::make_unique(ChatModelNames::ContainerTabContactRequest, UI::ElementType::Container, rootItem_); - - auto insertChild = [](PartyTreeItem* section, QVariant stored) -> PartyTreeItem* { - std::unique_ptr partyTreeItem = std::make_unique(stored, UI::ElementType::Party, section); - PartyTreeItem* pTreeItem = partyTreeItem.get(); - section->insertChildren(std::move(partyTreeItem)); - return pTreeItem; - }; - - const auto idPartyList = clientPartyModelPtr->getIdPartyList(); - const auto clientPartyPtrList = clientPartyModelPtr->getClientPartyListFromIdPartyList(idPartyList); - - for (const auto& clientPartyPtr : clientPartyPtrList) { - assert(clientPartyPtr); - - QVariant stored; - stored.setValue(clientPartyPtr); - - if (clientPartyPtr->isGlobalOTC()) { - insertChild(otcGlobalSection.get(), stored); - } - else if (clientPartyPtr->isGlobal()) { - insertChild(globalSection.get(), stored); - } - else if (clientPartyPtr->isPrivateStandard()) { - if (clientPartyPtr->partyState() == Chat::PartyState::REJECTED) { - continue; - } - - PartyTreeItem* parentSection = clientPartyPtr->partyState() == Chat::PartyState::INITIALIZED ? privateSection.get() : requestSection.get(); - PartyTreeItem* newChild = insertChild(parentSection, stored); - - assert(newChild); - auto it = reusableItemData.find(clientPartyPtr->id()); - if (it != reusableItemData.end()) { - newChild->applyReusableData(it.value()); - } - } - } - - rootItem_->insertChildren(std::move(globalSection)); - rootItem_->insertChildren(std::move(otcGlobalSection)); - rootItem_->insertChildren(std::move(privateSection)); - rootItem_->insertChildren(std::move(requestSection)); - - endResetModel(); - emit restoreSelectedIndex(); - - if (!reusableItemData.isEmpty()) { - resetOTCUnseen({}); - } - - onGlobalOTCChanged(reusableItemData); -} - -void ChatPartiesTreeModel::onGlobalOTCChanged(QMap reusableItemData /* = {} */) -{ - QModelIndex otcGlobalModelIndex = getOTCGlobalRoot(); - if (!otcGlobalModelIndex.isValid()) { - return; - } - - PartyTreeItem* otcParty = static_cast(otcGlobalModelIndex.internalPointer()); - if (reusableItemData.isEmpty()) { - reusableItemData = collectReusableData(otcParty); - } - - resetOTCUnseen(otcGlobalModelIndex, false, false); - if (otcParty->childCount() > 0) { - beginRemoveRows(otcGlobalModelIndex, 0, otcParty->childCount() - 1); - otcParty->removeAll(); - endRemoveRows(); - } - - auto fAddOtcParty = [this, &reusableItemData](const bs::network::otc::PeerPtr &peer, std::unique_ptr& section, otc::PeerType peerType) { - Chat::ClientPartyModelPtr clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - Chat::ClientPartyPtr otcPartyPtr = clientPartyModelPtr->getOtcPartyForUsers(currentUser(), peer->contactId); - if (!otcPartyPtr) { - return; - } - QVariant stored; - stored.setValue(otcPartyPtr); - - std::unique_ptr otcItem = std::make_unique(stored, UI::ElementType::Party, section.get()); - otcItem->peerType = peerType; - - auto it = reusableItemData.find(otcPartyPtr->id()); - if (it != reusableItemData.end()) { - otcItem->applyReusableData(it.value()); - } - - section->insertChildren(std::move(otcItem)); - }; - - beginInsertRows(otcGlobalModelIndex, 0, 1); - - std::unique_ptr sentSection = std::make_unique(ChatModelNames::TabOTCSentRequest, UI::ElementType::Container, otcParty); - for (const auto &peer : otcClient_->requests()) { - // Show only responded requests here - if (peer->state != otc::State::Idle) { - fAddOtcParty(peer, sentSection, otc::PeerType::Request); - } - } - otcParty->insertChildren(std::move(sentSection)); - - std::unique_ptr responseSection = std::make_unique(ChatModelNames::TabOTCReceivedResponse, UI::ElementType::Container, otcParty); - for (const auto &peer : otcClient_->responses()) { - fAddOtcParty(peer, responseSection, otc::PeerType::Response); - } - - otcParty->insertChildren(std::move(responseSection)); - - endInsertRows(); - emit restoreSelectedIndex(); - if (!reusableItemData.isEmpty()) { - resetOTCUnseen(otcGlobalModelIndex, true, false); - } -} - -void ChatPartiesTreeModel::onCleanModel() -{ - beginResetModel(); - rootItem_->removeAll(); - endResetModel(); -} - -void ChatPartiesTreeModel::onPartyStatusChanged(const Chat::ClientPartyPtr& clientPartyPtr) -{ - const QModelIndex partyIndex = getPartyIndexById(clientPartyPtr->id()); - - if (partyIndex.isValid()) { - emit dataChanged(partyIndex, partyIndex); - } -} - -void ChatPartiesTreeModel::onIncreaseUnseenCounter(const std::string& partyId, int newMessageCount, bool isUnseenOTCMessage /* = false */) -{ - const QModelIndex partyIndex = getPartyIndexById(partyId); - if (!partyIndex.isValid()) { - return; - } - - PartyTreeItem* partyItem = static_cast(partyIndex.internalPointer()); - partyItem->increaseUnseenCounter(newMessageCount); - - if (isUnseenOTCMessage) { - otcWatchIndx_.insert({ partyIndex }); - partyItem->enableOTCToggling(isUnseenOTCMessage); - } -} - -void ChatPartiesTreeModel::onDecreaseUnseenCounter(const std::string& partyId, int seenMessageCount) -{ - const QModelIndex partyIndex = getPartyIndexById(partyId); - if (!partyIndex.isValid()) { - return; - } - - PartyTreeItem* partyItem = static_cast(partyIndex.internalPointer()); - partyItem->decreaseUnseenCounter(seenMessageCount); - - - if (partyItem->isOTCTogglingMode()) { - partyItem->enableOTCToggling(false); - otcWatchIndx_.remove({ partyIndex }); - } -} - -void ChatPartiesTreeModel::onUpdateOTCAwaitingColor() -{ - if (otcWatchIndx_.isEmpty()) { - return; - } - - for (const auto& index : otcWatchIndx_) { - if (index.isValid()) { - PartyTreeItem* partyItem = static_cast(index.internalPointer()); - partyItem->changeOTCToggleState(); - - emit dataChanged(index, index, { Qt::DecorationRole }); - } - } -} - -const QModelIndex ChatPartiesTreeModel::getPartyIndexById(const std::string& partyId, const QModelIndex parent) const -{ - PartyTreeItem* parentItem = nullptr; - if (parent.isValid()) { - parentItem = static_cast(parent.internalPointer()); - } - else { - parentItem = rootItem_; - } - Q_ASSERT(parentItem); - - QList> itemsToCheck; - itemsToCheck.push_back({ parent , parentItem }); - - QPair currentPair; - while (!itemsToCheck.isEmpty()) { - currentPair = itemsToCheck[0]; - itemsToCheck.pop_front(); - - QModelIndex iterModelIndex = currentPair.first; - PartyTreeItem* iterItem= currentPair.second; - - - for (int iChild = 0; iChild < iterItem->childCount(); ++iChild) { - auto* child = iterItem->child(iChild); - - auto childIndex = index(iChild, 0, iterModelIndex); - if (child->modelType() == UI::ElementType::Container && child->data().canConvert()) { - if (child->data().toString().toStdString() == partyId) { - return childIndex; - } - } - else if (child->modelType() == UI::ElementType::Party && child->data().canConvert()) { - const Chat::ClientPartyPtr clientPtr = child->data().value(); - - if (!clientPtr) { - return {}; - } - - if (clientPtr->id() == partyId) { - return childIndex; - } - } - - if (child->childCount() != 0) { - itemsToCheck.push_back({ childIndex , child }); - } - } - - } - - return {}; -} - -PartyTreeItem* ChatPartiesTreeModel::getItem(const QModelIndex& index) const -{ - if (index.isValid()) { - PartyTreeItem* item = static_cast(index.internalPointer()); - if (item) { - return item; - } - } - - return rootItem_; -} - -void ChatPartiesTreeModel::forAllPartiesInModel(PartyTreeItem* parent, - std::function&& applyFunc) const -{ - QList itemsToCheck; - itemsToCheck.push_back(parent ? parent : rootItem_); - - while (!itemsToCheck.isEmpty()) { - PartyTreeItem* item = itemsToCheck[0]; - itemsToCheck.pop_front(); - - applyFunc(item); - - for (int i = 0; i < item->childCount(); ++i) { - itemsToCheck.push_back(item->child(i)); - } - } -} - -void ChatPartiesTreeModel::forAllIndexesInModel(const QModelIndex& parentIndex, - std::function&& applyFunc) const -{ - PartyTreeItem* item = nullptr; - if (!parentIndex.isValid()) { - item = rootItem_; - } - else { - item = static_cast(parentIndex.internalPointer()); - } - - QList itemsToCheck; - if (parentIndex.isValid()) { - itemsToCheck.push_back(parentIndex); - } - else { // root item - for (int i = 0; i < rootItem_->childCount(); ++i) { - itemsToCheck.push_back(index(i, 0)); - } - } - - while (!itemsToCheck.isEmpty()) { - QModelIndex itemIndex = std::move(itemsToCheck[0]); - itemsToCheck.pop_front(); - - applyFunc(itemIndex); - - item = static_cast(itemIndex.internalPointer()); - for (int i = 0; i < item->childCount(); ++i) { - itemsToCheck.push_back(index(i, 0, itemIndex)); - } - } -} - -QMap ChatPartiesTreeModel::collectReusableData(PartyTreeItem* parent) -{ - QMap reusableData; - forAllPartiesInModel(parent, [&](const PartyTreeItem* party) { - if (party->modelType() != UI::ElementType::Party) { - return; - } - - const Chat::ClientPartyPtr clientPtr = party->data().value(); - - if (party->unseenCount() != 0) { - reusableData.insert(clientPtr->id(), party->generateReusableData()); - } - }); - - return reusableData; -} - -void ChatPartiesTreeModel::resetOTCUnseen(const QModelIndex& parentIndex, - bool isAddChildren /*= true*/, bool isClearAll /*= true*/) -{ - if (isClearAll) { - otcWatchIndx_.clear(); - } - - forAllIndexesInModel(parentIndex, [&](const QModelIndex& index) { - PartyTreeItem* item = static_cast(index.internalPointer()); - if (item->modelType() != UI::ElementType::Party) { - return; - } - - if (item->isOTCTogglingMode()) { - if (isAddChildren) { - otcWatchIndx_.insert({ index }); - } - else { - otcWatchIndx_.remove({ index }); - } - - } - }); -} - -QModelIndex ChatPartiesTreeModel::getOTCGlobalRoot() const -{ - for (int iContainer = 0; iContainer < rootItem_->childCount(); ++iContainer) { - auto* container = rootItem_->child(iContainer); - - Q_ASSERT(container->data().canConvert()); - if (container->data().toString() != ChatModelNames::ContainerTabOTCIdentifier) { - continue; - } - - for (int iParty = 0; iParty < container->childCount(); ++iParty) { - const PartyTreeItem* party = container->child(iParty); - if (party->data().canConvert()) { - const Chat::ClientPartyPtr clientPtr = party->data().value(); - if (clientPtr->isGlobalOTC()) { - return index(iParty, 0, index(iContainer, 0)); - } - } - } - - return {}; - } - - return {}; -} - -QVariant ChatPartiesTreeModel::data(const QModelIndex& index, int role) const -{ - if (!index.isValid()) { - return QVariant(); - } - - if (role != Qt::DisplayRole) { - return QVariant(); - } - - PartyTreeItem* item = getItem(index); - - if (item->modelType() == UI::ElementType::Container) { - if (item->data().toString() == ChatModelNames::ContainerTabOTCIdentifier) { - return { ChatModelNames::ContainerTabOTCDisplayName }; - } - - return item->data(); - } - else if (item->modelType() == UI::ElementType::Party) { - Q_ASSERT(item->data().canConvert()); - Chat::ClientPartyPtr clientPartyPtr = item->data().value(); - if (!clientPartyPtr) { - return QVariant(); - } - - if (clientPartyPtr->isGlobalOTC()) { - return { ChatModelNames::PrivatePartyGlobalOTCDisplayName }; - } - return QString::fromStdString(clientPartyPtr->displayName()); - } - - return {}; -} - -QModelIndex ChatPartiesTreeModel::index(int row, int column, const QModelIndex& parent) const -{ - if (parent.isValid() && parent.column() != 0) { - return QModelIndex(); - } - - if (!hasIndex(row, column, parent)) { - return QModelIndex(); - } - - PartyTreeItem* parentItem = getItem(parent); - Q_ASSERT(parentItem); - - PartyTreeItem* childItem = parentItem->child(row); - if (childItem) { - return createIndex(row, column, childItem); - } - - return QModelIndex(); -} - -QModelIndex ChatPartiesTreeModel::parent(const QModelIndex& index) const -{ - if (!index.isValid()) { - return QModelIndex(); - } - - PartyTreeItem* childItem = getItem(index); - PartyTreeItem* parentItem = childItem->parent(); - - if (parentItem == rootItem_) { - return QModelIndex(); - } - - return createIndex(parentItem->childNumber(), 0, parentItem); -} - -int ChatPartiesTreeModel::rowCount(const QModelIndex& parent) const -{ - PartyTreeItem* parentItem = getItem(parent); - - return parentItem->childCount(); -} - -int ChatPartiesTreeModel::columnCount(const QModelIndex& parent) const -{ - return rootItem_->columnCount(); -} - -const std::string& ChatPartiesTreeModel::currentUser() const -{ - const auto chatModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - return chatModelPtr->ownUserName(); -} diff --git a/BlockSettleUILib/ChatUI/ChatPartiesTreeModel.h b/BlockSettleUILib/ChatUI/ChatPartiesTreeModel.h deleted file mode 100644 index 03560d2c9..000000000 --- a/BlockSettleUILib/ChatUI/ChatPartiesTreeModel.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATPARTYLISTMODEL_H -#define CHATPARTYLISTMODEL_H - -#include -#include "ChatProtocol/ChatClientService.h" -#include "PartyTreeItem.h" - -class OtcClient; -class ChatPartiesTreeModel : public QAbstractItemModel -{ - Q_OBJECT -public: - ChatPartiesTreeModel(const Chat::ChatClientServicePtr& chatClientServicePtr, OtcClient *otcClient - , QObject* parent = nullptr); - ~ChatPartiesTreeModel() override; - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex& index) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - int columnCount(const QModelIndex& parent = QModelIndex()) const override; - const std::string& currentUser() const; - - QModelIndex getOTCGlobalRoot() const; - const QModelIndex getPartyIndexById(const std::string& partyId, const QModelIndex parent = {}) const; - -signals: - void restoreSelectedIndex(); - -public slots: - void onPartyModelChanged(); - void onGlobalOTCChanged(QMap reusableItemData = {}); - void onCleanModel(); - void onPartyStatusChanged(const Chat::ClientPartyPtr& clientPartyPtr); - void onIncreaseUnseenCounter(const std::string& partyId, int newMessageCount, bool isUnseenOTCMessage = false); - void onDecreaseUnseenCounter(const std::string& partyId, int seenMessageCount); - -private slots: - void onUpdateOTCAwaitingColor(); - -private: - PartyTreeItem* getItem(const QModelIndex& index) const; - void forAllPartiesInModel(PartyTreeItem* parent, std::function&& applyFunc) const; - void forAllIndexesInModel(const QModelIndex& parent, std::function&& applyFunc) const; - QMap collectReusableData(PartyTreeItem* parent); - void resetOTCUnseen(const QModelIndex& parentIndex, bool isAddChildren = true, bool isClearAll = true); - - PartyTreeItem* rootItem_{}; - - Chat::ChatClientServicePtr chatClientServicePtr_; - OtcClient* otcClient_{}; - - QSet otcWatchIndx_; - QTimer otcWatchToggling_; -}; - -using ChatPartiesTreeModelPtr = std::shared_ptr; - -namespace ChatModelNames { - const QString ContainerTabGlobal = QObject::tr("Public"); - const QString ContainerTabPrivate = QObject::tr("Private"); - const QString ContainerTabContactRequest = QObject::tr("Contact request"); - - const QString ContainerTabOTCIdentifier = QObject::tr("C_OTC"); - const QString ContainerTabOTCDisplayName = QObject::tr("OTC"); - - const QString PrivatePartyGlobalOTCDisplayName = QObject::tr("Global"); - - // OTC - const QString TabOTCSentRequest = QObject::tr("Submitted quotes"); - const QString TabOTCReceivedResponse = QObject::tr("Received quotes"); -} - -#endif // CHATPARTYLISTMODEL_H diff --git a/BlockSettleUILib/ChatUI/ChatSearchLineEdit.cpp b/BlockSettleUILib/ChatUI/ChatSearchLineEdit.cpp deleted file mode 100644 index 226bcc48b..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchLineEdit.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatSearchLineEdit.h" - -#include -ChatSearchLineEdit::ChatSearchLineEdit(QWidget *parent) - : QLineEdit(parent) - , resetOnNextInput_(false) -{ - connect(this, &QLineEdit::textChanged, this, &ChatSearchLineEdit::onTextChanged); -} - -/* -void ChatSearchLineEdit::setActionsHandler(std::shared_ptr handler) -{ - handler_ = handler; -} -*/ - -void ChatSearchLineEdit::setResetOnNextInput(bool value) -{ - resetOnNextInput_ = value; -} - -void ChatSearchLineEdit::onTextChanged(const QString &text) -{ -/* - if (text.isEmpty() && handler_) { - handler_->onActionResetSearch(); - } -*/ -} - - -ChatSearchLineEdit::~ChatSearchLineEdit() = default; - -void ChatSearchLineEdit::keyPressEvent(QKeyEvent * e) -{ - switch (e->key()) { - case Qt::Key_Enter: //Qt::Key_Enter - Numpad Enter key - case Qt::Key_Return: //Qt::Key_Return - Main Enter key - { - emit keyEnterPressed(); - return e->ignore(); - } - case Qt::Key_Down: //Qt::Key_Down - For both standalone and Numpad arrow down keys - emit keyDownPressed(); - break; - case Qt::Key_Escape: - emit keyEscapePressed(); - break; - default: - break; - } - return QLineEdit::keyPressEvent(e); -} diff --git a/BlockSettleUILib/ChatUI/ChatSearchLineEdit.h b/BlockSettleUILib/ChatUI/ChatSearchLineEdit.h deleted file mode 100644 index 25307839c..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchLineEdit.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHAT_SEARCH_LINE_EDIT_H -#define CHAT_SEARCH_LINE_EDIT_H - -#include -#include - -class ChatSearchLineEdit : - public QLineEdit -{ - Q_OBJECT -public: - ChatSearchLineEdit(QWidget *parent = nullptr); - ~ChatSearchLineEdit() override; - //void setActionsHandler(std::shared_ptr handler); - void setResetOnNextInput(bool value); -private: - void onTextChanged(const QString& text); -private: - //std::shared_ptr handler_; - bool resetOnNextInput_; - - // QWidget interface -protected: - void keyPressEvent(QKeyEvent *event) override; - -signals: - void keyDownPressed(); - void keyEnterPressed(); - void keyEscapePressed(); -}; - -#endif //CHAT_SEARCH_LINE_EDIT_H diff --git a/BlockSettleUILib/ChatUI/ChatSearchListVew.cpp b/BlockSettleUILib/ChatUI/ChatSearchListVew.cpp deleted file mode 100644 index 6247c0704..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchListVew.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatSearchListVew.h" - -#include - -ChatSearchListVew::ChatSearchListVew(QWidget *parent) : QTreeView(parent) -{ - setHeaderHidden(true); - setRootIsDecorated(false); - setSelectionMode(QAbstractItemView::SingleSelection); - setContextMenuPolicy(Qt::CustomContextMenu); -} - -void ChatSearchListVew::keyPressEvent(QKeyEvent *event) -{ - switch (event->key()) { - case Qt::Key_Escape: - emit leaveWithCloseRequired(); - break; - case Qt::Key_Up: - if (currentIndex().row() == 0) { - emit leaveRequired(); - } - break; - default: - break; - } - return QTreeView::keyPressEvent(event); -} diff --git a/BlockSettleUILib/ChatUI/ChatSearchListVew.h b/BlockSettleUILib/ChatUI/ChatSearchListVew.h deleted file mode 100644 index 300d161dc..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchListVew.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATSEARCHLISTVEW_H -#define CHATSEARCHLISTVEW_H - -#include - -class ChatSearchListVew : public QTreeView -{ - Q_OBJECT -public: - explicit ChatSearchListVew(QWidget *parent = nullptr); - -protected: - void keyPressEvent(QKeyEvent *event) override; - -signals: - void leaveRequired(); - void leaveWithCloseRequired(); -}; - -#endif // CHATSEARCHLISTVEW_H diff --git a/BlockSettleUILib/ChatUI/ChatSearchListViewItemStyle.cpp b/BlockSettleUILib/ChatUI/ChatSearchListViewItemStyle.cpp deleted file mode 100644 index 877270794..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchListViewItemStyle.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatSearchListViewItemStyle.h" - -ChatSearchListViewItemStyle::ChatSearchListViewItemStyle(QWidget *parent) - : QWidget(parent) - , colorContactUnknown_(Qt::gray) - , colorContactAccepted_(Qt::cyan) - , colorContactIncoming_(Qt::darkYellow) - , colorContactOutgoing_(Qt::darkGreen) - , colorContactRejected_(Qt::red) -{ -} diff --git a/BlockSettleUILib/ChatUI/ChatSearchListViewItemStyle.h b/BlockSettleUILib/ChatUI/ChatSearchListViewItemStyle.h deleted file mode 100644 index c5e9b8e10..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchListViewItemStyle.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATSEARCHLISTVIEWITEMSTYLE_H -#define CHATSEARCHLISTVIEWITEMSTYLE_H - -#include - -class ChatSearchListViewItemStyle : public QWidget -{ - Q_OBJECT - Q_PROPERTY(QColor color_contact_unknown MEMBER colorContactUnknown_) - Q_PROPERTY(QColor color_contact_accepted MEMBER colorContactAccepted_) - Q_PROPERTY(QColor color_contact_incoming MEMBER colorContactIncoming_) - Q_PROPERTY(QColor color_contact_outgoing MEMBER colorContactOutgoing_) - Q_PROPERTY(QColor color_contact_rejected MEMBER colorContactRejected_) - -public: - explicit ChatSearchListViewItemStyle(QWidget *parent = nullptr); - -private: - QColor colorContactUnknown_; - QColor colorContactAccepted_; - QColor colorContactIncoming_; - QColor colorContactOutgoing_; - QColor colorContactRejected_; - -}; - -#endif // CHATSEARCHLISTVIEWITEMSTYLE_H diff --git a/BlockSettleUILib/ChatUI/ChatSearchPopup.cpp b/BlockSettleUILib/ChatUI/ChatSearchPopup.cpp deleted file mode 100644 index 42a0c5e11..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchPopup.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatSearchPopup.h" -#include "ui_ChatSearchPopup.h" - -#include -#include -#include - -ChatSearchPopup::ChatSearchPopup(QWidget *parent) : - QWidget(parent), - userID_(), - ui_(new Ui::ChatSearchPopup) -{ - ui_->setupUi(this); - - ui_->chatSearchPopupLabel->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui_->chatSearchPopupLabel, &QLabel::customContextMenuRequested, this, &ChatSearchPopup::onShowMenu); - - searchPopupMenu_ = new QMenu(this); - userContactAction_ = searchPopupMenu_->addAction(QString()); -} - -ChatSearchPopup::~ChatSearchPopup() -{ - searchPopupMenu_->deleteLater(); - delete ui_; -} - -void ChatSearchPopup::setUserID(const QString &userID) -{ - userID_ = userID; - - if (userID_.isEmpty()) { - ui_->chatSearchPopupLabel->setText(tr("User not found")); - } - else { - ui_->chatSearchPopupLabel->setText(userID_); - } -} - -void ChatSearchPopup::setUserIsInContacts(const bool &isInContacts) -{ - isInContacts_ = isInContacts; -} - -void ChatSearchPopup::onShowMenu(const QPoint &pos) -{ - if (!userID_.isEmpty()) { - - userContactAction_->disconnect(); - - if (!isInContacts_) { - userContactAction_->setText(tr("Add to contacts")); - userContactAction_->setStatusTip(QObject::tr("Click to add user to contact list")); - connect(userContactAction_, &QAction::triggered, - [this](bool) { emit sendFriendRequest(ui_->chatSearchPopupLabel->text()); } - ); - } - else { - userContactAction_->setText(tr("Remove from contacts")); - userContactAction_->setStatusTip(QObject::tr("Click to remove user from contact list")); - connect(userContactAction_, &QAction::triggered, - [this](bool) { emit removeFriendRequest(ui_->chatSearchPopupLabel->text()); } - ); - } - - searchPopupMenu_->exec(mapToGlobal(pos)); - } -} - -void ChatSearchPopup::setCustomPosition(const QWidget *widget, const int &moveX, const int &moveY) -{ - QPoint newPos = widget->mapToGlobal(widget->rect().bottomLeft()); - newPos.setX(newPos.x() + moveX); - newPos.setY(newPos.y() + moveY); - this->move(newPos); -} diff --git a/BlockSettleUILib/ChatUI/ChatSearchPopup.h b/BlockSettleUILib/ChatUI/ChatSearchPopup.h deleted file mode 100644 index 8b4deeace..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchPopup.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATSEARCHPOPUP_H -#define CHATSEARCHPOPUP_H - -#include - -namespace Ui { -class ChatSearchPopup; -} - -class QMenu; - -class ChatSearchPopup : public QWidget -{ - Q_OBJECT - -public: - explicit ChatSearchPopup(QWidget *parent = nullptr); - ~ChatSearchPopup(); - - void setUserID(const QString &userID); - void setUserIsInContacts(const bool &isInContacts); - void setCustomPosition(const QWidget *widget, const int &moveX, const int &moveY); - -signals: - void sendFriendRequest(const QString &userID); - void removeFriendRequest(const QString &userID); - -private slots: - void onShowMenu(const QPoint &pos); - -private: - Ui::ChatSearchPopup *ui_; - QMenu *searchPopupMenu_; - QString userID_; - bool isInContacts_; - QAction *userContactAction_; -}; - -#endif // CHATSEARCHPOPUP_H diff --git a/BlockSettleUILib/ChatUI/ChatSearchPopup.ui b/BlockSettleUILib/ChatUI/ChatSearchPopup.ui deleted file mode 100644 index 1c169c484..000000000 --- a/BlockSettleUILib/ChatUI/ChatSearchPopup.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - - ChatSearchPopup - - - - 0 - 0 - 400 - 300 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - 0 - - - - - - - - - - - - - - - - - - - - diff --git a/BlockSettleUILib/ChatUI/ChatUserListTreeView.cpp b/BlockSettleUILib/ChatUI/ChatUserListTreeView.cpp deleted file mode 100644 index 123def1b9..000000000 --- a/BlockSettleUILib/ChatUI/ChatUserListTreeView.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include -#include -#include -#include "ChatUserListTreeView.h" -#include "ChatClientUsersViewItemDelegate.h" -#include "BSMessageBox.h" -#include "EditContactDialog.h" - -using namespace bs; - -namespace { - // Translation - const QString contextMenuRemoveUser = QObject::tr("Remove from contacts"); - const QString contextMenuEditUser = QObject::tr("Edit contact"); - const QString contextMenuAcceptRequest = QObject::tr("Accept friend request"); - const QString contextMenuDeclineRequest = QObject::tr("Decline friend request"); - - const QString dialogRemoveContact = QObject::tr("Remove contact"); - const QString dialogRemoveCCAsContact = QObject::tr("Remove %1 as a contact?"); - const QString dialogRemoveContactAreYouSure = QObject::tr("Are you sure you wish to remove this contact?"); - - const QString noChatAvailable = QObject::tr("NO CHAT AVAILABLE"); - const QString chatTemplateSuffix = QObject::tr(" CHAT"); - const QString outgoingPendingContactRequest = QObject::tr("OUTGOING PENDING CONTACT REQUEST "); - const QString incomingContactRequest = QObject::tr("INCOMING CONTACT REQUEST "); -} - -ChatUserListTreeView::ChatUserListTreeView(QWidget *parent) - : QTreeView (parent) -{ - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &QAbstractItemView::customContextMenuRequested, this, &ChatUserListTreeView::onCustomContextMenu); - setItemDelegate(new ChatClientUsersViewItemDelegate({}, this)); - - connect(this, &QTreeView::clicked, this, &ChatUserListTreeView::onClicked); - connect(this, &QTreeView::doubleClicked, this, &ChatUserListTreeView::onDoubleClicked); -} - -void ChatUserListTreeView::setActiveChatLabel(QLabel *label) -{ - label_ = label; -} - -void ChatUserListTreeView::editContact(const QModelIndex& index) -{ - PartyTreeItem* item = internalPartyTreeItem(index); - if (nullptr == item) { - return; - } - - const Chat::ClientPartyPtr clientPartyPtr = item->data().value(); - if (nullptr == clientPartyPtr) { - return; - } - - if (clientPartyPtr->isPrivateStandard()) { - Chat::PartyRecipientPtr recipientPtr = clientPartyPtr->getSecondRecipient(currentUser()); - - if (nullptr == recipientPtr) - { - return; - } - - EditContactDialog dialog( - QString::fromStdString(clientPartyPtr->userHash()), - QString::fromStdString(clientPartyPtr->displayName()), - recipientPtr->publicKeyTime(), - QString::fromStdString(recipientPtr->publicKey().toHexStr()), - parentWidget()->window()); - if (dialog.exec() == QDialog::Accepted) { - - // do not allow display name to contains only whitespaces - std::string newDisplayName = dialog.displayName().toStdString(); - std::string::iterator it_first_nonspace = std::find_if(newDisplayName.begin(), newDisplayName.end() - , [l = std::locale{}](auto ch) { - return !std::isspace(ch); - }); - - if (it_first_nonspace != newDisplayName.end()) - { - emit setDisplayName(clientPartyPtr->id(), newDisplayName); - } - } - } -} - -void ChatUserListTreeView::onCustomContextMenu(const QPoint & point) -{ - QModelIndex index = indexAt(point); - emit partyClicked(index); - if (!index.isValid()) { - return; - } - - PartyTreeItem* item = internalPartyTreeItem(index); - if (nullptr == item) { - return; - } - - const Chat::ClientPartyPtr clientPartyPtr = item->data().value(); - if (nullptr == clientPartyPtr) { - return; - } - - if (!clientPartyPtr->isPrivate()) { - return; - } - - if (clientPartyPtr->isPrivateOTC()) - { - return; - } - - QMenu contextMenu; - if (Chat::PartyState::INITIALIZED == clientPartyPtr->partyState()) { - QAction* removeAction = contextMenu.addAction(contextMenuRemoveUser); - removeAction->setData(index); - connect(removeAction, &QAction::triggered, this, &ChatUserListTreeView::onRemoveFromContacts); - contextMenu.addAction(removeAction); - - QAction* editAction = contextMenu.addAction(contextMenuEditUser); - editAction->setData(index); - connect(editAction, &QAction::triggered, this, &ChatUserListTreeView::onEditContact); - contextMenu.addAction(editAction); - } - - if (Chat::PartyState::REQUESTED == clientPartyPtr->partyState()) { - if (clientPartyPtr->partyCreatorHash() != currentUser()) { - // receiver of party - QAction* acceptAction = contextMenu.addAction(contextMenuAcceptRequest); - acceptAction->setData(index); - connect(acceptAction, &QAction::triggered, this, &ChatUserListTreeView::onAcceptFriendRequest); - contextMenu.addAction(acceptAction); - - QAction* declineAction = contextMenu.addAction(contextMenuDeclineRequest); - declineAction->setData(index); - connect(declineAction, &QAction::triggered, this, &ChatUserListTreeView::onDeclineFriendRequest); - contextMenu.addAction(declineAction); - } - else { - // creator of party - QAction* removeAction = contextMenu.addAction(contextMenuRemoveUser); - removeAction->setData(index); - connect(removeAction, &QAction::triggered, this, &ChatUserListTreeView::onRemoveFromContacts); - contextMenu.addAction(removeAction); - } - } - - if (contextMenu.isEmpty()) { - return; - } - - contextMenu.exec(viewport()->mapToGlobal(point)); - //selectionModel()->clearSelection(); -} - -void ChatUserListTreeView::onExpandGlobalOTC() -{ - auto* proxyModel = static_cast(model()); - if (!proxyModel) { - return; - } - - const QModelIndex otcGlobalIndex = proxyModel->getOTCGlobalRoot(); - if (!otcGlobalIndex.isValid()) { - return; - } - - // Qt 5.13 -> change this two functions to expandRecursively - expand(otcGlobalIndex.child(0, 0)); - expand(otcGlobalIndex.child(1, 0)); -} - -PartyTreeItem* ChatUserListTreeView::internalPartyTreeItem(const QModelIndex& index) -{ - if (!index.isValid()) { - return nullptr; - } - - auto* proxyModel = static_cast(model()); - if (!proxyModel) { - return nullptr; - } - - return proxyModel->getInternalData(index); -} - -void ChatUserListTreeView::onClicked(const QModelIndex &index) -{ - emit partyClicked(index); - if (!index.isValid()) { - return; - } - - PartyTreeItem* item = internalPartyTreeItem(index); - - if (nullptr == item || UI::ElementType::Container != item->modelType()) { - return; - } - - if (item->data().canConvert() && item->data().toString() == ChatModelNames::ContainerTabGlobal) { - return; - } - - if (isExpanded(index)) { - collapse(index); - } - else { - expand(index); - } -} - -void ChatUserListTreeView::onDoubleClicked(const QModelIndex &index) -{ - emit partyClicked(index); - PartyTreeItem* item = internalPartyTreeItem(index); - - if (nullptr == item) { - return; - } - - if (item->modelType() == UI::ElementType::Party) { - editContact(index); - } -} - -void ChatUserListTreeView::updateDependUi(const QModelIndex& index) -{ - auto proxyModel = qobject_cast(index.model()); - QModelIndex currentIndex = proxyModel ? proxyModel->mapToSource(index) : index; - PartyTreeItem* item = static_cast(currentIndex.internalPointer()); - auto chatPartiesTreeModel = qobject_cast(currentIndex.model()); - - const Chat::ClientPartyPtr clientPartyPtr = item->data().value(); - - if (!chatPartiesTreeModel) { - label_->setText(noChatAvailable); - return; - } - - if (!clientPartyPtr) { - label_->setText(noChatAvailable); - return; - } - - if (!label_) { - return; - } - - const QString upperUserName = QString::fromStdString(clientPartyPtr->displayName()).toUpper(); - - if (clientPartyPtr->isGlobal()) { - label_->setText(upperUserName + chatTemplateSuffix); - } - - if (clientPartyPtr->isPrivateStandard()) { - if ((Chat::PartyState::UNINITIALIZED == clientPartyPtr->partyState()) - || (Chat::PartyState::REQUESTED == clientPartyPtr->partyState())) { - - if (clientPartyPtr->partyCreatorHash() == chatPartiesTreeModel->currentUser()) { - label_->setText(outgoingPendingContactRequest + upperUserName); - } - else { - label_->setText(incomingContactRequest + upperUserName); - } - } - else if (Chat::PartyState::INITIALIZED == clientPartyPtr->partyState()) { - label_->setText(upperUserName + chatTemplateSuffix); - } - } -} - -void ChatUserListTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) -{ - QTreeView::currentChanged(current, previous); - PartyTreeItem* item = internalPartyTreeItem(current); - - if (!item) { - return; - } - - if (item->modelType() == UI::ElementType::Party) { - updateDependUi(current); - } -} - -void ChatUserListTreeView::onEditContact() -{ - QAction* action = qobject_cast(sender()); - - if (nullptr == action) { - return; - } - - QModelIndex index = action->data().toModelIndex(); - - editContact(index); -} - -const Chat::ClientPartyPtr ChatUserListTreeView::clientPartyPtrFromAction(const QAction* action) -{ - if (nullptr == action) { - return nullptr; - } - - QModelIndex index = action->data().toModelIndex(); - - PartyTreeItem* item = internalPartyTreeItem(index); - if (nullptr == item) { - return nullptr; - } - - return item->data().value(); -} - -const std::string& ChatUserListTreeView::currentUser() const -{ - return static_cast(model())->currentUser(); -} - -void ChatUserListTreeView::onRemoveFromContacts() -{ - QAction* action = qobject_cast(sender()); - const Chat::ClientPartyPtr clientPartyPtr = clientPartyPtrFromAction(action); - - if (nullptr == clientPartyPtr) { - return; - } - - BSMessageBox confirmRemoveContact( - BSMessageBox::question, - dialogRemoveContact, - dialogRemoveCCAsContact.arg(QString::fromStdString(clientPartyPtr->displayName())), - dialogRemoveContactAreYouSure, parentWidget() - ); - - if (confirmRemoveContact.exec() != QDialog::Accepted) { - return; - } - - emit removeFromContacts(clientPartyPtr->id()); -} - -void ChatUserListTreeView::onAcceptFriendRequest() -{ - QAction* action = qobject_cast(sender()); - const Chat::ClientPartyPtr clientPartyPtr = clientPartyPtrFromAction(action); - - if (nullptr == clientPartyPtr) { - return; - } - - emit acceptFriendRequest(clientPartyPtr->id()); -} - -void ChatUserListTreeView::onDeclineFriendRequest() -{ - QAction* action = qobject_cast(sender()); - const Chat::ClientPartyPtr clientPartyPtr = clientPartyPtrFromAction(action); - - if (nullptr == clientPartyPtr) { - return; - } - - emit declineFriendRequest(clientPartyPtr->id()); -} diff --git a/BlockSettleUILib/ChatUI/ChatUserListTreeView.h b/BlockSettleUILib/ChatUI/ChatUserListTreeView.h deleted file mode 100644 index bef56573c..000000000 --- a/BlockSettleUILib/ChatUI/ChatUserListTreeView.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATCLIENTUSERVIEW_H -#define CHATCLIENTUSERVIEW_H - -#include -#include "ChatUsersViewItemStyle.h" -#include "ChatProtocol/ChatClientService.h" - -class QLabel; -class QMenu; - -class ChatUsersViewItemStyle; -class PartyTreeItem; -class ChatPartiesTreeModel; - -class ChatUserListTreeView : public QTreeView -{ - Q_OBJECT - -public: - ChatUserListTreeView(QWidget * parent = nullptr); - // #new_logic : this should leave in chat widget - void setActiveChatLabel(QLabel * label); - -public slots: - void onCustomContextMenu(const QPoint &); - void onExpandGlobalOTC(); - -signals: - void partyClicked(const QModelIndex& index); - void removeFromContacts(const std::string& partyId); - void acceptFriendRequest(const std::string& partyId); - void declineFriendRequest(const std::string& partyId); - void setDisplayName(const std::string& partyId, const std::string& contactName); - -protected slots: - void currentChanged(const QModelIndex& current, const QModelIndex& previous) override; - -private slots: - void onClicked(const QModelIndex &); - void onDoubleClicked(const QModelIndex &); - void onEditContact(); - void onRemoveFromContacts(); - void onAcceptFriendRequest(); - void onDeclineFriendRequest(); - -private: - PartyTreeItem* internalPartyTreeItem(const QModelIndex& index); - const Chat::ClientPartyPtr clientPartyPtrFromAction(const QAction* action); - const std::string& currentUser() const; - void editContact(const QModelIndex& index); - - // #new_logic : this should leave in chat widget - void updateDependUi(const QModelIndex& index); - - -private: - // #new_logic : this should leave in chat widget - QLabel * label_; -}; - -#endif // CHATCLIENTUSERVIEW_H diff --git a/BlockSettleUILib/ChatUI/ChatUsersViewItemStyle.cpp b/BlockSettleUILib/ChatUI/ChatUsersViewItemStyle.cpp deleted file mode 100644 index 84dafea58..000000000 --- a/BlockSettleUILib/ChatUI/ChatUsersViewItemStyle.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatUsersViewItemStyle.h" - -ChatUsersViewItemStyle::ChatUsersViewItemStyle(QWidget *parent) - : QWidget(parent) - , colorRoom_(Qt::white) - , colorUserOnline_(Qt::white) - , colorUserOffline_(Qt::gray) - , colorContactOnline_(Qt::white) - , colorContactOffline_(Qt::gray) - , colorContactIncoming_(Qt::darkYellow) - , colorContactOutgoing_(Qt::darkGreen) - , colorContactRejected_(Qt::darkRed) - , colorHighlightBackground_(Qt::cyan) -{ - -} - -QColor ChatUsersViewItemStyle::colorCategoryItem() const -{ - return colorCategoryItem_; -} - -QColor ChatUsersViewItemStyle::colorRoom() const -{ - return colorRoom_; -} - -QColor ChatUsersViewItemStyle::colorUserOnline() const -{ - return colorUserOnline_; -} - -QColor ChatUsersViewItemStyle::colorUserOffline() const -{ - return colorUserOffline_; -} - -QColor ChatUsersViewItemStyle::colorContactOnline() const -{ - return colorContactOnline_; -} - -QColor ChatUsersViewItemStyle::colorContactOffline() const -{ - return colorContactOffline_; -} - -QColor ChatUsersViewItemStyle::colorContactIncoming() const -{ - return colorContactIncoming_; -} - -QColor ChatUsersViewItemStyle::colorContactOutgoing() const -{ - return colorContactOutgoing_; -} - -QColor ChatUsersViewItemStyle::colorContactRejected() const -{ - return colorContactRejected_; -} - -QColor ChatUsersViewItemStyle::colorHighlightBackground() const -{ - return colorHighlightBackground_; -} - -void ChatUsersViewItemStyle::setColorCategoryItem(QColor colorCategoryItem) -{ - colorCategoryItem_ = colorCategoryItem; -} - -void ChatUsersViewItemStyle::setColorRoom(QColor colorRoom) -{ - colorRoom_ = colorRoom; -} - -void ChatUsersViewItemStyle::setColorUserOnline(QColor colorUserOnline) -{ - colorUserOnline_ = colorUserOnline; -} - -void ChatUsersViewItemStyle::setColorUserOffline(QColor colorUserOffline) -{ - colorUserOffline_ = colorUserOffline; -} - -void ChatUsersViewItemStyle::setColorContactOnline(QColor colorContactOnline) -{ - colorContactOnline_ = colorContactOnline; -} - -void ChatUsersViewItemStyle::setColorContactOffline(QColor colorContactOffline) -{ - colorContactOffline_ = colorContactOffline; -} - -void ChatUsersViewItemStyle::setColorContactIncoming(QColor colorContactIncoming) -{ - colorContactIncoming_ = colorContactIncoming; -} - -void ChatUsersViewItemStyle::setColorContactOutgoing(QColor colorContactOutgoing) -{ - colorContactOutgoing_ = colorContactOutgoing; -} - -void ChatUsersViewItemStyle::setColorContactRejected(QColor colorContactRejected) -{ - colorContactRejected_ = colorContactRejected; -} - -void ChatUsersViewItemStyle::setColorHighlightBackground(QColor colorHighlightBackground) -{ - colorHighlightBackground_ = colorHighlightBackground; -} diff --git a/BlockSettleUILib/ChatUI/ChatUsersViewItemStyle.h b/BlockSettleUILib/ChatUI/ChatUsersViewItemStyle.h deleted file mode 100644 index 629c6200f..000000000 --- a/BlockSettleUILib/ChatUI/ChatUsersViewItemStyle.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATUSERSVIEWITEMSTYLE_H -#define CHATUSERSVIEWITEMSTYLE_H - -#include - -// #UI_ChatParty : Names should be updated bellow as far as termins have been changed -class ChatUsersViewItemStyle : public QWidget -{ - Q_OBJECT - Q_PROPERTY(QColor color_category_item READ colorCategoryItem WRITE setColorCategoryItem) - Q_PROPERTY(QColor color_room READ colorRoom WRITE setColorRoom) - Q_PROPERTY(QColor color_user_online READ colorUserOnline WRITE setColorUserOnline) - Q_PROPERTY(QColor color_user_offline READ colorUserOffline WRITE setColorUserOffline) - Q_PROPERTY(QColor color_contact_online READ colorContactOnline WRITE setColorContactOnline) - Q_PROPERTY(QColor color_contact_offline READ colorContactOffline WRITE setColorContactOffline) - Q_PROPERTY(QColor color_contact_incoming READ colorContactIncoming WRITE setColorContactIncoming) - Q_PROPERTY(QColor color_contact_outgoing READ colorContactOutgoing WRITE setColorContactOutgoing) - Q_PROPERTY(QColor color_contact_rejected READ colorContactRejected WRITE setColorContactRejected) - Q_PROPERTY(QColor color_highlight_background READ colorHighlightBackground WRITE setColorHighlightBackground) -private: - QColor colorCategoryItem_; - QColor colorRoom_; - QColor colorUserOnline_; - QColor colorUserOffline_; - QColor colorContactOnline_; - QColor colorContactOffline_; - QColor colorContactIncoming_; - QColor colorContactOutgoing_; - QColor colorContactRejected_; - QColor colorHighlightBackground_; - -public: - explicit ChatUsersViewItemStyle(QWidget *parent = nullptr); - - QColor colorCategoryItem() const; - QColor colorRoom() const; - QColor colorUserOnline() const; - QColor colorUserOffline() const; - QColor colorContactOnline() const; - QColor colorContactOffline() const; - QColor colorContactIncoming() const; - QColor colorContactOutgoing() const; - QColor colorContactRejected() const; - QColor colorHighlightBackground() const; - -signals: - -public slots: - void setColorCategoryItem(QColor colorCategoryItem); - void setColorRoom(QColor colorRoom); - void setColorUserOnline(QColor colorUserOnline); - void setColorUserOffline(QColor colorUserOffline); - void setColorContactOnline(QColor colorContactOnline); - void setColorContactOffline(QColor colorContactOffline); - void setColorContactIncoming(QColor colorContactIncoming); - void setColorContactOutgoing(QColor colorContactOutgoing); - void setColorContactRejected(QColor colorContactRejected); - void setColorHighlightBackground(QColor colorHighlightBackground); -}; -#endif // CHATUSERSVIEWITEMSTYLE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidget.cpp b/BlockSettleUILib/ChatUI/ChatWidget.cpp deleted file mode 100644 index 2dd67652b..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidget.cpp +++ /dev/null @@ -1,735 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ChatWidget.h" - -#include - -#include -#include - -#include "BSChatInput.h" -#include "BSMessageBox.h" -#include "ImportKeyBox.h" -#include "ChatClientUsersViewItemDelegate.h" -#include "ChatWidgetStates/ChatWidgetStates.h" -#include "ChatOTCHelper.h" -#include "OtcUtils.h" -#include "OtcClient.h" -#include "OTCRequestViewModel.h" -#include "OTCShieldWidgets/OTCWindowsManager.h" -#include "AuthAddressManager.h" -#include "MDCallbacksQt.h" -#include "AssetManager.h" -#include "ui_ChatWidget.h" -#include "UtxoReservationManager.h" -#include "ApplicationSettings.h" -#include "chat.pb.h" - -using namespace bs; -using namespace bs::network; - -namespace -{ - const QString showHistoryButtonName = QObject::tr("Show History"); -} - -ChatWidget::ChatWidget(QWidget* parent) - : QWidget(parent), ui_(new Ui::ChatWidget) -{ - qRegisterMetaType>(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - - ui_->setupUi(this); - -#ifndef Q_OS_WIN - ui_->timeLabel->setMinimumSize(ui_->timeLabel->property("minimumSizeLinux").toSize()); -#endif - - ui_->textEditMessages->onSetColumnsWidth(ui_->timeLabel->minimumWidth(), - ui_->iconLabel->minimumWidth(), - ui_->userLabel->minimumWidth(), - ui_->messageLabel->minimumWidth()); - - //Init UI and other stuff - ui_->frameContactActions->setVisible(false); - - ui_->textEditMessages->viewport()->installEventFilter(this); - ui_->input_textEdit->viewport()->installEventFilter(this); - ui_->treeViewUsers->viewport()->installEventFilter(this); - - ui_->showHistoryButton->setText(showHistoryButtonName); - ui_->showHistoryButton->setVisible(false); - - otcWindowsManager_ = std::make_shared(); - auto* sWidget = ui_->stackedWidgetOTC; - for (int index = 0; index < sWidget->count(); ++index) { - auto* widget = qobject_cast(sWidget->widget(index)); - if (widget) { - widget->setChatOTCManager(otcWindowsManager_); - connect(this, &ChatWidget::chatRoomChanged, widget, &OTCWindowsAdapterBase::onChatRoomChanged); - connect(this, &ChatWidget::onAboutToHide, widget, &OTCWindowsAdapterBase::onParentAboutToHide); - } - } - connect(otcWindowsManager_.get(), &OTCWindowsManager::syncInterfaceRequired, this, &ChatWidget::onUpdateOTCShield); - - changeState(); //Initial state is LoggedOut -} - -ChatWidget::~ChatWidget() -{ - // Should be done explicitly, since destructor for state could make changes inside chatWidget - stateCurrent_.reset(); -} - -void ChatWidget::init(const std::shared_ptr& connectionManager - , bs::network::otc::Env env - , const Chat::ChatClientServicePtr& chatClientServicePtr - , const std::shared_ptr& loggerPtr - , const std::shared_ptr& walletsMgr - , const std::shared_ptr &authManager - , const std::shared_ptr& armory - , const std::shared_ptr& signContainer - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr& assetManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& applicationSettings) -{ - loggerPtr_ = loggerPtr; - - // OTC - otcHelper_ = new ChatOTCHelper(this); - otcHelper_->init(env, loggerPtr, walletsMgr, armory, signContainer, - authManager, utxoReservationManager, applicationSettings); - otcWindowsManager_->init(walletsMgr, authManager, mdCallbacks, assetManager - , armory, utxoReservationManager); - - chatClientServicePtr_ = chatClientServicePtr; - - installEventFilter(this); - - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::clientLoggedOutFromServer, this, &ChatWidget::onLogout, Qt::QueuedConnection); - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::partyModelChanged, this, &ChatWidget::onPartyModelChanged, Qt::QueuedConnection); - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::searchUserReply, ui_->searchWidget, &SearchWidget::onSearchUserReply); - connect(ui_->searchWidget, &SearchWidget::showUserRoom, this, &ChatWidget::onShowUserRoom); - connect(ui_->searchWidget, &SearchWidget::contactFriendRequest, this, &ChatWidget::onContactFriendRequest); - connect(ui_->searchWidget, &SearchWidget::emailHashRequested, this, &ChatWidget::emailHashRequested); - - chatPartiesTreeModel_ = std::make_shared(chatClientServicePtr_, otcHelper_->client()); - - const ChatPartiesSortProxyModelPtr charTreeSortModel = std::make_shared(chatPartiesTreeModel_); - ui_->treeViewUsers->setModel(charTreeSortModel.get()); - ui_->treeViewUsers->sortByColumn(0, Qt::AscendingOrder); - ui_->treeViewUsers->setSortingEnabled(true); - ui_->treeViewUsers->setItemDelegate(new ChatClientUsersViewItemDelegate(charTreeSortModel, this)); - ui_->treeViewUsers->setActiveChatLabel(ui_->labelActiveChat); - - ui_->searchWidget->init(chatClientServicePtr); - ui_->searchWidget->onClearLineEdit(); - ui_->searchWidget->onSetLineEditEnabled(false); - ui_->searchWidget->onSetListVisible(false); - - ui_->textEditMessages->onSwitchToChat("Global"); - - connect(ui_->showHistoryButton, &QPushButton::pressed, this, &ChatWidget::onRequestAllPrivateMessages); - - - ui_->widgetOTCShield->init(walletsMgr, authManager, applicationSettings); - connect(ui_->widgetOTCShield, &OTCShield::requestPrimaryWalletCreation, this, &ChatWidget::requestPrimaryWalletCreation); - - // connections - // User actions - connect(ui_->treeViewUsers, &ChatUserListTreeView::partyClicked, this, &ChatWidget::onUserListClicked); - connect(ui_->treeViewUsers, &ChatUserListTreeView::removeFromContacts, this, &ChatWidget::onRemovePartyRequest); - connect(ui_->treeViewUsers, &ChatUserListTreeView::acceptFriendRequest, this, &ChatWidget::onContactRequestAcceptClicked); - connect(ui_->treeViewUsers, &ChatUserListTreeView::declineFriendRequest, this, &ChatWidget::onContactRequestRejectClicked); - connect(ui_->treeViewUsers, &ChatUserListTreeView::setDisplayName, this, &ChatWidget::onSetDisplayName); - // This should be queued connection to make sure first view is updated - connect(chatPartiesTreeModel_.get(), &ChatPartiesTreeModel::restoreSelectedIndex, this, &ChatWidget::onActivateCurrentPartyId, Qt::QueuedConnection); - - connect(ui_->input_textEdit, &BSChatInput::sendMessage, this, &ChatWidget::onSendMessage); - connect(ui_->textEditMessages, &ChatMessagesTextEdit::messageRead, this, &ChatWidget::onMessageRead); - connect(ui_->textEditMessages, &ChatMessagesTextEdit::newPartyRequest, this, &ChatWidget::onNewPartyRequest); - connect(ui_->textEditMessages, &ChatMessagesTextEdit::removePartyRequest, this, &ChatWidget::onRemovePartyRequest); - connect(ui_->textEditMessages, &ChatMessagesTextEdit::switchPartyRequest, this, &ChatWidget::onActivatePartyId); - - //connect(chatClientServicePtr_.get(), &Chat::ChatClientService::clientLoggedInToServer, this, &ChatWidget::onLogin, Qt::QueuedConnection); - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::clientLoggedInToServer, this, &ChatWidget::onLogin); - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::clientLoggedOutFromServer, this, &ChatWidget::onLogout, Qt::QueuedConnection); - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::partyModelChanged, this, &ChatWidget::onPartyModelChanged, Qt::QueuedConnection); - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::privateMessagesHistoryCount, this, &ChatWidget::onPrivateMessagesHistoryCount, Qt::QueuedConnection); - - const Chat::ClientPartyModelPtr clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::messageArrived, this, &ChatWidget::onSendArrived, Qt::QueuedConnection); - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::clientPartyStatusChanged, this, &ChatWidget::onClientPartyStatusChanged, Qt::QueuedConnection); - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::messageStateChanged, this, &ChatWidget::onMessageStateChanged, Qt::QueuedConnection); - - // Connect all signal that influence on widget appearance - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::messageArrived, this, &ChatWidget::onRegisterNewChangingRefresh, Qt::QueuedConnection); - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::clientPartyStatusChanged, this, &ChatWidget::onRegisterNewChangingRefresh, Qt::QueuedConnection); - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::messageStateChanged, this, &ChatWidget::onRegisterNewChangingRefresh, Qt::QueuedConnection); - - // OTC - connect(clientPartyModelPtr.get(), &Chat::ClientPartyModel::otcPrivatePartyReady, this, &ChatWidget::onOtcPrivatePartyReady, Qt::QueuedConnection); - - ui_->textEditMessages->onSetClientPartyModel(clientPartyModelPtr); - - otcRequestViewModel_ = new OTCRequestViewModel(otcHelper_->client(), this); - ui_->treeViewOTCRequests->setModel(otcRequestViewModel_); - connect(ui_->treeViewOTCRequests->selectionModel(), &QItemSelectionModel::currentChanged, this, &ChatWidget::onOtcRequestCurrentChanged); - connect(chatPartiesTreeModel_.get(), &ChatPartiesTreeModel::restoreSelectedIndex, this, &ChatWidget::onActivateGlobalOTCTableRow, Qt::QueuedConnection); - - connect(otcHelper_->client(), &OtcClient::sendPbMessage, this, &ChatWidget::sendOtcPbMessage); - connect(otcHelper_->client(), &OtcClient::sendContactMessage, this, &ChatWidget::onSendOtcMessage); - connect(otcHelper_->client(), &OtcClient::sendPublicMessage, this, &ChatWidget::onSendOtcPublicMessage); - connect(otcHelper_->client(), &OtcClient::peerUpdated, this, &ChatWidget::onOtcUpdated); - connect(otcHelper_->client(), &OtcClient::publicUpdated, this, &ChatWidget::onOtcPublicUpdated); - connect(otcHelper_->client(), &OtcClient::publicUpdated, otcRequestViewModel_, &OTCRequestViewModel::onRequestsUpdated); - connect(otcHelper_->client(), &OtcClient::peerError, this, &ChatWidget::onOTCPeerError); - - - connect(ui_->widgetNegotiateRequest, &OTCNegotiationRequestWidget::requestCreated, this, &ChatWidget::onOtcRequestSubmit); - connect(ui_->widgetPullOwnOTCRequest, &PullOwnOTCRequestWidget::currentRequestPulled, this, &ChatWidget::onOtcPullOrRejectCurrent); - connect(ui_->widgetNegotiateResponse, &OTCNegotiationResponseWidget::responseAccepted, this, &ChatWidget::onOtcResponseAccept); - connect(ui_->widgetNegotiateResponse, &OTCNegotiationResponseWidget::responseUpdated, this, &ChatWidget::onOtcResponseUpdate); - connect(ui_->widgetNegotiateResponse, &OTCNegotiationResponseWidget::responseRejected, this, &ChatWidget::onOtcPullOrRejectCurrent); - connect(ui_->widgetCreateOTCRequest, &CreateOTCRequestWidget::requestCreated, this, &ChatWidget::onOtcQuoteRequestSubmit); - connect(ui_->widgetCreateOTCResponse, &CreateOTCResponseWidget::responseCreated, this, &ChatWidget::onOtcQuoteResponseSubmit); - - ui_->widgetCreateOTCRequest->init(env); -} - -otc::PeerPtr ChatWidget::currentPeer() const -{ - const auto chartProxyModel = dynamic_cast(ui_->treeViewUsers->model()); - PartyTreeItem* partyTreeItem = chartProxyModel->getInternalData(ui_->treeViewUsers->currentIndex()); - if (!partyTreeItem || partyTreeItem->modelType() == bs::UI::ElementType::Container) { - return nullptr; - } - - const auto clientPartyPtr = partyTreeItem->data().value(); - if (!clientPartyPtr || clientPartyPtr->isGlobalStandard()) { - return nullptr; - } - - if (clientPartyPtr->isGlobalOTC()) { - const auto ¤tIndex = ui_->treeViewOTCRequests->selectionModel()->currentIndex(); - if (!currentIndex.isValid() || currentIndex.row() < 0 || currentIndex.row() >= int(otcHelper_->client()->requests().size())) { - // Show by default own request (if available) - return otcHelper_->client()->ownRequest(); - } - - return otcHelper_->client()->requests().at(size_t(currentIndex.row())); - } - - return otcHelper_->client()->peer(clientPartyPtr->userHash(), partyTreeItem->peerType); -} - -void ChatWidget::setUserType(UserType userType) -{ - userType_ = userType; -} - -void acceptPartyRequest(const std::string& partyId) {} -void rejectPartyRequest(const std::string& partyId) {} -void sendPartyRequest(const std::string& partyId) {} -void removePartyRequest(const std::string& partyId) {} - -void ChatWidget::onContactRequestAcceptClicked(const std::string& partyId) const -{ - stateCurrent_->onAcceptPartyRequest(partyId); -} - -void ChatWidget::onContactRequestRejectClicked(const std::string& partyId) const -{ - stateCurrent_->onRejectPartyRequest(partyId); -} - -void ChatWidget::onContactRequestSendClicked(const std::string& partyId) const -{ - stateCurrent_->onSendPartyRequest(partyId); -} - -void ChatWidget::onContactRequestCancelClicked(const std::string& partyId) const -{ - stateCurrent_->onRemovePartyRequest(partyId); -} - -void ChatWidget::onNewPartyRequest(const std::string& userName, const std::string& initialMessage) const -{ - stateCurrent_->onNewPartyRequest(userName, initialMessage); -} - -void ChatWidget::onRemovePartyRequest(const std::string& partyId) const -{ - stateCurrent_->onRemovePartyRequest(partyId); -} - -void ChatWidget::onOtcUpdated(const otc::PeerPtr &peer) -{ - stateCurrent_->onOtcUpdated(peer); -} - -void ChatWidget::onOtcPublicUpdated() const -{ - stateCurrent_->onOtcPublicUpdated(); - ui_->treeViewUsers->onExpandGlobalOTC(); -} - -void ChatWidget::onOTCPeerError(const otc::PeerPtr &peer, bs::network::otc::PeerErrorType type, const std::string* errorMsg) -{ - stateCurrent_->onOTCPeerError(peer, type, errorMsg); -} - -void ChatWidget::onUpdateOTCShield() const -{ - stateCurrent_->onUpdateOTCShield(); -} - -void ChatWidget::onEmailHashReceived(const std::string &email, const std::string &hash) const -{ - ui_->searchWidget->onEmailHashReceived(email, hash); -} - -void ChatWidget::onPartyModelChanged() const -{ - stateCurrent_->onResetPartyModel(); - // #new_logic : save expanding state - ui_->treeViewUsers->expandAll(); -} - -void ChatWidget::onLogin() -{ - const auto clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - ownUserId_ = clientPartyModelPtr->ownUserName(); - otcHelper_->setCurrentUserId(ownUserId_); - - changeState(); - stateCurrent_->onResetPartyModel(); - ui_->treeViewUsers->expandAll(); - - onActivatePartyId(QString::fromLatin1(Chat::GlobalRoomName)); -} - -void ChatWidget::onLogout() -{ - ownUserId_.clear(); - changeState(); -} - -void ChatWidget::showEvent(QShowEvent* e) -{ - // refreshView - if (bNeedRefresh_) { - bNeedRefresh_ = false; - onActivatePartyId(QString::fromStdString(currentPartyId_)); - } -} - -void ChatWidget::hideEvent(QHideEvent* event) -{ - emit onAboutToHide(); - QWidget::hideEvent(event); -} - -bool ChatWidget::eventFilter(QObject* sender, QEvent* event) -{ - const auto fClearSelection = [](QTextEdit* widget, bool bForce = false) { - if (!widget->underMouse() || bForce) { - QTextCursor textCursor = widget->textCursor(); - textCursor.clearSelection(); - widget->setTextCursor(textCursor); - } - }; - - if ( QEvent::MouseButtonPress == event->type()) { - fClearSelection(ui_->textEditMessages); - fClearSelection(ui_->input_textEdit); - } - - if (QEvent::KeyPress == event->type()) { - auto*keyEvent = dynamic_cast(event); - - // handle ctrl+c (cmd+c on macOS) - if (keyEvent->modifiers().testFlag(Qt::ControlModifier)) { - if (Qt::Key_C == keyEvent->key()) { - if (ui_->textEditMessages->textCursor().hasSelection()) { - QApplication::clipboard()->setText(ui_->textEditMessages->getFormattedTextFromSelection()); - fClearSelection(ui_->textEditMessages, true); - return true; - } - } - } - if (keyEvent->matches(QKeySequence::SelectAll)) { - fClearSelection(ui_->textEditMessages); - } - } - - return QWidget::eventFilter(sender, event); -} - -void ChatWidget::onProcessOtcPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response) const -{ - stateCurrent_->onProcessOtcPbMessage(response); -} - -void ChatWidget::onSendOtcMessage(const std::string& contactId, const BinaryData& data) const -{ - auto* chatProxyModel = dynamic_cast(ui_->treeViewUsers->model()); - assert(chatProxyModel); - - const QModelIndex index = chatProxyModel->getProxyIndexById(currentPartyId_); - const auto party = chatProxyModel->getInternalData(index); - assert(party); - - bool const isOTCGlobalRoot = (chatProxyModel->getOTCGlobalRoot() == index); - - Chat::ClientPartyPtr clientPartyPtr = nullptr; - if (party->peerType == bs::network::otc::PeerType::Contact && !isOTCGlobalRoot) { - clientPartyPtr = chatClientServicePtr_->getClientPartyModelPtr()->getStandardPartyForUsers(ownUserId_, contactId); - } - else { - clientPartyPtr = chatClientServicePtr_->getClientPartyModelPtr()->getOtcPartyForUsers(ownUserId_, contactId); - } - - if (!clientPartyPtr) { - SPDLOG_LOGGER_ERROR(loggerPtr_, "can't find valid private party to send OTC message"); - return; - } - stateCurrent_->onSendOtcMessage(clientPartyPtr->id(), OtcUtils::serializeMessage(data)); -} - -void ChatWidget::onSendOtcPublicMessage(const BinaryData &data) const -{ - stateCurrent_->onSendOtcPublicMessage(OtcUtils::serializePublicMessage(data)); -} - -void ChatWidget::onNewChatMessageTrayNotificationClicked(const QString& partyId) -{ - onActivatePartyId(partyId); -} - -void ChatWidget::onSendMessage() const -{ - stateCurrent_->onSendMessage(); -} - -void ChatWidget::onMessageRead(const std::string& partyId, const std::string& messageId) const -{ - stateCurrent_->onMessageRead(partyId, messageId); -} - -void ChatWidget::onSendArrived(const Chat::MessagePtrList& messagePtrList) const -{ - stateCurrent_->onProcessMessageArrived(messagePtrList); - - const auto clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - for (const auto& message : messagePtrList) - { - const auto privatePartyPtr = clientPartyModelPtr->getPrivatePartyById(message->partyId()); - if (privatePartyPtr && privatePartyPtr->id() == currentPartyId_ && privatePartyPtr->isPrivateStandard()) - { - // request only for private standard parties - // and only once per loop - chatClientServicePtr_->RequestPrivateMessagesHistoryCount(privatePartyPtr->id()); - return; - } - } -} - -void ChatWidget::onClientPartyStatusChanged(const Chat::ClientPartyPtr& clientPartyPtr) const -{ - stateCurrent_->onChangePartyStatus(clientPartyPtr); -} - -void ChatWidget::onMessageStateChanged(const std::string& partyId, const std::string& message_id, const int party_message_state) const -{ - stateCurrent_->onChangeMessageState(partyId, message_id, party_message_state); -} - -void ChatWidget::onUserListClicked(const QModelIndex& index) -{ - if (!index.isValid()) { - return; - } - - auto* chartProxyModel = dynamic_cast(ui_->treeViewUsers->model()); - PartyTreeItem* partyTreeItem = chartProxyModel->getInternalData(index); - - if (partyTreeItem->modelType() == bs::UI::ElementType::Container) { - return; - } - - const auto clientPartyPtr = partyTreeItem->data().value(); - chatTransition(clientPartyPtr); -} - -void ChatWidget::chatTransition(const Chat::ClientPartyPtr& clientPartyPtr) -{ - ui_->showHistoryButton->setVisible(false); - - const auto transitionChange = [this, clientPartyPtr]() { - currentPartyId_ = clientPartyPtr->id(); - }; - - switch (clientPartyPtr->partyState()) - { - case Chat::PartyState::UNINITIALIZED: - changeState(transitionChange); - break; - case Chat::PartyState::REQUESTED: - if (clientPartyPtr->partyCreatorHash() == ownUserId_) { - changeState(transitionChange); - } - else { - changeState(transitionChange); - } - break; - case Chat::PartyState::INITIALIZED: - changeState(transitionChange); - if (clientPartyPtr->isPrivateStandard()) { - chatClientServicePtr_->RequestPrivateMessagesHistoryCount(clientPartyPtr->id()); - } - break; - default: - break; - } - - emit chatRoomChanged(); -} - -void ChatWidget::onActivatePartyId(const QString& partyId) -{ - auto* chartProxyModel = dynamic_cast(ui_->treeViewUsers->model()); - const QModelIndex partyProxyIndex = chartProxyModel->getProxyIndexById(partyId.toStdString()); - if (!partyProxyIndex.isValid()) { - if (ownUserId_.empty()) { - currentPartyId_.clear(); - changeState(); - } - else { - Q_ASSERT(partyId != QString::fromLatin1(Chat::GlobalRoomName)); - onActivatePartyId(QString::fromLatin1(Chat::GlobalRoomName)); - } - return; - } - - ui_->treeViewUsers->setCurrentIndex(partyProxyIndex); - onUserListClicked(partyProxyIndex); -} - -void ChatWidget::onActivateGlobalPartyId() -{ - onActivatePartyId(QString::fromLatin1(Chat::GlobalRoomName)); -} - -void ChatWidget::onActivateCurrentPartyId() -{ - if (currentPartyId_.empty()) { - return; - } - - auto* chatProxyModel = dynamic_cast(ui_->treeViewUsers->model()); - Q_ASSERT(chatProxyModel); - - const QModelIndex index = chatProxyModel->getProxyIndexById(currentPartyId_); - if (!index.isValid()) { - onActivateGlobalPartyId(); - } - - if (ui_->treeViewUsers->selectionModel()->currentIndex() != index) { - ui_->treeViewUsers->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); - } -} - -void ChatWidget::onActivateGlobalOTCTableRow() const -{ - const QDateTime timeStamp = otcHelper_->selectedGlobalOTCEntryTimeStamp(); - - if (!timeStamp.isValid()) { - return; - } - - const QModelIndex currentRequest = otcRequestViewModel_->getIndexByTimestamp(timeStamp); - - if (!currentRequest.isValid()) { - return; - } - - ui_->treeViewOTCRequests->setCurrentIndex(currentRequest); - stateCurrent_->onUpdateOTCShield(); -} - -void ChatWidget::onRegisterNewChangingRefresh() -{ - if (!isVisible() || !isActiveWindow()) { - bNeedRefresh_ = true; - } -} - -void ChatWidget::onShowUserRoom(const QString& userHash) -{ - const auto clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - - const Chat::ClientPartyPtr clientPartyPtr = clientPartyModelPtr->getStandardPartyForUsers(ownUserId_, userHash.toStdString()); - - if (!clientPartyPtr) { - return; - } - - chatTransition(clientPartyPtr); -} - -void ChatWidget::onContactFriendRequest(const QString& userHash) const -{ - ui_->textEditMessages->onShowRequestPartyBox(userHash.toStdString()); -} - -void ChatWidget::onSetDisplayName(const std::string& partyId, const std::string& contactName) const -{ - stateCurrent_->onUpdateDisplayName(partyId, contactName); -} - -void ChatWidget::onOtcRequestSubmit() const -{ - stateCurrent_->onOtcRequestSubmit(); -} - -void ChatWidget::onOtcResponseAccept() const -{ - stateCurrent_->onOtcResponseAccept(); -} - -void ChatWidget::onOtcResponseUpdate() const -{ - stateCurrent_->onOtcResponseUpdate(); -} - -void ChatWidget::onOtcQuoteRequestSubmit() const -{ - stateCurrent_->onOtcQuoteRequestSubmit(); -} - -void ChatWidget::onOtcQuoteResponseSubmit() const -{ - stateCurrent_->onOtcQuoteResponseSubmit(); -} - -void ChatWidget::onOtcPullOrRejectCurrent() const -{ - stateCurrent_->onOtcPullOrRejectCurrent(); -} - -void ChatWidget::onUserPublicKeyChanged(const Chat::UserPublicKeyInfoList& userPublicKeyInfoList) -{ - // only one key needs to be replaced - show one message box - if (userPublicKeyInfoList.size() == 1) - { - onConfirmContactNewKeyData(userPublicKeyInfoList, false); - return; - } - - // multiple keys replacing - const QString detailsPattern = tr("Contacts Require key update: %1"); - - const QString detailsString = detailsPattern.arg(userPublicKeyInfoList.size()); - - BSMessageBox bsMessageBox(BSMessageBox::question, tr("Contacts Information Update"), - tr("Do you wish to import your full Contact list?"), - tr("Press OK to Import all Contact ID keys. Selecting Cancel will allow you to determine each contact individually."), - detailsString); - const int ret = bsMessageBox.exec(); - - onConfirmContactNewKeyData(userPublicKeyInfoList, QDialog::Accepted == ret); -} - -void ChatWidget::onConfirmContactNewKeyData(const Chat::UserPublicKeyInfoList& userPublicKeyInfoList, bool bForceUpdateAllUsers) -{ - Chat::UserPublicKeyInfoList acceptList; - Chat::UserPublicKeyInfoList declineList; - - for (const auto& userPkPtr : userPublicKeyInfoList) - { - if (userPkPtr->newPublicKey() == userPkPtr->oldPublicKey()) { - continue; - } - - if (bForceUpdateAllUsers) - { - acceptList.push_back(userPkPtr); - continue; - } - - ImportKeyBox box(BSMessageBox::question, tr("Import Contact '%1' Public Key?").arg(userPkPtr->user_hash()), this); - box.setAddrPort(std::string()); - box.setNewKeyFromBinary(userPkPtr->newPublicKey()); - box.setOldKeyFromBinary(userPkPtr->oldPublicKey()); - box.setCancelVisible(true); - - if (box.exec() == QDialog::Accepted) - { - acceptList.push_back(userPkPtr); - continue; - } - - declineList.push_back(userPkPtr); - } - - if (!acceptList.empty()) - { - chatClientServicePtr_->AcceptNewPublicKeys(acceptList); - } - - if (!declineList.empty()) - { - chatClientServicePtr_->DeclineNewPublicKeys(declineList); - } -} - -void ChatWidget::onOtcRequestCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) const -{ - onOtcPublicUpdated(); - - QDateTime selectedPeerTimeStamp; - if (currentPartyId_ == Chat::OtcRoomName) { - if (current.isValid() && current.row() >= 0 && current.row() < int(otcHelper_->client()->requests().size())) { - selectedPeerTimeStamp = otcHelper_->client()->requests().at(size_t(current.row()))->request.timestamp; - } - } - - otcHelper_->setGlobalOTCEntryTimeStamp(selectedPeerTimeStamp); -} - -void ChatWidget::onOtcPrivatePartyReady(const Chat::ClientPartyPtr& clientPartyPtr) const -{ - stateCurrent_->onOtcPrivatePartyReady(clientPartyPtr); -} - -void ChatWidget::onPrivateMessagesHistoryCount(const std::string& partyId, quint64 count) const -{ - if (currentPartyId_ != partyId) { - return; - } - - if (ui_->textEditMessages->messagesCount(partyId) < count) - { - ui_->showHistoryButton->setVisible(true); - return; - } - - ui_->showHistoryButton->setVisible(false); -} - -void ChatWidget::onRequestAllPrivateMessages() const -{ - chatClientServicePtr_->RequestAllHistoryMessages(currentPartyId_); -} diff --git a/BlockSettleUILib/ChatUI/ChatWidget.h b/BlockSettleUILib/ChatUI/ChatWidget.h deleted file mode 100644 index af09a3802..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidget.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHAT_WIDGET_H -#define CHAT_WIDGET_H - -#include -#include -#include - -#include "ChatWidgetStates/AbstractChatWidgetState.h" -#include "ChatProtocol/ChatClientService.h" -#include "ChatProtocol/ClientParty.h" -#include "OtcTypes.h" - -class QItemSelection; - -class ApplicationSettings; -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class ChatOTCHelper; -class ChatPartiesTreeModel; -class MDCallbacksQt; -class OTCRequestViewModel; -class OTCWindowsManager; -class WalletSignerContainer; - -namespace Ui { - class ChatWidget; -} - -namespace bs { - namespace network { - enum class UserType : int; - } - namespace sync { - class WalletsManager; - } - class UTXOReservationManager; -} - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - } - } -} - -class ChatWidget final : public QWidget -{ - Q_OBJECT - -public: - explicit ChatWidget(QWidget* parent = nullptr); - ~ChatWidget() override; - - void init(const std::shared_ptr& connectionManager - , bs::network::otc::Env env - , const Chat::ChatClientServicePtr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr& - ); - - bs::network::otc::PeerPtr currentPeer() const; - - void setUserType(bs::network::UserType userType); - -protected: - void showEvent(QShowEvent* e) override; - void hideEvent(QHideEvent* event) override; - bool eventFilter(QObject* sender, QEvent* event) override; - -public slots: - void onProcessOtcPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response) const; - void onSendOtcMessage(const std::string& contactId, const BinaryData& data) const; - void onSendOtcPublicMessage(const BinaryData& data) const; - - void onNewChatMessageTrayNotificationClicked(const QString& partyId); - void onUpdateOTCShield() const; - - void onEmailHashReceived(const std::string &email, const std::string &hash) const; - void onUserPublicKeyChanged(const Chat::UserPublicKeyInfoList& userPublicKeyInfoList); - -private slots: - void onPartyModelChanged() const; - void onLogin(); - void onLogout(); - void onSendMessage() const; - void onMessageRead(const std::string& partyId, const std::string& messageId) const; - void onSendArrived(const Chat::MessagePtrList& messagePtrList) const; - void onClientPartyStatusChanged(const Chat::ClientPartyPtr& clientPartyPtr) const; - void onMessageStateChanged(const std::string& partyId, const std::string& message_id, int party_message_state) const; - void onUserListClicked(const QModelIndex& index); - void onActivatePartyId(const QString& partyId); - void onActivateGlobalPartyId(); - void onActivateCurrentPartyId(); - void onActivateGlobalOTCTableRow() const; - void onRegisterNewChangingRefresh(); - void onShowUserRoom(const QString& userHash); - void onContactFriendRequest(const QString& userHash) const; - void onSetDisplayName(const std::string& partyId, const std::string& contactName) const; - void onConfirmContactNewKeyData(const Chat::UserPublicKeyInfoList& userPublicKeyInfoList, bool bForceUpdateAllUsers); - void onPrivateMessagesHistoryCount(const std::string& partyId, quint64 count) const; - - void onOtcRequestCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) const; - - void onContactRequestAcceptClicked(const std::string& partyId) const; - void onContactRequestRejectClicked(const std::string& partyId) const; - void onContactRequestSendClicked(const std::string& partyId) const; - void onContactRequestCancelClicked(const std::string& partyId) const; - - void onNewPartyRequest(const std::string& userName, const std::string& initialMessage) const; - void onRemovePartyRequest(const std::string& partyId) const; - - void onOtcUpdated(const bs::network::otc::PeerPtr &peer); - void onOtcPublicUpdated() const; - void onOTCPeerError(const bs::network::otc::PeerPtr &peer, bs::network::otc::PeerErrorType type, const std::string* errorMsg); - - void onOtcRequestSubmit() const; - void onOtcResponseAccept() const; - void onOtcResponseUpdate() const; - void onOtcQuoteRequestSubmit() const; - void onOtcQuoteResponseSubmit() const; - void onOtcPullOrRejectCurrent() const; - - void onOtcPrivatePartyReady(const Chat::ClientPartyPtr& clientPartyPtr) const; - - void onRequestAllPrivateMessages() const; - -signals: - // OTC - void sendOtcPbMessage(const std::string& data); - void chatRoomChanged(); - void requestPrimaryWalletCreation(); - void emailHashRequested(const std::string &email); - void onAboutToHide(); - -private: - friend class AbstractChatWidgetState; - friend class ChatLogOutState; - friend class IdleState; - friend class PrivatePartyInitState; - friend class PrivatePartyUninitState; - friend class PrivatePartyRequestedOutgoingState; - friend class PrivatePartyRequestedIncomingState; - - template ::value>::type> - void changeState(std::function&& transitionChanges = []() {}) - { - // Exit previous state - stateCurrent_.reset(); - - // Enter new state - transitionChanges(); - stateCurrent_ = std::make_unique(this); - stateCurrent_->applyState(); - } - -protected: - std::unique_ptr stateCurrent_; - -private: - void chatTransition(const Chat::ClientPartyPtr& clientPartyPtr); - - QScopedPointer ui_; - Chat::ChatClientServicePtr chatClientServicePtr_; - OTCRequestViewModel* otcRequestViewModel_ = nullptr; - QPointer otcHelper_{}; - std::shared_ptr loggerPtr_; - std::shared_ptr chatPartiesTreeModel_; - std::shared_ptr otcWindowsManager_{}; - - std::string ownUserId_; - std::string currentPartyId_; - QMap draftMessages_; - bool bNeedRefresh_ = false; - - bs::network::UserType userType_{}; -}; - -#endif // CHAT_WIDGET_H diff --git a/BlockSettleUILib/ChatUI/ChatWidget.ui b/BlockSettleUILib/ChatUI/ChatWidget.ui deleted file mode 100644 index d1235a706..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidget.ui +++ /dev/null @@ -1,802 +0,0 @@ - - - - ChatWidget - - - - 0 - 0 - 1006 - 666 - - - - - 0 - 0 - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 270 - 0 - - - - - 250 - 16777215 - - - - false - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - Chat ID - - - - - - - Qt::Horizontal - - - - 100 - 20 - - - - - - - - - 0 - 0 - - - - offline - - - Qt::TextSelectableByMouse - - - false - - - - - - - - - - 0 - - - 2 - - - 2 - - - - - - 0 - 100 - - - - - - - - Qt::NoFocus - - - Qt::ScrollBarAlwaysOff - - - QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed - - - Qt::ElideMiddle - - - QAbstractItemView::ScrollPerPixel - - - 10 - - - true - - - false - - - false - - - true - - - true - - - false - - - - - - - - - - - - false - - - true - - - false - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - - 8 - - - - CHAT # - - - Qt::AlignCenter - - - true - - - true - - - - - - - PushButton - - - - - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 1 - - - - - 0 - 1 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 8 - 20 - - - - - - - - - 0 - 0 - - - - - 101 - 0 - - - - Time - - - - 110 - 0 - - - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - - - - - - - - - 0 - 0 - - - - - 90 - 0 - - - - User - - - - - - - - 50 - 0 - - - - Message - - - - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - true - - - QTextEdit::AutoAll - - - true - - - false - - - true - - - false - - - true - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - ACCEPT/SEND - - - - - - - REJECT/CANCEL - - - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 56 - - - - - 16777215 - 84 - - - - false - - - true - - - TYPE MESSAGE AND SEND - - - true - - - false - - - true - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::WheelFocus - - - true - - - QAbstractItemView::SingleSelection - - - 0 - - - false - - - false - - - false - - - true - - - true - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 270 - 0 - - - - - 270 - 16777215 - - - - false - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - ChatMessagesTextEdit - QTextBrowser -
ChatUI/ChatMessagesTextEdit.h
-
- - OTCGlobalTable - QTreeView -
ChatUI/OTCGlobalTable.h
-
- - OTCShield - QWidget -
ChatUI/OTCShieldWidgets/OTCShield.h
-
- - CreateOTCResponseWidget - QWidget -
ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.h
- 1 -
- - OTCNegotiationResponseWidget - QWidget -
ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.h
- 1 -
- - OTCNegotiationRequestWidget - QWidget -
ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.h
- 1 -
- - PullOwnOTCRequestWidget - QWidget -
ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.h
- 1 -
- - SearchWidget - QWidget -
ChatUI/SearchWidget.h
- 1 -
- - CreateOTCRequestWidget - QWidget -
ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.h
- 1 -
- - ChatUserListTreeView - QTreeView -
ChatUI/ChatUserListTreeView.h
-
- - BSChatInput - QTextBrowser -
ChatUI/BSChatInput.h
-
-
- - -
diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/AbstractChatWidgetState.cpp b/BlockSettleUILib/ChatUI/ChatWidgetStates/AbstractChatWidgetState.cpp deleted file mode 100644 index 12fd1ab12..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/AbstractChatWidgetState.cpp +++ /dev/null @@ -1,494 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "AbstractChatWidgetState.h" - -#include "ChatUI/ChatWidget.h" -#include "ChatUI/ChatOTCHelper.h" -#include "ChatUI/ChatPartiesTreeModel.h" -#include "ChatUI/OTCRequestViewModel.h" -#include "NotificationCenter.h" -#include "OtcClient.h" -#include "OtcUtils.h" - -namespace { - const int kMaxMessageNotifLength = 20; - const auto kTimeoutErrorWith = QObject::tr("OTC negotiation with %1 timed out"); - const auto kCancelledErrorWith = QObject::tr("OTC negotiation with %1 was canceled"); - const auto kRejectedErrorWith = QObject::tr("OTC order was rejected: %1"); -} // namespace - -using namespace bs::network; - -AbstractChatWidgetState::AbstractChatWidgetState(ChatWidget* chat) - : chat_(chat) -{ -} - -void AbstractChatWidgetState::onSendMessage() -{ - if (!canSendMessage()) { - return; - } - - std::string messageText = chat_->ui_->input_textEdit->toPlainText().toStdString(); - if (messageText.empty()) { - return; - } - - chat_->chatClientServicePtr_->SendPartyMessage(chat_->currentPartyId_, messageText); - chat_->ui_->input_textEdit->clear(); -} - -void AbstractChatWidgetState::onProcessMessageArrived(const Chat::MessagePtrList& messagePtrList) -{ - if (!canReceiveMessage()) { - return; - } - - if (messagePtrList.empty()) { - return; - } - - // Update all UI elements - - // Tab notifier - for (const auto message : messagePtrList) - { - if (message->partyMessageState() == Chat::PartyMessageState::SENT && - chat_->ownUserId_ != message->senderHash()) { - - const auto clientPartyPtr = getParty(message->partyId()); - if (!clientPartyPtr) { - continue; - } - - const auto messageTitle = clientPartyPtr->displayName(); - auto messageText = message->messageText(); - const auto otcText = OtcUtils::toReadableString(QString::fromStdString(messageText)); - const auto isOtc = !otcText.isEmpty(); - - if (!isOtc && messageText.length() > kMaxMessageNotifLength) { - messageText = messageText.substr(0, kMaxMessageNotifLength) + "..."; - } - - bs::ui::NotifyMessage notifyMsg; - notifyMsg.append(QString::fromStdString(messageTitle)); - notifyMsg.append(isOtc ? otcText : QString::fromStdString(messageText)); - notifyMsg.append(QString::fromStdString(message->partyId())); - notifyMsg.append(isOtc); - - NotificationCenter::notify(bs::ui::NotifyType::UpdateUnreadMessage, notifyMsg); - // Update tree - if (message->partyId() != chat_->currentPartyId_) { - chat_->chatPartiesTreeModel_->onIncreaseUnseenCounter(message->partyId(), 1, isOtc); - } - } - } - - chat_->ui_->textEditMessages->onMessageUpdate(messagePtrList); - - if (canPerformOTCOperations()) { - chat_->otcHelper_->onMessageArrived(messagePtrList); - } -} - -void AbstractChatWidgetState::onChangePartyStatus(const Chat::ClientPartyPtr& clientPartyPtr) -{ - if (!canChangePartyStatus()) { - return; - } - - chat_->chatPartiesTreeModel_->onPartyStatusChanged(clientPartyPtr); - chat_->otcHelper_->onPartyStateChanged(clientPartyPtr); - onUpdateOTCShield(); -} - -void AbstractChatWidgetState::onResetPartyModel() -{ - if (!canResetPartyModel()) { - return; - } - - chat_->chatPartiesTreeModel_->onPartyModelChanged(); - chat_->onActivatePartyId(QString::fromStdString(chat_->currentPartyId_)); -} - -void AbstractChatWidgetState::onMessageRead(const std::string& partyId, const std::string& messageId) -{ - if (!canResetReadMessage()) { - return; - } - - chat_->chatClientServicePtr_->SetMessageSeen(partyId, messageId); -} - -void AbstractChatWidgetState::onChangeMessageState(const std::string& partyId, const std::string& message_id, const int party_message_state) -{ - if (!canChangeMessageState()) { - return; - } - - const Chat::MessagePtr message = chat_->ui_->textEditMessages->onMessageStatusChanged(partyId, message_id, party_message_state); - - // Update tree view if needed - if (static_cast(party_message_state) == Chat::PartyMessageState::SEEN - && message && message->senderHash() != chat_->ownUserId_) { - chat_->chatPartiesTreeModel_->onDecreaseUnseenCounter(partyId, 1); - } -} - -void AbstractChatWidgetState::onAcceptPartyRequest(const std::string& partyId) -{ - if (!canAcceptPartyRequest()) { - return; - } - - chat_->chatClientServicePtr_->AcceptPrivateParty(partyId); -} - -void AbstractChatWidgetState::onRejectPartyRequest(const std::string& partyId) -{ - if (!canRejectPartyRequest()) { - return; - } - - chat_->chatClientServicePtr_->RejectPrivateParty(partyId); -} - -void AbstractChatWidgetState::onSendPartyRequest(const std::string& partyId) -{ - if (!canSendPartyRequest()) { - return; - } - - chat_->chatClientServicePtr_->RequestPrivateParty(partyId); -} - -void AbstractChatWidgetState::onRemovePartyRequest(const std::string& partyId) -{ - if (!canRemovePartyRequest()) { - return; - } - - chat_->chatClientServicePtr_->DeletePrivateParty(partyId); -} - -void AbstractChatWidgetState::onNewPartyRequest(const std::string& partyName, const std::string& initialMessage) -{ - if (!canSendPartyRequest()) { - return; - } - - chat_->chatClientServicePtr_->RequestPrivateParty(partyName, initialMessage); -} - -void AbstractChatWidgetState::onUpdateDisplayName(const std::string& partyId, const std::string& contactName) -{ - if (!canUpdatePartyName()) { - return; - } - - Chat::ClientPartyModelPtr clientPartyModelPtr = chat_->chatClientServicePtr_->getClientPartyModelPtr(); - Chat::ClientPartyPtr clientPartyPtr = clientPartyModelPtr->getClientPartyById(partyId); - clientPartyPtr->setDisplayName(contactName); - - if (clientPartyPtr->isGlobalStandard() || (clientPartyPtr->isPrivateStandard() && chat_->currentPartyId_ == partyId)) { - chat_->ui_->textEditMessages->onUpdatePartyName(partyId); - } -} - -void AbstractChatWidgetState::onSendOtcMessage(const std::string &partyId, const std::string& data) -{ - if (canPerformOTCOperations()) { - chat_->chatClientServicePtr_->SendPartyMessage(partyId, data); - } -} - -void AbstractChatWidgetState::onSendOtcPublicMessage(const std::string &data) -{ - if (canPerformOTCOperations()) { - chat_->chatClientServicePtr_->SendPartyMessage(Chat::OtcRoomName, data); - } -} - -void AbstractChatWidgetState::onProcessOtcPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response) -{ - if (canReceiveOTCOperations()) { - chat_->otcHelper_->onProcessOtcPbMessage(response); - } -} - -void AbstractChatWidgetState::onOtcUpdated(const otc::PeerPtr &peer) -{ - if (canReceiveOTCOperations() && chat_->currentPeer() == peer) { - onUpdateOTCShield(); - } -} - -void AbstractChatWidgetState::onOtcPublicUpdated() -{ - if (!canReceiveOTCOperations()) { - return; - } - - onUpdateOTCShield(); - chat_->chatPartiesTreeModel_->onGlobalOTCChanged(); -} - -void AbstractChatWidgetState::onUpdateOTCShield() -{ - if (!canReceiveOTCOperations()) { - return; - } - - applyRoomsFrameChange(); -} - -void AbstractChatWidgetState::onOTCPeerError(const bs::network::otc::PeerPtr &peer, bs::network::otc::PeerErrorType type, const std::string* errorMsg) -{ - if (!canReceiveOTCOperations()) { - return; - } - - const Chat::ClientPartyPtr clientPartyPtr = getPartyByUserHash(peer->contactId); - if (!clientPartyPtr) { - return; - } - - QString MessageBody; - switch (type) - { - case bs::network::otc::PeerErrorType::Timeout: - MessageBody = kTimeoutErrorWith.arg(QString::fromStdString(clientPartyPtr->displayName())); - break; - case bs::network::otc::PeerErrorType::Canceled: - MessageBody = kCancelledErrorWith.arg(QString::fromStdString(clientPartyPtr->displayName())); - break; - case bs::network::otc::PeerErrorType::Rejected: - assert(errorMsg); - MessageBody = kRejectedErrorWith.arg(QString::fromStdString(*errorMsg)); - break; - default: - break; - } - bs::ui::NotifyMessage notifyMsg; - notifyMsg.append(MessageBody); - - NotificationCenter::notify(bs::ui::NotifyType::OTCOrderError, notifyMsg); -} - -void AbstractChatWidgetState::onOtcRequestSubmit() -{ - if (canPerformOTCOperations()) { - chat_->otcHelper_->onOtcRequestSubmit(chat_->currentPeer(), chat_->ui_->widgetNegotiateRequest->offer()); - } -} - -void AbstractChatWidgetState::onOtcResponseAccept() -{ - if (canPerformOTCOperations()) { - chat_->otcHelper_->onOtcResponseAccept(chat_->currentPeer(), chat_->ui_->widgetNegotiateResponse->offer()); - } -} - -void AbstractChatWidgetState::onOtcResponseUpdate() -{ - if (canPerformOTCOperations()) { - chat_->otcHelper_->onOtcResponseUpdate(chat_->currentPeer(), chat_->ui_->widgetNegotiateResponse->offer()); - } -} - -void AbstractChatWidgetState::onOtcQuoteRequestSubmit() -{ - if (canPerformOTCOperations()) { - chat_->otcHelper_->onOtcQuoteRequestSubmit(chat_->ui_->widgetCreateOTCRequest->request()); - } -} - -void AbstractChatWidgetState::onOtcQuoteResponseSubmit() -{ - if (canPerformOTCOperations()) { - chat_->chatClientServicePtr_->RequestPrivatePartyOTC(chat_->currentPeer()->contactId); - } -} - -void AbstractChatWidgetState::onOtcPrivatePartyReady(const Chat::ClientPartyPtr& clientPartyPtr) -{ - if (canPerformOTCOperations() && clientPartyPtr->isPrivateOTC()) { - Chat::PartyRecipientsPtrList recipients = clientPartyPtr->getRecipientsExceptMe(chat_->ownUserId_); - for (const auto& recipient : recipients) { - if (chat_->currentPeer() && recipient->userHash() == chat_->currentPeer()->contactId) { - // found user, send request - chat_->otcHelper_->onOtcQuoteResponseSubmit(chat_->currentPeer(), chat_->ui_->widgetCreateOTCResponse->response()); - } - } - } -} - -void AbstractChatWidgetState::onOtcPullOrRejectCurrent() -{ - if (canPerformOTCOperations()) { - auto peer = chat_->currentPeer(); - if (!peer) { - assert(false); - return; - } - - Chat::ClientPartyModelPtr clientPartyModelPtr = chat_->chatClientServicePtr_->getClientPartyModelPtr(); - Chat::ClientPartyPtr clientPartyPtr = clientPartyModelPtr->getOtcPartyForUsers(chat_->ownUserId_, peer->contactId); - if (clientPartyPtr) { - chat_->chatClientServicePtr_->DeletePrivateParty(clientPartyPtr->id()); - } - - chat_->ui_->treeViewOTCRequests->selectionModel()->clearCurrentIndex(); - chat_->otcHelper_->onOtcPullOrReject(peer); - } -} - -void AbstractChatWidgetState::saveDraftMessage() -{ - const auto draft = chat_->ui_->input_textEdit->toPlainText(); - - if (draft.isEmpty()) { - chat_->draftMessages_.remove(chat_->currentPartyId_); - } - else { - chat_->draftMessages_.insert(chat_->currentPartyId_, draft); - } -} - -void AbstractChatWidgetState::restoreDraftMessage() -{ - const auto iDraft = chat_->draftMessages_.find(chat_->currentPartyId_); - if (iDraft != chat_->draftMessages_.cend()) { - chat_->ui_->input_textEdit->setText(iDraft.value()); - auto cursor = chat_->ui_->input_textEdit->textCursor(); - cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor); - chat_->ui_->input_textEdit->setTextCursor(cursor); - } -} - -void AbstractChatWidgetState::updateOtc() -{ - if (!canPerformOTCOperations()) { - chat_->ui_->widgetOTCShield->showOtcAvailableToTradingParticipants(); - return; - } - - const bool globalRoom = chat_->currentPartyId_ == Chat::OtcRoomName; - const auto &peer = chat_->currentPeer(); - if (!peer && !globalRoom) { - chat_->ui_->widgetOTCShield->showContactIsOffline(); - return; - } - - if (chat_->ui_->widgetOTCShield->onRequestCheckWalletSettings()) { - return; - } - - if (!peer) { - // Must be in globalRoom if checks above hold - assert(globalRoom); - chat_->ui_->stackedWidgetOTC->setCurrentIndex(static_cast(OTCPages::OTCCreateRequestPage)); - return; - } - - auto* otcWidget = qobject_cast(chat_->ui_->stackedWidgetOTC->currentWidget()); - bs::UtxoReservationToken reservation; // let's carry on reservation between otc windows changed - if (otcWidget) { - reservation = otcWidget->releaseReservation(); - if (!reservation.isValid()) { - reservation = chat_->otcHelper_->client()->releaseReservation(peer); - } - } - - using bs::network::otc::State; - OTCPages pageNumber = OTCPages::OTCShield; - switch (peer->state) { - case State::Idle: - if (peer->type == otc::PeerType::Contact) { - pageNumber = OTCPages::OTCNegotiateRequestPage; - } else if (peer->isOwnRequest) { - chat_->ui_->widgetPullOwnOTCRequest->setRequest(peer->request); - pageNumber = OTCPages::OTCPullOwnOTCRequestPage; - } else if (peer->type == otc::PeerType::Request) { - chat_->ui_->widgetCreateOTCResponse->setRequest(peer->request); - pageNumber = OTCPages::OTCCreateResponsePage; - } - reservation.release(); - break; - case State::QuoteSent: - chat_->ui_->widgetPullOwnOTCRequest->setResponse(peer->response); - pageNumber = OTCPages::OTCPullOwnOTCRequestPage; - break; - case State::QuoteRecv: - pageNumber = OTCPages::OTCNegotiateRequestPage; - break; - case State::OfferSent: - chat_->ui_->widgetPullOwnOTCRequest->setOffer(peer->offer); - pageNumber = OTCPages::OTCPullOwnOTCRequestPage; - break; - case State::OfferRecv: - chat_->ui_->widgetNegotiateResponse->setOffer(peer->offer); - pageNumber = OTCPages::OTCNegotiateResponsePage; - break; - case State::SentPayinInfo: - case State::WaitPayinInfo: - chat_->ui_->widgetOTCShield->showOtcSetupTransaction(); - if (reservation.isValid()) { - chat_->otcHelper_->client()->setReservation(peer, std::move(reservation)); - } - return; - case State::WaitBuyerSign: - chat_->ui_->widgetPullOwnOTCRequest->setPendingBuyerSign(peer->offer); - pageNumber = OTCPages::OTCPullOwnOTCRequestPage; - break; - case State::WaitSellerSeal: - chat_->ui_->widgetPullOwnOTCRequest->setPendingSellerSign(peer->offer); - pageNumber = OTCPages::OTCPullOwnOTCRequestPage; - break; - case State::WaitVerification: - case State::WaitSellerSign: - chat_->ui_->widgetOTCShield->showOtcSetupTransaction(); - if (reservation.isValid()) { - chat_->otcHelper_->client()->setReservation(peer, std::move(reservation)); - } - return; - case State::Blacklisted: - chat_->ui_->widgetOTCShield->showContactIsOffline(); - reservation.release(); - return; - default: - assert(false && " Did you forget to handle new otc::State state? "); - break; - } - - otcWidget = qobject_cast(chat_->ui_->stackedWidgetOTC->widget(static_cast(pageNumber))); - if (otcWidget) { - otcWidget->setPeer(*peer); - otcWidget->setReservation(std::move(reservation)); - otcWidget->onAboutToApply(); - } - - chat_->ui_->stackedWidgetOTC->setCurrentIndex(static_cast(pageNumber)); -} - -Chat::ClientPartyPtr AbstractChatWidgetState::getParty(const std::string& partyId) const -{ - Chat::ClientPartyModelPtr partyModel = chat_->chatClientServicePtr_->getClientPartyModelPtr(); - return partyModel->getClientPartyById(partyId); -} - -Chat::ClientPartyPtr AbstractChatWidgetState::getPartyByUserHash(const std::string& userHash) const -{ - Chat::ClientPartyModelPtr partyModel = chat_->chatClientServicePtr_->getClientPartyModelPtr(); - return partyModel->getStandardPartyForUsers(chat_->ownUserId_, userHash); -} diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/AbstractChatWidgetState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/AbstractChatWidgetState.h deleted file mode 100644 index 9c4c3f048..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/AbstractChatWidgetState.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef ABSTRACTCHATWIDGETSTATE_H -#define ABSTRACTCHATWIDGETSTATE_H - -#include "ChatProtocol/Message.h" -#include "ChatProtocol/ClientParty.h" -#include "ui_ChatWidget.h" - -enum class OTCPages : int -{ - OTCShield = 0, - OTCCreateRequestPage, - OTCCreateResponsePage, - OTCPullOwnOTCRequestPage, - OTCNegotiateRequestPage, - OTCNegotiateResponsePage -}; - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - } - } -} - -enum class StackedMessages { - TextEditMessage = 0, - OTCTable = 1 -}; - -class ChatWidget; -class AbstractChatWidgetState -{ -public: - explicit AbstractChatWidgetState(ChatWidget* chat); - virtual ~AbstractChatWidgetState() = default; - - void applyState() { - applyUserFrameChange(); - applyChatFrameChange(); - applyRoomsFrameChange(); - applyPostChanged(); - } - -protected: - virtual void applyUserFrameChange() = 0; - virtual void applyChatFrameChange() = 0; - virtual void applyRoomsFrameChange() = 0; - virtual void applyPostChanged() = 0; - - // slots -public: - void onSendMessage(); - void onProcessMessageArrived(const Chat::MessagePtrList& messagePtrList); - void onChangePartyStatus(const Chat::ClientPartyPtr& clientPartyPtr); - void onResetPartyModel(); - void onMessageRead(const std::string& partyId, const std::string& messageId); - void onChangeMessageState(const std::string& partyId, const std::string& message_id, const int party_message_state); - void onAcceptPartyRequest(const std::string& partyId); - void onRejectPartyRequest(const std::string& partyId); - void onSendPartyRequest(const std::string& partyId); - void onRemovePartyRequest(const std::string& partyId); - void onNewPartyRequest(const std::string& partyName, const std::string& initialMessage); - void onUpdateDisplayName(const std::string& partyId, const std::string& contactName); - - - // OTC - void onSendOtcMessage(const std::string &partyId, const std::string& data); - void onSendOtcPublicMessage(const std::string& data); - void onProcessOtcPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response); - - void onOtcUpdated(const bs::network::otc::PeerPtr &peer); - void onOtcPublicUpdated(); - void onUpdateOTCShield(); - void onOTCPeerError(const bs::network::otc::PeerPtr &peer, bs::network::otc::PeerErrorType type, const std::string* errorMsg); - - void onOtcRequestSubmit(); - void onOtcResponseAccept(); - void onOtcResponseUpdate(); - - void onOtcQuoteRequestSubmit(); - void onOtcQuoteResponseSubmit(); - - void onOtcPullOrRejectCurrent(); - - void onOtcPrivatePartyReady(const Chat::ClientPartyPtr& clientPartyPtr); - -protected: - - virtual bool canSendMessage() const { return false; } - virtual bool canReceiveMessage() const { return true; } - virtual bool canChangePartyStatus() const { return true; } - virtual bool canResetPartyModel() const { return true; } - virtual bool canResetReadMessage() const { return true; } - virtual bool canChangeMessageState() const { return true; } - virtual bool canAcceptPartyRequest() const { return true; } - virtual bool canRejectPartyRequest() const { return true; } - virtual bool canSendPartyRequest() const { return true; } - virtual bool canRemovePartyRequest() const { return true; } - virtual bool canUpdatePartyName() const { return true; } - virtual bool canPerformOTCOperations() const { return false; } - virtual bool canReceiveOTCOperations() const { return true; } - - void saveDraftMessage(); - void restoreDraftMessage(); - - void updateOtc(); - - Chat::ClientPartyPtr getParty(const std::string& partyId) const; - Chat::ClientPartyPtr getPartyByUserHash(const std::string& userHash) const; - - ChatWidget* chat_; -}; - -#endif // ABSTRACTCHATWIDGETSTATE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/ChatLogOutState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/ChatLogOutState.h deleted file mode 100644 index d4d5999a1..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/ChatLogOutState.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATLOGOUTSTATE_H -#define CHATLOGOUTSTATE_H - -#include "AbstractChatWidgetState.h" -#include "ChatUI/ChatOTCHelper.h" - -class ChatLogOutState : public AbstractChatWidgetState { -public: - explicit ChatLogOutState(ChatWidget* chat) : AbstractChatWidgetState(chat) {} - ~ChatLogOutState() override = default; -protected: - void applyUserFrameChange() override { - auto* searchWidget = chat_->ui_->searchWidget; - searchWidget->onClearLineEdit(); - searchWidget->onSetLineEditEnabled(false); - - if (chat_->chatPartiesTreeModel_) { - chat_->chatPartiesTreeModel_->onCleanModel(); - } - - chat_->ui_->labelUserName->setText(QObject::tr("offline")); - chat_->ui_->labelUserName->setProperty("headerLabelActivated", {}); - chat_->ui_->labelUserName->setTextInteractionFlags(Qt::NoTextInteraction); - qApp->style()->unpolish(chat_->ui_->labelUserName); - qApp->style()->polish(chat_->ui_->labelUserName); - } - void applyChatFrameChange() override { - chat_->ui_->stackedWidgetMessages->setCurrentIndex(static_cast(StackedMessages::TextEditMessage)); - chat_->ui_->textEditMessages->onLogout(); - - chat_->ui_->frameContactActions->setVisible(false); - - chat_->ui_->input_textEdit->setText(QLatin1Literal("")); - chat_->ui_->input_textEdit->setVisible(false); - chat_->ui_->input_textEdit->setEnabled(false); - - chat_->draftMessages_.clear(); - } - void applyRoomsFrameChange() override { - if (chat_->otcHelper_) { - chat_->otcHelper_->onLogout(); - } - - chat_->ui_->widgetOTCShield->showLoginToAccessOTC(); - } - - void applyPostChanged() override {}; - - bool canReceiveMessage() const override { return false; } - bool canResetReadMessage() const override { return false; } - bool canResetPartyModel() const override { return false; } - bool canChangeMessageState() const override { return false; } - bool canAcceptPartyRequest() const override { return false; } - bool canRejectPartyRequest() const override { return false; } - bool canSendPartyRequest() const override { return false; } - bool canRemovePartyRequest() const override { return false; } - bool canUpdatePartyName() const override { return false; } - bool canReceiveOTCOperations() const override { return false; } - -}; - -#endif // CHATLOGOUTSTATE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/ChatWidgetStates.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/ChatWidgetStates.h deleted file mode 100644 index 0e2fb41e2..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/ChatWidgetStates.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef CHATWIDGETSTATES_H -#define CHATWIDGETSTATES_H - -#include "ChatLogOutState.h" -#include "IdleState.h" -#include "PrivatePartyInitState.h" -#include "PrivatePartyUninitState.h" -#include "PrivatePartyRequestedOutgoingState.h" -#include "PrivatePartyRequestedIncomingState.h" - -#endif // CHATWIDGETSTATES_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/IdleState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/IdleState.h deleted file mode 100644 index 61123cfae..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/IdleState.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef IDLESTATE_H -#define IDLESTATE_H - -#include "AbstractChatWidgetState.h" - -class IdleState : public AbstractChatWidgetState { -public: - explicit IdleState(ChatWidget* chat) : AbstractChatWidgetState(chat) {} - ~IdleState() override = default; -protected: - void applyUserFrameChange() override { - chat_->ui_->searchWidget->onSetLineEditEnabled(true); - chat_->ui_->textEditMessages->onSwitchToChat({}); - - chat_->ui_->labelUserName->setText(QString::fromStdString(chat_->ownUserId_)); - chat_->ui_->labelUserName->setProperty("headerLabelActivated", true); - chat_->ui_->labelUserName->setTextInteractionFlags(Qt::TextSelectableByMouse); - qApp->style()->unpolish(chat_->ui_->labelUserName); - qApp->style()->polish(chat_->ui_->labelUserName); - } - void applyChatFrameChange() override { - chat_->ui_->textEditMessages->onSetOwnUserId(chat_->ownUserId_); - chat_->ui_->textEditMessages->onSwitchToChat(chat_->currentPartyId_); - - chat_->ui_->frameContactActions->setVisible(false); - - chat_->ui_->input_textEdit->setText({}); - chat_->ui_->input_textEdit->setVisible(true); - chat_->ui_->input_textEdit->setEnabled(false); - } - void applyRoomsFrameChange() override {} - void applyPostChanged() override {}; -}; - -#endif // IDLESTATE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyInitState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyInitState.h deleted file mode 100644 index 89eb463cf..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyInitState.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef PRIVATEPARTYINITSTATE_H -#define PRIVATEPARTYINITSTATE_H - -#include "AbstractChatWidgetState.h" -#include "ChatUI/ChatWidget.h" -#include "OtcClient.h" -#include "ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.h" -#include "ui_ChatWidget.h" - -class PrivatePartyInitState : public AbstractChatWidgetState { -public: - explicit PrivatePartyInitState(ChatWidget* chat) : AbstractChatWidgetState(chat) {} - ~PrivatePartyInitState() override { - saveDraftMessage(); - - // leave per, so let's send ownership over reservation to otc client if any - auto* otcWidget = qobject_cast(chat_->ui_->stackedWidgetOTC->currentWidget()); - if (otcWidget) { - auto reservation = otcWidget->releaseReservation(); - const auto &peer = chat_->currentPeer(); - if (reservation.isValid() && peer) { - chat_->otcHelper_->client()->setReservation(peer, std::move(reservation)); - } - } - }; -protected: - void applyUserFrameChange() override {} - void applyChatFrameChange() override { - Chat::ClientPartyPtr clientPartyPtr = getParty(chat_->currentPartyId_); - - assert(clientPartyPtr); - if (clientPartyPtr->isGlobalOTC()) { - chat_->ui_->treeViewOTCRequests->selectionModel()->reset(); - chat_->ui_->stackedWidgetMessages->setCurrentIndex(static_cast(StackedMessages::OTCTable)); - chat_->ui_->showHistoryButton->setVisible(false); - return; - } - - chat_->ui_->stackedWidgetMessages->setCurrentIndex(static_cast(StackedMessages::TextEditMessage)); - chat_->ui_->textEditMessages->onSwitchToChat(chat_->currentPartyId_); - - chat_->ui_->frameContactActions->setVisible(false); - - chat_->ui_->input_textEdit->setText({}); - chat_->ui_->input_textEdit->setVisible(true); - chat_->ui_->input_textEdit->setEnabled(true); - chat_->ui_->input_textEdit->setFocus(); - - restoreDraftMessage(); - } - void applyRoomsFrameChange() override { - Chat::ClientPartyPtr clientPartyPtr = getParty(chat_->currentPartyId_); - - auto checkIsTradingParticipant = [&]() -> bool { - const auto userCelerType = chat_->userType_; - if (bs::network::UserType::Dealing != userCelerType - && bs::network::UserType::Trading != userCelerType) { - chat_->ui_->widgetOTCShield->showOtcAvailableToTradingParticipants(); - return false; - } - - return true; - }; - - if (!clientPartyPtr) { - updateOtc(); - return; - } - - if (clientPartyPtr->isGlobalOTC()) { - if (!checkIsTradingParticipant()) { - return; - } - } - else if (clientPartyPtr->isGlobal()) { - chat_->ui_->widgetOTCShield->showChatExplanation(); - return; - } - else if (clientPartyPtr->isPrivate()) { - if (!checkIsTradingParticipant()) { - return; - } - - if (clientPartyPtr->clientStatus() == Chat::ClientStatus::OFFLINE) { - chat_->ui_->widgetOTCShield->showContactIsOffline(); - return; - } - - Chat::PartyRecipientPtr recipientPtr = clientPartyPtr->getSecondRecipient(chat_->ownUserId_); - CelerClient::CelerUserType counterPartyCelerType = recipientPtr->celerType(); - if (counterPartyCelerType != bs::network::UserType::Dealing - && counterPartyCelerType != bs::network::UserType::Trading) { - chat_->ui_->widgetOTCShield->showCounterPartyIsntTradingParticipant(); - return; - } - // check other party - } - - updateOtc(); - } - void applyPostChanged() override { - // enter peer chat window, let's take ownership on reservation - auto* otcWidget = qobject_cast(chat_->ui_->stackedWidgetOTC->currentWidget()); - const auto &peer = chat_->currentPeer(); - if (otcWidget && peer) { - otcWidget->setReservation(chat_->otcHelper_->client()->releaseReservation(peer)); - } - }; - - bool canSendMessage() const override { return true; } - bool canPerformOTCOperations() const override { return true; } -}; - -#endif // PRIVATEPARTYINITSTATE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyRequestedIncomingState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyRequestedIncomingState.h deleted file mode 100644 index 88729ba21..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyRequestedIncomingState.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef PRIVATEPARTYREQUESTEDINCOMINGSTATE_H -#define PRIVATEPARTYREQUESTEDINCOMINGSTATE_H - -#include "AbstractChatWidgetState.h" - -namespace { - // translation - const QString buttonAcceptPartyText = QObject::tr("ACCEPT"); - const QString buttonRejectPartyText = QObject::tr("REJECT"); -} - - -class PrivatePartyRequestedIncomingState : public AbstractChatWidgetState { -public: - explicit PrivatePartyRequestedIncomingState(ChatWidget* chat) : AbstractChatWidgetState(chat) {} - ~PrivatePartyRequestedIncomingState() override = default; -protected: - void applyUserFrameChange() override {} - void applyChatFrameChange() override { - chat_->ui_->textEditMessages->onSwitchToChat(chat_->currentPartyId_); - - chat_->ui_->pushButton_AcceptSend->setText(buttonAcceptPartyText); - chat_->ui_->pushButton_AcceptSend->disconnect(); - QObject::connect(chat_->ui_->pushButton_AcceptSend, &QPushButton::clicked, chat_, [this]() { - chat_->onContactRequestAcceptClicked(chat_->currentPartyId_); - }); - - chat_->ui_->pushButton_RejectCancel->setText(buttonRejectPartyText); - chat_->ui_->pushButton_RejectCancel->disconnect(); - QObject::connect(chat_->ui_->pushButton_RejectCancel, &QPushButton::clicked, chat_, [this]() { - chat_->onContactRequestRejectClicked(chat_->currentPartyId_); - }); - - chat_->ui_->frameContactActions->setVisible(true); - - chat_->ui_->input_textEdit->setText({}); - chat_->ui_->input_textEdit->setVisible(true); - chat_->ui_->input_textEdit->setEnabled(false); - } - void applyRoomsFrameChange() override { - chat_->ui_->widgetOTCShield->showOtcAvailableOnlyForAcceptedContacts(); - } - void applyPostChanged() override {}; -}; - -#endif // PRIVATEPARTYREQUESTEDINCOMINGSTATE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyRequestedOutgoingState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyRequestedOutgoingState.h deleted file mode 100644 index e17d73eff..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyRequestedOutgoingState.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef PRIVATEPARTYREQUESTEDOUTGOINGSTATE_H -#define PRIVATEPARTYREQUESTEDOUTGOINGSTATE_H - -#include "AbstractChatWidgetState.h" - -class PrivatePartyRequestedOutgoingState : public AbstractChatWidgetState { -public: - explicit PrivatePartyRequestedOutgoingState(ChatWidget* chat) : AbstractChatWidgetState(chat) {} - ~PrivatePartyRequestedOutgoingState() override = default; -protected: - void applyUserFrameChange() override {} - void applyChatFrameChange() override { - chat_->ui_->textEditMessages->onSwitchToChat(chat_->currentPartyId_); - - chat_->ui_->frameContactActions->setVisible(false); - - chat_->ui_->input_textEdit->setText({}); - chat_->ui_->input_textEdit->setVisible(true); - chat_->ui_->input_textEdit->setEnabled(false); - } - void applyRoomsFrameChange() override { - chat_->ui_->widgetOTCShield->showShieldOtcAvailableOnceAccepted(); - } - void applyPostChanged() override {}; -}; - -#endif // PRIVATEPARTYREQUESTEDOUTGOINGSTATE_H diff --git a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyUninitState.h b/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyUninitState.h deleted file mode 100644 index b5d2de78b..000000000 --- a/BlockSettleUILib/ChatUI/ChatWidgetStates/PrivatePartyUninitState.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef PRIVATEPARTYUNINITSTATE_H -#define PRIVATEPARTYUNINITSTATE_H - -#include "AbstractChatWidgetState.h" - -namespace { - // translation - const QString buttonSentPartyText = QObject::tr("SEND"); - const QString buttonCancelPartyText = QObject::tr("CANCEL"); -} - - -class PrivatePartyUninitState : public AbstractChatWidgetState { -public: - explicit PrivatePartyUninitState(ChatWidget* chat) : AbstractChatWidgetState(chat) {} - ~PrivatePartyUninitState() override = default; -protected: - void applyUserFrameChange() override {} - void applyChatFrameChange() override { - chat_->ui_->textEditMessages->onSwitchToChat(chat_->currentPartyId_); - - chat_->ui_->pushButton_AcceptSend->setText(buttonSentPartyText); - chat_->ui_->pushButton_AcceptSend->disconnect(); - QObject::connect(chat_->ui_->pushButton_AcceptSend, &QPushButton::clicked, chat_, [this]() { - chat_->onContactRequestSendClicked(chat_->currentPartyId_); - }); - - chat_->ui_->pushButton_RejectCancel->setText(buttonCancelPartyText); - chat_->ui_->pushButton_RejectCancel->disconnect(); - QObject::connect(chat_->ui_->pushButton_RejectCancel, &QPushButton::clicked, chat_, [this]() { - chat_->onContactRequestCancelClicked(chat_->currentPartyId_); - }); - - chat_->ui_->frameContactActions->setVisible(true); - - chat_->ui_->input_textEdit->setText({}); - chat_->ui_->input_textEdit->setVisible(true); - chat_->ui_->input_textEdit->setEnabled(false); - } - void applyRoomsFrameChange() override { - chat_->ui_->widgetOTCShield->showOtcAvailableOnlyForAcceptedContacts(); - } - void applyPostChanged() override {}; -}; - -#endif // PRIVATEPARTYUNINITSTATE_H diff --git a/BlockSettleUILib/ChatUI/OTCGlobalTable.cpp b/BlockSettleUILib/ChatUI/OTCGlobalTable.cpp deleted file mode 100644 index 7c82268c7..000000000 --- a/BlockSettleUILib/ChatUI/OTCGlobalTable.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCGlobalTable.h" -#include "OTCRequestViewModel.h" -#include "OtcTypes.h" - -namespace { - const int kLeftOffset = 10; -} - -OTCGlobalTable::OTCGlobalTable(QWidget* parent) - : TreeViewWithEnterKey(parent) -{ - setItemDelegateForColumn( - static_cast(OTCRequestViewModel::Columns::Duration), new OTCRequestsProgressDelegate(this)); - setItemDelegateForColumn( - static_cast(OTCRequestViewModel::Columns::Security), new LeftOffsetDelegate(this)); -} - -void OTCGlobalTable::drawRow(QPainter* painter,const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - if (!index.isValid()) { - TreeViewWithEnterKey::drawRow(painter, option, index); - return; - } - - const auto quotesModel = static_cast(model()); - bool isOwnQuote = quotesModel->data(index, static_cast(CustomRoles::OwnQuote)).toBool(); - - QStyleOptionViewItem itemOption(option); - if (isOwnQuote) { - itemOption.palette.setColor(QPalette::Text, itemStyle_.colorUserOnline()); - } - - TreeViewWithEnterKey::drawRow(painter, itemOption, index); -} - -bool OTCRequestsProgressDelegate::isDrawProgressBar(const QModelIndex& index) const -{ - return true; -} - -int OTCRequestsProgressDelegate::maxValue(const QModelIndex& index) const -{ - return std::chrono::duration_cast( - bs::network::otc::publicRequestTimeout()).count(); -} - -int OTCRequestsProgressDelegate::currentValue(const QModelIndex& index) const -{ - QDateTime startTimeStamp = index.data(static_cast(CustomRoles::RequestTimeStamp)).toDateTime(); - QDateTime endTimeStamp = startTimeStamp.addSecs( - std::chrono::duration_cast( - bs::network::otc::publicRequestTimeout()).count()); - - return QDateTime::currentDateTime().secsTo(endTimeStamp); -} - -void LeftOffsetDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opt, const QModelIndex& index) const -{ - QStyleOptionViewItem changedOpt = opt; - changedOpt.rect.setLeft(changedOpt.rect.left() + kLeftOffset); - - QStyledItemDelegate::paint(painter, changedOpt, index); -} diff --git a/BlockSettleUILib/ChatUI/OTCGlobalTable.h b/BlockSettleUILib/ChatUI/OTCGlobalTable.h deleted file mode 100644 index b79030600..000000000 --- a/BlockSettleUILib/ChatUI/OTCGlobalTable.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTC_GLOBAL_TABLE_H__ -#define __OTC_GLOBAL_TABLE_H__ - -#include "ChatUI/ChatUsersViewItemStyle.h" -#include "TreeViewWithEnterKey.h" -#include "ProgressViewDelegateBase.h" - -class OTCGlobalTable : public TreeViewWithEnterKey -{ - Q_OBJECT -public: - explicit OTCGlobalTable(QWidget* parent = nullptr); - ~OTCGlobalTable() override = default; - -protected: - void drawRow(QPainter* painter, const QStyleOptionViewItem& option, - const QModelIndex& index) const override; - -private: - ChatUsersViewItemStyle itemStyle_; -}; - -class OTCRequestsProgressDelegate : public ProgressViewDelegateBase -{ -public: - explicit OTCRequestsProgressDelegate(QWidget* parent = nullptr) - : ProgressViewDelegateBase(parent) - {} - ~OTCRequestsProgressDelegate() override = default; -protected: - bool isDrawProgressBar(const QModelIndex& index) const override; - int maxValue(const QModelIndex& index) const override; - int currentValue(const QModelIndex& index) const override; -}; - -class LeftOffsetDelegate : public QStyledItemDelegate -{ -public: - explicit LeftOffsetDelegate(QWidget* parent = nullptr) - : QStyledItemDelegate(parent) - {} - ~LeftOffsetDelegate() override = default; - - void paint(QPainter* painter, const QStyleOptionViewItem& opt, - const QModelIndex& index) const override; -}; - -#endif // __OTC_GLOBAL_TABLE_H__ diff --git a/BlockSettleUILib/ChatUI/OTCRequestViewModel.cpp b/BlockSettleUILib/ChatUI/OTCRequestViewModel.cpp deleted file mode 100644 index 6d57cca6e..000000000 --- a/BlockSettleUILib/ChatUI/OTCRequestViewModel.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCRequestViewModel.h" - -#include "OtcClient.h" -#include "OtcTypes.h" - -using namespace bs::network; - -namespace { - - const int kUpdateTimerInterval = 500; - - QString side(bs::network::otc::Side requestSide, bool isOwnRequest) { - if (!isOwnRequest) { - requestSide = bs::network::otc::switchSide(requestSide); - } - - return QString::fromStdString(otc::toString(requestSide)); - } - -} // namespace - -OTCRequestViewModel::OTCRequestViewModel(OtcClient *otcClient, QObject* parent) - : QAbstractTableModel(parent) - , otcClient_(otcClient) -{ - connect(&updateDurationTimer_, &QTimer::timeout, this, &OTCRequestViewModel::onUpdateDuration); - - updateDurationTimer_.setInterval(kUpdateTimerInterval); - updateDurationTimer_.start(); -} - -int OTCRequestViewModel::rowCount(const QModelIndex &parent) const -{ - return int(request_.size()); -} - -int OTCRequestViewModel::columnCount(const QModelIndex &parent) const -{ - return int(Columns::Latest) + 1; -} - -QVariant OTCRequestViewModel::data(const QModelIndex &index, int role) const -{ - const auto &requestData = request_.at(size_t(index.row())); - const auto &request = requestData.request_; - const auto column = Columns(index.column()); - - switch (role) { - case Qt::TextAlignmentRole: - return { static_cast(Qt::AlignLeft | Qt::AlignVCenter) }; - - case Qt::DisplayRole: - switch (column) { - case Columns::Security: return QStringLiteral("EUR/XBT"); - case Columns::Type: return QStringLiteral("OTC"); - case Columns::Product: return QStringLiteral("XBT"); - case Columns::Side: return side(request.ourSide, requestData.isOwnRequest_); - case Columns::Quantity: return QString::fromStdString(otc::toString(request.rangeType)); - case Columns::Duration: return {}; // OTCRequestsProgressDelegate - } - assert(false); - return {}; - - case static_cast(CustomRoles::OwnQuote): - return { requestData.isOwnRequest_ }; - - case static_cast(CustomRoles::RequestTimeStamp) : - return { request.timestamp }; - - default: - return {}; - } -} - -QVariant OTCRequestViewModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - switch (Columns(section)) { - case Columns::Security: return tr("Security"); - case Columns::Type: return tr("Type"); - case Columns::Product: return tr("Product"); - case Columns::Side: return tr("Side"); - case Columns::Quantity: return tr("Quantity"); - case Columns::Duration: return tr("Duration"); - } - assert(false); - return {}; - } - - return QVariant{}; -} - -QModelIndex OTCRequestViewModel::getIndexByTimestamp(QDateTime timeStamp) -{ - for (int iReq = 0; iReq < request_.size(); ++iReq) { - if (timeStamp == request_[iReq].request_.timestamp) { - return index(iReq, 0); - } - } - - return {}; -} - -void OTCRequestViewModel::onRequestsUpdated() -{ - beginResetModel(); - request_.clear(); - for (const auto &peer : otcClient_->requests()) { - request_.push_back({ peer->request, peer->isOwnRequest }); - } - endResetModel(); - emit restoreSelectedIndex(); -} - -void OTCRequestViewModel::onUpdateDuration() -{ - if (rowCount() == 0) { - return; - } - - const qint64 timeout = QDateTime::currentDateTime().toSecsSinceEpoch() - std::chrono::duration_cast( - bs::network::otc::publicRequestTimeout()).count(); - - auto it = std::remove_if(request_.begin(), request_.end(), [&](const OTCRequest& req) { - return req.request_.timestamp.toSecsSinceEpoch() < timeout; - }); - - if (it != request_.end()) { - const int startIndex = std::distance(request_.begin(), it); - beginRemoveRows({}, startIndex, request_.size() - 1); - request_.erase(it, request_.end()); - endRemoveRows(); - } - - emit dataChanged(index(0, static_cast(Columns::Duration)), - index(rowCount() - 1, static_cast(Columns::Duration)), { Qt::DisplayRole }); -} diff --git a/BlockSettleUILib/ChatUI/OTCRequestViewModel.h b/BlockSettleUILib/ChatUI/OTCRequestViewModel.h deleted file mode 100644 index 04d914010..000000000 --- a/BlockSettleUILib/ChatUI/OTCRequestViewModel.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTC_REQUEST_VIEW_MODEL_H__ -#define __OTC_REQUEST_VIEW_MODEL_H__ - -#include -#include - -#include "OtcTypes.h" - -class OtcClient; - -enum class CustomRoles -{ - OwnQuote = Qt::UserRole + 1, - RequestTimeStamp -}; - -class OTCRequestViewModel : public QAbstractTableModel -{ - Q_OBJECT - -public: - OTCRequestViewModel(OtcClient *otcClient, QObject* parent = nullptr); - ~OTCRequestViewModel() override = default; - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - QModelIndex getIndexByTimestamp(QDateTime timeStamp); - - enum class Columns - { - Security, - Type, - Product, - Side, - Quantity, - Duration, - - Latest = Duration, - }; - -public slots: - void onRequestsUpdated(); - -private slots: - void onUpdateDuration(); - -signals: - void restoreSelectedIndex(); - -private: - struct OTCRequest - { - bs::network::otc::QuoteRequest request_; - bool isOwnRequest_; - }; - std::vector request_; - - OtcClient *otcClient_{}; - QTimer updateDurationTimer_; - -}; - -#endif diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.cpp deleted file mode 100644 index 0eaf5908a..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "CreateOTCRequestWidget.h" - -#include "AssetManager.h" -#include "OtcTypes.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncWalletsManager.h" -#include "ui_CreateOTCRequestWidget.h" - -#include -#include - -using namespace bs::network; - -CreateOTCRequestWidget::CreateOTCRequestWidget(QWidget* parent) - : OTCWindowsAdapterBase{parent} - , ui_{new Ui::CreateOTCRequestWidget{}} -{ - ui_->setupUi(this); -} - -CreateOTCRequestWidget::~CreateOTCRequestWidget() = default; - -void CreateOTCRequestWidget::init(bs::network::otc::Env env) -{ - env_ = static_cast(env); - connect(ui_->pushButtonBuy, &QPushButton::clicked, this, &CreateOTCRequestWidget::onBuyClicked); - connect(ui_->pushButtonSell, &QPushButton::clicked, this, &CreateOTCRequestWidget::onSellClicked); - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &CreateOTCRequestWidget::requestCreated); - connect(ui_->pushButtonNumCcy, &QPushButton::clicked, this, &CreateOTCRequestWidget::onNumCcySelected); - - onSellClicked(); -} - -otc::QuoteRequest CreateOTCRequestWidget::request() const -{ - bs::network::otc::QuoteRequest result; - result.rangeType = otc::RangeType(ui_->comboBoxRange->currentData().toInt()); - result.ourSide = ui_->pushButtonSell->isChecked() ? otc::Side::Sell : otc::Side::Buy; - return result; -} - -void CreateOTCRequestWidget::onSellClicked() -{ - ui_->pushButtonSell->setChecked(true); - ui_->pushButtonBuy->setChecked(false); - onUpdateBalances(); -} - -void CreateOTCRequestWidget::onBuyClicked() -{ - ui_->pushButtonSell->setChecked(false); - ui_->pushButtonBuy->setChecked(true); - - onUpdateBalances(); -} - -void CreateOTCRequestWidget::onNumCcySelected() -{ - ui_->pushButtonNumCcy->setChecked(true); - ui_->pushButtonDenomCcy->setChecked(false); -} - -void CreateOTCRequestWidget::onUpdateBalances() -{ - QString totalBalance; - if (ui_->pushButtonBuy->isChecked()) { - const auto totalAssetBalance = getAssetManager()->getBalance(buyProduct_.toStdString(), bs::UTXOReservationManager::kIncludeZcOtc, nullptr); - - totalBalance = tr("%1 %2") - .arg(UiUtils::displayCurrencyAmount(totalAssetBalance)) - .arg(buyProduct_); - updateXBTRange(false, totalAssetBalance); - } - else { - const auto totalXBTBalance = getWalletManager()->getTotalBalance(); - - totalBalance = tr("%1 %2") - .arg(UiUtils::displayAmount(totalXBTBalance)) - .arg(QString::fromStdString(bs::network::XbtCurrency)); - - updateXBTRange(true, totalXBTBalance); - } - - ui_->labelBalanceValue->setText(totalBalance); -} - -void CreateOTCRequestWidget::updateXBTRange(bool isSell, BTCNumericTypes::balance_type balance /*= 0.0*/) -{ - otc::RangeType selectedRangeType = static_cast(ui_->comboBoxRange->currentData().toInt()); - - ui_->comboBoxRange->clear(); - - auto env = static_cast(env_); - auto lowestRangeType = otc::firstRangeValue(env); - ui_->comboBoxRange->addItem(QString::fromStdString(otc::toString(lowestRangeType)), static_cast(lowestRangeType)); - - otc::Range lowestRange = otc::getRange(lowestRangeType); - - auto checkLowestPossible = [&](int64_t lowestPrice) -> bool { - return (isSell && lowestPrice > balance) || (!isSell && lowestPrice > balance / buyIndicativePrice_); - }; - - if (checkLowestPossible(lowestRange.lower)) { - ui_->comboBoxRange->setDisabled(true); - ui_->pushButtonSubmit->setDisabled(true); - return; - } - - ui_->comboBoxRange->setEnabled(true); - ui_->pushButtonSubmit->setEnabled(true); - - int selectedIndex = 0; - for (int i = static_cast(lowestRangeType) + 1; - i <= static_cast(otc::lastRangeValue(env)); ++i) { - - auto rangeType = otc::RangeType(i); - if (checkLowestPossible(otc::getRange(rangeType).lower)) { - break; - } - - ui_->comboBoxRange->addItem(QString::fromStdString(otc::toString(rangeType)), i); - - if (rangeType == selectedRangeType) { - selectedIndex = static_cast(rangeType) - static_cast(lowestRangeType); - } - } - - ui_->comboBoxRange->setCurrentIndex(selectedIndex); -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.h deleted file mode 100644 index 4a72d9832..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __CREATE_OTC_REQUEST_WIDGET_H__ -#define __CREATE_OTC_REQUEST_WIDGET_H__ - -#include -#include - -#include "OtcTypes.h" -#include "OTCWindowsAdapterBase.h" - -namespace Ui { - class CreateOTCRequestWidget; -}; - -class CreateOTCRequestWidget : public OTCWindowsAdapterBase -{ - Q_OBJECT - -public: - CreateOTCRequestWidget(QWidget* parent = nullptr); - ~CreateOTCRequestWidget() override; - - void init(bs::network::otc::Env env); - - bs::network::otc::QuoteRequest request() const; - -signals: - void requestCreated(); - -protected slots: - void onUpdateBalances() override; - -private slots: - void onSellClicked(); - void onBuyClicked(); - void onNumCcySelected(); - -private: - void updateXBTRange(bool isSell, BTCNumericTypes::balance_type balance = 0.0); - - std::unique_ptr ui_; - - int env_{}; -}; - -#endif // __CREATE_OTC_REQUEST_WIDGET_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.ui b/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.ui deleted file mode 100644 index f12024a04..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCRequestWidget.ui +++ /dev/null @@ -1,739 +0,0 @@ - - - - CreateOTCRequestWidget - - - - 0 - 0 - 460 - 724 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - QUOTE REQUEST - - - true - - - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - GENERAL - - - true - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - - - Product Group - - - - - - - Spot XBT - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - 5 - - - 0 - - - 0 - - - - - Security ID - - - - - - - XBT/EUR - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - OTC DETAILS - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Product - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 60 - 30 - - - - - 60 - 30 - - - - XBT - - - true - - - true - - - true - - - - - - - false - - - - 60 - 30 - - - - - 60 - 30 - - - - EUR - - - false - - - true - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 1 - - - 0 - - - 0 - - - 0 - - - - - Side - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Buy - - - true - - - true - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Sell - - - true - - - true - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity Range: - - - - - - - Qt::Horizontal - - - - 139 - 20 - - - - - - - - - 120 - 0 - - - - - 120 - 16777215 - - - - - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 10 - - - 5 - - - 5 - - - 10 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 407 - - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - Settlement Period: 300 sec - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 35 - - - - - - - SUBMIT OTC - - - - - - - - - - - diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.cpp deleted file mode 100644 index b20e76694..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "CreateOTCResponseWidget.h" - -#include "AssetManager.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncWalletsManager.h" -#include "ui_CreateOTCResponseWidget.h" - -using namespace bs::network; - -CreateOTCResponseWidget::CreateOTCResponseWidget(QWidget* parent) - : OTCWindowsAdapterBase{parent} - , ui_{new Ui::CreateOTCResponseWidget{}} -{ - ui_->setupUi(this); - - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &CreateOTCResponseWidget::responseCreated); - connect(ui_->widgetAmountRange, &RangeWidget::upperValueChanged, this, &CreateOTCResponseWidget::updateAcceptButton); - connect(ui_->widgetPriceRange, &RangeWidget::upperValueChanged, this, &CreateOTCResponseWidget::updateAcceptButton); -} - -CreateOTCResponseWidget::~CreateOTCResponseWidget() = default; - -void CreateOTCResponseWidget::setRequest(const otc::QuoteRequest &request) -{ - // TODO: Use MD - ourSide_ = request.ourSide; - - double currentIndicativePrice = ourSide_ != bs::network::otc::Side::Sell ? sellIndicativePrice_ : buyIndicativePrice_; - int lowerBound = std::max(static_cast(std::floor((currentIndicativePrice - 1000) / 1000) * 1000), 0); - int upperBound = std::max(static_cast(std::ceil((currentIndicativePrice + 1000) / 1000) * 1000), 1000); - ui_->widgetPriceRange->SetRange(lowerBound, upperBound); - - ui_->sideValue->setText(QString::fromStdString(otc::toString(request.ourSide))); - - ui_->rangeValue->setText(QString::fromStdString(otc::toString(request.rangeType))); - - auto range = otc::getRange(request.rangeType); - ui_->widgetAmountRange->SetRange(int(range.lower), int(range.upper)); - ui_->widgetAmountRange->SetLowerValue(int(range.lower)); - ui_->widgetAmountRange->SetUpperValue(int(range.upper)); - - ui_->pushButtonPull->hide(); -} - -otc::QuoteResponse CreateOTCResponseWidget::response() const -{ - otc::QuoteResponse response; - response.ourSide = ourSide_; - response.amount.lower = ui_->widgetAmountRange->GetLowerValue(); - response.amount.upper = ui_->widgetAmountRange->GetUpperValue(); - response.price.lower = otc::toCents(ui_->widgetPriceRange->GetLowerValue()); - response.price.upper = otc::toCents(ui_->widgetPriceRange->GetUpperValue()); - return response; -} - -void CreateOTCResponseWidget::setPeer(const bs::network::otc::Peer &peer) -{ - ui_->sideValue->setText(getSide(ourSide_, peer.isOwnRequest)); -} - -void CreateOTCResponseWidget::onUpdateBalances() -{ - QString totalBalance = tr("%1 %2") - .arg(UiUtils::displayAmount(getWalletManager()->getTotalBalance())) - .arg(QString::fromStdString(bs::network::XbtCurrency)); - - ui_->labelBalanceValue->setText(totalBalance); - - updateAcceptButton(); -} - -void CreateOTCResponseWidget::updateAcceptButton() -{ - // We cannot offer zero as price - bool isEnabled = ui_->widgetAmountRange->GetUpperValue() != 0 && ui_->widgetPriceRange->GetUpperValue() != 0; - - const auto totalXBTBalance = getWalletManager()->getTotalBalance(); - const auto totalEurBalance = getAssetManager()->getBalance(buyProduct_.toStdString(), bs::UTXOReservationManager::kIncludeZcOtc, nullptr); - - switch (ourSide_) - { - case bs::network::otc::Side::Buy: { - if (ui_->widgetPriceRange->GetLowerValue() * ui_->widgetAmountRange->GetLowerValue() > totalEurBalance) { - isEnabled = false; - } - } - break; - case bs::network::otc::Side::Sell: { - if (ui_->widgetAmountRange->GetLowerValue() > totalXBTBalance) { - isEnabled = false; - } - } - break; - default: - break; - } - - ui_->pushButtonSubmit->setEnabled(isEnabled); -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.h deleted file mode 100644 index 16ba5da5c..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __CREATE_OTC_RESPONSE_WIDGET_H__ -#define __CREATE_OTC_RESPONSE_WIDGET_H__ - -#include - -#include "OTCWindowsAdapterBase.h" -#include "OtcTypes.h" - -namespace Ui { - class CreateOTCResponseWidget; -}; - -class CreateOTCResponseWidget : public OTCWindowsAdapterBase -{ - Q_OBJECT -public: - CreateOTCResponseWidget(QWidget* parent = nullptr); - ~CreateOTCResponseWidget() override; - - void setRequest(const bs::network::otc::QuoteRequest &request); - - bs::network::otc::QuoteResponse response() const; - void setPeer(const bs::network::otc::Peer &peer) override; - -protected slots: - void onUpdateBalances() override; - -private slots: - void updateAcceptButton(); - -signals: - void responseCreated(); - -private: - std::unique_ptr ui_; - - bs::network::otc::Side ourSide_{}; - QString buyProduct_{ QLatin1String("EUR") }; -}; - -#endif // __CREATE_OTC_RESPONSE_WIDGET_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.ui b/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.ui deleted file mode 100644 index f1e35c1ab..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/CreateOTCResponseWidget.ui +++ /dev/null @@ -1,581 +0,0 @@ - - - - CreateOTCResponseWidget - - - - 0 - 0 - 616 - 724 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - OTC RESPONSE - - - true - - - 5 - - - - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - GENERAL - - - true - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - - - Product Group - - - - - - - Spot XBT - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - 5 - - - 0 - - - 0 - - - - - Security ID - - - - - - - XBT/EUR - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Side - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity Range - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - OTC DETAILS - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 5 - - - - - - 0 - 0 - - - - - 3 - - - 10 - - - 5 - - - 5 - - - 10 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Bid ( EUR ) - - - - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity ( XBT ) - - - - - - - - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 252 - - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - Settlement Period: 300 sec - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 35 - - - - PULL - - - - - - - - 0 - 35 - - - - - - - SUBMIT - - - - - - - - - - - RangeWidget - QWidget -
RangeWidget.h
- 1 -
-
- - -
diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.cpp deleted file mode 100644 index 94b377671..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCNegotiationRequestWidget.h" - -#include "AssetManager.h" -#include "AuthAddressManager.h" -#include "BSMessageBox.h" -#include "OTCWindowsManager.h" -#include "OtcTypes.h" -#include "TradesUtils.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "ui_OTCNegotiationRequestWidget.h" - -#include -#include -#include - -using namespace bs::network; - -namespace { - double kQuantityXBTSimpleStepAmount = 0.001; - const QString paymentWallet = QObject::tr("Payment Wallet"); - const QString receivingWallet = QObject::tr("Receiving Wallet"); -} - -OTCNegotiationRequestWidget::OTCNegotiationRequestWidget(QWidget* parent) - : OTCWindowsAdapterBase{ parent } - , ui_{ new Ui::OTCNegotiationRequestWidget{} } -{ - ui_->setupUi(this); - - ui_->priceSpinBox->setAccelerated(true); - ui_->priceSpinBox->setAccelerated(true); - - connect(ui_->pushButtonBuy, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onBuyClicked); - connect(ui_->pushButtonBuy, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onUpdateBalances); - connect(ui_->pushButtonSell, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onSellClicked); - connect(ui_->pushButtonSell, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onUpdateBalances); - connect(ui_->pushButtonAcceptRequest, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onSubmited); - connect(ui_->toolButtonXBTInputs, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onShowXBTInputsClicked); - connect(this, &OTCWindowsAdapterBase::xbtInputsProcessed, this, &OTCNegotiationRequestWidget::onXbtInputsProcessed); - connect(ui_->comboBoxXBTWallets, QOverload::of(&QComboBox::currentIndexChanged), this, &OTCNegotiationRequestWidget::onCurrentWalletChanged); - connect(ui_->quantityMaxButton, &QPushButton::clicked, this, &OTCNegotiationRequestWidget::onMaxQuantityClicked); - - connect(ui_->priceSpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &OTCNegotiationRequestWidget::onChanged); - connect(ui_->quantitySpinBox, qOverload(&QDoubleSpinBox::valueChanged), this, &OTCNegotiationRequestWidget::onChanged); - connect(ui_->authenticationAddressComboBox, qOverload(&QComboBox::currentIndexChanged), this, &OTCNegotiationRequestWidget::onChanged); - - ui_->quantitySpinBox->setSingleStep(kQuantityXBTSimpleStepAmount); - - onSellClicked(); - onChanged(); -} - -OTCNegotiationRequestWidget::~OTCNegotiationRequestWidget() = default; - -bs::network::otc::Offer OTCNegotiationRequestWidget::offer() const -{ - bs::network::otc::Offer result; - const bool isSell = ui_->pushButtonSell->isChecked(); - result.ourSide = isSell ? bs::network::otc::Side::Sell : bs::network::otc::Side::Buy; - result.price = bs::network::otc::toCents(ui_->priceSpinBox->value()); - result.amount = bs::network::otc::btcToSat(ui_->quantitySpinBox->value()); - - result.hdWalletId = ui_->comboBoxXBTWallets->currentData(UiUtils::WalletIdRole).toString().toStdString(); - result.authAddress = ui_->authenticationAddressComboBox->currentText().toStdString(); - - if (!isSell && ui_->receivingAddressComboBox->currentIndex() != 0) { - result.recvAddress = ui_->receivingAddressComboBox->currentText().toStdString(); - } - - result.inputs = selectedUTXOs(); - - auto walletType = UiUtils::getSelectedWalletType(ui_->comboBoxXBTWallets); - if (walletType & UiUtils::WalletsTypes::HardwareSW) { - auto purpose = UiUtils::getHwWalletPurpose(walletType); - result.walletPurpose.reset(new bs::hd::Purpose(purpose)); - } - - return result; -} - -void OTCNegotiationRequestWidget::onAboutToApply() -{ - onUpdateIndicativePrice(); -} - -void OTCNegotiationRequestWidget::setPeer(const bs::network::otc::Peer &peer) -{ - const bool isContact = (peer.type == otc::PeerType::Contact); - - switch (peer.type) { - case otc::PeerType::Contact: { - // Reset side to sell by default for contacts - toggleSideButtons(/*isSell*/ true); - ui_->quantitySpinBox->setMinimum(0); - ui_->priceSpinBox->setMinimum(0); - ui_->quantitySpinBox->setMaximum(std::numeric_limits::max()); - ui_->priceSpinBox->setMaximum(std::numeric_limits::max()); - break; - } - case otc::PeerType::Request: { - toggleSideButtons(peer.request.ourSide == otc::Side::Sell); - ui_->labelQuantityValue->setText(QString::fromStdString(otc::toString(peer.request.rangeType))); - const auto range = otc::getRange(peer.request.rangeType); - ui_->quantitySpinBox->setMinimum(range.lower); - ui_->quantitySpinBox->setMaximum(range.upper); - break; - } - case otc::PeerType::Response: { - // For public OTC side is fixed, use it from original request details - toggleSideButtons(peer.response.ourSide == otc::Side::Sell); - ui_->labelQuantityValue->setText(getXBTRange(peer.response.amount)); - ui_->labelBidValue->setText(getCCRange(peer.response.price)); - ui_->quantitySpinBox->setMinimum(peer.response.amount.lower); - ui_->quantitySpinBox->setMaximum(peer.response.amount.upper); - ui_->priceSpinBox->setMinimum(bs::network::otc::fromCents(peer.response.price.lower)); - ui_->priceSpinBox->setMaximum(bs::network::otc::fromCents(peer.response.price.upper)); - break; - } - } - - ui_->pushButtonBuy->setEnabled(isContact); - ui_->pushButtonSell->setEnabled(isContact); - ui_->rangeQuantity->setVisible(!isContact); - ui_->rangeBid->setVisible(!isContact && peer.type == otc::PeerType::Response); - - setSelectedInputs(peer.offer.inputs); - onChanged(); -} - -void OTCNegotiationRequestWidget::onSyncInterface() -{ - int index = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWallets, getWalletManager(), UiUtils::WalletsTypes::All); - - const auto walletId = getWalletManager()->getDefaultSpendWalletId(); - UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWallets, walletId, UiUtils::WalletsTypes::All); - - onCurrentWalletChanged(); - - UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, getAuthManager()); - ui_->widgetButtons->setEnabled(ui_->authenticationAddressComboBox->isEnabled()); -} - -void OTCNegotiationRequestWidget::onUpdateBalances() -{ - QString totalBalance; - // #new_logic : fix me when different products security will be available - if (ui_->pushButtonBuy->isChecked()) { - totalBalance = tr("%1 %2") - .arg(UiUtils::displayCurrencyAmount(getAssetManager()->getBalance(buyProduct_.toStdString(), bs::UTXOReservationManager::kIncludeZcOtc, nullptr))) - .arg(buyProduct_); - ui_->quantitySpinBox->setMaximum(std::numeric_limits::max()); - } - else { - double currentBalance = getXBTSpendableBalance(); - totalBalance = tr("%1 %2") - .arg(UiUtils::displayAmount(currentBalance)) - .arg(QString::fromStdString(bs::network::XbtCurrency)); - ui_->quantitySpinBox->setMaximum(currentBalance); - } - - ui_->labelBalanceValue->setText(totalBalance); - onChanged(); -} - -void OTCNegotiationRequestWidget::onSubmited() -{ - auto minXbtAmount = bs::tradeutils::minXbtAmount(getUtxoManager()->feeRatePb()); - if (ui_->quantitySpinBox->value() < minXbtAmount.GetValueBitcoin()) { - auto minAmountStr = UiUtils::displayQuantity(minXbtAmount.GetValueBitcoin(), bs::network::XbtCurrency); - BSMessageBox(BSMessageBox::critical, tr("OTC"), tr("Invalid amount"), - tr("Amount will not cover network fee.\nMinimum amount: %1").arg(minAmountStr), this).exec(); - return; - } - - if (ui_->pushButtonBuy->isChecked()) { - emit requestCreated(); - return; - } - - if (!selectedUTXO_.empty()) { - emit requestCreated(); - return; - } - - submitProposal(ui_->comboBoxXBTWallets, bs::XBTAmount(ui_->quantitySpinBox->value()), - [caller = QPointer(this)]() { - if (!caller) { - return; - } - caller->requestCreated(); - }); -} - -std::shared_ptr OTCNegotiationRequestWidget::getCurrentHDWallet() const -{ - return getCurrentHDWalletFromCombobox(ui_->comboBoxXBTWallets); -} - -BTCNumericTypes::balance_type OTCNegotiationRequestWidget::getXBTSpendableBalance() const -{ - return getXBTSpendableBalanceFromCombobox(ui_->comboBoxXBTWallets); -} - -void OTCNegotiationRequestWidget::keyPressEvent(QKeyEvent* event) -{ - OTCWindowsAdapterBase::keyPressEvent(event); - if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) - && ui_->pushButtonAcceptRequest->isEnabled()) { - onSubmited(); - } -} - -void OTCNegotiationRequestWidget::onXbtInputsProcessed() -{ - onUpdateBalances(); - ui_->toolButtonXBTInputs->setEnabled(true); -} - -void OTCNegotiationRequestWidget::onSellClicked() -{ - ui_->pushButtonSell->setChecked(true); - ui_->pushButtonBuy->setChecked(false); - ui_->toolButtonXBTInputs->setVisible(true); - ui_->receivingAddressComboBox->setVisible(false); - ui_->receivingAddressLabel->setVisible(false); - ui_->quantityMaxButton->setVisible(true); - ui_->quantitySpinBox->setValue(0.0); - ui_->labelWallet->setText(paymentWallet); - - onUpdateIndicativePrice(); -} - -void OTCNegotiationRequestWidget::onBuyClicked() -{ - ui_->pushButtonSell->setChecked(false); - ui_->pushButtonBuy->setChecked(true); - ui_->toolButtonXBTInputs->setVisible(false); - ui_->receivingAddressComboBox->setVisible(true); - ui_->receivingAddressLabel->setVisible(true); - ui_->quantityMaxButton->setVisible(false); - ui_->quantitySpinBox->setValue(0.0); - ui_->labelWallet->setText(receivingWallet); - - onUpdateIndicativePrice(); -} - -void OTCNegotiationRequestWidget::onShowXBTInputsClicked() -{ - ui_->toolButtonXBTInputs->setEnabled(false); - showXBTInputsClicked(ui_->comboBoxXBTWallets); -} - -void OTCNegotiationRequestWidget::onChanged() -{ - bool activateAcceptButton = ui_->priceSpinBox->value() > 0 - && ui_->quantitySpinBox->value() > 0 - && !ui_->authenticationAddressComboBox->currentText().isEmpty(); - - if (!activateAcceptButton) { - ui_->pushButtonAcceptRequest->setDisabled(true); - return; - } - - if (ui_->pushButtonBuy->isChecked()) { - ui_->quantitySpinBox->setMaximum( - getAssetManager()->getBalance(buyProduct_.toStdString(), bs::UTXOReservationManager::kIncludeZcOtc, nullptr) / ui_->priceSpinBox->value()); - } - - ui_->pushButtonAcceptRequest->setEnabled(true); -} - -void OTCNegotiationRequestWidget::onChatRoomChanged() -{ - clearSelectedInputs(); -} - -void OTCNegotiationRequestWidget::onParentAboutToHide() -{ - clearSelectedInputs(); -} - -void OTCNegotiationRequestWidget::onCurrentWalletChanged() -{ - auto recvHdWallet = getCurrentHDWallet(); - if (!recvHdWallet) { - return; - } - if (!recvHdWallet->canMixLeaves()) { - auto xbtGroup = recvHdWallet->getGroup(recvHdWallet->getXBTGroupType()); - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWallets); - UiUtils::fillRecvAddressesComboBox(ui_->receivingAddressComboBox, { xbtGroup->getLeaf(purpose) }); - } - else { - UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, recvHdWallet, true); - } - - clearSelectedInputs(); - onUpdateBalances(); -} - -void OTCNegotiationRequestWidget::toggleSideButtons(bool isSell) -{ - ui_->pushButtonSell->setChecked(isSell); - ui_->pushButtonBuy->setChecked(!isSell); - if (isSell) { - onSellClicked(); - } - else { - onBuyClicked(); - } -} - -void OTCNegotiationRequestWidget::onUpdateIndicativePrice() -{ - const double indicativePrice = ui_->pushButtonBuy->isChecked() ? buyIndicativePrice_ : sellIndicativePrice_; - ui_->priceSpinBox->setValue(indicativePrice); -} - -void OTCNegotiationRequestWidget::onMaxQuantityClicked() -{ - const auto hdWallet = getCurrentHDWalletFromCombobox(ui_->comboBoxXBTWallets); - if (!hdWallet) { - ui_->quantitySpinBox->setValue(0); - return; - } - - std::vector utxos = selectedUTXOs(); - if (utxos.empty()) { - if (!hdWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWallets); - utxos = getUtxoManager()->getAvailableXbtUTXOs(hdWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcOtc); - } - else { - utxos = getUtxoManager()->getAvailableXbtUTXOs(hdWallet->walletId(), bs::UTXOReservationManager::kIncludeZcOtc); - } - } - - auto feeCb = [this, parentWidget = QPointer(this), utxos = std::move(utxos)](float fee) { - QMetaObject::invokeMethod(qApp, [this, parentWidget, fee, utxos = std::move(utxos)]{ - if (!parentWidget) { - return; - } - float feePerByteArmory = ArmoryConnection::toFeePerByte(fee); - auto feePerByte = std::max(feePerByteArmory, getUtxoManager()->feeRatePb()); - uint64_t total = 0; - for (const auto &utxo : utxos) { - total += utxo.getValue(); - } - const uint64_t fee = bs::tradeutils::estimatePayinFeeWithoutChange(utxos, feePerByte); - const double spendableQuantity = std::max(0.0, (total - fee) / BTCNumericTypes::BalanceDivider); - ui_->quantitySpinBox->setValue(spendableQuantity); - }); - }; - otcManager_->getArmory()->estimateFee(bs::tradeutils::feeTargetBlockCount(), feeCb); -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.h deleted file mode 100644 index 73f2b083a..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTC_NEGOTIATION_REQUEST_WIDGET_H__ -#define __OTC_NEGOTIATION_REQUEST_WIDGET_H__ - -#include -#include -#include - -#include "OtcTypes.h" -#include "OTCWindowsAdapterBase.h" - -namespace Ui { - class OTCNegotiationRequestWidget; -}; - -namespace bs { - namespace sync { - namespace hd { - class Wallet; - } - } -} - -class OTCNegotiationRequestWidget : public OTCWindowsAdapterBase -{ -Q_OBJECT -Q_DISABLE_COPY(OTCNegotiationRequestWidget) - -public: - OTCNegotiationRequestWidget(QWidget* parent = nullptr); - ~OTCNegotiationRequestWidget() override; - - bs::network::otc::Offer offer() const; - - void setPeer(const bs::network::otc::Peer &peer) override; - -signals: - void requestCreated(); - -public slots: - void onAboutToApply() override; - void onChatRoomChanged() override; - void onParentAboutToHide() override; - -protected slots: - void onSyncInterface() override; - void onUpdateBalances() override; - - void onSubmited(); - -protected: - std::shared_ptr getCurrentHDWallet() const; - BTCNumericTypes::balance_type getXBTSpendableBalance() const; - - void keyPressEvent(QKeyEvent* event) override; - -private slots: - void onSellClicked(); - void onBuyClicked(); - void onShowXBTInputsClicked(); - void onXbtInputsProcessed(); - void onChanged(); - void onUpdateIndicativePrice(); - void onMaxQuantityClicked(); - void onCurrentWalletChanged(); - -private: - void toggleSideButtons(bool isSell); - - std::unique_ptr ui_; -}; - -#endif // __OTC_NEGOTIATION_REQUEST_WIDGET_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.ui b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.ui deleted file mode 100644 index 6dbf0405f..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationRequestWidget.ui +++ /dev/null @@ -1,971 +0,0 @@ - - - - OTCNegotiationRequestWidget - - - - 0 - 0 - 558 - 961 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - OTC REQUEST - - - true - - - - - - - - - - 5 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - GENERAL - - - true - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - - - Product Group - - - - - - - Spot XBT - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - 5 - - - 0 - - - 0 - - - - - Security ID - - - - - - - XBT/EUR - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity (XBT) - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Bid (EUR) - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - OTC ENTRY - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 5 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Side - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Buy - - - true - - - true - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Sell - - - true - - - true - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - Price - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - 2 - - - 1.000000000000000 - - - 1000000.000000000000000 - - - 5.000000000000000 - - - 10000.000000000000000 - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - Quantity - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - 8 - - - 0.000000000000000 - - - 1000000.000000000000000 - - - 5.000000000000000 - - - 10000.000000000000000 - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Max - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 5 - - - 5 - - - 5 - - - 10 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - SETTLEMENT DETAILS - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 0 - - - - - Receiving Wallet - - - - - - - 5 - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Inputs - - - - - - - - - - - 0 - - - - - Receiving Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - 0 - - - - - Authentication Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 3 - - - - - - 0 - 35 - - - - - - - Submit - - - - - - - - - - - diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.cpp deleted file mode 100644 index 57c6403af..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCNegotiationResponseWidget.h" - -#include "AssetManager.h" -#include "AuthAddressManager.h" -#include "OtcClient.h" -#include "OtcTypes.h" -#include "TradesUtils.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "ui_OTCNegotiationResponseWidget.h" - -#include -#include - -namespace { - const QString paymentWallet = QObject::tr("Payment Wallet"); - const QString receivingWallet = QObject::tr("Receiving Wallet"); -} - -OTCNegotiationResponseWidget::OTCNegotiationResponseWidget(QWidget* parent) - : OTCWindowsAdapterBase{ parent } - , ui_{ new Ui::OTCNegotiationResponseWidget{} } -{ - ui_->setupUi(this); - - ui_->pushButtonCancel->setText(tr("Reject")); - - connect(ui_->offerSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged) - , this, &OTCNegotiationResponseWidget::onChanged); - connect(ui_->bidSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged) - , this, &OTCNegotiationResponseWidget::onChanged); - - connect(ui_->comboBoxXBTWallets, QOverload::of(&QComboBox::currentIndexChanged), this, &OTCNegotiationResponseWidget::onCurrentWalletChanged); - connect(ui_->pushButtonAccept, &QPushButton::clicked, this, &OTCNegotiationResponseWidget::onAcceptOrUpdateClicked); - connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &OTCNegotiationResponseWidget::responseRejected); - connect(ui_->toolButtonXBTInputs, &QPushButton::clicked, this, &OTCNegotiationResponseWidget::onShowXBTInputsClicked); - connect(this, &OTCWindowsAdapterBase::xbtInputsProcessed, this, &OTCNegotiationResponseWidget::onXbtInputsProcessed); - - ui_->quantitySpinBox->setEnabled(false); - - timeoutSec_ = getSeconds(bs::network::otc::negotiationTimeout()); - - onChanged(); -} - -OTCNegotiationResponseWidget::~OTCNegotiationResponseWidget() = default; - -void OTCNegotiationResponseWidget::setOffer(const bs::network::otc::Offer &offer) -{ - receivedOffer_ = offer; - - auto price = bs::network::otc::fromCents(offer.price); - auto amount = bs::network::otc::satToBtc(offer.amount); - const QString offerAndCurrency = QLatin1String("%1 %2"); - const bool isSell = offer.ourSide == bs::network::otc::Side::Sell; - - if (isSell) { - ui_->recieveValue->setText(offerAndCurrency.arg(UiUtils::displayCurrencyAmount(price * amount)).arg(buyProduct_)); - ui_->deliverValue->setText(offerAndCurrency.arg(amount).arg(sellProduct_)); - } - else { - ui_->recieveValue->setText(offerAndCurrency.arg(amount).arg(sellProduct_)); - ui_->deliverValue->setText(offerAndCurrency.arg(UiUtils::displayCurrencyAmount(price * amount)).arg(buyProduct_)); - } - - const QString productToPrice = QLatin1String("%1 %2 / 1 %3"); - ui_->priceValue->setText(productToPrice.arg(price).arg(buyProduct_).arg(sellProduct_)); - - ui_->quantitySpinBox->setValue(amount); - ui_->quantitySpinBox->setDisabled(true); - ui_->bidSpinBox->setValue(price); - ui_->bidSpinBox->setEnabled(!isSell); - ui_->offerSpinBox->setValue(price); - ui_->offerSpinBox->setEnabled(isSell); - ui_->receivingAddressWdgt->setVisible(!isSell); - ui_->labelWallet->setText(isSell ? paymentWallet : receivingWallet); - ui_->toolButtonXBTInputs->setVisible(offer.ourSide == bs::network::otc::Side::Sell); - - onChanged(); -} - -bs::network::otc::Offer OTCNegotiationResponseWidget::offer() const -{ - bs::network::otc::Offer result; - result.ourSide = receivedOffer_.ourSide; - result.amount = bs::network::otc::btcToSat(ui_->quantitySpinBox->value()); - if (receivedOffer_.ourSide == bs::network::otc::Side::Sell) { - result.price = bs::network::otc::toCents(ui_->offerSpinBox->value()); - } - else { - result.price = bs::network::otc::toCents(ui_->bidSpinBox->value()); - } - - result.hdWalletId = ui_->comboBoxXBTWallets->currentData(UiUtils::WalletIdRole).toString().toStdString(); - result.authAddress = ui_->authenticationAddressComboBox->currentText().toStdString(); - - if (ui_->receivingAddressComboBox->currentIndex() != 0) { - result.recvAddress = ui_->receivingAddressComboBox->currentText().toStdString(); - } - - result.inputs = selectedUTXOs(); - - auto walletType = UiUtils::getSelectedWalletType(ui_->comboBoxXBTWallets); - if (walletType & UiUtils::WalletsTypes::HardwareSW) { - auto purpose = UiUtils::getHwWalletPurpose(walletType); - result.walletPurpose.reset(new bs::hd::Purpose(purpose)); - } - - return result; -} - -void OTCNegotiationResponseWidget::setPeer(const bs::network::otc::Peer &peer) -{ - const bool isContact = (peer.type == bs::network::otc::PeerType::Contact); - - if (peer.type == bs::network::otc::PeerType::Request) { - ui_->labelQuantityValue->setText(getXBTRange(peer.response.amount)); - ui_->labelBidValue->setText(getCCRange(peer.response.price)); - - ui_->bidSpinBox->setMinimum(bs::network::otc::fromCents(peer.response.price.lower)); - ui_->bidSpinBox->setMaximum(bs::network::otc::fromCents(peer.response.price.upper)); - ui_->offerSpinBox->setMinimum(bs::network::otc::fromCents(peer.response.price.lower)); - ui_->offerSpinBox->setMaximum(bs::network::otc::fromCents(peer.response.price.upper)); - } - - ui_->rangeQuantity->setVisible(!isContact); - ui_->rangeBid->setVisible(!isContact); - - setupTimer({ peer.stateTimestamp, ui_->progressBarTimeLeft, ui_->labelTimeLeft }); - setSelectedInputs(peer.offer.inputs); -} - -void OTCNegotiationResponseWidget::onParentAboutToHide() -{ - clearSelectedInputs(); -} - -void OTCNegotiationResponseWidget::onSyncInterface() -{ - int index = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWallets, getWalletManager(), UiUtils::WalletsTypes::All); - const auto walletId = getWalletManager()->getDefaultSpendWalletId(); - UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWallets, walletId, UiUtils::WalletsTypes::All); - - onCurrentWalletChanged(); - - UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, getAuthManager()); - ui_->widgetButtons->setEnabled(ui_->authenticationAddressComboBox->isEnabled()); -} - -void OTCNegotiationResponseWidget::onUpdateBalances() -{ - double currentBalance = getXBTSpendableBalanceFromCombobox(ui_->comboBoxXBTWallets); - QString totalBalance = tr("%1 %2") - .arg(UiUtils::displayAmount(currentBalance)) - .arg(QString::fromStdString(bs::network::XbtCurrency)); - - ui_->labelBalanceValue->setText(totalBalance); -} - -void OTCNegotiationResponseWidget::onChanged() -{ - bool activateAcceptButton = true; - double price = 0.0; - if (receivedOffer_.ourSide == bs::network::otc::Side::Sell) { - price = ui_->offerSpinBox->value(); - } - else { - price = ui_->bidSpinBox->value(); - } - double quantity = ui_->quantitySpinBox->value(); - - if (receivedOffer_.ourSide == bs::network::otc::Side::Sell - && quantity > getXBTSpendableBalanceFromCombobox(ui_->comboBoxXBTWallets)) { - activateAcceptButton = false; - } - else if (receivedOffer_.ourSide == bs::network::otc::Side::Buy - && price * quantity - > getAssetManager()->getBalance(buyProduct_.toStdString(), bs::UTXOReservationManager::kIncludeZcOtc, nullptr)) { - activateAcceptButton = false; - } - - if (receivedOffer_.ourSide == bs::network::otc::Side::Buy && !selectedUTXOs().empty()) { - uint64_t totalSelected = 0; - for (const auto &utxo : selectedUTXOs()) { - totalSelected += utxo.getValue(); - } - // This does not take into account pay-in fee - if (totalSelected < static_cast(receivedOffer_.amount)) { - activateAcceptButton = false; - } - } - - ui_->pushButtonAccept->setEnabled(activateAcceptButton); - - if (receivedOffer_ == offer()) { - ui_->pushButtonAccept->setText(tr("Accept")); - } - else { - ui_->pushButtonAccept->setText(tr("Update")); - } - - // Review updated price, disable wallet details changing - bool walletDetailsFixed = !receivedOffer_.hdWalletId.empty(); - ui_->comboBoxXBTWallets->setEnabled(!walletDetailsFixed); - ui_->receivingAddressComboBox->setEnabled(!walletDetailsFixed); - ui_->toolButtonXBTInputs->setEnabled(!walletDetailsFixed); - ui_->authenticationAddressComboBox->setEnabled(!walletDetailsFixed); - if (walletDetailsFixed) { - UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWallets, receivedOffer_.hdWalletId); - ui_->receivingAddressComboBox->setCurrentText(QString::fromStdString(receivedOffer_.recvAddress)); - ui_->authenticationAddressComboBox->setCurrentText(QString::fromStdString(receivedOffer_.authAddress)); - } -} - -void OTCNegotiationResponseWidget::onAcceptOrUpdateClicked() -{ - QMetaMethod signal = (receivedOffer_ == offer()) - ? QMetaMethod::fromSignal(&OTCNegotiationResponseWidget::responseAccepted) - : QMetaMethod::fromSignal(&OTCNegotiationResponseWidget::responseUpdated); - - - if (receivedOffer_.ourSide == bs::network::otc::Side::Buy) { - signal.invoke(this); - return; - } - - if (!selectedUTXO_.empty()) { - signal.invoke(this); - return; - } - - submitProposal(ui_->comboBoxXBTWallets, bs::XBTAmount(ui_->quantitySpinBox->value()), - [caller = QPointer(this), signal]() { - if (!caller) { - return; - } - signal.invoke(caller); - }); -} - -void OTCNegotiationResponseWidget::onShowXBTInputsClicked() -{ - ui_->toolButtonXBTInputs->setEnabled(false); - showXBTInputsClicked(ui_->comboBoxXBTWallets); -} - -void OTCNegotiationResponseWidget::onXbtInputsProcessed() -{ - onUpdateBalances(); - ui_->toolButtonXBTInputs->setEnabled(true); - - // Check selected amount and update accept button enabled state - onChanged(); -} - -void OTCNegotiationResponseWidget::onCurrentWalletChanged() -{ - auto recvHdWallet = getCurrentHDWalletFromCombobox(ui_->comboBoxXBTWallets); - if (!recvHdWallet) { - return; - } - if (!recvHdWallet->canMixLeaves()) { - auto xbtGroup = recvHdWallet->getGroup(recvHdWallet->getXBTGroupType()); - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWallets); - UiUtils::fillRecvAddressesComboBox(ui_->receivingAddressComboBox, { xbtGroup->getLeaf(purpose) }); - } - else { - UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, recvHdWallet, true); - } - - clearSelectedInputs(); - onUpdateBalances(); -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.h deleted file mode 100644 index 6d619888e..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTC_NEGOTIATION_RESPONSE_WIDGET_H__ -#define __OTC_NEGOTIATION_RESPONSE_WIDGET_H__ - -#include -#include - -#include "OtcTypes.h" -#include "OTCWindowsAdapterBase.h" -#include "CommonTypes.h" - -namespace Ui { - class OTCNegotiationResponseWidget; -}; - -class OTCNegotiationResponseWidget : public OTCWindowsAdapterBase -{ -Q_OBJECT -Q_DISABLE_COPY(OTCNegotiationResponseWidget) - -public: - explicit OTCNegotiationResponseWidget(QWidget* parent = nullptr); - ~OTCNegotiationResponseWidget() override; - - void setOffer(const bs::network::otc::Offer &offer); - - bs::network::otc::Offer offer() const; - void setPeer(const bs::network::otc::Peer &peer) override; - -signals: - void responseAccepted(); - void responseUpdated(); - void responseRejected(); - -public slots: - void onParentAboutToHide() override; - -protected slots: - void onSyncInterface() override; - void onUpdateBalances() override; - -private slots: - void onChanged(); - void onAcceptOrUpdateClicked(); - void onShowXBTInputsClicked(); - void onXbtInputsProcessed(); - - void onCurrentWalletChanged(); - -private: - std::unique_ptr ui_; - bs::network::otc::Offer receivedOffer_; -}; - -#endif // __OTC_NEGOTIATION_RESPONSE_WIDGET_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.ui b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.ui deleted file mode 100644 index 538e49ec1..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCNegotiationResponseWidget.ui +++ /dev/null @@ -1,970 +0,0 @@ - - - - OTCNegotiationResponseWidget - - - - 0 - 0 - 558 - 961 - - - - Form - - - - 5 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - OTC RESPONSE - - - true - - - - - - - - - - OTC DETAILS - - - true - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Receive - - - - - - - xx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Deliver - - - - - - - yy - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Price - - - - - - - zz - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity (XBT) - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Bid (EUR) - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - UPDATE RESPONSE - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 5 - - - - - - 0 - 0 - - - - - 3 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - Quantity - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - 8 - - - 0.000000000000000 - - - 1000000.000000000000000 - - - 5.000000000000000 - - - 10000.000000000000000 - - - - - - - - - - - 5 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Bid - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - 2 - - - 1.000000000000000 - - - 1000000.000000000000000 - - - 5.000000000000000 - - - 10000.000000000000000 - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Offer - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - 2 - - - 1.000000000000000 - - - 1000000.000000000000000 - - - 5.000000000000000 - - - 10000.000000000000000 - - - - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 5 - - - 5 - - - 5 - - - 10 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - SETTLEMENT DETAILS - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - 0 - - - - - Payment Wallet - - - - - - - 5 - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Inputs - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Receiving Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - - 0 - - - - - Authentication Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - - - - - 5 - - - 5 - - - - - Qt::Vertical - - - - 20 - 496 - - - - - - - - - 0 - 7 - - - - - 16777215 - 7 - - - - 24 - - - Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing - - - false - - - false - - - - - - - - - - Qt::AlignCenter - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 3 - - - - - - 0 - 35 - - - - - - - PushButton - - - - - - - - 0 - 35 - - - - - - - PushButton - - - true - - - true - - - true - - - - - - - - - - - diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.cpp deleted file mode 100644 index 9728340d6..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCShield.h" -#include "AuthAddressManager.h" -#include "Wallets/SyncWalletsManager.h" - -namespace { - const QString shieldLoginToAccessOTC = QObject::tr("Login to access OTC chat"); - const QString shieldOtcAvailableToTradingParticipants = QObject::tr("OTC available to Trading Participants"); - const QString shieldCounterPartyIsntTradingParticipant = QObject::tr("Counterparty isn't a Trading Participant"); - const QString shieldContactIsOffline = QObject::tr("Contact is offline"); - const QString shieldOtcAvailableOnlyForAcceptedContacts = QObject::tr("OTC available only for Accepted contacts"); - const QString shieldOtcSetupTransactionData = QObject::tr("Setup OTC transaction data"); - const QString shieldOtcshowAvailableOnceAccepted = QObject::tr("OTC settlement available once contact request is accepted"); - - const QString tradingKeyWord = QObject::tr("trading"); - - const QString publicChatHeader = QObject::tr("Public chat"); - const QString privateChatHeader = QObject::tr("Private chat"); - const QString otcSettlementHeader = QObject::tr("OTC settlement"); - - const QString publicChatExplanation = QObject::tr("ChatID is a hash of the users email\n" - "Public rooms are unencrypted\n" - "(Trolls will be banned)\n"); - const QString privateChatExplanation = QObject::tr("Communication is end-to-end encrypted with the users key-pair(s)\n"); - const QString otcSettlementExplanation = QObject::tr("Negotiate and settle XBT/EUR with your chat contacts\n" - "Prices and volumes are not disclosed on exchange\n"); -} - -OTCShield::OTCShield(QWidget* parent) - : WalletShieldBase(parent) -{ - tabType_ = tradingKeyWord; -} - -OTCShield::~OTCShield() noexcept = default; - -void OTCShield::showLoginToAccessOTC() -{ - showShield(shieldLoginToAccessOTC); -} - -void OTCShield::showOtcAvailableToTradingParticipants() -{ - showShield(shieldOtcAvailableToTradingParticipants); -} - -void OTCShield::showCounterPartyIsntTradingParticipant() -{ - showShield(shieldCounterPartyIsntTradingParticipant); -} - -void OTCShield::showContactIsOffline() -{ - showShield(shieldContactIsOffline); -} - -void OTCShield::showOtcAvailableOnlyForAcceptedContacts() -{ - showShield(shieldOtcAvailableOnlyForAcceptedContacts); -} - -void OTCShield::showOtcSetupTransaction() -{ - showShield(shieldOtcSetupTransactionData); -} - -void OTCShield::showChatExplanation() -{ - showThreeBlockShield(publicChatHeader, publicChatExplanation, - privateChatHeader, privateChatExplanation, - otcSettlementHeader, otcSettlementExplanation); -} - -void OTCShield::showShieldOtcAvailableOnceAccepted() -{ - showShield(shieldOtcshowAvailableOnceAccepted); -} - -bool OTCShield::onRequestCheckWalletSettings() -{ - return checkWalletSettings(productType_, product_); -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h deleted file mode 100644 index 72ba8894b..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCShield.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTC_SHIELD_H__ -#define __OTC_SHIELD_H__ - -#include "Trading/WalletShieldBase.h" - -class OTCShield : public WalletShieldBase -{ -Q_OBJECT - -public: - explicit OTCShield(QWidget* parent = nullptr ); - ~OTCShield() noexcept override; - - void showLoginToAccessOTC(); - void showOtcAvailableToTradingParticipants(); - void showCounterPartyIsntTradingParticipant(); - void showContactIsOffline(); - void showOtcAvailableOnlyForAcceptedContacts(); - void showOtcSetupTransaction(); - void showChatExplanation(); - void showShieldOtcAvailableOnceAccepted(); - -public slots: - bool onRequestCheckWalletSettings(); - -private: - - const ProductType productType_ = ProductType::SpotXBT; - const QString product_ = QLatin1String("XBT/EUR"); -}; - -#endif // __OTC_SHIELD_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.cpp deleted file mode 100644 index 662e9f6ce..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCWindowsAdapterBase.h" -#include "OTCWindowsManager.h" - -#include "UiUtils.h" -#include "Wallets/SyncWalletsManager.h" -#include "Wallets/SyncHDWallet.h" -#include "AuthAddressManager.h" -#include "AssetManager.h" -#include "CoinControlDialog.h" -#include "SelectedTransactionInputs.h" -#include "TradesUtils.h" -#include "UtxoReservationManager.h" -#include "XBTAmount.h" -#include "BSMessageBox.h" - -#include -#include -#include - -namespace { - const std::chrono::milliseconds kTimerRepeatTimeMSec{ 500 }; - const QString secondsRemaining = QObject::tr("second(s) remaining"); -} - -OTCWindowsAdapterBase::OTCWindowsAdapterBase(QWidget* parent /*= nullptr*/) - : QWidget(parent) -{ - connect(&timeoutTimer_, &QTimer::timeout, this, &OTCWindowsAdapterBase::onUpdateTimerData); - timeoutTimer_.setInterval(kTimerRepeatTimeMSec); -} - -void OTCWindowsAdapterBase::setChatOTCManager(const std::shared_ptr& otcManager) -{ - otcManager_ = otcManager; - connect(otcManager_.get(), &OTCWindowsManager::syncInterfaceRequired, this, [this]() { - onSyncInterface(); - }); - - connect(otcManager_.get(), &OTCWindowsManager::updateMDDataRequired, this, - [this](bs::network::Asset::Type type, const QString& security, const bs::network::MDFields& fields) - { - onUpdateMD(type, security, fields); - }); - - connect(otcManager_.get(), &OTCWindowsManager::updateBalances, this, [this]() { - onUpdateBalances(); - }); -} - -std::shared_ptr OTCWindowsAdapterBase::getWalletManager() const -{ - return otcManager_->getWalletManager(); -} - -std::shared_ptr OTCWindowsAdapterBase::getAuthManager() const -{ - return otcManager_->getAuthManager(); -} - -std::shared_ptr OTCWindowsAdapterBase::getAssetManager() const -{ - return otcManager_->getAssetManager(); -} - -std::shared_ptr OTCWindowsAdapterBase::getUtxoManager() const -{ - return otcManager_->getUtxoManager(); -} - -void OTCWindowsAdapterBase::setPeer(const bs::network::otc::Peer &) -{ -} - -bs::UtxoReservationToken OTCWindowsAdapterBase::releaseReservation() -{ - return std::move(reservation_); -} - -void OTCWindowsAdapterBase::setReservation(bs::UtxoReservationToken&& reservation) -{ - reservation_ = std::move(reservation); -} - -void OTCWindowsAdapterBase::onSyncInterface() -{ -} - -void OTCWindowsAdapterBase::onUpdateMD(bs::network::Asset::Type type, const QString& security, const bs::network::MDFields& fields) -{ - if (productGroup_ != type || security_ != security) { - return; - } - - updateIndicativePrices(type, security, fields); - - // overloaded in direved class - onMDUpdated(); -} - -void OTCWindowsAdapterBase::onMDUpdated() -{ -} - -void OTCWindowsAdapterBase::onUpdateBalances() -{ -} - -void OTCWindowsAdapterBase::showXBTInputsClicked(QComboBox *walletsCombobox) -{ - reservation_.release(); - showXBTInputs(walletsCombobox); -} - -void OTCWindowsAdapterBase::showXBTInputs(QComboBox *walletsCombobox) -{ - const bool useAutoSel = selectedUTXO_.empty(); - - - const auto &hdWallet = getCurrentHDWalletFromCombobox(walletsCombobox); - - std::vector allUTXOs; - if (!hdWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(walletsCombobox); - allUTXOs = getUtxoManager()->getAvailableXbtUTXOs(hdWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcOtc); - } - else { - allUTXOs = getUtxoManager()->getAvailableXbtUTXOs(hdWallet->walletId(), bs::UTXOReservationManager::kIncludeZcOtc); - } - - auto inputs = std::make_shared(allUTXOs); - - // Set this to false is needed otherwise current selection would be cleared - inputs->SetUseAutoSel(useAutoSel); - - if (!useAutoSel) { - for (const auto &utxo : selectedUTXO_) { - inputs->SetUTXOSelection(utxo.getTxHash(), utxo.getTxOutIndex()); - } - } - - CoinControlDialog dialog(inputs, true, this); - int rc = dialog.exec(); - if (rc != QDialog::Accepted) { - emit xbtInputsProcessed(); - return; - } - - auto selectedInputs = dialog.selectedInputs(); - if (bs::UtxoReservation::instance()->containsReservedUTXO(selectedInputs)) { - BSMessageBox(BSMessageBox::critical, tr("UTXO reservation failed"), - tr("Some of selected UTXOs has been already reserved"), this).exec(); - showXBTInputs(walletsCombobox); - return; - } - selectedUTXO_ = std::move(selectedInputs); - if (!selectedUTXO_.empty()) { - reservation_ = getUtxoManager()->makeNewReservation(selectedUTXO_); - } - - emit xbtInputsProcessed(); -} - -void OTCWindowsAdapterBase::onUpdateTimerData() -{ - if (!currentTimeoutData_.progressBarTimeLeft_ || !currentTimeoutData_.labelTimeLeft_) { - timeoutTimer_.stop(); - return; - } - - const auto currentOfferEndTimestamp = currentTimeoutData_.offerTimestamp_ + std::chrono::seconds(timeoutSec_); - const auto diff = currentOfferEndTimestamp - std::chrono::steady_clock::now(); - const auto diffSeconds = std::chrono::duration_cast(diff); - - currentTimeoutData_.labelTimeLeft_->setText(QString(QLatin1String("%1 %2")).arg(diffSeconds.count()).arg(secondsRemaining)); - currentTimeoutData_.progressBarTimeLeft_->setMaximum(timeoutSec_.count()); - currentTimeoutData_.progressBarTimeLeft_->setValue(diffSeconds.count()); - - if (diffSeconds.count() < 0) { - timeoutTimer_.stop(); - } -} - -void OTCWindowsAdapterBase::updateIndicativePrices(bs::network::Asset::Type type, const QString& security - , const bs::network::MDFields& fields) -{ - for (const auto &field : fields) { - switch (field.type) { - case bs::network::MDField::PriceBid: - sellIndicativePrice_ = field.value; - break; - case bs::network::MDField::PriceOffer: - buyIndicativePrice_ = field.value; - break; - default: break; - } - } -} - -BTCNumericTypes::balance_type OTCWindowsAdapterBase::getXBTSpendableBalanceFromCombobox(QComboBox *walletsCombobox) const -{ - const auto hdWallet = getCurrentHDWalletFromCombobox(walletsCombobox); - if (!hdWallet) { - return .0; - } - - BTCNumericTypes::balance_type totalBalance{}; - if (selectedUTXO_.empty()) { - BTCNumericTypes::satoshi_type sum = 0; - if (!hdWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(walletsCombobox); - sum = getUtxoManager()->getAvailableXbtUtxoSum(hdWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcOtc); - } - else { - sum = getUtxoManager()->getAvailableXbtUtxoSum(hdWallet->walletId(), bs::UTXOReservationManager::kIncludeZcOtc); - } - - return bs::XBTAmount(sum).GetValueBitcoin(); - } - else { - for (const auto &utxo : selectedUTXO_) { - totalBalance += bs::XBTAmount((int64_t)utxo.getValue()).GetValueBitcoin(); - } - } - - return totalBalance; -} - -std::shared_ptr OTCWindowsAdapterBase::getCurrentHDWalletFromCombobox(QComboBox *walletsCombobox) const -{ - const auto walletId = walletsCombobox->currentData(UiUtils::WalletIdRole).toString().toStdString(); - return getWalletManager()->getHDWalletById(walletId); -} - -void OTCWindowsAdapterBase::submitProposal(QComboBox *walletsCombobox, bs::XBTAmount amount, CbSuccess onSuccess) -{ - const auto hdWallet = getCurrentHDWalletFromCombobox(walletsCombobox); - if (!hdWallet) { - return; - } - - auto cbUtxoSet = [caller = QPointer(this), cbSuccess = std::move(onSuccess)](std::vector&& utxos) { - if (!caller) { - return; - } - - caller->setSelectedInputs(utxos); - caller->setReservation(caller->getUtxoManager()->makeNewReservation(utxos)); - - cbSuccess(); - }; - - auto checkAmount = bs::UTXOReservationManager::CheckAmount::Enabled; - - if (!hdWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(walletsCombobox); - getUtxoManager()->getBestXbtUtxoSet(hdWallet->walletId(), purpose, amount.GetValue() - , std::move(cbUtxoSet), true, checkAmount); - } - else { - getUtxoManager()->getBestXbtUtxoSet(hdWallet->walletId(), amount.GetValue() - , std::move(cbUtxoSet), true, checkAmount, bs::UTXOReservationManager::kIncludeZcOtc); - } -} - -QString OTCWindowsAdapterBase::getXBTRange(bs::network::otc::Range xbtRange) -{ - return QStringLiteral("%1 - %2") - .arg(UiUtils::displayCurrencyAmount(xbtRange.lower)) - .arg(UiUtils::displayCurrencyAmount(xbtRange.upper)); -} - -QString OTCWindowsAdapterBase::getCCRange(bs::network::otc::Range ccRange) -{ - return QStringLiteral("%1 - %2") - .arg(UiUtils::displayCurrencyAmount(bs::network::otc::fromCents(ccRange.lower))) - .arg(UiUtils::displayCurrencyAmount(bs::network::otc::fromCents(ccRange.upper))); -} - -QString OTCWindowsAdapterBase::getSide(bs::network::otc::Side requestSide, bool isOwnRequest) -{ - if (!isOwnRequest) { - requestSide = bs::network::otc::switchSide(requestSide); - } - - return QString::fromStdString(bs::network::otc::toString(requestSide)); -} - -void OTCWindowsAdapterBase::clearSelectedInputs() -{ - selectedUTXO_.clear(); - reservation_ = {}; -} - -void OTCWindowsAdapterBase::setupTimer(TimeoutData&& timeoutData) -{ - currentTimeoutData_ = std::move(timeoutData); - onUpdateTimerData(); - timeoutTimer_.start(); -} - -std::chrono::seconds OTCWindowsAdapterBase::getSeconds(std::chrono::milliseconds durationInMillisecs) -{ - return std::chrono::duration_cast(durationInMillisecs); -} - -void OTCWindowsAdapterBase::setSelectedInputs(const std::vector& selectedUTXO) -{ - selectedUTXO_ = selectedUTXO; - -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.h deleted file mode 100644 index 49393ac13..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsAdapterBase.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTCWINDOWSMANAGER_H__ -#define __OTCWINDOWSMANAGER_H__ - -#include -#include "CommonTypes.h" -#include "OtcTypes.h" -#include "ValidityFlag.h" -#include "UtxoReservationToken.h" - -#include -#include -#include - -class QComboBox; -class OTCWindowsManager; -class AuthAddressManager; -class AssetManager; -class QLabel; -class QProgressBar; - -namespace bs { - namespace sync { - class WalletsManager; - - namespace hd { - class Wallet; - } - } - - namespace network { - namespace otc { - struct Peer; - } - } - class UTXOReservationManager; -} - -struct TimeoutData -{ - std::chrono::steady_clock::time_point offerTimestamp_{}; - QPointer progressBarTimeLeft_{}; - QPointer labelTimeLeft_{}; -}; - -using CbSuccess = std::function; -class OTCWindowsAdapterBase : public QWidget { - Q_OBJECT -public: - OTCWindowsAdapterBase(QWidget* parent = nullptr); - ~OTCWindowsAdapterBase() override = default; - - void setChatOTCManager(const std::shared_ptr& otcManager); - std::shared_ptr getWalletManager() const; - std::shared_ptr getAuthManager() const; - std::shared_ptr getAssetManager() const; - std::shared_ptr getUtxoManager() const; - - virtual void setPeer(const bs::network::otc::Peer &); - - bs::UtxoReservationToken releaseReservation(); - void setReservation(bs::UtxoReservationToken&& reservation); - -signals: - - void xbtInputsProcessed(); - -public slots: - virtual void onAboutToApply() {} - virtual void onChatRoomChanged() {} - virtual void onParentAboutToHide() {} - -protected slots: - virtual void onSyncInterface(); - void onUpdateMD(bs::network::Asset::Type, const QString&, const bs::network::MDFields&); - virtual void onMDUpdated(); - virtual void onUpdateBalances(); - void onUpdateTimerData(); - -protected: - // Shared function between children - void showXBTInputsClicked(QComboBox *walletsCombobox); - - void updateIndicativePrices( - bs::network::Asset::Type type - , const QString& security - , const bs::network::MDFields& fields); - - BTCNumericTypes::balance_type getXBTSpendableBalanceFromCombobox(QComboBox *walletsCombobox) const; - std::shared_ptr getCurrentHDWalletFromCombobox(QComboBox *walletsCombobox) const; - - void submitProposal(QComboBox *walletsCombobox, bs::XBTAmount amount, CbSuccess onSuccess); - - QString getXBTRange(bs::network::otc::Range xbtRange); - QString getCCRange(bs::network::otc::Range ccRange); - - QString getSide(bs::network::otc::Side requestSide, bool isOwnRequest); - - const std::vector &selectedUTXOs() const { return selectedUTXO_; } - void clearSelectedInputs(); - void setSelectedInputs(const std::vector& selectedUTXO); - - void setupTimer(TimeoutData&& timeoutData); - std::chrono::seconds getSeconds(std::chrono::milliseconds durationInMillisecs); - -protected: - std::shared_ptr otcManager_{}; - - bs::network::Asset::Type productGroup_ = bs::network::Asset::SpotXBT; - // #new_logic : fix security & product checking - QString security_{ QLatin1String("XBT/EUR") }; - QString sellProduct_{ QLatin1String("XBT") }; - QString buyProduct_{ QLatin1String("EUR") }; - double sellIndicativePrice_{}; - double buyIndicativePrice_{}; - - ValidityFlag validityFlag_; - std::chrono::seconds timeoutSec_{}; - - std::vector selectedUTXO_; - bs::UtxoReservationToken reservation_; - -private: - void showXBTInputs(QComboBox *walletsCombobox); - - QTimer timeoutTimer_; - TimeoutData currentTimeoutData_{}; -}; - -#endif // __OTCWINDOWSMANAGER_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsManager.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsManager.cpp deleted file mode 100644 index 69fa6cc55..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsManager.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OTCWindowsManager.h" -#include "Wallets/SyncWalletsManager.h" -#include "AuthAddressManager.h" -#include "MDCallbacksQt.h" -#include "AssetManager.h" -#include "UtxoReservationManager.h" - -OTCWindowsManager::OTCWindowsManager(QObject* parent /*= nullptr*/) -{ -} - -void OTCWindowsManager::init(const std::shared_ptr& walletsMgr - , const std::shared_ptr &authManager - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr& assetManager - , const std::shared_ptr &armory - , const std::shared_ptr &utxoReservationManager) -{ - // #new_logic : we shouldn't send aggregated signal for all events - - walletsMgr_ = walletsMgr; - - // Do not listen for walletChanged (too verbose and resets UI too often) and walletsReady (to late and resets UI after startup unexpectedly) - connect(walletsMgr_.get(), &bs::sync::WalletsManager::AuthLeafCreated, this, &OTCWindowsManager::syncInterfaceRequired); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletPromotedToPrimary, this, &OTCWindowsManager::syncInterfaceRequired); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletDeleted, this, &OTCWindowsManager::syncInterfaceRequired); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletAdded, this, &OTCWindowsManager::syncInterfaceRequired); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletsSynchronized, this, &OTCWindowsManager::syncInterfaceRequired); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::authWalletChanged, this, &OTCWindowsManager::syncInterfaceRequired); - - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &OTCWindowsManager::updateBalances); - - authManager_ = authManager; - connect(authManager_.get(), &AuthAddressManager::AddressListUpdated, this, &OTCWindowsManager::syncInterfaceRequired); - - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, this, &OTCWindowsManager::updateMDDataRequired); - - assetManager_ = assetManager; - connect(assetManager_.get(), &AssetManager::totalChanged, this, &OTCWindowsManager::updateBalances); - connect(assetManager_.get(), &AssetManager::securitiesChanged, this, &OTCWindowsManager::updateBalances); - - armory_ = armory; - utxoReservationManager_ = utxoReservationManager; - connect(utxoReservationManager_.get(), &bs::UTXOReservationManager::availableUtxoChanged, this, &OTCWindowsManager::updateBalances); - - emit syncInterfaceRequired(); -} - -std::shared_ptr OTCWindowsManager::getWalletManager() const -{ - return walletsMgr_; -} - -std::shared_ptr OTCWindowsManager::getAuthManager() const -{ - return authManager_; -} - -std::shared_ptr OTCWindowsManager::getAssetManager() const -{ - return assetManager_; -} - -std::shared_ptr OTCWindowsManager::getArmory() const -{ - return armory_; -} - -std::shared_ptr OTCWindowsManager::getUtxoManager() const -{ - return utxoReservationManager_; -} - diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsManager.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsManager.h deleted file mode 100644 index 2f7cc3abc..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/OTCWindowsManager.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __OTCWINDOWSADAPTERBASE_H__ -#define __OTCWINDOWSADAPTERBASE_H__ - -#include -#include "QObject" -#include "CommonTypes.h" - -namespace bs { - namespace sync { - class WalletsManager; - } -} -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class MDCallbacksQt; - -namespace bs { - class UTXOReservationManager; -} - -class OTCWindowsManager : public QObject { - Q_OBJECT -public: - OTCWindowsManager(QObject* parent = nullptr); - ~OTCWindowsManager() override = default; - - void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); - std::shared_ptr getWalletManager() const; - std::shared_ptr getAuthManager() const; - std::shared_ptr getAssetManager() const; - std::shared_ptr getArmory() const; - std::shared_ptr getUtxoManager() const; - -signals: - void syncInterfaceRequired(); - void updateMDDataRequired(bs::network::Asset::Type, const QString &, const bs::network::MDFields&); - void updateBalances(); - -protected: - std::shared_ptr walletsMgr_; - std::shared_ptr authManager_; - std::shared_ptr assetManager_; - std::shared_ptr armory_; - std::shared_ptr utxoReservationManager_; -}; - -#endif // __OTCWINDOWSADAPTERBASE_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.cpp b/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.cpp deleted file mode 100644 index 46902e4c8..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "PullOwnOTCRequestWidget.h" - -#include "OtcTypes.h" -#include "UiUtils.h" -#include "ui_PullOwnOTCRequestWidget.h" - -using namespace bs::network; - -namespace { - const QString headerTextOTCRequest = QObject::tr("OTC REQUEST"); - const QString headerTextOTCResponse = QObject::tr("OTC RESPONSE"); - const QString headerTextOTCPendingBuyerSign = QObject::tr("OTC PENDING SETTLEMENT PAY-IN"); - const QString headerTextOTCPendingSellerSign = QObject::tr("OTC PENDING SETTLEMENT PAY-OUT"); - - const QString buttonTextPull = QObject::tr("PULL"); - const QString buttonTextCancel = QObject::tr("CANCEL"); -} - -PullOwnOTCRequestWidget::PullOwnOTCRequestWidget(QWidget* parent) - : OTCWindowsAdapterBase(parent) - , ui_{ new Ui::PullOwnOTCRequestWidget() } -{ - ui_->setupUi(this); - connect(ui_->pullPushButton, &QPushButton::clicked, this, &PullOwnOTCRequestWidget::currentRequestPulled); -} - -PullOwnOTCRequestWidget::~PullOwnOTCRequestWidget() = default; - -void PullOwnOTCRequestWidget::setOffer(const bs::network::otc::Offer &offer) -{ - setupNegotiationInterface(headerTextOTCRequest); - setupOfferInfo(offer, true); - timeoutSec_ = getSeconds(bs::network::otc::negotiationTimeout()); -} - -void PullOwnOTCRequestWidget::setRequest(const bs::network::otc::QuoteRequest &request) -{ - setupNegotiationInterface(headerTextOTCRequest); - - ourSide_ = request.ourSide; - ui_->sideValue->setText(QString::fromStdString(otc::toString(request.ourSide))); - ui_->quantityValue->setText(QString::fromStdString(otc::toString(request.rangeType))); - ui_->priceValue->clear(); - ui_->priceWidget->hide(); - ui_->totalWidget->hide(); - - timeoutSec_ = getSeconds(bs::network::otc::publicRequestTimeout()); -} - -void PullOwnOTCRequestWidget::setResponse(const otc::QuoteResponse &response) -{ - setupNegotiationInterface(headerTextOTCResponse, true /* isResponse */); - - ourSide_ = response.ourSide; - ui_->sideValue->setText(QString::fromStdString(otc::toString(response.ourSide))); - ui_->quantityValue->setText(getXBTRange(response.amount)); - ui_->priceValue->setText(getCCRange(response.price)); - ui_->priceWidget->show(); - ui_->totalWidget->hide(); - - timeoutSec_ = std::chrono::seconds(0); -} - -void PullOwnOTCRequestWidget::setPendingBuyerSign(const bs::network::otc::Offer &offer) -{ - setupSignAwaitingInterface(headerTextOTCPendingSellerSign); - setupOfferInfo(offer, true); - timeoutSec_ = getSeconds(bs::network::otc::payoutTimeout()); -} - -void PullOwnOTCRequestWidget::setPendingSellerSign(const bs::network::otc::Offer &offer) -{ - setupSignAwaitingInterface(headerTextOTCPendingBuyerSign); - setupOfferInfo(offer, false); - timeoutSec_ = getSeconds(bs::network::otc::payinTimeout()); -} - -void PullOwnOTCRequestWidget::setPeer(const bs::network::otc::Peer &peer) -{ - using namespace bs::network::otc; - if ((peer.state == State::WaitBuyerSign && ourSide_ == otc::Side::Buy) || - (peer.state == State::WaitSellerSeal && ourSide_ == otc::Side::Sell)) { - timeoutSec_ = std::chrono::seconds(0); - ui_->progressBarTimeLeft->hide(); - ui_->labelTimeLeft->hide(); - ui_->horizontalWidgetSubmit->hide(); - } - - if (peer.state != State::Idle) { - ui_->sideValue->setText(getSide(ourSide_, peer.isOurSideSentOffer)); - } - - if (timeoutSec_.count()) { - setupTimer({ peer.stateTimestamp, ui_->progressBarTimeLeft, ui_->labelTimeLeft }); - } -} - -void PullOwnOTCRequestWidget::setupNegotiationInterface(const QString& headerText, bool isResponse /* = false */) -{ - ui_->progressBarTimeLeft->setVisible(!isResponse); - ui_->labelTimeLeft->setVisible(!isResponse); - ui_->horizontalWidgetSubmit->show(); - ui_->pullPushButton->setText(buttonTextPull); - ui_->headerLabel->setText(headerText); -} - -void PullOwnOTCRequestWidget::setupSignAwaitingInterface(const QString& headerText) -{ - ui_->progressBarTimeLeft->show(); - ui_->labelTimeLeft->show(); - ui_->horizontalWidgetSubmit->show(); - ui_->pullPushButton->setText(buttonTextCancel); - ui_->headerLabel->setText(headerText); -} - -void PullOwnOTCRequestWidget::setupOfferInfo(const bs::network::otc::Offer &offer, bool allowCancel) -{ - ourSide_ = offer.ourSide; - ui_->sideValue->setText(QString::fromStdString(otc::toString(offer.ourSide))); - - auto price = bs::network::otc::fromCents(offer.price); - auto amount = bs::network::otc::satToBtc(offer.amount); - ui_->quantityValue->setText(UiUtils::displayAmount(amount)); - ui_->priceValue->setText(UiUtils::displayCurrencyAmount(price)); - ui_->totalWidget->show(); - ui_->totalValue->setText(UiUtils::displayCurrencyAmount(price * amount)); - - ui_->priceWidget->show(); - ui_->pullPushButton->setVisible(allowCancel); -} diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.h b/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.h deleted file mode 100644 index 7d1d7c902..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __PULL_OWN_OTC_REQUEST_WIDGET_H__ -#define __PULL_OWN_OTC_REQUEST_WIDGET_H__ - -#include -#include -#include -#include - -#include "OtcTypes.h" -#include "OTCWindowsAdapterBase.h" - -namespace Ui { - class PullOwnOTCRequestWidget; -}; - -class PullOwnOTCRequestWidget : public OTCWindowsAdapterBase -{ -Q_OBJECT - -public: - explicit PullOwnOTCRequestWidget(QWidget* parent = nullptr); - ~PullOwnOTCRequestWidget() override; - - void setOffer(const bs::network::otc::Offer &offer); - void setRequest(const bs::network::otc::QuoteRequest &request); - void setResponse(const bs::network::otc::QuoteResponse &response); - void setPendingBuyerSign(const bs::network::otc::Offer &offer); - void setPendingSellerSign(const bs::network::otc::Offer &offer); - - void setPeer(const bs::network::otc::Peer &peer) override; - -signals: - void currentRequestPulled(); - - void saveOfflineClicked(); - void loadOfflineClicked(); - void broadcastOfflineClicked(); - -protected: - void setupNegotiationInterface(const QString& headerText, bool isResponse = false); - void setupSignAwaitingInterface(const QString& headerText); - void setupOfferInfo(const bs::network::otc::Offer &offer, bool allowCancel); - -private: - std::unique_ptr ui_; - bs::network::otc::Side ourSide_ = bs::network::otc::Side::Unknown; -}; - -#endif // __PULL_OWN_OTC_REQUEST_WIDGET_H__ diff --git a/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.ui b/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.ui deleted file mode 100644 index 3c6bb51db..000000000 --- a/BlockSettleUILib/ChatUI/OTCShieldWidgets/PullOwnOTCRequestWidget.ui +++ /dev/null @@ -1,508 +0,0 @@ - - - - PullOwnOTCRequestWidget - - - - 0 - 0 - 460 - 724 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - OTC REQUEST - - - true - - - - - - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - OTC DETAILS - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Product Group - - - - - - - XBT - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Security ID - - - - - - - XBT/EUR - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Side - - - - - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity (XBT) - - - - - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Price (EUR) - - - - - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Total Value (EUR) - - - - - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - - - - - 5 - - - 5 - - - - - Qt::Vertical - - - - 20 - 496 - - - - - - - - - 0 - 7 - - - - - 16777215 - 7 - - - - 24 - - - Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing - - - false - - - false - - - - - - - - - - Qt::AlignCenter - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 35 - - - - - - - PULL - - - - - - - - - - - diff --git a/BlockSettleUILib/ChatUI/PartyTreeItem.cpp b/BlockSettleUILib/ChatUI/PartyTreeItem.cpp deleted file mode 100644 index fa06f7ac5..000000000 --- a/BlockSettleUILib/ChatUI/PartyTreeItem.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "PartyTreeItem.h" - -using namespace bs; - -PartyTreeItem::PartyTreeItem(const QVariant& data, UI::ElementType modelType, PartyTreeItem* parent /*= nullptr*/) - : itemData_(data) - , modelType_(modelType) - , parentItem_(parent) -{ -} - -PartyTreeItem::~PartyTreeItem() -{ -} - -PartyTreeItem* PartyTreeItem::child(int number) -{ - Q_ASSERT(number >= 0 && number < childItems_.size()); - return childItems_[number].get(); -} - -int PartyTreeItem::childCount() const -{ - return childItems_.size(); -} - -int PartyTreeItem::columnCount() const -{ - return 1; -} - -QVariant PartyTreeItem::data() const -{ - return itemData_; -} - -bool PartyTreeItem::insertChildren(std::unique_ptr&& item) -{ - childItems_.push_back(std::move(item)); - return true; -} - -PartyTreeItem* PartyTreeItem::parent() -{ - return parentItem_; -} - -void PartyTreeItem::removeAll() -{ - childItems_.clear(); -} - -int PartyTreeItem::childNumber() const -{ - if (parentItem_) { - for (int iChild = 0; iChild < parentItem_->childCount(); ++iChild) { - if (parentItem_->childItems_[iChild].get() != this) { - continue; - } - - return iChild; - } - } - - Q_ASSERT(false); - return 0; -} - -bool PartyTreeItem::setData(const QVariant& value) -{ - itemData_ = value; - return true; -} - -UI::ElementType PartyTreeItem::modelType() const -{ - return modelType_; -} - -void PartyTreeItem::increaseUnseenCounter(int newMessageCount) -{ - Q_ASSERT(newMessageCount > 0); - unseenCounter_ += newMessageCount; -} - -void PartyTreeItem::decreaseUnseenCounter(int seenMessageCount) -{ - unseenCounter_ -= seenMessageCount; - unseenCounter_ = std::max(unseenCounter_, 0); -} - -bool PartyTreeItem::hasNewMessages() const -{ - return unseenCounter_ > 0; -} - -int PartyTreeItem::unseenCount() const -{ - return unseenCounter_; -} - -void PartyTreeItem::enableOTCToggling(bool otcToggling) -{ - otcTogglingMode_ = otcToggling; -} - -bool PartyTreeItem::isOTCTogglingMode() const -{ - // #flickeringOTC Flickering otc awaiting messages - disabled for now - return false; // otcTogglingMode_; -} - -void PartyTreeItem::changeOTCToggleState() -{ - currentOTCToggleState_ = !currentOTCToggleState_; -} - -bool PartyTreeItem::activeOTCToggleState() const -{ - return currentOTCToggleState_; -} - -void PartyTreeItem::applyReusableData(const ReusableItemData& data) -{ - unseenCounter_ = data.unseenCount_; - otcTogglingMode_ = data.otcTogglingMode_; -} - -ReusableItemData PartyTreeItem::generateReusableData() const -{ - return { unseenCounter_ , otcTogglingMode_ }; -} diff --git a/BlockSettleUILib/ChatUI/PartyTreeItem.h b/BlockSettleUILib/ChatUI/PartyTreeItem.h deleted file mode 100644 index 23cb326bf..000000000 --- a/BlockSettleUILib/ChatUI/PartyTreeItem.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef PARTYTREEITEM_H -#define PARTYTREEITEM_H - -#include -#include "QList" - -#include "../BlocksettleNetworkingLib/ChatProtocol/Party.h" -#include "chat.pb.h" -// Internal enum - -namespace bs { - namespace network { - namespace otc { - enum class PeerType : int; - } - } -} - -namespace bs { - namespace UI { - enum class ElementType - { - Root = 0, - Container, - Party, - }; - } -} - -struct ReusableItemData -{ - int unseenCount_{}; - bool otcTogglingMode_{}; -}; - -class PartyTreeItem -{ -public: - PartyTreeItem(const QVariant& data, bs::UI::ElementType modelType, PartyTreeItem* parent = nullptr); - ~PartyTreeItem(); - - PartyTreeItem* child(int number); - - int childCount() const; - int columnCount() const; - - QVariant data() const; - - bool insertChildren(std::unique_ptr&& item); - PartyTreeItem* parent(); - void removeAll(); - int childNumber() const; - bool setData(const QVariant& value); - - bs::UI::ElementType modelType() const; - - void increaseUnseenCounter(int newMessageCount); - void decreaseUnseenCounter(int seenMessageCount); - bool hasNewMessages() const; - int unseenCount() const; - - void enableOTCToggling(bool otcToggling); - bool isOTCTogglingMode() const; - - void changeOTCToggleState(); - bool activeOTCToggleState() const; - - void applyReusableData(const ReusableItemData& data); - ReusableItemData generateReusableData() const; - - bs::network::otc::PeerType peerType{}; - -private: - std::vector> childItems_; - QVariant itemData_; - PartyTreeItem* parentItem_; - bs::UI::ElementType modelType_; - int unseenCounter_{}; - - // OTC toggling - bool otcTogglingMode_{}; - bool currentOTCToggleState_{}; -}; -#endif // PARTYTREEITEM_H diff --git a/BlockSettleUILib/ChatUI/RequestPartyBox.cpp b/BlockSettleUILib/ChatUI/RequestPartyBox.cpp deleted file mode 100644 index 96ba50b24..000000000 --- a/BlockSettleUILib/ChatUI/RequestPartyBox.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RequestPartyBox.h" - -RequestPartyBox::RequestPartyBox(const QString& title, const QString& note, QWidget* parent) - : QDialog(parent), ui_(new Ui::RequestPartyBox) -{ - ui_->setupUi(this); - ui_->labelTitle->setText(title); - ui_->labelNote->setText(note); - resize(width(), 0); - - connect(ui_->pushButtonOk, &QPushButton::clicked, this, &RequestPartyBox::accept); - connect(ui_->plainTextEditMessage, &BSChatInput::sendMessage, this, &RequestPartyBox::accept); - connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &RequestPartyBox::reject); - - setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); -} - -QString RequestPartyBox::getCustomMessage() const -{ - return ui_->plainTextEditMessage->toPlainText(); -} \ No newline at end of file diff --git a/BlockSettleUILib/ChatUI/RequestPartyBox.h b/BlockSettleUILib/ChatUI/RequestPartyBox.h deleted file mode 100644 index 143e99c52..000000000 --- a/BlockSettleUILib/ChatUI/RequestPartyBox.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef REQUESTPARTY_H -#define REQUESTPARTY_H - -#include -#include -#include "ui_RequestPartyBox.h" - -class RequestPartyBox : public QDialog -{ - Q_OBJECT - -public: - RequestPartyBox(const QString& title, const QString& note, QWidget* parent = nullptr); - QString getCustomMessage() const; - -private: - QString title_; - QString note_; - std::unique_ptr ui_; -}; - -#endif // REQUESTPARTY_H diff --git a/BlockSettleUILib/ChatUI/RequestPartyBox.ui b/BlockSettleUILib/ChatUI/RequestPartyBox.ui deleted file mode 100644 index 803241e79..000000000 --- a/BlockSettleUILib/ChatUI/RequestPartyBox.ui +++ /dev/null @@ -1,361 +0,0 @@ - - - - RequestPartyBox - - - - 0 - 0 - 400 - 251 - - - - - 0 - 0 - - - - - 400 - 0 - - - - - 400 - 16777215 - - - - Friend Request - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - 12 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 43 - 43 - - - - - 43 - 43 - - - - - - - :/resources/notification_question.png - - - - - - - title - - - true - - - true - - - true - - - - - - - - - 12 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 43 - 0 - - - - - 43 - 16777215 - - - - - - - - Note - - - true - - - - - - - - - 0 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Qt::Vertical - - - - 0 - 40 - - - - - - - - - 0 - 0 - - - - - 0 - 70 - - - - - 16777215 - 16777215 - - - - true - - - false - - - Friend request message... - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 10 - - - 20 - - - 5 - - - 20 - - - 5 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 120 - 0 - - - - - 120 - 16777215 - - - - Cancel - - - - - - - - 120 - 0 - - - - - 120 - 16777215 - - - - OK - - - - - - - - - - - - - - - BSChatInput - QTextBrowser -
ChatUI/BSChatInput.h
-
-
- - - - - -
diff --git a/BlockSettleUILib/ChatUI/SearchWidget.cpp b/BlockSettleUILib/ChatUI/SearchWidget.cpp deleted file mode 100644 index 543d4092e..000000000 --- a/BlockSettleUILib/ChatUI/SearchWidget.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include -#include -#include -#include - -#include "SearchWidget.h" -#include "ui_SearchWidget.h" -#include "UserSearchModel.h" -#include "ChatUI/ChatSearchListViewItemStyle.h" - -#include "chat.pb.h" - -namespace { - constexpr int kShowEmptyFoundUserListTimeoutMs = 3000; - constexpr int kRowHeigth = 20; - constexpr int kUserListPaddings = 6; - constexpr int kMaxVisibleRows = 3; - constexpr int kBottomSpace = 25; - constexpr int kFullHeightMargins = 10; - - const QRegularExpression kRxEmail(QStringLiteral(R"(^[a-z0-9._-]+@([a-z0-9-]+\.)+[a-z]+$)"), - QRegularExpression::CaseInsensitiveOption); -} - -SearchWidget::SearchWidget(QWidget *parent) - : QWidget(parent) - , ui_(new Ui::SearchWidget) - , listVisibleTimer_(new QTimer) - , userSearchModel_(new UserSearchModel) - , emailRegex_(kRxEmail) -{ - ui_->setupUi(this); - - connect(ui_->chatSearchLineEdit, &ChatSearchLineEdit::textEdited, - this, &SearchWidget::onSearchUserTextEdited); - connect(ui_->chatSearchLineEdit, &ChatSearchLineEdit::textChanged, - this, &SearchWidget::searchTextChanged); - connect(ui_->chatSearchLineEdit, &ChatSearchLineEdit::textChanged, - this, &SearchWidget::onInputTextChanged); - connect(ui_->chatSearchLineEdit, &ChatSearchLineEdit::keyDownPressed, - this, &SearchWidget::onFocusResults); - connect(ui_->chatSearchLineEdit, &ChatSearchLineEdit::keyEnterPressed, - this, &SearchWidget::onFocusResults); - connect(ui_->chatSearchLineEdit, &ChatSearchLineEdit::keyEscapePressed, - this, &SearchWidget::onCloseResult); - - connect(ui_->searchResultTreeView, &ChatSearchListVew::customContextMenuRequested, - this, &SearchWidget::onShowContextMenu); - connect(ui_->searchResultTreeView, &ChatSearchListVew::activated, - this, &SearchWidget::onItemClicked); - connect(ui_->searchResultTreeView, &ChatSearchListVew::clicked, - this, &SearchWidget::onItemClicked); - connect(ui_->searchResultTreeView, &ChatSearchListVew::leaveRequired, - this, &SearchWidget::onLeaveSearchResults); - connect(ui_->searchResultTreeView, &ChatSearchListVew::leaveWithCloseRequired, - this, &SearchWidget::onLeaveAndCloseSearchResults); - - onSetListVisible(false); - assert(emailRegex_.isValid()); -} - -SearchWidget::~SearchWidget() = default; - -bool SearchWidget::eventFilter(QObject *watched, QEvent *event) -{ - if (ui_->searchResultTreeView->isVisible() && event->type() == QEvent::MouseButtonRelease) { - QPoint pos = ui_->searchResultTreeView->mapFromGlobal(QCursor::pos()); - - if (!ui_->searchResultTreeView->rect().contains(pos)) { - onSetListVisible(false); - } - } - - return QWidget::eventFilter(watched, event); -} - -void SearchWidget::init(const Chat::ChatClientServicePtr& chatClientServicePtr) -{ - chatClientServicePtr_ = chatClientServicePtr; - connect(chatClientServicePtr_.get(), &Chat::ChatClientService::searchUserReply, this, &SearchWidget::onSearchUserReply); - - //chatClient_ = chatClient; - //ui_->chatSearchLineEdit->setActionsHandler(chatClient); - userSearchModel_->setItemStyle(std::make_shared()); - ui_->searchResultTreeView->setModel(userSearchModel_.get()); - - //connect(chatClient_.get(), &ChatClient::SearchUserListReceived, this, &SearchWidget::onSearchUserListReceived); - - connect(userSearchModel_.get(), &QAbstractItemModel::rowsInserted, - this, &SearchWidget::onResetTreeView); - connect(userSearchModel_.get(), &QAbstractItemModel::rowsRemoved, - this, &SearchWidget::onResetTreeView); - connect(userSearchModel_.get(), &QAbstractItemModel::modelReset, - this, &SearchWidget::onResetTreeView); - - setMaximumHeight(kBottomSpace + kRowHeigth * kMaxVisibleRows + - ui_->chatSearchLineEdit->height() + kUserListPaddings + kFullHeightMargins); - setMinimumHeight(kBottomSpace + ui_->chatSearchLineEdit->height()); - - ui_->searchResultTreeView->setVisible(false); - ui_->notFoundLabel->setVisible(false); - - qApp->installEventFilter(this); - - listVisibleTimer_->setSingleShot(true); - connect(listVisibleTimer_.get(), &QTimer::timeout, [this] { - onSetListVisible(false); - }); -} - -bool SearchWidget::isLineEditEnabled() const -{ - return ui_->chatSearchLineEdit->isEnabled(); -} - -bool SearchWidget::isListVisible() const -{ - return ui_->searchResultTreeView->isVisible(); -} - -QString SearchWidget::searchText() const -{ - return ui_->chatSearchLineEdit->text(); -} - -void SearchWidget::onClearLineEdit() -{ - ui_->chatSearchLineEdit->clear(); -} - -void SearchWidget::onStartListAutoHide() -{ - listVisibleTimer_->start(kShowEmptyFoundUserListTimeoutMs); -} - -void SearchWidget::onSetLineEditEnabled(bool value) -{ - ui_->chatSearchLineEdit->setEnabled(value); -} - -void SearchWidget::onSetListVisible(bool value) -{ - auto *result = ui_->searchResultTreeView; - bool hasUsers = result->model() && result->model()->rowCount() > 0; - - result->setVisible(value && hasUsers); - result->scrollToTop(); - result->setCurrentIndex(QModelIndex()); - ui_->notFoundLabel->setVisible(value && !hasUsers); - layout()->update(); - listVisibleTimer_->stop(); - - // hide popup after a few sec - if (value && !hasUsers) { - onStartListAutoHide(); - } -} - -void SearchWidget::onSetSearchText(QString value) -{ - ui_->chatSearchLineEdit->setText(value); -} - -void SearchWidget::onResetTreeView() -{ - int rowCount = ui_->searchResultTreeView->model()->rowCount(); - int visibleRows = rowCount >= kMaxVisibleRows ? kMaxVisibleRows : rowCount; - ui_->searchResultTreeView->setFixedHeight(kRowHeigth * visibleRows + kUserListPaddings); -} - -void SearchWidget::onShowContextMenu(const QPoint &pos) -{ - QScopedPointer menu(new QMenu()); - auto index = ui_->searchResultTreeView->indexAt(pos); - if (!index.isValid()) { - return; - } - - onItemClicked(index); -} - -void SearchWidget::onFocusResults() -{ - if (ui_->searchResultTreeView->isVisible()) { - ui_->searchResultTreeView->setFocus(); - auto index = ui_->searchResultTreeView->model()->index(0, 0); - ui_->searchResultTreeView->setCurrentIndex(index); - return; - } - - onSetListVisible(true); -} - -void SearchWidget::onCloseResult() -{ - onSetListVisible(false); -} - -void SearchWidget::onItemClicked(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - const QString id = index.data(Qt::DisplayRole).toString(); - const auto status = index.data(UserSearchModel::UserStatusRole).value(); - switch (status) - { - case UserSearchModel::UserStatus::ContactUnknown: - case UserSearchModel::UserStatus::ContactRejected: - { - emit contactFriendRequest(id); - } - break; - case UserSearchModel::UserStatus::ContactAccepted: - case UserSearchModel::UserStatus::ContactPendingIncoming: - case UserSearchModel::UserStatus::ContactPendingOutgoing: - { - emit showUserRoom(id); - } - break; - default: - return; - } - - onSetListVisible(false); - onSetSearchText({}); -} - -void SearchWidget::onLeaveSearchResults() -{ - ui_->chatSearchLineEdit->setFocus(); - ui_->searchResultTreeView->clearSelection(); - auto currentIndex = ui_->searchResultTreeView->currentIndex(); - ui_->searchResultTreeView->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::Deselect); -} - -void SearchWidget::onLeaveAndCloseSearchResults() -{ - ui_->chatSearchLineEdit->setFocus(); - onSetListVisible(false); -} - -void SearchWidget::onInputTextChanged(const QString &text) -{ - if (text.isEmpty()) { - onSetListVisible(false); - } -} - -void SearchWidget::onSearchUserTextEdited() -{ - onSetListVisible(false); - std::string userToAdd = searchText().toStdString(); - - if (userToAdd.empty() || userToAdd.length() < 3) { - onSetListVisible(false); - userSearchModel_->setUsers({}); - return; - } - - QRegularExpressionMatch match = emailRegex_.match(QString::fromStdString(userToAdd)); - if (match.hasMatch()) { - emit emailHashRequested(userToAdd); - return; - } - - // ! Feature: Think how to prevent spamming server - - sendSearchRequest(userToAdd); -} - -void SearchWidget::onSearchUserReply(const Chat::SearchUserReplyList& userHashList, const std::string& searchId) -{ - if (searchId != lastSearchId_) { - return; - } - - Chat::ClientPartyModelPtr clientPartyModelPtr = chatClientServicePtr_->getClientPartyModelPtr(); - std::vector userInfoList; - - for (const auto& userHash : userHashList) { - Chat::PrivatePartyState privatePartyState = clientPartyModelPtr->deducePrivatePartyStateForUser(userHash); - - auto status = UserSearchModel::UserStatus::ContactUnknown; - switch (privatePartyState) - { - case Chat::PrivatePartyState::RequestedOutgoing: - status = UserSearchModel::UserStatus::ContactPendingOutgoing; - break; - case Chat::PrivatePartyState::RequestedIncoming: - status = UserSearchModel::UserStatus::ContactPendingIncoming; - break; - case Chat::PrivatePartyState::Rejected: - status = UserSearchModel::UserStatus::ContactRejected; - break; - case Chat::PrivatePartyState::Initialized: - status = UserSearchModel::UserStatus::ContactAccepted; - break; - default: - break; - } - - userInfoList.emplace_back(QString::fromStdString(userHash), status); - } - - userSearchModel_->setUsers(userInfoList); - - bool visible = !userInfoList.empty(); - onSetListVisible(visible); - - // hide popup after a few sec - if (visible && userInfoList.empty()) { - onStartListAutoHide(); - } -} - -void SearchWidget::onEmailHashReceived(const std::string &email, const std::string &hash) -{ - if (searchText().toStdString() != email) { - return; - } - - sendSearchRequest(hash); -} - -void SearchWidget::sendSearchRequest(const std::string &text) -{ - QUuid uid = QUuid::createUuid(); - lastSearchId_ = uid.toString(QUuid::WithoutBraces).toStdString(); - chatClientServicePtr_->SearchUser(text, lastSearchId_); -} diff --git a/BlockSettleUILib/ChatUI/SearchWidget.h b/BlockSettleUILib/ChatUI/SearchWidget.h deleted file mode 100644 index 6afcf8dfe..000000000 --- a/BlockSettleUILib/ChatUI/SearchWidget.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef SEARCHWIDGET_H -#define SEARCHWIDGET_H - -#include - -#include -#include - -#include "ChatProtocol/ChatClientService.h" - -class QAbstractItemModel; -class ChatClient; -class ChatSearchActionsHandler; -class UserSearchModel; - -namespace Ui { - class SearchWidget; -} - -namespace Chat { - class UserData; - class Data; -} - -class SearchWidget : public QWidget -{ - Q_OBJECT - -/* Properties Section Begin */ - Q_PROPERTY(bool lineEditEnabled - READ isLineEditEnabled - WRITE onSetLineEditEnabled - STORED false) - Q_PROPERTY(bool listVisible - READ isListVisible - WRITE onSetListVisible - STORED false) - Q_PROPERTY(QString searchText - READ searchText - WRITE onSetSearchText - NOTIFY searchTextChanged - USER true - STORED false) - -public: - bool isLineEditEnabled() const; - bool isListVisible() const; - QString searchText() const; - -public slots: - void onSetLineEditEnabled(bool value); - void onSetListVisible(bool value); - void onSetSearchText(QString value); - -signals: - void searchTextChanged(QString searchText); - void emailHashRequested(const std::string &email); -/* Properties Section End */ - -public: - explicit SearchWidget(QWidget *parent = nullptr); - ~SearchWidget() override; - - bool eventFilter(QObject *watched, QEvent *event) override; - - void init(const Chat::ChatClientServicePtr& chatClientServicePtr); - -public slots: - void onClearLineEdit(); - void onStartListAutoHide(); - void onSearchUserReply(const Chat::SearchUserReplyList& userHashList, const std::string& searchId); - void onEmailHashReceived(const std::string &email, const std::string &hash); - -private slots: - void onResetTreeView(); - void onShowContextMenu(const QPoint &pos); - void onFocusResults(); - void onCloseResult(); - void onItemClicked(const QModelIndex &index); - void onLeaveSearchResults(); - void onLeaveAndCloseSearchResults(); - void onInputTextChanged(const QString &text); - void onSearchUserTextEdited(); - -signals: - void contactFriendRequest(const QString &userID); - void showUserRoom(const QString &userID); - -private: - void sendSearchRequest(const std::string &text); - - QScopedPointer ui_; - QScopedPointer listVisibleTimer_; - QScopedPointer userSearchModel_; - Chat::ChatClientServicePtr chatClientServicePtr_; - std::string lastSearchId_; - - QRegularExpression emailRegex_; - -}; - -#endif // SEARCHWIDGET_H diff --git a/BlockSettleUILib/ChatUI/SearchWidget.ui b/BlockSettleUILib/ChatUI/SearchWidget.ui deleted file mode 100644 index d75c2d152..000000000 --- a/BlockSettleUILib/ChatUI/SearchWidget.ui +++ /dev/null @@ -1,160 +0,0 @@ - - - - SearchWidget - - - - 0 - 0 - 226 - 159 - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 2 - - - 0 - - - 5 - - - 0 - - - - - - - - Enter email or email hash - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - User not found - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - - - - ChatSearchLineEdit - QLineEdit -
ChatUI/ChatSearchLineEdit.h
-
- - ChatSearchListVew - QTreeView -
ChatUI/ChatSearchListVew.h
-
-
- - -
diff --git a/BlockSettleUILib/EditContactDialog.cpp b/BlockSettleUILib/EditContactDialog.cpp deleted file mode 100644 index 1b78a08cf..000000000 --- a/BlockSettleUILib/EditContactDialog.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "EditContactDialog.h" -#include "ui_EditContactDialog.h" - -EditContactDialog::EditContactDialog(const QString &contactId - , const QString &displayName - , const QDateTime ×tamp - , const QString &idKey - , QWidget *parent) : - QDialog(parent) - , ui_(new Ui::EditContactDialog()) - , contactId_(contactId) - , displayName_(displayName) - , timestamp_(timestamp) - , idKey_(idKey) -{ - ui_->setupUi(this); - - refillFields(); - connect(ui_->buttonBox, &QDialogButtonBox::accepted, this, &EditContactDialog::accept); - - ui_->nameOptionalLineEdit->setFocus(); - ui_->nameOptionalLineEdit->selectAll(); -} - -EditContactDialog::~EditContactDialog() noexcept = default; - -QString EditContactDialog::contactId() const -{ - return contactId_; -} - -QString EditContactDialog::displayName() const -{ - return displayName_; -} - -QDateTime EditContactDialog::timestamp() const -{ - return timestamp_; -} - -QString EditContactDialog::idKey() const -{ - return idKey_; -} - -void EditContactDialog::accept() -{ - displayName_ = ui_->nameOptionalLineEdit->text(); - QDialog::accept(); -} - -void EditContactDialog::reject() -{ - refillFields(); - QDialog::reject(); -} - -void EditContactDialog::showEvent(QShowEvent *event) -{ - Q_UNUSED(event) - auto dialogCenter = window()->mapToGlobal(window()->rect().center()); - auto parentWindow = parentWidget()->window(); - auto parentWindowCenter = parentWindow->mapToGlobal(parentWindow->rect().center()); - if (parentWindowCenter == dialogCenter) { - move(parentWindowCenter - window()->rect().center()); - } else { - move(parentWindowCenter - dialogCenter); - } -} - -void EditContactDialog::refillFields() -{ - ui_->nameOptionalLineEdit->setText(displayName_); - ui_->userIDLineEdit->setText(contactId_); - if (timestamp_.isValid()) { - ui_->contactDateLineEdit->setText(timestamp_.toString(Qt::SystemLocaleShortDate)); - } - ui_->iDKeyLineEdit->setText(idKey_); -} diff --git a/BlockSettleUILib/EditContactDialog.h b/BlockSettleUILib/EditContactDialog.h deleted file mode 100644 index fce436606..000000000 --- a/BlockSettleUILib/EditContactDialog.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef EDITCONTACTDIALOG_H -#define EDITCONTACTDIALOG_H - -#include -#include - -#include - -namespace Ui { -class EditContactDialog; -} - -class EditContactDialog : public QDialog -{ - Q_OBJECT - -public: - explicit EditContactDialog( - const QString &contactId - , const QString &displayName = QString() - , const QDateTime ×tamp = QDateTime() - , const QString &idKey = QString() - , QWidget *parent = nullptr); - ~EditContactDialog() noexcept override; - - QString contactId() const; - QString displayName() const; - QDateTime timestamp() const; - QString idKey() const; - -public slots: - void accept() override; - void reject() override; - -protected: - void showEvent(QShowEvent *event) override; - -private: - void refillFields(); - -private: - std::unique_ptr ui_; - QString contactId_; - QString displayName_; - QDateTime timestamp_; - QString idKey_; -}; - -#endif // EDITCONTACTDIALOG_H diff --git a/BlockSettleUILib/EditContactDialog.ui b/BlockSettleUILib/EditContactDialog.ui deleted file mode 100644 index 8d6aa1aed..000000000 --- a/BlockSettleUILib/EditContactDialog.ui +++ /dev/null @@ -1,346 +0,0 @@ - - - - EditContactDialog - - - - 0 - 0 - 595 - 182 - - - - - 225 - 0 - - - - Edit contact - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 15 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - - 15 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Contact Details - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 115 - 0 - - - - - 115 - 16777215 - - - - Name (optional) - - - - - - - Enter contact name... - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 115 - 0 - - - - - 115 - 16777215 - - - - User ID - - - - - - - -- - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 115 - 0 - - - - - 115 - 16777215 - - - - ID Key - - - - - - - -- - - - true - - - - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 115 - 0 - - - - - 115 - 16777215 - - - - Time Stamp - - - - - - - -- - - - true - - - - - - - - - - - - - - - - - - - true - - - - 5 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - QDialogButtonBox::Ok - - - - - - - - - - - diff --git a/BlockSettleUILib/LoginWindow.cpp b/BlockSettleUILib/LoginWindow.cpp index c5f023575..c34e48f7d 100644 --- a/BlockSettleUILib/LoginWindow.cpp +++ b/BlockSettleUILib/LoginWindow.cpp @@ -15,15 +15,12 @@ #include "InfoDialogs/AboutDialog.h" #include "ApplicationSettings.h" #include "BSMessageBox.h" -#include "BsClient.h" #include "UiUtils.h" -#include "ZmqContext.h" #include "ui_LoginWindow.h" #include "FutureValue.h" #include "QBitmap" namespace { - const auto kAutheIdTimeout = int(BsClient::autheidLoginTimeout() / std::chrono::seconds(1)); const auto kProdTitle = QObject::tr("Login to BlockSettle"); const auto kTestTitle = QObject::tr("Test Environment Login"); @@ -33,69 +30,6 @@ namespace { //Not signed up yet ? < / span>
Get a BlockSettle Account< / span>< / a> } -LoginWindow::LoginWindow(const std::shared_ptr &logger - , const std::shared_ptr &bsClient - , std::shared_ptr &settings - , QWidget* parent) - : QDialog(parent) - , ui_(new Ui::LoginWindow()) - , logger_(logger) - , settings_(settings) - , bsClient_(bsClient) -{ - ui_->setupUi(this); - ui_->progressBar->setMaximum(kAutheIdTimeout * 2); // update every 0.5 sec - const auto version = ui_->loginVersionLabel->text().replace(QLatin1String("{Version}") - , tr("Version %1").arg(QString::fromStdString(AboutDialog::version()))); - ui_->loginVersionLabel->setText(version); - resize(minimumSize()); - - const bool isProd = settings_->get(ApplicationSettings::envConfiguration) == - static_cast(ApplicationSettings::EnvConfiguration::Production); - - ApplicationSettings::Setting urlType; - auto getAccountText = ui_->labelGetAccount->text(); - QString title; - if (isProd) { - urlType = ApplicationSettings::GetAccount_UrlProd; - title = kProdTitle; - } - else { - urlType = ApplicationSettings::GetAccount_UrlTest; - title = kTestTitle; - getAccountText.replace(kCreateAccountProd, kCreateAccountTest); - } - - ui_->widgetSignup->setProperty("prodEnv", QVariant(true)); - getAccountText.replace(QLatin1String("{GetAccountLink}") - , settings->get(urlType)); - ui_->labelGetAccount->setText(getAccountText); - ui_->widgetSignup->update(); - - connect(ui_->lineEditUsername, &QLineEdit::textChanged, this, &LoginWindow::onTextChanged); - - ui_->checkBoxRememberUsername->setChecked(settings_->get(ApplicationSettings::rememberLoginUserName)); - - const QString username = settings_->get(ApplicationSettings::celerUsername); - if (!username.isEmpty() && ui_->checkBoxRememberUsername->isChecked()) { - ui_->lineEditUsername->setText(username); - } - else { - ui_->lineEditUsername->setFocus(); - } - - connect(ui_->signWithEidButton, &QPushButton::clicked, this, &LoginWindow::accept); - - timer_.setInterval(500); - connect(&timer_, &QTimer::timeout, this, &LoginWindow::onTimer); - timer_.start(); - - updateState(); - - connect(bsClient_.get(), &BsClientQt::startLoginDone, this, &LoginWindow::onStartLoginDone); - connect(bsClient_.get(), &BsClientQt::getLoginResultDone, this, &LoginWindow::onGetLoginResultDone); -} - LoginWindow::LoginWindow(const std::shared_ptr& logger , ApplicationSettings::EnvConfiguration envCfg, QWidget* parent) : QDialog(parent) @@ -103,7 +37,7 @@ LoginWindow::LoginWindow(const std::shared_ptr& logger , logger_(logger) { ui_->setupUi(this); - ui_->progressBar->setMaximum(kAutheIdTimeout * 2); // update every 0.5 sec + //ui_->progressBar->setMaximum(kAutheIdTimeout * 2); // update every 0.5 sec const auto version = ui_->loginVersionLabel->text().replace(QLatin1String("{Version}") , tr("Version %1").arg(QString::fromStdString(AboutDialog::version()))); ui_->loginVersionLabel->setText(version); @@ -201,6 +135,7 @@ void LoginWindow::onLoginStarted(const std::string& login, bool success } } +#if 0 void LoginWindow::onLoggedIn(const BsClientLoginResult& result) { if (result.status == AutheIDClient::Cancelled || result.status == AutheIDClient::Timeout) { @@ -214,6 +149,7 @@ void LoginWindow::onLoggedIn(const BsClientLoginResult& result) } QDialog::accept(); } +#endif void LoginWindow::onStartLoginDone(bool success, const std::string &errorMsg) { @@ -223,28 +159,11 @@ void LoginWindow::onStartLoginDone(bool success, const std::string &errorMsg) return; } - bsClient_->getLoginResult(); + //bsClient_->getLoginResult(); setState(WaitLoginResult); } -void LoginWindow::onGetLoginResultDone(const BsClientLoginResult &result) -{ - if (result.status == AutheIDClient::Cancelled || result.status == AutheIDClient::Timeout) { - setState(Idle); - return; - } - - if (result.status != AutheIDClient::NoError) { - setState(Idle); - displayError(result.errorMsg); - return; - } - - result_ = std::make_unique(result); - QDialog::accept(); -} - void LoginWindow::accept() { onAuthPressed(); @@ -253,12 +172,7 @@ void LoginWindow::accept() void LoginWindow::reject() { if (state_ == WaitLoginResult) { - if (bsClient_) { - bsClient_->cancelLogin(); - } - else { - emit needCancelLogin(); - } + emit needCancelLogin(); } QDialog::reject(); } @@ -305,17 +219,10 @@ void LoginWindow::onAuthPressed() return; } - timeLeft_ = kAutheIdTimeout; - QString login = ui_->lineEditUsername->text().trimmed(); ui_->lineEditUsername->setText(login); - if (bsClient_) { - bsClient_->startLogin(login.toStdString()); - } - else { - emit needStartLogin(login.toStdString()); - } + emit needStartLogin(login.toStdString()); if (ui_->checkBoxRememberUsername->isChecked()) { if (settings_) { diff --git a/BlockSettleUILib/LoginWindow.h b/BlockSettleUILib/LoginWindow.h index 0993002ab..446edfa35 100644 --- a/BlockSettleUILib/LoginWindow.h +++ b/BlockSettleUILib/LoginWindow.h @@ -15,7 +15,6 @@ #include #include #include "ApplicationSettings.h" -#include "AutheIDClient.h" namespace Ui { class LoginWindow; @@ -24,7 +23,6 @@ namespace spdlog { class logger; } -struct BsClientLoginResult; struct NetworkSettings; class ApplicationSettings; @@ -35,10 +33,6 @@ class LoginWindow : public QDialog Q_OBJECT public: - [[deprecated]] LoginWindow(const std::shared_ptr &logger - , const std::shared_ptr &bsClient - , std::shared_ptr &settings - , QWidget* parent = nullptr); LoginWindow(const std::shared_ptr& logger , ApplicationSettings::EnvConfiguration, QWidget* parent = nullptr); ~LoginWindow() override; @@ -49,12 +43,11 @@ Q_OBJECT }; QString email() const; - [[deprecated]] BsClientLoginResult *result() const { return result_.get(); } void setLogin(const QString&); void setRememberLogin(bool); void onLoginStarted(const std::string& login, bool success, const std::string& errMsg); - void onLoggedIn(const BsClientLoginResult&); + //void onLoggedIn(const BsClientLoginResult&); signals: void putSetting(ApplicationSettings::Setting, const QVariant&); @@ -63,7 +56,6 @@ Q_OBJECT private slots: void onStartLoginDone(bool success, const std::string &errorMsg); - void onGetLoginResultDone(const BsClientLoginResult &result); //deprecated void onTextChanged(); void onAuthPressed(); void onTimer(); @@ -85,7 +77,6 @@ private slots: QTimer timer_; float timeLeft_{}; std::shared_ptr bsClient_; - std::unique_ptr result_; }; #endif // __LOGIN_WINDOW_H__ diff --git a/BlockSettleUILib/NotificationCenter.cpp b/BlockSettleUILib/NotificationCenter.cpp index 1af96deba..b9d85985f 100644 --- a/BlockSettleUILib/NotificationCenter.cpp +++ b/BlockSettleUILib/NotificationCenter.cpp @@ -106,10 +106,6 @@ NotificationTabResponder::NotificationTabResponder(const Ui::BSTerminalMainWindo void NotificationTabResponder::respond(bs::ui::NotifyType nt, bs::ui::NotifyMessage msg) { - if (nt == bs::ui::NotifyType::UpdateUnreadMessage) { - return; - } - const auto tabAction = getTabActionFor(nt, msg); if ((tabAction.index >= 0) && (mainWinUi_->tabWidget->currentIndex() != tabAction.index)) { mainWinUi_->tabWidget->setTabIcon(tabAction.index, @@ -120,16 +116,8 @@ void NotificationTabResponder::respond(bs::ui::NotifyType nt, bs::ui::NotifyMess NotificationTabResponder::TabAction NotificationTabResponder::getTabActionFor(bs::ui::NotifyType nt, bs::ui::NotifyMessage msg) const { switch (nt) { - case bs::ui::NotifyType::DealerQuotes: - if (msg.empty()) { - return { -1, false, false }; - } - return { mainWinUi_->tabWidget->indexOf(mainWinUi_->widgetRFQReply), (msg[0].toInt() > 0), - appSettings_ ? !appSettings_->get(ApplicationSettings::DisableBlueDotOnTabOfRfqBlotter) : true}; - case bs::ui::NotifyType::BlockchainTX: return { mainWinUi_->tabWidget->indexOf(mainWinUi_->widgetTransactions), true, true }; - default: break; } return { -1, false, false }; @@ -200,19 +188,6 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif QString title, text, userId; int msecs = 10000; newVersionMessage_ = false; - newChatMessage_ = false; - newChatId_ = QString(); - - const int chatIndex = mainWinUi_->tabWidget->indexOf(mainWinUi_->widgetChat); - const bool isChatTabActive = mainWinUi_->tabWidget->currentIndex() == chatIndex && QApplication::activeWindow(); - auto updateChatIconAndCheckChatTab = [&]() -> bool { - if (isChatTabActive) { - mainWinUi_->tabWidget->setTabIcon(chatIndex, QIcon()); - } else { - mainWinUi_->tabWidget->setTabIcon(chatIndex, QIcon(QLatin1String(":/ICON_DOT"))); - } - return isChatTabActive; - }; switch (nt) { case bs::ui::NotifyType::BlockchainTX: @@ -223,29 +198,6 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif text = msg[1].toString(); break; - case bs::ui::NotifyType::CelerOrder: - if (msg.size() < 2) { - return; - } - if (msg[0].toBool()) { - title = tr("Order Filled"); - text = tr("Trade order %1 has been filled by the matching system").arg(msg[1].toString()); - } - else { - title = tr("Order Failed"); - text = tr("Order %1 was rejected with reason: %2").arg(msg[1].toString()).arg(msg[2].toString()); - icon = QSystemTrayIcon::Warning; - } - break; - - case bs::ui::NotifyType::AuthAddress: - if (msg.size() < 2) { - return; - } - title = tr("Authentication Address %1").arg(msg[1].toString()); - text = msg[0].toString(); - break; - case bs::ui::NotifyType::BroadcastError: if (msg.size() < 2) { return; @@ -265,47 +217,6 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif newVersionMessage_ = true; break; - case bs::ui::NotifyType::UpdateUnreadMessage: { - if (msg.size() != 4) { - return; - } - - const bool forceNotification = msg[3].toBool(); - if (updateChatIconAndCheckChatTab() && !forceNotification) { - return; - } - - title = msg[0].toString(); - text = msg[1].toString(); - userId = msg[2].toString(); - - if (title.isEmpty() || text.isEmpty() || userId.isEmpty()) { - return; - } - - newChatMessage_ = true; - newChatId_ = userId; - break; - } - case bs::ui::NotifyType::FriendRequest: - if (updateChatIconAndCheckChatTab() || msg.size() != 1) { - return; - } - - title = tr("New contact request"); - text = tr("%1 would like to add you as a contact").arg(msg[0].toString()); - break; - - case bs::ui::NotifyType::OTCOrderError: - if (msg.size() != 1) { - return; - } - - title = tr("OTC Failed"); - text = msg[0].toString(); - icon = QSystemTrayIcon::Warning; - break; - case bs::ui::NotifyType::BitcoinCoreOnline: title = tr("Bitcoin Core"); text = tr("Connected to Bitcoin Core"); @@ -339,16 +250,6 @@ void NotificationTrayIconResponder::respond(bs::ui::NotifyType nt, bs::ui::Notif "If you have any questions, don't hesitate to contact our Support team through the Client Portal."); icon = QSystemTrayIcon::Information; break; - case bs::ui::NotifyType::TradingEnabledOnPB: - icon = QSystemTrayIcon::Information; - title = tr("System status"); - text = tr("Order entry has resumed"); - break; - case bs::ui::NotifyType::TradingDisabledOnPB: - icon = QSystemTrayIcon::Information; - title = tr("System status"); - text = tr("Order entry has been temporarily suspended"); - break; default: return; } @@ -398,19 +299,6 @@ void NotificationTrayIconResponder::messageClicked() mb.exec(); #endif } - else if (newChatMessage_) { - if (!newChatId_.isNull() && globalInstance != nullptr) { - const int chatIndex = mainWinUi_->tabWidget->indexOf(mainWinUi_->widgetChat); - mainWinUi_->tabWidget->setTabIcon(chatIndex, QIcon()); - mainWinUi_->tabWidget->setCurrentWidget(mainWinUi_->widgetChat); - auto window = mainWinUi_->tabWidget->window(); - if (window) { - QMetaObject::invokeMethod(window, "raiseWindow", Qt::DirectConnection); - } - auto signal = QMetaMethod::fromSignal(&NotificationCenter::newChatMessageClick); - signal.invoke(globalInstance.get(), Q_ARG(QString, newChatId_)); - } - } } #ifdef BS_USE_DBUS diff --git a/BlockSettleUILib/NotificationCenter.h b/BlockSettleUILib/NotificationCenter.h index 082fcdabb..2a2bf9faf 100644 --- a/BlockSettleUILib/NotificationCenter.h +++ b/BlockSettleUILib/NotificationCenter.h @@ -40,21 +40,13 @@ namespace bs { enum class NotifyType { Unknown, BlockchainTX, - CelerOrder, - DealerQuotes, - AuthAddress, BroadcastError, NewVersion, - UpdateUnreadMessage, - FriendRequest, - OTCOrderError, LogOut, BitcoinCoreOnline, BitcoinCoreOffline, AccountDisabled, - AccountEnabled, - TradingEnabledOnPB, - TradingDisabledOnPB + AccountEnabled }; using NotifyMessage = QList; @@ -155,8 +147,6 @@ private slots: std::shared_ptr trayIcon_; std::shared_ptr appSettings_; bool newVersionMessage_ = false; - bool newChatMessage_ = false; - QString newChatId_; enum NotificationMode { QSystemTray, diff --git a/BlockSettleUILib/OrderListModel.h b/BlockSettleUILib/OrderListModel.h deleted file mode 100644 index b4923fdf4..000000000 --- a/BlockSettleUILib/OrderListModel.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef ORDERLISTMODEL_H -#define ORDERLISTMODEL_H - -#include -#include -#include - -#include "CommonTypes.h" -#include -#include -#include - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - class Response_DeliveryObligationsRequest; - class Response_UpdateOrdersAndObligations; - class Response_UpdateOrder; - } - } -} - -class AssetManager; - -class OrderListModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - struct Header { - enum Index { - Time = 0, - NameColumn = 0, - Product, - Side, - Quantity, - Price, - Value, - Status, - last - }; - static QString toString(Index); - }; - - struct DeliveryObligationData - { - std::string bsAddress; - bs::XBTAmount deliveryAmount; - - bool isValid() const { - return !bsAddress.empty(); - } - }; - - [[deprecated]] OrderListModel(const std::shared_ptr &, QObject *parent = nullptr); - OrderListModel(QObject* parent = nullptr); - ~OrderListModel() noexcept override = default; - - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &index) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - - bool DeliveryRequired(const QModelIndex &index); - - DeliveryObligationData getDeliveryObligationData(const QModelIndex &index) const; - - void onOrdersUpdate(const std::vector &); - void onMatchingLogin(); - void onMatchingLogout(); - -public slots: - void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); - void onDisconnected(); - -signals: - void newOrder(const QPersistentModelIndex &index); - void selectRow(const QPersistentModelIndex &row); - -private: - enum class DataType { - Data = 0, - Group, - Market, - StatusGroup - }; - - struct IndexHelper { - IndexHelper *parent_; - void *data_; - DataType type_; - - IndexHelper(IndexHelper *parent, void *data, DataType type) - : parent_(parent) - , data_(data) - , type_(type) - {} - }; - - struct Data { - QString time_; - QString product_; - QString side_; - QString quantity_; - QString price_; - QString value_; - QString status_; - QString id_; - QColor statusColor_; - IndexHelper idx_; - - Data(const QString &time, const QString &prod, - const QString &side, const QString &quantity, const QString &price, - const QString &value, const QString &status, const QString &id, - IndexHelper *parent) - : time_(time) - , product_(prod) - , side_(side) - , quantity_(quantity) - , price_(price) - , value_(value) - , status_(status) - , id_(id) - , statusColor_(Qt::white) - , idx_(parent, this, DataType::Data) - { - } - }; - - struct Group { - std::deque> rows_; - QString security_; - IndexHelper idx_; - - Group(const QString &sec, IndexHelper *parent) - : security_(sec) - , idx_(parent, this, DataType::Group) - { - } - - virtual ~Group() = default; - - virtual void addOrder(const bs::network::Order& order); - - virtual QVariant getQuantity() const; - virtual QVariant getQuantityColor() const; - - virtual QVariant getValue() const; - virtual QVariant getValueColor() const; - virtual QVariant getPrice() const; - - virtual QVariant getStatus() const; - protected: - void addRow(const bs::network::Order& order); - }; - - struct FuturesGroup : public Group - { - FuturesGroup(const QString &sec, IndexHelper *parent) - : Group(sec, parent) - {} - - ~FuturesGroup() override = default; - - void addOrder(const bs::network::Order& order) override; - - QVariant getQuantity() const override; - QVariant getQuantityColor() const override; - - QVariant getValue() const override; - QVariant getValueColor() const override; - - private: - double quantity_ = 0; - double value_ = 0; - }; - - struct FuturesDeliveryGroup : public Group - { - FuturesDeliveryGroup(const QString &sec, IndexHelper *parent - , int64_t quantity, double price - , const std::string& bsAddress - , const bool delivered); - - ~FuturesDeliveryGroup() override = default; - - QVariant getQuantity() const override; - QVariant getQuantityColor() const override; - - QVariant getValue() const override; - QVariant getValueColor() const override; - - QVariant getPrice() const override; - QVariant getStatus() const override; - - bool deliveryRequired() const; - - DeliveryObligationData getDeliveryObligationData() const; - - private: - double quantity_ = 0; - double value_ = 0; - double price_ = 0; - - bs::XBTAmount toDeliver_; - std::string bsAddress_; - - bool delivered_; - }; - - - struct Market { - std::vector> rows_; - - bs::network::Asset::Type assetType_; - QString name_; - IndexHelper idx_; - QFont font_; - - Market(const bs::network::Asset::Type assetType, IndexHelper *parent) - : assetType_{assetType} - , name_{tr(bs::network::Asset::toString(assetType))} - , idx_(parent, this, DataType::Market) - { - font_.setBold(true); - } - }; - - struct StatusGroup { - std::vector> rows_; - QString name_; - IndexHelper idx_; - int row_; - - enum Type { - first = 0, - PendingSettlements = first, - UnSettled, - Settled, - Undefined - }; - - StatusGroup(const QString &name, int row) - : name_(name) - , idx_(nullptr, this, DataType::StatusGroup) - , row_(row) - {} - - static QString toString(Type); - }; - - static StatusGroup::Type getStatusGroup(const bs::network::Order &); - - int findGroup(Market *market, Group *group) const; - int findMarket(StatusGroup *statusGroup, Market *market) const; - std::pair findItem(const bs::network::Order &order); - void setOrderStatus(Group *group, int index, const bs::network::Order& order, - bool emitUpdate = false); - void removeRowIfContainerChanged(const bs::network::Order &order, int &oldOrderRow, bool force); - void findMarketAndGroup(const bs::network::Order &order, Market *&market, Group *&group); - void createGroupsIfNeeded(const bs::network::Order &order, Market *&market, Group *&group); - - void reset(); - void resetLatestChangedStatus(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations &message); - - void DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation); - [[deprecated]] void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrdersAndObligations&); - [[deprecated]] void processUpdateOrder(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrder&); - void onOrderUpdated(const bs::network::Order&); - - bool isFutureDeliveryIndex(const QModelIndex &index) const; - FuturesDeliveryGroup* GetFuturesDeliveryGroup(const QModelIndex &index) const; - -private: - std::shared_ptr assetManager_; - std::unordered_map groups_; - std::unique_ptr unsettled_; - std::unique_ptr settled_; - std::unique_ptr pendingFuturesSettlement_; - QDateTime latestOrderTimestamp_; - - std::vector> sortedPeviousOrderStatuses_{}; - QDateTime latestChangedTimestamp_; - - bool connected_{}; -}; - -#endif // ORDERLISTMODEL_H diff --git a/BlockSettleUILib/PortfolioWidget.cpp b/BlockSettleUILib/PortfolioWidget.cpp index 5c9e47b0b..18917ce49 100644 --- a/BlockSettleUILib/PortfolioWidget.cpp +++ b/BlockSettleUILib/PortfolioWidget.cpp @@ -70,9 +70,6 @@ PortfolioWidget::PortfolioWidget(QWidget* parent) connect(ui_->treeViewUnconfirmedTransactions, &QTreeView::customContextMenuRequested, this, &PortfolioWidget::showContextMenu); connect(ui_->treeViewUnconfirmedTransactions, &QTreeView::activated, this, &PortfolioWidget::showTransactionDetails); ui_->treeViewUnconfirmedTransactions->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - - connect(ui_->widgetMarketData, &MarketDataWidget::needMdConnection, this, &PortfolioWidget::needMdConnection); - connect(ui_->widgetMarketData, &MarketDataWidget::needMdDisconnect, this, &PortfolioWidget::needMdDisconnect); } PortfolioWidget::~PortfolioWidget() = default; @@ -102,74 +99,6 @@ void PortfolioWidget::init(const std::shared_ptr& logger) void PortfolioWidget::shortcutActivated(ShortcutType s) {} -void PortfolioWidget::setAuthorized(bool authorized) -{ - ui_->widgetMarketData->setAuthorized(authorized); -} - -void PortfolioWidget::onMDConnected() -{ - ui_->widgetMarketData->onMDConnected(); -} - -void PortfolioWidget::onMDDisconnected() -{ - ui_->widgetMarketData->onMDDisconnected(); -} - -void PortfolioWidget::onMDUpdated(bs::network::Asset::Type assetType - , const QString& security, const bs::network::MDFields& fields) -{ - ui_->widgetMarketData->onMDUpdated(assetType, security, fields); - - if ((assetType == bs::network::Asset::Undefined) || security.isEmpty()) { - return; - } - double lastPx = 0; - double bidPrice = 0; - - double productPrice = 0; - CurrencyPair cp(security.toStdString()); - std::string ccy; - - switch (assetType) { - case bs::network::Asset::PrivateMarket: - ccy = cp.NumCurrency(); - break; - case bs::network::Asset::SpotXBT: - ccy = cp.DenomCurrency(); - break; - default: - return; - } - - if (ccy.empty()) { - return; - } - - for (const auto& field : fields) { - if (field.type == bs::network::MDField::PriceLast) { - lastPx = field.value; - break; - } else if (field.type == bs::network::MDField::PriceBid) { - bidPrice = field.value; - } - } - - productPrice = (lastPx > 0) ? lastPx : bidPrice; - - if (productPrice > 0) { - if (ccy == cp.DenomCurrency()) { - productPrice = 1 / productPrice; - } - portfolioModel_->onPriceChanged(ccy, productPrice); - ui_->widgetCCProtfolio->onPriceChanged(ccy, productPrice); - if (ccy == "EUR") { - ui_->widgetCCProtfolio->onBasePriceChanged(ccy, 1/productPrice); - } - } -} - void PortfolioWidget::onHDWallet(const bs::sync::WalletInfo& wi) { portfolioModel_->onHDWallet(wi); @@ -193,9 +122,7 @@ void PortfolioWidget::onBalance(const std::string& currency, double balance) } void PortfolioWidget::onEnvConfig(int value) -{ - ui_->widgetMarketData->onEnvConfig(value); -} +{} void PortfolioWidget::showTransactionDetails(const QModelIndex& index) { diff --git a/BlockSettleUILib/PortfolioWidget.h b/BlockSettleUILib/PortfolioWidget.h index fa49722a2..550cb8e47 100644 --- a/BlockSettleUILib/PortfolioWidget.h +++ b/BlockSettleUILib/PortfolioWidget.h @@ -55,22 +55,13 @@ Q_OBJECT void SetTransactionsModel(const std::shared_ptr& model); void shortcutActivated(ShortcutType s) override; - void setAuthorized(bool authorized); - void onMDConnected(); - void onMDDisconnected(); - void onMDUpdated(bs::network::Asset::Type, const QString& security - , const bs::network::MDFields&); void onHDWallet(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData&); void onWalletBalance(const bs::sync::WalletBalanceData&); void onBalance(const std::string& currency, double balance); void onEnvConfig(int); -signals: - void needMdConnection(ApplicationSettings::EnvConfiguration); - void needMdDisconnect(); - private slots: void showTransactionDetails(const QModelIndex& index); void showContextMenu(const QPoint& point); diff --git a/BlockSettleUILib/PortfolioWidget.ui b/BlockSettleUILib/PortfolioWidget.ui index 36c9c02f5..67fdbe9ac 100644 --- a/BlockSettleUILib/PortfolioWidget.ui +++ b/BlockSettleUILib/PortfolioWidget.ui @@ -69,22 +69,6 @@ 0 - - - - - 0 - 0 - - - - - 0 - 0 - - - - @@ -270,12 +254,6 @@ - - MarketDataWidget - QWidget -
MarketDataWidget.h
- 1 -
CCWidget QWidget diff --git a/BlockSettleUILib/Settings/ConfigDialog.cpp b/BlockSettleUILib/Settings/ConfigDialog.cpp index 01b546281..56377d1bb 100644 --- a/BlockSettleUILib/Settings/ConfigDialog.cpp +++ b/BlockSettleUILib/Settings/ConfigDialog.cpp @@ -18,7 +18,6 @@ #include "WalletSignerContainer.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" -#include "autheid_utils.h" #include "ui_ConfigDialog.h" @@ -208,38 +207,14 @@ void ConfigDialog::encryptData(const std::shared_ptr & , const SecureBinaryData &data , const ConfigDialog::EncryptCb &cb) { - getChatPrivKey(walletsMgr, signContainer, [data, cb](EncryptError error, const SecureBinaryData &privKey) { - if (error != EncryptError::NoError) { - cb(error, {}); - return; - } - auto pubKey = autheid::getPublicKey(autheid::PrivateKey(privKey.getPtr(), privKey.getPtr() + privKey.getSize())); - auto encrypted = autheid::encryptData(data.getPtr(), data.getSize(), pubKey); - if (encrypted.empty()) { - cb(EncryptError::EncryptError, {}); - return; - } - cb(EncryptError::NoError, SecureBinaryData(encrypted.data(), encrypted.size())); - }); + cb(EncryptError::NoEncryptionKey, {}); } void ConfigDialog::decryptData(const std::shared_ptr &walletsMgr , const std::shared_ptr &signContainer , const SecureBinaryData &data, const ConfigDialog::EncryptCb &cb) { - getChatPrivKey(walletsMgr, signContainer, [data, cb](EncryptError error, const SecureBinaryData &privKey) { - if (error != EncryptError::NoError) { - cb(error, {}); - return; - } - auto privKeyCopy = autheid::PrivateKey(privKey.getPtr(), privKey.getPtr() + privKey.getSize()); - auto decrypted = autheid::decryptData(data.getPtr(), data.getSize(), privKeyCopy); - if (decrypted.empty()) { - cb(EncryptError::EncryptError, {}); - return; - } - cb(EncryptError::NoError, SecureBinaryData(decrypted.data(), decrypted.size())); - }); + cb(EncryptError::NoEncryptionKey, {}); } void ConfigDialog::getChatPrivKey(const std::shared_ptr &walletsMgr diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 825b62bdf..6aef81a5e 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -17,101 +17,6 @@ #include -StatusBarView::StatusBarView(const std::shared_ptr &armory - , const std::shared_ptr &walletsManager - , std::shared_ptr assetManager - , const std::shared_ptr &celerClient - , const std::shared_ptr &container, QStatusBar *parent) - : QObject(nullptr) - , statusBar_(parent) - , iconSize_(16, 16) - , armoryConnState_(ArmoryState::Offline) - , walletsManager_(walletsManager) - , assetManager_(assetManager) -{ - init(armory.get()); - - for (int s : {16, 24, 32}) - { - iconCeler_.addFile(QString(QLatin1String(":/ICON_BS_%1")).arg(s), QSize(s, s), QIcon::Normal); - iconCeler_.addFile(QString(QLatin1String(":/ICON_BS_%1_GRAY")).arg(s), QSize(s, s), QIcon::Disabled); - } - estimateLabel_ = new QLabel(statusBar_); - - iconCelerOffline_ = iconCeler_.pixmap(iconSize_, QIcon::Disabled); - iconCelerConnecting_ = iconCeler_.pixmap(iconSize_, QIcon::Disabled); - iconCelerOnline_ = iconCeler_.pixmap(iconSize_, QIcon::Normal); - iconCelerError_ = iconCeler_.pixmap(iconSize_, QIcon::Disabled); - - QIcon contIconGray(QLatin1String(":/ICON_STATUS_OFFLINE")); - QIcon contIconYellow(QLatin1String(":/ICON_STATUS_CONNECTING")); - QIcon contIconRed(QLatin1String(":/ICON_STATUS_ERROR")); - QIcon contIconGreen(QLatin1String(":/ICON_STATUS_ONLINE")); - - iconContainerOffline_ = contIconGray.pixmap(iconSize_); - iconContainerError_ = contIconRed.pixmap(iconSize_); - iconContainerOnline_ = contIconGreen.pixmap(iconSize_); - - balanceLabel_ = new QLabel(statusBar_); - - progressBar_ = new CircleProgressBar(statusBar_); - progressBar_->setMinimum(0); - progressBar_->setMaximum(100); - progressBar_->hide(); - - celerConnectionIconLabel_ = new QLabel(statusBar_); - connectionStatusLabel_ = new QLabel(statusBar_); - - containerStatusLabel_ = new QLabel(statusBar_); - containerStatusLabel_->setPixmap(iconContainerOffline_); - containerStatusLabel_->setToolTip(tr("Signing container status")); - - statusBar_->addWidget(estimateLabel_); - statusBar_->addWidget(balanceLabel_); - - statusBar_->addPermanentWidget(celerConnectionIconLabel_); - statusBar_->addPermanentWidget(CreateSeparator()); - statusBar_->addPermanentWidget(progressBar_); - statusBar_->addPermanentWidget(connectionStatusLabel_); - statusBar_->addPermanentWidget(CreateSeparator()); - statusBar_->addPermanentWidget(containerStatusLabel_); - - QWidget* w = new QWidget(); - w->setFixedWidth(6); - statusBar_->addPermanentWidget(w); - - statusBar_->setStyleSheet(QLatin1String("QStatusBar::item { border: none; }")); - - SetLoggedOutStatus(); - - connect(assetManager_.get(), &AssetManager::totalChanged, this, &StatusBarView::updateBalances); - connect(assetManager_.get(), &AssetManager::securitiesChanged, this, &StatusBarView::updateBalances); - - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportStarted, this, &StatusBarView::onWalletImportStarted); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletImportFinished, this, &StatusBarView::onWalletImportFinished); - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &StatusBarView::updateBalances); - - connect(celerClient.get(), &CelerClientQt::OnConnectedToServer, this, &StatusBarView::onConnectedToMatching); - connect(celerClient.get(), &CelerClientQt::OnConnectionClosed, this, &StatusBarView::onDisconnectedFromMatching); - connect(celerClient.get(), &CelerClientQt::OnConnectionError, this, &StatusBarView::onMatchingConnError); - - // container might be null if user rejects remote signer key -/* if (container) { - // connected are not used here because we wait for authenticated signal instead - // disconnected are not used here because onContainerError should be always called - connect(container.get(), &SignContainer::authenticated, this, &StatusBarView::onContainerAuthorized); - connect(container.get(), &SignContainer::connectionError, this, &StatusBarView::onSignerStatusChanged); - }*/ - - onArmoryStateChanged(armory_->state(), armory_->topBlock()); - onDisconnectedFromMatching(); - setBalances(); - - containerStatusLabel_->setPixmap(iconContainerOffline_); - - connectionStatusLabel_->installEventFilter(this); -} - StatusBarView::StatusBarView(QStatusBar *parent) : QObject(nullptr) , statusBar_(parent) @@ -544,24 +449,6 @@ void StatusBarView::onDisconnectedFromMatching() SetLoggedOutStatus(); } -void StatusBarView::onMatchingConnError(int errorCode) -{ - switch(errorCode) - { - case BaseCelerClient::ResolveHostError: - statusBar_->showMessage(tr("Could not resolve Celer host")); - break; - case BaseCelerClient::LoginError: - statusBar_->showMessage(tr("Invalid login/password pair"), 2000); - break; - case BaseCelerClient::ServerMaintainanceError: - statusBar_->showMessage(tr("Server maintainance")); - break; - case BaseCelerClient::UndefinedError: - break; - } -} - void StatusBarView::onContainerAuthorized() { containerStatusLabel_->setPixmap(iconContainerOnline_); diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index 202943f25..015d75899 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -19,7 +19,6 @@ #include #include "ArmoryConnection.h" -#include "Celer/CelerClient.h" #include "CircleProgressBar.h" #include "SignContainer.h" @@ -35,10 +34,6 @@ class StatusBarView : public QObject, public ArmoryCallbackTarget { Q_OBJECT public: - [[deprecated]] StatusBarView(const std::shared_ptr & - , const std::shared_ptr & - , std::shared_ptr assetManager, const std::shared_ptr & - , const std::shared_ptr &, QStatusBar *parent); StatusBarView(QStatusBar *parent); ~StatusBarView() noexcept override; @@ -55,7 +50,6 @@ public slots: void onArmoryError(QString); void onConnectedToMatching(); void onDisconnectedFromMatching(); - void onMatchingConnError(int errorCode); void onContainerAuthorized(); void onSignerStatusChanged(SignContainer::ConnectionError error, const QString &details); void updateBalances(); //deprecated diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp b/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp deleted file mode 100644 index 543aae1ba..000000000 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "AutoSignQuoteProvider.h" - -#include "HeadlessContainer.h" -#include "WalletManager.h" -#include "Wallets/SyncWalletsManager.h" -#include "Wallets/SyncHDWallet.h" -#include "UserScriptRunner.h" - -#include -#include -#include -#include - - -AutoSignScriptProvider::AutoSignScriptProvider(const std::shared_ptr &logger - , UserScriptRunner *scriptRunner - , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &celerClient - , QObject *parent) - : QObject(parent), logger_(logger), scriptRunner_(scriptRunner) - , appSettings_(appSettings) - , signingContainer_(container) - , celerClient_(celerClient) -{ - scriptRunner_->setParent(this); - - if (walletsManager_) { - scriptRunner_->setWalletsManager(walletsManager_); - } - - connect(scriptRunner_, &UserScriptRunner::scriptLoaded, this, &AutoSignScriptProvider::onScriptLoaded); - connect(scriptRunner_, &UserScriptRunner::failedToLoad, this, &AutoSignScriptProvider::onScriptFailed); - - onSignerStateUpdated(); - - connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this, &AutoSignScriptProvider::onConnectedToCeler); - connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this, &AutoSignScriptProvider::onDisconnectedFromCeler); -} - -void AutoSignScriptProvider::onSignerStateUpdated() -{ - disableAutoSign(); - scriptRunner_->disable(); - - emit autoSignQuoteAvailabilityChanged(); -} - -void AutoSignScriptProvider::disableAutoSign() -{ - if (!walletsManager_) { - return; - } - - const auto wallet = walletsManager_->getPrimaryWallet(); - if (!wallet) { - logger_->error("Failed to obtain auto-sign primary wallet"); - return; - } - - QVariantMap data; - data[QLatin1String("rootId")] = QString::fromStdString(wallet->walletId()); - data[QLatin1String("enable")] = false; - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ActivateAutoSign, data); -} - -void AutoSignScriptProvider::tryEnableAutoSign() -{ - if (!walletsManager_ || !signingContainer_) { - return; - } - - const auto wallet = walletsManager_->getPrimaryWallet(); - if (!wallet) { - logger_->error("Failed to obtain auto-sign primary wallet"); - return; - } - - QVariantMap data; - data[QLatin1String("rootId")] = QString::fromStdString(wallet->walletId()); - data[QLatin1String("enable")] = true; - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ActivateAutoSign, data); -} - -bool AutoSignScriptProvider::isReady() const -{ - logger_->debug("[{}] signCont: {}, walletsMgr: {}, celer: {}", __func__ - , signingContainer_ && !signingContainer_->isOffline() - , walletsManager_ && walletsManager_->isReadyForTrading(), celerClient_->IsConnected()); - return signingContainer_ && !signingContainer_->isOffline() - && walletsManager_ && walletsManager_->isReadyForTrading() - && celerClient_->IsConnected(); -} - -void AutoSignScriptProvider::onAutoSignStateChanged(bs::error::ErrorCode result - , const std::string &walletId) -{ - autoSignState_ = result; - autoSignWalletId_ = QString::fromStdString(walletId); - emit autoSignStateChanged(); -} - -void AutoSignScriptProvider::setScriptLoaded(bool loaded) -{ - scriptLoaded_ = loaded; - if (!loaded) { - scriptRunner_->disable(); - } - emit scriptLoadedChanged(); -} - -void AutoSignScriptProvider::init(const QString &filename) -{ - if (filename.isEmpty()) { - return; - } - scriptLoaded_ = false; - scriptRunner_->enable(filename); - emit scriptLoadedChanged(); -} - -void AutoSignScriptProvider::deinit() -{ - scriptRunner_->disable(); - scriptLoaded_ = false; - emit scriptLoadedChanged(); -} - -void AutoSignScriptProvider::onScriptLoaded(const QString &filename) -{ - logger_->info("[AutoSignScriptProvider::onScriptLoaded] script {} loaded" - , filename.toStdString()); - scriptLoaded_ = true; - emit scriptLoadedChanged(); - - auto scripts = appSettings_->get(ApplicationSettings::aqScripts); - if (scripts.indexOf(filename) < 0) { - scripts << filename; - appSettings_->set(ApplicationSettings::aqScripts, scripts); - } - appSettings_->set(lastScript_, filename); - emit scriptHistoryChanged(); -} - -void AutoSignScriptProvider::onScriptFailed(const QString &filename, const QString &error) -{ - logger_->error("[AutoSignScriptProvider::onScriptLoaded] script {} loading failed: {}" - , filename.toStdString(), error.toStdString()); - setScriptLoaded(false); - - auto scripts = appSettings_->get(ApplicationSettings::aqScripts); - scripts.removeOne(filename); - appSettings_->set(ApplicationSettings::aqScripts, scripts); - appSettings_->reset(lastScript_); - emit scriptHistoryChanged(); -} - -void AutoSignScriptProvider::onConnectedToCeler() -{ - emit autoSignQuoteAvailabilityChanged(); -} - -void AutoSignScriptProvider::onDisconnectedFromCeler() -{ - scriptRunner_->disable(); - disableAutoSign(); - - emit autoSignQuoteAvailabilityChanged(); -} - -void AutoSignScriptProvider::setWalletsManager(std::shared_ptr &walletsMgr) -{ - walletsManager_ = walletsMgr; - scriptRunner_->setWalletsManager(walletsMgr); - - connect(walletsMgr.get(), &bs::sync::WalletsManager::walletDeleted, this - , &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged); - connect(walletsMgr.get(), &bs::sync::WalletsManager::walletAdded, this - , &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged); - connect(walletsMgr.get(), &bs::sync::WalletsManager::walletsReady, this - , &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged); - connect(walletsMgr.get(), &bs::sync::WalletsManager::walletsSynchronized, this - , &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged); - connect(walletsMgr.get(), &bs::sync::WalletsManager::newWalletAdded, this - , &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged); - connect(walletsMgr.get(), &bs::sync::WalletsManager::walletImportFinished, this - , &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged); - - emit autoSignQuoteAvailabilityChanged(); -} - -QString AutoSignScriptProvider::getAutoSignWalletName() -{ - if (!walletsManager_ || !signingContainer_) { - return QString(); - } - - const auto wallet = walletsManager_->getPrimaryWallet(); - if (!wallet) { - return QString(); - } - return QString::fromStdString(wallet->name()); -} - -QString AutoSignScriptProvider::getDefaultScriptsDir() -{ -#if defined(Q_OS_WIN) || defined(Q_OS_MACOS) - return QCoreApplication::applicationDirPath() + QStringLiteral("/scripts"); -#else - return QStringLiteral("/usr/share/blocksettle/scripts"); -#endif -} - -QStringList AutoSignScriptProvider::getScripts() -{ - return appSettings_->get(ApplicationSettings::aqScripts); -} - -QString AutoSignScriptProvider::getLastScript() -{ - return appSettings_->get(lastScript_); -} - -QString AutoSignScriptProvider::getLastDir() -{ - return appSettings_->get(ApplicationSettings::LastAqDir); -} - -void AutoSignScriptProvider::setLastDir(const QString &path) -{ - appSettings_->set(ApplicationSettings::LastAqDir, QFileInfo(path).dir().absolutePath()); -} - - -AutoSignAQProvider::AutoSignAQProvider(const std::shared_ptr &logger - , UserScriptRunner *scriptRunner - , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &celerClient - , QObject *parent) - : AutoSignScriptProvider(logger, scriptRunner, appSettings, container, celerClient, parent) -{ - auto botFileInfo = QFileInfo(getDefaultScriptsDir() + QStringLiteral("/RFQBot.qml")); - if (botFileInfo.exists() && botFileInfo.isFile()) { - auto list = appSettings_->get(ApplicationSettings::aqScripts); - if (list.indexOf(botFileInfo.absoluteFilePath()) == -1) { - list << botFileInfo.absoluteFilePath(); - } - appSettings_->set(ApplicationSettings::aqScripts, list); - const auto lastScript = appSettings_->get(ApplicationSettings::lastAqScript); - if (lastScript.isEmpty()) { - appSettings_->set(ApplicationSettings::lastAqScript, botFileInfo.absoluteFilePath()); - } - } - lastScript_ = ApplicationSettings::lastAqScript; -} - - -AutoSignRFQProvider::AutoSignRFQProvider(const std::shared_ptr &logger - , UserScriptRunner *scriptRunner - , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &celerClient - , QObject *parent) - : AutoSignScriptProvider(logger, scriptRunner, appSettings, container, celerClient, parent) -{ - auto botFileInfo = QFileInfo(getDefaultScriptsDir() + QStringLiteral("/AutoRFQ.qml")); - if (botFileInfo.exists() && botFileInfo.isFile()) { - auto list = appSettings_->get(ApplicationSettings::aqScripts); - if (list.indexOf(botFileInfo.absoluteFilePath()) == -1) { - list << botFileInfo.absoluteFilePath(); - } - appSettings_->set(ApplicationSettings::aqScripts, list); - const auto lastScript = appSettings_->get(ApplicationSettings::CurrentRFQScript); - if (lastScript.isEmpty()) { - appSettings_->set(ApplicationSettings::CurrentRFQScript, botFileInfo.absoluteFilePath()); - } - } - lastScript_ = ApplicationSettings::CurrentRFQScript; -} diff --git a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h b/BlockSettleUILib/Trading/AutoSignQuoteProvider.h deleted file mode 100644 index 28b43247b..000000000 --- a/BlockSettleUILib/Trading/AutoSignQuoteProvider.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef AUTOSIGNQUOTEPROVIDER_H -#define AUTOSIGNQUOTEPROVIDER_H - -#include "BSErrorCode.h" - -#include -#include -#include "ApplicationSettings.h" - -class CelerClientQt; -class SignContainer; -class UserScriptRunner; -class AssetManager; -class QuoteProvider; -class MDCallbacksQt; - -namespace bs { - namespace sync { - class Wallet; - class WalletsManager; - } -} -namespace spdlog { - class logger; -} - - -class AutoSignScriptProvider : public QObject -{ - Q_OBJECT -public: - [[deprecated]] explicit AutoSignScriptProvider(const std::shared_ptr & - , UserScriptRunner * - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , QObject *parent = nullptr); - - bool isScriptLoaded() const { return scriptLoaded_; } - void setScriptLoaded(bool loaded); - - void init(const QString &filename); - void deinit(); - - static QString getDefaultScriptsDir(); - - QStringList getScripts(); - QString getLastScript(); - - QString getLastDir(); - void setLastDir(const QString &path); - - // auto sign - bs::error::ErrorCode autoSignState() const { return autoSignState_; } - QString autoSignWalletId() const { return autoSignWalletId_; } - - void disableAutoSign(); - void tryEnableAutoSign(); - - bool isReady() const; - void setWalletsManager(std::shared_ptr &); - - QString getAutoSignWalletName(); - - UserScriptRunner *scriptRunner() const { return scriptRunner_; } - -public slots: - void onSignerStateUpdated(); - void onAutoSignStateChanged(bs::error::ErrorCode result, const std::string &walletId); - - void onScriptLoaded(const QString &filename); - void onScriptFailed(const QString &filename, const QString &error); - - void onConnectedToCeler(); - void onDisconnectedFromCeler(); - -signals: - void scriptHistoryChanged(); - void autoSignStateChanged(); - void autoSignQuoteAvailabilityChanged(); - void scriptLoadedChanged(); - -protected: - std::shared_ptr appSettings_; - ApplicationSettings::Setting lastScript_{ ApplicationSettings::_last }; - std::shared_ptr logger_; - std::shared_ptr signingContainer_; - std::shared_ptr walletsManager_; - std::shared_ptr celerClient_; - UserScriptRunner *scriptRunner_{}; - - bs::error::ErrorCode autoSignState_{ bs::error::ErrorCode::AutoSignDisabled }; - QString autoSignWalletId_; - bool scriptLoaded_{ false }; - bool celerConnected_{ false }; - bool newLoaded_{ false }; -}; - -class AutoSignAQProvider : public AutoSignScriptProvider -{ - Q_OBJECT -public: - explicit AutoSignAQProvider(const std::shared_ptr & - , UserScriptRunner * - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , QObject *parent = nullptr); -}; - -class AutoSignRFQProvider : public AutoSignScriptProvider -{ - Q_OBJECT -public: - explicit AutoSignRFQProvider(const std::shared_ptr & - , UserScriptRunner * - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , QObject *parent = nullptr); -}; - -#endif // AUTOSIGNQUOTEPROVIDER_H diff --git a/BlockSettleUILib/Trading/AutoSignQuoteWidget.cpp b/BlockSettleUILib/Trading/AutoSignQuoteWidget.cpp deleted file mode 100644 index ef8b4b593..000000000 --- a/BlockSettleUILib/Trading/AutoSignQuoteWidget.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "AutoSignQuoteWidget.h" -#include "ui_AutoSignQuoteWidget.h" - -#include "AutoSignQuoteProvider.h" -#include "BSErrorCodeStrings.h" -#include "BSMessageBox.h" - -#include -#include - -namespace { - constexpr int kSelectAQFileItemIndex = 1; -} - -AutoSignQuoteWidget::AutoSignQuoteWidget(QWidget *parent) : - QWidget(parent), - ui_(new Ui::AutoSignQuoteWidget) -{ - ui_->setupUi(this); - - connect(ui_->checkBoxAQ, &ToggleSwitch::clicked, this, &AutoSignQuoteWidget::onAutoQuoteToggled); - connect(ui_->comboBoxAQScript, SIGNAL(activated(int)), this, SLOT(scriptChanged(int))); - connect(ui_->checkBoxAutoSign, &ToggleSwitch::clicked, this, &AutoSignQuoteWidget::onAutoSignToggled); - - ui_->comboBoxAQScript->setFirstItemHidden(true); -} - -void AutoSignQuoteWidget::init(const std::shared_ptr &autoSignProvider) -{ - autoSignProvider_ = autoSignProvider; - fillScriptHistory(); - onAutoSignReady(); - - ui_->checkBoxAutoSign->setChecked(autoSignProvider_->autoSignState() == bs::error::ErrorCode::NoError); - - connect(autoSignProvider_.get(), &AutoSignScriptProvider::autoSignQuoteAvailabilityChanged - , this, &AutoSignQuoteWidget::onAutoSignReady); - connect(autoSignProvider_.get(), &AutoSignScriptProvider::autoSignStateChanged - , this, &AutoSignQuoteWidget::onAutoSignStateChanged); - connect(autoSignProvider_.get(), &AutoSignScriptProvider::scriptHistoryChanged - , this, &AutoSignQuoteWidget::fillScriptHistory); - connect(autoSignProvider_.get(), &AutoSignScriptProvider::scriptLoadedChanged - , this, &AutoSignQuoteWidget::validateGUI); - - ui_->labelAutoSignWalletName->setText(autoSignProvider_->getAutoSignWalletName()); -} - -AutoSignQuoteWidget::~AutoSignQuoteWidget() = default; - -void AutoSignQuoteWidget::onAutoQuoteToggled() -{ - bool isValidScript = (ui_->comboBoxAQScript->currentIndex() > kSelectAQFileItemIndex); - if (ui_->checkBoxAQ->isChecked() && !isValidScript) { - BSMessageBox question(BSMessageBox::question - , tr("Try to enable scripting") - , tr("Script is not specified. Do you want to select a script from file?")); - const bool answerYes = (question.exec() == QDialog::Accepted); - if (answerYes) { - const auto scriptFileName = askForScript(); - if (!scriptFileName.isEmpty()) { - autoSignProvider_->init(scriptFileName); - } - } - } - - if (autoSignProvider_->isScriptLoaded()) { - autoSignProvider_->setScriptLoaded(false); - } else { - autoSignProvider_->init(ui_->comboBoxAQScript->currentData().toString()); - } - - validateGUI(); -} - -void AutoSignQuoteWidget::onAutoSignStateChanged() -{ - ui_->checkBoxAutoSign->setChecked(autoSignProvider_->autoSignState() == bs::error::ErrorCode::NoError); - if (autoSignProvider_->autoSignState() != bs::error::ErrorCode::NoError - && autoSignProvider_->autoSignState() != bs::error::ErrorCode::AutoSignDisabled) { - BSMessageBox(BSMessageBox::warning, tr("Auto Signing") - , tr("Failed to enable Auto Signing") - , bs::error::ErrorCodeToString(autoSignProvider_->autoSignState())).exec(); - } - - ui_->labelAutoSignWalletName->setText(autoSignProvider_->getAutoSignWalletName()); -} - -void AutoSignQuoteWidget::onAutoSignReady() -{ - ui_->labelAutoSignWalletName->setText(autoSignProvider_->getAutoSignWalletName()); - const bool enableWidget = autoSignProvider_->isReady(); - ui_->groupBoxAutoSign->setEnabled(enableWidget); - ui_->checkBoxAutoSign->setEnabled(enableWidget); - ui_->checkBoxAQ->setEnabled(enableWidget); - validateGUI(); -} - -void AutoSignQuoteWidget::onUserConnected(bool autoSigning, bool autoQuoting) -{ - if (autoSigning) { - ui_->checkBoxAutoSign->setChecked(true); - onAutoSignToggled(); - } - if (autoQuoting) { - ui_->checkBoxAQ->setChecked(true); - onAutoQuoteToggled(); - } -} - -void AutoSignQuoteWidget::fillScriptHistory() -{ - ui_->comboBoxAQScript->clear(); - int curIndex = 0; - ui_->comboBoxAQScript->addItem(tr("Select script...")); - ui_->comboBoxAQScript->addItem(tr("Load new script")); - const auto scripts = autoSignProvider_->getScripts(); - if (!scripts.isEmpty()) { - const auto lastScript = autoSignProvider_->getLastScript(); - for (int i = 0; i < scripts.size(); i++) { - QFileInfo fi(scripts[i]); - ui_->comboBoxAQScript->addItem(fi.fileName(), scripts[i]); - if (scripts[i] == lastScript) { - curIndex = i + kSelectAQFileItemIndex + 1; // note the "Load" row in the head - } - } - } - ui_->comboBoxAQScript->setCurrentIndex(curIndex); -} - -void AutoSignQuoteWidget::scriptChanged(int curIndex) -{ - if (curIndex < kSelectAQFileItemIndex) { - return; - } - - if (curIndex == kSelectAQFileItemIndex) { - const auto scriptFileName = askForScript(); - - if (scriptFileName.isEmpty()) { - fillScriptHistory(); - return; - } - - autoSignProvider_->init(scriptFileName); - } else { - if (autoSignProvider_->isScriptLoaded()) { - autoSignProvider_->deinit(); - } - } -} - -void AutoSignQuoteWidget::onAutoSignToggled() -{ - if (ui_->checkBoxAutoSign->isChecked()) { - autoSignProvider_->tryEnableAutoSign(); - } else { - autoSignProvider_->disableAutoSign(); - } - ui_->checkBoxAutoSign->setChecked(autoSignProvider_->autoSignState() == bs::error::ErrorCode::NoError); -} - -void AutoSignQuoteWidget::validateGUI() -{ - ui_->checkBoxAQ->setChecked(autoSignProvider_->isScriptLoaded()); -} - -QString AutoSignQuoteWidget::askForScript() -{ - auto lastDir = autoSignProvider_->getLastDir(); - if (lastDir.isEmpty()) { - lastDir = AutoSignScriptProvider::getDefaultScriptsDir(); - } - - auto path = QFileDialog::getOpenFileName(this, tr("Open script file") - , lastDir, tr("QML files (*.qml)")); - - if (!path.isEmpty()) { - autoSignProvider_->setLastDir(path); - } - - return path; -} diff --git a/BlockSettleUILib/Trading/AutoSignQuoteWidget.h b/BlockSettleUILib/Trading/AutoSignQuoteWidget.h deleted file mode 100644 index 28be5bfd7..000000000 --- a/BlockSettleUILib/Trading/AutoSignQuoteWidget.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef AUTOSIGNQUOTEWIDGET_H -#define AUTOSIGNQUOTEWIDGET_H - -#include "BSErrorCode.h" - -#include -#include - -class AutoSignScriptProvider; - -namespace Ui { -class AutoSignQuoteWidget; -} - -class AutoSignQuoteWidget : public QWidget -{ - Q_OBJECT - -public: - explicit AutoSignQuoteWidget(QWidget *parent = nullptr); - ~AutoSignQuoteWidget(); - - void init(const std::shared_ptr &autoSignQuoteProvider); - -public slots: - void onAutoSignStateChanged(); - void onAutoSignReady(); - void onUserConnected(bool, bool); - -private slots: - void fillScriptHistory(); - void scriptChanged(int curIndex); - - void onAutoQuoteToggled(); - void onAutoSignToggled(); - -private: - QString askForScript(); - void validateGUI(); - -private: - std::unique_ptr ui_; - std::shared_ptr autoSignProvider_; -}; - -#endif // AUTOSIGNQUOTEWIDGET_H diff --git a/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui b/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui deleted file mode 100644 index a1b53cbe2..000000000 --- a/BlockSettleUILib/Trading/AutoSignQuoteWidget.ui +++ /dev/null @@ -1,398 +0,0 @@ - - - - AutoSignQuoteWidget - - - - 0 - 0 - 344 - 125 - - - - - 0 - 0 - - - - Form - - - - 2 - - - QLayout::SetMinimumSize - - - 6 - - - 2 - - - 6 - - - 2 - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 20 - 0 - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 3 - - - QLayout::SetMinimumSize - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - AUTOMATED DEALING - - - true - - - - 3 - - - 2 - - - 2 - - - - - - 0 - 16 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Auto Signing - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - <> - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 0 - 18 - - - - - 35 - 16777215 - - - - 50 - - - 1 - - - Qt::Horizontal - - - - - - - - - - Qt::Vertical - - - - 20 - 10 - - - - - - - - Auto Quoting - - - - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 170 - 0 - - - - - 170 - 16777215 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 0 - 20 - - - - - - - - true - - - - 0 - 0 - - - - - 0 - 18 - - - - - 35 - 16777215 - - - - 50 - - - 1 - - - Qt::Horizontal - - - - - - - - - - - - - - - - - ToggleSwitch - QSlider -
CustomControls/ToggleSwitch.h
-
- - CustomComboBox - QComboBox -
CustomControls/CustomComboBox.h
-
-
- - -
diff --git a/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp deleted file mode 100644 index 83bfb4e51..000000000 --- a/BlockSettleUILib/Trading/DealerCCSettlementContainer.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "DealerCCSettlementContainer.h" - -#include - -#include "BSErrorCodeStrings.h" -#include "CheckRecipSigner.h" -#include "SignContainer.h" -#include "SignerDefs.h" -#include "UiUtils.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWallet.h" -#include "Wallets/SyncWalletsManager.h" - -#include -#include - -using namespace bs::sync; -using namespace ArmorySigner; - -DealerCCSettlementContainer::DealerCCSettlementContainer(const std::shared_ptr &logger - , const bs::network::Order &order - , const std::string "eReqId, uint64_t lotSize - , const bs::Address &genAddr - , const std::string &ownRecvAddr - , const std::shared_ptr &xbtWallet - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &walletsMgr - , bs::hd::Purpose walletPurpose - , bs::UtxoReservationToken utxoRes - , bool expandTxDialogInfo) - : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) - , logger_(logger) - , order_(order) - , lotSize_(lotSize) - , genesisAddr_(genAddr) - , delivery_(order.side == bs::network::Side::Sell) - , xbtWallet_(xbtWallet) - , signingContainer_(container) - , walletsMgr_(walletsMgr) - , ownRecvAddr_(bs::Address::fromAddressString(ownRecvAddr)) - , orderId_(QString::fromStdString(order.clOrderId)) - , signer_(armory) -{ - if (lotSize == 0) { - throw std::logic_error("invalid lotSize"); - } - - ccWallet_ = walletsMgr->getCCWallet(order.product); - if (!ccWallet_) { - throw std::logic_error("can't find CC wallet"); - } - - if (!txReqData_.ParseFromString(BinaryData::CreateFromHex(order.reqTransaction).toBinStr())) { - throw std::invalid_argument("invalid requester transaction"); - } - - init(armory.get()); - settlWallet_ = armory->instantiateWallet(order_.clOrderId); - if (!settlWallet_) { - throw std::runtime_error("can't register settlement wallet in armory"); - } - - connect(this, &DealerCCSettlementContainer::genAddressVerified, this - , &DealerCCSettlementContainer::onGenAddressVerified, Qt::QueuedConnection); -} - -DealerCCSettlementContainer::~DealerCCSettlementContainer() -{ - settlWallet_->unregister(); - cleanup(); -} - -bs::sync::PasswordDialogData DealerCCSettlementContainer::toPasswordDialogData(QDateTime timestamp) const -{ - bs::sync::PasswordDialogData dialogData = SettlementContainer::toPasswordDialogData(timestamp); - dialogData.setValue(PasswordDialogData::IsDealer, true); - dialogData.setValue(PasswordDialogData::Market, "CC"); - dialogData.setValue(PasswordDialogData::AutoSignCategory, static_cast(bs::signer::AutoSignCategory::SettlementDealer)); - dialogData.setValue(PasswordDialogData::LotSize, static_cast(lotSize_)); - - if (side() == bs::network::Side::Sell) { - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Delivery")); - } - else { - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Payment")); - } - - // rfq details - dialogData.setValue(PasswordDialogData::Price, UiUtils::displayPriceCC(price())); - dialogData.setValue(PasswordDialogData::Quantity, order_.quantity); - - // tx details - if (side() == bs::network::Side::Buy) { - dialogData.setValue(PasswordDialogData::TxInputProduct, UiUtils::XbtCurrency); - } - else { - dialogData.setValue(PasswordDialogData::TxInputProduct, product()); - } - - // settlement details - dialogData.setValue(PasswordDialogData::DeliveryUTXOVerified, genAddrVerified_); - dialogData.setValue(PasswordDialogData::SigningAllowed, genAddrVerified_); - - dialogData.setValue(PasswordDialogData::RecipientsListVisible, true); - dialogData.setValue(PasswordDialogData::InputsListVisible, true); - - return dialogData; -} - -void DealerCCSettlementContainer::onZCReceived(const std::string & - , const std::vector &entries) -{ - if (expectedTxId_.empty()) { - return; - } - for (const auto &entry : entries) { - if (entry.txHash == expectedTxId_) { - emit completed(id()); - break; - } - } -} - -bool DealerCCSettlementContainer::startSigning(QDateTime timestamp) -{ - if (!ccWallet_ || !xbtWallet_) { - logger_->error("[DealerCCSettlementContainer::accept] failed to validate counterparty's TX - aborting"); - sendFailed(); - return false; - } - - const auto &cbTx = [this, handle = validityFlag_.handle(), logger = logger_] - (bs::error::ErrorCode result, const BinaryData &signedTX) - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "failed to sign TX half, already destroyed"); - return; - } - - signId_ = 0; - - if (result == bs::error::ErrorCode::NoError) { - emit signTxRequest(orderId_, signedTX.toHexStr()); - try { - Codec_SignerState::SignerState state; - if (!state.ParseFromString(signedTX.toBinStr())) { - throw std::runtime_error("invalid signed state"); - } - Signer signer(state); - expectedTxId_ = signer.getTxId(); - } - catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger, "failed to parse signer state: {}", e.what()); - } - // FIXME: Does not work as expected as signedTX txid is different from combined txid - //wallet_->setTransactionComment(signedTX, txComment()); - } - else if (result == bs::error::ErrorCode::TxCancelled) { - emit cancelTrade(orderId_.toStdString()); - } - else { - SPDLOG_LOGGER_ERROR(logger_, "failed to sign TX half: {}" - , bs::error::ErrorCodeToString(result).toStdString()); - emit error(id(), result, tr("TX half signing failed: %1") - .arg(bs::error::ErrorCodeToString(result))); - sendFailed(); - } - }; - - { - txReq_.armorySigner_.deserializeState(txReqData_); - auto inputs = bs::UtxoReservation::instance()->get(utxoRes_.reserveId()); - for (auto& input : inputs) { - try { - txReq_.armorySigner_.populateUtxo(input); - } - catch (const std::exception&) { - continue; - } - } - } - - txReq_.walletIds.clear(); - for (unsigned i=0; i < txReq_.armorySigner_.getTxInCount(); i++) { - const auto& utxo = txReq_.armorySigner_.getSpender(i)->getUtxo(); - const auto addr = bs::Address::fromUTXO(utxo); - const auto wallet = walletsMgr_->getWalletByAddress(addr); - if (!wallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find wallet from UTXO"); - continue; - } - txReq_.walletIds.push_back(wallet->walletId()); - } - - //Waiting for TX half signing... - SPDLOG_LOGGER_DEBUG(logger_, "signing with {} inputs", txReq_.armorySigner_.getTxInCount()); - signId_ = signingContainer_->signSettlementPartialTXRequest(txReq_, toPasswordDialogData(timestamp), cbTx); - return ( signId_ > 0); -} - -void DealerCCSettlementContainer::activate() -{ - settlWallet_->registerAddresses({ ownRecvAddr_.prefixed() }, true); - try { - signer_.deserializeState(txReqData_); - foundRecipAddr_ = signer_.findRecipAddress(ownRecvAddr_, [this](uint64_t value, uint64_t valReturn, uint64_t valInput) { - if ((order_.side == bs::network::Side::Buy) && qFuzzyCompare(order_.quantity, value / lotSize_)) { - amountValid_ = true; //valInput == (value + valReturn); - } - else if ((order_.side == bs::network::Side::Sell) && - (value == static_cast(std::llround(order_.quantity * order_.price * BTCNumericTypes::BalanceDivider)))) { - amountValid_ = true; //valInput > (value + valReturn); - } - }); - } - catch (const std::exception &e) { - logger_->error("Signer deser exc: {}", e.what()); - emit genAddressVerified(false); - return; - } - - if (!foundRecipAddr_ || !amountValid_) { - logger_->warn("[DealerCCSettlementContainer::activate] requester's TX verification failed: {}/{}" - , foundRecipAddr_, amountValid_); - ccWallet_ = nullptr; - xbtWallet_ = nullptr; - emit genAddressVerified(false); - } - else if (order_.side == bs::network::Side::Buy) { - //Waiting for genesis address verification to complete... - const auto &cbHasInput = [this, handle = validityFlag_.handle()](bool has) { - if (!handle.isValid()) { - return; - } - emit genAddressVerified(has); - }; - signer_.hasInputAddress(genesisAddr_, cbHasInput, lotSize_); - } - else { - emit genAddressVerified(true); - } - - startTimer(kWaitTimeoutInSec); -} - -void DealerCCSettlementContainer::onGenAddressVerified(bool addressVerified) -{ - genAddrVerified_ = addressVerified; - if (!addressVerified) { - logger_->warn("[DealerCCSettlementContainer::onGenAddressVerified] " - "counterparty's TX is unverified"); - emit error(id(), bs::error::ErrorCode::InternalError - , tr("Failed to verify counterparty's transaction")); - ccWallet_ = nullptr; - xbtWallet_ = nullptr; - } - - bs::sync::PasswordDialogData pd; - pd.setValue(PasswordDialogData::SettlementId, id()); - pd.setValue(PasswordDialogData::DeliveryUTXOVerified, addressVerified); - pd.setValue(PasswordDialogData::SigningAllowed, addressVerified); - - signingContainer_->updateDialogData(pd); -} - -bool DealerCCSettlementContainer::cancel() -{ - if (signId_ == 0) { - signingContainer_->CancelSignTx(BinaryData::fromString(id())); - } - - SettlementContainer::releaseUtxoRes(); - - emit timerExpired(); - return true; -} - -std::string DealerCCSettlementContainer::txComment() -{ - return std::string(bs::network::Side::toString(order_.side)) - + " " + order_.security + " @ " + std::to_string(order_.price); -} - -void DealerCCSettlementContainer::sendFailed() -{ - SettlementContainer::releaseUtxoRes(); - emit failed(id()); -} diff --git a/BlockSettleUILib/Trading/DealerCCSettlementContainer.h b/BlockSettleUILib/Trading/DealerCCSettlementContainer.h deleted file mode 100644 index a09a48b61..000000000 --- a/BlockSettleUILib/Trading/DealerCCSettlementContainer.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __DEALER_CC_SETTLEMENT_CONTAINER_H__ -#define __DEALER_CC_SETTLEMENT_CONTAINER_H__ - -#include -#include "AddressVerificator.h" -#include "CheckRecipSigner.h" -#include "SettlementContainer.h" -#include "UtxoReservationToken.h" -#include "CoreWallet.h" - -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - namespace hd { - class Wallet; - } - class Wallet; - class WalletsManager; - } -} -class ArmoryConnection; -class SignContainer; - - -class DealerCCSettlementContainer : public bs::SettlementContainer - , public ArmoryCallbackTarget -{ - Q_OBJECT -public: - DealerCCSettlementContainer(const std::shared_ptr &, const bs::network::Order & - , const std::string "eReqId, uint64_t lotSize - , const bs::Address &genAddr - , const std::string &ownRecvAddr - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &walletsMgr - , bs::hd::Purpose walletPurpose - , bs::UtxoReservationToken utxoRes - , bool expandTxDialogInfo); - ~DealerCCSettlementContainer() override; - - bool startSigning(QDateTime timestamp); - bool cancel() override; - - void activate() override; - void deactivate() override { stopTimer(); } - - std::string id() const override { return orderId_.toStdString(); } - bs::network::Asset::Type assetType() const override { return order_.assetType; } - std::string security() const override { return order_.security; } - std::string product() const override { return order_.product; } - bs::network::Side::Type side() const override { return order_.side; } - double quantity() const override { return order_.quantity; } - double price() const override { return order_.price; } - double amount() const override { return quantity(); } - bs::sync::PasswordDialogData toPasswordDialogData(QDateTime timestamp) const override; - - bool foundRecipAddr() const { return foundRecipAddr_; } - bool isAmountValid() const { return amountValid_; } - - bool isDelivery() const { return delivery_; } - -signals: - void signTxRequest(const QString &orderId, std::string txData); - void genAddressVerified(bool result); - - void cancelTrade(const std::string& clOrdId); - -private slots: - void onGenAddressVerified(bool result); - -private: - std::string txComment(); - void sendFailed(); - void onZCReceived(const std::string &, const std::vector &) override; - -private: - std::shared_ptr logger_; - const bs::network::Order order_; - const uint64_t lotSize_; - const bs::Address genesisAddr_; - const bool delivery_; - std::shared_ptr xbtWallet_; - std::shared_ptr signingContainer_; - std::shared_ptr walletsMgr_; - Codec_SignerState::SignerState txReqData_; - const bs::Address ownRecvAddr_; - const QString orderId_; - bool foundRecipAddr_ = false; - bool amountValid_ = false; - bool genAddrVerified_ = true; - unsigned int signId_ = 0; - bs::CheckRecipSigner signer_; - bs::core::wallet::TXSignRequest txReq_; - bool isGetAddressValid_ = false; - std::shared_ptr ccWallet_; - std::shared_ptr settlWallet_; - BinaryData expectedTxId_; -}; - -#endif // __DEALER_CC_SETTLEMENT_CONTAINER_H__ diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp deleted file mode 100644 index c2611da6b..000000000 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "DealerXBTSettlementContainer.h" - -#include "AuthAddressManager.h" -#include "CheckRecipSigner.h" -#include "CurrencyPair.h" -#include "HeadlessContainer.h" -#include "QuoteProvider.h" -#include "TradesUtils.h" -#include "TradesVerification.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" - -#include - -#include - -Q_DECLARE_METATYPE(AddressVerificationState) - -using namespace bs::sync; - -DealerXBTSettlementContainer::DealerXBTSettlementContainer(const std::shared_ptr &logger - , const bs::network::Order &order - , const std::shared_ptr &walletsMgr - , const std::shared_ptr &xbtWallet - , const std::shared_ptr "eProvider - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &authAddrMgr - , const bs::Address &authAddr - , const std::vector &utxosPayinFixed - , const bs::Address &recvAddr - , const std::shared_ptr &utxoReservationManager - , bs::hd::Purpose walletPurpose - , bs::UtxoReservationToken utxoRes - , bool expandTxDialogInfo - , uint64_t tier1XbtLimit ) - : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) - , order_(order) - , weSellXbt_((order.side == bs::network::Side::Buy) != (order.product == bs::network::XbtCurrency)) - , amount_((order.product != bs::network::XbtCurrency) ? order.quantity / order.price : order.quantity) - , logger_(logger) - , armory_(armory) - , walletsMgr_(walletsMgr) - , xbtWallet_(xbtWallet) - , signContainer_(container) - , authAddrMgr_(authAddrMgr) - , utxosPayinFixed_(utxosPayinFixed) - , recvAddr_(recvAddr) - , authAddr_(authAddr) - , utxoReservationManager_(utxoReservationManager) -{ - qRegisterMetaType(); - - CurrencyPair cp(security()); - fxProd_ = cp.ContraCurrency(bs::network::XbtCurrency); - - if (!xbtWallet_) { - throw std::runtime_error("no wallet"); - } - - auto qn = quoteProvider->getSubmittedXBTQuoteNotification(order.settlementId.toBinStr()); - if (qn.authKey.empty() || qn.reqAuthKey.empty() || qn.settlementId.empty()) { - throw std::invalid_argument("failed to get submitted QN for " + order.quoteId); - } - - const auto xbtAmount = bs::XBTAmount(amount_); - requesterAddressShouldBeVerified_ = xbtAmount > bs::XBTAmount((int64_t)tier1XbtLimit); - - // BST-2545: Use price as it see Genoa (and it computes it as ROUNDED_CCY / XBT) - const auto actualXbtPrice = UiUtils::actualXbtPrice(xbtAmount, price()); - - auto side = order.product == bs::network::XbtCurrency ? order.side : bs::network::Side::invert(order.side); - comment_ = fmt::format("{} {} @ {}", bs::network::Side::toString(side) - , order.security, UiUtils::displayPriceXBT(actualXbtPrice).toStdString()); - authKey_ = BinaryData::CreateFromHex(qn.authKey); - reqAuthKey_ = BinaryData::CreateFromHex(qn.reqAuthKey); - if (authKey_.empty() || reqAuthKey_.empty()) { - throw std::runtime_error("missing auth key"); - } - - init(armory_.get()); - settlementIdHex_ = qn.settlementId; - settlementId_ = BinaryData::CreateFromHex(qn.settlementId); - settlWallet_ = armory_->instantiateWallet(settlementIdHex_); - if (!settlWallet_) { - throw std::runtime_error("can't register settlement wallet in armory"); - } -} - -bool DealerXBTSettlementContainer::cancel() -{ - if (payinSignId_ != 0 || payoutSignId_ != 0) { - signContainer_->CancelSignTx(settlementId_); - } - - releaseUtxoRes(); - - SPDLOG_LOGGER_DEBUG(logger_, "cancel on a trade : {}", settlementIdHex_); - - emit timerExpired(); - return true; -} - -DealerXBTSettlementContainer::~DealerXBTSettlementContainer() -{ - settlWallet_->unregister(); - cleanup(); -} - -bs::sync::PasswordDialogData DealerXBTSettlementContainer::toPasswordDialogData(QDateTime timestamp) const -{ - bs::sync::PasswordDialogData dialogData = SettlementContainer::toPasswordDialogData(timestamp); - - dialogData.setValue(PasswordDialogData::IsDealer, true); - dialogData.setValue(PasswordDialogData::Market, "XBT"); - dialogData.setValue(PasswordDialogData::AutoSignCategory, static_cast(bs::signer::AutoSignCategory::SettlementDealer)); - - // rfq details - QString qtyProd = UiUtils::XbtCurrency; - - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Pay-In")); - dialogData.setValue(PasswordDialogData::Price, UiUtils::displayPriceXBT(price())); - dialogData.setValue(PasswordDialogData::FxProduct, fxProd_); - - bool isFxProd = (product() != bs::network::XbtCurrency); - - if (isFxProd) { - dialogData.setValue(PasswordDialogData::Quantity, tr("%1 %2") - .arg(UiUtils::displayAmountForProduct(quantity(), QString::fromStdString(fxProd_), bs::network::Asset::Type::SpotXBT)) - .arg(QString::fromStdString(fxProd_))); - - dialogData.setValue(PasswordDialogData::TotalValue, tr("%1 XBT") - .arg(UiUtils::displayAmount(quantity() / price()))); - } else { - dialogData.setValue(PasswordDialogData::Quantity, tr("%1 XBT") - .arg(UiUtils::displayAmount(amount()))); - - dialogData.setValue(PasswordDialogData::TotalValue, tr("%1 %2") - .arg(UiUtils::displayAmountForProduct(amount() * price(), QString::fromStdString(fxProd_), bs::network::Asset::Type::SpotXBT)) - .arg(QString::fromStdString(fxProd_))); - } - - // settlement details - dialogData.setValue(PasswordDialogData::SettlementId, settlementId_.toHexStr()); - dialogData.setValue(PasswordDialogData::SettlementAddress, settlAddr_.display()); - - dialogData.setValue(PasswordDialogData::RequesterAuthAddress, - bs::Address::fromPubKey(reqAuthKey_, AddressEntryType_P2WPKH).display()); - dialogData.setValue(PasswordDialogData::RequesterAuthAddressVerified, requestorAddressState_ == AddressVerificationState::Verified); - dialogData.setValue(PasswordDialogData::SigningAllowed, requestorAddressState_ == AddressVerificationState::Verified); - - dialogData.setValue(PasswordDialogData::ResponderAuthAddress, - bs::Address::fromPubKey(authKey_, AddressEntryType_P2WPKH).display()); - dialogData.setValue(PasswordDialogData::ResponderAuthAddressVerified, true); - - // tx details - dialogData.setValue(PasswordDialogData::TxInputProduct, UiUtils::XbtCurrency); - - return dialogData; -} - -void DealerXBTSettlementContainer::activate() -{ - startTimer(kWaitTimeoutInSec); - - addrVerificator_ = std::make_shared(logger_, armory_ - , [this, handle = validityFlag_.handle()](const bs::Address &address, AddressVerificationState state) - { - QMetaObject::invokeMethod(qApp, [this, handle, address, state] { - if (!handle.isValid()) { - return; - } - - SPDLOG_LOGGER_INFO(logger_, "counterparty's address verification {} for {}" - , to_string(state), address.display()); - requestorAddressState_ = state; - - if (state == AddressVerificationState::Verified) { - // we verify only requester's auth address - bs::sync::PasswordDialogData dialogData; - dialogData.setValue(PasswordDialogData::RequesterAuthAddressVerified, true); - dialogData.setValue(PasswordDialogData::SettlementId, settlementId_.toHexStr()); - dialogData.setValue(PasswordDialogData::SigningAllowed, true); - - signContainer_->updateDialogData(dialogData); - } - }); - }); - - addrVerificator_->SetBSAddressList(authAddrMgr_->GetBSAddresses()); - - if (requesterAddressShouldBeVerified_) { - const auto reqAuthAddrSW = bs::Address::fromPubKey(reqAuthKey_, AddressEntryType_P2WPKH); - addrVerificator_->addAddress(reqAuthAddrSW); - addrVerificator_->startAddressVerification(); - } else { - requestorAddressState_ = AddressVerificationState::Verified; - } - - const auto &authLeaf = walletsMgr_->getAuthWallet(); - signContainer_->setSettlAuthAddr(authLeaf->walletId(), settlementId_, authAddr_); -} - -void DealerXBTSettlementContainer::deactivate() -{ - stopTimer(); -} - -void DealerXBTSettlementContainer::onTXSigned(unsigned int idReq, BinaryData signedTX - , bs::error::ErrorCode errCode, std::string errMsg) -{ - if (payoutSignId_ && (payoutSignId_ == idReq)) { - payoutSignId_ = 0; - - if (errCode == bs::error::ErrorCode::TxCancelled) { - emit cancelTrade(settlementIdHex_); - return; - } - - if ((errCode != bs::error::ErrorCode::NoError) || signedTX.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "failed to sign pay-out: {} ({})", int(errCode), errMsg); - failWithErrorText(tr("Failed to sign pay-out"), errCode); - return; - } - - bs::tradeutils::PayoutVerifyArgs verifyArgs; - verifyArgs.signedTx = signedTX; - verifyArgs.settlAddr = settlAddr_; - verifyArgs.usedPayinHash = expectedPayinHash_; - verifyArgs.amount = bs::XBTAmount(amount_); - auto verifyResult = bs::tradeutils::verifySignedPayout(verifyArgs); - if (!verifyResult.success) { - SPDLOG_LOGGER_ERROR(logger_, "payout verification failed: {}", verifyResult.errorMsg); - failWithErrorText(tr("Payin verification failed"), errCode); - return; - } - - for (const auto &leaf : xbtWallet_->getGroup(bs::sync::hd::Wallet::getXBTGroupType())->getLeaves()) { - leaf->setTransactionComment(signedTX, comment_); - } -// settlWallet_->setTransactionComment(signedTX, comment_); //TODO: implement later - - SPDLOG_LOGGER_DEBUG(logger_, "signed payout: {}", signedTX.toHexStr()); - - emit sendSignedPayoutToPB(settlementIdHex_, signedTX); - - // ok. there is nothing this container could/should do -// emit completed(id()); - } - - if ((payinSignId_ != 0) && (payinSignId_ == idReq)) { - payinSignId_ = 0; - - if (errCode == bs::error::ErrorCode::TxCancelled) { - emit cancelTrade(settlementIdHex_); - return; - } - - if ((errCode != bs::error::ErrorCode::NoError) || signedTX.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "Failed to sign pay-in: {} ({})", (int)errCode, errMsg); - if (errCode == bs::error::ErrorCode::TxSpendLimitExceed) { - failWithErrorText(tr("Auto-signing (and auto-quoting) have been disabled" - " as your limit has been hit or elapsed"), errCode); - } - else { - failWithErrorText(tr("Failed to sign Pay-in"), errCode); - } - return; - } - - try { - const Tx tx(signedTX); - if (!tx.isInitialized()) { - throw std::runtime_error("uninited TX"); - } - - if (tx.getThisHash() != expectedPayinHash_) { - failWithErrorText(tr("payin hash mismatch"), bs::error::ErrorCode::TxInvalidRequest); - return; - } - } catch (const std::exception &e) { - failWithErrorText(tr("invalid signed pay-in"), bs::error::ErrorCode::TxInvalidRequest); - return; - } - - for (const auto &leaf : xbtWallet_->getGroup(bs::sync::hd::Wallet::getXBTGroupType())->getLeaves()) { - leaf->setTransactionComment(signedTX, comment_); - } -// settlWallet_->setTransactionComment(signedTX, comment_); //TODO: implement later - - emit sendSignedPayinToPB(settlementIdHex_, signedTX); - logger_->debug("[DealerXBTSettlementContainer::onTXSigned] Payin sent"); - - // ok. there is nothing this container could/should do -// emit completed(id()); - } -} - -void DealerXBTSettlementContainer::onZCReceived(const std::string & - , const std::vector &entries) -{ - const auto &cbTX = [this](const Tx &tx) - { - if (tx.getNumTxIn() != 1) { // not a pay-out - return; - } - const auto &txIn = tx.getTxInCopy(0); - const auto &op = txIn.getOutPoint(); - if (op.getTxHash() != expectedPayinHash_) { - return; // not our pay-out - } - const auto &serializedTx = tx.serialize(); - const auto &buyAuthKey = weSellXbt_ ? reqAuthKey_ : authKey_; - const auto &sellAuthKey = weSellXbt_ ? authKey_ : reqAuthKey_; - const auto &result = bs::TradesVerification::verifySignedPayout(serializedTx - , buyAuthKey.toHexStr(), sellAuthKey.toHexStr(), expectedPayinHash_ - , bs::XBTAmount(amount_).GetValue(), utxoReservationManager_->feeRatePb() - , settlementIdHex_, settlAddr_.display()); - if (result->success) { - emit completed(id()); - } - else { - emit failed(id()); - } - }; - - for (const auto &entry : entries) { - if (entry.walletIds.find(settlementIdHex_) == entry.walletIds.end()) { - continue; - } - if (entry.txHash == expectedPayinHash_) { - continue; // not interested in pay-in - } - armory_->getTxByHash(entry.txHash, cbTX, true); - } -} - -void DealerXBTSettlementContainer::onUnsignedPayinRequested(const std::string& settlementId) -{ - if (settlementIdHex_ != settlementId) { - // ignore - return; - } - - if (!weSellXbt_) { - SPDLOG_LOGGER_ERROR(logger_, "dealer is buying. Should not create unsigned payin on {}", settlementIdHex_); - return; - } - - bs::tradeutils::PayinArgs args; - initTradesArgs(args, settlementId); - args.fixedInputs = utxosPayinFixed_; - - const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); - if (!xbtWallet_->canMixLeaves()) { - const auto leaf = xbtGroup->getLeaf(walletPurpose_); - args.inputXbtWallets.push_back(leaf); - } - else { - for (const auto &leaf : xbtGroup->getLeaves()) { - args.inputXbtWallets.push_back(leaf); - } - } - - args.utxoReservation = bs::UtxoReservation::instance(); - - auto payinCb = bs::tradeutils::PayinResultCb([this, handle = validityFlag_.handle()] - (bs::tradeutils::PayinResult result) - { - QMetaObject::invokeMethod(qApp, [this, handle, result = std::move(result)] { - if (!handle.isValid()) { - return; - } - - if (!result.success) { - SPDLOG_LOGGER_ERROR(logger_, "creating payin request failed: {}", result.errorMsg); - failWithErrorText(tr("creating payin request failed"), bs::error::ErrorCode::InternalError); - return; - } - - settlAddr_ = result.settlementAddr; - settlWallet_->registerAddresses({ settlAddr_.prefixed() }, true); - - unsignedPayinRequest_ = std::move(result.signRequest); - // Reserve only automatic UTXO selection - if (utxosPayinFixed_.empty()) { - utxoRes_ = utxoReservationManager_->makeNewReservation(unsignedPayinRequest_.getInputs(nullptr), id()); - } - - emit sendUnsignedPayinToPB(settlementIdHex_ - , bs::network::UnsignedPayinData{ unsignedPayinRequest_.serializeState().SerializeAsString() }); - - const auto &authLeaf = walletsMgr_->getAuthWallet(); - signContainer_->setSettlCP(authLeaf->walletId(), result.payinHash, settlementId_, reqAuthKey_); - }); - }); - - bs::tradeutils::createPayin(std::move(args), std::move(payinCb)); -} - -void DealerXBTSettlementContainer::onSignedPayoutRequested(const std::string& settlementId - , const BinaryData& payinHash, QDateTime timestamp) -{ - if (settlementIdHex_ != settlementId) { - // ignore - return; - } - - startTimer(kWaitTimeoutInSec); - - if (weSellXbt_) { - SPDLOG_LOGGER_ERROR(logger_, "dealer is selling. Should not sign payout on {}", settlementIdHex_); - return; - } - - expectedPayinHash_ = payinHash; - - bs::tradeutils::PayoutArgs args; - initTradesArgs(args, settlementId); - args.payinTxId = payinHash; - args.recvAddr = recvAddr_; - - const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); - if (!xbtWallet_->canMixLeaves()) { - const auto leaf = xbtGroup->getLeaf(walletPurpose_); - args.outputXbtWallet = leaf; - } - else { - args.outputXbtWallet = xbtGroup->getLeaves().at(0); - } - - auto payoutCb = bs::tradeutils::PayoutResultCb([this, payinHash, timestamp, handle = validityFlag_.handle()] - (bs::tradeutils::PayoutResult result) - { - QMetaObject::invokeMethod(qApp, [this, payinHash, handle, timestamp, result = std::move(result)] { - if (!handle.isValid()) { - return; - } - - if (!result.success) { - SPDLOG_LOGGER_ERROR(logger_, "creating payout failed: {}", result.errorMsg); - failWithErrorText(tr("creating payout failed"), bs::error::ErrorCode::InternalError); - return; - } - - settlAddr_ = result.settlementAddr; - settlWallet_->registerAddresses({ settlAddr_.prefixed() }, true); - - bs::sync::PasswordDialogData dlgData = toPayOutTxDetailsPasswordDialogData(result.signRequest, timestamp); - dlgData.setValue(PasswordDialogData::IsDealer, true); - dlgData.setValue(PasswordDialogData::Market, "XBT"); - dlgData.setValue(PasswordDialogData::SettlementId, settlementId_.toHexStr()); - dlgData.setValue(PasswordDialogData::AutoSignCategory, static_cast(bs::signer::AutoSignCategory::SettlementDealer)); - - //note: signRequest should propably be a shared_ptr - auto signerObj = result.signRequest; - payoutSignId_ = signContainer_->signSettlementPayoutTXRequest(signerObj - , { settlementId_, reqAuthKey_, true }, dlgData); - }); - }); - bs::tradeutils::createPayout(std::move(args), std::move(payoutCb)); - - const auto &authLeaf = walletsMgr_->getAuthWallet(); - signContainer_->setSettlCP(authLeaf->walletId(), payinHash, settlementId_, reqAuthKey_); -} - -void DealerXBTSettlementContainer::onSignedPayinRequested(const std::string& settlementId - , const BinaryData &, const BinaryData& payinHash, QDateTime timestamp) -{ - if (settlementIdHex_ != settlementId) { - // ignore - return; - } - - if (payinHash.empty()) { - failWithErrorText(tr("Invalid Sign Pay-In request"), bs::error::ErrorCode::InternalError); - return; - } - - expectedPayinHash_ = payinHash; - - startTimer(kWaitTimeoutInSec); - - SPDLOG_LOGGER_DEBUG(logger_, "start sign payin: {}", settlementId); - - if (!weSellXbt_) { - SPDLOG_LOGGER_ERROR(logger_, "dealer is buying. Should not sign payin on {}", settlementIdHex_); - return; - } - - if (!unsignedPayinRequest_.isValid()) { - SPDLOG_LOGGER_ERROR(logger_, "unsigned payin request is invalid: {}", settlementIdHex_); - failWithErrorText(tr("Invalid unsigned pay-in"), bs::error::ErrorCode::InternalError); - return; - } - - bs::sync::PasswordDialogData dlgData = toPasswordDialogData(timestamp); - dlgData.setValue(PasswordDialogData::SettlementPayInVisible, true); - - payinSignId_ = signContainer_->signSettlementTXRequest(unsignedPayinRequest_, dlgData, SignContainer::TXSignMode::Full); -} - -void DealerXBTSettlementContainer::failWithErrorText(const QString &errorMessage, bs::error::ErrorCode code) -{ - SettlementContainer::releaseUtxoRes(); - - emit cancelTrade(settlementIdHex_); - - emit error(id(), code, errorMessage); - emit failed(id()); -} - -void DealerXBTSettlementContainer::initTradesArgs(bs::tradeutils::Args &args, const std::string &settlementId) -{ - args.amount = bs::XBTAmount{amount_}; - args.settlementId = BinaryData::CreateFromHex(settlementId); - args.ourAuthAddress = authAddr_; - args.cpAuthPubKey = reqAuthKey_; - args.walletsMgr = walletsMgr_; - args.armory = armory_; - args.signContainer = signContainer_; - args.feeRatePb_ = utxoReservationManager_->feeRatePb(); -} diff --git a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h b/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h deleted file mode 100644 index 60e9a92c8..000000000 --- a/BlockSettleUILib/Trading/DealerXBTSettlementContainer.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __DEALER_XBT_SETTLEMENT_CONTAINER_H__ -#define __DEALER_XBT_SETTLEMENT_CONTAINER_H__ - -#include "AddressVerificator.h" -#include "BSErrorCode.h" -#include "SettlementContainer.h" - -#include -#include - -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - namespace hd { - class Wallet; - } - class SettlementWallet; - class Wallet; - class WalletsManager; - } - namespace tradeutils { - struct Args; - } - class UTXOReservationManager; -} -class ArmoryConnection; -class AuthAddressManager; -class HeadlessContainer; -class QuoteProvider; - - -class DealerXBTSettlementContainer : public bs::SettlementContainer - , public ArmoryCallbackTarget -{ - Q_OBJECT -public: - DealerXBTSettlementContainer(const std::shared_ptr & - , const bs::network::Order & - , const std::shared_ptr & - , const std::shared_ptr &xbtWallet - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &authAddrMgr - , const bs::Address &authAddr - , const std::vector &utxosPayinFixed - , const bs::Address &recvAddr - , const std::shared_ptr &utxoReservationManager - , bs::hd::Purpose walletPurpose - , bs::UtxoReservationToken utxoRes - , bool expandTxDialogInfo - , uint64_t tier1XbtLimit); - ~DealerXBTSettlementContainer() override; - - bool cancel() override; - - void activate() override; - void deactivate() override; - - std::string id() const override { return order_.settlementId.toBinStr(); } - bs::network::Asset::Type assetType() const override { return order_.assetType; } - std::string security() const override { return order_.security; } - std::string product() const override { return order_.product; } - bs::network::Side::Type side() const override { return order_.side; } - double quantity() const override { return order_.quantity; } - double price() const override { return order_.price; } - double amount() const override { return amount_; } - bs::sync::PasswordDialogData toPasswordDialogData(QDateTime timestamp) const override; - -public slots: - void onUnsignedPayinRequested(const std::string& settlementId); - void onSignedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash - , QDateTime timestamp); - void onSignedPayinRequested(const std::string& settlementId, const BinaryData &unsignedPayin - , const BinaryData &payinHash, QDateTime timestamp); - -signals: - void sendUnsignedPayinToPB(const std::string& settlementId, const bs::network::UnsignedPayinData& unsignedPayinData); - void sendSignedPayinToPB(const std::string& settlementId, const BinaryData& signedPayin); - void sendSignedPayoutToPB(const std::string& settlementId, const BinaryData& signedPayout); - - void cancelTrade(const std::string& settlementId); - -private slots: - void onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode, std::string errMsg); - -private: - void failWithErrorText(const QString& error, bs::error::ErrorCode code); - - void initTradesArgs(bs::tradeutils::Args &args, const std::string &settlementId); - - void onZCReceived(const std::string &, const std::vector &) override; - -private: - const bs::network::Order order_; - std::string fxProd_; - const bool weSellXbt_; - std::string comment_; - const double amount_; - - std::shared_ptr logger_; - std::shared_ptr armory_; - std::shared_ptr walletsMgr_; - std::shared_ptr xbtWallet_; - std::shared_ptr addrVerificator_; - std::shared_ptr signContainer_; - std::shared_ptr authAddrMgr_; - std::shared_ptr utxoReservationManager_; - - AddressVerificationState requestorAddressState_ = AddressVerificationState::VerificationFailed; - bs::Address settlAddr_; - - std::string settlementIdHex_; - BinaryData settlementId_; - BinaryData authKey_; - BinaryData reqAuthKey_; - - bs::core::wallet::TXSignRequest unsignedPayinRequest_; - - unsigned int payinSignId_ = 0; - unsigned int payoutSignId_ = 0; - - BinaryData expectedPayinHash_; - - std::vector utxosPayinFixed_; - bs::Address recvAddr_; - bs::Address authAddr_; - - std::shared_ptr settlWallet_; - bool requesterAddressShouldBeVerified_ = true; -}; - -#endif // __DEALER_XBT_SETTLEMENT_CONTAINER_H__ diff --git a/BlockSettleUILib/Trading/FuturesTicket.cpp b/BlockSettleUILib/Trading/FuturesTicket.cpp deleted file mode 100644 index bcca4a9fe..000000000 --- a/BlockSettleUILib/Trading/FuturesTicket.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2020 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "FuturesTicket.h" -#include "ui_FuturesTicket.h" - -#include "AssetManager.h" -#include "BSMessageBox.h" -#include "CommonTypes.h" -#include "CurrencyPair.h" -#include "QuoteProvider.h" -#include "UiUtils.h" -#include "XbtAmountValidator.h" - -#include -#include -#include - -#include - -namespace { - const QString kEmptyInformationalLabelText = QStringLiteral("--"); - - const auto kInfValueStr = QStringLiteral("inf"); - const auto kInfValueAmount = bs::XBTAmount(std::numeric_limits::max()); - - QString formatPrice(double price, bs::network::Asset::Type at) { - if (price == 0) { - return kEmptyInformationalLabelText; - } - return UiUtils::displayPriceForAssetType(price, at); - } - - const auto kAmounts = std::vector{1., 5., 10., 25.}; - - const auto kLabelCount = kAmounts.size() + 1; - - std::string getLabel(size_t i) - { - if (i < kAmounts.size()) { - return fmt::format("{:0.2f}", kAmounts.at(i)); - } else { - return fmt::format(">{:0.2f}", kAmounts.back()); - } - } - - bs::XBTAmount getAmount(size_t i) { - if (i >= kAmounts.size()) { - return kInfValueAmount; - } - return bs::XBTAmount(kAmounts[i]); - } - - size_t getSelectedLine(double amount) { - for (size_t i = 0; i < kAmounts.size(); ++i) { - if (amount < kAmounts[i]) { - return i; - } - } - return kAmounts.size(); - } - -} - - -FuturesTicket::FuturesTicket(QWidget* parent) - : QWidget(parent) - , ui_(new Ui::FuturesTicket()) -{ - ui_->setupUi(this); - - xbtAmountValidator_ = new XbtAmountValidator(this); - - ui_->lineEditAmount->installEventFilter(this); - - connect(ui_->pushButtonBuy, &QPushButton::clicked, this, [this] { - submit(bs::network::Side::Buy); - }); - connect(ui_->pushButtonSell, &QPushButton::clicked, this, [this] { - submit(bs::network::Side::Sell); - }); - connect(ui_->lineEditAmount, &QLineEdit::textEdited, this, &FuturesTicket::onAmountEdited); - connect(ui_->pushButtonClose, &QPushButton::clicked, this, &FuturesTicket::onCloseAll); - - auto pricesLayout = new QGridLayout(ui_->widgetPrices); - pricesLayout->setSpacing(0); - pricesLayout->setContentsMargins(0, 0, 0, 0); - - labels_.resize(kLabelCount); - for (size_t i = 0; i < kLabelCount; ++i) { - auto label = new QLabel(this); - label->setText(QString::fromStdString(getLabel(i))); - pricesLayout->addWidget(label, int(i), 0, Qt::AlignCenter); - for (size_t j = 0; j < 2; ++j) { - auto label = new QLabel(this); - label->setObjectName(QStringLiteral("labelFuturePrice")); - pricesLayout->addWidget(label, int(i), int(j + 1), Qt::AlignCenter); - labels_[i][j] = label; - } - } - - ui_->helpLabel->hide(); - ui_->toolButtonMax->hide(); -} - -FuturesTicket::~FuturesTicket() = default; - -void FuturesTicket::resetTicket() -{ - ui_->labelProductGroup->setText(kEmptyInformationalLabelText); - ui_->labelSecurityId->setText(kEmptyInformationalLabelText); - - ui_->lineEditAmount->setValidator(xbtAmountValidator_); - ui_->lineEditAmount->clear(); -} - -void FuturesTicket::init(const std::shared_ptr &logger - , const std::shared_ptr &authAddressManager - , const std::shared_ptr& assetManager - , const std::shared_ptr "eProvider) -{ - logger_ = logger; - authAddressManager_ = authAddressManager; - assetManager_ = assetManager; - - connect(assetManager_.get(), &AssetManager::netDeliverableBalanceChanged, this, &FuturesTicket::updatePanel); - connect(assetManager_.get(), &AssetManager::balanceChanged, this, &FuturesTicket::updatePanel); - - updatePanel(); -} - -void FuturesTicket::setType(bs::network::Asset::Type type) -{ - type_ = type; - - ui_->pushButtonBuy->setEnabled(bs::network::Asset::isFuturesType(type)); - ui_->pushButtonSell->setEnabled(bs::network::Asset::isFuturesType(type)); -} - -void FuturesTicket::SetCurrencyPair(const QString& currencyPair) -{ - ui_->labelSecurityId->setText(currencyPair); - - CurrencyPair cp(currencyPair.toStdString()); - - currentProduct_ = cp.NumCurrency() == bs::network::XbtCurrency ? cp.DenomCurrency() : cp.NumCurrency(); - security_ = currencyPair; -} - -void FuturesTicket::SetProductAndSide(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice, bs::network::Side::Type side) -{ - resetTicket(); - - if (productGroup.isEmpty() || currencyPair.isEmpty()) { - return; - } - - ui_->labelProductGroup->setText(productGroup); - SetCurrencyPair(currencyPair); - //SetCurrentIndicativePrices(bidPrice, offerPrice); - - if (side == bs::network::Side::Type::Undefined) { - side = bs::network::Side::Buy; - } - - productSelectionChanged(); -} - -void FuturesTicket::setSecurityId(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice) -{ - SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Undefined); -} - -void FuturesTicket::setSecurityBuy(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice) -{ - SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Buy); -} - -void FuturesTicket::setSecuritySell(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice) -{ - SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Sell); -} - -std::string FuturesTicket::getProduct() const -{ - return currentProduct_; -} - -double FuturesTicket::getQuantity() const -{ - const CustomDoubleValidator *validator = dynamic_cast(ui_->lineEditAmount->validator()); - if (validator == nullptr) { - return 0; - } - return validator->GetValue(ui_->lineEditAmount->text()); -} - -void FuturesTicket::productSelectionChanged() -{ - const auto &mdInfos = mdInfo_[type_][security_.toStdString()]; - - for (size_t labelIndex = 0; labelIndex < labels_.size(); ++labelIndex) { - auto labelPair = labels_[labelIndex]; - auto mdInfoIt = mdInfos.find(getAmount(labelIndex)); - if (mdInfoIt != mdInfos.end()) { - const auto bidPrice = formatPrice(mdInfoIt->second.bidPrice, type_); - const auto askPrice = formatPrice(mdInfoIt->second.askPrice, type_); - labelPair[0]->setText(bidPrice); - labelPair[1]->setText(askPrice); - } else { - labelPair[0]->setText(kEmptyInformationalLabelText); - labelPair[1]->setText(kEmptyInformationalLabelText); - } - } - - updatePanel(); - - if (ui_->lineEditAmount->isEnabled()) { - ui_->lineEditAmount->setFocus(); - } -} - -void FuturesTicket::updatePanel() -{ - // currentProduct_ will be set to EURP or EURD - const double balanceIm = assetManager_ ? - assetManager_->getBalance(currentProduct_, false, nullptr) : 0.0; - auto currentProduct = "EUR"; - const double balanceReal = assetManager_ ? - assetManager_->getBalance(currentProduct, false, nullptr) : 0.0; - auto amountStringIm = UiUtils::displayCurrencyAmount(balanceIm); - auto amountStringReal = UiUtils::displayCurrencyAmount(balanceReal); - QString textIm = tr("%1 %2").arg(amountStringIm).arg(QString::fromStdString(currentProduct)); - QString textReal = tr("%1 %2").arg(amountStringReal).arg(QString::fromStdString(currentProduct)); - ui_->labelInitialMargin->setText(textIm); - ui_->labelBalanceValue->setText(textReal); - - int64_t futuresXbtAmount = 0; - double futuresBalance = 0; - if (assetManager_) { - futuresXbtAmount = currentProduct_ == "EURD" ? - assetManager_->futuresXbtAmountDeliverable() : assetManager_->futuresXbtAmountCashSettled(); - futuresBalance = currentProduct_ == "EURD" ? - assetManager_->futuresBalanceDeliverable() : assetManager_->futuresBalanceCashSettled(); - } - ui_->labelFutureBalanceValue->setText(UiUtils::displayAmount(futuresXbtAmount)); - const auto &mdInfos = mdInfo_[type_][security_.toStdString()]; - // TODO: Use current price from the server - double currentPrice = 0; - for (const auto &item : mdInfos) { - if (item.second.askPrice != 0 && item.second.bidPrice != 0) { - currentPrice = (item.second.askPrice + item.second.bidPrice) / 2; - break; - } - } - ui_->labelFutureBalanceValue->setProperty("positiveColor", futuresXbtAmount > 0); - ui_->labelFutureBalanceValue->setProperty("negativeColor", futuresXbtAmount < 0); - ui_->labelFutureBalanceValue->style()->unpolish(ui_->labelFutureBalanceValue); - ui_->labelFutureBalanceValue->style()->polish(ui_->labelFutureBalanceValue); - ui_->labelFutureBalanceValue->update(); - - double profitLoss = assetManager_ ? - assetManager_->profitLoss(futuresXbtAmount, futuresBalance, currentPrice) : 0; - auto profitLossString = UiUtils::displayCurrencyAmount(profitLoss); - QString profitLossText = tr("%1 %2").arg(profitLossString).arg(QString::fromStdString(currentProduct)); - ui_->labelProfitLoss->setText(profitLossText); - - double qty = getQuantity(); - size_t selectedLine = getSelectedLine(qty); - - const auto selectedProperyName = "selectedLine"; - for (size_t lineIndex = 0; lineIndex < kLabelCount; ++lineIndex) { - const auto &labelsLine = labels_.at(lineIndex); - for (size_t labelIndex = 0; labelIndex < labelsLine.size(); ++labelIndex) { - auto label = labelsLine.at(labelIndex); - bool isSelectedOld = label->property(selectedProperyName).toBool(); - bool isSelectedNew = lineIndex == selectedLine && qty > 0; - if (isSelectedNew != isSelectedOld) { - label->setProperty(selectedProperyName, isSelectedNew); - label->style()->unpolish(label); - label->style()->polish(label); - label->update(); - } - } - } -} - -void FuturesTicket::onCloseAll() -{ - int64_t netBalance = 0; - if (assetManager_) { - netBalance = (currentProduct_ == "EURD") ? - assetManager_->futuresXbtAmountDeliverable() : assetManager_->futuresXbtAmountCashSettled(); - } - auto amount = bs::XBTAmount(static_cast(std::abs(netBalance))); - auto side = netBalance < 0 ? bs::network::Side::Buy : bs::network::Side::Sell; - sendRequest(side, amount); -} - -bool FuturesTicket::eventFilter(QObject *watched, QEvent *evt) -{ - if (evt->type() == QEvent::KeyPress) { -// auto keyID = static_cast(evt)->key(); -// if (ui_->pushButtonSubmit->isEnabled() && ((keyID == Qt::Key_Return) || (keyID == Qt::Key_Enter))) { -// submitButtonClicked(); -// } - } - return QWidget::eventFilter(watched, evt); -} - -void FuturesTicket::submit(bs::network::Side::Type side) -{ - double amount = getQuantity(); - if (amount == 0) { - return; - } - sendRequest(side, bs::XBTAmount(amount)); - ui_->lineEditAmount->clear(); -} - -void FuturesTicket::sendRequest(bs::network::Side::Type side, bs::XBTAmount amount) -{ - if (amount.GetValue() == 0) { - return; - } - - const auto &mdInfos = mdInfo_[type_][security_.toStdString()]; - // Use upper_bound to get correct category - const auto mdInfoIt = mdInfos.upper_bound(amount); - if (mdInfoIt == mdInfos.end()) { - SPDLOG_LOGGER_ERROR(logger_, "can't find correct MD category"); - return; - } - const auto &mdInfo = mdInfoIt->second; - - double price = 0; - switch (side) { - case bs::network::Side::Buy: - price = mdInfo.askPrice; - break; - case bs::network::Side::Sell: - price = mdInfo.bidPrice; - break; - default: - break; - } - if (price == 0) { - return; - } - - bs::network::FutureRequest request; - request.side = side; - request.price = price; - request.amount = amount; - request.type = type_; - - emit sendFutureRequestToPB(request); -} - -void FuturesTicket::onMDUpdate(bs::network::Asset::Type type, const QString &security, bs::network::MDFields mdFields) -{ - if (!bs::network::Asset::isFuturesType(type)) { - return; - } - - auto &mdInfos = mdInfo_[type][security.toStdString()]; - mdInfos.clear(); - for (const auto &field : mdFields) { - bs::XBTAmount amount; - if (field.levelQuantity == kInfValueStr) { - amount = kInfValueAmount; - } else { - amount = bs::XBTAmount(field.levelQuantity.toDouble()); - } - auto &mdInfo = mdInfos[amount]; - - switch (field.type) { - case bs::network::MDField::PriceBid: - mdInfo.bidPrice = field.value; - break; - case bs::network::MDField::PriceOffer: - mdInfo.askPrice = field.value; - break; - case bs::network::MDField::PriceLast: - mdInfo.lastPrice = field.value; - break; - } - } - - if (type == type_) { - productSelectionChanged(); - } -} - -void FuturesTicket::onAmountEdited(const QString &) -{ - updatePanel(); -} diff --git a/BlockSettleUILib/Trading/FuturesTicket.h b/BlockSettleUILib/Trading/FuturesTicket.h deleted file mode 100644 index 046f1f4eb..000000000 --- a/BlockSettleUILib/Trading/FuturesTicket.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2020 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef FUTURES_TICKET_H -#define FUTURES_TICKET_H - -#include -#include - -#include -#include -#include -#include - -#include "BSErrorCode.h" -#include "CommonTypes.h" -#include "UtxoReservationToken.h" -#include "XBTAmount.h" -#include "UiUtils.h" - -namespace spdlog { - class logger; -} -namespace Ui { - class FuturesTicket; -} -class AssetManager; -class QuoteProvider; -class XbtAmountValidator; -class QLabel; - - -class FuturesTicket : public QWidget -{ -Q_OBJECT - -public: - FuturesTicket(QWidget* parent = nullptr); - ~FuturesTicket() override; - - void init(const std::shared_ptr &logger - , const std::shared_ptr & - , const std::shared_ptr &assetManager - , const std::shared_ptr "eProvider); - -signals: - void sendFutureRequestToPB(const bs::network::FutureRequest &details); - -public slots: - void setType(bs::network::Asset::Type type); - void SetProductAndSide(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice, bs::network::Side::Type side); - void setSecurityId(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice); - void setSecurityBuy(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice); - void setSecuritySell(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice); - void SetCurrencyPair(const QString& currencyPair); - - void onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields); - -private slots: - void onAmountEdited(const QString &); - void updatePanel(); - void onCloseAll(); - -private: - bool eventFilter(QObject *watched, QEvent *evt) override; - - std::string getProduct() const; - double getQuantity() const; - - void resetTicket(); - void productSelectionChanged(); - void submit(bs::network::Side::Type side); - void sendRequest(bs::network::Side::Type side, bs::XBTAmount amount); - - std::unique_ptr ui_; - - std::shared_ptr logger_; - std::shared_ptr assetManager_; - std::shared_ptr authAddressManager_; - - XbtAmountValidator *xbtAmountValidator_{}; - - bs::network::Asset::Type type_{}; - std::string currentProduct_; - QString security_; - - std::map>> mdInfo_; - - std::vector> labels_; -}; - -#endif // FUTURES_TICKET_H diff --git a/BlockSettleUILib/Trading/FuturesTicket.ui b/BlockSettleUILib/Trading/FuturesTicket.ui deleted file mode 100644 index 5ef1155ee..000000000 --- a/BlockSettleUILib/Trading/FuturesTicket.ui +++ /dev/null @@ -1,746 +0,0 @@ - - - - FuturesTicket - - - - 0 - 0 - 460 - 757 - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - REQUEST FOR STREAM - - - 0 - - - true - - - - - - - - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - GENERAL - - - true - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 16777215 - - - - Product Group - - - - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 16777215 - - - - Security ID - - - - - - - font-weight: bold; - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 21 - - - - - 16777215 - 21 - - - - - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Max - - - - - - - - - - - - - - - - - - - CLOSE ALL OPEN POSITIONS - - - - - - - Market - - - true - - - - - - - false - - - Limit - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - BUY - - - - - - - SELL - - - - - - - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 10 - - - 0 - - - 10 - - - 0 - - - - - Initial Margin - - - - - - - font-weight: bold; - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 10 - - - 0 - - - 10 - - - 0 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 10 - - - 0 - - - 10 - - - 0 - - - - - Net Deliverable Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 10 - - - 0 - - - 10 - - - 5 - - - - - Profit/Loss - - - - - - - font-weight: bold; - - - --- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - - 0 - 0 - - - - help text - - - true - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - diff --git a/BlockSettleUILib/Trading/MarketDataModel.cpp b/BlockSettleUILib/Trading/MarketDataModel.cpp deleted file mode 100644 index 50a81b75f..000000000 --- a/BlockSettleUILib/Trading/MarketDataModel.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "MarketDataModel.h" - -#include "Colors.h" -#include "CommonTypes.h" -#include "UiUtils.h" - -#include - -MarketDataModel::MarketDataModel(const QStringList &showSettings, QObject* parent) - : QStandardItemModel(parent) -{ - QStringList headerLabels; - for (int col = static_cast(MarketDataColumns::First); col < static_cast(MarketDataColumns::ColumnsCount); col++) { - headerLabels << columnName(static_cast(col)); - } - setHorizontalHeaderLabels(headerLabels); - - for (int col = static_cast(MarketDataColumns::First) + 1; col < static_cast(MarketDataColumns::ColumnsCount); col++) { - horizontalHeaderItem(col)->setTextAlignment(Qt::AlignCenter); - } - - for (const auto &setting : showSettings) { - instrVisible_.insert(setting); - } - - timer_.setInterval(500); - connect(&timer_, &QTimer::timeout, this, &MarketDataModel::ticker); - timer_.start(); -} - -QString MarketDataModel::columnName(MarketDataColumns col) const -{ - switch (col) - { - case MarketDataColumns::Product: return tr("Security"); - case MarketDataColumns::BidPrice: return tr("Bid"); - case MarketDataColumns::OfferPrice: return tr("Ask"); - case MarketDataColumns::LastPrice: return tr("Last"); - case MarketDataColumns::DailyVol: return tr("24h Volume"); - case MarketDataColumns::EmptyColumn: return QString(); - default: return tr("Unknown"); - } -} - -static double toDoubleFromPriceStr(const QString &priceStr) -{ - if (priceStr.isEmpty()) { - return std::numeric_limits::infinity(); - } - bool ok; - double rv = priceStr.toDouble(&ok); - if (!ok) { - rv = QLocale().toDouble(priceStr, &ok); - } - if (!ok) { - return std::numeric_limits::infinity(); - } - return rv; -} - -QToggleItem* priceItem(const QString &price) -{ - auto item = new QToggleItem(price); - item->setData(Qt::AlignRight, Qt::TextAlignmentRole); - return item; -} - -QToggleItem *MarketDataModel::getGroup(bs::network::Asset::Type assetType) -{ - QString productGroup; - if (assetType == bs::network::Asset::Undefined) { - productGroup = tr("Rejected"); - } - else { - productGroup = tr(bs::network::Asset::toString(assetType)); - } - auto groupItems = findItems(productGroup); - QToggleItem* groupItem = nullptr; - if (groupItems.isEmpty()) { - groupItem = new QToggleItem(productGroup, isVisible(productGroup)); - groupItem->setData(-1); - appendRow(QList() << groupItem); - } - else { - groupItem = static_cast(groupItems.first()); - } - return groupItem; -} - -struct PriceItem { - QString str; - double value; -}; -using PriceMap = std::map; - -static QString getVolumeString(double value, bs::network::Asset::Type at) -{ - if (qFuzzyIsNull(value)) { - return QString{}; - } - - switch(at) { - case bs::network::Asset::SpotFX: - return UiUtils::displayCurrencyAmount(value); - case bs::network::Asset::SpotXBT: - return UiUtils::displayAmount(value); - case bs::network::Asset::PrivateMarket: - return UiUtils::displayCCAmount(value); - } - - return QString(); -} - -static void FieldsToMap(bs::network::Asset::Type at, const bs::network::MDFields &fields, PriceMap &map) -{ - for (const auto &field : fields) { - switch (field.type) { - case bs::network::MDField::PriceBid: - map[MarketDataModel::MarketDataColumns::BidPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; - break; - case bs::network::MDField::PriceOffer: - map[MarketDataModel::MarketDataColumns::OfferPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; - break; - case bs::network::MDField::PriceLast: - map[MarketDataModel::MarketDataColumns::LastPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; - break; - case bs::network::MDField::DailyVolume: - map[MarketDataModel::MarketDataColumns::DailyVol] = { getVolumeString(field.value, at), field.value }; - break; - case bs::network::MDField::Reject: - map[MarketDataModel::MarketDataColumns::ColumnsCount] = { field.levelQuantity, 0 }; - break; - default: break; - } - } -} - -static void FutureFieldsToMap(bs::network::Asset::Type at, const bs::network::MDFields &fields, PriceMap &map) -{ - for (const auto &field : fields) { - switch (field.type) { - case bs::network::MDField::PriceBid: - if (field.isIndicativeForFutures()) { - map[MarketDataModel::MarketDataColumns::BidPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; - } - break; - case bs::network::MDField::PriceOffer: - if (field.isIndicativeForFutures()) { - map[MarketDataModel::MarketDataColumns::OfferPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; - } - break; - case bs::network::MDField::PriceLast: - map[MarketDataModel::MarketDataColumns::LastPrice] = { UiUtils::displayPriceForAssetType(field.value, at), UiUtils::truncatePriceForAsset(field.value, at) }; - break; - case bs::network::MDField::DailyVolume: - map[MarketDataModel::MarketDataColumns::DailyVol] = { getVolumeString(field.value, at), field.value }; - break; - case bs::network::MDField::Reject: - map[MarketDataModel::MarketDataColumns::ColumnsCount] = { field.levelQuantity, 0 }; - break; - default: break; - } - } -} - -bool MarketDataModel::isVisible(const QString &id) const -{ - if (instrVisible_.empty()) { - return true; - } - const auto itVisible = instrVisible_.find(id); - if (itVisible != instrVisible_.end()) { - return true; - } - return false; -} - -void MarketDataModel::onMDUpdated(bs::network::Asset::Type assetType - , const QString &security, const bs::network::MDFields &mdFields) -{ - if ((assetType == bs::network::Asset::Undefined) && security.isEmpty()) { // Celer disconnected - priceUpdates_.clear(); - removeRows(0, rowCount()); - return; - } - - PriceMap fieldsMap; - - if (bs::network::Asset::isFuturesType(assetType)) { - FutureFieldsToMap(bs::network::Asset::SpotXBT, mdFields, fieldsMap); - } else { - FieldsToMap(assetType, mdFields, fieldsMap); - } - - auto groupItem = getGroup(assetType); - auto childRow = groupItem->findRowWithText(security); - const auto timeNow = QDateTime::currentDateTime(); - if (!childRow.empty()) { - for (const auto &price : fieldsMap) { - childRow[static_cast(price.first)]->setText(price.second.str); - childRow[static_cast(price.first)]->setBackground(bgColorForCol(security, price.first, price.second.value, timeNow, childRow)); - } - return; - } - - // If we reach here, the product wasn't found, so we make a new row for it - QToggleItem::QToggleRow items; - if (assetType == bs::network::Asset::Type::Undefined) { - const auto rejItem = new QToggleItem(fieldsMap[MarketDataColumns::ColumnsCount].str); - rejItem->setForeground(Qt::red); - items << rejItem; - } - else { - items << new QToggleItem(security, isVisible(security) | groupItem->isVisible()); - for (int col = static_cast(MarketDataColumns::First) + 1; col < static_cast(MarketDataColumns::ColumnsCount); col++) { - const auto price = fieldsMap[static_cast(col)]; - auto item = priceItem(price.str); - items << item; - } - } - groupItem->addRow(items); -} - -QBrush MarketDataModel::bgColorForCol(const QString &security, MarketDataModel::MarketDataColumns col, double price - , const QDateTime &updTime, const QList &row) -{ - switch (col) { - case MarketDataModel::MarketDataColumns::BidPrice: - case MarketDataModel::MarketDataColumns::OfferPrice: - case MarketDataModel::MarketDataColumns::LastPrice: - { - const auto prev = priceUpdates_[security][col].price; - priceUpdates_[security][col] = { price, updTime, {} }; - if (!qFuzzyIsNull(prev)) { - if (price > prev) { - priceUpdates_[security][col].row = row; - return c_greenColor; - } - else if (price < prev) { - priceUpdates_[security][col].row = row; - return c_redColor; - } - } - break; - } - default: break; - } - return {}; -} - -QStringList MarketDataModel::getVisibilitySettings() const -{ - QStringList rv; - for (int i = 0; i < rowCount(); i++) { - const auto toggleGrp = static_cast(item(i)); - if (toggleGrp == nullptr) { - continue; - } - if (toggleGrp->isVisible()) { - rv << toggleGrp->text(); - continue; - } - for (int i = 0; i < toggleGrp->rowCount(); i++) { - const auto child = static_cast(toggleGrp->child(i, 0)); - if (child == nullptr) { - continue; - } - if (child->isVisible()) { - rv << child->text(); - } - } - } - return rv; -} - -void MarketDataModel::onVisibilityToggled(bool filtered) -{ - for (int i = 0; i < rowCount(); i++) { - auto toggleGrp = static_cast(item(i)); - if (toggleGrp == nullptr) { - continue; - } - toggleGrp->showCheckBox(!filtered); - } - emit needResize(); -} - -void MarketDataModel::ticker() -{ - const auto timeNow = QDateTime::currentDateTime(); - for (const auto &priceUpd : priceUpdates_) { - for (auto price : priceUpd.second) { - if (price.second.row.empty()) { - continue; - } - if (price.second.updated.msecsTo(timeNow) > 3000) { - price.second.row[static_cast(price.first)]->setBackground(QBrush()); - price.second.row.clear(); - } - } - } -} - - -QToggleItem::QToggleRow QToggleItem::findRowWithText(const QString &text, int column) -{ - for (int i = 0; i < rowCount(); i++) { - if (child(i, column)->text() == text) { - QToggleRow rv; - for (int j = 0; j < columnCount(); j++) { - rv << static_cast(child(i, j)); - } - return rv; - } - } - for (const auto invChild : invisibleChildren_) { - if (invChild.at(column)->text() == text) { - return invChild; - } - } - return {}; -} - -void QToggleItem::showCheckBox(bool state, int column) -{ - if (state) { - setCheckState(isVisible_ ? Qt::Checked : Qt::Unchecked); - for (const auto &row : invisibleChildren_) { - appendRow(row); - } - } - - setCheckable(state); - for (int i = 0; i < rowCount(); i++) { - auto tgChild = static_cast(child(i, column)); - tgChild->showCheckBox(state, column); - } - - if (!state) { - setData(QVariant(), Qt::CheckStateRole); - - QList > takenRows; - int i = 0; - while (rowCount() > 0) { - if (i >= rowCount()) { - break; - } - auto tgChild = static_cast(child(i, column)); - if (tgChild->isVisible()) { - i++; - } - else { - takenRows << takeRow(i); - } - } - invisibleChildren_.clear(); - for (const auto row : takenRows) { - QToggleRow tRow; - for (const auto item : row) { - tRow << static_cast(item); - } - invisibleChildren_ << tRow; - } - } -} - -void QToggleItem::setData(const QVariant &value, int role) -{ - QStandardItem::setData(value, role); - - if ((role == Qt::CheckStateRole) && value.isValid()) { - const auto state = static_cast(value.toInt()); - setVisible(state == Qt::Checked); - auto tParent = dynamic_cast(parent()); - if (tParent != nullptr) { - tParent->updateCheckMark(); - } else { - for (int i=0; i < rowCount(); i++) { - auto childItem = dynamic_cast(child(i, 0)); - childItem->setVisible(state == Qt::Checked); - childItem->QStandardItem::setData(state, Qt::CheckStateRole); - } - } - } -} - -void QToggleItem::updateCheckMark(int column) -{ - int nbVisibleChildren = 0; - for (int i = 0; i < rowCount(); i++) { - const auto tgChild = static_cast(child(i, column)); - if (tgChild->isVisible()) { - nbVisibleChildren++; - } - } - if (!nbVisibleChildren) { - QStandardItem::setData(Qt::Unchecked, Qt::CheckStateRole); - } - else { - setVisible(nbVisibleChildren != 0); - QStandardItem::setData((nbVisibleChildren == rowCount()) ? Qt::Checked : Qt::PartiallyChecked, Qt::CheckStateRole); - } -} - -void QToggleItem::addRow(const QToggleItem::QToggleRow &row, int visColumn) -{ - if (row[visColumn]->isVisible()) { - appendRow(row); - } - else { - invisibleChildren_ << row; - } -} - -void QToggleItem::appendRow(const QToggleItem::QToggleRow &row) -{ - QList list; - for (const auto &item : row) { - list << item; - } - QStandardItem::appendRow(list); -} - - -MDSortFilterProxyModel::MDSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) -{ } - -bool MDSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - QVariant leftData = sourceModel()->data(left); - QVariant rightData = sourceModel()->data(right); - - static const std::map groups = { - {tr(bs::network::Asset::toString(bs::network::Asset::PrivateMarket)), 0}, - {tr(bs::network::Asset::toString(bs::network::Asset::SpotXBT)), 1}, - {tr(bs::network::Asset::toString(bs::network::Asset::SpotFX)), 2}, - }; - - if (!left.parent().isValid() && !right.parent().isValid()) { - try { - return (groups.at(leftData.toString()) < groups.at(rightData.toString())); - } catch (const std::out_of_range &) { - return true; - } - } - - if ((leftData.type() == QVariant::String) && (rightData.type() == QVariant::String)) { - if ((left.column() > 0) && (right.column() > 0)) { - double priceLeft = toDoubleFromPriceStr(leftData.toString()); - double priceRight = toDoubleFromPriceStr(rightData.toString()); - - if ((priceLeft > 0) && (priceRight > 0)) - return (priceLeft < priceRight); - } - return (leftData.toString() < rightData.toString()); - } - return (leftData < rightData); -} diff --git a/BlockSettleUILib/Trading/MarketDataModel.h b/BlockSettleUILib/Trading/MarketDataModel.h deleted file mode 100644 index 26e27555e..000000000 --- a/BlockSettleUILib/Trading/MarketDataModel.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __MARKET_DATA_MODEL_H__ -#define __MARKET_DATA_MODEL_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "CommonTypes.h" - - -class QToggleItem : public QStandardItem -{ -public: - QToggleItem(const QString &text, bool visible = true) - : QStandardItem(text), isVisible_(visible) {} - - typedef QList QToggleRow; - QToggleRow findRowWithText(const QString &text, int column = 0); - - void addRow(const QToggleRow &, int visColumn = 0); - void setVisible(bool visible) { isVisible_ = visible; } - bool isVisible() const { return isVisible_; } - void showCheckBox(bool state, int column = 0); - void updateCheckMark(int column = 0); - - void setData(const QVariant &value, int role = Qt::UserRole + 1) override; - -private: - bool isVisible_ = true; - QList invisibleChildren_; - - void appendRow(const QToggleRow &); -}; - -class MarketDataModel : public QStandardItemModel -{ -Q_OBJECT -public: - MarketDataModel(const QStringList &showSettings = {}, QObject *parent = nullptr); - ~MarketDataModel() noexcept = default; - - MarketDataModel(const MarketDataModel&) = delete; - MarketDataModel& operator = (const MarketDataModel&) = delete; - MarketDataModel(MarketDataModel&&) = delete; - MarketDataModel& operator = (MarketDataModel&&) = delete; - - QStringList getVisibilitySettings() const; - -public slots: - void onMDUpdated(bs::network::Asset::Type, const QString &security - , const bs::network::MDFields &); - void onVisibilityToggled(bool filtered); - -signals: - void needResize(); - -private slots: - void ticker(); - -public: - enum class MarketDataColumns : int - { - First, - Product = First, - BidPrice, - OfferPrice, - LastPrice, - DailyVol, - EmptyColumn, - ColumnsCount - }; - -private: - struct PriceUpdate { - double price; - QDateTime updated; - QList row; - }; - typedef std::map PriceByCol; - typedef std::map PriceUpdates; - - std::set instrVisible_; - PriceUpdates priceUpdates_; - QTimer timer_; - -private: - QToggleItem *getGroup(bs::network::Asset::Type); - QString columnName(MarketDataColumns) const; - bool isVisible(const QString &id) const; - QBrush bgColorForCol(const QString &security, MarketDataModel::MarketDataColumns, double price - , const QDateTime &, const QList &row); -}; - - -class MDSortFilterProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - explicit MDSortFilterProxyModel(QObject *parent = nullptr); - -protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; -}; - -#endif // __MARKET_DATA_MODEL_H__ diff --git a/BlockSettleUILib/Trading/MarketDataWidget.cpp b/BlockSettleUILib/Trading/MarketDataWidget.cpp deleted file mode 100644 index e88eaa025..000000000 --- a/BlockSettleUILib/Trading/MarketDataWidget.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "MarketDataWidget.h" - -#include "ui_MarketDataWidget.h" -#include "MarketDataProvider.h" -#include "MarketDataModel.h" -#include "MDCallbacksQt.h" -#include "TreeViewWithEnterKey.h" - -constexpr int EMPTY_COLUMN_WIDTH = 0; - -bool MarketSelectedInfo::isValid() const -{ - return !productGroup_.isEmpty() && - !currencyPair_.isEmpty() && - !bidPrice_.isEmpty() && - !offerPrice_.isEmpty() - ; -} - -MarketDataWidget::MarketDataWidget(QWidget* parent) - : QWidget(parent) - , ui_(new Ui::MarketDataWidget()) - , marketDataModel_(nullptr) - , mdSortFilterModel_(nullptr) -{ - ui_->setupUi(this); - - marketDataModel_ = new MarketDataModel({}, ui_->treeViewMarketData); - mdSortFilterModel_ = new MDSortFilterProxyModel(ui_->treeViewMarketData); - mdSortFilterModel_->setSourceModel(marketDataModel_); - - ui_->treeViewMarketData->setModel(mdSortFilterModel_); - ui_->treeViewMarketData->setSortingEnabled(true); - - ui_->treeViewMarketData->setHeader(mdHeader_.get()); - ui_->treeViewMarketData->header()->setSortIndicator(static_cast(MarketDataModel::MarketDataColumns::First) - , Qt::AscendingOrder); - ui_->treeViewMarketData->header()->resizeSection(static_cast(MarketDataModel::MarketDataColumns::EmptyColumn) - , EMPTY_COLUMN_WIDTH); - - connect(marketDataModel_, &QAbstractItemModel::rowsInserted, [this]() { - if (mdHeader_ != nullptr) { - mdHeader_->setEnabled(true); - } - }); - connect(mdSortFilterModel_, &QAbstractItemModel::rowsInserted, this, &MarketDataWidget::resizeAndExpand); - connect(marketDataModel_, &MarketDataModel::needResize, this, &MarketDataWidget::resizeAndExpand); - - connect(ui_->treeViewMarketData, &QTreeView::clicked, this, &MarketDataWidget::clicked); - connect(ui_->treeViewMarketData->selectionModel(), &QItemSelectionModel::currentChanged - , this, &MarketDataWidget::onSelectionChanged); - - connect(ui_->pushButtonMDConnection, &QPushButton::clicked, this - , &MarketDataWidget::ChangeMDSubscriptionState); - - ui_->pushButtonMDConnection->setText(tr("Subscribe")); -} - -MarketDataWidget::~MarketDataWidget() -{} - -void MarketDataWidget::init(const std::shared_ptr &appSettings, ApplicationSettings::Setting param - , const std::shared_ptr &mdProvider - , const std::shared_ptr &mdCallbacks) -{ - mdProvider_ = mdProvider; - - QStringList visSettings; - if (appSettings != nullptr) { - settingVisibility_ = param; - visSettings = appSettings->get(settingVisibility_); - appSettings_ = appSettings; - } - if (marketDataModel_) { - marketDataModel_->deleteLater(); - } - if (mdSortFilterModel_) { - mdSortFilterModel_->deleteLater(); - } - marketDataModel_ = new MarketDataModel(visSettings, ui_->treeViewMarketData); - mdSortFilterModel_ = new MDSortFilterProxyModel(ui_->treeViewMarketData); - mdSortFilterModel_->setSourceModel(marketDataModel_); - - ui_->treeViewMarketData->setModel(mdSortFilterModel_); - ui_->treeViewMarketData->setSortingEnabled(true); - - if (appSettings != nullptr) { - mdHeader_ = std::make_shared(Qt::Horizontal, ui_->treeViewMarketData); - connect(mdHeader_.get(), &MDHeader::stateChanged, marketDataModel_, &MarketDataModel::onVisibilityToggled); - connect(mdHeader_.get(), &MDHeader::stateChanged, this, &MarketDataWidget::onHeaderStateChanged); - mdHeader_->setEnabled(false); - mdHeader_->setToolTip(tr("Toggles filtered/selection view")); - mdHeader_->setStretchLastSection(true); - mdHeader_->show(); - } - - ui_->treeViewMarketData->setHeader(mdHeader_.get()); - ui_->treeViewMarketData->header()->setSortIndicator( - static_cast(MarketDataModel::MarketDataColumns::First), Qt::AscendingOrder); - ui_->treeViewMarketData->header()->resizeSection(static_cast(MarketDataModel::MarketDataColumns::EmptyColumn), - EMPTY_COLUMN_WIDTH); - - connect(marketDataModel_, &QAbstractItemModel::rowsInserted, [this]() { - if (mdHeader_ != nullptr) { - mdHeader_->setEnabled(true); - } - }); - connect(mdSortFilterModel_, &QAbstractItemModel::rowsInserted, this, &MarketDataWidget::resizeAndExpand); - connect(marketDataModel_, &MarketDataModel::needResize, this, &MarketDataWidget::resizeAndExpand); - - connect(ui_->treeViewMarketData, &QTreeView::clicked, this, &MarketDataWidget::clicked); - connect(ui_->treeViewMarketData->selectionModel(), &QItemSelectionModel::currentChanged, this, &MarketDataWidget::onSelectionChanged); - - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, marketDataModel_, &MarketDataModel::onMDUpdated); - connect(mdCallbacks.get(), &MDCallbacksQt::MDReqRejected, this, &MarketDataWidget::onMDRejected); - - connect(ui_->pushButtonMDConnection, &QPushButton::clicked, this, &MarketDataWidget::ChangeMDSubscriptionState); - - connect(mdCallbacks.get(), &MDCallbacksQt::WaitingForConnectionDetails, this, &MarketDataWidget::onLoadingNetworkSettings); - connect(mdCallbacks.get(), &MDCallbacksQt::StartConnecting, this, &MarketDataWidget::OnMDConnecting); - connect(mdCallbacks.get(), &MDCallbacksQt::Connected, this, &MarketDataWidget::OnMDConnected); - connect(mdCallbacks.get(), &MDCallbacksQt::Disconnecting, this, &MarketDataWidget::OnMDDisconnecting); - connect(mdCallbacks.get(), &MDCallbacksQt::Disconnected, this, &MarketDataWidget::OnMDDisconnected); - - ui_->pushButtonMDConnection->setText(tr("Subscribe")); -} - -void MarketDataWidget::onLoadingNetworkSettings() -{ - ui_->pushButtonMDConnection->setText(tr("Connecting")); - ui_->pushButtonMDConnection->setEnabled(false); - ui_->pushButtonMDConnection->setToolTip(tr("Waiting for connection details")); -} - -void MarketDataWidget::OnMDConnecting() -{ - ui_->pushButtonMDConnection->setText(tr("Connecting")); - ui_->pushButtonMDConnection->setEnabled(false); - ui_->pushButtonMDConnection->setToolTip(QString{}); -} - -void MarketDataWidget::OnMDConnected() -{ - ui_->pushButtonMDConnection->setText(tr("Disconnect")); - ui_->pushButtonMDConnection->setEnabled(!authorized_); -} - -void MarketDataWidget::OnMDDisconnecting() -{ - ui_->pushButtonMDConnection->setText(tr("Disconnecting")); - ui_->pushButtonMDConnection->setEnabled(false); - mdProvider_->UnsubscribeFromMD(); -} - -void MarketDataWidget::OnMDDisconnected() -{ - ui_->pushButtonMDConnection->setText(tr("Subscribe")); - ui_->pushButtonMDConnection->setEnabled(!authorized_); -} - -void MarketDataWidget::ChangeMDSubscriptionState() -{ - if (mdProvider_) { - if (mdProvider_->IsConnectionActive()) { - mdProvider_->DisconnectFromMDSource(); - } else { - mdProvider_->SubscribeToMD(); - } - } - else { - if (envConf_ == ApplicationSettings::EnvConfiguration::Unknown) { - return; // pop up error? - } - if (connected_) { - emit needMdDisconnect(); - } - else { - emit needMdConnection(envConf_); - } - } -} - -MarketSelectedInfo MarketDataWidget::getRowInfo(const QModelIndex& index) const -{ - if (!index.isValid() || !index.parent().isValid()) { - return {}; - } - - auto pairIndex = mdSortFilterModel_->index(index.row(), static_cast(MarketDataModel::MarketDataColumns::Product), index.parent()); - auto bidIndex = mdSortFilterModel_->index(index.row(), static_cast(MarketDataModel::MarketDataColumns::BidPrice), index.parent()); - auto offerIndex = mdSortFilterModel_->index(index.row(), static_cast(MarketDataModel::MarketDataColumns::OfferPrice), index.parent()); - - MarketSelectedInfo selectedInfo; - selectedInfo.productGroup_ = mdSortFilterModel_->data(index.parent()).toString(); - selectedInfo.currencyPair_ = mdSortFilterModel_->data(pairIndex).toString(); - selectedInfo.bidPrice_ = mdSortFilterModel_->data(bidIndex).toString(); - selectedInfo.offerPrice_ = mdSortFilterModel_->data(offerIndex).toString(); - - return selectedInfo; -} - -TreeViewWithEnterKey* MarketDataWidget::view() const -{ - return ui_->treeViewMarketData; -} - -void MarketDataWidget::setAuthorized(bool authorized) -{ - ui_->pushButtonMDConnection->setEnabled(!authorized); - authorized_ = authorized; -} - -MarketSelectedInfo MarketDataWidget::getCurrentlySelectedInfo() const -{ - if (!ui_->treeViewMarketData) { - return {}; - } - - const QModelIndex index = ui_->treeViewMarketData->selectionModel()->currentIndex(); - return getRowInfo(index); -} - -void MarketDataWidget::onMDConnected() -{ - connected_ = true; - OnMDConnected(); -} - -void MarketDataWidget::onMDDisconnected() -{ - connected_ = false; - OnMDDisconnected(); -} - -void MarketDataWidget::onMDUpdated(bs::network::Asset::Type assetType - , const QString& security, const bs::network::MDFields& fields) -{ - marketDataModel_->onMDUpdated(assetType, security, fields); -} - -void MarketDataWidget::onEnvConfig(int value) -{ - envConf_ = static_cast(value); -} - -void MarketDataWidget::onMDRejected(const std::string &security, const std::string &reason) -{ - if (security.empty()) { - return; - } - bs::network::MDFields mdFields = { { bs::network::MDField::Reject, 0, QString::fromStdString(reason) } }; - marketDataModel_->onMDUpdated(bs::network::Asset::Undefined, QString::fromStdString(security), mdFields); -} - -void MarketDataWidget::onRowClicked(const QModelIndex& index) -{ - if (!filteredView_ || !index.isValid()) { - return; - } - - // Tab clicked - if (!index.parent().isValid()) { - emit MDHeaderClicked(); - return; - } - - MarketSelectedInfo selectedInfo = getRowInfo(index); - - switch (static_cast(index.column())) - { - case MarketDataModel::MarketDataColumns::BidPrice: { - emit BidClicked(selectedInfo); - break; - } - case MarketDataModel::MarketDataColumns::OfferPrice: { - emit AskClicked(selectedInfo); - break; - } - default: { - emit CurrencySelected(selectedInfo); - break; - } - } -} - -void MarketDataWidget::onSelectionChanged(const QModelIndex ¤t, const QModelIndex &) -{ - auto sourceIndex = mdSortFilterModel_->index(current.row(), - current.column(), current.parent()); - - onRowClicked(sourceIndex); -} - -void MarketDataWidget::resizeAndExpand() -{ - ui_->treeViewMarketData->expandAll(); - ui_->treeViewMarketData->resizeColumnToContents(0); - ui_->treeViewMarketData->header()->resizeSection(static_cast(MarketDataModel::MarketDataColumns::EmptyColumn), - EMPTY_COLUMN_WIDTH); -} - -void MarketDataWidget::onHeaderStateChanged(bool state) -{ - filteredView_ = state; - marketDataModel_->setHeaderData(0, Qt::Horizontal, state ? tr("Filtered view") : tr("Visibility selection")); - ui_->treeViewMarketData->resizeColumnToContents(0); - ui_->treeViewMarketData->header()->resizeSection(static_cast(MarketDataModel::MarketDataColumns::EmptyColumn), - EMPTY_COLUMN_WIDTH); - - if (state && (appSettings_ != nullptr)) { - const auto settings = marketDataModel_->getVisibilitySettings(); - appSettings_->set(settingVisibility_, settings); - } -} diff --git a/BlockSettleUILib/Trading/MarketDataWidget.h b/BlockSettleUILib/Trading/MarketDataWidget.h deleted file mode 100644 index 1e3e00485..000000000 --- a/BlockSettleUILib/Trading/MarketDataWidget.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __MARKET_DATA_WIDGET_H__ -#define __MARKET_DATA_WIDGET_H__ - -#include -#include -#include -#include "ApplicationSettings.h" -#include "CommonTypes.h" - - -namespace Ui { - class MarketDataWidget; -}; - -class MarketDataModel; -class MarketDataProvider; -class MDCallbacksQt; -class MDSortFilterProxyModel; -class MDHeader; -class TreeViewWithEnterKey; - -struct MarketSelectedInfo { - QString productGroup_; - QString currencyPair_; - QString bidPrice_; - QString offerPrice_; - - bool isValid() const; -}; - -Q_DECLARE_METATYPE(MarketSelectedInfo); - -class MarketDataWidget : public QWidget -{ -Q_OBJECT - -public: - MarketDataWidget(QWidget* parent = nullptr); - ~MarketDataWidget() override; - - [[deprecated]] void init(const std::shared_ptr &appSettings, ApplicationSettings::Setting paramVis - , const std::shared_ptr &, const std::shared_ptr &); - - TreeViewWithEnterKey* view() const; - - void setAuthorized(bool authorized); - MarketSelectedInfo getCurrentlySelectedInfo() const; - - void onMDConnected(); - void onMDDisconnected(); - void onMDUpdated(bs::network::Asset::Type, const QString& security - , const bs::network::MDFields&); - void onEnvConfig(int); - -signals: - void CurrencySelected(const MarketSelectedInfo& selectedInfo); - void AskClicked(const MarketSelectedInfo& selectedInfo); - void BidClicked(const MarketSelectedInfo& selectedInfo); - void MDHeaderClicked(); - void clicked(); - void needMdConnection(ApplicationSettings::EnvConfiguration); - void needMdDisconnect(); - -private slots: - void resizeAndExpand(); - void onHeaderStateChanged(bool state); - void onRowClicked(const QModelIndex& index); - void onSelectionChanged(const QModelIndex &, const QModelIndex &); - void onMDRejected(const std::string &security, const std::string &reason); - - void onLoadingNetworkSettings(); - - void OnMDConnecting(); - void OnMDConnected(); - void OnMDDisconnecting(); - void OnMDDisconnected(); - - void ChangeMDSubscriptionState(); - -protected: - MarketSelectedInfo getRowInfo(const QModelIndex& index) const; - -private: - std::unique_ptr ui_; - MarketDataModel* marketDataModel_{ nullptr }; - MDSortFilterProxyModel* mdSortFilterModel_{ nullptr }; - std::shared_ptr appSettings_; - ApplicationSettings::Setting settingVisibility_; - std::shared_ptr mdHeader_; - bool filteredView_ = true; - std::shared_ptr mdProvider_; - bool authorized_{ false }; - bool connected_{ false }; - ApplicationSettings::EnvConfiguration envConf_{ ApplicationSettings::EnvConfiguration::Unknown }; -}; - -#include -#include -#include -#include - -class MDHeader : public QHeaderView -{ - Q_OBJECT -public: - MDHeader(Qt::Orientation orient, QWidget *parent = nullptr) : QHeaderView(orient, parent) {} - -protected: - void paintSection(QPainter *painter, const QRect &rect, int logIndex) const override { - painter->save(); - QHeaderView::paintSection(painter, rect, logIndex); - painter->restore(); - - if (logIndex == 0) { - QStyleOptionButton option; - const QSize ch = checkboxSizeHint(); - option.rect = QRect(2, (height() - ch.height()) / 2, ch.width(), ch.height()); - option.state = QStyle::State_Enabled; - option.state |= state_ ? QStyle::State_On : QStyle::State_Off; - - style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); - } - } - - QSize sectionSizeFromContents(int logicalIndex) const override - { - if (logicalIndex == 0) { - const QSize orig = QHeaderView::sectionSizeFromContents(logicalIndex); - const QSize checkbox = checkboxSizeHint(); - - return QSize(orig.width() + checkbox.width() + 4, - qMax(orig.height(), checkbox.height() + 4)); - } else { - return QHeaderView::sectionSizeFromContents(logicalIndex); - } - } - - void mousePressEvent(QMouseEvent *event) override { - if (QRect(0, 0, checkboxSizeHint().width() + 4, height()).contains(event->x(), event->y())) { - state_ = !state_; - emit stateChanged(state_); - update(); - } - else { - QHeaderView::mousePressEvent(event); - } - } - -private: - QSize checkboxSizeHint() const - { - QStyleOptionButton opt; - return style()->subElementRect(QStyle::SE_CheckBoxIndicator, &opt).size(); - } - -private: - bool state_ = true; - -signals: - void stateChanged(bool); -}; - - -#endif // __MARKET_DATA_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/MarketDataWidget.ui b/BlockSettleUILib/Trading/MarketDataWidget.ui deleted file mode 100644 index 92c4f1890..000000000 --- a/BlockSettleUILib/Trading/MarketDataWidget.ui +++ /dev/null @@ -1,159 +0,0 @@ - - - - MarketDataWidget - - - - 0 - 0 - 629 - 240 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - Market Data - - - true - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Subscribe - - - - - - - - - - QAbstractItemView::NoEditTriggers - - - true - - - true - - - true - - - - - - - - TreeViewWithEnterKey - QTreeView -
TreeViewWithEnterKey.h
-
-
- - -
diff --git a/BlockSettleUILib/Trading/OtcClient.cpp b/BlockSettleUILib/Trading/OtcClient.cpp deleted file mode 100644 index d4c88ed60..000000000 --- a/BlockSettleUILib/Trading/OtcClient.cpp +++ /dev/null @@ -1,1837 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OtcClient.h" - -#include -#include -#include - -#include - -#include "AddressVerificator.h" -#include "AuthAddressManager.h" -#include "BtcUtils.h" -#include "CommonTypes.h" -#include "EncryptionUtils.h" -#include "OfflineSigner.h" -#include "ProtobufUtils.h" -#include "TradesUtils.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDLeaf.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "ApplicationSettings.h" - -#include "bs_proxy_terminal_pb.pb.h" -#include "otc.pb.h" - -using namespace Blocksettle::Communication::Otc; -using namespace Blocksettle::Communication; -using namespace bs::network; -using namespace bs::network::otc; -using namespace bs::sync; - -struct OtcClientDeal -{ - bs::network::otc::Side side{}; - - std::string hdWalletId; - std::string settlementId; - bs::Address settlementAddr; - - bs::core::wallet::TXSignRequest payin; - std::string payinState; - bs::core::wallet::TXSignRequest payout; - - bs::signer::RequestId payinReqId{}; - bs::signer::RequestId payoutReqId{}; - - BinaryData unsignedPayinHash; - - BinaryData signedTx; - - bs::Address ourAuthAddress; - BinaryData cpPubKey; - - int64_t amount{}; - int64_t fee{}; - int64_t price{}; - - bool success{false}; - std::string errorMsg; - - std::unique_ptr addressVerificator; - - bs::network::otc::PeerPtr peer; - ValidityHandle peerHandle; - - bs::Address cpAuthAddress() const - { - return bs::Address::fromPubKey(cpPubKey, AddressEntryType_P2WPKH); - } - - bs::Address authAddress(bool forSeller) const - { - const bool weSell = (side == bs::network::otc::Side::Sell); - if (forSeller == weSell) { - return ourAuthAddress; - } else { - return cpAuthAddress(); - } - } - - bool isRequestor() const { return side == bs::network::otc::Side::Sell; } - bs::Address requestorAuthAddress() const { return authAddress(true); } - bs::Address responderAuthAddress() const { return authAddress(false); } - - static OtcClientDeal error(const std::string &msg) - { - OtcClientDeal result; - result.errorMsg = msg; - return result; - } -}; - -namespace { - - const int kContactIdSize = 12; - - const int kSettlementIdHexSize = 64; - const int kTxHashSize = 32; - const int kPubKeySize = 33; - - // Normally pay-in/pay-out timeout is detected using server's status update. - // Use some delay to detect networking problems locally to prevent race. - const auto kLocalTimeoutDelay = std::chrono::seconds(5); - - const auto kStartOtcTimeout = std::chrono::seconds(10); - - bs::sync::PasswordDialogData toPasswordDialogData(const OtcClientDeal &deal - , const bs::core::wallet::TXSignRequest &signRequest - , QDateTime timestamp, bool expandTxInfo) - { - double price = fromCents(deal.price); - - QString qtyProd = UiUtils::XbtCurrency; - QString fxProd = QString::fromStdString("EUR"); - - bs::sync::PasswordDialogData dialogData; - - dialogData.setValue(PasswordDialogData::Market, "XBT"); - - dialogData.setValue(PasswordDialogData::ProductGroup, QObject::tr(bs::network::Asset::toString(bs::network::Asset::SpotXBT))); - dialogData.setValue(PasswordDialogData::Security, "XBT/EUR"); - dialogData.setValue(PasswordDialogData::Product, "XBT"); - dialogData.setValue(PasswordDialogData::FxProduct, fxProd); - - dialogData.setValue(PasswordDialogData::Side, QObject::tr(bs::network::Side::toString(bs::network::Side::Type(deal.side)))); - dialogData.setValue(PasswordDialogData::Price, UiUtils::displayPriceXBT(price)); - - dialogData.setValue(PasswordDialogData::Quantity, qApp->tr("%1 XBT") - .arg(UiUtils::displayAmount(deal.amount))); - - dialogData.setValue(PasswordDialogData::TotalValue, qApp->tr("%1 %2") - .arg(UiUtils::displayAmountForProduct((deal.amount / BTCNumericTypes::BalanceDivider) * price, fxProd, bs::network::Asset::Type::SpotXBT)) - .arg(fxProd)); - - dialogData.setValue(PasswordDialogData::SettlementAddress, deal.settlementAddr.display()); - dialogData.setValue(PasswordDialogData::SettlementId, deal.settlementId); - - dialogData.setValue(PasswordDialogData::IsDealer, !deal.isRequestor()); - dialogData.setValue(PasswordDialogData::RequesterAuthAddress, deal.requestorAuthAddress().display()); - dialogData.setValue(PasswordDialogData::RequesterAuthAddressVerified, deal.isRequestor()); - dialogData.setValue(PasswordDialogData::ResponderAuthAddress, deal.responderAuthAddress().display()); - dialogData.setValue(PasswordDialogData::ResponderAuthAddressVerified, !deal.isRequestor()); - - // Set timestamp that will be used by auth eid server to update timers. - dialogData.setValue(PasswordDialogData::DurationTimestamp, static_cast(timestamp.toSecsSinceEpoch())); - - dialogData.setValue(PasswordDialogData::ExpandTxInfo, expandTxInfo); - - return dialogData; - } - - bs::sync::PasswordDialogData toPasswordDialogDataPayin(const OtcClientDeal &deal - , const bs::core::wallet::TXSignRequest &signRequest - , QDateTime timestamp, bool expandTxInfo) - { - auto dialogData = toPasswordDialogData(deal, signRequest, timestamp, expandTxInfo); - dialogData.setValue(PasswordDialogData::SettlementPayInVisible, true); - dialogData.setValue(PasswordDialogData::Title, QObject::tr("Settlement Pay-In")); - dialogData.setValue(PasswordDialogData::DurationTotal, int(std::chrono::duration_cast(otc::payinTimeout()).count())); - dialogData.setValue(PasswordDialogData::DurationLeft, int(std::chrono::duration_cast(otc::payinTimeout()).count())); - return dialogData; - } - - bs::sync::PasswordDialogData toPasswordDialogDataPayout(const OtcClientDeal &deal - , const bs::core::wallet::TXSignRequest &signRequest - , QDateTime timestamp, bool expandTxInfo) - { - auto dialogData = toPasswordDialogData(deal, signRequest, timestamp, expandTxInfo); - dialogData.setValue(PasswordDialogData::SettlementPayOutVisible, true); - dialogData.setValue(PasswordDialogData::Title, QObject::tr("Settlement Pay-Out")); - dialogData.setValue(PasswordDialogData::DurationTotal, int(std::chrono::duration_cast(otc::payoutTimeout()).count())); - dialogData.setValue(PasswordDialogData::DurationLeft, int(std::chrono::duration_cast(otc::payoutTimeout()).count())); - return dialogData; - } - - bool isValidOffer(const ContactMessage_Offer &offer) - { - return offer.price() > 0 && offer.amount() > 0; - } - - void copyOffer(const Offer &src, ContactMessage_Offer *dst) - { - dst->set_price(src.price); - dst->set_amount(src.amount); - } - - void copyRange(const otc::Range &src, Otc::Range *dst) - { - dst->set_lower(src.lower); - dst->set_upper(src.upper); - } - - void copyRange(const Otc::Range&src, otc::Range *dst) - { - dst->lower = src.lower(); - dst->upper = src.upper(); - } - - PeerPtr findPeer(std::unordered_map> &map, const std::string &contactId) - { - auto it = map.find(contactId); - return it == map.end() ? nullptr : it->second; - } - -} // namespace - -OtcClient::OtcClient(const std::shared_ptr &logger - , const std::shared_ptr &walletsMgr - , const std::shared_ptr &armory - , const std::shared_ptr &signContainer - , const std::shared_ptr &authAddressManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& applicationSettings - , OtcClientParams params - , QObject *parent) - : QObject (parent) - , logger_(logger) - , walletsMgr_(walletsMgr) - , armory_(armory) - , signContainer_(signContainer) - , authAddressManager_(authAddressManager) - , utxoReservationManager_(utxoReservationManager) - , applicationSettings_(applicationSettings) - , params_(std::move(params)) -{} - -OtcClient::~OtcClient() = default; - -PeerPtr OtcClient::contact(const std::string &contactId) -{ - return findPeer(contactMap_, contactId); -} - -PeerPtr OtcClient::request(const std::string &contactId) -{ - return findPeer(requestMap_, contactId); -} - -PeerPtr OtcClient::response(const std::string &contactId) -{ - return findPeer(responseMap_, contactId); -} - -PeerPtr OtcClient::peer(const std::string &contactId, PeerType type) -{ - if (contactId.size() != kContactIdSize) { - SPDLOG_LOGGER_DEBUG(logger_, "unexpected contact requested: {}", contactId); - } - - switch (type) - { - case bs::network::otc::PeerType::Contact: - return contact(contactId); - case bs::network::otc::PeerType::Request: - return request(contactId); - case bs::network::otc::PeerType::Response: - return response(contactId); - } - - assert(false); - return {}; -} - -void OtcClient::setOwnContactId(const std::string &contactId) -{ - ownContactId_ = contactId; -} - -const std::string &OtcClient::ownContactId() const -{ - return ownContactId_; -} - -bool OtcClient::sendQuoteRequest(const QuoteRequest &request) -{ - if (ownRequest_) { - SPDLOG_LOGGER_ERROR(logger_, "own quote request was already sent"); - return false; - } - - if (ownContactId_.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "own contact id is not set"); - return false; - } - - ownRequest_ = std::make_unique(ownContactId_, PeerType::Request); - ownRequest_->request = request; - ownRequest_->request.timestamp = QDateTime::currentDateTime(); - ownRequest_->isOwnRequest = true; - scheduleCloseAfterTimeout(otc::publicRequestTimeout(), ownRequest_); - - Otc::PublicMessage msg; - auto d = msg.mutable_request(); - d->set_sender_side(Otc::Side(request.ourSide)); - d->set_range(Otc::RangeType(request.rangeType)); - emit sendPublicMessage(BinaryData::fromString(msg.SerializeAsString())); - - updatePublicLists(); - - return true; -} - -bool OtcClient::sendQuoteResponse(const PeerPtr &peer, const QuoteResponse "eResponse) -{ - if (peer->state != State::Idle) { - SPDLOG_LOGGER_ERROR(logger_, "can't send offer to '{}', peer should be in Idle state", peer->toString()); - return false; - } - - if (!isSubRange(otc::getRange(peer->request.rangeType), quoteResponse.amount)) { - SPDLOG_LOGGER_ERROR(logger_, "invalid range"); - return false; - } - - changePeerState(peer, State::QuoteSent); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - peer->response = quoteResponse; - - Otc::ContactMessage msg; - auto d = msg.mutable_quote_response(); - d->set_sender_side(Otc::Side(quoteResponse.ourSide)); - copyRange(quoteResponse.price, d->mutable_price()); - copyRange(quoteResponse.amount, d->mutable_amount()); - send(peer, msg); - - updatePublicLists(); - return true; -} - -bool OtcClient::sendOffer(const PeerPtr &peer, const Offer &offer) -{ - SPDLOG_LOGGER_DEBUG(logger_, "send offer to {} (price: {}, amount: {})", peer->toString(), offer.price, offer.amount); - - if (!verifyOffer(offer)) { - SPDLOG_LOGGER_ERROR(logger_, "invalid offer details"); - return false; - } - - auto settlementLeaf = findSettlementLeaf(offer.authAddress); - if (!settlementLeaf) { - SPDLOG_LOGGER_ERROR(logger_, "can't find settlement leaf with address '{}'", offer.authAddress); - return false; - } - - settlementLeaf->getRootPubkey([this, logger = logger_, peer, offer, handle = peer->validityFlag.handle()] - (const SecureBinaryData &ourPubKey) - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - if (ourPubKey.getSize() != kPubKeySize) { - SPDLOG_LOGGER_ERROR(logger_, "invalid auth address root public key"); - return; - } - - switch (peer->type) { - case PeerType::Contact: - if (peer->state != State::Idle) { - SPDLOG_LOGGER_ERROR(logger_, "can't send offer to '{}', peer should be in idle state", peer->toString()); - return; - } - break; - case PeerType::Request: - SPDLOG_LOGGER_ERROR(logger_, "can't send offer to '{}'", peer->toString()); - return; - case PeerType::Response: - if (peer->state != State::QuoteRecv) { - SPDLOG_LOGGER_ERROR(logger_, "can't send offer to '{}', peer should be in QuoteRecv state", peer->toString()); - return; - } - break; - } - - peer->offer = offer; - peer->ourAuthPubKey = ourPubKey; - peer->isOurSideSentOffer = true; - changePeerState(peer, State::OfferSent); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - - ContactMessage msg; - if (offer.ourSide == otc::Side::Buy) { - auto d = msg.mutable_buyer_offers(); - copyOffer(offer, d->mutable_offer()); - d->set_auth_address_buyer(peer->ourAuthPubKey.toBinStr()); - } else { - auto d = msg.mutable_seller_offers(); - copyOffer(offer, d->mutable_offer()); - } - send(peer, msg); - }); - - return true; -} - -bool OtcClient::pullOrReject(const PeerPtr &peer) -{ - if (peer->isOwnRequest) { - assert(peer == ownRequest_); - - SPDLOG_LOGGER_DEBUG(logger_, "pull own quote request"); - ownRequest_.reset(); - - // This will remove everything when we pull public request. - // We could keep current shield and show that our public request was pulled instead. - responseMap_.clear(); - - Otc::PublicMessage msg; - msg.mutable_close(); - emit sendPublicMessage(BinaryData::fromString(msg.SerializeAsString())); - - updatePublicLists(); - return true; - } - - switch (peer->state) { - case State::QuoteSent: - case State::OfferSent: - case State::OfferRecv: { - SPDLOG_LOGGER_DEBUG(logger_, "pull or reject offer from {}", peer->toString()); - - ContactMessage msg; - msg.mutable_close(); - send(peer, msg); - - switch (peer->type) { - case PeerType::Contact: - resetPeerStateToIdle(peer); - break; - case PeerType::Request: - // Keep public request even if we reject it - resetPeerStateToIdle(peer); - // Need to call this as peer would be removed from "sent requests" list - updatePublicLists(); - break; - case PeerType::Response: - // Remove peer from received responses if we reject it - responseMap_.erase(peer->contactId); - updatePublicLists(); - break; - } - - return true; - } - - case State::WaitBuyerSign: - case State::WaitSellerSeal: { - if (peer->state == State::WaitSellerSeal && peer->offer.ourSide == bs::network::otc::Side::Buy) { - SPDLOG_LOGGER_ERROR(logger_, "buyer can't cancel deal while waiting payin sign", peer->toString()); - return false; - } - - auto deal = deals_.at(peer->settlementId).get(); - ProxyTerminalPb::Request request; - auto d = request.mutable_cancel(); - d->set_settlement_id(deal->settlementId); - emit sendPbMessage(request.SerializeAsString()); - - switch (peer->type) { - case PeerType::Request: - requestMap_.erase(peer->contactId); - updatePublicLists(); - break; - case PeerType::Response: - responseMap_.erase(peer->contactId); - updatePublicLists(); - break; - } - - return true; - } - - default: { - SPDLOG_LOGGER_ERROR(logger_, "can't pull offer from '{}'", peer->toString()); - return false; - } - } -} - -void OtcClient::setReservation(const PeerPtr &peer, bs::UtxoReservationToken&& reserv) -{ - reservedTokens_.erase(peer->contactId); - reservedTokens_.insert({ peer->contactId , std::move(reserv) }); -} - -bs::UtxoReservationToken OtcClient::releaseReservation(const bs::network::otc::PeerPtr &peer) -{ - bs::UtxoReservationToken reservation; - auto token = reservedTokens_.find(peer->contactId); - if (token == reservedTokens_.end()) { - return reservation; - } - - reservation = std::move(token->second); - reservedTokens_.erase(token); - return reservation; -} - -bool OtcClient::acceptOffer(const PeerPtr &peer, const bs::network::otc::Offer &offer) -{ - SPDLOG_LOGGER_DEBUG(logger_, "accept offer from {} (price: {}, amount: {})", peer->toString(), offer.price, offer.amount); - - if (!verifyOffer(offer)) { - SPDLOG_LOGGER_ERROR(logger_, "invalid offer details"); - return false; - } - - auto settlementLeaf = findSettlementLeaf(offer.authAddress); - if (!settlementLeaf) { - SPDLOG_LOGGER_ERROR(logger_, "can't find settlement leaf with address '{}'", offer.authAddress); - return false; - } - - settlementLeaf->getRootPubkey([this, offer, peer, handle = peer->validityFlag.handle(), logger = logger_] - (const SecureBinaryData &ourPubKey) - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - if (peer->state != State::OfferRecv) { - SPDLOG_LOGGER_ERROR(logger_, "can't accept offer from '{}', we should be in OfferRecv state", peer->toString()); - return; - } - - if (ourPubKey.getSize() != kPubKeySize) { - SPDLOG_LOGGER_ERROR(logger_, "invalid auth address root public key"); - return; - } - - assert(offer == peer->offer); - - peer->offer = offer; - peer->ourAuthPubKey = ourPubKey; - - if (peer->offer.ourSide == otc::Side::Sell) { - sendSellerAccepts(peer); - return; - } - - // Need to get other details from seller first. - // They should be available from Accept reply. - ContactMessage msg; - auto d = msg.mutable_buyer_accepts(); - copyOffer(offer, d->mutable_offer()); - d->set_auth_address_buyer(peer->ourAuthPubKey.toBinStr()); - send(peer, msg); - - changePeerState(peer, State::WaitPayinInfo); - }); - - return true; -} - -bool OtcClient::updateOffer(const PeerPtr &peer, const Offer &offer) -{ - SPDLOG_LOGGER_DEBUG(logger_, "update offer from {} (price: {}, amount: {})", peer->toString(), offer.price, offer.amount); - - if (!verifyOffer(offer)) { - SPDLOG_LOGGER_ERROR(logger_, "invalid offer details"); - return false; - } - - auto settlementLeaf = findSettlementLeaf(offer.authAddress); - if (!settlementLeaf) { - SPDLOG_LOGGER_ERROR(logger_, "can't find settlement leaf with address '{}'", offer.authAddress); - return false; - } - - settlementLeaf->getRootPubkey([this, offer, peer, handle = peer->validityFlag.handle(), logger = logger_] - (const SecureBinaryData &ourPubKey) - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - if (peer->state != State::OfferRecv) { - SPDLOG_LOGGER_ERROR(logger_, "can't pull offer from '{}', we should be in OfferRecv state", peer->toString()); - return; - } - - if (ourPubKey.getSize() != kPubKeySize) { - SPDLOG_LOGGER_ERROR(logger_, "invalid auth address root public key"); - return; - } - - // Only price could be updated, amount and side must be the same - assert(offer.price != peer->offer.price); - assert(offer.amount == peer->offer.amount); - assert(offer.ourSide == peer->offer.ourSide); - - peer->offer = offer; - peer->ourAuthPubKey = ourPubKey; - - ContactMessage msg; - if (offer.ourSide == otc::Side::Buy) { - auto d = msg.mutable_buyer_offers(); - copyOffer(offer, d->mutable_offer()); - - d->set_auth_address_buyer(peer->ourAuthPubKey.toBinStr()); - } else { - auto d = msg.mutable_seller_offers(); - copyOffer(offer, d->mutable_offer()); - } - send(peer, msg); - - changePeerState(peer, State::OfferSent); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - }); - - return true; -} - -const PeerPtr &OtcClient::ownRequest() const -{ - return ownRequest_; -} - -void OtcClient::contactConnected(const std::string &contactId) -{ - assert(!contact(contactId)); - contactMap_.emplace(contactId, std::make_shared(contactId, PeerType::Contact)); - emit publicUpdated(); -} - -void OtcClient::contactDisconnected(const std::string &contactId) -{ - const auto &peer = contactMap_.at(contactId); - // Do not try to cancel deal waiting pay-in sign (will result in ban) - if (peer->state == State::WaitBuyerSign) { - pullOrReject(peer); - } - contactMap_.erase(contactId); - reservedTokens_.erase(contactId); - emit publicUpdated(); -} - -void OtcClient::processContactMessage(const std::string &contactId, const BinaryData &data) -{ - ContactMessage message; - bool result = message.ParseFromArray(data.getPtr(), int(data.getSize())); - if (!result) { - SPDLOG_LOGGER_ERROR(logger_, "can't parse OTC message"); - return; - } - - bs::network::otc::PeerPtr peer; - switch (message.contact_type()) { - case Otc::CONTACT_TYPE_PRIVATE: - peer = contact(contactId); - if (!peer) { - SPDLOG_LOGGER_ERROR(logger_, "can't find peer '{}'", contactId); - return; - } - - if (peer->state == State::Blacklisted) { - SPDLOG_LOGGER_DEBUG(logger_, "ignoring message from blacklisted peer '{}'", contactId); - return; - } - break; - - case Otc::CONTACT_TYPE_PUBLIC_REQUEST: - if (!ownRequest_) { - SPDLOG_LOGGER_ERROR(logger_, "response is not expected"); - return; - } - - peer = response(contactId); - if (!peer) { - auto result = responseMap_.emplace(contactId, std::make_shared(contactId, PeerType::Response)); - peer = result.first->second; - emit publicUpdated(); - } - break; - - case Otc::CONTACT_TYPE_PUBLIC_RESPONSE: - peer = request(contactId); - if (!peer) { - SPDLOG_LOGGER_ERROR(logger_, "request is not expected"); - return; - } - break; - - default: - SPDLOG_LOGGER_ERROR(logger_, "unknown message type"); - return; - } - - switch (message.data_case()) { - case ContactMessage::kBuyerOffers: - processBuyerOffers(peer, message.buyer_offers()); - return; - case ContactMessage::kSellerOffers: - processSellerOffers(peer, message.seller_offers()); - return; - case ContactMessage::kBuyerAccepts: - processBuyerAccepts(peer, message.buyer_accepts()); - return; - case ContactMessage::kSellerAccepts: - processSellerAccepts(peer, message.seller_accepts()); - return; - case ContactMessage::kBuyerAcks: - processBuyerAcks(peer, message.buyer_acks()); - return; - case ContactMessage::kClose: - processClose(peer, message.close()); - return; - case ContactMessage::kQuoteResponse: - processQuoteResponse(peer, message.quote_response()); - return; - case ContactMessage::DATA_NOT_SET: - blockPeer("unknown or empty OTC message", peer); - return; - } - - SPDLOG_LOGGER_CRITICAL(logger_, "unknown response was detected!"); -} - -void OtcClient::processPbMessage(const ProxyTerminalPb::Response &response) -{ - switch (response.data_case()) { - case ProxyTerminalPb::Response::kStartOtc: - processPbStartOtc(response.start_otc()); - return; - case ProxyTerminalPb::Response::kUpdateOtcState: - processPbUpdateOtcState(response.update_otc_state()); - return; - case ProxyTerminalPb::Response::DATA_NOT_SET: - SPDLOG_LOGGER_ERROR(logger_, "response from PB is invalid"); - return; - default: - // if not processed - not OTC message. not error - break; - } -} - -void OtcClient::processPublicMessage(QDateTime timestamp, const std::string &contactId, const BinaryData &data) -{ - assert(!ownContactId_.empty()); - if (contactId == ownContactId_) { - return; - } - - Otc::PublicMessage msg; - bool result = msg.ParseFromArray(data.getPtr(), int(data.getSize())); - if (!result) { - SPDLOG_LOGGER_ERROR(logger_, "parsing public OTC message failed"); - return; - } - - switch (msg.data_case()) { - case Otc::PublicMessage::kRequest: - processPublicRequest(timestamp, contactId, msg.request()); - return; - case Otc::PublicMessage::kClose: - processPublicClose(timestamp, contactId, msg.close()); - return; - case Otc::PublicMessage::DATA_NOT_SET: - SPDLOG_LOGGER_ERROR(logger_, "invalid public request detected"); - return; - } - - SPDLOG_LOGGER_CRITICAL(logger_, "unknown public message was detected!"); -} - -void OtcClient::onTxSigned(unsigned reqId, BinaryData signedTX - , bs::error::ErrorCode result, const std::string &errorReason) -{ - auto it = signRequestIds_.find(reqId); - if (it == signRequestIds_.end()) { - return; - } - const auto settlementId = std::move(it->second); - signRequestIds_.erase(it); - - auto dealIt = deals_.find(settlementId); - if (dealIt == deals_.end()) { - SPDLOG_LOGGER_ERROR(logger_, "unknown sign request"); - return; - } - OtcClientDeal *deal = dealIt->second.get(); - - if (result == bs::error::ErrorCode::NoError) { - if (deal->payinReqId == reqId) { - SPDLOG_LOGGER_DEBUG(logger_, "pay-in was succesfully signed, settlementId: {}", deal->settlementId); - deal->signedTx = signedTX; - - ProxyTerminalPb::Request request; - auto d = request.mutable_seal_payin_validity(); - d->set_settlement_id(deal->settlementId); - emit sendPbMessage(request.SerializeAsString()); - } - - if (deal->payoutReqId == reqId) { - SPDLOG_LOGGER_DEBUG(logger_, "pay-out was succesfully signed, settlementId: {}", deal->settlementId); - deal->signedTx = signedTX; - trySendSignedTx(deal); - } - return; - } - else { - SPDLOG_LOGGER_ERROR(logger_, "sign error: {}", errorReason); - } - - if (!deal->peerHandle.isValid()) { - SPDLOG_LOGGER_ERROR(logger_, "peer was destroyed"); - return; - } - auto peer = deal->peer; - peer->activeSettlementId.clear(); - pullOrReject(peer); -} - -void OtcClient::processBuyerOffers(const PeerPtr &peer, const ContactMessage_BuyerOffers &msg) -{ - if (!isValidOffer(msg.offer())) { - blockPeer("invalid offer", peer); - return; - } - - if (msg.auth_address_buyer().size() != kPubKeySize) { - blockPeer("invalid auth_address_buyer in buyer offer", peer); - return; - } - peer->authPubKey = BinaryData::fromString(msg.auth_address_buyer()); - - switch (peer->state) { - case State::Idle: - peer->offer.ourSide = otc::Side::Sell; - peer->offer.amount = msg.offer().amount(); - peer->offer.price = msg.offer().price(); - changePeerState(peer, State::OfferRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - break; - - case State::QuoteSent: - peer->offer.ourSide = otc::Side::Sell; - peer->offer.amount = msg.offer().amount(); - peer->offer.price = msg.offer().price(); - changePeerState(peer, State::OfferRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - break; - - case State::QuoteRecv: - SPDLOG_LOGGER_ERROR(logger_, "not implemented"); - return; - - case State::OfferSent: - if (peer->offer.ourSide != otc::Side::Sell) { - blockPeer("unexpected side in counter-offer", peer); - return; - } - if (peer->offer.amount != msg.offer().amount()) { - blockPeer("invalid amount in counter-offer", peer); - return; - } - - peer->offer.price = msg.offer().price(); - changePeerState(peer, State::OfferRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - break; - - case State::OfferRecv: - case State::WaitPayinInfo: - case State::SentPayinInfo: - blockPeer("unexpected offer", peer); - break; - - case State::Blacklisted: - assert(false); - break; - } -} - -void OtcClient::processSellerOffers(const PeerPtr &peer, const ContactMessage_SellerOffers &msg) -{ - if (!isValidOffer(msg.offer())) { - blockPeer("invalid offer", peer); - return; - } - - switch (peer->state) { - case State::Idle: - peer->offer.ourSide = otc::Side::Buy; - peer->offer.amount = msg.offer().amount(); - peer->offer.price = msg.offer().price(); - changePeerState(peer, State::OfferRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - break; - - case State::QuoteSent: - peer->offer.ourSide = otc::Side::Buy; - peer->offer.amount = msg.offer().amount(); - peer->offer.price = msg.offer().price(); - changePeerState(peer, State::OfferRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - break; - - case State::QuoteRecv: - SPDLOG_LOGGER_ERROR(logger_, "not implemented"); - return; - - case State::OfferSent: - if (peer->offer.ourSide != otc::Side::Buy) { - blockPeer("unexpected side in counter-offer", peer); - return; - } - if (peer->offer.amount != msg.offer().amount()) { - blockPeer("invalid amount in counter-offer", peer); - return; - } - - peer->offer.price = msg.offer().price(); - changePeerState(peer, State::OfferRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - break; - - case State::OfferRecv: - case State::WaitPayinInfo: - case State::SentPayinInfo: - blockPeer("unexpected offer", peer); - break; - - case State::Blacklisted: - assert(false); - break; - } -} - -void OtcClient::processBuyerAccepts(const PeerPtr &peer, const ContactMessage_BuyerAccepts &msg) -{ - if (peer->state != State::OfferSent || peer->offer.ourSide != otc::Side::Sell) { - blockPeer("unexpected BuyerAccepts message, should be in OfferSent state and be seller", peer); - return; - } - - if (msg.offer().price() != peer->offer.price || msg.offer().amount() != peer->offer.amount) { - blockPeer("wrong accepted price or amount in BuyerAccepts message", peer); - return; - } - - if (msg.auth_address_buyer().size() != kPubKeySize) { - blockPeer("invalid auth_address in BuyerAccepts message", peer); - return; - } - peer->authPubKey = BinaryData::fromString(msg.auth_address_buyer()); - - sendSellerAccepts(peer); -} - -void OtcClient::processSellerAccepts(const PeerPtr &peer, const ContactMessage_SellerAccepts &msg) -{ - if (msg.offer().price() != peer->offer.price || msg.offer().amount() != peer->offer.amount) { - blockPeer("wrong accepted price or amount in SellerAccepts message", peer); - return; - } - - if (msg.settlement_id().size() != kSettlementIdHexSize) { - blockPeer("invalid settlement_id in SellerAccepts message", peer); - return; - } - const auto &settlementId = msg.settlement_id(); - - if (msg.auth_address_seller().size() != kPubKeySize) { - blockPeer("invalid auth_address_seller in SellerAccepts message", peer); - return; - } - peer->authPubKey = BinaryData::fromString(msg.auth_address_seller()); - - if (msg.payin_tx_id().size() != kTxHashSize) { - blockPeer("invalid payin_tx_id in SellerAccepts message", peer); - return; - } - peer->payinTxIdFromSeller = BinaryData::fromString(msg.payin_tx_id()); - - createBuyerRequest(settlementId, peer, [this, peer, settlementId, offer = peer->offer - , handle = peer->validityFlag.handle(), logger = logger_] (OtcClientDeal &&deal) - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - if (!deal.success) { - SPDLOG_LOGGER_ERROR(logger_, "creating pay-out sign request fails: {}", deal.errorMsg); - return; - } - - if ((peer->state != State::WaitPayinInfo && peer->state != State::OfferSent) || peer->offer.ourSide != otc::Side::Buy) { - blockPeer("unexpected SellerAccepts message, should be in WaitPayinInfo or OfferSent state and be buyer", peer); - return; - } - - if (offer != peer->offer) { - SPDLOG_LOGGER_ERROR(logger_, "offer details have changed unexpectedly"); - return; - } - - auto unsignedPayout = deal.payout.serializeState(); - - deal.peer = peer; - deal.peerHandle = std::move(handle); - deals_.emplace(settlementId, std::make_unique(std::move(deal))); - - ContactMessage msg; - msg.mutable_buyer_acks()->set_settlement_id(settlementId); - send(peer, msg); - - changePeerState(peer, otc::State::WaitVerification); - - ProxyTerminalPb::Request request; - auto d = request.mutable_verify_otc(); - d->set_is_seller(false); - d->set_price(peer->offer.price); - d->set_amount(peer->offer.amount); - d->set_settlement_id(settlementId); - d->set_auth_address_buyer(peer->ourAuthPubKey.toBinStr()); - d->set_auth_address_seller(peer->authPubKey.toBinStr()); - d->set_unsigned_tx(unsignedPayout.SerializeAsString()); - d->set_payin_tx_hash(peer->payinTxIdFromSeller.toBinStr()); - d->set_chat_id_buyer(ownContactId_); - d->set_chat_id_seller(peer->contactId); - emit sendPbMessage(request.SerializeAsString()); - }); -} - -void OtcClient::processBuyerAcks(const PeerPtr &peer, const ContactMessage_BuyerAcks &msg) -{ - if (peer->state != State::SentPayinInfo || peer->offer.ourSide != otc::Side::Sell) { - blockPeer("unexpected BuyerAcks message, should be in SentPayinInfo state and be seller", peer); - return; - } - - const auto &settlementId = msg.settlement_id(); - - const auto it = deals_.find(settlementId); - if (it == deals_.end()) { - SPDLOG_LOGGER_ERROR(logger_, "unknown settlementId from BuyerAcks: {}", settlementId); - return; - } - const auto &deal = it->second; - assert(deal->success); - - changePeerState(peer, otc::State::WaitVerification); - - ProxyTerminalPb::Request request; - - auto d = request.mutable_verify_otc(); - d->set_is_seller(true); - d->set_price(peer->offer.price); - d->set_amount(peer->offer.amount); - d->set_settlement_id(settlementId); - d->set_auth_address_buyer(peer->authPubKey.toBinStr()); - d->set_auth_address_seller(peer->ourAuthPubKey.toBinStr()); - d->set_unsigned_tx(deal->payinState); - - d->set_payin_tx_hash(deal->unsignedPayinHash.toBinStr()); - - d->set_chat_id_seller(ownContactId_); - d->set_chat_id_buyer(peer->contactId); - emit sendPbMessage(request.SerializeAsString()); -} - -void OtcClient::processClose(const PeerPtr &peer, const ContactMessage_Close &msg) -{ - switch (peer->state) { - case State::QuoteSent: - case State::QuoteRecv: - case State::OfferSent: - case State::OfferRecv: - case State::WaitPayinInfo: { - if (peer->type == PeerType::Response) { - SPDLOG_LOGGER_DEBUG(logger_, "remove active response because peer have sent close message"); - responseMap_.erase(peer->contactId); - updatePublicLists(); - return; - } - - resetPeerStateToIdle(peer); - if (peer->type != PeerType::Contact) { - updatePublicLists(); - } - break; - } - - case State::Idle: - // Could happen if both sides press cancel at the same time - break; - - case State::WaitVerification: - case State::WaitBuyerSign: - case State::WaitSellerSeal: - case State::WaitSellerSign: - // After sending verification details both sides should use PB only - SPDLOG_LOGGER_DEBUG(logger_, "ignoring unexpected close request"); - break; - - case State::SentPayinInfo: { - blockPeer("unexpected close", peer); - break; - } - - case State::Blacklisted: { - assert(false); - break; - } - } -} - -void OtcClient::processQuoteResponse(const PeerPtr &peer, const ContactMessage_QuoteResponse &msg) -{ - if (!ownRequest_) { - SPDLOG_LOGGER_ERROR(logger_, "own request is not available"); - return; - } - - if (peer->type != PeerType::Response) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected request"); - return; - } - - changePeerState(peer, State::QuoteRecv); - scheduleCloseAfterTimeout(otc::negotiationTimeout(), peer); - peer->response.ourSide = otc::switchSide(otc::Side(msg.sender_side())); - copyRange(msg.price(), &peer->response.price); - copyRange(msg.amount(), &peer->response.amount); - - updatePublicLists(); -} - -void OtcClient::processPublicRequest(QDateTime timestamp, const std::string &contactId, const PublicMessage_Request &msg) -{ - auto range = otc::RangeType(msg.range()); - if (range < otc::firstRangeValue(params_.env) || range > otc::lastRangeValue(params_.env)) { - SPDLOG_LOGGER_ERROR(logger_, "invalid range"); - return; - } - - requestMap_.erase(contactId); - auto result = requestMap_.emplace(contactId, std::make_shared(contactId, PeerType::Request)); - const auto &peer = result.first->second; - - peer->request.ourSide = otc::switchSide(otc::Side(msg.sender_side())); - peer->request.rangeType = range; - peer->request.timestamp = timestamp; - - updatePublicLists(); -} - -void OtcClient::processPublicClose(QDateTime timestamp, const std::string &contactId, const PublicMessage_Close &msg) -{ - requestMap_.erase(contactId); - - updatePublicLists(); -} - -void OtcClient::processPbStartOtc(const ProxyTerminalPb::Response_StartOtc &response) -{ - auto it = waitSettlementIds_.find(response.request_id()); - if (it == waitSettlementIds_.end()) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected StartOtc response: can't find request"); - return; - } - auto handle = std::move(it->second.handle); - auto peer = it->second.peer; - waitSettlementIds_.erase(it); - - const auto &settlementId = response.settlement_id(); - - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger_, "peer was destroyed"); - return; - } - - createSellerRequest(settlementId, peer, [this, peer, settlementId, offer = peer->offer - , handle = peer->validityFlag.handle(), logger = logger_](OtcClientDeal &&deal) - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - if (!deal.success) { - SPDLOG_LOGGER_ERROR(logger_, "creating pay-in sign request fails: {}", deal.errorMsg); - return; - } - - if (offer.ourSide != otc::Side::Sell) { - SPDLOG_LOGGER_ERROR(logger_, "can't send pay-in info, wrong side"); - return; - } - - if (offer != peer->offer) { - SPDLOG_LOGGER_ERROR(logger_, "offer details have changed unexpectedly"); - return; - } - - ContactMessage msg; - auto d = msg.mutable_seller_accepts(); - copyOffer(peer->offer, d->mutable_offer()); - d->set_settlement_id(settlementId); - d->set_auth_address_seller(peer->ourAuthPubKey.toBinStr()); - d->set_payin_tx_id(deal.unsignedPayinHash.toBinStr()); - d->set_payin_tx(deal.payin.serializeState().SerializeAsString()); - send(peer, msg); - - deal.peer = peer; - deal.peerHandle = std::move(handle); - deal.payinState = deal.payin.serializeState().SerializeAsString(); - deals_.emplace(settlementId, std::make_unique(std::move(deal))); - - changePeerState(peer, State::SentPayinInfo); - }); -} - -void OtcClient::processPbUpdateOtcState(const ProxyTerminalPb::Response_UpdateOtcState &response) -{ - auto it = deals_.find(response.settlement_id()); - if (it == deals_.end()) { - SPDLOG_LOGGER_ERROR(logger_, "unknown settlementId in UpdateOtcState message"); - return; - } - auto deal = it->second.get(); - - switch (response.state()) { - case ProxyTerminalPb::OTC_STATE_WAIT_SELLER_SIGN: - if (deal->side == otc::Side::Sell) { - trySendSignedTx(deal); - } - break; - default: - break; - } - - if (!deal->peerHandle.isValid()) { - SPDLOG_LOGGER_ERROR(logger_, "peer was destroyed"); - return; - } - auto peer = deal->peer; - - SPDLOG_LOGGER_DEBUG(logger_, "change OTC trade state to: {}, settlementId: {}" - , response.settlement_id(), ProxyTerminalPb::OtcState_Name(response.state())); - - auto timestamp = QDateTime::fromMSecsSinceEpoch(response.timestamp_ms()); - - switch (response.state()) { - case ProxyTerminalPb::OTC_STATE_FAILED: { - switch (peer->state) { - case State::WaitVerification: - case State::WaitBuyerSign: - case State::WaitSellerSeal: - case State::WaitSellerSign: - break; - default: - SPDLOG_LOGGER_ERROR(logger_, "unexpected state update request"); - return; - } - - SPDLOG_LOGGER_ERROR(logger_, "OTC trade failed: {}", response.error_msg()); - emit peerError(peer, PeerErrorType::Rejected, &response.error_msg()); - - resetPeerStateToIdle(peer); - break; - } - - case ProxyTerminalPb::OTC_STATE_WAIT_BUYER_SIGN: { - if (peer->state != State::WaitVerification) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected state update request"); - return; - } - - if (deal->side == otc::Side::Buy) { - assert(deal->payout.isValid()); - - bs::core::wallet::SettlementData settlData; - settlData.settlementId = BinaryData::CreateFromHex(deal->settlementId); - settlData.cpPublicKey = deal->cpPubKey; - settlData.ownKeyFirst = true; - - auto payoutInfo = toPasswordDialogDataPayout(*deal, deal->payout, timestamp, expandTxDialog()); - auto reqId = signContainer_->signSettlementPayoutTXRequest(deal->payout, settlData, payoutInfo); - signRequestIds_[reqId] = deal->settlementId; - deal->payoutReqId = reqId; - verifyAuthAddresses(deal); - peer->activeSettlementId = BinaryData::fromString(deal->settlementId); - } - - changePeerState(peer, State::WaitBuyerSign); - - QTimer::singleShot(payoutTimeout() + kLocalTimeoutDelay, this, [this, peer, handle = peer->validityFlag.handle()] { - if (!handle.isValid() || peer->state != State::WaitBuyerSign) { - return; - } - emit peerError(peer, PeerErrorType::Timeout, nullptr); - resetPeerStateToIdle(peer); - }); - break; - } - - case ProxyTerminalPb::OTC_STATE_WAIT_SELLER_SEAL: { - if (peer->state != State::WaitBuyerSign) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected state update request"); - return; - } - - if (deal->side == otc::Side::Sell) { - assert(deal->payin.isValid()); - - const auto &authLeaf = walletsMgr_->getAuthWallet(); - if (!authLeaf) { - SPDLOG_LOGGER_ERROR(logger_, "can't find auth wallet"); - return; - } - signContainer_->setSettlCP(authLeaf->walletId(), deal->unsignedPayinHash, BinaryData::CreateFromHex(deal->settlementId), deal->cpPubKey); - signContainer_->setSettlAuthAddr(authLeaf->walletId(), BinaryData::CreateFromHex(deal->settlementId), deal->ourAuthAddress); - - auto payinInfo = toPasswordDialogDataPayin(*deal, deal->payin, timestamp, expandTxDialog()); - auto reqId = signContainer_->signSettlementTXRequest(deal->payin, payinInfo); - signRequestIds_[reqId] = deal->settlementId; - deal->payinReqId = reqId; - verifyAuthAddresses(deal); - peer->activeSettlementId = BinaryData::fromString(deal->settlementId); - } - - changePeerState(peer, State::WaitSellerSeal); - - QTimer::singleShot(payinTimeout() + kLocalTimeoutDelay, this, [this, peer, handle = peer->validityFlag.handle()] { - if (!handle.isValid() || peer->state != State::WaitSellerSeal) { - return; - } - emit peerError(peer, PeerErrorType::Timeout, nullptr); - resetPeerStateToIdle(peer); - }); - break; - } - - case ProxyTerminalPb::OTC_STATE_WAIT_SELLER_SIGN: { - if (peer->state != State::WaitSellerSeal) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected state update request"); - return; - } - changePeerState(peer, State::WaitSellerSign); - break; - } - - case ProxyTerminalPb::OTC_STATE_CANCELLED: { - if (peer->state != State::WaitBuyerSign && peer->state != State::WaitSellerSeal) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected state update request"); - return; - } - emit peerError(peer, PeerErrorType::Canceled, nullptr); - switch (peer->type) { - case PeerType::Contact: - resetPeerStateToIdle(peer); - break; - case PeerType::Request: - requestMap_.erase(peer->contactId); - updatePublicLists(); - break; - case PeerType::Response: - responseMap_.erase(peer->contactId); - updatePublicLists(); - break; - } - - break; - } - - case ProxyTerminalPb::OTC_STATE_SUCCEED: { - if (peer->state != State::WaitSellerSign) { - SPDLOG_LOGGER_ERROR(logger_, "unexpected state update request"); - return; - } - - resetPeerStateToIdle(peer); - break; - } - - default: { - SPDLOG_LOGGER_ERROR(logger_, "unexpected new state value: {}", int(response.state())); - break; - } - } -} - -bool OtcClient::verifyOffer(const Offer &offer) const -{ - assert(offer.ourSide != otc::Side::Unknown); - assert(offer.amount > 0); - assert(offer.price > 0); - assert(!offer.hdWalletId.empty()); - assert(bs::Address::fromAddressString(offer.authAddress).isValid()); - - if (!offer.recvAddress.empty()) { - auto offerRecvAddress = bs::Address::fromAddressString(offer.recvAddress); - assert(offerRecvAddress.isValid()); - auto wallet = walletsMgr_->getWalletByAddress(offerRecvAddress); - - if (!wallet || wallet->type() != bs::core::wallet::Type::Bitcoin) { - SPDLOG_LOGGER_CRITICAL(logger_, "invalid receiving address selected for OTC, selected address: {}", offer.recvAddress); - return false; - } - - auto hdWalletRecv = walletsMgr_->getHDRootForLeaf(wallet->walletId()); - if (!hdWalletRecv || hdWalletRecv->walletId() != offer.hdWalletId) { - SPDLOG_LOGGER_CRITICAL(logger_, "invalid receiving address selected for OTC (invalid hd wallet), selected address: {}", offer.recvAddress); - return false; - } - } - - if (!walletsMgr_->getHDWalletById(offer.hdWalletId)) { - SPDLOG_LOGGER_ERROR(logger_, "hd wallet not found: {}", offer.hdWalletId); - return false; - } - if (offer.ourSide == bs::network::otc::Side::Buy && !offer.inputs.empty()) { - SPDLOG_LOGGER_CRITICAL(logger_, "inputs must be empty for sell"); - return false; - } - for (const auto &input : offer.inputs) { - auto address = bs::Address::fromUTXO(input); - auto wallet = walletsMgr_->getWalletByAddress(address); - if (!wallet) { - SPDLOG_LOGGER_CRITICAL(logger_, "wallet not found for UTXO from address: {}", address.display()); - return false; - } - const auto hdWalletId = walletsMgr_->getHDRootForLeaf(wallet->walletId())->walletId(); - if (hdWalletId != offer.hdWalletId) { - SPDLOG_LOGGER_CRITICAL(logger_, "invalid UTXO, hdWalletId: {}, expected hdWalletId: {}" - , hdWalletId, offer.hdWalletId); - return false; - } - } - - // utxoReservationManager_ is not available in unit tests - if (utxoReservationManager_) { - auto minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); - if (offer.amount < static_cast(minXbtAmount.GetValue())) { - SPDLOG_LOGGER_ERROR(logger_, "amount is too low: {}, min amount: {}", offer.amount, minXbtAmount.GetValue()); - return false; - } - } - - return true; -} - -void OtcClient::blockPeer(const std::string &reason, const PeerPtr &peer) -{ - SPDLOG_LOGGER_ERROR(logger_, "block broken peer '{}': {}", peer->toString(), reason); - changePeerState(peer, State::Blacklisted); - emit peerUpdated(peer); -} - -void OtcClient::send(const PeerPtr &peer, ContactMessage &msg) -{ - assert(!peer->contactId.empty()); - msg.set_contact_type(Otc::ContactType(peer->type)); - emit sendContactMessage(peer->contactId, BinaryData::fromString(msg.SerializeAsString())); -} - -void OtcClient::createSellerRequest(const std::string &settlementId, const PeerPtr &peer, const OtcClientDealCb &cb) -{ - assert(peer->authPubKey.getSize() == kPubKeySize); - assert(settlementId.size() == kSettlementIdHexSize); - assert(!peer->offer.authAddress.empty()); - assert(peer->offer.ourSide == bs::network::otc::Side::Sell); - - auto primaryHdWallet = walletsMgr_->getPrimaryWallet(); - if (!primaryHdWallet) { - cb(OtcClientDeal::error("can't find primary wallet")); - return; - } - - auto targetHdWallet = walletsMgr_->getHDWalletById(peer->offer.hdWalletId); - if (!targetHdWallet) { - cb(OtcClientDeal::error(fmt::format("can't find wallet: {}", peer->offer.hdWalletId))); - return; - } - - auto group = targetHdWallet->getGroup(targetHdWallet->getXBTGroupType()); - std::vector> xbtWallets; - if (!targetHdWallet->canMixLeaves()) { - assert(peer->offer.walletPurpose); - xbtWallets.push_back(group->getLeaf(*peer->offer.walletPurpose)); - } - else { - xbtWallets = group->getAllLeaves(); - } - - if (xbtWallets.empty()) { - cb(OtcClientDeal::error("can't find XBT wallets")); - return; - } - - bs::tradeutils::PayinArgs args; - initTradesArgs(args, peer, settlementId); - args.fixedInputs = peer->offer.inputs; - args.inputXbtWallets = xbtWallets; - - auto payinCb = bs::tradeutils::PayinResultCb([this, cb, peer, settlementId - , targetHdWallet, handle = peer->validityFlag.handle(), logger = logger_] - (bs::tradeutils::PayinResult payin) - { - QMetaObject::invokeMethod(this, [this, cb, targetHdWallet, settlementId, handle, logger, peer, payin = std::move(payin)] - { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - if (!payin.success) { - SPDLOG_LOGGER_ERROR(logger, "creating unsigned payin failed: {}", payin.errorMsg); - cb(OtcClientDeal::error("invalid pay-in transaction")); - return; - } - - peer->settlementId = settlementId; - - auto result = std::make_shared(); - result->settlementId = settlementId; - result->settlementAddr = payin.settlementAddr; - result->ourAuthAddress = bs::Address::fromAddressString(peer->offer.authAddress); - result->cpPubKey = peer->authPubKey; - result->amount = peer->offer.amount; - result->price = peer->offer.price; - result->hdWalletId = targetHdWallet->walletId(); - result->success = true; - result->side = otc::Side::Sell; - result->payin = std::move(payin.signRequest); - result->payin.expiredTimestamp = std::chrono::system_clock::now() + otc::payinTimeout(); - - result->unsignedPayinHash = payin.payinHash; - - result->fee = int64_t(result->payin.fee); - - const auto &cbResolveSpenders = [result, cb, this](bs::error::ErrorCode errCode - , const Codec_SignerState::SignerState &state) - { - if (errCode == bs::error::ErrorCode::NoError) { - result->payin.armorySigner_.deserializeState(state); - } - cb(std::move(*result)); - }; - signContainer_->resolvePublicSpenders(result->payin, cbResolveSpenders); - }); - }); - - bs::tradeutils::createPayin(std::move(args), std::move(payinCb)); -} - -void OtcClient::createBuyerRequest(const std::string &settlementId, const PeerPtr &peer, const OtcClient::OtcClientDealCb &cb) -{ - assert(peer->authPubKey.getSize() == kPubKeySize); - assert(settlementId.size() == kSettlementIdHexSize); - assert(!peer->offer.authAddress.empty()); - assert(peer->offer.ourSide == bs::network::otc::Side::Buy); - assert(peer->payinTxIdFromSeller.getSize() == kTxHashSize); - - auto targetHdWallet = walletsMgr_->getHDWalletById(peer->offer.hdWalletId); - if (!targetHdWallet) { - cb(OtcClientDeal::error(fmt::format("can't find wallet: {}", peer->offer.hdWalletId))); - return; - } - - auto group = targetHdWallet->getGroup(targetHdWallet->getXBTGroupType()); - std::vector> xbtWallets; - if (!targetHdWallet->canMixLeaves()) { - assert(peer->offer.walletPurpose); - xbtWallets.push_back(group->getLeaf(*peer->offer.walletPurpose)); - } - else { - xbtWallets = group->getAllLeaves(); - } - - bs::tradeutils::PayoutArgs args; - initTradesArgs(args, peer, settlementId); - args.payinTxId = peer->payinTxIdFromSeller; - if (!peer->offer.recvAddress.empty()) { - args.recvAddr = bs::Address::fromAddressString(peer->offer.recvAddress); - } - args.outputXbtWallet = xbtWallets.front(); - - auto payoutCb = bs::tradeutils::PayoutResultCb([this, cb, peer, settlementId, targetHdWallet, handle = peer->validityFlag.handle(), logger = logger_] - (bs::tradeutils::PayoutResult payout) - { - QMetaObject::invokeMethod(this, [cb, targetHdWallet, settlementId, handle, peer, logger, payout = std::move(payout)] { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - peer->settlementId = settlementId; - - OtcClientDeal result; - result.settlementId = settlementId; - result.settlementAddr = payout.settlementAddr; - result.ourAuthAddress = bs::Address::fromAddressString(peer->offer.authAddress); - result.cpPubKey = peer->authPubKey; - result.amount = peer->offer.amount; - result.price = peer->offer.price; - result.hdWalletId = targetHdWallet->walletId(); - result.success = true; - result.side = otc::Side::Buy; - result.payout = std::move(payout.signRequest); - result.fee = int64_t(result.payout.fee); - cb(std::move(result)); - }); - }); - - bs::tradeutils::createPayout(std::move(args), std::move(payoutCb)); -}; - -void OtcClient::sendSellerAccepts(const PeerPtr &peer) -{ - int requestId = genLocalUniqueId(); - waitSettlementIds_.emplace(requestId, SettlementIdRequest{peer, peer->validityFlag.handle()}); - - ProxyTerminalPb::Request request; - auto d = request.mutable_start_otc(); - d->set_request_id(requestId); - emit sendPbMessage(request.SerializeAsString()); - - QTimer::singleShot(kStartOtcTimeout, this, [this, requestId, peer, handle = peer->validityFlag.handle()] { - if (!handle.isValid()) { - return; - } - auto it = waitSettlementIds_.find(requestId); - if (it == waitSettlementIds_.end()) { - return; - } - waitSettlementIds_.erase(it); - SPDLOG_LOGGER_ERROR(logger_, "can't get settlementId from PB: timeout"); - emit peerError(peer, PeerErrorType::Timeout, nullptr); - pullOrReject(peer); - }); -} - -std::shared_ptr OtcClient::findSettlementLeaf(const std::string &ourAuthAddress) -{ - return walletsMgr_->getSettlementLeaf(bs::Address::fromAddressString(ourAuthAddress)); -} - -void OtcClient::changePeerStateWithoutUpdate(const PeerPtr &peer, State state) -{ - SPDLOG_LOGGER_DEBUG(logger_, "changing peer '{}' state from {} to {}" - , peer->toString(), toString(peer->state), toString(state)); - peer->state = state; - peer->stateTimestamp = std::chrono::steady_clock::now(); - - switch (state) - { - case bs::network::otc::State::Idle: - case bs::network::otc::State::Blacklisted: - releaseReservation(peer); - break; - default: - break; - } -} - -void OtcClient::changePeerState(const PeerPtr &peer, bs::network::otc::State state) -{ - changePeerStateWithoutUpdate(peer, state); - emit peerUpdated(peer); -} - -void OtcClient::resetPeerStateToIdle(const PeerPtr &peer) -{ - if (!peer->activeSettlementId.empty()) { - signContainer_->CancelSignTx(peer->activeSettlementId); - peer->activeSettlementId.clear(); - } - - changePeerStateWithoutUpdate(peer, State::Idle); - auto request = std::move(peer->request); - if (!peer->settlementId.empty()) { - deals_.erase(peer->settlementId); - } - *peer = Peer(peer->contactId, peer->type); - peer->request = std::move(request); - emit peerUpdated(peer); -} - -void OtcClient::scheduleCloseAfterTimeout(std::chrono::milliseconds timeout, const PeerPtr &peer) -{ - // Use PreciseTimer to prevent time out earlier than expected - QTimer::singleShot(timeout, Qt::PreciseTimer, this, [this, peer, oldState = peer->state, handle = peer->validityFlag.handle(), timeout] { - if (!handle.isValid() || peer->state != oldState) { - return; - } - // Prevent closing from some old state if peer had switched back and forth - auto diff = std::chrono::steady_clock::now() - peer->stateTimestamp; - if (diff >= timeout) { - pullOrReject(peer); - } - }); -} - -void OtcClient::trySendSignedTx(OtcClientDeal *deal) -{ - ProxyTerminalPb::Request request; - auto d = request.mutable_process_tx(); - d->set_signed_tx(deal->signedTx.toBinStr()); - d->set_settlement_id(deal->settlementId); - emit sendPbMessage(request.SerializeAsString()); - - setComments(deal); -} - -void OtcClient::verifyAuthAddresses(OtcClientDeal *deal) -{ - if (!authAddressManager_) { - SPDLOG_LOGGER_DEBUG(logger_, "authAddressManager_ is not set, auth address verification skipped"); - return; - } - - auto verificatorCb = [this, logger = logger_, handle = deal->peerHandle, settlementId = deal->settlementId - , requesterAuthAddr = deal->requestorAuthAddress(), responderAuthAddr = deal->responderAuthAddress()] - (const bs::Address &address, AddressVerificationState state) - { - QMetaObject::invokeMethod(qApp, [this, logger, handle, state, address, settlementId, requesterAuthAddr, responderAuthAddr] { - if (!handle.isValid()) { - SPDLOG_LOGGER_ERROR(logger, "peer was destroyed"); - return; - } - - SPDLOG_LOGGER_DEBUG(logger_, "counterparty's auth address ({}) status: {}", address.display(), to_string(state)); - if (state != AddressVerificationState::Verified) { - return; - } - - bs::sync::PasswordDialogData dialogData; - dialogData.setValue(PasswordDialogData::SettlementId, settlementId); - if (address == requesterAuthAddr) { - dialogData.setValue(PasswordDialogData::RequesterAuthAddressVerified, true); - } else if (address == responderAuthAddr) { - dialogData.setValue(PasswordDialogData::ResponderAuthAddressVerified, true); - } else { - SPDLOG_LOGGER_ERROR(logger, "unexpected auth address"); - return; - } - signContainer_->updateDialogData(dialogData); - }); - }; - - if (authAddressVerificationRequired(deal)) { - deal->addressVerificator = std::make_unique(logger_, armory_, verificatorCb); - deal->addressVerificator->SetBSAddressList(authAddressManager_->GetBSAddresses()); - deal->addressVerificator->addAddress(deal->cpAuthAddress()); - deal->addressVerificator->startAddressVerification(); - } else { - verificatorCb(deal->cpAuthAddress(), AddressVerificationState::Verified); - } -} - -void OtcClient::setComments(OtcClientDeal *deal) -{ - auto hdWallet = walletsMgr_->getHDWalletById(deal->hdWalletId); - auto group = hdWallet ? hdWallet->getGroup(hdWallet->getXBTGroupType()) : nullptr; - auto leaves = group ? group->getAllLeaves() : std::vector>(); - for (const auto & leaf : leaves) { - const double price = bs::network::otc::fromCents(deal->price); - auto comment = fmt::format("{} XBT/EUR @ {} (OTC)" - , bs::network::otc::toString(deal->side), UiUtils::displayPriceXBT(price).toStdString()); - leaf->setTransactionComment(deal->signedTx, comment); - } -} - -void OtcClient::updatePublicLists() -{ - contacts_.clear(); - contacts_.reserve(contactMap_.size()); - for (auto &item : contactMap_) { - contacts_.push_back(item.second); - } - - requests_.clear(); - requests_.reserve(requestMap_.size() + (ownRequest_ ? 1 : 0)); - if (ownRequest_) { - requests_.push_back(ownRequest_); - } - for (auto &item : requestMap_) { - requests_.push_back(item.second); - } - - responses_.clear(); - responses_.reserve(responseMap_.size()); - for (auto &item : responseMap_) { - responses_.push_back(item.second); - } - - emit publicUpdated(); -} - -void OtcClient::initTradesArgs(bs::tradeutils::Args &args, const PeerPtr &peer, const std::string &settlementId) -{ - args.amount = bs::XBTAmount(static_cast(peer->offer.amount)); - args.settlementId = BinaryData::CreateFromHex(settlementId); - args.walletsMgr = walletsMgr_; - args.ourAuthAddress = bs::Address::fromAddressString(peer->offer.authAddress); - args.cpAuthPubKey = peer->authPubKey; - args.armory = armory_; - args.signContainer = signContainer_; - // utxoReservationManager_ is null in unit tests - if (utxoReservationManager_) { - args.feeRatePb_ = utxoReservationManager_->feeRatePb(); - } -} - -bool OtcClient::expandTxDialog() const -{ - return applicationSettings_->get( - ApplicationSettings::DetailedSettlementTxDialogByDefault); -} - -bool OtcClient::authAddressVerificationRequired(OtcClientDeal *deal) const -{ - if (!applicationSettings_) { - return true; - } - const auto tier1XbtLimit = applicationSettings_->get( - ApplicationSettings::SubmittedAddressXbtLimit); - return static_cast(deal->amount) > tier1XbtLimit; -} diff --git a/BlockSettleUILib/Trading/OtcClient.h b/BlockSettleUILib/Trading/OtcClient.h deleted file mode 100644 index 7ad30ca3a..000000000 --- a/BlockSettleUILib/Trading/OtcClient.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef OTC_CLIENT_H -#define OTC_CLIENT_H - -#include -#include -#include -#include - -#include "BSErrorCode.h" -#include "BinaryData.h" -#include "OtcTypes.h" -#include "UtxoReservationToken.h" - -namespace spdlog { - class logger; -} - -namespace Blocksettle { - namespace Communication { - namespace Otc { - class ContactMessage; - class ContactMessage_BuyerOffers; - class ContactMessage_SellerOffers; - class ContactMessage_BuyerAccepts; - class ContactMessage_SellerAccepts; - class ContactMessage_BuyerAcks; - class ContactMessage_Close; - class ContactMessage_QuoteResponse; - class PublicMessage_Request; - class PublicMessage_Close; - class PublicMessage_PrivateMessage; - } - } -} - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - class Response_StartOtc; - class Response_UpdateOtcState; - } - } -} - -namespace bs { - namespace tradeutils { - struct Args; - } - class Address; - namespace core { - namespace wallet { - struct TXSignRequest; - } - } - namespace sync { - class Wallet; - class WalletsManager; - namespace hd { - class SettlementLeaf; - } - } - class UTXOReservationManager; -} - -class ApplicationSettings; -class ArmoryConnection; -class AuthAddressManager; -class WalletSignerContainer; -struct OtcClientDeal; - -struct OtcClientParams -{ - bs::network::otc::Env env{}; -}; - -class OtcClient : public QObject -{ - Q_OBJECT - -public: - // authAddressManager could be null. If not set peer's auth address won't be verified (and verification affects only signer UI for now). - OtcClient(const std::shared_ptr &logger - , const std::shared_ptr &walletsMgr - , const std::shared_ptr &armory - , const std::shared_ptr &signContainer - , const std::shared_ptr &authAddressManager - , const std::shared_ptr &utxoReservationManager - , const std::shared_ptr& applicationSettings - , OtcClientParams params - , QObject *parent = nullptr); - ~OtcClient() override; - - bs::network::otc::PeerPtr contact(const std::string &contactId); - bs::network::otc::PeerPtr request(const std::string &contactId); - bs::network::otc::PeerPtr response(const std::string &contactId); - - // Calls one of above methods depending on type - bs::network::otc::PeerPtr peer(const std::string &contactId, bs::network::otc::PeerType type); - - void setOwnContactId(const std::string &contactId); - const std::string &ownContactId() const; - - bool sendQuoteRequest(const bs::network::otc::QuoteRequest &request); - bool sendQuoteResponse(const bs::network::otc::PeerPtr &peer, const bs::network::otc::QuoteResponse "eResponse); - bool sendOffer(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer &offer); - bool acceptOffer(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer &offer); - bool updateOffer(const bs::network::otc::PeerPtr &peer, const bs::network::otc::Offer &offer); - bool pullOrReject(const bs::network::otc::PeerPtr &peer); - void setReservation(const bs::network::otc::PeerPtr &peer, bs::UtxoReservationToken&& reserv); - bs::UtxoReservationToken releaseReservation(const bs::network::otc::PeerPtr &peer); - - const bs::network::otc::PeerPtrs &requests() { return requests_; } - const bs::network::otc::PeerPtrs &responses() { return responses_; } - const bs::network::otc::PeerPtr &ownRequest() const; - -public slots: - void contactConnected(const std::string &contactId); - void contactDisconnected(const std::string &contactId); - void processContactMessage(const std::string &contactId, const BinaryData &data); - void processPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response &response); - void processPublicMessage(QDateTime timestamp, const std::string &contactId, const BinaryData &data); - -signals: - void sendContactMessage(const std::string &contactId, const BinaryData &data); - void sendPbMessage(const std::string &data); - void sendPublicMessage(const BinaryData &data); - - void peerUpdated(const bs::network::otc::PeerPtr &peer); - // Used to update UI when there is some problems (for example deal verification failed) - void peerError(const bs::network::otc::PeerPtr &peer, bs::network::otc::PeerErrorType type, const std::string *errorMsg); - - void publicUpdated(); - -private slots: - void onTxSigned(unsigned reqId, BinaryData signedTX, bs::error::ErrorCode result, const std::string &errorReason); - -private: - using OtcClientDealCb = std::function; - - struct SettlementIdRequest - { - bs::network::otc::PeerPtr peer; - ValidityHandle handle; - }; - - void processQuoteResponse(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_QuoteResponse &msg); - void processBuyerOffers(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_BuyerOffers &msg); - void processSellerOffers(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_SellerOffers &msg); - void processBuyerAccepts(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_BuyerAccepts &msg); - void processSellerAccepts(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_SellerAccepts &msg); - void processBuyerAcks(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_BuyerAcks &msg); - void processClose(const bs::network::otc::PeerPtr &peer, const Blocksettle::Communication::Otc::ContactMessage_Close &msg); - - void processPublicRequest(QDateTime timestamp, const std::string &contactId, const Blocksettle::Communication::Otc::PublicMessage_Request &msg); - void processPublicClose(QDateTime timestamp, const std::string &contactId, const Blocksettle::Communication::Otc::PublicMessage_Close &msg); - - void processPbStartOtc(const Blocksettle::Communication::ProxyTerminalPb::Response_StartOtc &response); - void processPbUpdateOtcState(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOtcState &response); - - // Checks that hdWallet, auth address and recv address (is set) are valid - bool verifyOffer(const bs::network::otc::Offer &offer) const; - void blockPeer(const std::string &reason, const bs::network::otc::PeerPtr &peer); - - void send(const bs::network::otc::PeerPtr &peer, Blocksettle::Communication::Otc::ContactMessage &msg); - - void createSellerRequest(const std::string &settlementId, const bs::network::otc::PeerPtr &peer, const OtcClientDealCb &cb); - void createBuyerRequest(const std::string &settlementId, const bs::network::otc::PeerPtr &peer, const OtcClientDealCb &cb); - void sendSellerAccepts(const bs::network::otc::PeerPtr &peer); - - std::shared_ptr findSettlementLeaf(const std::string &ourAuthAddress); - - void changePeerStateWithoutUpdate(const bs::network::otc::PeerPtr &peer, bs::network::otc::State state); - void changePeerState(const bs::network::otc::PeerPtr &peer, bs::network::otc::State state); - void resetPeerStateToIdle(const bs::network::otc::PeerPtr &peer); - void scheduleCloseAfterTimeout(std::chrono::milliseconds timeout, const bs::network::otc::PeerPtr &peer); - - int genLocalUniqueId() { return ++latestUniqueId_; } - void trySendSignedTx(OtcClientDeal *deal); - void verifyAuthAddresses(OtcClientDeal *deal); - void setComments(OtcClientDeal *deal); - - void updatePublicLists(); - - void initTradesArgs(bs::tradeutils::Args &args, const bs::network::otc::PeerPtr &peer, const std::string &settlementId); - - bool expandTxDialog() const; - bool authAddressVerificationRequired(OtcClientDeal *deal) const; - - std::shared_ptr logger_; - - std::shared_ptr walletsMgr_; - std::shared_ptr armory_; - std::shared_ptr signContainer_; - std::shared_ptr authAddressManager_; - std::shared_ptr utxoReservationManager_; - std::shared_ptr applicationSettings_; - - std::string ownContactId_; - - // Maps settlementId to OtcClientDeal - std::map> deals_; - - int latestUniqueId_{}; - std::map waitSettlementIds_; - - // Maps sign requests to settlementId - std::map signRequestIds_; - - // Own public request if exists - bs::network::otc::PeerPtr ownRequest_; - - // Maps contactId to corresponding Peer - std::unordered_map contactMap_; - std::unordered_map requestMap_; - std::unordered_map responseMap_; - - // Cached pointer lists from the above - bs::network::otc::PeerPtrs contacts_; - bs::network::otc::PeerPtrs requests_; - bs::network::otc::PeerPtrs responses_; - - OtcClientParams params_; - - // Utxo reservation - std::unordered_map reservedTokens_; -}; - -#endif diff --git a/BlockSettleUILib/Trading/OtcUtils.cpp b/BlockSettleUILib/Trading/OtcUtils.cpp deleted file mode 100644 index 02191803d..000000000 --- a/BlockSettleUILib/Trading/OtcUtils.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "OtcUtils.h" - -#include "OtcTypes.h" -#include "UiUtils.h" -#include "otc.pb.h" - -using namespace Blocksettle::Communication; -using namespace bs::network::otc; - -namespace { - - const std::string kSerializePrefix = "OTC:"; - - QString formatResponse(const Otc::ContactMessage::QuoteResponse &response) - { - return QStringLiteral("%1-%2 XBT %3-%4 EUR") - .arg(response.amount().lower()) - .arg(response.amount().upper()) - .arg(UiUtils::displayPriceXBT(fromCents(response.price().lower()))) - .arg(UiUtils::displayPriceXBT(fromCents(response.price().upper()))); - } - - QString formatOffer(const Otc::ContactMessage::Offer &offer) - { - return QStringLiteral("%1 XBT - %2 EUR") - .arg(UiUtils::displayAmount(offer.amount())) - .arg(UiUtils::displayPriceXBT(fromCents(offer.price()))); - } - -} // namespace - -std::string OtcUtils::serializeMessage(const BinaryData &data) -{ - return kSerializePrefix + data.toHexStr(); -} - -BinaryData OtcUtils::deserializeMessage(const std::string &data) -{ - size_t pos = data.find(kSerializePrefix); - if (pos != 0) { - return {}; - } - try { - return BinaryData::CreateFromHex(data.substr(kSerializePrefix.size())); - } catch(...) { - return {}; - } -} - -std::string OtcUtils::serializePublicMessage(const BinaryData &data) -{ - return data.toHexStr(); -} - -BinaryData OtcUtils::deserializePublicMessage(const std::string &data) -{ - try { - return BinaryData::CreateFromHex(data); - } catch(...) { - return {}; - } -} - -QString OtcUtils::toReadableString(const QString &text) -{ - auto msgData = OtcUtils::deserializeMessage(text.toStdString()); - if (msgData.empty()) { - return {}; - } - - Otc::ContactMessage msg; - bool result = msg.ParseFromArray(msgData.getPtr(), int(msgData.getSize())); - if (!result) { - return {}; - } - - switch (msg.data_case()) { - case Otc::ContactMessage::kQuoteResponse: - return QObject::tr("OTC RESPONSE - XBT/EUR - %1").arg(formatResponse(msg.quote_response())); - case Otc::ContactMessage::kBuyerOffers: - return QObject::tr("OTC REQUEST - XBT/EUR - BUY - %1").arg(formatOffer(msg.buyer_offers().offer())); - case Otc::ContactMessage::kSellerOffers: - return QObject::tr("OTC REQUEST - XBT/EUR - SELL - %1").arg(formatOffer(msg.seller_offers().offer())); - case Otc::ContactMessage::kBuyerAccepts: - return QObject::tr("OTC ACCEPT - XBT/EUR - BUY - %1").arg(formatOffer(msg.buyer_accepts().offer())); - case Otc::ContactMessage::kSellerAccepts: - return QObject::tr("OTC ACCEPT - XBT/EUR - SELL - %1").arg(formatOffer(msg.seller_accepts().offer())); - case Otc::ContactMessage::kBuyerAcks: - return QObject::tr("OTC ACCEPTED"); - case Otc::ContactMessage::kClose: - return QObject::tr("OTC CANCEL"); - case Otc::ContactMessage::DATA_NOT_SET: - return QObject::tr("OTC INVALID MESSAGE"); - } - - assert(false); - return {}; -} diff --git a/BlockSettleUILib/Trading/OtcUtils.h b/BlockSettleUILib/Trading/OtcUtils.h deleted file mode 100644 index 2b9c2d745..000000000 --- a/BlockSettleUILib/Trading/OtcUtils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef OTC_UTILS_H -#define OTC_UTILS_H - -#include -#include -#include "BinaryData.h" - -class OtcUtils -{ -public: - static std::string serializeMessage(const BinaryData &data); - static BinaryData deserializeMessage(const std::string &data); - - static std::string serializePublicMessage(const BinaryData &data); - static BinaryData deserializePublicMessage(const std::string &data); - - // Parse incoming message and convert it into readable string (that will be visible in the UI). - // If not OTC return empty string. - static QString toReadableString(const QString &text); - -}; - -#endif diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp b/BlockSettleUILib/Trading/QuoteRequestsModel.cpp deleted file mode 100644 index ea75a225a..000000000 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.cpp +++ /dev/null @@ -1,1406 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "QuoteRequestsModel.h" - -#include "ApplicationSettings.h" -#include "AssetManager.h" -#include "Celer/CelerClient.h" -#include "Celer/SubmitQuoteNotifSequence.h" -#include "Colors.h" -#include "CommonTypes.h" -#include "CurrencyPair.h" -#include "DealerCCSettlementContainer.h" -#include "QuoteRequestsWidget.h" -#include "SettlementContainer.h" -#include "UiUtils.h" - -#include - - -QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptr &statsCollector - , std::shared_ptr celerClient, std::shared_ptr appSettings - , QObject* parent) - : QAbstractItemModel(parent) - , secStatsCollector_(statsCollector) - , celerClient_(celerClient) - , appSettings_(appSettings) -{ - timer_.setInterval(500); - connect(&timer_, &QTimer::timeout, this, &QuoteRequestsModel::ticker); - timer_.start(); - - connect(&priceUpdateTimer_, &QTimer::timeout, this, &QuoteRequestsModel::onPriceUpdateTimer); - - setPriceUpdateInterval(appSettings_->get(ApplicationSettings::PriceUpdateInterval)); - - connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, - this, &QuoteRequestsModel::clearModel); - connect(this, &QuoteRequestsModel::deferredUpdate, - this, &QuoteRequestsModel::onDeferredUpdate, Qt::QueuedConnection); -} - -QuoteRequestsModel::QuoteRequestsModel(const std::shared_ptr& statsCollector - , QObject* parent) - : QAbstractItemModel(parent), secStatsCollector_(statsCollector) -{ - timer_.setInterval(500); - connect(&timer_, &QTimer::timeout, this, &QuoteRequestsModel::ticker); - timer_.start(); - - connect(&priceUpdateTimer_, &QTimer::timeout, this, &QuoteRequestsModel::onPriceUpdateTimer); - - setPriceUpdateInterval(-1); //FIXME - - connect(this, &QuoteRequestsModel::deferredUpdate, - this, &QuoteRequestsModel::onDeferredUpdate, Qt::QueuedConnection); -} - -QuoteRequestsModel::~QuoteRequestsModel() -{ - secStatsCollector_->saveState(); -} - -int QuoteRequestsModel::columnCount(const QModelIndex &) const -{ - return 10; -} - -QVariant QuoteRequestsModel::data(const QModelIndex &index, int role) const -{ - IndexHelper *idx = static_cast(index.internalPointer()); - - switch (idx->type_) { - case DataType::RFQ : { - RFQ *r = static_cast(idx->data_); - - switch (role) { - case Qt::DisplayRole : { - switch(static_cast(index.column())) { - case Column::SecurityID : { - return r->securityDefinition_; - } - - case Column::Product : { - return r->product_; - } - case Column::Side : { - return r->sideString_; - } - - case Column::Quantity : { - return r->quantityString_; - } - - case Column::Party : { - return r->party_; - } - - case Column::Status : { - return r->status_.status_; - } - - case Column::QuotedPx : { - return r->quotedPriceString_; - } - - case Column::IndicPx : { - return r->indicativePxString_; - } - - case Column::BestPx : { - return r->bestQuotedPxString_; - } - - default: - return QVariant(); - } - } - - case static_cast(Role::Type) : { - return static_cast(DataType::RFQ); - } - - case static_cast(Role::LimitOfRfqs) : { - return static_cast(r->idx_.parent_->data_)->limit_; - } - - case static_cast(Role::QuotedRfqsCount) : { - return -1; - } - - case static_cast(Role::Quoted) : { - return r->quoted_; - } - - case static_cast(Role::ShowProgress) : { - return r->status_.showProgress_; - } - - case static_cast(Role::Timeout) : { - return r->status_.timeout_; - } - - case static_cast(Role::TimeLeft) : { - return r->status_.timeleft_; - } - - case static_cast(Role::BestQPrice) : { - return r->bestQuotedPx_; - } - - case static_cast(Role::QuotedPrice) : { - return r->quotedPrice_; - } - - case static_cast(Role::AllowFiltering) : { - if (index.column() == 0) { - return true; - } else { - return false; - } - } - - case static_cast(Role::ReqId) : { - return QString::fromStdString(r->reqId_); - } - - case Qt::BackgroundRole : { - switch (static_cast(index.column())) { - case Column::QuotedPx : - return r->quotedPriceBrush_; - - case Column::IndicPx : - return r->indicativePxBrush_; - - case Column::Status : - return r->stateBrush_; - - default : - return QVariant(); - } - } - - case Qt::TextColorRole: { - if (secStatsCollector_ && index.column() < static_cast(Column::Status)) { - return secStatsCollector_->getColorFor(r->securityDefinition_.toStdString()); - } - else { - return QVariant(); - } - } - - case static_cast(Role::Visible) : { - return r->visible_; - } - - case static_cast(Role::SortOrder) : { - return r->status_.timeleft_; - } - - default: - return QVariant(); - } - } - - case DataType::Group : { - Group *g = static_cast(idx->data_); - - switch (role) { - case Qt::FontRole : { - if (index.column() == static_cast(Column::SecurityID)) { - return g->font_; - } else { - return QVariant(); - } - } - - case static_cast(Role::Type) : { - return static_cast(DataType::Group); - } - - case static_cast(Role::HasHiddenChildren) : { - return (static_cast(g->visibleCount_ + g->quotedRfqsCount_) < - g->rfqs_.size()); - } - - case static_cast(Role::LimitOfRfqs) : { - return g->limit_; - } - - case static_cast(Role::QuotedRfqsCount) : { - return g->quotedRfqsCount_; - } - - case Qt::DisplayRole : { - switch (index.column()) { - case static_cast(Column::SecurityID) : - return g->security_; - - default : - return QVariant(); - } - } - - case static_cast(Role::StatText) : { - return (g->limit_ > 0 ? tr("%1 of %2") - .arg(QString::number(g->visibleCount_ + - (showQuoted_ ? g->quotedRfqsCount_ : 0))) - .arg(QString::number(g->rfqs_.size())) : - tr("%1 RFQ").arg(QString::number(g->rfqs_.size()))); - } - - case static_cast(Role::CountOfRfqs) : { - return static_cast(g->rfqs_.size()); - } - - case Qt::TextColorRole : { - switch (index.column()) { - case static_cast(Column::SecurityID) : { - if (secStatsCollector_) { - return secStatsCollector_->getColorFor(g->security_.toStdString()); - } else { - return QVariant(); - } - } - - default : - return QVariant(); - } - } - - case static_cast(Role::Grade) : { - if (secStatsCollector_) { - return secStatsCollector_->getGradeFor(g->security_.toStdString()); - } else { - return QVariant(); - } - } - - case static_cast(Role::AllowFiltering) : { - return true; - } - - case static_cast(Role::SortOrder) : { - return g->security_; - } - - default : - return QVariant(); - } - } - - case DataType::Market : { - Market *m = static_cast(idx->data_); - - switch (role) { - case static_cast(Role::Grade) : { - return 1; - } - - case static_cast(Role::Type) : { - return static_cast(DataType::Market); - } - - case static_cast(Role::LimitOfRfqs) : { - return m->limit_; - } - - case static_cast(Role::QuotedRfqsCount) : { - return -1; - } - - case Qt::DisplayRole : { - switch (static_cast(index.column())) { - case Column::SecurityID : { - return m->security_; - } - - default : - return QVariant(); - } - } - - case Qt::TextColorRole : { - if (index.column() == static_cast(Column::Product)) { - return c_greenColor; - } else if (index.column() == static_cast(Column::Side)) { - return c_redColor; - } else { - return QVariant(); - } - } - - case Qt::TextAlignmentRole : { - if (index.column() == static_cast(Column::Product) - || index.column() == static_cast(Column::Side)) { - return Qt::AlignRight; - } else { - return QVariant(); - } - } - - case static_cast(Role::SortOrder) : { - if (m->security_ == QLatin1String(bs::network::Asset::toString(bs::network::Asset::Type::PrivateMarket))) { - return 3; - } - else if (m->security_ == QLatin1String(bs::network::Asset::toString(bs::network::Asset::Type::SpotXBT))) { - return 2; - } - else if (m->security_ == QLatin1String(bs::network::Asset::toString(bs::network::Asset::Type::SpotFX))) { - return 1; - } - else if (m->security_ == groupNameSettlements_) { - return 0; - } - } - - default : - return QVariant(); - } - } - - default : - return QVariant(); - } -} - -QModelIndex QuoteRequestsModel::index(int r, int column, const QModelIndex &parent) const -{ - std::size_t row = static_cast(r); - - if (parent.isValid()) { - IndexHelper *idx = static_cast(parent.internalPointer()); - - switch (idx->type_) { - case DataType::Market : { - Market *m = static_cast(idx->data_); - - if (row < m->groups_.size()) { - return createIndex(r, column, &m->groups_.at(row)->idx_); - } else if (row < m->groups_.size() + m->settl_.rfqs_.size()){ - return createIndex(r, column, &m->settl_.rfqs_.at(row - m->groups_.size())->idx_); - } else { - return QModelIndex(); - } - } - - case DataType::Group : { - Group *g = static_cast(idx->data_); - - if (row < g->rfqs_.size()) { - return createIndex(r, column, &g->rfqs_.at(row)->idx_); - } else { - return QModelIndex(); - } - } - - default : - return QModelIndex(); - } - } else if (row < data_.size()) { - return createIndex(r, column, &data_.at(row)->idx_); - } else { - return QModelIndex(); - } -} - -int QuoteRequestsModel::findGroup(IndexHelper *idx) const -{ - if (idx->parent_) { - auto *market = static_cast(idx->parent_->data_); - - auto it = std::find_if(market->groups_.cbegin(), market->groups_.cend(), - [&idx](const std::unique_ptr &g) { return (&g->idx_ == idx); }); - - if (it != market->groups_.cend()) { - return static_cast(std::distance(market->groups_.cbegin(), it)); - } else { - return -1; - } - } else if (idx->type_ == DataType::Market) { - return findMarket(idx); - } else { - return -1; - } -} - -QuoteRequestsModel::Group* QuoteRequestsModel::findGroup(Market *market, const QString &security) const -{ - auto it = std::find_if(market->groups_.cbegin(), market->groups_.cend(), - [&] (const std::unique_ptr &g) { return (g->security_ == security); } ); - - if (it != market->groups_.cend()) { - return it->get(); - } else { - return nullptr; - } -} - -int QuoteRequestsModel::findMarket(IndexHelper *idx) const -{ - auto it = std::find_if(data_.cbegin(), data_.cend(), - [&idx](const std::unique_ptr &m) { return (&m->idx_ == idx); }); - - if (it != data_.cend()) { - return static_cast(std::distance(data_.cbegin(), it)); - } else { - return -1; - } -} - -QuoteRequestsModel::Market* QuoteRequestsModel::findMarket(const QString &name) const -{ - for (size_t i = 0; i < data_.size(); ++i) { - if (data_[i]->security_ == name) { - return data_[i].get(); - } - } - - return nullptr; -} - -QModelIndex QuoteRequestsModel::parent(const QModelIndex &index) const -{ - if (index.isValid()) { - IndexHelper *idx = static_cast(index.internalPointer()); - - if (idx->parent_) { - switch (idx->parent_->type_) { - case DataType::Group : { - return createIndex(findGroup(idx->parent_), 0, idx->parent_); - } - - case DataType::Market : { - return createIndex(findMarket(idx->parent_), 0, idx->parent_); - } - - default : - return QModelIndex(); - } - } else { - return QModelIndex(); - } - } else { - return QModelIndex(); - } -} - -int QuoteRequestsModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - IndexHelper *idx = static_cast(parent.internalPointer()); - - switch (idx->type_) { - case DataType::Group : { - return static_cast(static_cast(idx->data_)->rfqs_.size()); - } - - case DataType::Market : { - auto *market = static_cast(idx->data_); - return static_cast(market->groups_.size() + market->settl_.rfqs_.size()); - } - - default : - return 0; - } - } else { - return static_cast(data_.size()); - } -} - -QVariant QuoteRequestsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != Qt::Horizontal) { - return QVariant(); - } - - if (role != Qt::DisplayRole) { - return QVariant(); - } - - switch (static_cast(section)) { - case Column::SecurityID: return tr("SecurityID"); - case Column::Product: return tr("Product"); - case Column::Side: return tr("Side"); - case Column::Quantity: return tr("Quantity"); - case Column::Party: return tr("Party"); - case Column::Status: return tr("Status"); - case Column::QuotedPx: return tr("Quoted Price"); - case Column::IndicPx: return tr("Indicative price"); - case Column::BestPx: return tr("Best Quoted price"); - default: return QString(); - } -} - -void QuoteRequestsModel::limitRfqs(const QModelIndex &index, int limit) -{ - if (!index.parent().isValid() && index.row() < static_cast(data_.size())) { - IndexHelper *idx = static_cast(index.internalPointer()); - - auto *m = static_cast(idx->data_); - - m->limit_ = limit; - - for (auto it = m->groups_.begin(), last = m->groups_.end(); it != last; ++it) { - (*it)->limit_ = limit; - clearVisibleFlag(it->get()); - showRfqsFromFront(it->get()); - } - - emit invalidateFilterModel(); - } -} - -QModelIndex QuoteRequestsModel::findMarketIndex(const QString &name) const -{ - auto *m = findMarket(name); - - if (m) { - return createIndex(findMarket(&m->idx_), 0, &m->idx_); - } else { - return QModelIndex(); - } -} - -void QuoteRequestsModel::setPriceUpdateInterval(int interval) -{ - priceUpdateInterval_ = interval; - priceUpdateTimer_.stop(); - - onPriceUpdateTimer(); - - if (priceUpdateInterval_ > 0) { - priceUpdateTimer_.start(priceUpdateInterval_); - } -} - -void QuoteRequestsModel::showQuotedRfqs(bool on) -{ - if (showQuoted_ != on) { - showQuoted_ = on; - - for (auto mit = data_.cbegin(), mlast = data_.cend(); mit != mlast; ++mit) { - for (auto it = (*mit)->groups_.cbegin(), last = (*mit)->groups_.cend(); - it != last; ++it) { - const auto idx = createIndex( - static_cast(std::distance((*mit)->groups_.cbegin(), it)), - static_cast(Column::Product), &(*it)->idx_); - emit dataChanged(idx, idx); - } - } - } -} - -void QuoteRequestsModel::SetAssetManager(const std::shared_ptr& assetManager) -{ - assetManager_ = assetManager; -} - -void QuoteRequestsModel::ticker() -{ - std::unordered_set deletedRows; - const auto timeNow = QDateTime::currentDateTime(); - - for (const auto &id : pendingDeleteIds_) { - forSpecificId(id, [this](Group *g, int idxItem) { - beginRemoveRows(createIndex(findGroup(&g->idx_), 0, &g->idx_), idxItem, idxItem); - if (g->rfqs_[static_cast(idxItem)]->quoted_) { - --g->quotedRfqsCount_; - } - if (g->rfqs_[static_cast(idxItem)]->visible_) { - --g->visibleCount_; - showRfqsFromBack(g); - } - g->rfqs_.erase(g->rfqs_.begin() + idxItem); - endRemoveRows(); - - emit invalidateFilterModel(); - }); - deletedRows.insert(id); - } - pendingDeleteIds_.clear(); - - for (auto qrn : notifications_) { - const auto& expTime = QDateTime::fromMSecsSinceEpoch(qrn.second.expirationTime); - const auto timeDiff = timeNow.msecsTo(expTime.addMSecs(qrn.second.timeSkewMs)); - if ((timeDiff < 0) || (qrn.second.status == bs::network::QuoteReqNotification::Withdrawn)) { - forSpecificId(qrn.second.quoteRequestId, [this](Group *grp, int itemIndex) { - const auto row = findGroup(&grp->idx_); - beginRemoveRows(createIndex(row, 0, &grp->idx_), itemIndex, itemIndex); - if (grp->rfqs_[static_cast(itemIndex)]->quoted_) { - --grp->quotedRfqsCount_; - } - if (grp->rfqs_[static_cast(itemIndex)]->visible_) { - --grp->visibleCount_; - showRfqsFromBack(grp); - } - grp->rfqs_.erase(grp->rfqs_.begin() + itemIndex); - endRemoveRows(); - - if ((grp->rfqs_.size() == 0) && (row >= 0)) { - const auto m = findMarket(grp->idx_.parent_); - beginRemoveRows(createIndex(m, 0, grp->idx_.parent_), row, row); - data_[m]->groups_.erase(data_[m]->groups_.begin() + row); - endRemoveRows(); - } else { - emit invalidateFilterModel(); - } - }); - deletedRows.insert(qrn.second.quoteRequestId); - } - else if ((qrn.second.status == bs::network::QuoteReqNotification::PendingAck) - || (qrn.second.status == bs::network::QuoteReqNotification::Replied)) { - forSpecificId(qrn.second.quoteRequestId, [timeDiff](Group *grp, int itemIndex) { - grp->rfqs_[static_cast(itemIndex)]->status_.timeleft_ = - static_cast(timeDiff); - }); - } - } - - for (auto delRow : deletedRows) { - notifications_.erase(delRow); - } - - if (!settlContainers_.empty()) { - for (const auto& settlContainer : settlContainers_) { - forSpecificId(settlContainer.second->id(), - [timeLeft = settlContainer.second->timeLeftMs()](Group* grp, int itemIndex) { - grp->rfqs_[static_cast(itemIndex)]->status_.timeleft_ = - static_cast(timeLeft); - }); - } - } - else { - for (const auto& settl : settlTimeLeft_) { - forSpecificId(settl.first, [timeLeft = settl.second] - (Group* grp, int itemIndex) { - grp->rfqs_[static_cast(itemIndex)]->status_.timeleft_ = - static_cast(timeLeft); - }); - } - } - - if (!data_.empty()) { - for (size_t i = 0; i < data_.size(); ++i) { - for (size_t j = 0; j < data_[i]->groups_.size(); ++j) { - if (data_[i]->groups_[j]->rfqs_.empty()) { - continue; - } - emit dataChanged(createIndex(0, static_cast(Column::Status), - &data_[i]->groups_[j]->rfqs_.front()->idx_), - createIndex(static_cast(data_[i]->groups_[j]->rfqs_.size() - 1), - static_cast(Column::Status), - &data_[i]->groups_[j]->rfqs_.back()->idx_)); - } - - if (!data_[i]->settl_.rfqs_.empty()) { - const int r = static_cast(data_[i]->groups_.size()); - emit dataChanged(createIndex(r, static_cast(Column::Status), - &data_[i]->settl_.rfqs_.front()->idx_), - createIndex(r + static_cast(data_[i]->settl_.rfqs_.size()) - 1, - static_cast(Column::Status), - &data_[i]->settl_.rfqs_.back()->idx_)); - } - } - } -} - -void QuoteRequestsModel::onQuoteNotifCancelled(const QString &reqId) -{ - int row = -1; - RFQ *rfq = nullptr; - - forSpecificId(reqId.toStdString(), [&](Group *group, int i) { - row = i; - rfq = group->rfqs_[static_cast(i)].get(); - rfq->quotedPriceString_ = tr("pulled"); - }); - - setStatus(reqId.toStdString(), bs::network::QuoteReqNotification::PendingAck); - - if (row >= 0 && rfq) { - static const QVector roles({Qt::DisplayRole}); - const QModelIndex idx = createIndex(row, static_cast(Column::QuotedPx), &rfq->idx_); - emit dataChanged(idx, idx, roles); - } -} - -void QuoteRequestsModel::onAllQuoteNotifCancelled(const QString &reqId) -{ - int row = -1; - RFQ *rfq = nullptr; - - forSpecificId(reqId.toStdString(), [&](Group *group, int i) { - row = i; - rfq = group->rfqs_[static_cast(i)].get(); - rfq->bestQuotedPxString_.clear(); - }); - - if (row >= 0 && rfq) { - static const QVector roles({ Qt::DisplayRole }); - const QModelIndex idx = createIndex(row, static_cast(Column::BestPx), &rfq->idx_); - emit dataChanged(idx, idx, roles); - } -} - -void QuoteRequestsModel::onQuoteReqCancelled(const QString &reqId, bool byUser) -{ - if (!byUser) { - onAllQuoteNotifCancelled(reqId); - return; - } - setStatus(reqId.toStdString(), bs::network::QuoteReqNotification::Withdrawn); -} - -void QuoteRequestsModel::onQuoteRejected(const QString &reqId, const QString &reason) -{ - setStatus(reqId.toStdString(), bs::network::QuoteReqNotification::Rejected, reason); -} - -void QuoteRequestsModel::updateBestQuotePrice(const QString &reqId, double price, bool own, - std::vector> *idxs) -{ - int row = -1; - Group *g = nullptr; - - forSpecificId(reqId.toStdString(), [&](Group *grp, int index) { - row = index; - g = grp; - const auto assetType = grp->rfqs_[static_cast(index)]->assetType_; - - grp->rfqs_[static_cast(index)]->bestQuotedPxString_ = - UiUtils::displayPriceForAssetType(price, assetType); - grp->rfqs_[static_cast(index)]->bestQuotedPx_ = price; - grp->rfqs_[static_cast(index)]->quotedPriceBrush_ = colorForQuotedPrice( - grp->rfqs_[static_cast(index)]->quotedPrice_, price, own); - }); - - if (row >= 0 && g) { - static const QVector roles({static_cast(Qt::DisplayRole), - static_cast(Qt::BackgroundRole)}); - const auto idx1 = createIndex(row, static_cast(Column::QuotedPx), - &g->rfqs_[static_cast(row)]->idx_); - const auto idx2 = createIndex(row, static_cast(Column::BestPx), - &g->rfqs_[static_cast(row)]->idx_); - - if (!idxs) { - emit dataChanged(idx1, idx2, roles); - } else { - idxs->push_back(std::make_pair(idx1, idx2)); - } - } -} - -void QuoteRequestsModel::onBestQuotePrice(const QString reqId, double price, bool own) -{ - if (priceUpdateInterval_ < 1) { - updateBestQuotePrice(reqId, price, own); - } else { - bestQuotePrices_[reqId] = {price, own}; - } -} - -void QuoteRequestsModel::onQuoteReqNotifReplied(const bs::network::QuoteNotification &qn) -{ - int row = -1; - Group *g = nullptr; - bool withdrawn = false; - - forSpecificId(qn.quoteRequestId, [&](Group *group, int i) { - if (group->rfqs_[static_cast(i)]->withdrawn_) { - withdrawn = true; - return; - } - - row = i; - g = group; - - const auto assetType = group->rfqs_[static_cast(i)]->assetType_; - - group->rfqs_[static_cast(i)]->quotedPriceString_ = - UiUtils::displayPriceForAssetType(qn.price, assetType); - group->rfqs_[static_cast(i)]->quotedPrice_ = qn.price; - group->rfqs_[static_cast(i)]->quotedPriceBrush_ = - colorForQuotedPrice(qn.price, group->rfqs_[static_cast(i)]->bestQuotedPx_); - }); - - if (withdrawn) { - return; // nothing to do, rfq was withdrawn - } - - if (row >= 0 && g) { - static const QVector roles({static_cast(Qt::DisplayRole), - static_cast(Qt::BackgroundRole)}); - const QModelIndex idx = createIndex(row, static_cast(Column::QuotedPx), - &g->rfqs_[static_cast(row)]->idx_); - emit dataChanged(idx, idx, roles); - } - - setStatus(qn.quoteRequestId, bs::network::QuoteReqNotification::Replied); -} - -QBrush QuoteRequestsModel::colorForQuotedPrice(double quotedPrice, double bestQPrice, bool own) -{ - if (qFuzzyIsNull(quotedPrice) || qFuzzyIsNull(bestQPrice)) { - return {}; - } - - if (own && qFuzzyCompare(quotedPrice, bestQPrice)) { - return c_greenColor; - } - - return c_redColor; -} - -void QuoteRequestsModel::onQuoteReqNotifReceived(const bs::network::QuoteReqNotification &qrn) -{ - QString marketName = tr(bs::network::Asset::toString(qrn.assetType)); - auto *market = findMarket(marketName); - - if (!market) { - beginInsertRows(QModelIndex(), static_cast(data_.size()), - static_cast(data_.size())); - data_.push_back(std::unique_ptr(new Market(marketName, - appSettings_ ? appSettings_->get(UiUtils::limitRfqSetting(qrn.assetType)) : 5))); - market = data_.back().get(); - endInsertRows(); - } - - QString groupNameSec = QString::fromStdString(qrn.security); - auto *group = findGroup(market, groupNameSec); - - if (!group) { - beginInsertRows(createIndex(findMarket(&market->idx_), 0, &market->idx_), - static_cast(market->groups_.size()), - static_cast(market->groups_.size())); - QFont font; - font.setBold(true); - market->groups_.push_back(std::unique_ptr(new Group(groupNameSec, - market->limit_, font))); - market->groups_.back()->idx_.parent_ = &market->idx_; - group = market->groups_.back().get(); - endInsertRows(); - } - - insertRfq(group, qrn); -} - -void QuoteRequestsModel::insertRfq(Group *group, const bs::network::QuoteReqNotification &qrn) -{ - auto itQRN = notifications_.find(qrn.quoteRequestId); - - if (itQRN == notifications_.end()) { - const CurrencyPair cp(qrn.security); - const bool isBid = (qrn.side == bs::network::Side::Buy) ^ (cp.NumCurrency() == qrn.product); - const double indicPrice = isBid ? mdPrices_[qrn.security][Role::BidPrice] : - mdPrices_[qrn.security][Role::OfferPrice]; - - beginInsertRows(createIndex(findGroup(&group->idx_), 0, &group->idx_), - static_cast(group->rfqs_.size()), - static_cast(group->rfqs_.size())); - - group->rfqs_.push_back(std::unique_ptr(new RFQ(QString::fromStdString(qrn.security), - QString::fromStdString(qrn.product), tr(bs::network::Side::toString(qrn.side)) - , QString(), (qrn.assetType == bs::network::Asset::Type::PrivateMarket) ? - UiUtils::displayCCAmount(qrn.quantity) : UiUtils::displayQty(qrn.quantity, qrn.product) - , QString(), !qFuzzyIsNull(indicPrice) - ? UiUtils::displayPriceForAssetType(indicPrice, qrn.assetType) : QString() - , QString(), { quoteReqStatusDesc(qrn.status), - ((qrn.status == bs::network::QuoteReqNotification::Status::PendingAck) - || (qrn.status == bs::network::QuoteReqNotification::Status::Replied)), - 30000 } - , indicPrice, 0.0, 0.0, qrn.side, qrn.assetType, qrn.quoteRequestId))); - - group->rfqs_.back()->idx_.parent_ = &group->idx_; - - endInsertRows(); - - notifications_[qrn.quoteRequestId] = qrn; - - if (group->limit_ > 0 && group->limit_ > group->visibleCount_) { - group->rfqs_.back()->visible_ = true; - ++group->visibleCount_; - - emit invalidateFilterModel(); - } - } - else { - setStatus(qrn.quoteRequestId, qrn.status); - } -} - -bool QuoteRequestsModel::StartCCSignOnOrder(const QString& orderId, QDateTime timestamp) -{ - auto it = settlContainers_.find(orderId.toStdString()); - if (it == settlContainers_.end()) { - return false; - } - - auto container = std::dynamic_pointer_cast(it->second); - if (container != nullptr) { - return container->startSigning(timestamp); - } - - return false; -} - -void QuoteRequestsModel::onSettlementPending(const BinaryData& settlementId - , int timeLeftMS) -{ - settlTimeLeft_[settlementId.toHexStr()] = timeLeftMS; -} - -void QuoteRequestsModel::onSettlementComplete(const BinaryData& settlementId) -{ - settlTimeLeft_.erase(settlementId.toHexStr()); -} - -void QuoteRequestsModel::addSettlementContainer(const std::shared_ptr &container) -{ - const auto &id = container->id(); - settlContainers_[id] = container; - - // Use queued connections to not destroy SettlementContainer inside callbacks - connect(container.get(), &bs::SettlementContainer::failed, this - , &QuoteRequestsModel::deleteSettlement, Qt::QueuedConnection); - connect(container.get(), &bs::SettlementContainer::completed, this - , &QuoteRequestsModel::deleteSettlement, Qt::QueuedConnection); - connect(container.get(), &bs::SettlementContainer::timerExpired, this, [this, id] { - deleteSettlement(id); - }, Qt::QueuedConnection); - - auto *market = findMarket(groupNameSettlements_); - - if (!market) { - beginInsertRows(QModelIndex(), static_cast(data_.size()), - static_cast(data_.size())); - data_.push_back(std::unique_ptr(new Market(groupNameSettlements_, -1))); - market = data_.back().get(); - endInsertRows(); - } - - const auto collector = std::make_shared(container); - const auto amountStr = (container->assetType() == bs::network::Asset::Type::PrivateMarket) - ? UiUtils::displayCCAmount(container->quantity()) - : UiUtils::displayQty(container->quantity(), container->product()); - - beginInsertRows(createIndex(findMarket(&market->idx_), 0, &market->idx_), - static_cast(market->groups_.size() + market->settl_.rfqs_.size()), - static_cast(market->groups_.size() + market->settl_.rfqs_.size())); - - // settlement containers not ment to be used with futures - market->settl_.rfqs_.push_back(std::unique_ptr(new RFQ( - QString::fromStdString(container->security()), - QString::fromStdString(container->product()), - tr(bs::network::Side::toString(container->side())), - QString(), - amountStr, - QString(), - UiUtils::displayPriceForAssetType(container->price(), container->assetType()), - QString(), - { - QString(), - true - }, - container->price(), 0.0, 0.0, - container->side(), - container->assetType(), - container->id()))); - - market->settl_.rfqs_.back()->idx_.parent_ = &market->idx_; - - connect(container.get(), &bs::SettlementContainer::timerStarted, - [s = market->settl_.rfqs_.back().get(), this, - row = static_cast(market->groups_.size() + market->settl_.rfqs_.size() - 1)](int msDuration) { - s->status_.timeout_ = msDuration; - const QModelIndex idx = createIndex(row, 0, &s->idx_); - static const QVector roles({static_cast(Role::Timeout)}); - emit dataChanged(idx, idx, roles); - } - ); - - endInsertRows(); -} - -void QuoteRequestsModel::clearModel() -{ - beginResetModel(); - data_.clear(); - endResetModel(); -} - -void QuoteRequestsModel::onDeferredUpdate(const QPersistentModelIndex &index) -{ - if (index.isValid()) { - emit dataChanged(index, index); - } -} - -void QuoteRequestsModel::onPriceUpdateTimer() -{ - std::vector> idxs; - - for (auto it = prices_.cbegin(), last = prices_.cend(); it != last; ++it) { - updatePrices(it->first, it->second.first, it->second.second, &idxs); - } - - prices_.clear(); - - for (auto it = bestQuotePrices_.cbegin(), last = bestQuotePrices_.cend(); it != last; ++it) { - updateBestQuotePrice(it->first, it->second.price_, it->second.own_, &idxs); - } - - bestQuotePrices_.clear(); - - if (!idxs.empty()) { - struct Index { - int row_; - int column_; - }; - - std::map> mapOfIdxs; - - for (const auto &idx: idxs) { - auto it = mapOfIdxs.find(idx.first.parent()); - - if (it != mapOfIdxs.end()) { - if (idx.first.row() < it->second.first.row_) { - it->second.first.row_ = idx.first.row(); - } - - if (idx.first.column() < it->second.first.column_) { - it->second.first.column_ = idx.first.column(); - } - - if (idx.second.row() > it->second.second.row_) { - it->second.second.row_ = idx.second.row(); - } - - if (idx.second.column() > it->second.second.column_) { - it->second.second.column_ = idx.second.column(); - } - } else { - mapOfIdxs[idx.first.parent()] = std::make_pair( - {idx.first.row(), idx.first.column()}, - {idx.second.row(), idx.second.column()}); - } - } - - for (auto it = mapOfIdxs.cbegin(), last = mapOfIdxs.cend(); it != last; ++it) { - emit dataChanged(index(it->second.first.row_, it->second.first.column_, it->first), - index(it->second.second.row_, it->second.second.column_, it->first)); - } - } -} - -void QuoteRequestsModel::deleteSettlement(const std::string &id) -{ - updateSettlementCounters(); - - auto it = settlContainers_.find(id); - if (it != settlContainers_.end()) { - pendingDeleteIds_.insert(id); - it->second->deactivate(); - settlContainers_.erase(it); - } -} - -void QuoteRequestsModel::updateSettlementCounters() -{ - auto * market = findMarket(groupNameSettlements_); - - if (market) { - const int row = findMarket(&market->idx_); - - emit dataChanged(createIndex(row, static_cast(Column::Product), &market->idx_), - createIndex(row, static_cast(Column::Side), &market->idx_)); - } -} - -void QuoteRequestsModel::forSpecificId(const std::string &reqId, const cbItem &cb) -{ - for (size_t i = 0; i < data_.size(); ++i) { - if (!data_[i]->settl_.rfqs_.empty()) { - for (size_t k = 0; k < data_[i]->settl_.rfqs_.size(); ++k) { - if (data_[i]->settl_.rfqs_[k]->reqId_ == reqId) { - cb(&data_[i]->settl_, static_cast(k)); - return; - } - } - } - - for (size_t j = 0; j < data_[i]->groups_.size(); ++j) { - for (size_t k = 0; k < data_[i]->groups_[j]->rfqs_.size(); ++k) { - if (data_[i]->groups_[j]->rfqs_[k]->reqId_ == reqId) { - cb(data_[i]->groups_[j].get(), static_cast(k)); - return; - } - } - } - } -} - -void QuoteRequestsModel::forEachSecurity(const QString &security, const cbItem &cb) -{ - for (size_t i = 0; i < data_.size(); ++i) { - for (size_t j = 0; j < data_[i]->groups_.size(); ++j) { - if (data_[i]->groups_[j]->security_ != security) - continue; - for (size_t k = 0; k < data_[i]->groups_[j]->rfqs_.size(); ++k) { - cb(data_[i]->groups_[j].get(), static_cast(k)); - } - } - } -} - -const bs::network::QuoteReqNotification &QuoteRequestsModel::getQuoteReqNotification(const std::string &id) const -{ - static bs::network::QuoteReqNotification emptyQRN; - - auto itQRN = notifications_.find(id); - if (itQRN == notifications_.end()) return emptyQRN; - return itQRN->second; -} - -double QuoteRequestsModel::getPrice(const std::string &security, Role role) const -{ - const auto itMDP = mdPrices_.find(security); - if (itMDP != mdPrices_.end()) { - const auto itP = itMDP->second.find(role); - if (itP != itMDP->second.end()) { - return itP->second; - } - } - return 0; -} - -QString QuoteRequestsModel::getMarketSecurity(const QModelIndex &index) -{ - for (auto parent = index; parent.isValid(); parent = parent.parent()) { - IndexHelper *idx = static_cast(parent.internalPointer()); - - if (idx->type_ != DataType::Market) { - continue; - } - - Market *marketInfo = static_cast(idx->data_); - if (marketInfo) { - return marketInfo->security_; - } - } - - return {}; -} - -QString QuoteRequestsModel::quoteReqStatusDesc(bs::network::QuoteReqNotification::Status status) -{ - switch (status) { - case bs::network::QuoteReqNotification::Withdrawn: return tr("Withdrawn"); - case bs::network::QuoteReqNotification::PendingAck: return tr("PendingAck"); - case bs::network::QuoteReqNotification::Replied: return tr("Replied"); - case bs::network::QuoteReqNotification::TimedOut: return tr("TimedOut"); - case bs::network::QuoteReqNotification::Rejected: return tr("Rejected"); - default: return QString(); - } -} - -QBrush QuoteRequestsModel::bgColorForStatus(bs::network::QuoteReqNotification::Status status) -{ - switch (status) { - case bs::network::QuoteReqNotification::Withdrawn: return Qt::magenta; - case bs::network::QuoteReqNotification::Rejected: return c_redColor; - case bs::network::QuoteReqNotification::Replied: return c_greenColor; - case bs::network::QuoteReqNotification::TimedOut: return c_yellowColor; - case bs::network::QuoteReqNotification::PendingAck: - default: return QBrush(); - } -} - -void QuoteRequestsModel::setStatus(const std::string &reqId, bs::network::QuoteReqNotification::Status status - , const QString &details) -{ - auto itQRN = notifications_.find(reqId); - if (itQRN != notifications_.end()) { - itQRN->second.status = status; - - forSpecificId(reqId, [this, status, details](Group *grp, int index) { - - auto *rfq = grp->rfqs_[static_cast(index)].get(); - - if (!details.isEmpty()) { - rfq->status_.status_ = details; - } - else { - rfq->status_.status_ = - quoteReqStatusDesc(status); - } - - bool emitUpdate = false; - - if (status == bs::network::QuoteReqNotification::Replied) { - assert(!rfq->withdrawn_); - - if (!rfq->quoted_) { - rfq->quoted_ = true; - ++grp->quotedRfqsCount_; - emitUpdate = true; - } - - if (rfq->visible_) { - rfq->visible_ = false; - --grp->visibleCount_; - showRfqsFromBack(grp); - emitUpdate = true; - } - - emit invalidateFilterModel(); - } - - if (status == bs::network::QuoteReqNotification::Withdrawn) { - if (rfq->quoted_) { - rfq->quoted_ = false; - --grp->quotedRfqsCount_; - emit invalidateFilterModel(); - emitUpdate = true; - } - rfq->withdrawn_ = true; - } - - rfq->stateBrush_ = bgColorForStatus(status); - - const bool showProgress = ((status == bs::network::QuoteReqNotification::Status::PendingAck) - || (status == bs::network::QuoteReqNotification::Status::Replied)); - grp->rfqs_[index]->status_.showProgress_ = showProgress; - - const QModelIndex idx = createIndex(index, static_cast(Column::Status), - &grp->rfqs_[index]->idx_); - emit dataChanged(idx, idx); - - if (emitUpdate) { - const QModelIndex gidx = createIndex(findGroup(&grp->idx_), 0, &grp->idx_); - emit dataChanged(gidx, gidx); - } - }); - - emit quoteReqNotifStatusChanged(itQRN->second); - } -} - -void QuoteRequestsModel::updatePrices(const QString &security, const bs::network::MDField &pxBid, - const bs::network::MDField &pxOffer, std::vector> *idxs) -{ - forEachSecurity(security, [pxBid, pxOffer, this, idxs](Group *grp, int index) { - - const auto& rfqOnIndex = grp->rfqs_[static_cast(index)]; - - const CurrencyPair cp(rfqOnIndex->securityDefinition_.toStdString()); - const bool isBuy = (rfqOnIndex->side_ == bs::network::Side::Buy) - ^ (cp.NumCurrency() == rfqOnIndex->product_.toStdString()); - double indicPrice = 0; - - if (isBuy && (pxBid.type != bs::network::MDField::Unknown)) { - indicPrice = pxBid.value; - } else if (!isBuy && (pxOffer.type != bs::network::MDField::Unknown)) { - indicPrice = pxOffer.value; - } - - if (indicPrice > 0) { - const auto prevPrice = rfqOnIndex->indicativePx_; - const auto assetType = rfqOnIndex->assetType_; - rfqOnIndex->indicativePxString_ = - UiUtils::displayPriceForAssetType(indicPrice, assetType); - rfqOnIndex->indicativePx_ = indicPrice; - - if (!qFuzzyIsNull(prevPrice)) { - if (indicPrice > prevPrice) { - rfqOnIndex->indicativePxBrush_ = c_greenColor; - } - else if (indicPrice < prevPrice) { - rfqOnIndex->indicativePxBrush_ = c_redColor; - } - } - - const QModelIndex idx = createIndex(index, static_cast(Column::IndicPx), - &(rfqOnIndex->idx_)); - - if (!idxs) { - emit dataChanged(idx, idx); - } else { - idxs->push_back(std::make_pair(idx, idx)); - } - } - }); -} - -void QuoteRequestsModel::showRfqsFromBack(Group *g) -{ - if (g->limit_ > 0) { - for (auto it = g->rfqs_.rbegin(), last = g->rfqs_.rend(); it != last; ++it) { - if (g->visibleCount_ < g->limit_) { - if (!(*it)->quoted_ && !(*it)->visible_) { - (*it)->visible_ = true; - ++g->visibleCount_; - } - } else { - break; - } - } - } -} - -void QuoteRequestsModel::showRfqsFromFront(Group *g) -{ - if (g->limit_ > 0) { - for (auto it = g->rfqs_.begin(), last = g->rfqs_.end(); it != last; ++it) { - if (g->visibleCount_ < g->limit_) { - if (!(*it)->quoted_ && !(*it)->visible_) { - (*it)->visible_ = true; - ++g->visibleCount_; - } - } else { - break; - } - } - } -} - -void QuoteRequestsModel::clearVisibleFlag(Group *g) -{ - if (g->limit_ > 0) { - for (auto it = g->rfqs_.begin(), last = g->rfqs_.end(); it != last; ++it) { - (*it)->visible_ = false; - } - - g->visibleCount_ = 0; - } -} - -void QuoteRequestsModel::onSecurityMDUpdated(const QString &security, const bs::network::MDFields &mdFields) -{ - // NOTE: - // there is duplicate for security name for future and XBT product XBT/EUR - // but that is fine for price updates, since it goes through all securities in all groups - - const auto pxBid = bs::network::MDField::get(mdFields, bs::network::MDField::PriceBid); - const auto pxOffer = bs::network::MDField::get(mdFields, bs::network::MDField::PriceOffer); - if (pxBid.type != bs::network::MDField::Unknown) { - mdPrices_[security.toStdString()][Role::BidPrice] = pxBid.value; - } - if (pxOffer.type != bs::network::MDField::Unknown) { - mdPrices_[security.toStdString()][Role::OfferPrice] = pxOffer.value; - } - - if (priceUpdateInterval_ < 1) { - updatePrices(security, pxBid, pxOffer); - } else { - prices_[security] = std::make_pair(pxBid, pxOffer); - } -} diff --git a/BlockSettleUILib/Trading/QuoteRequestsModel.h b/BlockSettleUILib/Trading/QuoteRequestsModel.h deleted file mode 100644 index 014c3d50a..000000000 --- a/BlockSettleUILib/Trading/QuoteRequestsModel.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef QUOTEREQUESTSMODEL_H -#define QUOTEREQUESTSMODEL_H - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "CommonTypes.h" - - -namespace bs { - namespace network { - struct QuoteNotification; - } - class StatsCollector; - class SecurityStatsCollector; - class SettlementContainer; -} -class AssetManager; -class ApplicationSettings; -class CelerClientQt; - -class QuoteRequestsModel : public QAbstractItemModel -{ - Q_OBJECT - -signals: - void quoteReqNotifStatusChanged(const bs::network::QuoteReqNotification &qrn); - void invalidateFilterModel(); - void deferredUpdate(const QPersistentModelIndex&); - -public: - enum class Column { - SecurityID = 0, - Product, - Side, - Quantity, - Party, - Status, - QuotedPx, - IndicPx, - BestPx, - Empty - }; - - enum class Role { - ReqId = Qt::UserRole, - Side, - ShowProgress, - Timeout, - TimeLeft, - BidPrice, - OfferPrice, - Grade, - AssetType, - QuotedPrice, - BestQPrice, - Product, - AllowFiltering, - HasHiddenChildren, - Quoted, - Type, - LimitOfRfqs, - QuotedRfqsCount, - Visible, - StatText, - CountOfRfqs, - SortOrder - }; - - enum class DataType { - RFQ = 0, - Group, - Market, - Unknown - }; - -public: - [[deprecated]] QuoteRequestsModel(const std::shared_ptr & - , std::shared_ptr celerClient - , std::shared_ptr appSettings - , QObject* parent); - QuoteRequestsModel(const std::shared_ptr& - , QObject* parent); - ~QuoteRequestsModel() override; - - QuoteRequestsModel(const QuoteRequestsModel&) = delete; - QuoteRequestsModel& operator=(const QuoteRequestsModel&) = delete; - QuoteRequestsModel(QuoteRequestsModel&&) = delete; - QuoteRequestsModel& operator=(QuoteRequestsModel&&) = delete; - - [[deprecated]] void SetAssetManager(const std::shared_ptr& assetManager); - const bs::network::QuoteReqNotification &getQuoteReqNotification(const std::string &id) const; - double getPrice(const std::string &security, Role) const; - QString getMarketSecurity(const QModelIndex &index); - - void addSettlementContainer(const std::shared_ptr &); - bool StartCCSignOnOrder(const QString& orderId, QDateTime timestamp); - - void onSettlementPending(const BinaryData& settlementId, int timeLeftMS); - void onSettlementComplete(const BinaryData& settlementId); - -public: - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &index) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - -public: - void onQuoteReqNotifReplied(const bs::network::QuoteNotification &qn); - void onQuoteNotifCancelled(const QString &reqId); - void onAllQuoteNotifCancelled(const QString &reqId); - void onQuoteReqCancelled(const QString &reqId, bool byUser); - void onQuoteRejected(const QString &reqId, const QString &reason); - void onSecurityMDUpdated(const QString &security, const bs::network::MDFields &); - void onQuoteReqNotifReceived(const bs::network::QuoteReqNotification &qrn); - void onBestQuotePrice(const QString reqId, double price, bool own); - void limitRfqs(const QModelIndex &index, int limit); - QModelIndex findMarketIndex(const QString &name) const; - void setPriceUpdateInterval(int interval); - void showQuotedRfqs(bool on = true); - -private slots: - void ticker(); - void clearModel(); - void onDeferredUpdate(const QPersistentModelIndex &index); - void onPriceUpdateTimer(); - -private: - using Prices = std::map; - using MDPrices = std::unordered_map; - - std::shared_ptr assetManager_; - std::unordered_map notifications_; - std::unordered_map> settlContainers_; - std::unordered_map settlTimeLeft_; - QTimer timer_; - QTimer priceUpdateTimer_; - MDPrices mdPrices_; - const QString groupNameSettlements_ = tr("Settlements"); - std::shared_ptr secStatsCollector_; - std::shared_ptr celerClient_; - std::shared_ptr appSettings_; - std::unordered_set pendingDeleteIds_; - int priceUpdateInterval_; - bool showQuoted_; - - struct IndexHelper { - IndexHelper *parent_; - void *data_; - DataType type_; - - IndexHelper() - : parent_(nullptr) - , data_(nullptr) - , type_(DataType::Unknown) - {} - - IndexHelper(IndexHelper *parent, void *data, DataType type) - : parent_(parent) - , data_(data) - , type_(type) - {} - }; - - struct Status { - QString status_; - bool showProgress_; - int timeout_; - int timeleft_; - - Status() - : showProgress_(false) - , timeout_(0) - , timeleft_(0) - {} - - Status(const QString &status, - bool showProgress, - int timeout = 0, - int timeleft = 0) - : status_(status) - , showProgress_(showProgress) - , timeout_(timeout) - , timeleft_(timeleft) - {} - }; - - struct RFQ { - QString securityDefinition_; - QString product_; - QString sideString_; - QString party_; - QString quantityString_; - QString quotedPriceString_; - QString indicativePxString_; - QString bestQuotedPxString_; - Status status_; - double indicativePx_; - double quotedPrice_; - double bestQuotedPx_; - bs::network::Side::Type side_; - bs::network::Asset::Type assetType_; - std::string reqId_; - QBrush quotedPriceBrush_; - QBrush indicativePxBrush_; - QBrush stateBrush_; - IndexHelper idx_; - bool quoted_; - bool visible_; - bool withdrawn_ = false; - - RFQ() - : idx_(nullptr, this, DataType::RFQ) - , quoted_(false) - , visible_(false) - {} - - RFQ(const QString &securityDefinition, - const QString &product, - const QString &sideString, - const QString &party, - const QString &quantityString, - const QString "edPriceString, - const QString &indicativePxString, - const QString &bestQuotedPxString, - const Status &status, - double indicativePx, - double quotedPrice, - double bestQuotedPx, - bs::network::Side::Type side, - bs::network::Asset::Type assetType, - const std::string &reqId) - : securityDefinition_(securityDefinition) - , product_(product) - , sideString_(sideString) - , party_(party) - , quantityString_(quantityString) - , quotedPriceString_(quotedPriceString) - , indicativePxString_(indicativePxString) - , bestQuotedPxString_(bestQuotedPxString) - , status_(status) - , indicativePx_(indicativePx) - , quotedPrice_(quotedPrice) - , bestQuotedPx_(bestQuotedPx) - , side_(side) - , assetType_(assetType) - , reqId_(reqId) - , idx_(nullptr, this, DataType::RFQ) - , quoted_(false) - , visible_(false) - {} - }; - - struct Group { - QString security_; - QFont font_; - std::vector> rfqs_; - IndexHelper idx_; - int limit_; - int quotedRfqsCount_; - int visibleCount_; - - Group() - : idx_(nullptr, this, DataType::Group) - , limit_(5) - , quotedRfqsCount_(0) - , visibleCount_(0) - {} - - explicit Group(const QString &security, int limit, const QFont & font = QFont()) - : security_(security) - , font_(font) - , idx_(nullptr, this, DataType::Group) - , limit_(limit) - , quotedRfqsCount_(0) - , visibleCount_(0) - {} - }; - - struct Market { - QString security_; - QFont font_; - std::vector> groups_; - IndexHelper idx_; - Group settl_; - int limit_; - - Market() - : idx_(nullptr, this, DataType::Market) - , limit_(5) - { - settl_.idx_ = idx_; - } - - explicit Market(const QString &security, int limit, const QFont & font = QFont()) - : security_(security) - , font_(font) - , idx_(nullptr, this, DataType::Market) - , limit_(limit) - { - settl_.idx_ = idx_; - } - }; - - std::vector> data_; - - struct BestQuotePrice { - double price_; - bool own_; - }; - - std::map bestQuotePrices_; - std::map> prices_; - -private: - int findGroup(IndexHelper *idx) const; - Group* findGroup(Market *market, const QString &security) const; - int findMarket(IndexHelper *idx) const; - Market* findMarket(const QString &name) const; - void updatePrices(const QString &security, const bs::network::MDField &pxBid, - const bs::network::MDField &pxOffer, std::vector> *idxs = nullptr); - void showRfqsFromBack(Group *g); - void showRfqsFromFront(Group *g); - void clearVisibleFlag(Group *g); - void updateBestQuotePrice(const QString &reqId, double price, bool own, - std::vector> *idxs = nullptr); - -private: - using cbItem = std::function; - - void insertRfq(Group *group, const bs::network::QuoteReqNotification &qrn); - void forSpecificId(const std::string &, const cbItem &); - void forEachSecurity(const QString &, const cbItem &); - void setStatus(const std::string &reqId, bs::network::QuoteReqNotification::Status, const QString &details = {}); - void updateSettlementCounters(); - void deleteSettlement(const std::string &id); - static QString quoteReqStatusDesc(bs::network::QuoteReqNotification::Status status); - static QBrush bgColorForStatus(bs::network::QuoteReqNotification::Status status); - static QBrush colorForQuotedPrice(double quotedPx, double bestQuotedPx, bool own = false); -}; - -#endif // QUOTEREQUESTSMODEL_H diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp b/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp deleted file mode 100644 index 69643a8c0..000000000 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.cpp +++ /dev/null @@ -1,641 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "QuoteRequestsWidget.h" -#include "ui_QuoteRequestsWidget.h" -#include - -#include "AssetManager.h" -#include "CurrencyPair.h" -#include "NotificationCenter.h" -#include "QuoteProvider.h" -#include "RFQBlotterTreeView.h" -#include "SettlementContainer.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" - -#include -#include -#include -#include - -namespace bs { - -void StatsCollector::saveState() -{ -} - -} /* namespace bs */ - - -// -// DoNotDrawSelectionDelegate -// - -//! This delegate just clears selection bit and paints item as -//! unselected always. -class DoNotDrawSelectionDelegate final : public QStyledItemDelegate -{ -public: - explicit DoNotDrawSelectionDelegate(QObject *parent) : QStyledItemDelegate(parent) {} - - void paint(QPainter *painter, const QStyleOptionViewItem &opt, - const QModelIndex &index) const override - { - QStyleOptionViewItem changedOpt = opt; - changedOpt.state &= ~(QStyle::State_Selected); - - QStyledItemDelegate::paint(painter, changedOpt, index); - } -}; // class DoNotDrawSelectionDelegate - - -QuoteRequestsWidget::QuoteRequestsWidget(QWidget* parent) - : QWidget(parent) - , ui_(new Ui::QuoteRequestsWidget()) - , model_(nullptr) - , sortModel_(nullptr) -{ - ui_->setupUi(this); - ui_->treeViewQuoteRequests->setUniformRowHeights(true); - - connect(ui_->treeViewQuoteRequests, &QTreeView::clicked, this, &QuoteRequestsWidget::onQuoteReqNotifSelected); - connect(ui_->treeViewQuoteRequests, &QTreeView::doubleClicked, this, &QuoteRequestsWidget::onQuoteReqNotifSelected); -} - -QuoteRequestsWidget::~QuoteRequestsWidget() = default; - -void QuoteRequestsWidget::init(std::shared_ptr logger - , const std::shared_ptr "eProvider - , const std::shared_ptr& assetManager - , const std::shared_ptr &statsCollector - , const std::shared_ptr &appSettings - , std::shared_ptr celerClient) -{ - logger_ = logger; - assetManager_ = assetManager; - appSettings_ = appSettings; - collapsed_ = appSettings_->get(ApplicationSettings::Filter_MD_QN); - dropQN_ = appSettings->get(ApplicationSettings::dropQN); - - model_ = new QuoteRequestsModel(statsCollector, celerClient, appSettings_, - ui_->treeViewQuoteRequests); - model_->SetAssetManager(assetManager); - - sortModel_ = new QuoteReqSortModel(model_, this); - sortModel_->setSourceModel(model_); - sortModel_->showQuoted(appSettings_->get(ApplicationSettings::ShowQuoted)); - - ui_->treeViewQuoteRequests->setModel(sortModel_); - ui_->treeViewQuoteRequests->setRfqModel(model_); - ui_->treeViewQuoteRequests->setSortModel(sortModel_); - ui_->treeViewQuoteRequests->setAppSettings(appSettings_); - ui_->treeViewQuoteRequests->header()->setSectionResizeMode( - static_cast(QuoteRequestsModel::Column::SecurityID), - QHeaderView::ResizeToContents); - - connect(ui_->treeViewQuoteRequests, &QTreeView::collapsed, - this, &QuoteRequestsWidget::onCollapsed); - connect(ui_->treeViewQuoteRequests, &QTreeView::expanded, - this, &QuoteRequestsWidget::onExpanded); - connect(model_, &QuoteRequestsModel::quoteReqNotifStatusChanged, [this](const bs::network::QuoteReqNotification &qrn) { - emit quoteReqNotifStatusChanged(qrn); - }); - connect(model_, &QAbstractItemModel::rowsInserted, this, &QuoteRequestsWidget::onRowsInserted); - connect(model_, &QAbstractItemModel::rowsRemoved, this, &QuoteRequestsWidget::onRowsRemoved); - connect(sortModel_, &QSortFilterProxyModel::rowsInserted, this, &QuoteRequestsWidget::onRowsChanged); - connect(sortModel_, &QSortFilterProxyModel::rowsRemoved, this, &QuoteRequestsWidget::onRowsChanged); - - connect(quoteProvider.get(), &QuoteProvider::quoteReqNotifReceived, this, &QuoteRequestsWidget::onQuoteRequest); - connect(appSettings.get(), &ApplicationSettings::settingChanged, this, &QuoteRequestsWidget::onSettingChanged); - - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::Status), new RequestsProgressDelegate(ui_->treeViewQuoteRequests)); - - auto *doNotDrawSelectionDelegate = new DoNotDrawSelectionDelegate(ui_->treeViewQuoteRequests); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::QuotedPx), - doNotDrawSelectionDelegate); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::IndicPx), - doNotDrawSelectionDelegate); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::BestPx), - doNotDrawSelectionDelegate); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::Empty), - doNotDrawSelectionDelegate); - - const auto opt = ui_->treeViewQuoteRequests->viewOptions(); - const int width = opt.fontMetrics.boundingRect(tr("No quote received")).width() + 10; - ui_->treeViewQuoteRequests->header()->resizeSection( - static_cast(QuoteRequestsModel::Column::Status), - width); -} - -void QuoteRequestsWidget::init(const std::shared_ptr&logger - , const std::shared_ptr& statsCollector) -{ - logger_ = logger; - - dropQN_ = false; //FIXME - - model_ = new QuoteRequestsModel(statsCollector, ui_->treeViewQuoteRequests); - - sortModel_ = new QuoteReqSortModel(model_, this); - sortModel_->setSourceModel(model_); - sortModel_->showQuoted(true); //FIXME - - ui_->treeViewQuoteRequests->setModel(sortModel_); - ui_->treeViewQuoteRequests->setRfqModel(model_); - ui_->treeViewQuoteRequests->setSortModel(sortModel_); - ui_->treeViewQuoteRequests->header()->setSectionResizeMode( - static_cast(QuoteRequestsModel::Column::SecurityID), - QHeaderView::ResizeToContents); - - connect(ui_->treeViewQuoteRequests, &QTreeView::collapsed, - this, &QuoteRequestsWidget::onCollapsed); - connect(ui_->treeViewQuoteRequests, &QTreeView::expanded, - this, &QuoteRequestsWidget::onExpanded); - connect(model_, &QuoteRequestsModel::quoteReqNotifStatusChanged, [this](const bs::network::QuoteReqNotification& qrn) { - emit quoteReqNotifStatusChanged(qrn); - }); - connect(model_, &QAbstractItemModel::rowsInserted, this, &QuoteRequestsWidget::onRowsInserted); - connect(model_, &QAbstractItemModel::rowsRemoved, this, &QuoteRequestsWidget::onRowsRemoved); - connect(sortModel_, &QSortFilterProxyModel::rowsInserted, this, &QuoteRequestsWidget::onRowsChanged); - connect(sortModel_, &QSortFilterProxyModel::rowsRemoved, this, &QuoteRequestsWidget::onRowsChanged); - - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::Status), new RequestsProgressDelegate(ui_->treeViewQuoteRequests)); - - auto* doNotDrawSelectionDelegate = new DoNotDrawSelectionDelegate(ui_->treeViewQuoteRequests); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::QuotedPx), - doNotDrawSelectionDelegate); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::IndicPx), - doNotDrawSelectionDelegate); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::BestPx), - doNotDrawSelectionDelegate); - ui_->treeViewQuoteRequests->setItemDelegateForColumn( - static_cast(QuoteRequestsModel::Column::Empty), - doNotDrawSelectionDelegate); - - const auto opt = ui_->treeViewQuoteRequests->viewOptions(); - const int width = opt.fontMetrics.boundingRect(tr("No quote received")).width() + 10; - ui_->treeViewQuoteRequests->header()->resizeSection( - static_cast(QuoteRequestsModel::Column::Status), - width); -} - -void QuoteRequestsWidget::onQuoteReqNotifSelected(const QModelIndex& index) -{ - const auto quoteIndex = sortModel_->index(index.row(), 0, index.parent()); - std::string qId = sortModel_->data(quoteIndex, - static_cast(QuoteRequestsModel::Role::ReqId)).toString().toStdString(); - const bs::network::QuoteReqNotification &qrn = model_->getQuoteReqNotification(qId); - - double bidPx = model_->getPrice(qrn.security, QuoteRequestsModel::Role::BidPrice); - double offerPx = model_->getPrice(qrn.security, QuoteRequestsModel::Role::OfferPrice); - const double bestQPx = sortModel_->data(quoteIndex, - static_cast(QuoteRequestsModel::Role::BestQPrice)).toDouble(); - if (!qFuzzyIsNull(bestQPx)) { - CurrencyPair cp(qrn.security); - bool isBuy = (qrn.side == bs::network::Side::Buy) ^ (cp.NumCurrency() == qrn.product); - const double quotedPx = sortModel_->data(quoteIndex, - static_cast(QuoteRequestsModel::Role::QuotedPrice)).toDouble(); - const auto pip = qFuzzyCompare(bestQPx, quotedPx) ? 0.0 : std::pow(10, -UiUtils::GetPricePrecisionForAssetType(qrn.assetType)); - if (isBuy) { - bidPx = bestQPx + pip; - } - else { - offerPx = bestQPx - pip; - } - } - - const QString productGroup = model_->getMarketSecurity(sortModel_->mapToSource(index)); - - emit Selected(productGroup, qrn, bidPx, offerPx); -} - -void QuoteRequestsWidget::addSettlementContainer(const std::shared_ptr &container) -{ - if (model_) { - model_->addSettlementContainer(container); - } -} - -bool QuoteRequestsWidget::StartCCSignOnOrder(const QString& orderId, QDateTime timestamp) -{ - if (model_) { - return model_->StartCCSignOnOrder(orderId, timestamp); - } - - return false; -} - -RFQBlotterTreeView* QuoteRequestsWidget::view() const -{ - return ui_->treeViewQuoteRequests; -} - -void QuoteRequestsWidget::onSettlementPending(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) -{ - if (model_) { - model_->onSettlementPending(settlementId, timeLeftMS); - } -} - -void QuoteRequestsWidget::onSettlementComplete(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId) -{ - if (model_) { - model_->onSettlementComplete(settlementId); - } -} - -void QuoteRequestsWidget::onQuoteReqNotifReplied(const bs::network::QuoteNotification &qn) -{ - if (model_) { - model_->onQuoteReqNotifReplied(qn); - } -} - -void QuoteRequestsWidget::onQuoteNotifCancelled(const QString &reqId) -{ - if (model_) { - model_->onQuoteNotifCancelled(reqId); - } -} - -void QuoteRequestsWidget::onAllQuoteNotifCancelled(const QString &reqId) -{ - if (model_) { - model_->onAllQuoteNotifCancelled(reqId); - } -} - -void QuoteRequestsWidget::onQuoteReqCancelled(const QString &reqId, bool byUser) -{ - if (model_) { - model_->onQuoteReqCancelled(reqId, byUser); - } -} - -void QuoteRequestsWidget::onQuoteRejected(const QString &reqId, const QString &reason) -{ - if (model_) { - model_->onQuoteRejected(reqId, reason); - } -} - -void QuoteRequestsWidget::onBestQuotePrice(const QString reqId, double price, bool own) -{ - if (model_) { - model_->onBestQuotePrice(reqId, price, own); - } -} - -void QuoteRequestsWidget::onSecurityMDUpdated(bs::network::Asset::Type assetType, const QString &security, bs::network::MDFields mdFields) -{ - Q_UNUSED(assetType); - if (model_ && !mdFields.empty()) { - model_->onSecurityMDUpdated(security, mdFields); - } -} - -void QuoteRequestsWidget::onQuoteRequest(const bs::network::QuoteReqNotification &qrn) -{ - const bool includeZc = - (qrn.assetType == bs::network::Asset::SpotXBT) - ? bs::UTXOReservationManager::kIncludeZcDealerSpotXbt - : bs::UTXOReservationManager::kIncludeZcDealerCc; - - if (dropQN_) { - bool checkResult = true; - if (qrn.side == bs::network::Side::Buy) { - checkResult = assetManager_->checkBalance(qrn.product, qrn.quantity, includeZc); - } - else { - CurrencyPair cp(qrn.security); - checkResult = assetManager_->checkBalance(cp.ContraCurrency(qrn.product), 0.01, includeZc); - } - if (!checkResult) { - return; - } - } - if (model_ != nullptr) { - model_->onQuoteReqNotifReceived(qrn); - } -} - -void QuoteRequestsWidget::onSettingChanged(int setting, QVariant val) -{ - switch (static_cast(setting)) - { - case ApplicationSettings::dropQN: - dropQN_ = val.toBool(); - break; - - case ApplicationSettings::FxRfqLimit : - ui_->treeViewQuoteRequests->setLimit(ApplicationSettings::FxRfqLimit, val.toInt()); - break; - - case ApplicationSettings::XbtRfqLimit : - ui_->treeViewQuoteRequests->setLimit(ApplicationSettings::XbtRfqLimit, val.toInt()); - break; - - case ApplicationSettings::PmRfqLimit : - ui_->treeViewQuoteRequests->setLimit(ApplicationSettings::PmRfqLimit, val.toInt()); - break; - - case ApplicationSettings::PriceUpdateInterval : - model_->setPriceUpdateInterval(val.toInt()); - break; - - case ApplicationSettings::ShowQuoted : - sortModel_->showQuoted(val.toBool()); - break; - - default: - break; - } -} - -void QuoteRequestsWidget::onRowsChanged() -{ - unsigned int cntChildren = 0; - for (int row = 0; row < sortModel_->rowCount(); row++) { - cntChildren += sortModel_->rowCount(sortModel_->index(row, 0)); - } - NotificationCenter::notify(bs::ui::NotifyType::DealerQuotes, { cntChildren }); -} - -void QuoteRequestsWidget::onRowsInserted(const QModelIndex &parent, int first, int last) -{ - for (int row = first; row <= last; row++) { - const auto &index = model_->index(row, 0, parent); - if (index.data(static_cast(QuoteRequestsModel::Role::ReqId)).isNull()) { - expandIfNeeded(); - } - else { - for (int i = 1; i < sortModel_->columnCount(); ++i) { - if (i != static_cast(QuoteRequestsModel::Column::Status)) { - ui_->treeViewQuoteRequests->resizeColumnToContents(i); - } - } - } - } -} - -void QuoteRequestsWidget::onRowsRemoved(const QModelIndex &, int, int) -{ - const auto &indices = ui_->treeViewQuoteRequests->selectionModel()->selectedIndexes(); - if (indices.isEmpty()) { - emit Selected({}, bs::network::QuoteReqNotification(), 0, 0); - } - else { - onQuoteReqNotifSelected(indices.first()); - } -} - -void QuoteRequestsWidget::onCollapsed(const QModelIndex &index) -{ - if (index.isValid()) { - collapsed_.append(UiUtils::modelPath(sortModel_->mapToSource(index), model_)); - saveCollapsedState(); - } -} - -void QuoteRequestsWidget::onExpanded(const QModelIndex &index) -{ - if (index.isValid()) { - collapsed_.removeOne(UiUtils::modelPath(sortModel_->mapToSource(index), model_)); - saveCollapsedState(); - } -} - -void QuoteRequestsWidget::saveCollapsedState() -{ - if (appSettings_) { - appSettings_->set(ApplicationSettings::Filter_MD_QN, collapsed_); - } - else { - emit putSetting(ApplicationSettings::Filter_MD_QN, collapsed_); - } -} - -void QuoteRequestsWidget::expandIfNeeded(const QModelIndex &index) -{ - if (!collapsed_.contains(UiUtils::modelPath(sortModel_->mapToSource(index), model_))) - ui_->treeViewQuoteRequests->expand(index); - - for (int i = 0; i < sortModel_->rowCount(index); ++i) - expandIfNeeded(sortModel_->index(i, 0, index)); -} - -bs::SecurityStatsCollector::SecurityStatsCollector(const std::shared_ptr appSettings - , ApplicationSettings::Setting param) - : appSettings_(appSettings), param_(param) -{ - const auto map = appSettings_->get(param); - for (auto it = map.begin(); it != map.end(); ++it) { - counters_[it.key().toStdString()] = it.value().toUInt(); - } - connect(appSettings.get(), &ApplicationSettings::settingChanged, this, &bs::SecurityStatsCollector::onSettingChanged); - - gradeColors_ = { QColor(Qt::white), QColor(Qt::lightGray), QColor(Qt::gray), QColor(Qt::darkGray) }; - gradeBoundary_.resize(gradeColors_.size(), 0); - - timer_.setInterval(60 * 1000); // once in a minute - connect(&timer_, &QTimer::timeout, this, &bs::SecurityStatsCollector::saveState); - timer_.start(); -} - -bs::SecurityStatsCollector::SecurityStatsCollector(ApplicationSettings::Setting param) - : param_(param) -{ - gradeColors_ = { QColor(Qt::white), QColor(Qt::lightGray), QColor(Qt::gray), QColor(Qt::darkGray) }; - gradeBoundary_.resize(gradeColors_.size(), 0); - - timer_.setInterval(60 * 1000); // once in a minute - connect(&timer_, &QTimer::timeout, this, &bs::SecurityStatsCollector::saveState); - timer_.start(); -} - -void bs::SecurityStatsCollector::saveState() -{ - if (!modified_) { - return; - } - QVariantMap map; - for (const auto counter : counters_) { - map[QString::fromStdString(counter.first)] = counter.second; - } - if (appSettings_) { - appSettings_->set(param_, map); - } - modified_ = false; -} - -void bs::SecurityStatsCollector::onSettingChanged(int setting, QVariant val) -{ - if ((static_cast(setting) == param_) && val.toMap().empty()) { - resetCounters(); - } -} - -void bs::SecurityStatsCollector::onQuoteSubmitted(const bs::network::QuoteNotification &qn) -{ - counters_[qn.security]++; - modified_ = true; - recalculate(); -} - -unsigned int bs::SecurityStatsCollector::getGradeFor(const std::string &security) const -{ - const auto itSec = counters_.find(security); - if (itSec == counters_.end()) { - return gradeBoundary_.size() - 1; - } - for (size_t i = 0; i < gradeBoundary_.size(); i++) { - if (itSec->second <= gradeBoundary_[i]) { - return gradeBoundary_.size() - 1 - i; - } - } - return 0; -} - -QColor bs::SecurityStatsCollector::getColorFor(const std::string &security) const -{ - return gradeColors_[getGradeFor(security)]; -} - -void bs::SecurityStatsCollector::resetCounters() -{ - counters_.clear(); - modified_ = true; - recalculate(); -} - -void bs::SecurityStatsCollector::recalculate() -{ - std::vector counts; - for (const auto &counter : counters_) { - counts.push_back(counter.second); - } - std::sort(counts.begin(), counts.end()); - - if (counts.size() < gradeBoundary_.size()) { - for (unsigned int i = 0; i < counts.size(); i++) { - gradeBoundary_[gradeBoundary_.size() - counts.size() + i] = counts[i]; - } - } - else { - const unsigned int step = counts.size() / gradeBoundary_.size(); - for (unsigned int i = 0; i < gradeBoundary_.size(); i++) { - gradeBoundary_[i] = counts[qMin((i + 1) * step, counts.size() - 1)]; - } - } -} - - -QColor bs::SettlementStatsCollector::getColorFor(const std::string &) const -{ - return QColor(Qt::cyan); -} - -unsigned int bs::SettlementStatsCollector::getGradeFor(const std::string &) const -{ - return container_->timeLeftMs(); -} - - -QuoteReqSortModel::QuoteReqSortModel(QuoteRequestsModel *model, QObject *parent) - : QSortFilterProxyModel(parent) - , model_(model) - , showQuoted_(true) -{ - connect(model_, &QuoteRequestsModel::invalidateFilterModel, - this, &QuoteReqSortModel::invalidateFilter); -} - -bool QuoteReqSortModel::filterAcceptsRow(int row, const QModelIndex &parent) const -{ - const auto index = sourceModel()->index(row, 0, parent); - - if (!index.isValid()) { - return false; - } - - if (index.data(static_cast(QuoteRequestsModel::Role::Type)).toInt() != - static_cast(QuoteRequestsModel::DataType::RFQ)) { - return true; - } - - if (parent.data(static_cast(QuoteRequestsModel::Role::LimitOfRfqs)).toInt() <= 0) { - return true; - } - - if (index.data(static_cast(QuoteRequestsModel::Role::Visible)).toBool()) { - return true; - } - else if (index.data(static_cast(QuoteRequestsModel::Role::Quoted)).toBool() && - showQuoted_) { - return true; - } - - return false; -} - -void QuoteReqSortModel::showQuoted(bool on) -{ - if (showQuoted_ != on) { - showQuoted_ = on; - - model_->showQuotedRfqs(on); - - invalidateFilter(); - } -} - -bool QuoteReqSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const auto leftGrade = sourceModel()->data(left, - static_cast(QuoteRequestsModel::Role::Grade)); - const auto rightGrade = sourceModel()->data(right, - static_cast(QuoteRequestsModel::Role::Grade)); - if (leftGrade != rightGrade) { - return (leftGrade < rightGrade); - } - const auto leftTL = sourceModel()->data(left, - static_cast(QuoteRequestsModel::Role::SortOrder)); - const auto rightTL = sourceModel()->data(right, - static_cast(QuoteRequestsModel::Role::SortOrder)); - - return (leftTL < rightTL); -} - -bool RequestsProgressDelegate::isDrawProgressBar(const QModelIndex& index) const -{ - return index.data(static_cast(QuoteRequestsModel::Role::ShowProgress)).toBool(); -} - -int RequestsProgressDelegate::maxValue(const QModelIndex& index) const -{ - return index.data(static_cast(QuoteRequestsModel::Role::Timeout)).toInt(); -} - -int RequestsProgressDelegate::currentValue(const QModelIndex& index) const -{ - return index.data(static_cast(QuoteRequestsModel::Role::TimeLeft)).toInt(); -} diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.h b/BlockSettleUILib/Trading/QuoteRequestsWidget.h deleted file mode 100644 index 5083fe0e8..000000000 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef QUOTE_REQUESTS_WIDGET_H -#define QUOTE_REQUESTS_WIDGET_H - -#include "ApplicationSettings.h" -#include "QuoteRequestsModel.h" -#include "ProgressViewDelegateBase.h" - -#include -#include -#include -#include - -#include -#include -#include - -namespace Ui { - class QuoteRequestsWidget; -}; -namespace spdlog { - class logger; -} -class QuoteProvider; - -namespace bs { - class SettlementContainer; - namespace network { - struct QuoteReqNotification; - struct QuoteNotification; - } - - class StatsCollector - { - public: - virtual ~StatsCollector() noexcept = default; - virtual QColor getColorFor(const std::string &key) const = 0; - virtual unsigned int getGradeFor(const std::string &key) const = 0; - virtual void saveState(); - }; - - class SecurityStatsCollector : public QObject, public StatsCollector - { - Q_OBJECT - public: - [[deprecated]] SecurityStatsCollector(const std::shared_ptr - , ApplicationSettings::Setting param); - SecurityStatsCollector(ApplicationSettings::Setting param); - - QColor getColorFor(const std::string &key) const override; - unsigned int getGradeFor(const std::string &key) const override; - - void saveState() override; - - public slots: - void onQuoteSubmitted(const network::QuoteNotification &); - - private slots: - void onSettingChanged(int setting, QVariant val); - - private: - std::shared_ptr appSettings_; - ApplicationSettings::Setting param_; - std::unordered_map counters_; - std::vector gradeColors_; - std::vector gradeBoundary_; - QTimer timer_; - bool modified_ = false; - - private: - void recalculate(); - void resetCounters(); - }; - - class SettlementStatsCollector : public StatsCollector - { - public: - SettlementStatsCollector(const std::shared_ptr &container) - : container_(container) {} - QColor getColorFor(const std::string &key) const override; - unsigned int getGradeFor(const std::string &key) const override; - - private: - std::shared_ptr container_; - }; - -} // namespace bs - - -class RequestsProgressDelegate : public ProgressViewDelegateBase -{ -public: - explicit RequestsProgressDelegate(QWidget* parent = nullptr) - : ProgressViewDelegateBase(parent) - {} - ~RequestsProgressDelegate() override = default; -protected: - bool isDrawProgressBar(const QModelIndex& index) const override; - int maxValue(const QModelIndex& index) const override; - int currentValue(const QModelIndex& index) const override; -}; - - -class AssetManager; -class CelerClientQt; -class QuoteRequestsModel; -class QuoteReqSortModel; -class RFQBlotterTreeView; - -class QuoteRequestsWidget : public QWidget -{ -Q_OBJECT - -public: - QuoteRequestsWidget(QWidget* parent = nullptr); - ~QuoteRequestsWidget() override; - - [[deprecated]] void init(std::shared_ptr logger, const std::shared_ptr "eProvider - , const std::shared_ptr& assetManager - , const std::shared_ptr &statsCollector - , const std::shared_ptr &appSettings - , std::shared_ptr celerClient); - void init(const std::shared_ptr& - , const std::shared_ptr& statsCollector); - - [[deprecated]] void addSettlementContainer(const std::shared_ptr &); - bool StartCCSignOnOrder(const QString& orderId, QDateTime timestamp); - - RFQBlotterTreeView* view() const; - - void onSettlementPending(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId, int timeLeftMS); - void onSettlementComplete(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId); - -signals: - void Selected(const QString& productGroup, const bs::network::QuoteReqNotification& qrc, double indicBid, double indicAsk); - void quoteReqNotifStatusChanged(const bs::network::QuoteReqNotification &qrn); - void putSetting(ApplicationSettings::Setting, const QVariant&); - -public slots: - void onQuoteRequest(const bs::network::QuoteReqNotification& qrn); - void onQuoteReqNotifReplied(const bs::network::QuoteNotification &); - void onQuoteReqNotifSelected(const QModelIndex& index); - void onQuoteNotifCancelled(const QString &reqId); - void onAllQuoteNotifCancelled(const QString &reqId); - void onQuoteReqCancelled(const QString &reqId, bool byUser); - void onQuoteRejected(const QString &reqId, const QString &reason); - void onSecurityMDUpdated(bs::network::Asset::Type, const QString &security, bs::network::MDFields); - void onBestQuotePrice(const QString reqId, double price, bool own); - -private slots: - void onSettingChanged(int setting, QVariant val); - void onRowsChanged(); - void onRowsInserted(const QModelIndex &parent, int first, int last); - void onRowsRemoved(const QModelIndex &parent, int first, int last); - void onCollapsed(const QModelIndex &index); - void onExpanded(const QModelIndex &index); - -private: - void expandIfNeeded(const QModelIndex &index = QModelIndex()); - void saveCollapsedState(); - -private: - std::unique_ptr ui_; - std::shared_ptr logger_; - std::shared_ptr assetManager_; - std::shared_ptr appSettings_; - QStringList collapsed_; - QuoteRequestsModel * model_; - QuoteReqSortModel * sortModel_; - bool dropQN_ = false; -}; - - -class QuoteReqSortModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - QuoteReqSortModel(QuoteRequestsModel *model, QObject *parent); - - void showQuoted(bool on = true); - -protected: - bool filterAcceptsRow(int row, const QModelIndex &parent) const override; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - -private: - QuoteRequestsModel * model_; - bool showQuoted_; -}; - -#endif // QUOTE_REQUESTS_WIDGET_H diff --git a/BlockSettleUILib/Trading/QuoteRequestsWidget.ui b/BlockSettleUILib/Trading/QuoteRequestsWidget.ui deleted file mode 100644 index d419fb48b..000000000 --- a/BlockSettleUILib/Trading/QuoteRequestsWidget.ui +++ /dev/null @@ -1,69 +0,0 @@ - - - - QuoteRequestsWidget - - - - 0 - 0 - 629 - 240 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractItemView::NoEditTriggers - - - true - - - true - - - true - - - true - - - - - - - - RFQBlotterTreeView - QTreeView -
RFQBlotterTreeView.h
-
-
- - -
diff --git a/BlockSettleUILib/Trading/RFQBlotterTreeView.cpp b/BlockSettleUILib/Trading/RFQBlotterTreeView.cpp deleted file mode 100644 index 59141fb5c..000000000 --- a/BlockSettleUILib/Trading/RFQBlotterTreeView.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ - -#include "RFQBlotterTreeView.h" -#include "QuoteRequestsModel.h" -#include "QuoteRequestsWidget.h" -#include "UiUtils.h" - -#include -#include -#include -#include - - -// -// RFQBlotterTreeView -// - -RFQBlotterTreeView::RFQBlotterTreeView(QWidget *parent) - : TreeViewWithEnterKey (parent) - , model_(nullptr) - , sortModel_(nullptr) -{ -} - -void RFQBlotterTreeView::setRfqModel(QuoteRequestsModel *model) -{ - model_ = model; -} - -void RFQBlotterTreeView::setSortModel(QuoteReqSortModel *model) -{ - sortModel_ = model; -} - -void RFQBlotterTreeView::setAppSettings(std::shared_ptr appSettings) -{ - appSettings_ = appSettings; -} - -void RFQBlotterTreeView::setLimit(ApplicationSettings::Setting s, int limit) -{ - setLimit(findMarket(UiUtils::marketNameForLimit(s)), limit); -} - -void RFQBlotterTreeView::contextMenuEvent(QContextMenuEvent *e) -{ - auto index = currentIndex(); - - if (index.isValid()) { - QMenu menu(this); - QMenu limit(tr("Limit To"), &menu); - limit.addAction(tr("1 RFQ"), [this] () { this->setLimit(1); }); - limit.addAction(tr("3 RFQs"), [this] () { this->setLimit(3); }); - limit.addAction(tr("5 RFQs"), [this] () { this->setLimit(5); }); - limit.addAction(tr("10 RFQs"), [this] () { this->setLimit(10); }); - menu.addMenu(&limit); - - if (index.data(static_cast(QuoteRequestsModel::Role::LimitOfRfqs)).toInt() > 0) { - menu.addSeparator(); - menu.addAction(tr("All RFQs"), [this] () { this->setLimit(-1); }); - } - - menu.exec(e->globalPos()); - e->accept(); - } else { - e->ignore(); - } -} - -void RFQBlotterTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QTreeView::drawRow(painter, option, index); - - if(index.data(static_cast(QuoteRequestsModel::Role::Type)).toInt() == - static_cast(QuoteRequestsModel::DataType::Group)) { - int left = header()->sectionViewportPosition( - static_cast(QuoteRequestsModel::Column::Product)); - int right = header()->sectionViewportPosition( - static_cast(QuoteRequestsModel::Column::Empty)) + - header()->sectionSize(static_cast(QuoteRequestsModel::Column::Empty)); - - QRect r = option.rect; - r.setX(left); - r.setWidth(right - left); - - painter->save(); - painter->setFont(option.font); - painter->setPen(QColor(0x00, 0xA9, 0xE3)); - - if (isExpanded(index)) { - painter->drawText(r, 0, - index.data(static_cast(QuoteRequestsModel::Role::StatText)).toString()); - } else { - painter->drawText(r, 0, - tr("0 of %1").arg( - index.data(static_cast(QuoteRequestsModel::Role::CountOfRfqs)).toInt())); - } - - painter->restore(); - } -} - -void RFQBlotterTreeView::setLimit(const QModelIndex &index, int limit) -{ - if (index.isValid()) { - model_->limitRfqs(index, limit); - } -} - -void RFQBlotterTreeView::setLimit(int limit) -{ - auto index = sortModel_->mapToSource(currentIndex()); - - if (index.isValid()) { - while (index.parent().isValid()) { - index = index.parent(); - } - - appSettings_->set(UiUtils::limitRfqSetting(index.data().toString()), limit); - - setLimit(index, limit); - } -} - -QModelIndex RFQBlotterTreeView::findMarket(const QString &name) const -{ - return model_->findMarketIndex(name); -} diff --git a/BlockSettleUILib/Trading/RFQBlotterTreeView.h b/BlockSettleUILib/Trading/RFQBlotterTreeView.h deleted file mode 100644 index f7d321ae6..000000000 --- a/BlockSettleUILib/Trading/RFQBlotterTreeView.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ - -#ifndef RFQBLOTTERTREEVIEW_H_INCLUDED -#define RFQBLOTTERTREEVIEW_H_INCLUDED - -#include "TreeViewWithEnterKey.h" -#include "ApplicationSettings.h" - -#include - - -class QuoteRequestsModel; -class QuoteReqSortModel; - -// -// RFQBlotterTreeView -// - -//! Tree view for RFQ blotter. -class RFQBlotterTreeView : public TreeViewWithEnterKey -{ - Q_OBJECT - -public: - RFQBlotterTreeView(QWidget *parent); - ~RFQBlotterTreeView() noexcept override = default; - - void setRfqModel(QuoteRequestsModel *model); - void setSortModel(QuoteReqSortModel *model); - [[deprecated]] void setAppSettings(std::shared_ptr appSettings); - - void setLimit(ApplicationSettings::Setting s, int limit); - -protected: - void contextMenuEvent(QContextMenuEvent *e) override; - void drawRow(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - -private: - void setLimit(const QModelIndex &index, int limit); - void setLimit(int limit); - QModelIndex findMarket(const QString &name) const; - -private: - QuoteRequestsModel * model_; - QuoteReqSortModel *sortModel_; - std::shared_ptr appSettings_; -}; // class RFQBlotterTreeView - -#endif // RFQBLOTTERTREEVIEW_H_INCLUDED diff --git a/BlockSettleUILib/Trading/RFQDealerReply.cpp b/BlockSettleUILib/Trading/RFQDealerReply.cpp deleted file mode 100644 index d215958d8..000000000 --- a/BlockSettleUILib/Trading/RFQDealerReply.cpp +++ /dev/null @@ -1,1511 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RFQDealerReply.h" -#include "ui_RFQDealerReply.h" - -#include - -#include - -#include -#include - -#include "ApplicationSettings.h" -#include "AssetManager.h" -#include "AuthAddressManager.h" -#include "AutoSignQuoteProvider.h" -#include "BSErrorCodeStrings.h" -#include "BSMessageBox.h" -#include "CoinControlDialog.h" -#include "CoinControlWidget.h" -#include "CommonTypes.h" -#include "CurrencyPair.h" -#include "CustomControls/CustomComboBox.h" -#include "FastLock.h" -#include "QuoteProvider.h" -#include "SelectedTransactionInputs.h" -#include "SignContainer.h" -#include "TradesUtils.h" -#include "TxClasses.h" -#include "UiUtils.h" -#include "UserScriptRunner.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" - -namespace { - const QString kNoBalanceAvailable = QLatin1String("-"); - const QString kReservedBalance = QLatin1String("Reserved input balance"); - const QString kAvailableBalance = QLatin1String("Available balance"); -} - -using namespace bs::ui; - -RFQDealerReply::RFQDealerReply(QWidget* parent) - : QWidget(parent) - , ui_(new Ui::RFQDealerReply()) -{ - ui_->setupUi(this); - initUi(); - - connect(ui_->spinBoxBidPx, static_cast(&QDoubleSpinBox::valueChanged), this, &RFQDealerReply::priceChanged); - connect(ui_->spinBoxOfferPx, static_cast(&QDoubleSpinBox::valueChanged), this, &RFQDealerReply::priceChanged); - - ui_->spinBoxBidPx->installEventFilter(this); - ui_->spinBoxOfferPx->installEventFilter(this); - - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &RFQDealerReply::submitButtonClicked); - connect(ui_->pushButtonPull, &QPushButton::clicked, this, &RFQDealerReply::pullButtonClicked); - connect(ui_->toolButtonXBTInputsSend, &QPushButton::clicked, this, &RFQDealerReply::showCoinControl); - - connect(ui_->comboBoxXbtWallet, qOverload(&QComboBox::currentIndexChanged), this, &RFQDealerReply::walletSelected); - connect(ui_->authenticationAddressComboBox, qOverload(&QComboBox::currentIndexChanged), this, &RFQDealerReply::onAuthAddrChanged); - - ui_->groupBoxSettlementInputs->hide(); -} - -RFQDealerReply::~RFQDealerReply() = default; - -void RFQDealerReply::init(const std::shared_ptr logger - , const std::shared_ptr &authAddressManager - , const std::shared_ptr& assetManager - , const std::shared_ptr& quoteProvider - , const std::shared_ptr &appSettings - , const std::shared_ptr &connectionManager - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &autoSignProvider - , const std::shared_ptr &utxoReservationManager) -{ - logger_ = logger; - assetManager_ = assetManager; - quoteProvider_ = quoteProvider; - authAddressManager_ = authAddressManager; - appSettings_ = appSettings; - signingContainer_ = container; - armory_ = armory; - connectionManager_ = connectionManager; - autoSignProvider_ = autoSignProvider; - utxoReservationManager_ = utxoReservationManager; - - connect((AQScriptRunner *)autoSignProvider_->scriptRunner(), &AQScriptRunner::sendQuote - , this, &RFQDealerReply::onAQReply, Qt::QueuedConnection); - connect((AQScriptRunner *)autoSignProvider_->scriptRunner(), &AQScriptRunner::pullQuoteNotif - , this, &RFQDealerReply::pullQuoteNotif, Qt::QueuedConnection); - - connect(autoSignProvider_.get(), &AutoSignScriptProvider::autoSignStateChanged, - this, &RFQDealerReply::onAutoSignStateChanged, Qt::QueuedConnection); - connect(utxoReservationManager_.get(), &bs::UTXOReservationManager::availableUtxoChanged, - this, &RFQDealerReply::onUTXOReservationChanged); -} - -void bs::ui::RFQDealerReply::init(const std::shared_ptr& logger) -{ - logger_ = logger; -} - -void RFQDealerReply::initUi() -{ - invalidBalanceFont_ = ui_->labelBalanceValue->font(); - invalidBalanceFont_.setStrikeOut(true); - - ui_->authenticationAddressLabel->hide(); - ui_->authenticationAddressComboBox->hide(); - ui_->pushButtonSubmit->setEnabled(false); - ui_->pushButtonPull->setEnabled(false); - ui_->widgetWallet->hide(); - - ui_->spinBoxBidPx->clear(); - ui_->spinBoxOfferPx->clear(); - ui_->spinBoxBidPx->setEnabled(false); - ui_->spinBoxOfferPx->setEnabled(false); - - ui_->labelProductGroup->clear(); - - validateGUI(); -} - -void RFQDealerReply::setWalletsManager(const std::shared_ptr &walletsManager) -{ - walletsManager_ = walletsManager; - validateGUI(); - - connect(walletsManager_.get(), &bs::sync::WalletsManager::CCLeafCreated, this, &RFQDealerReply::onHDLeafCreated); - connect(walletsManager_.get(), &bs::sync::WalletsManager::CCLeafCreateFailed, this, &RFQDealerReply::onCreateHDWalletError); - - if (autoSignProvider_->scriptRunner()) { - autoSignProvider_->scriptRunner()->setWalletsManager(walletsManager_); - } - - auto updateAuthAddresses = [this] { - UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, authAddressManager_); - onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); - }; - updateAuthAddresses(); - connect(authAddressManager_.get(), &AuthAddressManager::AddressListUpdated, this, updateAuthAddresses); -} - -CustomDoubleSpinBox* RFQDealerReply::bidSpinBox() const -{ - return ui_->spinBoxBidPx; -} - -CustomDoubleSpinBox* RFQDealerReply::offerSpinBox() const -{ - return ui_->spinBoxOfferPx; -} - -QPushButton* RFQDealerReply::pullButton() const -{ - return ui_->pushButtonPull; -} - -QPushButton* RFQDealerReply::quoteButton() const -{ - return ui_->pushButtonSubmit; -} - -void RFQDealerReply::updateRespQuantity() -{ - if (currentQRN_.empty()) { - ui_->labelRespQty->clear(); - return; - } - - if (product_ == bs::network::XbtCurrency) { - ui_->labelRespQty->setText(UiUtils::displayAmount(getValue())); - } else { - ui_->labelRespQty->setText(UiUtils::displayCurrencyAmount(getValue())); - } -} - -void RFQDealerReply::reset() -{ - payInRecipId_ = UINT_MAX; - if (currentQRN_.empty() - || (currentQRN_.assetType == bs::network::Asset::Type::Undefined)) { - ui_->labelProductGroup->clear(); - ui_->labelSecurity->clear(); - ui_->labelReqProduct->clear(); - ui_->labelReqSide->clear(); - ui_->labelReqQty->clear(); - ui_->labelRespProduct->clear(); - ui_->labelQuantity->clear(); - ui_->labelProduct->clear(); - indicBid_ = indicAsk_ = 0; - selectedXbtInputs_.clear(); - selectedXbtRes_.release(); - setBalanceOk(true); - } - else { - CurrencyPair cp(currentQRN_.security); - baseProduct_ = cp.NumCurrency(); - product_ = cp.ContraCurrency(currentQRN_.product); - - if (currentQRN_.assetType == bs::network::Asset::Type::Undefined) { - logger_->error("[RFQDealerReply::reset] could not get asset type for {}", currentQRN_.security); - } - const auto priceDecimals = UiUtils::GetPricePrecisionForAssetType(currentQRN_.assetType); - ui_->spinBoxBidPx->setDecimals(priceDecimals); - ui_->spinBoxOfferPx->setDecimals(priceDecimals); - ui_->spinBoxBidPx->setSingleStep(std::pow(10, -priceDecimals)); - ui_->spinBoxOfferPx->setSingleStep(std::pow(10, -priceDecimals)); - - const auto priceWidget = getActivePriceWidget(); - ui_->spinBoxBidPx->setEnabled(priceWidget == ui_->spinBoxBidPx); - ui_->spinBoxOfferPx->setEnabled(priceWidget == ui_->spinBoxOfferPx); -/* priceWidget->setFocus(); - priceWidget->selectAll();*/ - - ui_->labelProductGroup->setText(tr(bs::network::Asset::toString(currentQRN_.assetType))); - ui_->labelSecurity->setText(QString::fromStdString(currentQRN_.security)); - ui_->labelReqProduct->setText(QString::fromStdString(currentQRN_.product)); - ui_->labelReqSide->setText(tr(bs::network::Side::toString(currentQRN_.side))); - ui_->labelReqQty->setText(UiUtils::displayAmountForProduct(currentQRN_.quantity - , QString::fromStdString(currentQRN_.product), currentQRN_.assetType)); - - ui_->labelRespProduct->setText(QString::fromStdString(product_)); - ui_->labelQuantity->setText(UiUtils::displayAmountForProduct(currentQRN_.quantity - , QString::fromStdString(currentQRN_.product), currentQRN_.assetType)); - ui_->labelProduct->setText(QString::fromStdString(currentQRN_.product)); - - if (sentNotifs_.count(currentQRN_.quoteRequestId) == 0) { - selectedXbtInputs_.clear(); - } - else { - if (getLastUTXOReplyCb_) { - const auto* lastSettlement = getLastUTXOReplyCb_(currentQRN_.settlementId); - if (lastSettlement && selectedXbtInputs_ != *lastSettlement) { - selectedXbtInputs_ = *lastSettlement; - } - } - } - } - - updateRespQuantity(); - updateSpinboxes(); - refreshSettlementDetails(); -} - -void RFQDealerReply::quoteReqNotifStatusChanged(const bs::network::QuoteReqNotification &qrn) -{ - if (!QuoteProvider::isRepliableStatus(qrn.status)) { - sentNotifs_.erase(qrn.quoteRequestId); - addresses_.erase(qrn.quoteRequestId); - } - - if (qrn.quoteRequestId == currentQRN_.quoteRequestId) { - updateQuoteReqNotification(qrn); - } - - refreshSettlementDetails(); -} - -void RFQDealerReply::setQuoteReqNotification(const bs::network::QuoteReqNotification &qrn - , double indicBid, double indicAsk) -{ - indicBid_ = indicBid; - indicAsk_ = indicAsk; - - updateQuoteReqNotification(qrn); -} - -void RFQDealerReply::updateQuoteReqNotification(const bs::network::QuoteReqNotification &qrn) -{ - const auto &oldReqId = currentQRN_.quoteRequestId; - const bool qrnChanged = (oldReqId != qrn.quoteRequestId); - currentQRN_ = qrn; - - const bool isXBT = (qrn.assetType == bs::network::Asset::SpotXBT); - const bool isPrivMkt = (qrn.assetType == bs::network::Asset::PrivateMarket); - - dealerSellXBT_ = (isXBT || isPrivMkt) && ((qrn.product == bs::network::XbtCurrency) != (qrn.side == bs::network::Side::Sell)); - - ui_->authenticationAddressLabel->setVisible(isXBT); - ui_->authenticationAddressComboBox->setVisible(isXBT); - ui_->widgetWallet->setVisible(isXBT || isPrivMkt); - ui_->toolButtonXBTInputsSend->setVisible(dealerSellXBT_ && isXBT); - ui_->labelWallet->setText(dealerSellXBT_ ? tr("Payment Wallet") : tr("Receiving Wallet")); - - updateUiWalletFor(qrn); - - if (qrnChanged) { - reset(); - } - - if (qrn.assetType == bs::network::Asset::SpotFX || - qrn.assetType == bs::network::Asset::Undefined || - qrn.assetType == bs::network::Asset::DeliverableFutures) { - ui_->groupBoxSettlementInputs->hide(); - } else { - ui_->groupBoxSettlementInputs->show(); - } - - updateSubmitButton(); -} - -std::shared_ptr RFQDealerReply::getCCWallet(const std::string &cc) const -{ - return walletsManager_->getCCWallet(cc); -} - -std::shared_ptr RFQDealerReply::getCCWallet(const bs::network::QuoteReqNotification &qrn) const -{ - return getCCWallet(qrn.product); -} - -void RFQDealerReply::getAddress(const std::string "eRequestId, const std::shared_ptr &wallet - , AddressType type, std::function cb) -{ - if (!wallet) { - cb({}); - return; - } - - auto address = addresses_[quoteRequestId][wallet->walletId()].at(static_cast(type)); - if (!address.empty()) { - cb(address); - return; - } - - auto cbWrap = [this, quoteRequestId, wallet, cb = std::move(cb), type](const bs::Address &addr) { - if (wallet->type() != bs::core::wallet::Type::ColorCoin && type == AddressType::Recv) { - wallet->setAddressComment(addr, bs::sync::wallet::Comment::toString(bs::sync::wallet::Comment::SettlementPayOut)); - } - addresses_[quoteRequestId][wallet->walletId()][static_cast(type)] = addr; - cb(addr); - }; - switch (type) { - case AddressType::Recv: - // BST-2474: All addresses related to trading, not just change addresses, should use internal addresses. - // CC wallets have only external addresses so getNewIntAddress will return external address instead but that's not a problem. - wallet->getNewIntAddress(cbWrap); - break; - case AddressType::Change: - wallet->getNewChangeAddress(cbWrap); - break; - } -} - -void RFQDealerReply::updateUiWalletFor(const bs::network::QuoteReqNotification &qrn) -{ - if (armory_ && (armory_->state() != ArmoryState::Ready)) { - return; - } - if (qrn.assetType == bs::network::Asset::PrivateMarket) { - if (qrn.side == bs::network::Side::Sell) { - const auto &ccWallet = getCCWallet(qrn.product); - if (!ccWallet) { - if (signingContainer_ && !signingContainer_->isOffline()) { - MessageBoxCCWalletQuestion qryCCWallet(QString::fromStdString(qrn.product), this); - - if (qryCCWallet.exec() == QDialog::Accepted) { - if (!walletsManager_->CreateCCLeaf(qrn.product)) { - BSMessageBox errorMessage(BSMessageBox::critical, tr("Internal error") - , tr("Failed create CC subwallet.") - , this); - errorMessage.exec(); - } - } - } else { - BSMessageBox errorMessage(BSMessageBox::critical, tr("Signer not connected") - , tr("Could not create CC subwallet.") - , this); - errorMessage.exec(); - } - } - } - walletFlags_ = (qrn.side == bs::network::Side::Sell) ? UiUtils::WalletsTypes::Full - : UiUtils::WalletsTypes::All; - } else if (qrn.assetType == bs::network::Asset::SpotXBT) { - walletFlags_ = (qrn.side == bs::network::Side::Sell) ? - (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW) - : UiUtils::WalletsTypes::All; - } - updateWalletsList(walletFlags_); -} - -void RFQDealerReply::priceChanged() -{ - updateRespQuantity(); - updateSubmitButton(); -} - -void RFQDealerReply::onAuthAddrChanged(int index) -{ - auto addressString = ui_->authenticationAddressComboBox->itemText(index).toStdString(); - if (addressString.empty()) { - logger_->error("[{}] empty address string", __func__); - return; - } - const auto& authAddr = bs::Address::fromAddressString(addressString); - if ((authAddr_ == authAddr) && !authKey_.empty()) { - return; - } - authAddr_ = authAddr; - authKey_.clear(); - - if (authAddr_.empty()) { - logger_->warn("[{}] empty auth address", __func__); - return; - } - if (walletsManager_) { - const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); - - const auto& cbPubKey = [this](const SecureBinaryData& pubKey) { - authKey_ = pubKey.toHexStr(); - QMetaObject::invokeMethod(this, &RFQDealerReply::updateSubmitButton); - }; - - if (settlLeaf) { - settlLeaf->getRootPubkey(cbPubKey); - } else { - walletsManager_->createSettlementLeaf(authAddr_, cbPubKey); - } - } - else { - emit needAuthKey(authAddr_); - } -} - -void RFQDealerReply::updateSubmitButton() -{ - if (!logger_) { - return; - } - if (!currentQRN_.empty() && activeQuoteSubmits_.find(currentQRN_.quoteRequestId) != activeQuoteSubmits_.end()) { - // Do not allow re-enter into submitReply as it could cause problems - ui_->pushButtonSubmit->setEnabled(false); - ui_->pushButtonPull->setEnabled(false); - return; - } - - updateBalanceLabel(); - bool isQRNRepliable = (!currentQRN_.empty() && QuoteProvider::isRepliableStatus(currentQRN_.status)); - if ((currentQRN_.assetType != bs::network::Asset::SpotFX) - && signingContainer_ && signingContainer_->isOffline()) { - isQRNRepliable = false; - } - - ui_->pushButtonSubmit->setEnabled(isQRNRepliable); - ui_->pushButtonPull->setEnabled(isQRNRepliable); - if (!isQRNRepliable) { - ui_->spinBoxBidPx->setEnabled(false); - ui_->spinBoxOfferPx->setEnabled(false); - return; - } - - const auto itQN = sentNotifs_.find(currentQRN_.quoteRequestId); - ui_->pushButtonPull->setEnabled(itQN != sentNotifs_.end()); - - const auto price = getPrice(); - if (qFuzzyIsNull(price) || ((itQN != sentNotifs_.end()) && qFuzzyCompare(itQN->second, price))) { - ui_->pushButtonSubmit->setEnabled(false); - return; - } - - if ((currentQRN_.assetType == bs::network::Asset::SpotXBT) && authKey_.empty()) { - ui_->pushButtonSubmit->setEnabled(false); - return; - } - - const bool isBalanceOk = assetManager_ ? checkBalance() : true; - ui_->pushButtonSubmit->setEnabled(isBalanceOk); - setBalanceOk(isBalanceOk); -} - -void RFQDealerReply::setBalanceOk(bool ok) -{ - if (!ok) { - QPalette palette = ui_->labelRespQty->palette(); - palette.setColor(QPalette::WindowText, Qt::red); - ui_->labelRespQty->setPalette(palette); - return; - } - ui_->labelRespQty->setPalette(QPalette()); -} - -bool RFQDealerReply::checkBalance() const -{ - if (!assetManager_) { - return false; - } - - if (currentQRN_.assetType == bs::network::Asset::DeliverableFutures) { - return true; - } - - // #UTXO_MANAGER: Balance check should account for fee? - - if ((currentQRN_.side == bs::network::Side::Buy) != (product_ == baseProduct_)) { - const auto amount = getAmount(); - if (currentQRN_.assetType == bs::network::Asset::SpotXBT) { - return amount <= getXbtBalance(bs::UTXOReservationManager::kIncludeZcDealerSpotXbt).GetValueBitcoin(); - } else if (currentQRN_.assetType == bs::network::Asset::PrivateMarket) { - return amount <= getPrivateMarketCoinBalance(); - } - const auto product = (product_ == baseProduct_) ? product_ : currentQRN_.product; - return assetManager_->checkBalance(product, amount, false); - } else if ((currentQRN_.side == bs::network::Side::Buy) && (product_ == baseProduct_)) { - const bool includeZc = - (currentQRN_.assetType == bs::network::Asset::SpotXBT) - ? bs::UTXOReservationManager::kIncludeZcDealerSpotXbt - : bs::UTXOReservationManager::kIncludeZcDealerCc; - return assetManager_->checkBalance(currentQRN_.product, currentQRN_.quantity, includeZc); - } - - if (currentQRN_.assetType == bs::network::Asset::PrivateMarket) { - return currentQRN_.quantity * getPrice() <= getXbtBalance(bs::UTXOReservationManager::kIncludeZcDealerCc).GetValueBitcoin(); - } - - const double value = getValue(); - if (qFuzzyIsNull(value)) { - return true; - } - const bool isXbt = (currentQRN_.assetType == bs::network::Asset::PrivateMarket) || - ((currentQRN_.assetType == bs::network::Asset::SpotXBT) && (product_ == baseProduct_)); - if (isXbt) { - return value <= getXbtBalance(bs::UTXOReservationManager::kIncludeZcDealerSpotXbt).GetValueBitcoin(); - } - return assetManager_->checkBalance(product_, value, false); -} - -void RFQDealerReply::walletSelected(int index) -{ - reset(); - updateSubmitButton(); -} - -QDoubleSpinBox *RFQDealerReply::getActivePriceWidget() const -{ - if (currentQRN_.empty()) { - return nullptr; - } - - if (baseProduct_ == currentQRN_.product) { - if (currentQRN_.side == bs::network::Side::Buy) { - return ui_->spinBoxOfferPx; - } - return ui_->spinBoxBidPx; - } - else { - if (currentQRN_.side == bs::network::Side::Buy) { - return ui_->spinBoxBidPx; - } - return ui_->spinBoxOfferPx; - } -} - -double RFQDealerReply::getPrice() const -{ - const auto spinBox = getActivePriceWidget(); - return spinBox ? spinBox->value() : 0; -} - -double RFQDealerReply::getValue() const -{ - const double price = getPrice(); - if (baseProduct_ == product_) { - if (!qFuzzyIsNull(price)) { - return currentQRN_.quantity / price; - } - return 0; - } - return price * currentQRN_.quantity; -} - -double RFQDealerReply::getAmount() const -{ - if (baseProduct_ == product_) { - const double price = getPrice(); - if (!qFuzzyIsNull(price)) { - return currentQRN_.quantity / price; - } - return 0; - } - return currentQRN_.quantity; -} - -std::shared_ptr RFQDealerReply::getSelectedXbtWallet(ReplyType replyType) const -{ - if (!walletsManager_) { - return nullptr; - } - return walletsManager_->getHDWalletById(getSelectedXbtWalletId(replyType)); -} - -bs::Address RFQDealerReply::selectedAuthAddress(ReplyType replyType) const -{ - if (authAddressManager_ && (replyType == ReplyType::Script)) { - return authAddressManager_->getDefault(); - } - return authAddr_; -} - -std::vector RFQDealerReply::selectedXbtInputs(ReplyType replyType) const -{ - if (replyType == ReplyType::Script) { - return {}; - } - - return selectedXbtInputs_; -} - -void RFQDealerReply::setSubmitQuoteNotifCb(RFQDealerReply::SubmitQuoteNotifCb cb) -{ - submitQuoteNotifCb_ = std::move(cb); -} - -void RFQDealerReply::setResetCurrentReservation(RFQDealerReply::ResetCurrentReservationCb cb) -{ - resetCurrentReservationCb_ = std::move(cb); -} - -void bs::ui::RFQDealerReply::setGetLastSettlementReply(GetLastUTXOReplyCb cb) -{ - getLastUTXOReplyCb_ = std::move(cb); -} - -void bs::ui::RFQDealerReply::onParentAboutToHide() -{ - if (sentNotifs_.count(currentQRN_.quoteRequestId) == 0) { - selectedXbtInputs_.clear(); - } - selectedXbtRes_.release(); -} - -void bs::ui::RFQDealerReply::onHDWallet(const bs::sync::HDWalletData& wallet) -{ - const auto& it = std::find_if(wallets_.cbegin(), wallets_.cend() - , [wallet](const bs::sync::HDWalletData& w) { return (wallet.id == w.id); }); - if (it == wallets_.end()) { - wallets_.push_back(wallet); - } else { - wallets_.emplace(it, wallet); - } -} - -void bs::ui::RFQDealerReply::onBalance(const std::string& currency, double balance) -{ - balances_[currency] = balance; -} - -void bs::ui::RFQDealerReply::onWalletBalance(const bs::sync::WalletBalanceData& wbd) -{ - balances_[wbd.id] = wbd.balSpendable; -} - -void bs::ui::RFQDealerReply::onAuthKey(const bs::Address& addr, const BinaryData& authKey) -{ - if (addr == authAddr_) { - const auto& authKeyHex = authKey.toHexStr(); - if (authKey_ != authKeyHex) { - logger_->debug("[{}] got auth key: {}", __func__, authKeyHex); - authKey_ = authKeyHex; - } - } -} - -void bs::ui::RFQDealerReply::onVerifiedAuthAddresses(const std::vector& addrs) -{ - if (addrs.empty()) { - return; - } - UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, addrs); - onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); -} - -void bs::ui::RFQDealerReply::onReservedUTXOs(const std::string& resId - , const std::string& subId, const std::vector& utxos) -{ - const auto& itRes = pendingReservations_.find(resId); - if (itRes == pendingReservations_.end()) { - return; // not our request - } - if (utxos.empty()) { - logger_->warn("[{}] UTXO reservation failed", __func__); - pendingReservations_.erase(itRes); - return; - } - submit(itRes->second->price, itRes->second); - pendingReservations_.erase(itRes); -} - -void RFQDealerReply::submitReply(const bs::network::QuoteReqNotification &qrn - , double price, ReplyType replyType) -{ - if (qFuzzyIsNull(price)) { - SPDLOG_LOGGER_ERROR(logger_, "invalid price"); - return; - } - - const auto itQN = sentNotifs_.find(qrn.quoteRequestId); - if (itQN != sentNotifs_.end() && itQN->second == price) { - SPDLOG_LOGGER_ERROR(logger_, "quote have been already sent"); - return; - } - - auto replyData = std::make_shared(); - replyData->qn = bs::network::QuoteNotification(qrn, authKey_, price, ""); - - auto quoteAssetType = qrn.assetType; - if (quoteAssetType == bs::network::Asset::DeliverableFutures) { - quoteAssetType = bs::network::Asset::SpotFX; - } - if (quoteAssetType != bs::network::Asset::SpotFX) { - if (walletsManager_) { - replyData->xbtWallet = getSelectedXbtWallet(replyType); - if (!replyData->xbtWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't submit CC/XBT reply without XBT wallet"); - return; - } - if (!replyData->xbtWallet->canMixLeaves()) { - replyData->walletPurpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - } - } - else { - replyData->xbtWalletId = getSelectedXbtWalletId(replyType); - } - } - - if (quoteAssetType == bs::network::Asset::SpotXBT) { - replyData->authAddr = selectedAuthAddress(replyType); - if (!replyData->authAddr.isValid()) { - SPDLOG_LOGGER_ERROR(logger_, "can't submit XBT without valid auth address"); - return; - } - - bs::XBTAmount minXbtAmount; - if (utxoReservationManager_) { - minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); - } - else { - minXbtAmount = bs::tradeutils::minXbtAmount(1); //FIXME: should populate PB fee rate somehow - } - auto xbtAmount = XBTAmount(qrn.product == bs::network::XbtCurrency ? qrn.quantity : qrn.quantity / price); - if (xbtAmount.GetValue() < minXbtAmount.GetValue()) { - SPDLOG_LOGGER_ERROR(logger_, "XBT amount is too low to cover network fee: {}, min. amount: {}" - , xbtAmount.GetValue(), minXbtAmount.GetValue()); - return; - } - } - - auto it = activeQuoteSubmits_.find(replyData->qn.quoteRequestId); - if (it != activeQuoteSubmits_.end()) { - SPDLOG_LOGGER_ERROR(logger_, "quote submit already active for quote request '{}'" - , replyData->qn.quoteRequestId); - return; - } - activeQuoteSubmits_.insert(replyData->qn.quoteRequestId); - updateSubmitButton(); - - switch (quoteAssetType) { - case bs::network::Asset::SpotFX: { - submit(price, replyData); - break; - } - - case bs::network::Asset::SpotXBT: { - reserveBestUtxoSetAndSubmit(qrn.quantity, price, replyData, replyType); - break; - } - - case bs::network::Asset::PrivateMarket: { - auto ccWallet = getCCWallet(qrn); - if (!ccWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find required CC wallet ({})", qrn.product); - return; - } - - const bool isSpendCC = qrn.side == bs::network::Side::Buy; - int64_t spendVal = 0; - if (isSpendCC) { - spendVal = static_cast(qrn.quantity * assetManager_->getCCLotSize(qrn.product)); - } else { - spendVal = bs::XBTAmount(price * qrn.quantity).GetValue(); - } - - auto xbtLeaves = replyData->xbtWallet->getGroup(bs::sync::hd::Wallet::getXBTGroupType())->getLeaves(); - if (xbtLeaves.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "empty XBT leaves in wallet {}", replyData->xbtWallet->walletId()); - return; - } - auto xbtWallets = std::vector>(xbtLeaves.begin(), xbtLeaves.end()); - auto xbtWallet = xbtWallets.front(); - - const auto &spendWallet = isSpendCC ? ccWallet : xbtWallet; - const auto &recvWallet = isSpendCC ? xbtWallet : ccWallet; - - preparingCCRequest_.insert(replyData->qn.quoteRequestId); - auto recvAddrCb = [this, replyData, qrn, spendWallet, spendVal, isSpendCC, ccWallet, xbtWallets, price](const bs::Address &addr) { - replyData->qn.receiptAddress = addr.display(); - replyData->qn.reqAuthKey = qrn.requestorRecvAddress; - - const auto &cbFee = [this, qrn, spendVal, spendWallet, isSpendCC, replyData, ccWallet, xbtWallets, price](float feePerByteArmory) { - auto feePerByte = std::max(feePerByteArmory, utxoReservationManager_->feeRatePb()); - auto inputsCb = [this, qrn, feePerByte, replyData, spendVal, spendWallet, isSpendCC, price] - (const std::map &inputs) - { - QMetaObject::invokeMethod(this, [this, feePerByte, qrn, replyData, spendVal, spendWallet, isSpendCC, inputs, price] { - const auto &cbChangeAddr = [this, feePerByte, qrn, replyData, spendVal, spendWallet, inputs, price, isSpendCC] - (const bs::Address &changeAddress) - { - try { - //group 1 for cc, group 2 for xbt - unsigned spendGroup = isSpendCC ? RECIP_GROUP_SPEND_1 : RECIP_GROUP_SPEND_2; - unsigned changGroup = isSpendCC ? RECIP_GROUP_CHANG_1 : RECIP_GROUP_CHANG_2; - - std::map>> recipientMap; - const auto recipient = bs::Address::fromAddressString(qrn.requestorRecvAddress) - .getRecipient(bs::XBTAmount{ spendVal }); - std::vector> recVec({recipient}); - recipientMap.emplace(spendGroup, std::move(recVec)); - - - Codec_SignerState::SignerState state; - state.ParseFromString(BinaryData::CreateFromHex(qrn.requestorAuthPublicKey).toBinStr()); - auto txReq = bs::sync::WalletsManager::createPartialTXRequest(spendVal, inputs - , changeAddress - , isSpendCC ? 0 : feePerByte, armory_->topBlock() - , recipientMap, changGroup, state - , false, UINT32_MAX, logger_); - logger_->debug("[RFQDealerReply::submitReply] {} input[s], fpb={}, recip={}, " - "change amount={}, prevPart={}", inputs.size(), feePerByte - , bs::Address::fromAddressString(qrn.requestorRecvAddress).display() - , txReq.change.value, qrn.requestorAuthPublicKey); - - signingContainer_->resolvePublicSpenders(txReq, [replyData, this, price, txReq] - (bs::error::ErrorCode result, const Codec_SignerState::SignerState &state) - { - if (preparingCCRequest_.count(replyData->qn.quoteRequestId) == 0) { - return; - } - - if (result == bs::error::ErrorCode::NoError) { - replyData->qn.transactionData = BinaryData::fromString(state.SerializeAsString()).toHexStr(); - replyData->utxoRes = utxoReservationManager_->makeNewReservation( - txReq.getInputs(nullptr), replyData->qn.quoteRequestId); - submit(price, replyData); - } - else { - SPDLOG_LOGGER_ERROR(logger_, "error resolving public spenders: {}" - , bs::error::ErrorCodeToString(result).toStdString()); - } - preparingCCRequest_.erase(replyData->qn.quoteRequestId); - }); - } catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "error creating own unsigned half: {}", e.what()); - return; - } - }; - - if (isSpendCC) { - uint64_t inputsVal = 0; - for (const auto &input : inputs) { - inputsVal += input.first.getValue(); - } - if (inputsVal == spendVal) { - cbChangeAddr({}); - return; - } - } - getAddress(qrn.quoteRequestId, spendWallet, AddressType::Change, cbChangeAddr); - }); - }; - - // Try to reset current reservation if needed when user sends another quote - resetCurrentReservationCb_(replyData); - - if (isSpendCC) { - const auto inputsWrapCb = [inputsCb, ccWallet](const std::vector &utxos) { - std::map inputs; - for (const auto &utxo : utxos) { - inputs[utxo] = ccWallet->walletId(); - } - inputsCb(inputs); - }; - // For CC search for exact amount (preferable without change) - ccWallet->getSpendableTxOutList(inputsWrapCb, spendVal, true); - } else { - // For XBT request all available inputs as we don't know fee yet (createPartialTXRequest will use correct inputs if fee rate is set) - std::vector utxos; - if (!replyData->xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - utxos = utxoReservationManager_->getAvailableXbtUTXOs( - replyData->xbtWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcDealerCc); - } - else { - utxos = utxoReservationManager_->getAvailableXbtUTXOs( - replyData->xbtWallet->walletId(), bs::UTXOReservationManager::kIncludeZcDealerCc); - } - auto fixedUtxo = utxoReservationManager_->convertUtxoToPartialFixedInput(replyData->xbtWallet->walletId(), utxos); - inputsCb(fixedUtxo.inputs); - } - }; - - if (qrn.side == bs::network::Side::Buy) { - cbFee(0); - } else { - walletsManager_->estimatedFeePerByte(2, cbFee, this); - } - }; - // recv. address is always set automatically - getAddress(qrn.quoteRequestId, recvWallet, AddressType::Recv, recvAddrCb); - break; - } - - default: { - break; - } - } -} - -void RFQDealerReply::updateWalletsList(int walletsFlags) -{ - auto oldWalletId = UiUtils::getSelectedWalletId(ui_->comboBoxXbtWallet); - auto oldType = UiUtils::getSelectedWalletType(ui_->comboBoxXbtWallet); - int defaultIndex = 0; - if (walletsManager_) { - defaultIndex = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXbtWallet, walletsManager_, walletsFlags); - } - else { - defaultIndex = UiUtils::fillHDWalletsComboBox(ui_->comboBoxXbtWallet, wallets_, walletsFlags); - } - int oldIndex = UiUtils::selectWalletInCombobox(ui_->comboBoxXbtWallet, oldWalletId, oldType); - if (oldIndex < 0) { - ui_->comboBoxXbtWallet->setCurrentIndex(defaultIndex); - } - walletSelected(ui_->comboBoxXbtWallet->currentIndex()); -} - -bool RFQDealerReply::isXbtSpend() const -{ - bool isXbtSpend = (currentQRN_.assetType == bs::network::Asset::PrivateMarket && currentQRN_.side == bs::network::Side::Sell) || - ((currentQRN_.assetType == bs::network::Asset::SpotXBT) && (currentQRN_.side == bs::network::Side::Buy)); - return isXbtSpend; -} - -std::string RFQDealerReply::getSelectedXbtWalletId(ReplyType replyType) const -{ - std::string walletId; - if (replyType == ReplyType::Manual) { - walletId = ui_->comboBoxXbtWallet->currentData(UiUtils::WalletIdRole).toString().toStdString(); - } - else { - if (walletsManager_) { - walletId = walletsManager_->getPrimaryWallet()->walletId(); - } //new code doesn't support scripting in the GUI - } - return walletId; -} - -void RFQDealerReply::onReservedUtxosChanged(const std::string &walletId, const std::vector &utxos) -{ - onTransactionDataChanged(); -} - -void RFQDealerReply::submitButtonClicked() -{ - const auto price = getPrice(); - if (!ui_->pushButtonSubmit->isEnabled() || price == 0) { - return; - } - - submitReply(currentQRN_, price, ReplyType::Manual); - - updateSubmitButton(); -} - -void RFQDealerReply::pullButtonClicked() -{ - if (currentQRN_.empty()) { - return; - } - emit pullQuoteNotif(currentQRN_.settlementId, currentQRN_.quoteRequestId, currentQRN_.sessionToken); - sentNotifs_.erase(currentQRN_.quoteRequestId); - updateSubmitButton(); - refreshSettlementDetails(); -} - -bool RFQDealerReply::eventFilter(QObject *watched, QEvent *evt) -{ - if (evt->type() == QEvent::KeyPress) { - auto keyID = static_cast(evt)->key(); - if ((keyID == Qt::Key_Return) || (keyID == Qt::Key_Enter)) { - submitButtonClicked(); - } - } else if ((evt->type() == QEvent::FocusIn) || (evt->type() == QEvent::FocusOut)) { - auto activePriceWidget = getActivePriceWidget(); - if (activePriceWidget != nullptr) { - autoUpdatePrices_ = !(activePriceWidget->hasFocus()); - } else { - autoUpdatePrices_ = false; - } - } - return QWidget::eventFilter(watched, evt); -} - -void RFQDealerReply::showCoinControl() -{ - auto xbtWallet = getSelectedXbtWallet(ReplyType::Manual); - if (!xbtWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find XBT wallet"); - return; - } - - const auto &leaves = xbtWallet->getGroup(xbtWallet->getXBTGroupType())->getLeaves(); - std::vector> wallets(leaves.begin(), leaves.end()); - ui_->toolButtonXBTInputsSend->setEnabled(false); - - // Need to release current reservation to be able select them back - selectedXbtRes_.release(); - std::vector allUTXOs; - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - allUTXOs = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcDealerSpotXbt); - } - else { - allUTXOs = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet->walletId(), bs::UTXOReservationManager::kIncludeZcDealerSpotXbt); - } - - ui_->toolButtonXBTInputsSend->setEnabled(true); - - const bool useAutoSel = selectedXbtInputs_.empty(); - - auto inputs = std::make_shared(allUTXOs); - - // Set this to false is needed otherwise current selection would be cleared - inputs->SetUseAutoSel(useAutoSel); - for (const auto &utxo : selectedXbtInputs_) { - inputs->SetUTXOSelection(utxo.getTxHash(), utxo.getTxOutIndex()); - } - - CoinControlDialog dialog(inputs, true, this); - int rc = dialog.exec(); - if (rc != QDialog::Accepted) { - return; - } - - auto selectedInputs = dialog.selectedInputs(); - if (bs::UtxoReservation::instance()->containsReservedUTXO(selectedInputs)) { - BSMessageBox(BSMessageBox::critical, tr("UTXO reservation failed"), - tr("Some of selected UTXOs has been already reserved"), this).exec(); - showCoinControl(); - return; - } - - selectedXbtInputs_.clear(); - for (const auto &selectedInput : selectedInputs) { - selectedXbtInputs_.push_back(selectedInput); - } - - if (!selectedXbtInputs_.empty()) { - selectedXbtRes_ = utxoReservationManager_->makeNewReservation(selectedInputs); - } - - updateSubmitButton(); -} - -void RFQDealerReply::validateGUI() -{ - updateSubmitButton(); -} - -void RFQDealerReply::onTransactionDataChanged() -{ - QMetaObject::invokeMethod(this, &RFQDealerReply::updateSubmitButton); -} - -void RFQDealerReply::onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields mdFields) -{ - auto &mdInfo = mdInfo_[security.toStdString()]; - mdInfo.merge(bs::network::MDField::get(mdFields)); - if (autoUpdatePrices_ && (currentQRN_.security == security.toStdString()) - && (bestQPrices_.find(currentQRN_.quoteRequestId) == bestQPrices_.end())) { - if (!qFuzzyIsNull(mdInfo.bidPrice)) { - ui_->spinBoxBidPx->setValue(mdInfo.bidPrice); - } - if (!qFuzzyIsNull(mdInfo.askPrice)) { - ui_->spinBoxOfferPx->setValue(mdInfo.askPrice); - } - } -} - -void RFQDealerReply::onBestQuotePrice(const QString reqId, double price, bool own) -{ - bestQPrices_[reqId.toStdString()] = price; - - if (!currentQRN_.empty() && (currentQRN_.quoteRequestId == reqId.toStdString())) { - if (autoUpdatePrices_) { - auto priceWidget = getActivePriceWidget(); - if (priceWidget && !own) { - double improvedPrice = price; - if (currentQRN_.assetType != bs::network::Asset::Type::Undefined) { - const auto pip = std::pow(10, -UiUtils::GetPricePrecisionForAssetType(currentQRN_.assetType)); - if (priceWidget == ui_->spinBoxBidPx) { - improvedPrice += pip; - } else { - improvedPrice -= pip; - } - } else { - logger_->error("[RFQDealerReply::onBestQuotePrice] could not get type for {}", currentQRN_.security); - } - priceWidget->setValue(improvedPrice); - } - } - } - - updateSpinboxes(); -} - -void RFQDealerReply::onAQReply(const bs::network::QuoteReqNotification &qrn, double price) -{ - // Check assets first - bool ok = true; - if (qrn.assetType == bs::network::Asset::Type::SpotXBT) { - if (qrn.side == bs::network::Side::Sell && qrn.product == bs::network::XbtCurrency) { - CurrencyPair currencyPair(qrn.security); - ok = assetManager_->checkBalance(currencyPair.ContraCurrency(qrn.product), qrn.quantity * price, bs::UTXOReservationManager::kIncludeZcDealerSpotXbt); - } - else if (qrn.side == bs::network::Side::Buy && qrn.product != bs::network::XbtCurrency) { - ok = assetManager_->checkBalance(qrn.product, qrn.quantity, bs::UTXOReservationManager::kIncludeZcDealerSpotXbt); - } - } - else if (qrn.assetType == bs::network::Asset::Type::SpotFX) { - if (qrn.side == bs::network::Side::Sell) { - auto quantity = qrn.quantity; - CurrencyPair currencyPair(qrn.security); - const auto contrCurrency = currencyPair.ContraCurrency(qrn.product); - if (currencyPair.NumCurrency() == contrCurrency) { - quantity /= price; - } - else { - quantity *= price; - } - ok = assetManager_->checkBalance(currencyPair.ContraCurrency(qrn.product), quantity, false); - } - else { - ok = assetManager_->checkBalance(qrn.product, qrn.quantity, false); - } - } - - if (!ok) { - return; - } - - submitReply(qrn, price, ReplyType::Script); -} - -void RFQDealerReply::onHDLeafCreated(const std::string& ccName) -{ - if (product_ != ccName) { - return; - } - - auto ccLeaf = walletsManager_->getCCWallet(ccName); - if (ccLeaf == nullptr) { - logger_->error("[RFQDealerReply::onHDLeafCreated] CC wallet {} should exists" - , ccName); - return; - } - - updateUiWalletFor(currentQRN_); - reset(); -} - -void RFQDealerReply::onCreateHDWalletError(const std::string& ccName, bs::error::ErrorCode result) -{ - if (product_ != ccName) { - return; - } - - BSMessageBox(BSMessageBox::critical, tr("Failed to create wallet") - , tr("Failed to create wallet") - , tr("%1 Wallet. Error: %2").arg(QString::fromStdString(product_)).arg(bs::error::ErrorCodeToString(result))).exec(); -} - -void RFQDealerReply::onCelerConnected() -{ - celerConnected_ = true; - validateGUI(); -} - -void RFQDealerReply::onCelerDisconnected() -{ - logger_->info("Disabled auto-quoting due to Celer disconnection"); - celerConnected_ = false; - validateGUI(); -} - -void RFQDealerReply::onAutoSignStateChanged() -{ - if (autoSignProvider_->autoSignState() == bs::error::ErrorCode::NoError) { - ui_->comboBoxXbtWallet->setCurrentText(autoSignProvider_->getAutoSignWalletName()); - } - ui_->comboBoxXbtWallet->setEnabled(autoSignProvider_->autoSignState() == bs::error::ErrorCode::AutoSignDisabled); -} - -void bs::ui::RFQDealerReply::onQuoteCancelled(const std::string "eId) -{ - preparingCCRequest_.erase(quoteId); -} - -void bs::ui::RFQDealerReply::onUTXOReservationChanged(const std::string& walletId) -{ - if (walletId.empty()) { - updateBalanceLabel(); - return; - } - - auto xbtWallet = getSelectedXbtWallet(ReplyType::Manual); - if (xbtWallet && (walletId == xbtWallet->walletId() || xbtWallet->getLeaf(walletId))) { - updateBalanceLabel(); - } -} - -void bs::ui::RFQDealerReply::submit(double price, const std::shared_ptr& replyData) -{ - SPDLOG_LOGGER_DEBUG(logger_, "submitted quote reply on {}: {}" - , replyData->qn.quoteRequestId, replyData->qn.price); - sentNotifs_[replyData->qn.quoteRequestId] = price; - submitQuoteNotifCb_(replyData); - activeQuoteSubmits_.erase(replyData->qn.quoteRequestId); - updateSubmitButton(); - refreshSettlementDetails(); -} - -void bs::ui::RFQDealerReply::reserveBestUtxoSetAndSubmit(double quantity, double price, - const std::shared_ptr& replyData, ReplyType replyType) -{ - if (walletsManager_ && utxoReservationManager_) { - auto replyRFQWrapper = [rfqReply = QPointer(this), - price, replyData, replyType](std::vector utxos) { - if (!rfqReply) { - return; - } - - if (utxos.empty()) { - if (replyType == ReplyType::Manual) { - replyData->fixedXbtInputs = rfqReply->selectedXbtInputs_; - replyData->utxoRes = std::move(rfqReply->selectedXbtRes_); - } - - rfqReply->submit(price, replyData); - return; - } - - if (replyType == ReplyType::Manual) { - rfqReply->selectedXbtInputs_ = utxos; - } - - replyData->utxoRes = rfqReply->utxoReservationManager_->makeNewReservation(utxos); - replyData->fixedXbtInputs = std::move(utxos); - - rfqReply->submit(price, replyData); - }; - - if ((replyData->qn.side == bs::network::Side::Sell && replyData->qn.product != bs::network::XbtCurrency) || - (replyData->qn.side == bs::network::Side::Buy && replyData->qn.product == bs::network::XbtCurrency)) { - replyRFQWrapper({}); - return; // Nothing to reserve - } - - // We shouldn't recalculate better utxo set if that not first quote response - // otherwise, we should chose best set if that wasn't done by user and this is not auto quoting script - if (sentNotifs_.count(replyData->qn.quoteRequestId) || (!selectedXbtInputs_.empty() && replyType == ReplyType::Manual)) { - replyRFQWrapper({}); - return; // already reserved by user - } - - auto security = mdInfo_.find(replyData->qn.security); - if (security == mdInfo_.end()) { - // there is no MD data available so we really can't forecast - replyRFQWrapper({}); - return; - } - - BTCNumericTypes::satoshi_type xbtQuantity = 0; - if (replyData->qn.side == bs::network::Side::Buy) { - if (replyData->qn.assetType == bs::network::Asset::PrivateMarket) { - xbtQuantity = XBTAmount(quantity * mdInfo_[replyData->qn.security].bidPrice).GetValue(); - } else if (replyData->qn.assetType == bs::network::Asset::SpotXBT) { - xbtQuantity = XBTAmount(quantity / mdInfo_[replyData->qn.security].askPrice).GetValue(); - } - } else { - xbtQuantity = XBTAmount(quantity).GetValue(); - } - xbtQuantity = static_cast(xbtQuantity * tradeutils::reservationQuantityMultiplier()); - - auto cbBestUtxoSet = [rfqReply = QPointer(this), - replyRFQ = std::move(replyRFQWrapper)](std::vector&& utxos) { - if (!rfqReply) { - return; - } - replyRFQ(std::move(utxos)); - }; - - // Check amount (required for AQ scripts) - auto checkAmount = bs::UTXOReservationManager::CheckAmount::Enabled; - - if (!replyData->xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - utxoReservationManager_->getBestXbtUtxoSet(replyData->xbtWallet->walletId(), purpose, - xbtQuantity, cbBestUtxoSet, true, checkAmount); - } else { - const bool includeZc = - (replyData->qn.assetType == bs::network::Asset::SpotXBT) - ? bs::UTXOReservationManager::kIncludeZcDealerSpotXbt - : bs::UTXOReservationManager::kIncludeZcDealerCc; - utxoReservationManager_->getBestXbtUtxoSet(replyData->xbtWallet->walletId(), - xbtQuantity, cbBestUtxoSet, true, checkAmount, includeZc); - } - } - else { // new code - replyData->price = price; - if ((replyData->qn.side == bs::network::Side::Sell && replyData->qn.product != bs::network::XbtCurrency) || - (replyData->qn.side == bs::network::Side::Buy && replyData->qn.product == bs::network::XbtCurrency)) { - submit(price, replyData); - return; // Nothing to reserve - } - // We shouldn't recalculate better utxo set if that not first quote response - // otherwise, we should chose best set if that wasn't done by user and this is not auto quoting script - if (sentNotifs_.count(replyData->qn.quoteRequestId) || - (!selectedXbtInputs_.empty() && replyType == ReplyType::Manual)) { - submit(price, replyData); - return; // already reserved by user - } - - if (mdInfo_.find(replyData->qn.security) == mdInfo_.end()) { - logger_->warn("[{}] no MD found for {}", __func__, replyData->qn.security); - submit(price, replyData); - return; - } - - pendingReservations_[replyData->qn.quoteRequestId] = replyData; - BTCNumericTypes::satoshi_type xbtQuantity = 0; - if (replyData->qn.side == bs::network::Side::Buy) { - if (replyData->qn.assetType == bs::network::Asset::PrivateMarket) { - xbtQuantity = XBTAmount(quantity * mdInfo_[replyData->qn.security].bidPrice).GetValue(); - } else if (replyData->qn.assetType == bs::network::Asset::SpotXBT) { - xbtQuantity = XBTAmount(quantity / mdInfo_[replyData->qn.security].askPrice).GetValue(); - } - } else { - xbtQuantity = XBTAmount(quantity).GetValue(); - } - xbtQuantity = static_cast(xbtQuantity * tradeutils::reservationQuantityMultiplier()); - emit needReserveUTXOs(replyData->qn.quoteRequestId, replyData->xbtWalletId, xbtQuantity, true); - } -} - -void bs::ui::RFQDealerReply::refreshSettlementDetails() -{ - if (currentQRN_.empty()) { - ui_->groupBoxSettlementInputs->setEnabled(true); - return; - } - - ui_->groupBoxSettlementInputs->setEnabled(!sentNotifs_.count(currentQRN_.quoteRequestId)); -} - -void bs::ui::RFQDealerReply::updateSpinboxes() -{ - auto setSpinboxValue = [&](CustomDoubleSpinBox* spinBox, double value, double changeSign) { - if (qFuzzyIsNull(value)) { - spinBox->clear(); - return; - } - - if (!spinBox->isEnabled()) { - spinBox->setValue(value); - return; - } - - auto bestQuotePrice = bestQPrices_.find(currentQRN_.quoteRequestId); - if (bestQuotePrice != bestQPrices_.end()) { - spinBox->setValue(bestQuotePrice->second + changeSign * spinBox->singleStep()); - } - else { - spinBox->setValue(value); - } - }; - - // The best quote response for buy orders should decrease price - setSpinboxValue(ui_->spinBoxBidPx, indicBid_, 1.0); - setSpinboxValue(ui_->spinBoxOfferPx, indicAsk_, -1.0); -} - -void bs::ui::RFQDealerReply::updateBalanceLabel() -{ - QString totalBalance = kNoBalanceAvailable; - - const bool includeZc = - (currentQRN_.assetType == bs::network::Asset::SpotXBT) - ? bs::UTXOReservationManager::kIncludeZcDealerSpotXbt - : bs::UTXOReservationManager::kIncludeZcDealerCc; - - if (isXbtSpend()) { - totalBalance = tr("%1 %2") - .arg(UiUtils::displayAmount(getXbtBalance(includeZc).GetValueBitcoin())) - .arg(QString::fromStdString(bs::network::XbtCurrency)); - } else if ((currentQRN_.side == bs::network::Side::Buy) && (currentQRN_.assetType == bs::network::Asset::PrivateMarket)) { - totalBalance = tr("%1 %2") - .arg(UiUtils::displayCCAmount(getPrivateMarketCoinBalance())) - .arg(QString::fromStdString(baseProduct_)); - } else { - if (assetManager_) { - totalBalance = tr("%1 %2") - .arg(UiUtils::displayCurrencyAmount(assetManager_->getBalance(product_, includeZc, nullptr))) - .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); - } - else { - try { - totalBalance = tr("%1 %2").arg(UiUtils::displayCurrencyAmount(balances_.at(product_))) - .arg(QString::fromStdString(currentQRN_.side == bs::network::Side::Buy ? baseProduct_ : product_)); - } - catch (const std::exception&) {} - } - } - - ui_->labelBalanceValue->setText(totalBalance); - ui_->cashBalanceLabel->setText(selectedXbtInputs_.empty() ? kAvailableBalance : kReservedBalance); -} - -bs::XBTAmount RFQDealerReply::getXbtBalance(bool includeZc) const -{ - const auto fixedInputs = selectedXbtInputs(ReplyType::Manual); - if (!fixedInputs.empty()) { - int64_t sum = 0; - for (const auto &utxo : fixedInputs) { - sum += utxo.getValue(); - } - return bs::XBTAmount(sum); - } - - if (walletsManager_) { - auto xbtWallet = getSelectedXbtWallet(ReplyType::Manual); - if (!xbtWallet) { - return {}; - } - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXbtWallet); - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId(), purpose, includeZc)); - } else { - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId(), includeZc)); - } - } - else { - const auto& xbtWalletId = getSelectedXbtWalletId(ReplyType::Manual); - if (xbtWalletId.empty()) { // no wallet selected - return {}; - } - //TODO: distinguish between HW and SW wallets later - double balance = 0; - for (const auto& wallet : wallets_) { - if (wallet.id == xbtWalletId) { - for (const auto& group : wallet.groups) { - switch (group.type) { - case bs::hd::CoinType::Bitcoin_main: - case bs::hd::CoinType::Bitcoin_test: - for (const auto& leaf : group.leaves) { - for (const auto& id : leaf.ids) { - try { - balance += balances_.at(id); - } - catch (const std::exception&) {} - } - } - break; - default: break; - } - } - break; - } - } - return bs::XBTAmount{balance}; - } -} - -BTCNumericTypes::balance_type bs::ui::RFQDealerReply::getPrivateMarketCoinBalance() const -{ - if (walletsManager_) { - auto ccWallet = getCCWallet(currentQRN_.product); - if (!ccWallet) { - return 0; - } - return ccWallet->getSpendableBalance(); - } - else { - //TODO - return 0; - } -} diff --git a/BlockSettleUILib/Trading/RFQDealerReply.h b/BlockSettleUILib/Trading/RFQDealerReply.h deleted file mode 100644 index 77d3b877a..000000000 --- a/BlockSettleUILib/Trading/RFQDealerReply.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __RFQ_DEALER_REPLY_H__ -#define __RFQ_DEALER_REPLY_H__ - -#include -#include - -#include -#include -#include -#include -#include - -#include "BSErrorCode.h" -#include "CommonTypes.h" -#include "EncryptionUtils.h" -#include "QWalletInfo.h" -#include "HDPath.h" -#include "UtxoReservationToken.h" -#include "CommonTypes.h" - -namespace Ui { - class RFQDealerReply; -} -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - namespace hd { - class Leaf; - } - class Wallet; - class WalletsManager; - } - class UTXOReservationManager; -} -class ApplicationSettings; -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class AutoSignScriptProvider; -class QuoteProvider; -class SelectedTransactionInputs; -class SignContainer; -class CustomDoubleSpinBox; -class MarketDataProvider; -class ConnectionManager; - -QT_BEGIN_NAMESPACE -class QDoubleSpinBox; -class QPushButton; -QT_END_NAMESPACE - -namespace UiUtils { - enum WalletsTypes : int; -} - -namespace bs { - namespace network { - struct QuoteNotification; - } - - namespace ui { - - struct SubmitQuoteReplyData - { - bs::network::QuoteNotification qn; - bs::UtxoReservationToken utxoRes; - std::shared_ptr xbtWallet; - std::string xbtWalletId; - bs::Address authAddr; - std::vector fixedXbtInputs; - bs::hd::Purpose walletPurpose; - double price; - }; - - class RFQDealerReply : public QWidget - { - Q_OBJECT - - public: - RFQDealerReply(QWidget* parent = nullptr); - ~RFQDealerReply() override; - - [[deprecated]] void init(const std::shared_ptr logger - , const std::shared_ptr & - , const std::shared_ptr& assetManager - , const std::shared_ptr& quoteProvider - , const std::shared_ptr & - , const std::shared_ptr &connectionManager - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &autoSignQuoteProvider - , const std::shared_ptr& utxoReservationManager); - void init(const std::shared_ptr&); - - [[deprecated]] void setWalletsManager(const std::shared_ptr &); - - CustomDoubleSpinBox* bidSpinBox() const; - CustomDoubleSpinBox* offerSpinBox() const; - - QPushButton* pullButton() const; - QPushButton* quoteButton() const; - - using SubmitQuoteNotifCb = std::function &data)>; - void setSubmitQuoteNotifCb(SubmitQuoteNotifCb cb); - - using ResetCurrentReservationCb = std::function &data)>; - void setResetCurrentReservation(ResetCurrentReservationCb cb); - - using GetLastUTXOReplyCb = std::function*(const std::string&)>; - void setGetLastSettlementReply(GetLastUTXOReplyCb cb); - - void onParentAboutToHide(); - - void onHDWallet(const bs::sync::HDWalletData&); - void onBalance(const std::string& currency, double balance); - void onWalletBalance(const bs::sync::WalletBalanceData&); - void onAuthKey(const bs::Address&, const BinaryData& authKey); - void onVerifiedAuthAddresses(const std::vector&); - void onReservedUTXOs(const std::string& resId, const std::string& subId - , const std::vector&); - - signals: - void pullQuoteNotif(const std::string& settlementId, const std::string& reqId, const std::string& reqSessToken); - void needAuthKey(const bs::Address&); - void needReserveUTXOs(const std::string& reserveId, const std::string& subId - , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); - - public slots: - void setQuoteReqNotification(const network::QuoteReqNotification &, double indicBid, double indicAsk); - void quoteReqNotifStatusChanged(const network::QuoteReqNotification &); - void onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields); - void onBestQuotePrice(const QString reqId, double price, bool own); - void onCelerConnected(); - void onCelerDisconnected(); - void onAutoSignStateChanged(); - void onQuoteCancelled(const std::string "eId); - - private slots: - void initUi(); - void priceChanged(); - void updateSubmitButton(); - void submitButtonClicked(); - void pullButtonClicked(); - void showCoinControl(); - void walletSelected(int index); - void onTransactionDataChanged(); - void onAQReply(const bs::network::QuoteReqNotification &qrn, double price); - void onReservedUtxosChanged(const std::string &walletId, const std::vector &); - void onHDLeafCreated(const std::string& ccName); - void onCreateHDWalletError(const std::string& ccName, bs::error::ErrorCode result); - void onAuthAddrChanged(int); - void onUTXOReservationChanged(const std::string& walletId); - - protected: - bool eventFilter(QObject *watched, QEvent *evt) override; - - private: - std::unique_ptr ui_; - std::shared_ptr logger_; - std::shared_ptr walletsManager_; - std::shared_ptr authAddressManager_; - std::shared_ptr assetManager_; - std::shared_ptr quoteProvider_; - std::shared_ptr appSettings_; - std::shared_ptr connectionManager_; - std::shared_ptr signingContainer_; - std::shared_ptr armory_; - std::shared_ptr autoSignProvider_; - std::shared_ptr utxoReservationManager_; - std::string authKey_; - bs::Address authAddr_; - - std::unordered_map sentNotifs_; - network::QuoteReqNotification currentQRN_; - unsigned int payInRecipId_{UINT_MAX}; - bool dealerSellXBT_{false}; - - double indicBid_{}; - double indicAsk_{}; - std::atomic_bool autoUpdatePrices_{true}; - - std::string autoSignWalletId_; - - std::string product_; - std::string baseProduct_; - - bool celerConnected_{false}; - - std::unordered_map bestQPrices_; - QFont invalidBalanceFont_; - - std::unordered_map mdInfo_; - - std::vector selectedXbtInputs_; - bs::UtxoReservationToken selectedXbtRes_; - - SubmitQuoteNotifCb submitQuoteNotifCb_; - ResetCurrentReservationCb resetCurrentReservationCb_; - GetLastUTXOReplyCb getLastUTXOReplyCb_; - - std::set preparingCCRequest_; - - private: - enum class ReplyType - { - Manual, - Script, //obsoleted, as we won't support scripting in the GUI - }; - - enum class AddressType - { - Recv, - Change, - - Max = Change, - }; - - void reset(); - void validateGUI(); - void updateRespQuantity(); - void updateSpinboxes(); - void updateQuoteReqNotification(const network::QuoteReqNotification &); - void updateBalanceLabel(); - double getPrice() const; - double getValue() const; - double getAmount() const; - [[deprecated]] std::shared_ptr getCCWallet(const std::string &cc) const; - [[deprecated]] std::shared_ptr getCCWallet(const bs::network::QuoteReqNotification &qrn) const; - [[deprecated]] void getAddress(const std::string "eRequestId, const std::shared_ptr &wallet - , AddressType type, std::function cb); - void setBalanceOk(bool ok); - bool checkBalance() const; - XBTAmount getXbtBalance(bool includeZc) const; - BTCNumericTypes::balance_type getPrivateMarketCoinBalance() const; - QDoubleSpinBox *getActivePriceWidget() const; - void updateUiWalletFor(const bs::network::QuoteReqNotification &qrn); - // xbtWallet - what XBT wallet to use for XBT/CC trades (selected from UI for manual trades, default wallet for AQ trades), empty for FX trades - void submitReply(const network::QuoteReqNotification &qrn, double price, ReplyType replyType); - void updateWalletsList(int walletsFlags); - bool isXbtSpend() const; - std::string getSelectedXbtWalletId(ReplyType replyType) const; - [[deprecated]] std::shared_ptr getSelectedXbtWallet(ReplyType replyType) const; - bs::Address selectedAuthAddress(ReplyType replyType) const; - std::vector selectedXbtInputs(ReplyType replyType) const; - void submit(double price, const std::shared_ptr& replyData); - void reserveBestUtxoSetAndSubmit(double quantity, double price, - const std::shared_ptr& replyData, ReplyType replyType); - void refreshSettlementDetails(); - - - std::set activeQuoteSubmits_; - std::map(AddressType::Max) + 1>>> addresses_; - - int walletFlags_{ 0 }; - std::vector wallets_; - std::unordered_map balances_; - std::unordered_map> pendingReservations_; - }; - - } //namespace ui -} //namespace bs - -#endif // __RFQ_TICKET_XBT_H__ diff --git a/BlockSettleUILib/Trading/RFQDealerReply.ui b/BlockSettleUILib/Trading/RFQDealerReply.ui deleted file mode 100644 index dc19cec24..000000000 --- a/BlockSettleUILib/Trading/RFQDealerReply.ui +++ /dev/null @@ -1,1665 +0,0 @@ - - - - RFQDealerReply - - - - 0 - 0 - 738 - 750 - - - - - 0 - 0 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - RECEIVED RFQ - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 90 - 0 - - - - - 90 - 16777215 - - - - Product Group - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - - 75 - true - - - - QFrame::Raised - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - false - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 90 - 0 - - - - - 90 - 16777215 - - - - Security - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - - 75 - true - - - - QFrame::Raised - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 90 - 0 - - - - - 90 - 16777215 - - - - Product - - - true - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - - 75 - true - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - false - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 90 - 0 - - - - - 90 - 16777215 - - - - Side - - - true - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - - 75 - true - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - false - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 90 - 0 - - - - - 90 - 16777215 - - - - Quantity - - - true - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - - 75 - true - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - false - - - true - - - - - - - - - - - - - QUOTE RESPONSE - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 80 - 0 - - - - - 80 - 16777215 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 0 - 20 - - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - Product - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - Quantity - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 10 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 3 - - - QLayout::SetMinimumSize - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Requested - - - 5 - - - true - - - - - - - - 0 - 0 - - - - - 0 - - - QLayout::SetMinimumSize - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - -- - - - 5 - - - false - - - true - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - -- - - - 0 - - - 5 - - - false - - - true - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - QLayout::SetMinimumSize - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Responsive - - - 5 - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - -- - - - 5 - - - false - - - true - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - -- - - - 0 - - - 5 - - - false - - - true - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 50 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Bid - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - 0 - - - 10000000.000000000000000 - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Offer - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - 0 - - - 10000000.000000000000000 - - - - - - - - - - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - SETTLEMENT DETAILS - - - true - - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Payment Wallet - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 21 - - - - - 16777215 - 21 - - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Inputs - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Authentication Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 74 - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - false - - - - 0 - 40 - - - - - 16777215 - 40 - - - - - - - PULL - - - - - - - - 0 - 40 - - - - - 16777215 - 40 - - - - - - - QUOTE - - - - - - - - - - - CustomDoubleSpinBox - QDoubleSpinBox -
CustomControls/CustomDoubleSpinBox.h
-
-
- - -
diff --git a/BlockSettleUILib/Trading/RFQDialog.cpp b/BlockSettleUILib/Trading/RFQDialog.cpp deleted file mode 100644 index 16d8ba2d2..000000000 --- a/BlockSettleUILib/Trading/RFQDialog.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RFQDialog.h" -#include "ui_RFQDialog.h" - -#include - -#include "AssetManager.h" -#include "BSMessageBox.h" -#include "HeadlessContainer.h" -#include "QuoteProvider.h" -#include "RFQRequestWidget.h" -#include "ReqCCSettlementContainer.h" -#include "ReqXBTSettlementContainer.h" -#include "RfqStorage.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" - - -RFQDialog::RFQDialog(const std::shared_ptr &logger - , const std::string &id, const bs::network::RFQ& rfq - , const std::shared_ptr& quoteProvider - , const std::shared_ptr& authAddressManager - , const std::shared_ptr& assetManager - , const std::shared_ptr &walletsManager - , const std::shared_ptr &signContainer - , const std::shared_ptr &armory - , const std::shared_ptr &celerClient - , const std::shared_ptr &appSettings - , const std::shared_ptr &rfqStorage - , const std::shared_ptr &xbtWallet - , const bs::Address &recvXbtAddrIfSet - , const bs::Address &authAddr - , const std::shared_ptr &utxoReservationManager - , const std::map &fixedXbtInputs - , bs::UtxoReservationToken fixedXbtUtxoRes - , bs::UtxoReservationToken ccUtxoRes - , bs::hd::Purpose purpose - , RFQRequestWidget *parent) - : QDialog(parent) - , ui_(new Ui::RFQDialog()) - , logger_(logger) - , id_(id), rfq_(rfq) - , recvXbtAddrIfSet_(recvXbtAddrIfSet) - , quoteProvider_(quoteProvider) - , authAddressManager_(authAddressManager) - , walletsManager_(walletsManager) - , signContainer_(signContainer) - , assetMgr_(assetManager) - , armory_(armory) - , celerClient_(celerClient) - , appSettings_(appSettings) - , rfqStorage_(rfqStorage) - , xbtWallet_(xbtWallet) - , authAddr_(authAddr) - , fixedXbtInputs_(fixedXbtInputs) - , fixedXbtUtxoRes_(std::move(fixedXbtUtxoRes)) - , requestWidget_(parent) - , utxoReservationManager_(utxoReservationManager) - , ccUtxoRes_(std::move(ccUtxoRes)) - , walletPurpose_(purpose) -{ - ui_->setupUi(this); - - ui_->pageRequestingQuote->SetAssetManager(assetMgr_); - ui_->pageRequestingQuote->SetCelerClient(celerClient_); - - // NOTE: RFQDialog could be destroyed before SettlementContainer work is done. - // Do not make connections that must live after RFQDialog closing. - - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::cancelRFQ, this, &RFQDialog::reject); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::requestTimedOut, this, &RFQDialog::onTimeout); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteAccepted, this - , &RFQDialog::onRFQResponseAccepted, Qt::QueuedConnection); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFinished, this, &RFQDialog::onQuoteFinished); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFailed, this, &RFQDialog::onQuoteFailed); - - connect(quoteProvider_.get(), &QuoteProvider::quoteReceived, this, &RFQDialog::onQuoteReceived); - connect(quoteProvider_.get(), &QuoteProvider::quoteRejected, ui_->pageRequestingQuote, &RequestingQuoteWidget::onReject); - connect(quoteProvider_.get(), &QuoteProvider::orderRejected, ui_->pageRequestingQuote, &RequestingQuoteWidget::onReject); - connect(quoteProvider_.get(), &QuoteProvider::quoteCancelled, ui_->pageRequestingQuote, &RequestingQuoteWidget::onQuoteCancelled); - - connect(quoteProvider_.get(), &QuoteProvider::orderFailed, this, &RFQDialog::onOrderFailed); - connect(quoteProvider_.get(), &QuoteProvider::quoteOrderFilled, this, &RFQDialog::onOrderFilled); - connect(quoteProvider_.get(), &QuoteProvider::signTxRequested, this, &RFQDialog::onSignTxRequested); - - ui_->pageRequestingQuote->populateDetails(rfq_); - - quoteProvider_->SubmitRFQ(rfq_); -} - -RFQDialog::RFQDialog(const std::shared_ptr& logger - , const std::string& id, const bs::network::RFQ& rfq - , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet - , const bs::Address& authAddr - , bs::hd::Purpose purpose - , RFQRequestWidget* parent) - : QDialog(parent) - , ui_(new Ui::RFQDialog()) - , logger_(logger) - , id_(id), rfq_(rfq) - , recvXbtAddrIfSet_(recvXbtAddrIfSet) - , authAddr_(authAddr) - , requestWidget_(parent) - , walletPurpose_(purpose) -{ - ui_->setupUi(this); - - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::cancelRFQ, this - , &RFQDialog::reject); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::requestTimedOut - , this, &RFQDialog::onTimeout); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteAccepted, this - , &RFQDialog::onRFQResponseAccepted); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFinished, this - , &RFQDialog::onQuoteFinished); - connect(ui_->pageRequestingQuote, &RequestingQuoteWidget::quoteFailed, this - , &RFQDialog::onQuoteFailed); - - ui_->pageRequestingQuote->populateDetails(rfq_); -} - -RFQDialog::~RFQDialog() = default; - -void RFQDialog::onOrderFilled(const std::string "eId) -{ - if (quote_.quoteId != quoteId) { - return; - } - - if (rfq_.assetType == bs::network::Asset::SpotFX - || rfq_.assetType == bs::network::Asset::DeliverableFutures) { - ui_->pageRequestingQuote->onOrderFilled(quoteId); - } -} - -void RFQDialog::onOrderFailed(const std::string& quoteId, const std::string& reason) -{ - if (quote_.quoteId != quoteId) { - return; - } - - if (rfq_.assetType == bs::network::Asset::SpotFX - || rfq_.assetType == bs::network::Asset::DeliverableFutures) { - ui_->pageRequestingQuote->onOrderFailed(quoteId, reason); - } - close(); -} - -void RFQDialog::onRFQResponseAccepted(const std::string &reqId - , const bs::network::Quote "e) -{ - emit accepted(reqId, quote); - quote_ = quote; - - if (rfq_.assetType == bs::network::Asset::SpotFX) { - if (quoteProvider_) { - quoteProvider_->AcceptQuoteFX(QString::fromStdString(reqId), quote); - } - } - else { - if (armory_ && walletsManager_) { - if (rfq_.assetType == bs::network::Asset::SpotXBT) { - curContainer_ = newXBTcontainer(); - } else { - curContainer_ = newCCcontainer(); - } - - if (curContainer_) { - rfqStorage_->addSettlementContainer(curContainer_); - curContainer_->activate(); - - // Do not capture `this` here! - auto failedCb = [qId = quote_.quoteId, curContainer = curContainer_.get()] - (const std::string& quoteId, const std::string& reason) - { - if (qId == quoteId) { - curContainer->cancel(); - } - }; - connect(quoteProvider_.get(), &QuoteProvider::orderFailed, curContainer_.get(), failedCb); - } - } - else { - logger_->debug("[{}] non-FX", __func__); - } - } -} - -void RFQDialog::logError(const std::string &id, bs::error::ErrorCode code, const QString &errorMessage) -{ - logger_->error("[RFQDialog::logError] {}: {}", id, errorMessage.toStdString()); - - if (bs::error::ErrorCode::TxCancelled != code) { - // Do not use this as the parent as it will be destroyed when RFQDialog is closed - QMetaObject::invokeMethod(qApp, [code, errorMessage] { - MessageBoxBroadcastError(errorMessage, code).exec(); - }, Qt::QueuedConnection); - } -} - -std::shared_ptr RFQDialog::newXBTcontainer() -{ - if (!xbtWallet_) { - SPDLOG_LOGGER_ERROR(logger_, "xbt wallet is not set"); - return nullptr; - } - - const bool expandTxInfo = appSettings_->get( - ApplicationSettings::DetailedSettlementTxDialogByDefault); - - const auto tier1XbtLimit = appSettings_->get( - ApplicationSettings::SubmittedAddressXbtLimit); - - try { - xbtSettlContainer_ = std::make_shared(logger_ - , authAddressManager_, signContainer_, armory_, xbtWallet_, walletsManager_ - , rfq_, quote_, authAddr_, fixedXbtInputs_, std::move(fixedXbtUtxoRes_), utxoReservationManager_ - , walletPurpose_, recvXbtAddrIfSet_, expandTxInfo, tier1XbtLimit); - - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::settlementAccepted - , this, &RFQDialog::onXBTSettlementAccepted); - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::settlementCancelled - , this, &QDialog::close); - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::acceptQuote - , this, &RFQDialog::onXBTQuoteAccept); - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::error - , this, &RFQDialog::logError); - - // Use requestWidget_ as RFQDialog could be already destroyed before this moment - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::sendUnsignedPayinToPB - , requestWidget_, &RFQRequestWidget::sendUnsignedPayinToPB); - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::sendSignedPayinToPB - , requestWidget_, &RFQRequestWidget::sendSignedPayinToPB); - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::sendSignedPayoutToPB - , requestWidget_, &RFQRequestWidget::sendSignedPayoutToPB); - - connect(xbtSettlContainer_.get(), &ReqXBTSettlementContainer::cancelTrade - , requestWidget_, &RFQRequestWidget::cancelXBTTrade); - } - catch (const std::exception &e) { - logError({}, bs::error::ErrorCode::InternalError - , tr("Failed to create XBT settlement container: %1") - .arg(QString::fromLatin1(e.what()))); - } - - return xbtSettlContainer_; -} - -void RFQDialog::hideIfNoRemoteSignerMode() -{ - if (signContainer_->opMode() != SignContainer::OpMode::Remote) { - hide(); - } -} - -std::shared_ptr RFQDialog::newCCcontainer() -{ - const bool expandTxInfo = appSettings_->get( - ApplicationSettings::DetailedSettlementTxDialogByDefault); - - try { - ccSettlContainer_ = std::make_shared(logger_ - , signContainer_, armory_, assetMgr_, walletsManager_, rfq_, quote_ - , xbtWallet_, fixedXbtInputs_, utxoReservationManager_, walletPurpose_ - , std::move(ccUtxoRes_), expandTxInfo); - - connect(ccSettlContainer_.get(), &ReqCCSettlementContainer::txSigned - , this, &RFQDialog::onCCTxSigned); - connect(ccSettlContainer_.get(), &ReqCCSettlementContainer::sendOrder - , this, &RFQDialog::onCCQuoteAccepted); - connect(ccSettlContainer_.get(), &ReqCCSettlementContainer::settlementCancelled - , this, &QDialog::close); - connect(ccSettlContainer_.get(), &ReqCCSettlementContainer::error - , this, &RFQDialog::logError); - - connect(ccSettlContainer_.get(), &ReqCCSettlementContainer::cancelTrade - , requestWidget_, &RFQRequestWidget::cancelCCTrade); - - // Do not make circular dependency, capture bare pointer - auto orderUpdatedCb = [qId = quote_.quoteId, ccContainer = ccSettlContainer_.get()] (const bs::network::Order& order) { - if (order.status == bs::network::Order::Pending && order.quoteId == qId) { - ccContainer->setClOrdId(order.clOrderId); - } - }; - connect(quoteProvider_.get(), &QuoteProvider::orderUpdated, ccSettlContainer_.get(), orderUpdatedCb); - } - catch (const std::exception &e) { - logError({}, bs::error::ErrorCode::InternalError - , tr("Failed to create CC settlement container: %1") - .arg(QString::fromLatin1(e.what()))); - } - - return ccSettlContainer_; -} - -void RFQDialog::onCCTxSigned() -{ - quoteProvider_->SignTxRequest(ccOrderId_, ccSettlContainer_->txSignedData()); - close(); -} - -void RFQDialog::reject() -{ - cancel(false); - emit cancelled(id_); - QDialog::reject(); -} - -void RFQDialog::cancel(bool force) -{ - // curContainer_->cancel call could emit settlementCancelled which will result in RFQDialog::reject re-enter. - // This will result in duplicated finished signals. Let's add a workaround for this. - if (isRejectStarted_) { - return; - } - isRejectStarted_ = true; - - if (cancelOnClose_) { - if (curContainer_) { - if (curContainer_->cancel()) { - logger_->debug("[RFQDialog::reject] container cancelled"); - } else { - logger_->warn("[RFQDialog::reject] settlement container failed to cancel"); - } - } - else { - fixedXbtUtxoRes_.release(); - ccUtxoRes_.release(); - } - } - - if (cancelOnClose_) { - if (quoteProvider_) { - quoteProvider_->CancelQuote(QString::fromStdString(rfq_.requestId)); - } - else { - emit cancelled(rfq_.requestId); - } - } - if (force) { - close(); - } -} - -void RFQDialog::onBalance(const std::string& currency, double balance) -{ - ui_->pageRequestingQuote->onBalance(currency, balance); -} - -void RFQDialog::onMatchingLogout() -{ - ui_->pageRequestingQuote->onMatchingLogout(); -} - -void RFQDialog::onSettlementPending(const std::string& quoteId, const BinaryData& settlementId) -{ - //TODO: update UI state -} - -void RFQDialog::onSettlementComplete() -{ - accept(); -} - -void RFQDialog::onTimeout() -{ - emit expired(id_); - cancelOnClose_ = false; - hide(); -} - -void RFQDialog::onQuoteFinished() -{ - if (quoteProvider_) { - emit accepted(id_, quote_); - } - cancelOnClose_ = false; - hide(); -} - -void RFQDialog::onQuoteFailed() -{ - emit cancelled(id_); - close(); -} - -bool RFQDialog::close() -{ - curContainer_.reset(); - cancelOnClose_ = false; - return QDialog::close(); -} - -void RFQDialog::onQuoteReceived(const bs::network::Quote& quote) -{ - ui_->pageRequestingQuote->onQuoteReceived(quote); -} - -void RFQDialog::onXBTSettlementAccepted() -{ - if (xbtSettlContainer_) { - close(); - } else { - logger_->error("[RFQDialog::onXBTSettlementAccepted] XBT settlement accepted with empty container"); - } -} - -void RFQDialog::onCCQuoteAccepted() -{ - if (ccSettlContainer_) { - quoteProvider_->AcceptQuote(QString::fromStdString(rfq_.requestId), quote_ - , ccSettlContainer_->txData()); - } -} - -void RFQDialog::onSignTxRequested(QString orderId, QString reqId, QDateTime timestamp) -{ - if (QString::fromStdString(rfq_.requestId) != reqId) { - logger_->debug("[RFQDialog::onSignTxRequested] not our request. ignore"); - return; - } - - if (ccSettlContainer_ == nullptr) { - logger_->error("[RFQDialog::onSignTxRequested] could not sign with missing container"); - return; - } - - hideIfNoRemoteSignerMode(); - - ccOrderId_ = orderId; - ccSettlContainer_->startSigning(timestamp); -} - -void RFQDialog::onXBTQuoteAccept(std::string reqId, std::string hexPayoutTx) -{ - quoteProvider_->AcceptQuote(QString::fromStdString(reqId), quote_, hexPayoutTx); -} - -void RFQDialog::onUnsignedPayinRequested(const std::string& settlementId) -{ - if (!xbtSettlContainer_ || (settlementId != quote_.settlementId)) { - return; - } - - xbtSettlContainer_->onUnsignedPayinRequested(settlementId); -} - -void RFQDialog::onSignedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash, QDateTime timestamp) -{ - if (!xbtSettlContainer_ || (settlementId != quote_.settlementId)) { - return; - } - - hideIfNoRemoteSignerMode(); - - xbtSettlContainer_->onSignedPayoutRequested(settlementId, payinHash, timestamp); -} - -void RFQDialog::onSignedPayinRequested(const std::string& settlementId - , const BinaryData& unsignedPayin, const BinaryData &payinHash, QDateTime timestamp) -{ - if (!xbtSettlContainer_ || (settlementId != quote_.settlementId)) { - return; - } - - hideIfNoRemoteSignerMode(); - - xbtSettlContainer_->onSignedPayinRequested(settlementId, payinHash, timestamp); -} diff --git a/BlockSettleUILib/Trading/RFQDialog.h b/BlockSettleUILib/Trading/RFQDialog.h deleted file mode 100644 index 85f00e938..000000000 --- a/BlockSettleUILib/Trading/RFQDialog.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __RFQ_DIALOG_H__ -#define __RFQ_DIALOG_H__ - -#include - -#include - -#include "CommonTypes.h" -#include "UtxoReservationToken.h" -#include "BSErrorCode.h" -#include "HDPath.h" - -namespace Ui { - class RFQDialog; -} -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - namespace hd { - class Wallet; - } - class Wallet; - class WalletsManager; - } - class SettlementContainer; - class UTXOReservationManager; -} -class ApplicationSettings; -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class CelerClientQt; -class CCSettlementTransactionWidget; -class HeadlessContainer; -class QuoteProvider; -class RFQRequestWidget; -class ReqCCSettlementContainer; -class ReqXBTSettlementContainer; -class RfqStorage; -class XBTSettlementTransactionWidget; - -class RFQDialog : public QDialog -{ -Q_OBJECT - -public: - [[deprecated]] RFQDialog(const std::shared_ptr &logger - , const std::string &id, const bs::network::RFQ& rfq - , const std::shared_ptr& quoteProvider - , const std::shared_ptr& authAddressManager - , const std::shared_ptr& assetManager - , const std::shared_ptr &walletsManager - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &celerClient - , const std::shared_ptr &appSettings - , const std::shared_ptr &rfqStorage - , const std::shared_ptr &xbtWallet - , const bs::Address &recvXbtAddrIfSet - , const bs::Address &authAddr - , const std::shared_ptr &utxoReservationManager - , const std::map &fixedXbtInputs - , bs::UtxoReservationToken fixedXbtUtxoRes - , bs::UtxoReservationToken ccUtxoRes - , bs::hd::Purpose purpose - , RFQRequestWidget* parent = nullptr); - RFQDialog(const std::shared_ptr& logger - , const std::string& id, const bs::network::RFQ& rfq - , const std::string& xbtWalletId, const bs::Address& recvXbtAddrIfSet - , const bs::Address& authAddr - , bs::hd::Purpose purpose - , RFQRequestWidget* parent = nullptr); - ~RFQDialog() override; - - void cancel(bool force = true); - - void onBalance(const std::string& currency, double balance); - void onMatchingLogout(); - void onSettlementPending(const std::string& quoteId, const BinaryData& settlementId); - void onSettlementComplete(); - -signals: - void accepted(const std::string &id, const bs::network::Quote&); - void expired(const std::string &id); - void cancelled(const std::string &id); - -protected: - void reject() override; - -public slots: - void onUnsignedPayinRequested(const std::string& settlementId); - void onSignedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash, QDateTime timestamp); - void onSignedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin - , const BinaryData &payinHash, QDateTime timestamp); - void onQuoteReceived(const bs::network::Quote& quote); - void onOrderFilled(const std::string& quoteId); - void onOrderFailed(const std::string& quoteId, const std::string& reason); - -private slots: - bool close(); - void onTimeout(); - void onQuoteFinished(); - void onQuoteFailed(); - - void onRFQResponseAccepted(const std::string &reqId, const bs::network::Quote& quote); - void onXBTSettlementAccepted(); - - void onSignTxRequested(QString orderId, QString reqId, QDateTime timestamp); - void onCCQuoteAccepted(); - void onCCTxSigned(); - - void onXBTQuoteAccept(std::string reqId, std::string hexPayoutTx); - void logError(const std::string &id, bs::error::ErrorCode code - , const QString &errorMessage); - -private: - std::shared_ptr newCCcontainer(); - std::shared_ptr newXBTcontainer(); - void hideIfNoRemoteSignerMode(); - -private: - std::unique_ptr ui_; - std::shared_ptr logger_; - const std::string id_; - const bs::network::RFQ rfq_; - bs::network::Quote quote_; - bs::Address recvXbtAddrIfSet_; - - std::shared_ptr quoteProvider_; - std::shared_ptr authAddressManager_; - std::shared_ptr walletsManager_; - std::shared_ptr signContainer_; - std::shared_ptr assetMgr_; - std::shared_ptr armory_; - std::shared_ptr celerClient_; - std::shared_ptr appSettings_; - std::shared_ptr rfqStorage_; - std::shared_ptr xbtWallet_; - std::shared_ptr utxoReservationManager_; - - std::shared_ptr curContainer_; - std::shared_ptr ccSettlContainer_; - std::shared_ptr xbtSettlContainer_; - - const bs::Address authAddr_; - const std::map fixedXbtInputs_; - bs::UtxoReservationToken fixedXbtUtxoRes_; - - bool cancelOnClose_ = true; - bool isRejectStarted_ = false; - - RFQRequestWidget *requestWidget_{}; - - QString ccOrderId_; - bs::UtxoReservationToken ccUtxoRes_; - bs::hd::Purpose walletPurpose_; - -}; - -#endif // __RFQ_DIALOG_H__ diff --git a/BlockSettleUILib/Trading/RFQDialog.ui b/BlockSettleUILib/Trading/RFQDialog.ui deleted file mode 100644 index 719c5425c..000000000 --- a/BlockSettleUILib/Trading/RFQDialog.ui +++ /dev/null @@ -1,64 +0,0 @@ - - - - RFQDialog - - - - 0 - 0 - 340 - 246 - - - - - 0 - 0 - - - - Request-For-Quote - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - RequestingQuoteWidget - QWidget -
RequestingQuoteWidget.h
- 1 -
-
- - -
diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.cpp b/BlockSettleUILib/Trading/RFQReplyWidget.cpp deleted file mode 100644 index 66633b625..000000000 --- a/BlockSettleUILib/Trading/RFQReplyWidget.cpp +++ /dev/null @@ -1,847 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RFQReplyWidget.h" -#include "ui_RFQReplyWidget.h" - -#include "AssetManager.h" -#include "AuthAddressManager.h" -#include "AutoSignQuoteProvider.h" -#include "BSMessageBox.h" -#include "Celer/CelerClient.h" -#include "Celer/SubmitQuoteNotifSequence.h" -#include "CustomControls/CustomDoubleSpinBox.h" -#include "DealerCCSettlementContainer.h" -#include "DialogManager.h" -#include "HeadlessContainer.h" -#include "MDCallbacksQt.h" -#include "OrderListModel.h" -#include "OrdersView.h" -#include "QuoteProvider.h" -#include "RFQBlotterTreeView.h" -#include "SelectedTransactionInputs.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "UserScriptRunner.h" -#include "UtxoReservationManager.h" - -#include "bs_proxy_terminal_pb.pb.h" - -#include - -#include -#include -#include -#include - -using namespace bs::ui; -using namespace Blocksettle::Communication; - -enum class DealingPages : int -{ - ShieldPage = 0, - DealingPage -}; - -RFQReplyWidget::RFQReplyWidget(QWidget* parent) - : TabWithShortcut(parent) - , ui_(new Ui::RFQReplyWidget()) -{ - ui_->setupUi(this); - ui_->shieldPage->setTabType(QLatin1String("dealing")); - - connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::quoteReqNotifStatusChanged, ui_->pageRFQReply - , &RFQDealerReply::quoteReqNotifStatusChanged, Qt::QueuedConnection); - connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::putSetting, this, &RFQReplyWidget::putSetting); - - connect(ui_->shieldPage, &RFQShieldPage::requestPrimaryWalletCreation, - this, &RFQReplyWidget::requestPrimaryWalletCreation); - - ui_->shieldPage->showShieldLoginToResponseRequired(); - popShield(); -} - -RFQReplyWidget::~RFQReplyWidget() = default; - -void RFQReplyWidget::setWalletsManager(const std::shared_ptr &walletsManager) -{ - if (!walletsManager_ && walletsManager) { - walletsManager_ = walletsManager; - ui_->pageRFQReply->setWalletsManager(walletsManager_); - ui_->shieldPage->init(walletsManager_, authAddressManager_, appSettings_); - - if (signingContainer_) { - auto primaryWallet = walletsManager_->getPrimaryWallet(); - if (primaryWallet != nullptr) { - signingContainer_->GetInfo(primaryWallet->walletId()); - } - } - } -} - -void RFQReplyWidget::shortcutActivated(ShortcutType s) -{ - switch (s) { - case ShortcutType::Alt_1 : { - ui_->widgetQuoteRequests->view()->activate(); - } - break; - - case ShortcutType::Alt_2 : { - if (ui_->pageRFQReply->bidSpinBox()->isVisible()) { - if (ui_->pageRFQReply->bidSpinBox()->isEnabled()) - ui_->pageRFQReply->bidSpinBox()->setFocus(); - else - ui_->pageRFQReply->offerSpinBox()->setFocus(); - } else { - ui_->pageRFQReply->setFocus(); - } - } - break; - - case ShortcutType::Alt_3 : { - ui_->treeViewOrders->activate(); - } - break; - - case ShortcutType::Ctrl_Q : { - if (ui_->pageRFQReply->quoteButton()->isEnabled()) - ui_->pageRFQReply->quoteButton()->click(); - } - break; - - case ShortcutType::Ctrl_P : { - if (ui_->pageRFQReply->pullButton()->isEnabled()) - ui_->pageRFQReply->pullButton()->click(); - } - break; - - default : - break; - } -} - -void RFQReplyWidget::onMatchingLogout() -{ - userType_ = bs::network::UserType::Undefined; -} - -void RFQReplyWidget::onMDUpdated(bs::network::Asset::Type at, const QString& security - , const bs::network::MDFields& fields) -{ - ui_->widgetQuoteRequests->onSecurityMDUpdated(at, security, fields); - ui_->pageRFQReply->onMDUpdate(at, security, fields); -} - -void RFQReplyWidget::onBalance(const std::string& currency, double balance) -{ - ui_->pageRFQReply->onBalance(currency, balance); -} - -void RFQReplyWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) -{ - ui_->pageRFQReply->onWalletBalance(wbd); -} - -void RFQReplyWidget::onHDWallet(const bs::sync::HDWalletData& wd) -{ - ui_->pageRFQReply->onHDWallet(wd); -} - -void RFQReplyWidget::onAuthKey(const bs::Address& addr, const BinaryData& authKey) -{ - ui_->pageRFQReply->onAuthKey(addr, authKey); -} - -void RFQReplyWidget::onVerifiedAuthAddresses(const std::vector& addrs) -{ - ui_->pageRFQReply->onVerifiedAuthAddresses(addrs); -} - -void RFQReplyWidget::onReservedUTXOs(const std::string& resId - , const std::string& subId, const std::vector& utxos) -{ - ui_->pageRFQReply->onReservedUTXOs(resId, subId, utxos); -} - -void RFQReplyWidget::onQuoteReqNotification(const bs::network::QuoteReqNotification& qrn) -{ - ui_->widgetQuoteRequests->onQuoteRequest(qrn); -} - -void RFQReplyWidget::onQuoteMatched(const std::string& rfqId - , const std::string& quoteId) -{ - logger_->debug("[{}] {}", __func__, rfqId); - const auto& itAsset = submittedQuote_.find(rfqId); - if (itAsset == submittedQuote_.end()) { - logger_->debug("[{}] not our match on {}", __func__, rfqId); - return; - } - if (itAsset->second == bs::network::Asset::SpotFX) { - onCompleteSettlement(rfqId); - } - else if (itAsset->second == bs::network::Asset::SpotXBT) { - //TODO - } - else if (itAsset->second == bs::network::Asset::PrivateMarket) { - //TODO - } - submittedQuote_.erase(itAsset); -} - -void RFQReplyWidget::onQuoteFailed(const std::string& rfqId, const std::string& quoteId - , const std::string& info) -{ - logger_->debug("[{}] {}", __func__, rfqId); -} - -void RFQReplyWidget::onSettlementPending(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) -{ - logger_->debug("[{}] {}", __func__, rfqId); - const auto& itAsset = submittedQuote_.find(rfqId); - if (itAsset == submittedQuote_.end()) { - logger_->debug("[{}] not our settlement on {}", __func__, rfqId); - return; - } - ui_->widgetQuoteRequests->onSettlementPending(rfqId, quoteId, settlementId, timeLeftMS); -} - -void RFQReplyWidget::onSettlementComplete(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId) -{ - logger_->debug("[{}] {}", __func__, rfqId); - ui_->widgetQuoteRequests->onSettlementComplete(rfqId, quoteId, settlementId); - onCompleteSettlement(rfqId); -} - -void RFQReplyWidget::init(const std::shared_ptr &logger - , const std::shared_ptr& celerClient - , const std::shared_ptr &authAddressManager - , const std::shared_ptr& quoteProvider - , const std::shared_ptr& mdCallbacks - , const std::shared_ptr& assetManager - , const std::shared_ptr &appSettings - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &connectionManager - , const std::shared_ptr &autoSignProvider - , const std::shared_ptr &utxoReservationManager - , OrderListModel *orderListModel -) -{ - logger_ = logger; - celerClient_ = celerClient; - authAddressManager_ = authAddressManager; - quoteProvider_ = quoteProvider; - assetManager_ = assetManager; - signingContainer_ = container; - armory_ = armory; - appSettings_ = appSettings; - connectionManager_ = connectionManager; - autoSignProvider_ = autoSignProvider; - utxoReservationManager_ = utxoReservationManager; - orderListModel_ = orderListModel; - - statsCollector_ = std::make_shared(appSettings - , ApplicationSettings::Filter_MD_QN_cnt); - - ui_->widgetQuoteRequests->init(logger_, quoteProvider_, assetManager, statsCollector_ - , appSettings, celerClient_); - ui_->pageRFQReply->init(logger, authAddressManager, assetManager, quoteProvider_ - , appSettings, connectionManager, signingContainer_, armory_, autoSignProvider - , utxoReservationManager); - ui_->widgetAutoSignQuote->init(autoSignProvider); - - connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::Selected, this - , &RFQReplyWidget::onSelected); - - ui_->pageRFQReply->setSubmitQuoteNotifCb([this] - (const std::shared_ptr &data) - { - statsCollector_->onQuoteSubmitted(data->qn); - quoteProvider_->SubmitQuoteNotif(data->qn); - ui_->widgetQuoteRequests->onQuoteReqNotifReplied(data->qn); - onReplied(data); - }); - - ui_->pageRFQReply->setGetLastSettlementReply([this] - (const std::string& settlementId) -> const std::vector* - { - auto lastReply = sentXbtReplies_.find(settlementId); - if (lastReply == sentXbtReplies_.end()) { - return nullptr; - } - - return &(lastReply->second.utxosPayinFixed); - }); - - ui_->pageRFQReply->setResetCurrentReservation([this](const std::shared_ptr &data) { - onResetCurrentReservation(data); - }); - - connect(ui_->pageRFQReply, &RFQDealerReply::pullQuoteNotif, this - , &RFQReplyWidget::onPulled); - - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, ui_->widgetQuoteRequests - , &QuoteRequestsWidget::onSecurityMDUpdated); - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, ui_->pageRFQReply - , &RFQDealerReply::onMDUpdate); - - connect(quoteProvider_.get(), &QuoteProvider::orderUpdated, this - , &RFQReplyWidget::onOrder); - connect(quoteProvider_.get(), &QuoteProvider::quoteCancelled, this - , &RFQReplyWidget::onQuoteCancelled); - connect(quoteProvider_.get(), &QuoteProvider::bestQuotePrice, ui_->widgetQuoteRequests - , &QuoteRequestsWidget::onBestQuotePrice, Qt::QueuedConnection); - connect(quoteProvider_.get(), &QuoteProvider::bestQuotePrice, ui_->pageRFQReply - , &RFQDealerReply::onBestQuotePrice, Qt::QueuedConnection); - - connect(quoteProvider_.get(), &QuoteProvider::quoteRejected, this, &RFQReplyWidget::onQuoteRejected); - - connect(quoteProvider_.get(), &QuoteProvider::quoteNotifCancelled, this - , &RFQReplyWidget::onQuoteNotifCancelled); - connect(quoteProvider_.get(), &QuoteProvider::allQuoteNotifCancelled - , ui_->widgetQuoteRequests, &QuoteRequestsWidget::onAllQuoteNotifCancelled); - connect(quoteProvider_.get(), &QuoteProvider::signTxRequested, this - , &RFQReplyWidget::onSignTxRequested); - - ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->treeViewOrders->setModel(orderListModel); - ui_->treeViewOrders->initWithModel(orderListModel); - - connect(celerClient_.get(), &CelerClientQt::OnConnectedToServer, this - , &RFQReplyWidget::onConnectedToCeler); - connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, this - , &RFQReplyWidget::onDisconnectedFromCeler); - - connect(ui_->widgetQuoteRequests->view(), &TreeViewWithEnterKey::enterKeyPressed - , this, &RFQReplyWidget::onEnterKeyPressed); -} - -void RFQReplyWidget::init(const std::shared_ptr& logger - , OrderListModel* orderListModel) -{ - logger_ = logger; - statsCollector_ = std::make_shared(ApplicationSettings::Filter_MD_QN_cnt); - - ui_->widgetQuoteRequests->init(logger_, statsCollector_); - ui_->pageRFQReply->init(logger); - - connect(ui_->pageRFQReply, &RFQDealerReply::pullQuoteNotif, this, &RFQReplyWidget::onPulled); - connect(ui_->pageRFQReply, &RFQDealerReply::needAuthKey, this, &RFQReplyWidget::needAuthKey); - connect(ui_->pageRFQReply, &RFQDealerReply::needReserveUTXOs, this, &RFQReplyWidget::needReserveUTXOs); - - ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->treeViewOrders->setModel(orderListModel); - ui_->treeViewOrders->initWithModel(orderListModel); - - connect(ui_->widgetQuoteRequests->view(), &TreeViewWithEnterKey::enterKeyPressed - , this, &RFQReplyWidget::onEnterKeyPressed); - connect(ui_->widgetQuoteRequests, &QuoteRequestsWidget::Selected, this - , &RFQReplyWidget::onSelected); - - ui_->pageRFQReply->setSubmitQuoteNotifCb([this] - (const std::shared_ptr& data) - { - statsCollector_->onQuoteSubmitted(data->qn); - emit submitQuote(data->qn); - submittedQuote_[data->qn.quoteRequestId] = data->qn.assetType; - ui_->widgetQuoteRequests->onQuoteReqNotifReplied(data->qn); - }); -} - -void RFQReplyWidget::forceCheckCondition() -{ - const QModelIndex index = ui_->widgetQuoteRequests->view()->selectionModel()->currentIndex(); - if (!index.isValid()) { - return; - } - ui_->widgetQuoteRequests->onQuoteReqNotifSelected(index); -} - -void RFQReplyWidget::onReplied(const std::shared_ptr &data) -{ - switch (data->qn.assetType) { - case bs::network::Asset::SpotXBT: { - assert(data->xbtWallet); - if (sentReplyToSettlementsIds_.count(data->qn.quoteRequestId)) { - break; // already answered, nothing to do there - } - sentReplyToSettlementsIds_[data->qn.quoteRequestId] = data->qn.settlementId; - settlementToReplyIds_[data->qn.settlementId] = data->qn.quoteRequestId; - auto &reply = sentXbtReplies_[data->qn.settlementId]; - reply.xbtWallet = data->xbtWallet; - reply.authAddr = data->authAddr; - reply.utxosPayinFixed = data->fixedXbtInputs; - reply.utxoRes = std::move(data->utxoRes); - reply.walletPurpose = data->walletPurpose; - break; - } - - case bs::network::Asset::PrivateMarket: { - assert(data->xbtWallet); - auto &reply = sentCCReplies_[data->qn.quoteRequestId]; - reply.recipientAddress = data->qn.receiptAddress; - reply.requestorAuthAddress = data->qn.reqAuthKey; - reply.utxoRes = std::move(data->utxoRes); - reply.xbtWallet = data->xbtWallet; - reply.walletPurpose = data->walletPurpose; - break; - } - - default: { - break; - } - } -} - -void RFQReplyWidget::onPulled(const std::string& settlementId - , const std::string& reqId, const std::string& reqSessToken) -{ - sentXbtReplies_.erase(settlementId); - sentReplyToSettlementsIds_.erase(reqId); - settlementToReplyIds_.erase(settlementId); - if (quoteProvider_) { - quoteProvider_->CancelQuoteNotif(QString::fromStdString(reqId) - , QString::fromStdString(reqSessToken)); - } - else { - emit pullQuote(settlementId, reqId, reqSessToken); - } -} - -void RFQReplyWidget::onUserConnected(const bs::network::UserType &userType) -{ - if (appSettings_) { - const bool autoSigning = appSettings_->get(ApplicationSettings::AutoSigning); - const bool autoQuoting = appSettings_->get(ApplicationSettings::AutoQouting); - - ui_->widgetAutoSignQuote->onUserConnected(autoSigning, autoQuoting); - } - else { //no AQ support in new code - logger_->debug("[{}] user type = {}", __func__, (int)userType); - userType_ = userType; - } -} - -void RFQReplyWidget::onResetCurrentReservation(const std::shared_ptr &data) -{ - switch (data->qn.assetType) { - case bs::network::Asset::PrivateMarket: { - auto it = sentCCReplies_.find(data->qn.quoteRequestId); - if (it != sentCCReplies_.end()) { - it->second.utxoRes.release(); - } - break; - } - - default: { - break; - } - } -} - -void RFQReplyWidget::onOrder(const bs::network::Order &order) -{ - const auto "eReqId = quoteProvider_->getQuoteReqId(order.quoteId); - if (order.assetType == bs::network::Asset::SpotFX - || order.assetType == bs::network::Asset::DeliverableFutures) { - if (order.status == bs::network::Order::Filled) { - onCompleteSettlement(quoteReqId); - quoteProvider_->delQuoteReqId(quoteReqId); - } - return; - } - - const bool expandTxInfo = appSettings_->get( - ApplicationSettings::DetailedSettlementTxDialogByDefault); - - if (order.status == bs::network::Order::Pending) { - if (order.assetType == bs::network::Asset::PrivateMarket) { - if (quoteReqId.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "quoteReqId is empty for {}", order.quoteId); - return; - } - const auto itCCSR = sentCCReplies_.find(quoteReqId); - if (itCCSR == sentCCReplies_.end()) { - SPDLOG_LOGGER_DEBUG(logger_, "missing previous CC reply for {}", quoteReqId); - return; - } - sentReplyToSettlementsIds_[quoteReqId] = order.clOrderId; - settlementToReplyIds_[order.clOrderId] = quoteReqId; - auto &sr = itCCSR->second; - try { - const auto settlContainer = std::make_shared(logger_ - , order, quoteReqId, assetManager_->getCCLotSize(order.product) - , assetManager_->getCCGenesisAddr(order.product), sr.recipientAddress - , sr.xbtWallet, signingContainer_, armory_, walletsManager_ - , sr.walletPurpose, std::move(sr.utxoRes), expandTxInfo); - connect(settlContainer.get(), &DealerCCSettlementContainer::signTxRequest - , this, &RFQReplyWidget::saveTxData); - connect(settlContainer.get(), &DealerCCSettlementContainer::error - , this, &RFQReplyWidget::onTransactionError); - connect(settlContainer.get(), &DealerCCSettlementContainer::cancelTrade - , this, &RFQReplyWidget::onCancelCCTrade); - connect(settlContainer.get(), &DealerCCSettlementContainer::completed - , this, &RFQReplyWidget::onCompleteSettlement); - - // Do not make circular dependency, capture bare pointer - auto orderUpdatedCb = [settlContainer = settlContainer.get(), quoteId = order.quoteId] - (const std::string& failedQuoteId, const std::string& reason) { - if (settlContainer && quoteId == failedQuoteId) { - settlContainer->cancel(); - } - }; - connect(quoteProvider_.get(), &QuoteProvider::orderFailed, settlContainer.get(), orderUpdatedCb); - - ui_->widgetQuoteRequests->addSettlementContainer(settlContainer); - settlContainer->activate(); - - } catch (const std::exception &e) { - BSMessageBox box(BSMessageBox::critical, tr("Settlement error") - , tr("Failed to start dealer's CC settlement") - , QString::fromLatin1(e.what()) - , this); - box.exec(); - } - } else { - const auto &it = sentXbtReplies_.find(order.settlementId.toBinStr()); - if (it == sentXbtReplies_.end()) { - // Looks like this is not error, not sure why we need this - SPDLOG_LOGGER_DEBUG(logger_, "haven't seen QuoteNotif with settlId={}" - , order.settlementId.toBinStr()); - return; - } - try { - auto &reply = it->second; - // Dealers can't select receiving address, use new - const auto recvXbtAddr = bs::Address(); - -#if 0 //TODO: use new arch settlement signals instead of settlement container - const auto tier1XbtLimit = appSettings_->get( - ApplicationSettings::SubmittedAddressXbtLimit); - - const auto settlContainer = std::make_shared(logger_ - , order, walletsManager_, reply.xbtWallet, quoteProvider_, signingContainer_ - , armory_, authAddressManager_, reply.authAddr, reply.utxosPayinFixed - , recvXbtAddr, utxoReservationManager_, reply.walletPurpose - , std::move(reply.utxoRes), expandTxInfo, tier1XbtLimit); - - connect(settlContainer.get(), &DealerXBTSettlementContainer::sendUnsignedPayinToPB - , this, &RFQReplyWidget::sendUnsignedPayinToPB); - connect(settlContainer.get(), &DealerXBTSettlementContainer::sendSignedPayinToPB - , this, &RFQReplyWidget::sendSignedPayinToPB); - connect(settlContainer.get(), &DealerXBTSettlementContainer::sendSignedPayoutToPB - , this, &RFQReplyWidget::sendSignedPayoutToPB); - connect(settlContainer.get(), &DealerXBTSettlementContainer::cancelTrade - , this, &RFQReplyWidget::onCancelXBTTrade); - connect(settlContainer.get(), &DealerXBTSettlementContainer::error - , this, &RFQReplyWidget::onTransactionError); - connect(settlContainer.get(), &DealerCCSettlementContainer::completed - , this, &RFQReplyWidget::onCompleteSettlement); - - connect(this, &RFQReplyWidget::unsignedPayinRequested, settlContainer.get() - , &DealerXBTSettlementContainer::onUnsignedPayinRequested); - connect(this, &RFQReplyWidget::signedPayoutRequested, settlContainer.get() - , &DealerXBTSettlementContainer::onSignedPayoutRequested); - connect(this, &RFQReplyWidget::signedPayinRequested, settlContainer.get() - , &DealerXBTSettlementContainer::onSignedPayinRequested); - - // Do not make circular dependency, capture bare pointer - connect(quoteProvider_.get(), &QuoteProvider::orderFailed, settlContainer.get() - , [settlContainer = settlContainer.get(), quoteId = order.quoteId] - (const std::string& failedQuoteId, const std::string& reason) { - if (quoteId == failedQuoteId) { - settlContainer->cancel(); - } - }); - - // Add before calling activate as this will hook some events - ui_->widgetQuoteRequests->addSettlementContainer(settlContainer); - - settlContainer->activate(); -#endif //0 - } catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "settlement failed: {}", e.what()); - BSMessageBox box(BSMessageBox::critical, tr("Settlement error") - , tr("Failed to start dealer's settlement") - , QString::fromLatin1(e.what()) - , this); - box.exec(); - } - } - } else { - if (!quoteReqId.empty()) { - sentCCReplies_.erase(quoteReqId); - quoteProvider_->delQuoteReqId(quoteReqId); - } - sentXbtReplies_.erase(order.settlementId.toBinStr()); - } -} - -void RFQReplyWidget::onQuoteCancelled(const QString &reqId, bool userCancelled) -{ - eraseReply(reqId); - ui_->widgetQuoteRequests->onQuoteReqCancelled(reqId, userCancelled); - ui_->pageRFQReply->onQuoteCancelled(reqId.toStdString()); -} - -void RFQReplyWidget::onQuoteRejected(const QString &reqId, const QString &reason) -{ - eraseReply(reqId); - ui_->widgetQuoteRequests->onQuoteRejected(reqId, reason); -} - -void RFQReplyWidget::onQuoteNotifCancelled(const QString &reqId) -{ - eraseReply(reqId); - ui_->widgetQuoteRequests->onQuoteNotifCancelled(reqId); -} - -void RFQReplyWidget::onConnectedToCeler() -{ - ui_->shieldPage->showShieldSelectTargetDealing(); - popShield(); - ui_->pageRFQReply->onCelerConnected(); -} - -void RFQReplyWidget::onDisconnectedFromCeler() -{ - ui_->shieldPage->showShieldLoginToResponseRequired(); - popShield(); - ui_->pageRFQReply->onCelerDisconnected(); -} - -void RFQReplyWidget::onEnterKeyPressed(const QModelIndex &index) -{ - ui_->widgetQuoteRequests->onQuoteReqNotifSelected(index); - - if (ui_->pageRFQReply->quoteButton()->isEnabled()) { - ui_->pageRFQReply->quoteButton()->click(); - return; - } - - if (ui_->pageRFQReply->pullButton()->isEnabled()) { - ui_->pageRFQReply->pullButton()->click(); - return; - } -} - -void RFQReplyWidget::onSelected(const QString& productGroup, const bs::network::QuoteReqNotification& request, double indicBid, double indicAsk) -{ - if (!checkConditions(productGroup, request)) { - logger_->debug("[{}] checkConditions failed", __func__); - return; - } - - ui_->pageRFQReply->setQuoteReqNotification(request, indicBid, indicAsk); -} - -void RFQReplyWidget::onTransactionError(const std::string &id - , bs::error::ErrorCode code, const QString& error) -{ - const auto &itReqId = settlementToReplyIds_.find(id); - if (itReqId != settlementToReplyIds_.end()) { - ((AQScriptRunner *)autoSignProvider_->scriptRunner())->cancelled(itReqId->second); - } - if (bs::error::ErrorCode::TxCancelled != code) { - // Use QueuedConnection to not start new even loop from SettlementContainer callbacks. - // Otherwise SettlementContainer might be already destroyed when this method returns. - QMetaObject::invokeMethod(this, [this, error, code] { - MessageBoxBroadcastError(error, code, this).exec(); - }, Qt::QueuedConnection); - } -} - -void RFQReplyWidget::onCancelXBTTrade(const std::string& settlementId) -{ - const auto &itReqId = settlementToReplyIds_.find(settlementId); - if (itReqId != settlementToReplyIds_.end()) { - ((AQScriptRunner *)autoSignProvider_->scriptRunner())->cancelled(itReqId->second); - } - emit cancelXBTTrade(settlementId); -} - -void RFQReplyWidget::onCancelCCTrade(const std::string& clientOrderId) -{ - const auto &itReqId = settlementToReplyIds_.find(clientOrderId); - if (itReqId != settlementToReplyIds_.end()) { - ((AQScriptRunner *)autoSignProvider_->scriptRunner())->cancelled(itReqId->second); - } - emit cancelCCTrade(clientOrderId); -} - -void RFQReplyWidget::onCompleteSettlement(const std::string &id) -{ - if (!autoSignProvider_) { - return; - } - const auto &itReqId = settlementToReplyIds_.find(id); - if (itReqId == settlementToReplyIds_.end()) { - ((AQScriptRunner *)autoSignProvider_->scriptRunner())->settled(id); // FX settlement - } - else { - ((AQScriptRunner *)autoSignProvider_->scriptRunner())->settled(itReqId->second); - } -} - -void RFQReplyWidget::saveTxData(QString orderId, std::string txData) -{ - quoteProvider_->SignTxRequest(orderId, txData); -} - -void RFQReplyWidget::onSignTxRequested(QString orderId, QString reqId, QDateTime timestamp) -{ - Q_UNUSED(reqId); - - if (!ui_->widgetQuoteRequests->StartCCSignOnOrder(orderId, timestamp)) { - // Not an error because onSignTxRequested is also called for requesters - logger_->debug("[RFQReplyWidget::onSignTxRequested] failed to initiate sign on CC order: {}" - , orderId.toStdString()); - } -} - -bool RFQReplyWidget::checkConditions(const QString& productGroup - , const bs::network::QuoteReqNotification& request) -{ - ui_->stackedWidget->setEnabled(true); - - if (productGroup.isEmpty() || request.product.empty()) { - ui_->shieldPage->showShieldSelectTargetDealing(); - popShield(); - return true; - } - - const auto &userType = celerClient_ ? celerClient_->celerUserType() : userType_; - - using GroupType = RFQShieldPage::ProductType; - const GroupType group = RFQShieldPage::getProductGroup(productGroup); - - switch (userType) { - case bs::network::UserType::Market: - if (group == GroupType::SpotFX) { - ui_->shieldPage->showShieldReservedTradingParticipant(); - popShield(); - return false; - } - else if (group == GroupType::SpotXBT) { - ui_->shieldPage->showShieldReservedDealingParticipant(); - popShield(); - return false; - } - else if (ui_->shieldPage->checkWalletSettings(group, QString::fromStdString(request.product))) { - popShield(); - return false; - } - break; - case bs::network::UserType::Trading: - if (group == GroupType::SpotXBT) { - ui_->shieldPage->showShieldReservedDealingParticipant(); - return false; - } else if (group == GroupType::PrivateMarket && - ui_->shieldPage->checkWalletSettings(group, QString::fromStdString(request.product))) { - popShield(); - return false; - } - break; - case bs::network::UserType::Dealing: - if ((group == GroupType::SpotXBT || group == GroupType::PrivateMarket) && - ui_->shieldPage->checkWalletSettings(group, QString::fromStdString(request.product))) { - popShield(); - return false; - } - break; - default: break; - } - - if (ui_->stackedWidget->currentIndex() != static_cast(DealingPages::DealingPage)) { - showEditableRFQPage(); - } - - return true; -} - -void RFQReplyWidget::popShield() -{ - ui_->stackedWidget->setEnabled(true); - - ui_->stackedWidget->setCurrentIndex(static_cast(DealingPages::ShieldPage)); - ui_->pageRFQReply->setDisabled(true); -} - -void RFQReplyWidget::showEditableRFQPage() -{ - ui_->stackedWidget->setEnabled(true); - ui_->pageRFQReply->setEnabled(true); - ui_->stackedWidget->setCurrentIndex(static_cast(DealingPages::DealingPage)); -} - - -void RFQReplyWidget::eraseReply(const QString &reqId) -{ - const auto &itSettlId = sentReplyToSettlementsIds_.find(reqId.toStdString()); - if (itSettlId != sentReplyToSettlementsIds_.end()) { - settlementToReplyIds_.erase(itSettlId->second); - sentXbtReplies_.erase(itSettlId->second); - sentReplyToSettlementsIds_.erase(itSettlId); - } - sentCCReplies_.erase(reqId.toStdString()); -} - -void RFQReplyWidget::hideEvent(QHideEvent* event) -{ - ui_->pageRFQReply->onParentAboutToHide(); - QWidget::hideEvent(event); -} - -void RFQReplyWidget::onMessageFromPB(const ProxyTerminalPb::Response &response) -{ - switch (response.data_case()) { - case Blocksettle::Communication::ProxyTerminalPb::Response::kSendUnsignedPayin: { - const auto &command = response.send_unsigned_payin(); - emit unsignedPayinRequested(command.settlement_id()); - break; - } - - case Blocksettle::Communication::ProxyTerminalPb::Response::kSignPayout: { - const auto &command = response.sign_payout(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(command.timestamp_ms()); - // payin_data - payin hash . binary - emit signedPayoutRequested(command.settlement_id(), BinaryData::fromString(command.payin_data()), timestamp); - break; - } - - case Blocksettle::Communication::ProxyTerminalPb::Response::kSignPayin: { - auto command = response.sign_payin(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(command.timestamp_ms()); - // unsigned_payin_data - serialized payin. binary - emit signedPayinRequested(command.settlement_id(), BinaryData::fromString(command.unsigned_payin_data()) - , BinaryData::fromString(command.payin_hash()), timestamp); - break; - } - - default: - break; - } - // if not processed - not RFQ releated message. not error -} - -void RFQReplyWidget::onOrderClicked(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - if (orderListModel_->DeliveryRequired(index)) { - emit CreateObligationDeliveryTX(index); - } -} diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.h b/BlockSettleUILib/Trading/RFQReplyWidget.h deleted file mode 100644 index 086a00b87..000000000 --- a/BlockSettleUILib/Trading/RFQReplyWidget.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __RFQ_REPLY_WIDGET_H__ -#define __RFQ_REPLY_WIDGET_H__ - -#include -#include -#include -#include -#include -#include -#include "ApplicationSettings.h" -#include "BSErrorCode.h" -#include "CoinControlModel.h" -#include "CommonTypes.h" -#include "HDPath.h" -#include "SignerDefs.h" -#include "TabWithShortcut.h" -#include "UtxoReservationToken.h" - -#include -#include -#include -#include - -namespace Ui { - class RFQReplyWidget; -} -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - namespace hd { - class Wallet; - } - class WalletsManager; - } - class SettlementAddressEntry; - class SecurityStatsCollector; - class UTXOReservationManager; -} -class ApplicationSettings; -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class AutoSignScriptProvider; -class CelerClientQt; -class ConnectionManager; -class MDCallbacksQt; -class OrderListModel; -class QuoteProvider; - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - class Response_UpdateOrders; - } - } -} - -namespace bs { - namespace ui { - struct SubmitQuoteReplyData; - } -} - -class RFQReplyWidget : public TabWithShortcut -{ -Q_OBJECT - -public: - RFQReplyWidget(QWidget* parent = nullptr); - ~RFQReplyWidget() override; - - [[deprecated]] void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , OrderListModel *orderListModel); - void init(const std::shared_ptr&, OrderListModel*); - [[deprecated]] void setWalletsManager(const std::shared_ptr &); - - void shortcutActivated(ShortcutType s) override; - - void onMatchingLogout(); - void onMDUpdated(bs::network::Asset::Type, const QString& security - , const bs::network::MDFields&); - void onBalance(const std::string& currency, double balance); - void onWalletBalance(const bs::sync::WalletBalanceData&); - void onHDWallet(const bs::sync::HDWalletData&); - void onAuthKey(const bs::Address&, const BinaryData& authKey); - void onVerifiedAuthAddresses(const std::vector&); - void onReservedUTXOs(const std::string& resId, const std::string& subId - , const std::vector&); - - void onQuoteReqNotification(const bs::network::QuoteReqNotification&); - void onQuoteMatched(const std::string& rfqId, const std::string& quoteId); - void onQuoteFailed(const std::string& rfqId, const std::string& quoteId - , const std::string& info); - void onSettlementPending(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId, int timeLeftMS); - void onSettlementComplete(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId); - -signals: - void orderFilled(); - void requestPrimaryWalletCreation(); - - void sendUnsignedPayinToPB(const std::string& settlementId, const bs::network::UnsignedPayinData& unsignedPayinData); - void sendSignedPayinToPB(const std::string& settlementId, const BinaryData& signedPayin); - void sendSignedPayoutToPB(const std::string& settlementId, const BinaryData& signedPayout); - - void cancelXBTTrade(const std::string& settlementId); - void cancelCCTrade(const std::string& clientOrderId); - - void unsignedPayinRequested(const std::string& settlementId); - void signedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash, QDateTime timestamp); - void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin - , const BinaryData &payinHash, QDateTime timestamp); - - void CreateObligationDeliveryTX(const QModelIndex& index); - - void submitQuote(const bs::network::QuoteNotification&); - void pullQuote(const std::string& settlementId, const std::string& reqId - , const std::string& reqSessToken); - - void putSetting(ApplicationSettings::Setting, const QVariant&); - void needAuthKey(const bs::Address&); - void needReserveUTXOs(const std::string& reserveId, const std::string& subId - , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); - -public slots: - void forceCheckCondition(); - - void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); - void onUserConnected(const bs::network::UserType &); - void onQuoteCancelled(const QString& reqId, bool userCancelled); - void onQuoteNotifCancelled(const QString& reqId); - -private slots: - void onOrder(const bs::network::Order &o); - void onQuoteRejected(const QString &reqId, const QString &reason); - - void saveTxData(QString orderId, std::string txData); - void onSignTxRequested(QString orderId, QString reqId, QDateTime timestamp); - void onConnectedToCeler(); - void onDisconnectedFromCeler(); - void onEnterKeyPressed(const QModelIndex &index); - void onSelected(const QString& productGroup, const bs::network::QuoteReqNotification& request, double indicBid, double indicAsk); - void onTransactionError(const std::string &id, bs::error::ErrorCode code, const QString& error); - - void onReplied(const std::shared_ptr &data); - void onPulled(const std::string& settlementId, const std::string& reqId, const std::string& reqSessToken); - - void onCancelXBTTrade(const std::string& settlementId); - void onCancelCCTrade(const std::string& clientOrderId); - void onCompleteSettlement(const std::string &id); - - void onOrderClicked(const QModelIndex &index); - -private: - void onResetCurrentReservation(const std::shared_ptr &data); - bool checkConditions(const QString& productGroup, const bs::network::QuoteReqNotification& request); - void popShield(); - void showEditableRFQPage(); - void eraseReply(const QString &reqId); - -protected: - void hideEvent(QHideEvent* event) override; - -private: - struct SentXbtReply - { - std::shared_ptr xbtWallet; - bs::Address authAddr; - std::vector utxosPayinFixed; - bs::UtxoReservationToken utxoRes; - bs::hd::Purpose walletPurpose; - }; - - struct SentCCReply - { - std::string recipientAddress; - std::string requestorAuthAddress; - std::shared_ptr xbtWallet; - bs::UtxoReservationToken utxoRes; - bs::hd::Purpose walletPurpose; - }; - -private: - std::unique_ptr ui_; - std::shared_ptr logger_; - std::shared_ptr celerClient_; - std::shared_ptr quoteProvider_; - std::shared_ptr authAddressManager_; - std::shared_ptr assetManager_; - std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; - std::shared_ptr armory_; - std::shared_ptr appSettings_; - std::shared_ptr connectionManager_; - std::shared_ptr autoSignProvider_; - std::shared_ptr utxoReservationManager_; - OrderListModel* orderListModel_; - - std::unordered_map sentXbtReplies_; - std::unordered_map sentCCReplies_; - std::shared_ptr statsCollector_; - std::unordered_map sentReplyToSettlementsIds_, settlementToReplyIds_; - - bs::network::UserType userType_{ bs::network::UserType::Undefined }; - std::unordered_map submittedQuote_; -}; - -#include - -class PushButtonDelegate : public QStyledItemDelegate -{ - Q_OBJECT - -public: - explicit PushButtonDelegate(QWidget* parent) - { - button_ = new QPushButton(tr("Submit"), parent); - button_->hide(); - } - ~PushButtonDelegate() override = default; - - QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const override - { - auto statusGroupIndex = index; - int depth = 0; - while (statusGroupIndex.parent().isValid()) { - statusGroupIndex = statusGroupIndex.parent(); - ++depth; - } - - if (statusGroupIndex.row() != 2 || depth != 1) { - return QStyledItemDelegate::sizeHint(option, index); - } - - auto minSizeHint = button_->minimumSizeHint(); - auto minSize = button_->minimumSize(); - auto sizeHint = button_->sizeHint(); - - return sizeHint; - } - - void paint(QPainter* painter, const QStyleOptionViewItem& opt, - const QModelIndex& index) const override - { - auto statusGroupIndex = index; - int depth = 0; - while (statusGroupIndex.parent().isValid()) { - statusGroupIndex = statusGroupIndex.parent(); - ++depth; - } - - if (statusGroupIndex.row() != 2 || depth != 2) { - QStyledItemDelegate::paint(painter, opt, index); - return; - } - - auto minSizeHint = button_->minimumSizeHint(); - auto rect = opt.rect; - - if (rect.width() < minSizeHint.width()) { - rect.setWidth(minSizeHint.width()); - } - - if (rect.height() < minSizeHint.height()) { - rect.setHeight(minSizeHint.height()); - } - - button_->setGeometry(rect); - - qDebug() << "PushButtonDelegate " << opt.state; - - QPixmap map = button_->grab(); - painter->drawPixmap(rect.x(), rect.y(), map); - } - -private: - QPushButton *button_; -}; - -#endif // __RFQ_REPLY_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/RFQReplyWidget.ui b/BlockSettleUILib/Trading/RFQReplyWidget.ui deleted file mode 100644 index 2b898e9b3..000000000 --- a/BlockSettleUILib/Trading/RFQReplyWidget.ui +++ /dev/null @@ -1,498 +0,0 @@ - - - - RFQReplyWidget - - - - 0 - 0 - 754 - 601 - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - QUOTE REQUEST BLOTTER - - - true - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - - - - - - - - 16777215 - 16777215 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - - 16777215 - 30 - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - TRADE BLOTTER - - - true - - - - - - - - - - - 16777215 - 16777215 - - - - QAbstractItemView::NoEditTriggers - - - true - - - true - - - true - - - true - - - 92 - - - - - - - - - - - - - - 0 - 0 - - - - - 300 - 0 - - - - - 300 - 16777215 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - 0 - 0 - - - - - - - 0 - 0 - - - - - 350 - 16777215 - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - QUOTE RESPONSE - - - true - - - - - - - - - - - 0 - 0 - - - - - 0 - 460 - - - - - 16777215 - 16777215 - - - - true - - - - - - - - - - - - 0 - 0 - - - - - 0 - 120 - - - - - 16777215 - 120 - - - - - - - - - - - - OrdersView - QTreeView -
OrdersView.h
-
- - QuoteRequestsWidget - QWidget -
QuoteRequestsWidget.h
- 1 -
- - bs::ui::RFQDealerReply - QWidget -
RFQDealerReply.h
-
- - RFQShieldPage - QWidget -
RFQShieldPage.h
- 1 -
- - AutoSignQuoteWidget - QWidget -
AutoSignQuoteWidget.h
- 1 -
-
- - -
diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.cpp b/BlockSettleUILib/Trading/RFQRequestWidget.cpp deleted file mode 100644 index a1f2bced6..000000000 --- a/BlockSettleUILib/Trading/RFQRequestWidget.cpp +++ /dev/null @@ -1,688 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RFQRequestWidget.h" - -#include -#include - -#include "ApplicationSettings.h" -#include "AuthAddressManager.h" -#include "AutoSignQuoteProvider.h" -#include "BSMessageBox.h" -#include "Celer/CelerClient.h" -#include "CurrencyPair.h" -#include "DialogManager.h" -#include "HeadlessContainer.h" -#include "MDCallbacksQt.h" -#include "NotificationCenter.h" -#include "OrderListModel.h" -#include "OrdersView.h" -#include "QuoteProvider.h" -#include "RFQDialog.h" -#include "RfqStorage.h" -#include "UserScriptRunner.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" - -#include "bs_proxy_terminal_pb.pb.h" - -#include "ui_RFQRequestWidget.h" - -using namespace Blocksettle::Communication; -namespace { - enum class RFQPages : int - { - ShieldPage = 0, - EditableRFQPage, - Futures, - }; -} - -RFQRequestWidget::RFQRequestWidget(QWidget* parent) - : TabWithShortcut(parent) - , ui_(new Ui::RFQRequestWidget()) -{ - rfqStorage_ = std::make_shared(); - - ui_->setupUi(this); - ui_->shieldPage->setTabType(QLatin1String("trade")); - - connect(ui_->shieldPage, &RFQShieldPage::requestPrimaryWalletCreation, this, &RFQRequestWidget::requestPrimaryWalletCreation); - connect(ui_->shieldPage, &RFQShieldPage::loginRequested, this, &RFQRequestWidget::loginRequested); - - connect(ui_->pageRFQTicket, &RFQTicketXBT::needWalletData, this, &RFQRequestWidget::needWalletData); - connect(ui_->pageRFQTicket, &RFQTicketXBT::needAuthKey, this, &RFQRequestWidget::needAuthKey); - connect(ui_->pageRFQTicket, &RFQTicketXBT::needReserveUTXOs, this, &RFQRequestWidget::needReserveUTXOs); - - ui_->pageRFQTicket->setSubmitRFQ([this] - (const std::string &id, const bs::network::RFQ& rfq, bs::UtxoReservationToken utxoRes) - { - onRFQSubmit(id, rfq, std::move(utxoRes)); - }); - ui_->pageRFQTicket->setCancelRFQ([this] (const std::string &id) - { - onRFQCancel(id); - }); - - ui_->shieldPage->showShieldLoginToSubmitRequired(); - - ui_->pageRFQTicket->lineEditAmount()->installEventFilter(this); - popShield(); -} - -RFQRequestWidget::~RFQRequestWidget() = default; - -void RFQRequestWidget::shortcutActivated(ShortcutType s) -{ - switch (s) { - case ShortcutType::Alt_1 : { - ui_->widgetMarketData->view()->activate(); - } - break; - - case ShortcutType::Alt_2 : { - if (ui_->pageRFQTicket->lineEditAmount()->isVisible()) { - ui_->pageRFQTicket->lineEditAmount()->setFocus(); - } - else { - ui_->pageRFQTicket->setFocus(); - } - } - break; - - case ShortcutType::Alt_3 : { - ui_->treeViewOrders->activate(); - } - break; - - case ShortcutType::Ctrl_S : { - if (ui_->pageRFQTicket->submitButton()->isEnabled()) { - ui_->pageRFQTicket->submitButton()->click(); - } - } - break; - - case ShortcutType::Alt_S : { - if (ui_->pageRFQTicket->isEnabled()) { - ui_->pageRFQTicket->sellButton()->click(); - } - } - break; - - case ShortcutType::Alt_B : { - if (ui_->pageRFQTicket->isEnabled()) { - ui_->pageRFQTicket->buyButton()->click(); - } - } - break; - - case ShortcutType::Alt_P : { - if (ui_->pageRFQTicket->isEnabled()) { - if (ui_->pageRFQTicket->numCcyButton()->isChecked()) { - ui_->pageRFQTicket->denomCcyButton()->click(); - } - else { - ui_->pageRFQTicket->numCcyButton()->click(); - } - } - } - break; - - default : - break; - } -} - -void RFQRequestWidget::setAuthorized(bool authorized) -{ - ui_->widgetMarketData->setAuthorized(authorized); -} - -void RFQRequestWidget::onNewSecurity(const std::string& name, bs::network::Asset::Type at) -{ - ui_->pageRFQTicket->onNewSecurity(name, at); -} - -void RFQRequestWidget::onMDUpdated(bs::network::Asset::Type assetType - , const QString& security, const bs::network::MDFields &fields) -{ - ui_->widgetMarketData->onMDUpdated(assetType, security, fields); -} - -void RFQRequestWidget::onBalance(const std::string& currency, double balance) -{ - ui_->pageRFQTicket->onBalance(currency, balance); - balances_[currency] = balance; -} - -void RFQRequestWidget::onWalletBalance(const bs::sync::WalletBalanceData& wbd) -{ - ui_->pageRFQTicket->onWalletBalance(wbd); -} - -void RFQRequestWidget::onHDWallet(const bs::sync::HDWalletData& wallet) -{ - ui_->pageRFQTicket->onHDWallet(wallet); -} - -void RFQRequestWidget::onWalletData(const std::string& walletId - , const bs::sync::WalletData& wd) -{ - ui_->pageRFQTicket->onWalletData(walletId, wd); -} - -void RFQRequestWidget::onMatchingLogin(const std::string& mtchLogin - , BaseCelerClient::CelerUserType userType, const std::string& userId) -{ - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::CurrencySelected, - this, &RFQRequestWidget::onCurrencySelected)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::BidClicked, - this, &RFQRequestWidget::onBidClicked)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::AskClicked, - this, &RFQRequestWidget::onAskClicked)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::MDHeaderClicked, - this, &RFQRequestWidget::onDisableSelectedInfo)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::clicked, - this, &RFQRequestWidget::onRefreshFocus)); - - userType_ = userType; - ui_->shieldPage->showShieldSelectTargetTrade(); - popShield(); -} - -void RFQRequestWidget::onMatchingLogout() -{ - for (QMetaObject::Connection& conn : marketDataConnection_) { - QObject::disconnect(conn); - } - for (const auto& dialog : dialogs_) { - dialog.second->onMatchingLogout(); - dialog.second->deleteLater(); - } - dialogs_.clear(); - userType_ = BaseCelerClient::CelerUserType::Undefined; - ui_->shieldPage->showShieldLoginToSubmitRequired(); - popShield(); -} - -void RFQRequestWidget::onVerifiedAuthAddresses(const std::vector& addrs) -{ - ui_->pageRFQTicket->onVerifiedAuthAddresses(addrs); - forceCheckCondition(); -} - -void RFQRequestWidget::onAuthKey(const bs::Address& addr, const BinaryData& authKey) -{ - ui_->pageRFQTicket->onAuthKey(addr, authKey); -} - -void RFQRequestWidget::onTradeSettings(const std::shared_ptr& ts) -{ - ui_->pageRFQTicket->onTradeSettings(ts); -} - -void RFQRequestWidget::onQuoteReceived(const bs::network::Quote& quote) -{ - const auto& itDlg = dialogs_.find(quote.requestId); - if (itDlg != dialogs_.end()) { - itDlg->second->onQuoteReceived(quote); - } -} - -void RFQRequestWidget::onQuoteMatched(const std::string& rfqId, const std::string& quoteId) -{ - const auto& itDlg = dialogs_.find(rfqId); - if (itDlg != dialogs_.end()) { - itDlg->second->onOrderFilled(quoteId); - } -} - -void RFQRequestWidget::onQuoteFailed(const std::string& rfqId - , const std::string& quoteId, const std::string &info) -{ - const auto& itDlg = dialogs_.find(rfqId); - if (itDlg != dialogs_.end()) { - itDlg->second->onOrderFailed(quoteId, info); - } -} - -void RFQRequestWidget::onSettlementPending(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) -{ - const auto& itDlg = dialogs_.find(rfqId); - if (itDlg != dialogs_.end()) { - itDlg->second->onSettlementPending(quoteId, settlementId); - } -} - -void RFQRequestWidget::onSettlementComplete(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId) -{ - const auto& itDlg = dialogs_.find(rfqId); - if (itDlg != dialogs_.end()) { - itDlg->second->onSettlementComplete(); - } else { - logger_->warn("[{}] RFQ dialog for {} not found", __func__, rfqId); - } -} - -void RFQRequestWidget::onReservedUTXOs(const std::string& resId - , const std::string& subId, const std::vector& utxos) -{ - ui_->pageRFQTicket->onReservedUTXOs(resId, subId, utxos); -} - -void RFQRequestWidget::hideEvent(QHideEvent* event) -{ - ui_->pageRFQTicket->onParentAboutToHide(); - QWidget::hideEvent(event); -} - -bool RFQRequestWidget::eventFilter(QObject* sender, QEvent* event) -{ - if (QEvent::KeyPress == event->type() && ui_->pageRFQTicket->lineEditAmount() == sender) { - QKeyEvent *keyEvent = static_cast(event); - if (Qt::Key_Up == keyEvent->key() || Qt::Key_Down == keyEvent->key()) { - QKeyEvent *pEvent = new QKeyEvent(QEvent::KeyPress, keyEvent->key(), keyEvent->modifiers()); - QCoreApplication::postEvent(ui_->widgetMarketData->view(), pEvent); - return true; - } - } - return false; -} - -void RFQRequestWidget::showEditableRFQPage() -{ - ui_->stackedWidgetRFQ->setEnabled(true); - ui_->pageRFQTicket->enablePanel(); - ui_->stackedWidgetRFQ->setCurrentIndex(static_cast(RFQPages::EditableRFQPage)); -} - -void RFQRequestWidget::showFuturesPage(bs::network::Asset::Type type) -{ - ui_->stackedWidgetRFQ->setEnabled(true); - ui_->stackedWidgetRFQ->setCurrentIndex(static_cast(RFQPages::Futures)); - ui_->pageFutures->setType(type); -} - -void RFQRequestWidget::popShield() -{ - ui_->stackedWidgetRFQ->setEnabled(true); - - ui_->stackedWidgetRFQ->setCurrentIndex(static_cast(RFQPages::ShieldPage)); - ui_->pageRFQTicket->disablePanel(); - ui_->widgetMarketData->view()->setFocus(); -} - -void RFQRequestWidget::init(const std::shared_ptr&logger - , const std::shared_ptr& dialogMgr, OrderListModel* orderListModel) -{ - logger_ = logger; - dialogManager_ = dialogMgr; - ui_->pageRFQTicket->init(logger); - - ui_->treeViewOrders->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui_->treeViewOrders->setModel(orderListModel); - ui_->treeViewOrders->initWithModel(orderListModel); - - ui_->pageRFQTicket->disablePanel(); -} - -void RFQRequestWidget::onConnectedToCeler() -{ - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::CurrencySelected, - this, &RFQRequestWidget::onCurrencySelected)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::BidClicked, - this, &RFQRequestWidget::onBidClicked)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::AskClicked, - this, &RFQRequestWidget::onAskClicked)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::MDHeaderClicked, - this, &RFQRequestWidget::onDisableSelectedInfo)); - marketDataConnection_.push_back(connect(ui_->widgetMarketData, &MarketDataWidget::clicked, - this, &RFQRequestWidget::onRefreshFocus)); - - ui_->shieldPage->showShieldSelectTargetTrade(); - popShield(); -} - -void RFQRequestWidget::onDisconnectedFromCeler() -{ - for (QMetaObject::Connection &conn : marketDataConnection_) { - QObject::disconnect(conn); - } - - ui_->shieldPage->showShieldLoginToSubmitRequired(); - popShield(); -} - -void RFQRequestWidget::onRFQSubmit(const std::string &id, const bs::network::RFQ& rfq - , bs::UtxoReservationToken ccUtxoRes) -{ - auto authAddr = ui_->pageRFQTicket->selectedAuthAddress(); - RFQDialog* dialog = nullptr; - auto fixedXbtInputs = ui_->pageRFQTicket->fixedXbtInputs(); - bs::hd::Purpose purpose = bs::hd::Purpose::Unknown; - - if (walletsManager_) { - auto xbtWallet = ui_->pageRFQTicket->xbtWallet(); - - if (xbtWallet && !xbtWallet->canMixLeaves()) { - auto walletType = ui_->pageRFQTicket->xbtWalletType(); - purpose = UiUtils::getHwWalletPurpose(walletType); - } - - dialog = new RFQDialog(logger_, id, rfq, quoteProvider_ - , authAddressManager_, assetManager_, walletsManager_, signingContainer_ - , armory_, celerClient_, appSettings_, rfqStorage_, xbtWallet - , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, utxoReservationManager_ - , fixedXbtInputs.inputs, std::move(fixedXbtInputs.utxoRes) - , std::move(ccUtxoRes), purpose, this); - } - else { - std::string xbtWalletId; - dialog = new RFQDialog(logger_, id, rfq, xbtWalletId - , ui_->pageRFQTicket->recvXbtAddressIfSet(), authAddr, purpose, this); - const std::string reserveId = (rfq.assetType == bs::network::Asset::SpotFX) ? - "" : rfq.requestId; - emit needSubmitRFQ(rfq, reserveId); - } - - connect(this, &RFQRequestWidget::unsignedPayinRequested, dialog, &RFQDialog::onUnsignedPayinRequested); - connect(this, &RFQRequestWidget::signedPayoutRequested, dialog, &RFQDialog::onSignedPayoutRequested); - connect(this, &RFQRequestWidget::signedPayinRequested, dialog, &RFQDialog::onSignedPayinRequested); - connect(dialog, &RFQDialog::accepted, this, &RFQRequestWidget::onRFQAccepted); - connect(dialog, &RFQDialog::expired, this, &RFQRequestWidget::onRFQExpired); - connect(dialog, &RFQDialog::cancelled, this, &RFQRequestWidget::onRFQCancelled); - - dialogManager_->adjustDialogPosition(dialog); - dialog->show(); - - const auto &itDlg = dialogs_.find(id); - if (itDlg != dialogs_.end()) { //np, most likely a resend from script - itDlg->second->deleteLater(); - itDlg->second = dialog; - } - else { - dialogs_[id] = dialog; - } - ui_->pageRFQTicket->resetTicket(); - for (const auto& bal : balances_) { - dialog->onBalance(bal.first, bal.second); - } - - const auto& currentInfo = ui_->widgetMarketData->getCurrentlySelectedInfo(); - ui_->pageRFQTicket->SetProductAndSide(currentInfo.productGroup_ - , currentInfo.currencyPair_, currentInfo.bidPrice_, currentInfo.offerPrice_ - , bs::network::Side::Undefined); - ui_->pageFutures->SetProductAndSide(currentInfo.productGroup_ - , currentInfo.currencyPair_, currentInfo.bidPrice_, currentInfo.offerPrice_ - , bs::network::Side::Undefined); - - std::vector closedDialogs; - for (const auto &dlg : dialogs_) { - if (dlg.second->isHidden()) { - dlg.second->deleteLater(); - closedDialogs.push_back(dlg.first); - } - } - for (const auto &dlg : closedDialogs) { - dialogs_.erase(dlg); - } -} - -void RFQRequestWidget::onRFQCancel(const std::string &id) -{ - deleteDialog(id); -} - -void RFQRequestWidget::deleteDialog(const std::string &rfqId) -{ - const auto &itDlg = dialogs_.find(rfqId); - if (itDlg == dialogs_.end()) { - return; - } - itDlg->second->cancel(); - itDlg->second->deleteLater(); - dialogs_.erase(itDlg); -} - -void RFQRequestWidget::processFutureResponse(const ProxyTerminalPb::Response_FutureResponse &msg) -{ - QMetaObject::invokeMethod(this, [this, msg] { - if (!msg.success()) { - BSMessageBox errorMessage(BSMessageBox::critical, tr("Order message") - , tr("Trade rejected"), QString::fromStdString(msg.error_msg()), this); - errorMessage.exec(); - return; - } - auto productStr = QString::fromStdString(msg.product().empty() ? "" : msg.product()); - auto sideStr = msg.side() == bs::types::Side::SIDE_SELL ? tr("Sell") : tr("Buy"); - auto amountStr = UiUtils::displayAmount(msg.amount()); - auto priceStr = UiUtils::displayPriceXBT(msg.price()); - auto details = tr("Product:\t%1\nSide:\t%2\nVolume:\t%3\nPrice:\t%4") - .arg(productStr).arg(sideStr).arg(amountStr).arg(priceStr); - BSMessageBox errorMessage(BSMessageBox::info, tr("Order message"), tr("Order confirmation"), details, this); - errorMessage.exec(); - }); -} - -bool RFQRequestWidget::checkConditions(const MarketSelectedInfo& selectedInfo) -{ - ui_->stackedWidgetRFQ->setEnabled(true); - using UserType = CelerClient::CelerUserType; - const UserType userType = celerClient_ ? celerClient_->celerUserType() : userType_; - - const auto group = RFQShieldPage::getProductGroup(selectedInfo.productGroup_); - - if (group == WalletShieldBase::ProductType::CashSettledFutures - || group == WalletShieldBase::ProductType::DeliverableFutures) { - showFuturesPage(group); - return true; - } - - switch (userType) { - case UserType::Market: { - if (group == WalletShieldBase::ProductType::SpotFX || group == WalletShieldBase::ProductType::SpotXBT) { - ui_->shieldPage->showShieldReservedTradingParticipant(); - popShield(); - return false; - } else if (checkWalletSettings(group, selectedInfo)) { - return false; - } - break; - } - case UserType::Dealing: - case UserType::Trading: { - if ((group == WalletShieldBase::ProductType::SpotXBT || group == WalletShieldBase::ProductType::PrivateMarket) && - checkWalletSettings(group, selectedInfo)) { - return false; - } - break; - } - default: break; - } - - if (ui_->stackedWidgetRFQ->currentIndex() != static_cast(RFQPages::EditableRFQPage)) { - showEditableRFQPage(); - } - - return true; -} - -bool RFQRequestWidget::checkWalletSettings(bs::network::Asset::Type productType, const MarketSelectedInfo& selectedInfo) -{ - const CurrencyPair cp(selectedInfo.currencyPair_.toStdString()); - const QString currentProduct = QString::fromStdString(cp.NumCurrency()); - if (ui_->shieldPage->checkWalletSettings(productType, currentProduct)) { - popShield(); - return true; - } - return false; -} - -void RFQRequestWidget::forceCheckCondition() -{ - if (celerClient_) { - if (!ui_->widgetMarketData || !celerClient_->IsConnected()) { - return; - } - } - - const auto& currentInfo = ui_->widgetMarketData->getCurrentlySelectedInfo(); - if (!currentInfo.isValid()) { - return; - } - onCurrencySelected(currentInfo); -} - -void RFQRequestWidget::onCurrencySelected(const MarketSelectedInfo& selectedInfo) -{ - if (!checkConditions(selectedInfo)) { - return; - } - ui_->pageRFQTicket->setSecurityId(selectedInfo.productGroup_ - , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); - ui_->pageFutures->setSecurityId(selectedInfo.productGroup_ - , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); -} - -void RFQRequestWidget::onBidClicked(const MarketSelectedInfo& selectedInfo) -{ - if (!checkConditions(selectedInfo)) { - return; - } - ui_->pageRFQTicket->setSecuritySell(selectedInfo.productGroup_ - , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); - ui_->pageFutures->setSecuritySell(selectedInfo.productGroup_ - , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); -} - -void RFQRequestWidget::onAskClicked(const MarketSelectedInfo& selectedInfo) -{ - if (!checkConditions(selectedInfo)) { - return; - } - ui_->pageRFQTicket->setSecurityBuy(selectedInfo.productGroup_ - , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); - ui_->pageFutures->setSecurityBuy(selectedInfo.productGroup_ - , selectedInfo.currencyPair_, selectedInfo.bidPrice_, selectedInfo.offerPrice_); -} - -void RFQRequestWidget::onDisableSelectedInfo() -{ - ui_->shieldPage->showShieldSelectTargetTrade(); - popShield(); -} - -void RFQRequestWidget::onRefreshFocus() -{ - if (ui_->stackedWidgetRFQ->currentIndex() == static_cast(RFQPages::EditableRFQPage)) { - ui_->pageRFQTicket->lineEditAmount()->setFocus(); - } -} - -void RFQRequestWidget::onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response) -{ - switch (response.data_case()) { - case Blocksettle::Communication::ProxyTerminalPb::Response::kSendUnsignedPayin: { - const auto &command = response.send_unsigned_payin(); - emit unsignedPayinRequested(command.settlement_id()); - break; - } - - case Blocksettle::Communication::ProxyTerminalPb::Response::kSignPayout: { - const auto &command = response.sign_payout(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(command.timestamp_ms()); - // payin_data - payin hash . binary - emit signedPayoutRequested(command.settlement_id(), BinaryData::fromString(command.payin_data()), timestamp); - break; - } - - case Blocksettle::Communication::ProxyTerminalPb::Response::kSignPayin: { - const auto &command = response.sign_payin(); - auto timestamp = QDateTime::fromMSecsSinceEpoch(command.timestamp_ms()); - // unsigned_payin_data - serialized payin. binary - emit signedPayinRequested(command.settlement_id(), BinaryData::fromString(command.unsigned_payin_data()) - , BinaryData::fromString(command.payin_hash()), timestamp); - break; - } - - case Blocksettle::Communication::ProxyTerminalPb::Response::kFutureResponse: { - processFutureResponse(response.future_response()); - break; - } - - default: - break; - } - // if not processed - not RFQ releated message. not error -} - -void RFQRequestWidget::onUserConnected(const bs::network::UserType &ut) -{ - if (appSettings_ && appSettings_->get(ApplicationSettings::AutoStartRFQScript)) { - QTimer::singleShot(1000, [this] { // add some delay to allow initial sync of data - ((RFQScriptRunner *)autoSignProvider_->scriptRunner())->start( - autoSignProvider_->getLastScript()); - }); - } -} - -void RFQRequestWidget::onUserDisconnected() -{ - if (autoSignProvider_) { - ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->suspend(); - } -} - -void RFQRequestWidget::onRFQAccepted(const std::string &id - , const bs::network::Quote& quote) -{ - if (autoSignProvider_) { - ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->rfqAccepted(id); - } - else { - emit needAcceptRFQ(id, quote); - } -} - -void RFQRequestWidget::onRFQExpired(const std::string &id) -{ - deleteDialog(id); - if (autoSignProvider_) { - ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->rfqExpired(id); - } - else { - emit needExpireRFQ(id); - } -} - -void RFQRequestWidget::onRFQCancelled(const std::string &id) -{ - if (autoSignProvider_) { - ((RFQScriptRunner*)autoSignProvider_->scriptRunner())->rfqCancelled(id); - } - else { - emit needCancelRFQ(id); - } -} - -void RFQRequestWidget::onOrderClicked(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - if (orderListModel_->DeliveryRequired(index)) { - emit CreateObligationDeliveryTX(index); - } -} diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.h b/BlockSettleUILib/Trading/RFQRequestWidget.h deleted file mode 100644 index eaf99849d..000000000 --- a/BlockSettleUILib/Trading/RFQRequestWidget.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __RFQ_REQUEST_WIDGET_H__ -#define __RFQ_REQUEST_WIDGET_H__ - -#include -#include -#include -#include "Celer/BaseCelerClient.h" -#include "CommonTypes.h" -#include "MarketDataWidget.h" -#include "SignerDefs.h" -#include "TabWithShortcut.h" -#include "UtxoReservationToken.h" - -namespace Ui { - class RFQRequestWidget; -} -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - class WalletsManager; - } - struct TradeSettings; - class UTXOReservationManager; -} - -namespace Blocksettle { - namespace Communication { - namespace ProxyTerminalPb { - class Response; - class Response_FutureResponse; - } - } -} - -class ApplicationSettings; -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class AutoSignScriptProvider; -class CelerClientQt; -class DialogManager; -class HeadlessContainer; -class MarketDataProvider; -class MDCallbacksQt; -class OrderListModel; -class QuoteProvider; -class RFQDialog; -class RfqStorage; - -class RFQRequestWidget : public TabWithShortcut -{ -Q_OBJECT - -public: - RFQRequestWidget(QWidget* parent = nullptr); - ~RFQRequestWidget() override; - - void init(const std::shared_ptr& - , const std::shared_ptr& - , OrderListModel* orderListModel); - - void shortcutActivated(ShortcutType s) override; - - void setAuthorized(bool authorized); - - void onNewSecurity(const std::string& name, bs::network::Asset::Type); - void onMDUpdated(bs::network::Asset::Type, const QString& security - , const bs::network::MDFields &); - void onBalance(const std::string& currency, double balance); - void onWalletBalance(const bs::sync::WalletBalanceData&); - void onHDWallet(const bs::sync::HDWalletData&); - void onWalletData(const std::string& walletId, const bs::sync::WalletData&); - - void onMatchingLogin(const std::string& mtchLogin, BaseCelerClient::CelerUserType - , const std::string& userId); - void onMatchingLogout(); - void onVerifiedAuthAddresses(const std::vector&); - void onAuthKey(const bs::Address&, const BinaryData& authKey); - void onTradeSettings(const std::shared_ptr&); - - void onQuoteReceived(const bs::network::Quote&); - void onQuoteMatched(const std::string &rfqId, const std::string& quoteId); - void onQuoteFailed(const std::string& rfqId, const std::string& quoteId - , const std::string& info); - void onSettlementPending(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId, int timeLeftMS); - void onSettlementComplete(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId); - - void onReservedUTXOs(const std::string& resId, const std::string &subId - , const std::vector&); - -protected: - void hideEvent(QHideEvent* event) override; - bool eventFilter(QObject* sender, QEvent* event) override; - -signals: - void requestPrimaryWalletCreation(); - void loginRequested(); - - void sendUnsignedPayinToPB(const std::string& settlementId, const bs::network::UnsignedPayinData& unsignedPayinData); - void sendSignedPayinToPB(const std::string& settlementId, const BinaryData& signedPayin); - void sendSignedPayoutToPB(const std::string& settlementId, const BinaryData& signedPayout); - - void sendFutureRequestToPB(const bs::network::FutureRequest &details); - - void cancelXBTTrade(const std::string& settlementId); - void cancelCCTrade(const std::string& orderId); - - void unsignedPayinRequested(const std::string& settlementId); - void signedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash, QDateTime timestamp); - void signedPayinRequested(const std::string& settlementId, const BinaryData& unsignedPayin - , const BinaryData &payinHash, QDateTime timestamp); - - void CreateObligationDeliveryTX(const QModelIndex& index); - - void needWalletData(const std::string& walletId); - void needSubmitRFQ(const bs::network::RFQ&, const std::string& reserveId = {}); - void needAcceptRFQ(const std::string& id, const bs::network::Quote&); - void needExpireRFQ(const std::string& id); - void needCancelRFQ(const std::string& id); - void needAuthKey(const bs::Address&); - - void needReserveUTXOs(const std::string& reserveId, const std::string& subId - , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); - -private: - void showEditableRFQPage(); - void showFuturesPage(bs::network::Asset::Type type); - void popShield(); - - bool checkConditions(const MarketSelectedInfo& productGroup); - bool checkWalletSettings(bs::network::Asset::Type productType - , const MarketSelectedInfo& productGroup); - void onRFQSubmit(const std::string &rfqId, const bs::network::RFQ& rfq - , bs::UtxoReservationToken ccUtxoRes); - void onRFQCancel(const std::string &rfqId); - void deleteDialog(const std::string &rfqId); - void processFutureResponse(const Blocksettle::Communication::ProxyTerminalPb::Response_FutureResponse &msg); - -public slots: - void onCurrencySelected(const MarketSelectedInfo& selectedInfo); - void onBidClicked(const MarketSelectedInfo& selectedInfo); - void onAskClicked(const MarketSelectedInfo& selectedInfo); - void onDisableSelectedInfo(); - void onRefreshFocus(); - - void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); //deprecated - void onUserConnected(const bs::network::UserType &); - void onUserDisconnected(); - -private slots: - void onConnectedToCeler(); - void onDisconnectedFromCeler(); - void onRFQAccepted(const std::string &id, const bs::network::Quote&); - void onRFQExpired(const std::string &id); - void onRFQCancelled(const std::string &id); - - void onOrderClicked(const QModelIndex &index); - -public slots: - void forceCheckCondition(); - -private: - std::unique_ptr ui_; - - std::shared_ptr logger_; - std::shared_ptr celerClient_; - std::shared_ptr quoteProvider_; - std::shared_ptr assetManager_; - std::shared_ptr authAddressManager_; - std::shared_ptr dialogManager_; - OrderListModel* orderListModel_; - - std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; - std::shared_ptr armory_; - std::shared_ptr appSettings_; - std::shared_ptr autoSignProvider_; - std::shared_ptr utxoReservationManager_; - - std::shared_ptr rfqStorage_; - - QList marketDataConnection_; - - std::unordered_map balances_; - std::unordered_map dialogs_; - - BaseCelerClient::CelerUserType userType_{ BaseCelerClient::CelerUserType::Undefined }; -}; - -#endif // __RFQ_REQUEST_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/RFQRequestWidget.ui b/BlockSettleUILib/Trading/RFQRequestWidget.ui deleted file mode 100644 index 87007cb90..000000000 --- a/BlockSettleUILib/Trading/RFQRequestWidget.ui +++ /dev/null @@ -1,294 +0,0 @@ - - - - RFQRequestWidget - - - true - - - - 0 - 0 - 616 - 367 - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - TRADE BLOTTER - - - true - - - - - - - - - - QAbstractItemView::NoEditTriggers - - - true - - - true - - - true - - - true - - - 92 - - - - - - - - - - - - - - 0 - 0 - - - - - 270 - 0 - - - - - 250 - 16777215 - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - - - - - - - - - - OrdersView - QTreeView -
OrdersView.h
-
- - RFQShieldPage - QWidget -
RFQShieldPage.h
- 1 -
- - MarketDataWidget - QWidget -
MarketDataWidget.h
- 1 -
- - RFQTicketXBT - QWidget -
RFQTicketXBT.h
- 1 -
- - FuturesTicket - QWidget -
FuturesTicket.h
- 1 -
-
- - -
diff --git a/BlockSettleUILib/Trading/RFQShieldPage.cpp b/BlockSettleUILib/Trading/RFQShieldPage.cpp deleted file mode 100644 index 53ce9e919..000000000 --- a/BlockSettleUILib/Trading/RFQShieldPage.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RFQShieldPage.h" - -namespace { - // Label texts - const QString shieldLoginToSubmitRFQs = QObject::tr("Login to submit RFQs"); - const QString shieldLoginToResponseRFQs = QObject::tr("Login to submit responsive quotes"); - const QString shieldTradingParticipantOnly = QObject::tr("Reserved for Trading Participants"); - const QString shieldDealingParticipantOnly = QObject::tr("Reserved for Dealing Participants"); - const QString shieldTradeUnselectedTargetRequest = QObject::tr("In the Market Data window, please click on the product / security you wish to trade"); - const QString shieldDealingUnselectedTargetRequest = QObject::tr("In the Quote Request Blotter, please click on the product / security you wish to quote"); -} - -RFQShieldPage::RFQShieldPage(QWidget *parent) : - WalletShieldBase(parent) -{ -} - -RFQShieldPage::~RFQShieldPage() noexcept = default; - -void RFQShieldPage::showShieldLoginToSubmitRequired() -{ - showShield(shieldLoginToSubmitRFQs, tr("Login")); - setShieldButtonAction([this]() { - emit loginRequested(); - }, false); -} - -void RFQShieldPage::showShieldLoginToResponseRequired() -{ - showShield(shieldLoginToResponseRFQs); -} - -void RFQShieldPage::showShieldReservedTradingParticipant() -{ - showShield(shieldTradingParticipantOnly); -} - -void RFQShieldPage::showShieldReservedDealingParticipant() -{ - showShield(shieldDealingParticipantOnly); -} - -void RFQShieldPage::showShieldSelectTargetTrade() -{ - showShield(shieldTradeUnselectedTargetRequest); -} - -void RFQShieldPage::showShieldSelectTargetDealing() -{ - showShield(shieldDealingUnselectedTargetRequest); -} - -void RFQShieldPage::setLoginEnabled(bool enabled) -{ - loginEnabled_ = enabled; -} diff --git a/BlockSettleUILib/Trading/RFQShieldPage.h b/BlockSettleUILib/Trading/RFQShieldPage.h deleted file mode 100644 index 6c7278e72..000000000 --- a/BlockSettleUILib/Trading/RFQShieldPage.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef RFQREPLYLOGINREQUIREDSHIELD_H -#define RFQREPLYLOGINREQUIREDSHIELD_H - -#include "WalletShieldBase.h" - -class RFQShieldPage : public WalletShieldBase -{ - Q_OBJECT - -public: - explicit RFQShieldPage(QWidget *parent = nullptr); - ~RFQShieldPage() noexcept override; - - void showShieldLoginToSubmitRequired(); - void showShieldLoginToResponseRequired(); - void showShieldReservedTradingParticipant(); - void showShieldReservedDealingParticipant(); - void showShieldSelectTargetTrade(); - void showShieldSelectTargetDealing(); - - void setLoginEnabled(bool enabled); - -private: - bool loginEnabled_{}; -}; - -#endif // RFQREPLYLOGINREQUIREDSHIELD_H diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.cpp b/BlockSettleUILib/Trading/RFQTicketXBT.cpp deleted file mode 100644 index 0141ba73a..000000000 --- a/BlockSettleUILib/Trading/RFQTicketXBT.cpp +++ /dev/null @@ -1,1813 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RFQTicketXBT.h" -#include "ui_RFQTicketXBT.h" - -#include "AssetManager.h" -#include "AuthAddressManager.h" -#include "BSErrorCodeStrings.h" -#include "BSMessageBox.h" -#include "CCAmountValidator.h" -#include "CoinControlDialog.h" -#include "CoinSelection.h" -#include "CommonTypes.h" -#include "CurrencyPair.h" -#include "EncryptionUtils.h" -#include "FXAmountValidator.h" -#include "HeadlessContainer.h" -#include "QuoteProvider.h" -#include "SelectedTransactionInputs.h" -#include "TradeSettings.h" -#include "TradesUtils.h" -#include "TxClasses.h" -#include "UiUtils.h" -#include "UtxoReservation.h" -#include "UtxoReservationManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "XbtAmountValidator.h" - -#include -#include -#include -#include - -#include - -#include - -namespace { - static const QString kEmptyInformationalLabelText = QString::fromStdString("--"); -} - - -RFQTicketXBT::RFQTicketXBT(QWidget* parent) - : QWidget(parent) - , ui_(new Ui::RFQTicketXBT()) -{ - ui_->setupUi(this); - - initProductGroupMap(); - - invalidBalanceFont_ = ui_->labelBalanceValue->font(); - invalidBalanceFont_.setStrikeOut(true); - - ui_->pushButtonCreateWallet->hide(); - ui_->pushButtonCreateWallet->setEnabled(false); - - ccAmountValidator_ = new CCAmountValidator(this); - fxAmountValidator_ = new FXAmountValidator(this); - xbtAmountValidator_ = new XbtAmountValidator(this); - - ui_->lineEditAmount->installEventFilter(this); - - connect(ui_->pushButtonNumCcy, &QPushButton::clicked, this, &RFQTicketXBT::onNumCcySelected); - connect(ui_->pushButtonDenomCcy, &QPushButton::clicked, this, &RFQTicketXBT::onDenomCcySelected); - - connect(ui_->pushButtonSell, &QPushButton::clicked, this, &RFQTicketXBT::onSellSelected); - connect(ui_->pushButtonBuy, &QPushButton::clicked, this, &RFQTicketXBT::onBuySelected); - - connect(ui_->pushButtonSubmit, &QPushButton::clicked, this, &RFQTicketXBT::submitButtonClicked); - connect(ui_->toolButtonXBTInputsSend, &QPushButton::clicked, this, &RFQTicketXBT::showCoinControl); - connect(ui_->toolButtonMax, &QPushButton::clicked, this, &RFQTicketXBT::onMaxClicked); - connect(ui_->comboBoxXBTWalletsRecv, qOverload(&QComboBox::currentIndexChanged), this, &RFQTicketXBT::walletSelectedRecv); - connect(ui_->comboBoxXBTWalletsSend, qOverload(&QComboBox::currentIndexChanged), this, &RFQTicketXBT::walletSelectedSend); - - connect(ui_->pushButtonCreateWallet, &QPushButton::clicked, this, &RFQTicketXBT::onCreateWalletClicked); - - connect(ui_->lineEditAmount, &QLineEdit::textEdited, this, &RFQTicketXBT::onAmountEdited); - - connect(ui_->authenticationAddressComboBox, qOverload(&QComboBox::currentIndexChanged) - , this, &RFQTicketXBT::onAuthAddrChanged); - - ui_->comboBoxXBTWalletsRecv->setEnabled(false); - ui_->comboBoxXBTWalletsSend->setEnabled(false); - - disablePanel(); -} - -RFQTicketXBT::~RFQTicketXBT() = default; - -void RFQTicketXBT::resetTicket() -{ - ui_->labelProductGroup->setText(kEmptyInformationalLabelText); - ui_->labelSecurityId->setText(kEmptyInformationalLabelText); - ui_->labelIndicativePrice->setText(kEmptyInformationalLabelText); - - currentBidPrice_ = kEmptyInformationalLabelText; - currentOfferPrice_ = kEmptyInformationalLabelText; - currentGroupType_ = ProductGroupType::GroupNotSelected; - - ui_->lineEditAmount->setValidator(nullptr); - ui_->lineEditAmount->setEnabled(false); - ui_->lineEditAmount->clear(); - - rfqMap_.clear(); - - HideRFQControls(); - - updatePanel(); -} - -bs::FixedXbtInputs RFQTicketXBT::fixedXbtInputs() -{ - return std::move(fixedXbtInputs_); -} - -std::shared_ptr RFQTicketXBT::getCCWallet(const std::string &cc) const -{ - if (walletsManager_) { - return walletsManager_->getCCWallet(cc); - } - - return nullptr; -} - -void RFQTicketXBT::updatePanel() -{ - const auto selectedSide = getSelectedSide(); - - if (selectedSide == bs::network::Side::Undefined) { - showHelp(tr("Click on desired product in MD list")); - ui_->pushButtonSubmit->setEnabled(false); - return; - } - - ui_->toolButtonMax->setVisible(selectedSide == bs::network::Side::Sell); - - if (currentGroupType_ != ProductGroupType::FXGroupType) { - const bool buyXBT = getProductToRecv() == UiUtils::XbtCurrency; - ui_->recAddressLayout->setVisible(buyXBT); - ui_->XBTWalletLayoutRecv->setVisible(buyXBT); - ui_->XBTWalletLayoutSend->setVisible(!buyXBT); - } - - updateIndicativePrice(); - updateBalances(); - updateSubmitButton(); -} - -void RFQTicketXBT::onHDLeafCreated(const std::string& ccName) -{ - if (getProduct().toStdString() != ccName) { - return; - } - - auto leaf = walletsManager_->getCCWallet(ccName); - if (leaf == nullptr) { - showHelp(tr("Leaf not created")); - return; - } - - ui_->pushButtonCreateWallet->hide(); - ui_->pushButtonCreateWallet->setText(tr("Create Wallet")); - ui_->pushButtonSubmit->show(); - - clearHelp(); - updatePanel(); -} - -void RFQTicketXBT::onCreateHDWalletError(const std::string& ccName, bs::error::ErrorCode result) -{ - if (getProduct().toStdString() != ccName) { - return; - } - - showHelp(tr("Failed to create wallet: %1").arg(bs::error::ErrorCodeToString(result))); -} - -void RFQTicketXBT::updateBalances() -{ - const auto balance = getBalanceInfo(); - - QString amountString; - switch (balance.productType) { - case ProductGroupType::XBTGroupType: - amountString = UiUtils::displayAmount(balance.amount); - break; - case ProductGroupType::CCGroupType: - amountString = UiUtils::displayCCAmount(balance.amount); - break; - case ProductGroupType::FXGroupType: - amountString = UiUtils::displayCurrencyAmount(balance.amount); - break; - } - - QString text = tr("%1 %2").arg(amountString).arg(balance.product); - ui_->labelBalanceValue->setText(text); -} - -RFQTicketXBT::BalanceInfoContainer RFQTicketXBT::getBalanceInfo() const -{ - BalanceInfoContainer balance; - if (!assetManager_ && balances_.empty()) { - return balance; - } - - QString productToSpend = getProductToSpend(); - - if (UiUtils::XbtCurrency == productToSpend) { - balance.amount = getXbtBalance().GetValueBitcoin(); - balance.product = UiUtils::XbtCurrency; - balance.productType = ProductGroupType::XBTGroupType; - } else { - if (currentGroupType_ == ProductGroupType::CCGroupType) { - balance.amount = utxoReservationManager_->getAvailableCCUtxoSum(getProduct().toStdString()); - balance.product = productToSpend; - balance.productType = ProductGroupType::CCGroupType; - } else { - const double divisor = std::pow(10, UiUtils::GetAmountPrecisionFX()); - try { - const double bal = assetManager_ ? - assetManager_->getBalance(productToSpend.toStdString(), bs::UTXOReservationManager::kIncludeZcRequestor, nullptr) - : balances_.at(productToSpend.toStdString()); - balance.amount = std::floor(bal * divisor) / divisor; - balance.product = productToSpend; - balance.productType = ProductGroupType::FXGroupType; - } - catch (const std::exception &) {} - } - } - return balance; -} - -QString RFQTicketXBT::getProduct() const -{ - return currentProduct_; -} - -void RFQTicketXBT::init(const std::shared_ptr& logger) -{ - logger_ = logger; -} - -void RFQTicketXBT::walletsLoaded() -{ - if (!signingContainer_ || !walletsManager_ || walletsManager_->hdWallets().empty()) { - return; - } - - ui_->comboBoxXBTWalletsRecv->clear(); - ui_->comboBoxXBTWalletsSend->clear(); - - if (signingContainer_->isOffline()) { - ui_->comboBoxXBTWalletsRecv->setEnabled(false); - ui_->comboBoxXBTWalletsSend->setEnabled(false); - } else { - ui_->comboBoxXBTWalletsRecv->setEnabled(true); - ui_->comboBoxXBTWalletsSend->setEnabled(true); - - UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWalletsRecv, walletsManager_, UiUtils::WalletsTypes::All); - // CC does not support to send from hardware wallets - int sendWalletTypes = (currentGroupType_ == ProductGroupType::CCGroupType) ? - UiUtils::WalletsTypes::Full : (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW); - UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWalletsSend, walletsManager_, sendWalletTypes); - - const auto walletId = walletsManager_->getDefaultSpendWalletId(); - - auto selected = UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWalletsSend, walletId, static_cast(sendWalletTypes)); - if (selected == -1) { - auto primaryWallet = walletsManager_->getPrimaryWallet(); - if (primaryWallet != nullptr) { - UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWalletsSend, primaryWallet->walletId(), static_cast(sendWalletTypes)); - } - } - - selected = UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWalletsRecv, walletId, UiUtils::WalletsTypes::All); - if (selected == -1) { - auto primaryWallet = walletsManager_->getPrimaryWallet(); - if (primaryWallet != nullptr) { - UiUtils::selectWalletInCombobox(ui_->comboBoxXBTWalletsRecv, primaryWallet->walletId(), UiUtils::WalletsTypes::All); - } - } - } - - productSelectionChanged(); -} - -void RFQTicketXBT::onSignerReady() -{ - updateSubmitButton(); - ui_->receivingWalletWidgetRecv->setEnabled(!signingContainer_->isOffline()); - ui_->receivingWalletWidgetSend->setEnabled(!signingContainer_->isOffline()); -} - -void RFQTicketXBT::fillRecvAddresses() -{ - if (walletsManager_) { - auto recvWallet = getRecvXbtWallet(); - if (recvWallet) { - if (!recvWallet->canMixLeaves()) { - auto xbtGroup = recvWallet->getGroup(recvWallet->getXBTGroupType()); - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsRecv); - UiUtils::fillRecvAddressesComboBox(ui_->receivingAddressComboBox, { xbtGroup->getLeaf(purpose) }); - } else { - UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, recvWallet, true); - } - } - } - else { - const auto& walletId = ui_->comboBoxXBTWalletsRecv->currentData(UiUtils::WalletIdRole).toString().toStdString(); - emit needWalletData(walletId); - } -} - -bool RFQTicketXBT::preSubmitCheck() -{ - if (currentGroupType_ == ProductGroupType::XBTGroupType) { - if ((!authAddressManager_ && !authAddr_.empty()) - || (authAddressManager_->GetState(authAddr_) == AuthAddressManager::AuthAddressState::Verified)) { - return true; - } - - const auto qty = getQuantity(); - assert(tradeSettings_); - - bool validAmount = false; - if (currentProduct_ == UiUtils::XbtCurrency) { - validAmount = tradeSettings_->xbtTier1Limit > bs::XBTAmount(qty).GetValue(); - } else { - const double indPrice = getIndicativePrice(); - bs::XBTAmount price(indPrice * (1.0 + (tradeSettings_->xbtPriceBand / 100.0))); - validAmount = price > bs::XBTAmount(qty); - } - - if (!validAmount) { - auto amountStr = UiUtils::displayQuantity(bs::XBTAmount((int64_t)tradeSettings_->xbtTier1Limit).GetValueBitcoin() - , bs::network::XbtCurrency); - BSMessageBox(BSMessageBox::info - , tr("Notice"), tr("Authentication Address not verified") - , tr("Trades above %1 are not permitted for non-verified Authentication Addresses. " - "To verify your Authentication Address, execute three trades below the %2 threshold, " - "and BlockSettle will validate the address during its next cycle.").arg(amountStr).arg(amountStr), this).exec(); - return false; - } - } - - return true; -} - -void RFQTicketXBT::showCoinControl() -{ - const auto xbtWallet = getSendXbtWallet(); - if (!xbtWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find XBT wallet"); - return; - } - auto walletId = xbtWallet->walletId(); - ui_->toolButtonXBTInputsSend->setEnabled(false); - - // Need to release current reservation to be able select them back - fixedXbtInputs_.utxoRes.release(); - - std::vector utxos; - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsSend); - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcRequestor); - } - else { - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet->walletId(), bs::UTXOReservationManager::kIncludeZcRequestor); - } - - ui_->toolButtonXBTInputsSend->setEnabled(true); - const bool useAutoSel = fixedXbtInputs_.inputs.empty(); - - auto inputs = std::make_shared(utxos); - // Set this to false is needed otherwise current selection would be cleared - inputs->SetUseAutoSel(useAutoSel); - for (const auto &utxo : fixedXbtInputs_.inputs) { - inputs->SetUTXOSelection(utxo.first.getTxHash(), utxo.first.getTxOutIndex()); - } - - CoinControlDialog dialog(inputs, true, this); - int rc = dialog.exec(); - if (rc != QDialog::Accepted) { - return; - } - - auto selectedInputs = dialog.selectedInputs(); - if (bs::UtxoReservation::instance()->containsReservedUTXO(selectedInputs)) { - BSMessageBox(BSMessageBox::critical, tr("UTXO reservation failed"), - tr("Some of selected UTXOs has been already reserved"), this).exec(); - showCoinControl(); - return; - } - - fixedXbtInputs_.inputs.clear(); - for (const auto &selectedInput : selectedInputs) { - fixedXbtInputs_.inputs.emplace(selectedInput, walletId); - } - - if (!selectedInputs.empty()) { - fixedXbtInputs_.utxoRes = utxoReservationManager_->makeNewReservation(selectedInputs); - } - - updateBalances(); - updateSubmitButton(); -} - -void RFQTicketXBT::walletSelectedRecv(int index) -{ - productSelectionChanged(); -} - -void RFQTicketXBT::walletSelectedSend(int index) -{ - productSelectionChanged(); -} - -void RFQTicketXBT::SetProductGroup(const QString& productGroup) -{ - currentGroupType_ = getProductGroupType(productGroup); - if (currentGroupType_ != ProductGroupType::GroupNotSelected) { - ui_->labelProductGroup->setText(productGroup); - - ui_->lineBeforeProduct->setVisible(true); - ui_->verticalWidgetSelectedProduct->setVisible(true); - - ui_->lineBeforeBalance->setVisible(true); - ui_->balanceLayout->setVisible(true); - - switch (currentGroupType_) { - case ProductGroupType::FXGroupType: - ui_->groupBoxSettlementInputs->setVisible(false); - break; - case ProductGroupType::XBTGroupType: - ui_->authAddressLayout->setVisible(true); - case ProductGroupType::CCGroupType: - ui_->groupBoxSettlementInputs->setVisible(true); - break; - } - } else { - ui_->labelProductGroup->setText(tr("XXX")); - } - - walletsLoaded(); -} - -void RFQTicketXBT::SetCurrencyPair(const QString& currencyPair) -{ - if (currentGroupType_ != ProductGroupType::GroupNotSelected) { - clearHelp(); - - ui_->labelSecurityId->setText(currencyPair); - - CurrencyPair cp(currencyPair.toStdString()); - - currentProduct_ = QString::fromStdString(cp.NumCurrency()); - contraProduct_ = QString::fromStdString(cp.DenomCurrency()); - - ui_->pushButtonNumCcy->setText(currentProduct_); - ui_->pushButtonNumCcy->setChecked(true); - - ui_->pushButtonDenomCcy->setText(contraProduct_); - ui_->pushButtonDenomCcy->setChecked(false); - - ui_->pushButtonDenomCcy->setEnabled(currentGroupType_ != ProductGroupType::CCGroupType); - } -} - -void RFQTicketXBT::SetProductAndSide(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice, bs::network::Side::Type side) -{ - resetTicket(); - - if (productGroup.isEmpty() || currencyPair.isEmpty()) { - return; - } - - SetProductGroup(productGroup); - SetCurrencyPair(currencyPair); - SetCurrentIndicativePrices(bidPrice, offerPrice); - - if (side == bs::network::Side::Type::Undefined) { - side = getLastSideSelection(getProduct().toStdString(), currencyPair.toStdString()); - } - - ui_->pushButtonSell->setChecked(side == bs::network::Side::Sell); - ui_->pushButtonBuy->setChecked(side == bs::network::Side::Buy); - - productSelectionChanged(); -} - -void RFQTicketXBT::setSecurityId(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice) -{ - SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Undefined); -} - -void RFQTicketXBT::setSecurityBuy(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice) -{ - SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Buy); -} - -void RFQTicketXBT::setSecuritySell(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice) -{ - SetProductAndSide(productGroup, currencyPair, bidPrice, offerPrice, bs::network::Side::Sell); -} - -bs::network::Side::Type RFQTicketXBT::getSelectedSide() const -{ - if (currentGroupType_ == ProductGroupType::GroupNotSelected) { - return bs::network::Side::Undefined; - } - - if (ui_->pushButtonSell->isChecked()) { - return bs::network::Side::Sell; - } - - return bs::network::Side::Buy; -} - -void RFQTicketXBT::sendDeferredRFQs() -{ - if (authKey_.empty() || deferredRFQs_.empty()) { - return; - } - decltype(deferredRFQs_) tmpRFQs; - tmpRFQs.swap(deferredRFQs_); - logger_->debug("[RFQTicketXBT::sendDeferredRFQs] {} RFQ[s]", tmpRFQs.size()); - - for (const auto& id : tmpRFQs) { - sendRFQ(id); - } -} - -void RFQTicketXBT::onSettlLeavesLoaded(unsigned int) -{ - if (authKey_.empty()) { - if (authAddr_.empty()) { - logger_->warn("[RFQTicketXBT::onSettlLeavesLoaded] no default auth address"); - return; - } - const auto &cbPubKey = [this](const SecureBinaryData &pubKey) { - authKey_ = pubKey.toHexStr(); - sendDeferredRFQs(); - }; - const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); - if (!settlLeaf) { - logger_->warn("[RFQTicketXBT::onSettlLeavesLoaded] no settlement leaf" - " for auth address {}", authAddr_.display()); - return; - } - settlLeaf->getRootPubkey(cbPubKey); - } - else { - sendDeferredRFQs(); - } -} - -std::string RFQTicketXBT::authKey() const -{ - return authKey_; -} - -void RFQTicketXBT::onAuthAddrChanged(int index) -{ - auto addressString = ui_->authenticationAddressComboBox->itemText(index).toStdString(); - if (addressString.empty()) { - return; - } - - authAddr_ = bs::Address::fromAddressString(addressString); - - if (walletsManager_) { - authKey_.clear(); - const auto settlLeaf = walletsManager_->getSettlementLeaf(authAddr_); - - const auto& cbPubKey = [this](const SecureBinaryData& pubKey) { - authKey_ = pubKey.toHexStr(); - QMetaObject::invokeMethod(this, &RFQTicketXBT::updateSubmitButton); - }; - - if (settlLeaf) { - settlLeaf->getRootPubkey(cbPubKey); - } else { - walletsManager_->createSettlementLeaf(authAddr_, cbPubKey); - } - } - else { - emit needAuthKey(authAddr_); - } -} - -void RFQTicketXBT::onUTXOReservationChanged(const std::string& walletId) -{ - logger_->debug("[RFQTicketXBT::onUTXOReservationChanged] walletId='{}'", walletId); - if (walletId.empty()) { - updateBalances(); - updateSubmitButton(); - return; - } - - auto xbtWallet = getSendXbtWallet(); - if (xbtWallet && (walletId == xbtWallet->walletId() || xbtWallet->getLeaf(walletId))) { - updateBalances(); - } -} - -void RFQTicketXBT::setSubmitRFQ(RFQTicketXBT::SubmitRFQCb submitRFQCb) -{ - submitRFQCb_ = std::move(submitRFQCb); -} - -void RFQTicketXBT::setCancelRFQ(RFQTicketXBT::CancelRFQCb cb) -{ - cancelRFQCb_ = std::move(cb); -} - -bs::Address RFQTicketXBT::recvXbtAddressIfSet() const -{ - const auto index = ui_->receivingAddressComboBox->currentIndex(); - if (index < 0) { - SPDLOG_LOGGER_ERROR(logger_, "invalid address index"); - return bs::Address(); - } - - if (index == 0) { - // Automatic address generation - return bs::Address(); - } - - bs::Address address; - const auto &addressStr = ui_->receivingAddressComboBox->currentText().toStdString(); - try { - address = bs::Address::fromAddressString(addressStr); - } catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "can't parse address '{}': {}", addressStr, e.what()); - return address; - } - - // Sanity checks - auto recvWallet = getRecvXbtWallet(); - if (!recvWallet) { - SPDLOG_LOGGER_ERROR(logger_, "recv XBT wallet is not set"); - return bs::Address(); - } - auto wallet = walletsManager_->getWalletByAddress(address); - if (!wallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find receiving wallet for address {}", address.display()); - return bs::Address(); - } - auto hdWallet = walletsManager_->getHDRootForLeaf(wallet->walletId()); - if (!hdWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find HD wallet for receiving wallet {}", wallet->walletId()); - return bs::Address(); - } - if (hdWallet != recvWallet) { - SPDLOG_LOGGER_ERROR(logger_, "receiving HD wallet {} does not contain waller {}", hdWallet->walletId(), wallet->walletId()); - return bs::Address(); - } - - return address; -} - -bool RFQTicketXBT::checkBalance(double qty) const -{ - const auto balance = getBalanceInfo(); - if (getSelectedSide() == bs::network::Side::Buy) { - if (currentGroupType_ == ProductGroupType::CCGroupType) { - return balance.amount >= getXbtReservationAmountForCc(qty, getOfferPrice()).GetValueBitcoin(); - } - return (balance.amount > 0); - } - else { - return (qty <= balance.amount); - } -} - -bool RFQTicketXBT::checkAuthAddr() const -{ - if (!ui_->authenticationAddressComboBox->isVisible()) { - return true; - } - else if (ui_->authenticationAddressComboBox->count() == 0) { - return false; - } - - if (authAddressManager_) { - if (authAddressManager_->GetState(authAddr_) == AuthAddressManager::AuthAddressState::Verified) { - return true; - } - - const auto& tradeSettings = authAddressManager_->tradeSettings(); - if (!tradeSettings) { - return false; - } - } - else { - //TODO: implement more thorough checking if needed - return (!authAddr_.empty()); - } - - return true; -} - -void RFQTicketXBT::updateSubmitButton() -{ - ui_->pushButtonSubmit->setEnabled(false); - - if (!assetManager_ && signingContainer_) { - return; - } - - if (currentGroupType_ != ProductGroupType::FXGroupType) { - if (signingContainer_) { - if (signingContainer_->isOffline()) { - showHelp(tr("Signer is offline - settlement will not be possible")); - return; - } - else { - clearHelp(); - } - } - - if (getProductToSpend() == UiUtils::XbtCurrency) { - if (walletsManager_ && !getSendXbtWallet()) { - return; - } - else if (!hasSendXbtWallet()) { - return; - } - } - - if (getProductToRecv() == UiUtils::XbtCurrency) { - if (walletsManager_ && !getRecvXbtWallet()) { - return; - } - else if (!hasRecvXbtWallet()) { - return; - } - } - - if (currentGroupType_ == ProductGroupType::CCGroupType) { - if (walletsManager_) { - auto ccWallet = getCCWallet(getProduct().toStdString()); - if (!ccWallet) { - return; - } - } - else { - if (!hasCCWallet()) { - return; - } - } - } - } - - const double qty = getQuantity(); - if (qFuzzyIsNull(qty)) { - return; - } - - const bool isBalanceOk = checkBalance(qty); - const bool isAuthOk = checkAuthAddr(); - - if (!isBalanceOk || !isAuthOk) { - ui_->labelBalanceValue->setFont(invalidBalanceFont_); - return; - } - ui_->labelBalanceValue->setFont(QFont()); - - if ((currentGroupType_ == ProductGroupType::XBTGroupType) && authKey().empty()) { - return; - } - - showHelp({}); - ui_->pushButtonSubmit->setEnabled(true); -} - -std::string RFQTicketXBT::mkRFQkey(const bs::network::RFQ &rfq) -{ - return rfq.security + "_" + rfq.product + "_" + std::to_string(rfq.side); -} - -void RFQTicketXBT::putRFQ(const bs::network::RFQ &rfq) -{ - rfqMap_[mkRFQkey(rfq)] = rfq.quantity; -} - -bool RFQTicketXBT::existsRFQ(const bs::network::RFQ &rfq) -{ - const auto rfqIt = rfqMap_.find(mkRFQkey(rfq)); - if (rfqIt == rfqMap_.end()) { - return false; - } - return qFuzzyCompare(rfq.quantity, rfqIt->second); -} - -bool RFQTicketXBT::eventFilter(QObject *watched, QEvent *evt) -{ - if (evt->type() == QEvent::KeyPress) { - auto keyID = static_cast(evt)->key(); - if (ui_->pushButtonSubmit->isEnabled() && ((keyID == Qt::Key_Return) || (keyID == Qt::Key_Enter))) { - submitButtonClicked(); - } - } - return QWidget::eventFilter(watched, evt); -} - -double RFQTicketXBT::getQuantity() const -{ - const CustomDoubleValidator *validator = dynamic_cast(ui_->lineEditAmount->validator()); - if (validator == nullptr) { - return 0; - } - return validator->GetValue(ui_->lineEditAmount->text()); -} - -double RFQTicketXBT::getOfferPrice() const -{ - const CustomDoubleValidator *validator = dynamic_cast(ui_->lineEditAmount->validator()); - if (validator == nullptr) { - return 0; - } - return validator->GetValue(currentOfferPrice_); -} - -void RFQTicketXBT::submitButtonClicked() -{ - if (!preSubmitCheck()) { - return; - } - - auto rfq = std::make_shared(); - rfq->side = getSelectedSide(); - - rfq->security = ui_->labelSecurityId->text().toStdString(); - rfq->product = getProduct().toStdString(); - - if (rfq->security.empty() || rfq->product.empty()) { - return; - } - - saveLastSideSelection(rfq->product, rfq->security, getSelectedSide()); - - rfq->quantity = getQuantity(); - if (qFuzzyIsNull(rfq->quantity)) { - return; - } - - if (currentGroupType_ == ProductGroupType::XBTGroupType) { - if (utxoReservationManager_) { - auto minXbtAmount = bs::tradeutils::minXbtAmount(utxoReservationManager_->feeRatePb()); - if (expectedXbtAmountMin().GetValue() < minXbtAmount.GetValue()) { - auto minAmountStr = UiUtils::displayQuantity(minXbtAmount.GetValueBitcoin(), bs::network::XbtCurrency); - BSMessageBox(BSMessageBox::critical, tr("Spot XBT"), tr("Invalid amount") - , tr("Expected bitcoin amount will not cover network fee.\nMinimum amount: %1").arg(minAmountStr), this).exec(); - return; - } - } - } - - switch (currentGroupType_) { - case ProductGroupType::GroupNotSelected: - rfq->assetType = bs::network::Asset::Undefined; - break; - case ProductGroupType::FXGroupType: - rfq->assetType = bs::network::Asset::SpotFX; - break; - case ProductGroupType::XBTGroupType: - rfq->assetType = bs::network::Asset::SpotXBT; - break; - case ProductGroupType::CCGroupType: - rfq->assetType = bs::network::Asset::PrivateMarket; - break; - } - - const auto &rfqId = CryptoPRNG::generateRandom(8).toHexStr(); - pendingRFQs_[rfqId] = rfq; - - if (!existsRFQ(*rfq)) { - putRFQ(*rfq); - sendRFQ(rfqId); - } -} - -void RFQTicketXBT::onSendRFQ(const std::string &id, const QString &symbol, double amount, bool buy) -{ - auto rfq = std::make_shared(); - rfq->side = buy ? bs::network::Side::Buy : bs::network::Side::Sell; - - const CurrencyPair cp(symbol.toStdString()); - rfq->security = symbol.toStdString(); - rfq->product = cp.NumCurrency(); - rfq->quantity = amount; - if (assetManager_) { - rfq->assetType = assetManager_->GetAssetTypeForSecurity(rfq->security); - } - else { - try { - rfq->assetType = assetTypes_.at(rfq->security); - } - catch (const std::exception&) { - logger_->error("[{}] no asset type found for {}", __func__, rfq->security); - return; - } - } - - if (rfq->security.empty() || rfq->product.empty() || qFuzzyIsNull(rfq->quantity)) { - return; - } - - pendingRFQs_[id] = rfq; - - if (rfq->assetType == bs::network::Asset::SpotXBT) { - if (authAddressManager_) { - authAddr_ = authAddressManager_->getDefault(); - } - if (authAddr_.empty()) { - deferredRFQs_.push_back(id); - return; - } - if (walletsManager_) { - if (!walletsManager_->getSettlementLeaf(authAddr_)) { - deferredRFQs_.push_back(id); - return; - } - } - } - - sendRFQ(id); -} - -void RFQTicketXBT::sendRFQ(const std::string &id) -{ - const auto &itRFQ = pendingRFQs_.find(id); - if (itRFQ == pendingRFQs_.end()) { - logger_->error("[RFQTicketXBT::onSendRFQ] RFQ with id {} not found", id); - return; - } - logger_->debug("[RFQTicketXBT::sendRFQ] sending RFQ {}", id); - - auto rfq = itRFQ->second; - - if (rfq->requestId.empty()) { - rfq->requestId = id; - } - - if (rfq->assetType == bs::network::Asset::SpotXBT) { - rfq->requestorAuthPublicKey = authKey(); - if (rfq->requestorAuthPublicKey.empty()) { - logger_->debug("[RFQTicketXBT::onSendRFQ] auth key is empty for {}", authAddr_.display()); - deferredRFQs_.push_back(id); - } - else { - reserveBestUtxoSetAndSubmit(id, rfq); - } - return; - } - else if (rfq->assetType == bs::network::Asset::PrivateMarket) { - auto ccWallet = getCCWallet(rfq->product); - if (!ccWallet) { - SPDLOG_LOGGER_ERROR(logger_, "can't find CC wallet for {}", rfq->product); - return; - } - - if (rfq->side == bs::network::Side::Sell) { - // Sell - const auto &recvXbtAddressCb = [this, id, rfq, ccWallet] - (const bs::Address &recvXbtAddr) - { - rfq->receiptAddress = recvXbtAddr.display(); - const uint64_t spendVal = rfq->quantity * assetManager_->getCCLotSize(rfq->product); - if (!ccWallet) { - SPDLOG_LOGGER_ERROR(logger_, "ccWallet is not set"); - return; - } - - const auto ccInputsCb = [this, id, spendVal, rfq, ccWallet] - (const std::vector &ccInputs) mutable - { - QMetaObject::invokeMethod(this, [this, id, spendVal, rfq, ccInputs, ccWallet] { - uint64_t inputVal = 0; - for (const auto &input : ccInputs) { - inputVal += input.getValue(); - } - if (inputVal < spendVal) { - // This should not normally happen! - SPDLOG_LOGGER_ERROR(logger_, "insufficient input amount: {}, expected: {}, requestId: {}", inputVal, spendVal, rfq->requestId); - BSMessageBox(BSMessageBox::critical, tr("RFQ not sent") - , tr("Insufficient input amount")).exec(); - return; - } - - const auto cbAddr = [this, spendVal, id, rfq, ccInputs, ccWallet](const bs::Address &addr) - { - try { - const auto txReq = ccWallet->createPartialTXRequest( - spendVal, ccInputs - , { addr, RECIP_GROUP_CHANG_1 } //change to group 1 (cc group) - , 0, {}, {} - /* - This cc is created without recipients. Set the assumed recipient count - to 1 so the coin selection algo can run, otherwise all presented inputs - will be selected, which is wasteful. - - The assumed recipient count isn't relevant to the fee calculation on - the cc side of the tx since only the xbt side covers network fees. - */ - , 1); - - auto resolveCB = [this, id, rfq] - (bs::error::ErrorCode result, const Codec_SignerState::SignerState &state) - { - if (result != bs::error::ErrorCode::NoError) { - std::stringstream ss; - ss << "failed to resolve CC half signer with error code: " << (int)result; - throw std::runtime_error(ss.str()); - } - - bs::core::wallet::TXSignRequest req; - req.armorySigner_.deserializeState(state); - auto reservationToken = utxoReservationManager_->makeNewReservation( - req.getInputs(nullptr), rfq->requestId); - - rfq->coinTxInput = BinaryData::fromString(state.SerializeAsString()).toHexStr(); - submitRFQCb_(id, *rfq, std::move(reservationToken)); - }; - signingContainer_->resolvePublicSpenders(txReq, resolveCB); - } - catch (const std::exception &e) { - BSMessageBox(BSMessageBox::critical, tr("RFQ Failure") - , QString::fromLatin1(e.what()), this).exec(); - return; - } - }; - if (inputVal == spendVal) { - cbAddr({}); - } - else { - ccWallet->getNewChangeAddress(cbAddr); - } - }); - }; - bool result = ccWallet->getSpendableTxOutList(ccInputsCb, spendVal, true); - if (!result) { - SPDLOG_LOGGER_ERROR(logger_, "can't spendable TX list"); - } - }; - - auto recvXbtAddrIfSet = recvXbtAddressIfSet(); - if (recvXbtAddrIfSet.isValid()) { - recvXbtAddressCb(recvXbtAddrIfSet); - } - else { - auto recvXbtWallet = getRecvXbtWallet(); - if (!recvXbtWallet) { - SPDLOG_LOGGER_ERROR(logger_, "recv XBT wallet is not set"); - return; - } - auto leaves = recvXbtWallet->getGroup(recvXbtWallet->getXBTGroupType())->getLeaves(); - if (leaves.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "can't find XBT leaves"); - return; - } - // BST-2474: All addresses related to trading, not just change addresses, should use internal addresses - leaves.front()->getNewIntAddress(recvXbtAddressCb); - } - return; - } - - // Buy - auto cbRecvAddr = [this, id, rfq](const bs::Address &recvAddr) { - rfq->receiptAddress = recvAddr.display(); - reserveBestUtxoSetAndSubmit(id, rfq); - }; - // BST-2474: All addresses related to trading, not just change addresses, should use internal addresses. - // This has no effect as CC wallets have only external addresses. - // But CCLeaf::getSpendableTxOutList is require only 1 conf so it's OK. - ccWallet->getNewIntAddress(cbRecvAddr); - return; - } - - submitRFQCb_(id, *rfq, bs::UtxoReservationToken{}); - pendingRFQs_.erase(itRFQ); -} - -void RFQTicketXBT::onCancelRFQ(const std::string &id) -{ - const auto &itRFQ = pendingRFQs_.find(id); - if (itRFQ == pendingRFQs_.end()) { - logger_->error("[RFQTicketXBT::onCancelRFQ] failed to find RFQ {}", id); - return; - } - logger_->debug("[RFQTicketXBT::onCancelRFQ] cancelling RFQ {}", id); - if (cancelRFQCb_) { - cancelRFQCb_(id); - } - pendingRFQs_.erase(itRFQ); -} - -void RFQTicketXBT::onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields mdFields) -{ - auto &mdInfo = mdInfo_[security.toStdString()]; - mdInfo.merge(bs::network::MDField::get(mdFields)); -} - -QPushButton* RFQTicketXBT::submitButton() const -{ - return ui_->pushButtonSubmit; -} - -QLineEdit* RFQTicketXBT::lineEditAmount() const -{ - return ui_->lineEditAmount; -} - -QPushButton* RFQTicketXBT::buyButton() const -{ - return ui_->pushButtonBuy; -} - -QPushButton* RFQTicketXBT::sellButton() const -{ - return ui_->pushButtonSell; -} - -QPushButton* RFQTicketXBT::numCcyButton() const -{ - return ui_->pushButtonNumCcy; -} - -QPushButton* RFQTicketXBT::denomCcyButton() const -{ - return ui_->pushButtonDenomCcy; -} - -bs::Address RFQTicketXBT::selectedAuthAddress() const -{ - return authAddr_; -} - -void RFQTicketXBT::saveLastSideSelection(const std::string& product, const std::string& security, bs::network::Side::Type sideIndex) -{ - lastSideSelection_[product + security] = sideIndex; -} - -bs::network::Side::Type RFQTicketXBT::getLastSideSelection(const std::string& product, const std::string& security) -{ - auto it = lastSideSelection_.find(product + security); - if (it == lastSideSelection_.end()) { - return bs::network::Side::Sell; - } - - return it->second; -} - -void RFQTicketXBT::disablePanel() -{ - resetTicket(); - - // show help - showHelp(tr("Login in order to send RFQ")); -} - -std::shared_ptr RFQTicketXBT::xbtWallet() const -{ - if (getProductToSpend() == UiUtils::XbtCurrency) { - return getSendXbtWallet(); - } - if (getProductToRecv() == UiUtils::XbtCurrency) { - return getRecvXbtWallet(); - } - return nullptr; -} - -UiUtils::WalletsTypes RFQTicketXBT::xbtWalletType() const -{ - QComboBox* combobox = nullptr; - if (getProductToSpend() == UiUtils::XbtCurrency) { - combobox = ui_->comboBoxXBTWalletsSend; - } - if (getProductToRecv() == UiUtils::XbtCurrency) { - combobox = ui_->comboBoxXBTWalletsRecv; - } - - if (!combobox) { - return UiUtils::None; - } - - return UiUtils::getSelectedWalletType(combobox); -} - -void RFQTicketXBT::onParentAboutToHide() -{ - fixedXbtInputs_ = {}; -} - -void RFQTicketXBT::onVerifiedAuthAddresses(const std::vector& addrs) -{ - if (addrs.empty()) { - return; - } - UiUtils::fillAuthAddressesComboBoxWithSubmitted(ui_->authenticationAddressComboBox, addrs); - onAuthAddrChanged(ui_->authenticationAddressComboBox->currentIndex()); -} - -void RFQTicketXBT::onBalance(const std::string& currency, double balance) -{ - balances_[currency] = balance; - updateBalances(); -} - -void RFQTicketXBT::onWalletBalance(const bs::sync::WalletBalanceData& wbd) -{ - balances_[wbd.id] = wbd.balSpendable; - updateBalances(); -} - -void RFQTicketXBT::onHDWallet(const bs::sync::HDWalletData& wallet) -{ - const auto &it = std::find_if(wallets_.cbegin(), wallets_.cend() - , [wallet](const bs::sync::HDWalletData &w) { return (wallet.id == w.id); }); - if (it == wallets_.end()) { - wallets_.push_back(wallet); - } - else { - wallets_.emplace(it, wallet); - } - UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWalletsRecv, wallets_, UiUtils::WalletsTypes::All); - // CC does not support to send from hardware wallets - int sendWalletTypes = (currentGroupType_ == ProductGroupType::CCGroupType) ? - UiUtils::WalletsTypes::Full : (UiUtils::WalletsTypes::Full | UiUtils::WalletsTypes::HardwareSW); - UiUtils::fillHDWalletsComboBox(ui_->comboBoxXBTWalletsSend, wallets_, sendWalletTypes); -} - -void RFQTicketXBT::onWalletData(const std::string& walletId, const bs::sync::WalletData& wd) -{ - if (ui_->comboBoxXBTWalletsRecv->currentData(UiUtils::WalletIdRole).toString().toStdString() == walletId) { - UiUtils::fillRecvAddressesComboBoxHDWallet(ui_->receivingAddressComboBox, { wd }); - } -} - -void RFQTicketXBT::onAuthKey(const bs::Address& addr, const BinaryData& authKey) -{ - if (addr == authAddr_) { - const auto& authKeyHex = authKey.toHexStr(); - if (authKey_ != authKeyHex) { - logger_->debug("[{}] got auth key: {}", __func__, authKeyHex); - authKey_ = authKeyHex; - updateSubmitButton(); - } - sendDeferredRFQs(); - } -} - -void RFQTicketXBT::onTradeSettings(const std::shared_ptr& ts) -{ - tradeSettings_ = ts; -} - -void RFQTicketXBT::onReservedUTXOs(const std::string& resId - , const std::string& subId, const std::vector& utxos) -{ - const auto& it = pendingRFQs_.find(resId); - if (it == pendingRFQs_.end()) { - return; - } - logger_->debug("[{}] sending RFQ {} after {} UTXOs reservation", __func__ - , resId, utxos.size()); - if (utxos.empty()) { - logger_->error("[{}] failed to reserve {}/{}", resId, subId); - } - else { - submitRFQCb_(resId, *it->second, {}); - } - pendingRFQs_.erase(it); -} - -void RFQTicketXBT::enablePanel() -{ - resetTicket(); - clearHelp(); -} - -void RFQTicketXBT::HideRFQControls() -{ - ui_->groupBoxSettlementInputs->setVisible(false); - - // amount and balance controls - ui_->lineBeforeProduct->setVisible(false); - ui_->verticalWidgetSelectedProduct->setVisible(false); - - ui_->lineBeforeBalance->setVisible(false); - ui_->balanceLayout->setVisible(false); -} - -void RFQTicketXBT::showHelp(const QString& helpText) -{ - ui_->helpLabel->setText(helpText); - ui_->helpLabel->setVisible(true); -} - -void RFQTicketXBT::clearHelp() -{ - ui_->helpLabel->setVisible(false); -} - -void RFQTicketXBT::initProductGroupMap() -{ - groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::PrivateMarket) - , ProductGroupType::CCGroupType); - groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::SpotXBT) - , ProductGroupType::XBTGroupType); - groupNameToType_.emplace(bs::network::Asset::toString(bs::network::Asset::SpotFX) - , ProductGroupType::FXGroupType); -} - -RFQTicketXBT::ProductGroupType RFQTicketXBT::getProductGroupType(const QString& productGroup) -{ - auto it = groupNameToType_.find(productGroup.toStdString()); - if (it != groupNameToType_.end()) { - return it->second; - } - - return ProductGroupType::GroupNotSelected; -} - -void RFQTicketXBT::onMaxClicked() -{ - auto balanceInfo = getBalanceInfo(); - - switch(balanceInfo.productType) { - case ProductGroupType::XBTGroupType: - { - const auto xbtWallet = getSendXbtWallet(); - if (!xbtWallet) { - ui_->lineEditAmount->clear(); - updateSubmitButton(); - return; - } - - std::vector utxos; - if (!fixedXbtInputs_.inputs.empty()) { - utxos.reserve(fixedXbtInputs_.inputs.size()); - for (const auto &utxoPair : fixedXbtInputs_.inputs) { - utxos.push_back(utxoPair.first); - } - } - else { - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsSend); - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcRequestor); - } - else { - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet->walletId(), bs::UTXOReservationManager::kIncludeZcRequestor); - } - } - - auto feeCb = [this, utxos = std::move(utxos)](float fee) { - QMetaObject::invokeMethod(this, [this, fee, utxos = std::move(utxos)]{ - float feePerByteArmory = ArmoryConnection::toFeePerByte(fee); - auto feePerByte = std::max(feePerByteArmory, utxoReservationManager_->feeRatePb()); - uint64_t total = 0; - for (const auto &utxo : utxos) { - total += utxo.getValue(); - } - const uint64_t fee = bs::tradeutils::estimatePayinFeeWithoutChange(utxos, feePerByte); - const double spendableQuantity = std::max(0.0, (total - fee) / BTCNumericTypes::BalanceDivider); - ui_->lineEditAmount->setText(UiUtils::displayAmount(spendableQuantity)); - updateSubmitButton(); - }); - }; - armory_->estimateFee(bs::tradeutils::feeTargetBlockCount(), feeCb); - - return; - } - case ProductGroupType::CCGroupType: { - ui_->lineEditAmount->setText(UiUtils::displayCCAmount(qMax(balanceInfo.amount, 0))); - break; - } - case ProductGroupType::FXGroupType: { - ui_->lineEditAmount->setText(UiUtils::displayCurrencyAmount(qMax(balanceInfo.amount, 0))); - break; - } - } - - updateSubmitButton(); -} - -void RFQTicketXBT::onAmountEdited(const QString &) -{ - updateSubmitButton(); -} - -void RFQTicketXBT::SetCurrentIndicativePrices(const QString& bidPrice, const QString& offerPrice) -{ - if (bidPrice.isEmpty()) { - currentBidPrice_ = kEmptyInformationalLabelText; - } else { - currentBidPrice_ = bidPrice; - } - - if (offerPrice.isEmpty()) { - currentOfferPrice_ = kEmptyInformationalLabelText; - } else { - currentOfferPrice_ = offerPrice; - } -} - -void RFQTicketXBT::updateIndicativePrice() -{ - auto selectedSide = getSelectedSide(); - if (selectedSide != bs::network::Side::Undefined) { - int numCcySelected = ui_->pushButtonNumCcy->isChecked(); - bool isSell = numCcySelected ^ (selectedSide == bs::network::Side::Buy); - - if (isSell) { - ui_->labelIndicativePrice->setText(currentBidPrice_); - } else { - ui_->labelIndicativePrice->setText(currentOfferPrice_); - } - } else { - ui_->labelIndicativePrice->setText(kEmptyInformationalLabelText); - } -} - -double RFQTicketXBT::getIndicativePrice() const -{ - const auto &mdIt = mdInfo_.find(ui_->labelSecurityId->text().toStdString()); - if (mdIt == mdInfo_.end()) { - return false; - } - - auto selectedSide = getSelectedSide(); - if (selectedSide == bs::network::Side::Undefined) { - return .0; - } - bool numCcySelected = ui_->pushButtonNumCcy->isChecked(); - bool isSell = selectedSide == bs::network::Side::Buy - ? !numCcySelected - : numCcySelected; - - if (isSell) { - return mdIt->second.bidPrice; - } - else { - return mdIt->second.askPrice; - } -} - -void RFQTicketXBT::onNumCcySelected() -{ - ui_->pushButtonNumCcy->setChecked(true); - ui_->pushButtonDenomCcy->setChecked(false); - - currentProduct_ = ui_->pushButtonNumCcy->text(); - contraProduct_ = ui_->pushButtonDenomCcy->text(); - - productSelectionChanged(); -} - -void RFQTicketXBT::onDenomCcySelected() -{ - ui_->pushButtonNumCcy->setChecked(false); - ui_->pushButtonDenomCcy->setChecked(true); - - currentProduct_ = ui_->pushButtonDenomCcy->text(); - contraProduct_ = ui_->pushButtonNumCcy->text(); - - productSelectionChanged(); -} - -void RFQTicketXBT::onSellSelected() -{ - ui_->pushButtonSell->setChecked(true); - ui_->pushButtonBuy->setChecked(false); - productSelectionChanged(); -} - -void RFQTicketXBT::onBuySelected() -{ - ui_->pushButtonSell->setChecked(false); - ui_->pushButtonBuy->setChecked(true); - productSelectionChanged(); -} - -void RFQTicketXBT::productSelectionChanged() -{ - rfqMap_.clear(); - - ui_->pushButtonSubmit->show(); - ui_->pushButtonCreateWallet->hide(); - - ui_->lineEditAmount->setValidator(nullptr); - ui_->lineEditAmount->setEnabled(false); - ui_->lineEditAmount->clear(); - - ui_->toolButtonMax->setEnabled(true); - ui_->toolButtonXBTInputsSend->setEnabled(true); - - fixedXbtInputs_ = {}; - - if (currentGroupType_ == ProductGroupType::FXGroupType) { - ui_->lineEditAmount->setValidator(fxAmountValidator_); - ui_->lineEditAmount->setEnabled(true); - } else { - bool canTradeXBT = false; - if (armory_ && signingContainer_) { - canTradeXBT = (armory_->state() == ArmoryState::Ready) - && !signingContainer_->isOffline(); - } - else { - canTradeXBT = true; - } - - ui_->lineEditAmount->setEnabled(canTradeXBT); - ui_->toolButtonMax->setEnabled(canTradeXBT); - ui_->toolButtonXBTInputsSend->setEnabled(canTradeXBT); - - if (!canTradeXBT) { - ui_->labelBalanceValue->setText(tr("---")); - return; - } - - if (currentGroupType_ == ProductGroupType::CCGroupType) { - ui_->lineEditAmount->setValidator(ccAmountValidator_); - - const auto &product = getProduct(); - const auto ccWallet = getCCWallet(product.toStdString()); - if (!ccWallet) { - if (signingContainer_ && !signingContainer_->isOffline() && walletsManager_) { - ui_->pushButtonSubmit->hide(); - ui_->pushButtonCreateWallet->show(); - ui_->pushButtonCreateWallet->setEnabled(true); - ui_->pushButtonCreateWallet->setText(tr("Create %1 wallet").arg(product)); - } else { - BSMessageBox errorMessage(BSMessageBox::critical, tr("Signer not connected") - , tr("Could not create CC subwallet.") - , this); - errorMessage.exec(); - showHelp(tr("CC wallet missing")); - } - } - } else { - if (currentProduct_ == UiUtils::XbtCurrency) { - ui_->lineEditAmount->setValidator(xbtAmountValidator_); - } else { - ui_->lineEditAmount->setValidator(fxAmountValidator_); - } - } - } - - ui_->lineEditAmount->setFocus(); - - updatePanel(); - - fillRecvAddresses(); -} - -std::shared_ptr RFQTicketXBT::getSendXbtWallet() const -{ - if (!walletsManager_) { - return nullptr; - } - auto wallet = walletsManager_->getHDWalletById(ui_->comboBoxXBTWalletsSend-> - currentData(UiUtils::WalletIdRole).toString().toStdString()); - if (!wallet) { - const auto &defaultWallet = walletsManager_->getDefaultWallet(); - if (defaultWallet) { - wallet = walletsManager_->getHDRootForLeaf(defaultWallet->walletId()); - } - } - return wallet; -} - -std::shared_ptr RFQTicketXBT::getRecvXbtWallet() const -{ - if (!walletsManager_) { - return nullptr; - } - auto wallet = walletsManager_->getHDWalletById(ui_->comboBoxXBTWalletsRecv-> - currentData(UiUtils::WalletIdRole).toString().toStdString()); - if (!wallet && walletsManager_->getDefaultWallet()) { - wallet = walletsManager_->getHDRootForLeaf(walletsManager_->getDefaultWallet()->walletId()); - } - return wallet; -} - -std::string RFQTicketXBT::getXbtLeafId(const std::string& hdWalletId) const -{ - const auto& it = std::find_if(wallets_.cbegin(), wallets_.cend() - , [hdWalletId](const bs::sync::HDWalletData& wd) { return (wd.id == hdWalletId); }); - if (it == wallets_.end()) { - for (const auto& wallet : wallets_) { - for (const auto& group : wallet.groups) { - if ((group.type != bs::hd::CoinType::Bitcoin_main) && (group.type != bs::hd::Bitcoin_test)) { - continue; - } - for (const auto& leaf : group.leaves) { - if (std::find_if(leaf.ids.cbegin(), leaf.ids.cend() - , [hdWalletId](const std::string& id) { return (id == hdWalletId); }) - != leaf.ids.end()) { - return hdWalletId; - } - } - } - } - return {}; - } - for (const auto& group : it->groups) { - if ((group.type != bs::hd::CoinType::Bitcoin_main) && (group.type != bs::hd::Bitcoin_test)) { - continue; - } - if (!group.leaves.empty()) { - const auto& leaf = *group.leaves.cbegin(); - return *leaf.ids.cbegin(); - } - } - return {}; -} - -bool RFQTicketXBT::hasSendXbtWallet() const -{ - return !wallets_.empty(); //TODO: implement more thorough checking -} - -bool RFQTicketXBT::hasRecvXbtWallet() const -{ - return !wallets_.empty(); //TODO: implement more thorough checking -} - -bs::XBTAmount RFQTicketXBT::getXbtBalance() const -{ - const auto &fixedInputs = fixedXbtInputs_.inputs; - if (!fixedXbtInputs_.inputs.empty()) { - int64_t sum = 0; - for (const auto &utxo : fixedInputs) { - sum += utxo.first.getValue(); - } - return bs::XBTAmount(sum); - } - - if (walletsManager_) { - auto xbtWallet = getSendXbtWallet(); - if (!xbtWallet) { - return bs::XBTAmount(0.0); - } - - if (!xbtWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(ui_->comboBoxXBTWalletsSend); - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId(), purpose, bs::UTXOReservationManager::kIncludeZcRequestor)); - } else { - return bs::XBTAmount(utxoReservationManager_->getAvailableXbtUtxoSum( - xbtWallet->walletId(), bs::UTXOReservationManager::kIncludeZcRequestor)); - } - } - else { - const auto &walletId = ui_->comboBoxXBTWalletsSend->currentData(UiUtils::WalletIdRole).toString().toStdString(); - const auto& xbtLeafId = getXbtLeafId(walletId); - if (xbtLeafId.empty()) { - return bs::XBTAmount(0.0); //TODO: use default XBT leaf - } - try { - return bs::XBTAmount(balances_.at(xbtLeafId)); - } - catch (const std::exception&) { - logger_->error("[{}] balance for leaf {} not found", __func__, xbtLeafId); - return bs::XBTAmount(0.0); - } - } -} - -QString RFQTicketXBT::getProductToSpend() const -{ - if (getSelectedSide() == bs::network::Side::Sell) { - return currentProduct_; - } else { - return contraProduct_; - } -} - -QString RFQTicketXBT::getProductToRecv() const -{ - if (getSelectedSide() == bs::network::Side::Buy) { - return currentProduct_; - } else { - return contraProduct_; - } -} - -bs::XBTAmount RFQTicketXBT::expectedXbtAmountMin() const -{ - if (currentProduct_ == UiUtils::XbtCurrency) { - return bs::XBTAmount(getQuantity()); - } - const auto &tradeSettings = authAddressManager_->tradeSettings(); - auto maxPrice = getIndicativePrice() * (1 + (tradeSettings->xbtPriceBand / 100)); - return bs::XBTAmount(getQuantity() / maxPrice); -} - -bs::XBTAmount RFQTicketXBT::getXbtReservationAmountForCc(double quantity, double offerPrice) const -{ - return bs::XBTAmount(quantity * offerPrice * bs::tradeutils::reservationQuantityMultiplier()); -} - -void RFQTicketXBT::reserveBestUtxoSetAndSubmit(const std::string &id - , const std::shared_ptr& rfq) -{ - if (walletsManager_) { - // Skip UTXO reservations amount checks for buy fiat requests as reserved XBT amount is 20% more than expected from current price. - auto checkAmount = (rfq->side == bs::network::Side::Sell && rfq->product != bs::network::XbtCurrency) ? - bs::UTXOReservationManager::CheckAmount::Enabled : bs::UTXOReservationManager::CheckAmount::Disabled; - - const auto &submitRFQWrapper = [rfqTicket = QPointer(this), id, rfq] - { - if (!rfqTicket) { - return; - } - rfqTicket->submitRFQCb_(id, *rfq, std::move(rfqTicket->fixedXbtInputs_.utxoRes)); - }; - auto getWalletAndReserve = [rfqTicket = QPointer(this), submitRFQWrapper, checkAmount] - (BTCNumericTypes::satoshi_type amount, bool partial) - { - auto cbBestUtxoSet = [rfqTicket, submitRFQWrapper](bs::FixedXbtInputs&& fixedXbt) { - if (!rfqTicket) { - return; - } - rfqTicket->fixedXbtInputs_ = std::move(fixedXbt); - submitRFQWrapper(); - }; - - auto hdWallet = rfqTicket->getSendXbtWallet(); - if (!hdWallet->canMixLeaves()) { - auto purpose = UiUtils::getSelectedHwPurpose(rfqTicket->ui_->comboBoxXBTWalletsSend); - rfqTicket->utxoReservationManager_->reserveBestXbtUtxoSet( - hdWallet->walletId(), purpose, amount, - partial, std::move(cbBestUtxoSet), true, checkAmount); - } else { - rfqTicket->utxoReservationManager_->reserveBestXbtUtxoSet( - hdWallet->walletId(), amount, - partial, std::move(cbBestUtxoSet), true, checkAmount, bs::UTXOReservationManager::kIncludeZcRequestor); - } - }; - - if (!fixedXbtInputs_.inputs.empty()) { - submitRFQWrapper(); - return; // already reserved by user - } - - auto quantity = bs::XBTAmount(rfq->quantity).GetValue(); - if (rfq->side == bs::network::Side::Buy) { - if (rfq->assetType == bs::network::Asset::PrivateMarket) { - quantity *= bs::XBTAmount(getOfferPrice()).GetValue(); - } else if (rfq->assetType == bs::network::Asset::SpotXBT) { - quantity /= getOfferPrice(); - } - } - - const bool partial = rfq->assetType == bs::network::Asset::PrivateMarket; - getWalletAndReserve(quantity, partial); - } - else { - const auto& walletId = ui_->comboBoxXBTWalletsSend->currentData(UiUtils::WalletIdRole).toString().toStdString(); - if (rfq->assetType == bs::network::Asset::PrivateMarket - && rfq->side == bs::network::Side::Buy) { - auto maxXbtQuantity = getXbtReservationAmountForCc(rfq->quantity, getOfferPrice()).GetValue(); - emit needReserveUTXOs(id, walletId, maxXbtQuantity, true); - } - else { - if ((rfq->side == bs::network::Side::Sell && rfq->product != bs::network::XbtCurrency) || - (rfq->side == bs::network::Side::Buy && rfq->product == bs::network::XbtCurrency)) { - submitRFQCb_(id, *rfq, {}); // already reserved - pendingRFQs_.erase(id); - } - else if (!fixedXbtInputs_.inputs.empty()) { - submitRFQCb_(id, *rfq, {}); // already reserved - pendingRFQs_.erase(id); - } - - auto quantity = bs::XBTAmount(rfq->quantity).GetValue(); - if (rfq->side == bs::network::Side::Buy) { - if (rfq->assetType == bs::network::Asset::PrivateMarket) { - quantity *= bs::XBTAmount(getOfferPrice()).GetValue(); - } else if (rfq->assetType == bs::network::Asset::SpotXBT) { - quantity /= getOfferPrice(); - } - } - const bool partial = rfq->assetType == bs::network::Asset::PrivateMarket; - emit needReserveUTXOs(id, walletId, quantity, partial); - } - } -} - -void RFQTicketXBT::onCreateWalletClicked() -{ - ui_->pushButtonCreateWallet->setEnabled(false); - - if (!walletsManager_->CreateCCLeaf(getProduct().toStdString())) { - showHelp(tr("Create CC wallet request failed")); - } -} diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.h b/BlockSettleUILib/Trading/RFQTicketXBT.h deleted file mode 100644 index 803a27f9f..000000000 --- a/BlockSettleUILib/Trading/RFQTicketXBT.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __RFQ_TICKET_XBT_H__ -#define __RFQ_TICKET_XBT_H__ - -#include -#include - -#include -#include -#include -#include - -#include "BSErrorCode.h" -#include "CommonTypes.h" -#include "UtxoReservationToken.h" -#include "XBTAmount.h" -#include "UiUtils.h" - -QT_BEGIN_NAMESPACE -class QPushButton; -class QLineEdit; -QT_END_NAMESPACE - -namespace spdlog { - class logger; -} -namespace Ui { - class RFQTicketXBT; -} -namespace bs { - namespace sync { - namespace hd { - class Leaf; - class Wallet; - } - class Wallet; - class WalletsManager; - } - struct TradeSettings; - class UTXOReservationManager; -} -class ArmoryConnection; -class AssetManager; -class AuthAddressManager; -class CCAmountValidator; -class FXAmountValidator; -class HeadlessContainer; -class QuoteProvider; -class SelectedTransactionInputs; -class XbtAmountValidator; - - -class RFQTicketXBT : public QWidget -{ -Q_OBJECT - -public: - RFQTicketXBT(QWidget* parent = nullptr); - ~RFQTicketXBT() override; - - void init(const std::shared_ptr&); - - void resetTicket(); - - bs::FixedXbtInputs fixedXbtInputs(); - - QPushButton* submitButton() const; - QLineEdit* lineEditAmount() const; - QPushButton* buyButton() const; - QPushButton* sellButton() const; - QPushButton* numCcyButton() const; - QPushButton* denomCcyButton() const; - - bs::Address selectedAuthAddress() const; - // returns empty address if automatic selected - bs::Address recvXbtAddressIfSet() const; - - using SubmitRFQCb = std::function; - void setSubmitRFQ(SubmitRFQCb); - using CancelRFQCb = std::function; - void setCancelRFQ(CancelRFQCb); - - std::shared_ptr xbtWallet() const; - UiUtils::WalletsTypes xbtWalletType() const; - - void onParentAboutToHide(); - - void onVerifiedAuthAddresses(const std::vector&); - void onBalance(const std::string& currency, double balance); - void onWalletBalance(const bs::sync::WalletBalanceData&); - void onHDWallet(const bs::sync::HDWalletData&); - void onWalletData(const std::string& walletId, const bs::sync::WalletData&); - void onAuthKey(const bs::Address&, const BinaryData& authKey); - void onTradeSettings(const std::shared_ptr&); - void onReservedUTXOs(const std::string& resId, const std::string& subId - , const std::vector&); - -signals: - void needWalletData(const std::string& walletId); - void needAuthKey(const bs::Address&); - void needReserveUTXOs(const std::string& reserveId, const std::string& walletId - , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); - -public slots: - void SetProductAndSide(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice, bs::network::Side::Type side); - void setSecurityId(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice); - void setSecurityBuy(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice); - void setSecuritySell(const QString& productGroup, const QString& currencyPair - , const QString& bidPrice, const QString& offerPrice); - - void enablePanel(); - void disablePanel(); - - void onSendRFQ(const std::string &id, const QString &symbol, double amount, bool buy); - void onCancelRFQ(const std::string &id); - - void onNewSecurity(const std::string& name, bs::network::Asset::Type at) { assetTypes_[name] = at; } - void onMDUpdate(bs::network::Asset::Type, const QString &security, bs::network::MDFields); - -private slots: - void updateBalances(); - void onSignerReady(); //deprecated - void walletsLoaded(); //deprecated - - void onNumCcySelected(); - void onDenomCcySelected(); - - void onSellSelected(); - void onBuySelected(); - - void showCoinControl(); - void walletSelectedRecv(int index); - void walletSelectedSend(int index); - - void updateSubmitButton(); - void submitButtonClicked(); - - void onHDLeafCreated(const std::string& ccName); - void onCreateHDWalletError(const std::string& ccName, bs::error::ErrorCode result); - - void onMaxClicked(); - void onAmountEdited(const QString &); - - void onCreateWalletClicked(); - - void onAuthAddrChanged(int); - void onSettlLeavesLoaded(unsigned int); //deprecated - - void onUTXOReservationChanged(const std::string& walletId); - -protected: - bool eventFilter(QObject *watched, QEvent *evt) override; - -private: - enum class ProductGroupType - { - GroupNotSelected, - FXGroupType, - XBTGroupType, - CCGroupType - }; - - struct BalanceInfoContainer - { - double amount; - QString product; - ProductGroupType productType; - }; - -private: - void showHelp(const QString& helpText); - void clearHelp(); - void sendRFQ(const std::string &id); - - void updatePanel(); - - void fillRecvAddresses(); - - bool preSubmitCheck(); - - BalanceInfoContainer getBalanceInfo() const; - QString getProduct() const; - [[deprecated]] std::shared_ptr getCCWallet(const std::string &cc) const; - bool checkBalance(double qty) const; - bool checkAuthAddr() const; - bs::network::Side::Type getSelectedSide() const; - std::string authKey() const; - - void putRFQ(const bs::network::RFQ &); - bool existsRFQ(const bs::network::RFQ &); - - static std::string mkRFQkey(const bs::network::RFQ &); - - void SetProductGroup(const QString& productGroup); - void SetCurrencyPair(const QString& currencyPair); - - void saveLastSideSelection(const std::string& product, const std::string& currencyPair, bs::network::Side::Type side); - bs::network::Side::Type getLastSideSelection(const std::string& product, const std::string& currencyPair); - - void HideRFQControls(); - - void initProductGroupMap(); - ProductGroupType getProductGroupType(const QString& productGroup); - - double getQuantity() const; - double getOfferPrice() const; - - void SetCurrentIndicativePrices(const QString& bidPrice, const QString& offerPrice); - void updateIndicativePrice(); - double getIndicativePrice() const; - - void productSelectionChanged(); - - [[deprecated]] std::shared_ptr getSendXbtWallet() const; - [[deprecated]] std::shared_ptr getRecvXbtWallet() const; - std::string getXbtLeafId(const std::string& hdWalletId) const; - bool hasSendXbtWallet() const; - bool hasRecvXbtWallet() const; - bool hasCCWallet() const { return true; } //TODO: add proper checking - bs::XBTAmount getXbtBalance() const; - QString getProductToSpend() const; - QString getProductToRecv() const; - bs::XBTAmount expectedXbtAmountMin() const; - bs::XBTAmount getXbtReservationAmountForCc(double quantity, double offerPrice) const; - - void reserveBestUtxoSetAndSubmit(const std::string &id - , const std::shared_ptr& rfq); - - void sendDeferredRFQs(); - -private: - std::unique_ptr ui_; - - std::shared_ptr logger_; - std::shared_ptr assetManager_; - std::shared_ptr authAddressManager_; - - std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; - std::shared_ptr armory_; - std::shared_ptr utxoReservationManager_; - - mutable bs::Address authAddr_; - mutable std::string authKey_; - - unsigned int leafCreateReqId_ = 0; - - std::unordered_map rfqMap_; - std::unordered_map> pendingRFQs_; - - std::unordered_map lastSideSelection_; - - QFont invalidBalanceFont_; - - CCAmountValidator *ccAmountValidator_{}; - FXAmountValidator *fxAmountValidator_{}; - XbtAmountValidator *xbtAmountValidator_{}; - - std::unordered_map groupNameToType_; - ProductGroupType currentGroupType_ = ProductGroupType::GroupNotSelected; - - QString currentProduct_; - QString contraProduct_; - - QString currentBidPrice_; - QString currentOfferPrice_; - - SubmitRFQCb submitRFQCb_{}; - CancelRFQCb cancelRFQCb_{}; - - bs::FixedXbtInputs fixedXbtInputs_; - - bool autoRFQenabled_{ false }; - std::vector deferredRFQs_; - - std::unordered_map assetTypes_; - std::unordered_map mdInfo_; - std::unordered_map balances_; - std::vector wallets_; - std::shared_ptr tradeSettings_; -}; - -#endif // __RFQ_TICKET_XBT_H__ diff --git a/BlockSettleUILib/Trading/RFQTicketXBT.ui b/BlockSettleUILib/Trading/RFQTicketXBT.ui deleted file mode 100644 index c90f52eb2..000000000 --- a/BlockSettleUILib/Trading/RFQTicketXBT.ui +++ /dev/null @@ -1,1192 +0,0 @@ - - - - RFQTicketXBT - - - - 0 - 0 - 460 - 757 - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 25 - - - - true - - - - 5 - - - 5 - - - 0 - - - 0 - - - 0 - - - - - QUOTE REQUEST - - - 0 - - - true - - - - - - - - - - - 10 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - GENERAL - - - true - - - - 10 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 5 - - - 5 - - - 5 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 16777215 - - - - Product Group - - - - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 16777215 - - - - Security ID - - - - - - - font-weight: bold; - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 16777215 - - - - Indicative Price - - - - - - - -- - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 5 - - - 5 - - - 0 - - - 5 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Product - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 60 - 30 - - - - - 60 - 30 - - - - --- - - - true - - - true - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - --- - - - true - - - true - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 1 - - - 0 - - - 0 - - - 0 - - - - - Side - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Buy - - - true - - - true - - - - - - - - 60 - 30 - - - - - 60 - 30 - - - - Sell - - - true - - - true - - - - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Quantity - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 21 - - - - - 16777215 - 21 - - - - - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Max - - - - - - - - - - - - - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 3 - - - 10 - - - 0 - - - 10 - - - 10 - - - - - Balance - - - - - - - font-weight: bold; - - - xxx - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - - - - 0 - 0 - - - - SETTLEMENT DETAILS - - - true - - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Payment Wallet - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 21 - - - - - 16777215 - 21 - - - - - - - - - 100 - 21 - - - - - 100 - 21 - - - - &Inputs - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Receiving Wallet - - - - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 21 - - - - - 16777215 - 21 - - - - - - - - - - - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Receiving Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 3 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Authentication Address - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 16777215 - 20 - - - - - - - - - - - - - - - 0 - 0 - - - - help text - - - true - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - 0 - 0 - - - - true - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 35 - - - - - - - SUBMIT QUOTE REQUEST - - - - - - - - 0 - 35 - - - - Create Wallet - - - - - - - - - - - diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp deleted file mode 100644 index b0809460f..000000000 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ReqCCSettlementContainer.h" -#include -#include "AssetManager.h" -#include "CheckRecipSigner.h" -#include "HeadlessContainer.h" -#include "TradesUtils.h" -#include "TransactionData.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "BSErrorCodeStrings.h" -#include "UiUtils.h" -#include "XBTAmount.h" -#include "UtxoReservationManager.h" - -using namespace bs::sync; - -ReqCCSettlementContainer::ReqCCSettlementContainer(const std::shared_ptr &logger - , const std::shared_ptr &container - , const std::shared_ptr &armory - , const std::shared_ptr &assetMgr - , const std::shared_ptr &walletsMgr - , const bs::network::RFQ &rfq - , const bs::network::Quote "e - , const std::shared_ptr &xbtWallet - , const std::map &manualXbtInputs - , const std::shared_ptr &utxoReservationManager - , bs::hd::Purpose walletPurpose - , bs::UtxoReservationToken utxoRes - , bool expandTxDialogInfo) - : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) - , logger_(logger) - , signingContainer_(container) - , xbtWallet_(xbtWallet) - , assetMgr_(assetMgr) - , walletsMgr_(walletsMgr) - , rfq_(rfq) - , quote_(quote) - , genAddress_(assetMgr_->getCCGenesisAddr(product())) - , dealerAddress_(quote_.dealerAuthPublicKey) - , signer_(armory) - , lotSize_(assetMgr_->getCCLotSize(product())) - , manualXbtInputs_(manualXbtInputs) - , utxoReservationManager_(utxoReservationManager) - , armory_(armory) -{ - if (!xbtWallet_) { - throw std::logic_error("invalid hd wallet"); - } - - auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); - if (!xbtGroup) { - throw std::invalid_argument(fmt::format("can't find XBT group in {}", xbtWallet_->walletId())); - } - auto xbtLeaves = xbtGroup->getLeaves(); - xbtLeaves_.insert(xbtLeaves_.end(), xbtLeaves.begin(), xbtLeaves.end()); - if (xbtLeaves_.empty()) { - throw std::invalid_argument(fmt::format("empty XBT group in {}", xbtWallet_->walletId())); - } - - if (lotSize_ == 0) { - throw std::runtime_error("invalid lot size"); - } - - ccWallet_ = walletsMgr->getCCWallet(rfq.product); - if (!ccWallet_) { - throw std::logic_error("can't find CC wallet"); - } - - connect(this, &ReqCCSettlementContainer::genAddressVerified, this - , &ReqCCSettlementContainer::onGenAddressVerified, Qt::QueuedConnection); - - const auto &rootWallet = (rfq.side == bs::network::Side::Sell) ? walletsMgr_->getHDRootForLeaf(ccWallet_->walletId()) : xbtWallet_; - if (!rootWallet) { - throw std::runtime_error("missing signing wallet"); - } - walletInfo_ = bs::hd::WalletInfo(walletsMgr_, rootWallet); - infoReqId_ = signingContainer_->GetInfo(walletInfo_.rootId().toStdString()); - - if (!dealerTx_.ParseFromString(BinaryData::CreateFromHex(quote_.dealerTransaction).toBinStr())) { - throw std::invalid_argument("invalid dealer's transaction"); - } -} - -ReqCCSettlementContainer::~ReqCCSettlementContainer() = default; - -bs::sync::PasswordDialogData ReqCCSettlementContainer::toPasswordDialogData(QDateTime timestamp) const -{ - bs::sync::PasswordDialogData dialogData = SettlementContainer::toPasswordDialogData(timestamp); - dialogData.setValue(PasswordDialogData::Market, "CC"); - dialogData.setValue(PasswordDialogData::AutoSignCategory, static_cast(bs::signer::AutoSignCategory::SettlementRequestor)); - dialogData.setValue(PasswordDialogData::LotSize, static_cast(lotSize_)); - - if (side() == bs::network::Side::Sell) { - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Delivery")); - } - else { - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Payment")); - } - - // rfq details - dialogData.setValue(PasswordDialogData::Price, UiUtils::displayPriceCC(price())); - dialogData.setValue(PasswordDialogData::Quantity, quantity()); - - // tx details - if (side() == bs::network::Side::Buy) { - dialogData.setValue(PasswordDialogData::TxInputProduct, UiUtils::XbtCurrency); - } - else { - dialogData.setValue(PasswordDialogData::TxInputProduct, product()); - } - - - // settlement details - dialogData.setValue(PasswordDialogData::DeliveryUTXOVerified, genAddrVerified_); - dialogData.setValue(PasswordDialogData::SigningAllowed, genAddrVerified_); - - dialogData.setValue(PasswordDialogData::RecipientsListVisible, true); - dialogData.setValue(PasswordDialogData::InputsListVisible, true); - - return dialogData; -} - -void ReqCCSettlementContainer::activate() -{ - if (side() == bs::network::Side::Buy) { - double balance = 0; - for (const auto &leaf : xbtWallet_->getGroup(xbtWallet_->getXBTGroupType())->getLeaves()) { - balance += assetMgr_->getBalance(bs::network::XbtCurrency, bs::UTXOReservationManager::kIncludeZcRequestor, leaf); - } - if (amount() > balance) { - emit paymentVerified(false, tr("Insufficient XBT balance in signing wallet")); - return; - } - } - - startTimer(kWaitTimeoutInSec); - - userKeyOk_ = false; - bool foundRecipAddr = false; - bool amountValid = false; - try { - signer_.deserializeState(dealerTx_); - foundRecipAddr = signer_.findRecipAddress(bs::Address::fromAddressString(rfq_.receiptAddress) - , [this, &amountValid](uint64_t value, uint64_t valReturn, uint64_t valInput) { - if ((quote_.side == bs::network::Side::Sell) && qFuzzyCompare(quantity(), value / lotSize_)) { - amountValid = valInput == (value + valReturn); - } - else if (quote_.side == bs::network::Side::Buy) { - const auto quoteVal = static_cast(amount() * BTCNumericTypes::BalanceDivider); - const auto diff = (quoteVal > value) ? quoteVal - value : value - quoteVal; - if (diff < 3) { - amountValid = valInput > (value + valReturn); - } - } - }); - } - catch (const std::exception &e) { - logger_->debug("Signer deser exc: {}", e.what()); - emit error(id(), bs::error::ErrorCode::InternalError - , tr("Failed to verify dealer's TX: %1").arg(QLatin1String(e.what()))); - } - - emit paymentVerified(foundRecipAddr && amountValid, QString{}); - - if (genAddress_.empty()) { - emit genAddressVerified(false, tr("GA is null")); - } - else if (side() == bs::network::Side::Buy) { - //Waiting for genesis address verification to complete... - - const auto &cbHasInput = [this, handle = validityFlag_.handle()](bool has) { - if (!handle.isValid()) { - return; - } - userKeyOk_ = has; - emit genAddressVerified(has, has ? QString{} : tr("GA check failed")); - }; - signer_.hasInputAddress(genAddress_, cbHasInput, lotSize_); - } - else { - userKeyOk_ = true; - emit genAddressVerified(true, QString{}); - } - - if (!createCCUnsignedTXdata()) { - userKeyOk_ = false; - emit error(id(), bs::error::ErrorCode::InternalError - , tr("Failed to create unsigned CC transaction")); - } -} - -void ReqCCSettlementContainer::deactivate() -{ - stopTimer(); -} - -// KLUDGE currently this code not just making unsigned TX, but also initiate signing -bool ReqCCSettlementContainer::createCCUnsignedTXdata() -{ - if (side() == bs::network::Side::Sell) { - const int64_t spendVal = quantity() * assetMgr_->getCCLotSize(product()); - logger_->debug("[{}] sell amount={}, spend value = {}", __func__, quantity(), spendVal); - ccTxData_.walletIds = { ccWallet_->walletId() }; - ccTxData_.armorySigner_.deserializeState(dealerTx_); - const auto recipient = bs::Address::fromAddressString(dealerAddress_).getRecipient(bs::XBTAmount{ spendVal }); - if (recipient) { - ccTxData_.armorySigner_.addRecipient(recipient, RECIP_GROUP_SPEND_1); - } - else { - logger_->error("[{}] failed to create recipient from {} and value {}" - , __func__, dealerAddress_, spendVal); - return false; - } - - logger_->debug("[{}] {} CC inputs reserved ({} recipients)" - , __func__, ccTxData_.armorySigner_.getTxInCount(), - ccTxData_.armorySigner_.getTxOutCount()); - - // KLUDGE - in current implementation, we should sign first to have sell/buy process aligned - AcceptQuote(); - } - else { - const auto &cbFee = [this](float feePerByteArmory) { - auto feePerByte = std::max(feePerByteArmory, utxoReservationManager_->feeRatePb()); - const int64_t spendVal = bs::XBTAmount(amount()).GetValue(); - auto inputsCb = [this, feePerByte, spendVal](const std::map &xbtInputs, bool useAllInputs = false) { - auto changeAddrCb = [this, feePerByte, xbtInputs, spendVal, useAllInputs](const bs::Address &changeAddr) { - try { - - const auto recipient = bs::Address::fromAddressString(dealerAddress_).getRecipient(bs::XBTAmount{ spendVal }); - if (!recipient) { - logger_->error("[{}] invalid recipient: {}", __func__, dealerAddress_); - return; - } - std::map>> recipientMap; - std::vector> recVec({recipient}); - recipientMap.emplace(RECIP_GROUP_SPEND_2, std::move(recVec)); - - ccTxData_ = bs::sync::WalletsManager::createPartialTXRequest(spendVal - , xbtInputs, changeAddr, feePerByte, armory_->topBlock() - , recipientMap, RECIP_GROUP_CHANG_2 - , dealerTx_, useAllInputs, UINT32_MAX, logger_); - - logger_->debug("{} inputs in ccTxData", ccTxData_.armorySigner_.getTxInCount()); - // Must release old reservation first (we reserve excessive XBT inputs in advance for CC buy requests)! - - auto resolveCB = [this]( - bs::error::ErrorCode result, const Codec_SignerState::SignerState &state) - { - utxoRes_.release(); - if (result != bs::error::ErrorCode::NoError) { - std::stringstream ss; - ss << "failed to resolve CC half reply with error code: " << (int)result; - throw std::runtime_error(ss.str()); - } - - ccTxData_.armorySigner_.deserializeState(state); - utxoRes_ = utxoReservationManager_->makeNewReservation(ccTxData_.getInputs(nullptr), id()); - AcceptQuote(); - }; - signingContainer_->resolvePublicSpenders(ccTxData_, resolveCB); - } - catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "Failed to create partial CC TX " - "to {}: {}", dealerAddress_, e.what()); - emit error(id(), bs::error::ErrorCode::InternalError - , tr("Failed to create CC TX half")); - } - }; - xbtLeaves_.front()->getNewChangeAddress(changeAddrCb); - }; - if (manualXbtInputs_.empty()) { - std::vector utxos; - if (!xbtWallet_->canMixLeaves()) { - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet_->walletId(), walletPurpose_, bs::UTXOReservationManager::kIncludeZcRequestor); - } - else { - utxos = utxoReservationManager_->getAvailableXbtUTXOs(xbtWallet_->walletId(), bs::UTXOReservationManager::kIncludeZcRequestor); - } - - auto fixedUtxo = utxoReservationManager_->convertUtxoToPartialFixedInput(xbtWallet_->walletId(), utxos); - inputsCb(fixedUtxo.inputs); - } else { - inputsCb(manualXbtInputs_, true); - } - }; - walletsMgr_->estimatedFeePerByte(0, cbFee, this); - } - - return true; -} - -void ReqCCSettlementContainer::AcceptQuote() -{ - if (side() == bs::network::Side::Sell) { - if (!ccTxData_.isValid()) { - logger_->error("[CCSettlementTransactionWidget::AcceptQuote] CC TX half wasn't created properly"); - emit error(id(), bs::error::ErrorCode::InternalError - , tr("Failed to create TX half")); - return; - } - } - signingContainer_->resolvePublicSpenders(ccTxData_, [this] - (bs::error::ErrorCode result, const Codec_SignerState::SignerState &state) - { - if (result == bs::error::ErrorCode::NoError) { - ccTxResolvedData_ = state; - emit sendOrder(); - } - else { - emit error(id(), result, bs::error::ErrorCodeToString(result)); - } - }); -} - -bool ReqCCSettlementContainer::startSigning(QDateTime timestamp) -{ - const auto &cbTx = [this, handle = validityFlag_.handle(), logger=logger_](bs::error::ErrorCode result, const BinaryData &signedTX) { - if (!handle.isValid()) { - logger->warn("[ReqCCSettlementContainer::onTXSigned] failed to sign TX half, already destroyed"); - return; - } - - ccSignId_ = 0; - - if (result == bs::error::ErrorCode::NoError) { - ccTxSigned_ = signedTX.toHexStr(); - - // notify RFQ dialog that signed half could be saved - emit txSigned(); - - // FIXME: disabled as it does not work correctly (signedTX txid is different from combined txid) -#if 0 - for (const auto &xbtLeaf : xbtLeaves_) { - xbtLeaf->setTransactionComment(signedTX, txComment()); - } - ccWallet_->setTransactionComment(signedTX, txComment()); -#endif - } - else if (result == bs::error::ErrorCode::TxCancelled) { - SettlementContainer::releaseUtxoRes(); - emit cancelTrade(clOrdId_); - } - else { - logger->error("[CCSettlementTransactionWidget::onTXSigned] CC TX sign failure: {}", bs::error::ErrorCodeToString(result).toStdString()); - emit error(id(), result, tr("Own TX half signing failed: %1") - .arg(bs::error::ErrorCodeToString(result))); - } - - // Call completed to remove from RfqStorage and cleanup memory - emit completed(id()); - }; - - ccSignId_ = signingContainer_->signSettlementPartialTXRequest(ccTxData_, toPasswordDialogData(timestamp), cbTx); - logger_->debug( - "[CCSettlementTransactionWidget::createCCSignedTXdata] {} recipients", - ccTxData_.armorySigner_.getTxInCount()); - return (ccSignId_ > 0); -} - -std::string ReqCCSettlementContainer::txComment() -{ - return std::string(bs::network::Side::toString(bs::network::Side::invert(quote_.side))) + " " - + quote_.security + " @ " + std::to_string(price()); -} - -void ReqCCSettlementContainer::onWalletInfo(unsigned int reqId, const bs::hd::WalletInfo &walletInfo) -{ - if (!infoReqId_ || (reqId != infoReqId_)) { - return; - } - - // just update walletInfo_ to save walletName and id - walletInfo_.setEncKeys(walletInfo.encKeys()); - walletInfo_.setEncTypes(walletInfo.encTypes()); - walletInfo_.setKeyRank(walletInfo.keyRank()); - - emit walletInfoReceived(); -} - -void ReqCCSettlementContainer::onGenAddressVerified(bool addressVerified, const QString &error) -{ - genAddrVerified_ = addressVerified; - - bs::sync::PasswordDialogData pd; - pd.setValue(PasswordDialogData::SettlementId, id()); - pd.setValue(PasswordDialogData::DeliveryUTXOVerified, addressVerified); - pd.setValue(PasswordDialogData::SigningAllowed, addressVerified); - signingContainer_->updateDialogData(pd); -} - -bool ReqCCSettlementContainer::cancel() -{ - deactivate(); - if (ccSignId_ != 0) { - signingContainer_->CancelSignTx(BinaryData::fromString(id())); - } - - SettlementContainer::releaseUtxoRes(); - emit settlementCancelled(); - - return true; -} - -std::string ReqCCSettlementContainer::txData() const -{ - if (!ccTxResolvedData_.IsInitialized()) { - logger_->error("[ReqCCSettlementContainer::txData] no resolved data"); - return {}; - } - return BinaryData::fromString(ccTxResolvedData_.SerializeAsString()).toHexStr(); -} - -void ReqCCSettlementContainer::setClOrdId(const std::string& clientOrderId) -{ - clOrdId_ = clientOrderId; -} diff --git a/BlockSettleUILib/Trading/ReqCCSettlementContainer.h b/BlockSettleUILib/Trading/ReqCCSettlementContainer.h deleted file mode 100644 index d94c7f847..000000000 --- a/BlockSettleUILib/Trading/ReqCCSettlementContainer.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __REQ_CC_SETTLEMENT_CONTAINER_H__ -#define __REQ_CC_SETTLEMENT_CONTAINER_H__ - -#include -#include "CheckRecipSigner.h" -#include "SettlementContainer.h" -#include "CommonTypes.h" -#include "CoreWallet.h" -#include "QWalletInfo.h" -#include "UtxoReservationToken.h" - -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - class WalletsManager; - } - class UTXOReservationManager; -} -class ArmoryConnection; -class AssetManager; -class HeadlessContainer; - - -class ReqCCSettlementContainer : public bs::SettlementContainer -{ - Q_OBJECT -public: - ReqCCSettlementContainer(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const bs::network::RFQ & - , const bs::network::Quote & - , const std::shared_ptr &xbtWallet - , const std::map &manualXbtInputs - , const std::shared_ptr &utxoReservationManager - , bs::hd::Purpose walletPurpose - , bs::UtxoReservationToken utxoRes - , bool expandTxDialogInfo); - ~ReqCCSettlementContainer() override; - - bool cancel() override; - - void activate() override; - void deactivate() override; - - std::string id() const override { return rfq_.requestId; } - bs::network::Asset::Type assetType() const override { return rfq_.assetType; } - std::string security() const override { return rfq_.security; } - std::string product() const override { return rfq_.product; } - bs::network::Side::Type side() const override { return rfq_.side; } - double quantity() const override { return quote_.quantity; } - double price() const override { return quote_.price; } - double amount() const override { return quantity() * price(); } - bs::sync::PasswordDialogData toPasswordDialogData(QDateTime timestamp) const override; - - bs::hd::WalletInfo walletInfo() const { return walletInfo_; } - std::string txData() const; - std::string txSignedData() const { return ccTxSigned_; } - - bool startSigning(QDateTime timestamp); - - void setClOrdId(const std::string& clientOrderId); - -signals: - void sendOrder(); - - void settlementCancelled(); - - void txSigned(); - void genAddressVerified(bool result, QString error); - void paymentVerified(bool result, QString error); - void walletInfoReceived(); - - void cancelTrade(const std::string& orderId); - -private slots: - void onWalletInfo(unsigned int reqId, const bs::hd::WalletInfo& walletInfo); - void onGenAddressVerified(bool addressVerified, const QString &error); - -private: - // read comments in source code - bool createCCUnsignedTXdata(); - std::string txComment(); - - void AcceptQuote(); - -private: - std::shared_ptr logger_; - std::shared_ptr signingContainer_; - std::shared_ptr xbtWallet_; - std::vector> xbtLeaves_; - std::shared_ptr ccWallet_; - std::shared_ptr assetMgr_; - std::shared_ptr walletsMgr_; - std::shared_ptr utxoReservationManager_; - std::shared_ptr armory_; - bs::network::RFQ rfq_; - bs::network::Quote quote_; - const bs::Address genAddress_; - const std::string dealerAddress_; - bs::CheckRecipSigner signer_; - - const uint64_t lotSize_; - unsigned int ccSignId_ = 0; - unsigned int infoReqId_ = 0; - bool userKeyOk_ = false; - - Codec_SignerState::SignerState dealerTx_; - bs::core::wallet::TXSignRequest ccTxData_; - Codec_SignerState::SignerState ccTxResolvedData_; - std::string ccTxSigned_; - bool genAddrVerified_ = false; - - bs::hd::WalletInfo walletInfo_; - std::map manualXbtInputs_; - - std::string clOrdId_; - -}; - -#endif // __REQ_CC_SETTLEMENT_CONTAINER_H__ diff --git a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp deleted file mode 100644 index 09a105c8c..000000000 --- a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "ReqXBTSettlementContainer.h" - -#include - -#include - -#include "AssetManager.h" -#include "AuthAddressManager.h" -#include "CheckRecipSigner.h" -#include "CurrencyPair.h" -#include "HeadlessContainer.h" -#include "QuoteProvider.h" -#include "TradesUtils.h" -#include "UiUtils.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "UtxoReservationManager.h" - -#include - -using namespace bs::sync; - -Q_DECLARE_METATYPE(AddressVerificationState) - -ReqXBTSettlementContainer::ReqXBTSettlementContainer(const std::shared_ptr &logger - , const std::shared_ptr &authAddrMgr - , const std::shared_ptr &signContainer - , const std::shared_ptr &armory - , const std::shared_ptr &xbtWallet - , const std::shared_ptr &walletsMgr - , const bs::network::RFQ &rfq - , const bs::network::Quote "e - , const bs::Address &authAddr - , const std::map &utxosPayinFixed - , bs::UtxoReservationToken utxoRes - , const std::shared_ptr &utxoReservationManager - , bs::hd::Purpose walletPurpose - , const bs::Address &recvAddrIfSet - , bool expandTxDialogInfo - , uint64_t tier1XbtLimit) - : bs::SettlementContainer(std::move(utxoRes), walletPurpose, expandTxDialogInfo) - , logger_(logger) - , authAddrMgr_(authAddrMgr) - , walletsMgr_(walletsMgr) - , signContainer_(signContainer) - , armory_(armory) - , xbtWallet_(xbtWallet) - , rfq_(rfq) - , quote_(quote) - , recvAddrIfSet_(recvAddrIfSet) - , weSellXbt_(!rfq.isXbtBuy()) - , authAddr_(authAddr) - , utxosPayinFixed_(utxosPayinFixed) - , utxoReservationManager_(utxoReservationManager) -{ - assert(authAddr.isValid()); - - qRegisterMetaType(); - - connect(this, &ReqXBTSettlementContainer::timerExpired, this, &ReqXBTSettlementContainer::onTimerExpired); - - CurrencyPair cp(quote_.security); - const bool isFxProd = (quote_.product != bs::network::XbtCurrency); - fxProd_ = cp.ContraCurrency(bs::network::XbtCurrency); - amount_ = isFxProd ? quantity() / price() : quantity(); - - const auto xbtAmount = bs::XBTAmount(amount_); - - // BST-2545: Use price as it see Genoa (and it computes it as ROUNDED_CCY / XBT) - const auto actualXbtPrice = UiUtils::actualXbtPrice(xbtAmount, price()); - - auto side = quote_.product == bs::network::XbtCurrency ? bs::network::Side::invert(quote_.side) : quote_.side; - comment_ = fmt::format("{} {} @ {}", bs::network::Side::toString(side) - , quote_.security, UiUtils::displayPriceXBT(actualXbtPrice).toStdString()); - - dealerAddressValidationRequired_ = xbtAmount > bs::XBTAmount((int64_t)tier1XbtLimit); -} - -ReqXBTSettlementContainer::~ReqXBTSettlementContainer() = default; - -void ReqXBTSettlementContainer::acceptSpotXBT() -{ - emit acceptQuote(rfq_.requestId, "not used"); -} - -bool ReqXBTSettlementContainer::cancel() -{ - deactivate(); - - if (payinSignId_ != 0 || payoutSignId_ != 0) { - signContainer_->CancelSignTx(settlementId_); - } - - SettlementContainer::releaseUtxoRes(); - emit settlementCancelled(); - - return true; -} - -void ReqXBTSettlementContainer::onTimerExpired() -{ - cancel(); -} - -void ReqXBTSettlementContainer::activate() -{ - startTimer(kWaitTimeoutInSec); - - settlementIdHex_ = quote_.settlementId; - - addrVerificator_ = std::make_shared(logger_, armory_ - , [this, handle = validityFlag_.handle()](const bs::Address &address, AddressVerificationState state) - { - QMetaObject::invokeMethod(qApp, [this, handle, address, state] { - if (!handle.isValid()) { - return; - } - dealerVerifStateChanged(state); - }); - }); - - addrVerificator_->SetBSAddressList(authAddrMgr_->GetBSAddresses()); - - settlementId_ = BinaryData::CreateFromHex(quote_.settlementId); - userKey_ = BinaryData::CreateFromHex(quote_.requestorAuthPublicKey); - dealerAuthKey_ = BinaryData::CreateFromHex(quote_.dealerAuthPublicKey); - dealerAuthAddress_ = bs::Address::fromPubKey(dealerAuthKey_, AddressEntryType_P2WPKH); - - acceptSpotXBT(); - - const auto &authLeaf = walletsMgr_->getAuthWallet(); - signContainer_->setSettlAuthAddr(authLeaf->walletId(), settlementId_, authAddr_); -} - -void ReqXBTSettlementContainer::deactivate() -{ - stopTimer(); -} - -bs::sync::PasswordDialogData ReqXBTSettlementContainer::toPasswordDialogData(QDateTime timestamp) const -{ - bs::sync::PasswordDialogData dialogData = SettlementContainer::toPasswordDialogData(timestamp); - dialogData.setValue(PasswordDialogData::Market, "XBT"); - dialogData.setValue(PasswordDialogData::AutoSignCategory, static_cast(bs::signer::AutoSignCategory::SettlementRequestor)); - - // rfq details - QString qtyProd = UiUtils::XbtCurrency; - QString fxProd = QString::fromStdString(fxProd_); - - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Pay-In")); - dialogData.setValue(PasswordDialogData::Price, UiUtils::displayPriceXBT(price())); - dialogData.setValue(PasswordDialogData::FxProduct, fxProd); - - bool isFxProd = (quote_.product != bs::network::XbtCurrency); - - if (isFxProd) { - dialogData.setValue(PasswordDialogData::Quantity, tr("%1 %2") - .arg(UiUtils::displayAmountForProduct(quantity(), fxProd, bs::network::Asset::Type::SpotXBT)) - .arg(fxProd)); - - dialogData.setValue(PasswordDialogData::TotalValue, tr("%1 XBT") - .arg(UiUtils::displayAmount(quantity() / price()))); - } - else { - dialogData.setValue(PasswordDialogData::Quantity, tr("%1 XBT") - .arg(UiUtils::displayAmount(amount()))); - - dialogData.setValue(PasswordDialogData::TotalValue, tr("%1 %2") - .arg(UiUtils::displayAmountForProduct(amount() * price(), fxProd, bs::network::Asset::Type::SpotXBT)) - .arg(fxProd)); - } - - // settlement details - dialogData.setValue(PasswordDialogData::SettlementId, settlementIdHex_); - dialogData.setValue(PasswordDialogData::SettlementAddress, settlAddr_.display()); - - dialogData.setValue(PasswordDialogData::RequesterAuthAddress, authAddr_.display()); - dialogData.setValue(PasswordDialogData::RequesterAuthAddressVerified, true); - - dialogData.setValue(PasswordDialogData::ResponderAuthAddress, - dealerAuthAddress_.display()); - dialogData.setValue(PasswordDialogData::ResponderAuthAddressVerified, dealerVerifState_ == AddressVerificationState::Verified); - dialogData.setValue(PasswordDialogData::SigningAllowed, dealerVerifState_ == AddressVerificationState::Verified); - - // tx details - dialogData.setValue(PasswordDialogData::TxInputProduct, UiUtils::XbtCurrency); - - return dialogData; -} - -void ReqXBTSettlementContainer::dealerVerifStateChanged(AddressVerificationState state) -{ - dealerVerifState_ = state; - bs::sync::PasswordDialogData pd; - pd.setValue(PasswordDialogData::SettlementId, settlementIdHex_); - pd.setValue(PasswordDialogData::ResponderAuthAddress, dealerAuthAddress_.display()); - pd.setValue(PasswordDialogData::ResponderAuthAddressVerified, state == AddressVerificationState::Verified); - pd.setValue(PasswordDialogData::SigningAllowed, state == AddressVerificationState::Verified); - signContainer_->updateDialogData(pd); -} - -void ReqXBTSettlementContainer::cancelWithError(const QString& errorMessage, bs::error::ErrorCode code) -{ - emit cancelTrade(settlementIdHex_); - emit error(id(), code, errorMessage); - cancel(); - - // Call failed to remove from RfqStorage and cleanup memory - emit failed(id()); -} - -void ReqXBTSettlementContainer::onTXSigned(unsigned int idReq, BinaryData signedTX - , bs::error::ErrorCode errCode, std::string errTxt) -{ - if ((payoutSignId_ != 0) && (payoutSignId_ == idReq)) { - payoutSignId_ = 0; - - if (errCode == bs::error::ErrorCode::TxCancelled) { - SPDLOG_LOGGER_DEBUG(logger_, "cancel on a trade : {}", settlementIdHex_); - deactivate(); - emit cancelTrade(settlementIdHex_); - return; - } - - if ((errCode != bs::error::ErrorCode::NoError) || signedTX.empty()) { - logger_->warn("[ReqXBTSettlementContainer::onTXSigned] Pay-Out sign failure: {} ({})" - , (int)errCode, errTxt); - cancelWithError(tr("Pay-Out signing failed: %1").arg(bs::error::ErrorCodeToString(errCode)), errCode); - return; - } - - SPDLOG_LOGGER_DEBUG(logger_, "signed payout: {}", signedTX.toHexStr()); - - bs::tradeutils::PayoutVerifyArgs verifyArgs; - verifyArgs.signedTx = signedTX; - verifyArgs.settlAddr = settlAddr_; - verifyArgs.usedPayinHash = expectedPayinHash_; - verifyArgs.amount = bs::XBTAmount(amount_); - auto verifyResult = bs::tradeutils::verifySignedPayout(verifyArgs); - if (!verifyResult.success) { - SPDLOG_LOGGER_ERROR(logger_, "payout verification failed: {}", verifyResult.errorMsg); - cancelWithError(tr("payin verification failed: %1").arg(bs::error::ErrorCodeToString(errCode)), errCode); - return; - } - - emit sendSignedPayoutToPB(settlementIdHex_, signedTX); - - for (const auto &leaf : xbtWallet_->getGroup(xbtWallet_->getXBTGroupType())->getLeaves()) { - leaf->setTransactionComment(signedTX, comment_); - } -// walletsMgr_->getSettlementWallet()->setTransactionComment(payoutData_, comment_); //TODO: later - - // OK. if payout created - settletlement accepted for this RFQ - deactivate(); - emit settlementAccepted(); - - // Call completed to remove from RfqStorage and cleanup memory - emit completed(id()); - } - - if ((payinSignId_ != 0) && (payinSignId_ == idReq)) { - payinSignId_ = 0; - - if (errCode == bs::error::ErrorCode::TxCancelled) { - SPDLOG_LOGGER_DEBUG(logger_, "cancel on a trade : {}", settlementIdHex_); - deactivate(); - emit cancelTrade(settlementIdHex_); - return; - } - - if ((errCode != bs::error::ErrorCode::NoError) || signedTX.empty()) { - SPDLOG_LOGGER_ERROR(logger_, "failed to create pay-in TX: {} ({})", static_cast(errCode), errTxt); - cancelWithError(tr("Failed to create Pay-In TX: %1").arg(bs::error::ErrorCodeToString(errCode)), errCode); - return; - } - - try { - const Tx tx(signedTX); - if (!tx.isInitialized()) { - throw std::runtime_error("uninited TX"); - } - - if (tx.getThisHash() != expectedPayinHash_) { - emit cancelWithError(tr("payin hash mismatch"), bs::error::ErrorCode::TxInvalidRequest); - return; - } - } - catch (const std::exception &e) { - emit cancelWithError(tr("invalid signed pay-in"), bs::error::ErrorCode::TxInvalidRequest); - return; - } - - for (const auto &leaf : xbtWallet_->getGroup(xbtWallet_->getXBTGroupType())->getLeaves()) { - leaf->setTransactionComment(signedTX, comment_); - } - - emit sendSignedPayinToPB(settlementIdHex_, signedTX); - - // OK. if payin created - settletlement accepted for this RFQ - deactivate(); - emit settlementAccepted(); - - // Call completed to remove from RfqStorage and cleanup memory - emit completed(id()); - } -} - -void ReqXBTSettlementContainer::onUnsignedPayinRequested(const std::string& settlementId) -{ - if (settlementIdHex_ != settlementId) { - SPDLOG_LOGGER_ERROR(logger_, "invalid id : {} . {} expected", settlementId, settlementIdHex_); - return; - } - - if (!weSellXbt_) { - SPDLOG_LOGGER_ERROR(logger_, "customer buy on thq rfq {}. should not create unsigned payin" - , settlementId); - return; - } - - SPDLOG_LOGGER_DEBUG(logger_, "unsigned payin requested: {}", settlementId); - - bs::tradeutils::PayinArgs args; - initTradesArgs(args, settlementId); - args.fixedInputs.reserve(utxosPayinFixed_.size()); - for (const auto &input : utxosPayinFixed_) { - args.fixedInputs.push_back(input.first); - } - - const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); - if (!xbtWallet_->canMixLeaves()) { - const auto leaf = xbtGroup->getLeaf(walletPurpose_); - args.inputXbtWallets.push_back(leaf); - } - else { - for (const auto &leaf : xbtGroup->getLeaves()) { - args.inputXbtWallets.push_back(leaf); - } - } - - args.utxoReservation = bs::UtxoReservation::instance(); - - auto payinCb = bs::tradeutils::PayinResultCb([this, handle = validityFlag_.handle()] - (bs::tradeutils::PayinResult result) - { - QMetaObject::invokeMethod(qApp, [this, handle, result = std::move(result)] { - if (!handle.isValid()) { - return; - } - - if (!result.success) { - SPDLOG_LOGGER_ERROR(logger_, "payin sign request creation failed: {}", result.errorMsg); - cancelWithError(tr("payin failed"), bs::error::ErrorCode::InternalError); - return; - } - - settlAddr_ = result.settlementAddr; - - const auto list = authAddrMgr_->GetSubmittedAddressList(); - const auto userAddress = bs::Address::fromPubKey(userKey_, AddressEntryType_P2WPKH); - userKeyOk_ = (std::find(list.begin(), list.end(), userAddress) != list.end()); - if (!userKeyOk_) { - SPDLOG_LOGGER_WARN(logger_, "userAddr {} not found in verified addrs list ({})" - , userAddress.display(), list.size()); - return; - } - - if (dealerAddressValidationRequired_) { - addrVerificator_->addAddress(dealerAuthAddress_); - addrVerificator_->startAddressVerification(); - } else { - dealerVerifState_ = AddressVerificationState::Verified; - } - - unsignedPayinRequest_ = std::move(result.signRequest); - - // Make new reservation only for automatic inputs. - // Manual inputs should be already reserved. - if (utxosPayinFixed_.empty()) { - utxoRes_ = utxoReservationManager_->makeNewReservation( - unsignedPayinRequest_.getInputs(nullptr), id()); - } - - emit sendUnsignedPayinToPB(settlementIdHex_ - , bs::network::UnsignedPayinData{ unsignedPayinRequest_.serializeState().SerializeAsString() }); - - const auto &authLeaf = walletsMgr_->getAuthWallet(); - signContainer_->setSettlCP(authLeaf->walletId(), result.payinHash, settlementId_, dealerAuthKey_); - }); - }); - - bs::tradeutils::createPayin(std::move(args), std::move(payinCb)); -} - -void ReqXBTSettlementContainer::onSignedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash, QDateTime timestamp) -{ - if (settlementIdHex_ != settlementId) { - SPDLOG_LOGGER_ERROR(logger_, "invalid id : {} . {} expected", settlementId, settlementIdHex_); - return; - } - - startTimer(kWaitTimeoutInSec); - - SPDLOG_LOGGER_DEBUG(logger_, "create payout for {} on {} for {}", settlementId, payinHash.toHexStr(), amount_); - expectedPayinHash_ = payinHash; - - bs::tradeutils::PayoutArgs args; - initTradesArgs(args, settlementId); - args.payinTxId = payinHash; - args.recvAddr = recvAddrIfSet_; - - const auto xbtGroup = xbtWallet_->getGroup(xbtWallet_->getXBTGroupType()); - if (!xbtWallet_->canMixLeaves()) { - const auto leaf = xbtGroup->getLeaf(walletPurpose_); - args.outputXbtWallet = leaf; - } - else { - args.outputXbtWallet = xbtGroup->getLeaves().at(0); - } - - auto payoutCb = bs::tradeutils::PayoutResultCb([this, payinHash, timestamp, handle = validityFlag_.handle()] - (bs::tradeutils::PayoutResult result) - { - QMetaObject::invokeMethod(qApp, [this, payinHash, handle, timestamp, result = std::move(result)] { - if (!handle.isValid()) { - return; - } - - if (!result.success) { - SPDLOG_LOGGER_ERROR(logger_, "creating payout failed: {}", result.errorMsg); - cancelWithError(tr("payout failed"), bs::error::ErrorCode::InternalError); - return; - } - - settlAddr_ = result.settlementAddr; - - bs::sync::PasswordDialogData dlgData = toPayOutTxDetailsPasswordDialogData(result.signRequest, timestamp); - dlgData.setValue(PasswordDialogData::Market, "XBT"); - dlgData.setValue(PasswordDialogData::SettlementId, settlementIdHex_); - dlgData.setValue(PasswordDialogData::ResponderAuthAddressVerified, true); - dlgData.setValue(PasswordDialogData::SigningAllowed, true); - - SPDLOG_LOGGER_DEBUG(logger_, "pay-out fee={}, qty={} ({}), payin hash={}" - , result.signRequest.fee, amount_, amount_ * BTCNumericTypes::BalanceDivider, payinHash.toHexStr(true)); - - //note: signRequest should prolly be a shared_ptr - auto signerObj = result.signRequest; - payoutSignId_ = signContainer_->signSettlementPayoutTXRequest(signerObj - , {settlementId_, dealerAuthKey_, true}, dlgData); - }); - }); - bs::tradeutils::createPayout(std::move(args), std::move(payoutCb)); - -} - -void ReqXBTSettlementContainer::onSignedPayinRequested(const std::string& settlementId - , const BinaryData &payinHash, QDateTime timestamp) -{ - if (settlementIdHex_ != settlementId) { - SPDLOG_LOGGER_ERROR(logger_, "invalid id: {} - {} expected", settlementId - , settlementIdHex_); - return; - } - - if (payinHash.empty()) { - logger_->error("[ReqXBTSettlementContainer::onSignedPayinRequested] missing expected payin hash"); - emit cancelWithError(tr("payin hash mismatch"), bs::error::ErrorCode::TxInvalidRequest); - return; - } - - expectedPayinHash_ = payinHash; - - startTimer(kWaitTimeoutInSec); - - if (!weSellXbt_) { - SPDLOG_LOGGER_ERROR(logger_, "customer buy on thq rfq {}. should not sign payin", settlementId); - return; - } - - if (!unsignedPayinRequest_.isValid()) { - SPDLOG_LOGGER_ERROR(logger_, "unsigned payin request is invalid: {}", settlementIdHex_); - return; - } - - SPDLOG_LOGGER_DEBUG(logger_, "signed payin requested {}", settlementId); - - // XXX check unsigned payin? - - bs::sync::PasswordDialogData dlgData = toPasswordDialogData(timestamp); - dlgData.setValue(PasswordDialogData::SettlementPayInVisible, true); - - payinSignId_ = signContainer_->signSettlementTXRequest(unsignedPayinRequest_, dlgData); -} - -void ReqXBTSettlementContainer::initTradesArgs(bs::tradeutils::Args &args, const std::string &settlementId) -{ - args.amount = bs::XBTAmount{amount_}; - args.settlementId = BinaryData::CreateFromHex(settlementId); - args.ourAuthAddress = authAddr_; - args.cpAuthPubKey = dealerAuthKey_; - args.walletsMgr = walletsMgr_; - args.armory = armory_; - args.signContainer = signContainer_; - args.feeRatePb_ = utxoReservationManager_->feeRatePb(); -} diff --git a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h b/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h deleted file mode 100644 index fbe6b7979..000000000 --- a/BlockSettleUILib/Trading/ReqXBTSettlementContainer.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __REQ_XBT_SETTLEMENT_CONTAINER_H__ -#define __REQ_XBT_SETTLEMENT_CONTAINER_H__ - -#include -#include -#include "AddressVerificator.h" -#include "BSErrorCode.h" -#include "QWalletInfo.h" -#include "SettlementContainer.h" - -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - class WalletsManager; - } - namespace tradeutils { - struct Args; - } - class UTXOReservationManager; -} -class AddressVerificator; -class ArmoryConnection; -class AuthAddressManager; -class HeadlessContainer; -class QuoteProvider; - - -class ReqXBTSettlementContainer : public bs::SettlementContainer -{ - Q_OBJECT -public: - ReqXBTSettlementContainer(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &xbtWallet - , const std::shared_ptr & - , const bs::network::RFQ & - , const bs::network::Quote & - , const bs::Address &authAddr - , const std::map &utxosPayinFixed - , bs::UtxoReservationToken utxoRes - , const std::shared_ptr &utxoReservationManager - , bs::hd::Purpose walletPurpose - , const bs::Address &recvAddrIfSet - , bool expandTxDialogInfo - , uint64_t tier1XbtLimit); - ~ReqXBTSettlementContainer() override; - - bool cancel() override; - - void activate() override; - void deactivate() override; - - std::string id() const override { return quote_.requestId; } - bs::network::Asset::Type assetType() const override { return rfq_.assetType; } - std::string security() const override { return rfq_.security; } - std::string product() const override { return rfq_.product; } - bs::network::Side::Type side() const override { return rfq_.side; } - double quantity() const override { return quote_.quantity; } - double price() const override { return quote_.price; } - double amount() const override { return amount_; } - bs::sync::PasswordDialogData toPasswordDialogData(QDateTime timestamp) const override; - - void onUnsignedPayinRequested(const std::string& settlementId); - void onSignedPayoutRequested(const std::string& settlementId, const BinaryData& payinHash - , QDateTime timestamp); - void onSignedPayinRequested(const std::string& settlementId, const BinaryData &payinHash - , QDateTime timestamp); - -signals: - void settlementCancelled(); - void settlementAccepted(); - void acceptQuote(std::string reqId, std::string hexPayoutTx); - - void sendUnsignedPayinToPB(const std::string& settlementId, const bs::network::UnsignedPayinData& unsignedPayinData); - void sendSignedPayinToPB(const std::string& settlementId, const BinaryData& signedPayin); - void sendSignedPayoutToPB(const std::string& settlementId, const BinaryData& signedPayout); - - void cancelTrade(const std::string& settlementId); - -private slots: - void onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode, std::string error); - void onTimerExpired(); - -private: - void acceptSpotXBT(); - void dealerVerifStateChanged(AddressVerificationState); - - void cancelWithError(const QString& errorMessage, bs::error::ErrorCode code); - - void initTradesArgs(bs::tradeutils::Args &args, const std::string &settlementId); - -private: - std::shared_ptr logger_; - std::shared_ptr authAddrMgr_; - std::shared_ptr walletsMgr_; - std::shared_ptr signContainer_; - std::shared_ptr armory_; - std::shared_ptr xbtWallet_; - std::shared_ptr utxoReservationManager_; - - bs::network::RFQ rfq_; - bs::network::Quote quote_; - bs::Address settlAddr_; - - std::shared_ptr addrVerificator_; - - double amount_{}; - std::string fxProd_; - BinaryData settlementId_; - std::string settlementIdHex_; - BinaryData userKey_; - BinaryData dealerAuthKey_; - bs::Address recvAddrIfSet_; - AddressVerificationState dealerVerifState_ = AddressVerificationState::VerificationFailed; - - std::string comment_; - const bool weSellXbt_; - bool userKeyOk_ = false; - - unsigned int payinSignId_ = 0; - unsigned int payoutSignId_ = 0; - - const bs::Address authAddr_; - bs::Address dealerAuthAddress_; - - bs::core::wallet::TXSignRequest unsignedPayinRequest_; - BinaryData expectedPayinHash_; - std::map utxosPayinFixed_; - - bool tradeCancelled_ = false; - bool dealerAddressValidationRequired_ = true; -}; - -#endif // __REQ_XBT_SETTLEMENT_CONTAINER_H__ diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp b/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp deleted file mode 100644 index d545d552f..000000000 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RequestingQuoteWidget.h" -#include "ui_RequestingQuoteWidget.h" -#include - -#include "AssetManager.h" -#include "BlockDataManagerConfig.h" -#include "Celer/CelerClient.h" -#include "CurrencyPair.h" -#include "UiUtils.h" -#include "UtxoReservationManager.h" - -// XXX [AT] : possible concurent change of states - could lead to multiple signals emited -// add atomic flag - -static const char* WaitingPropertyName = "statusWarning"; -static const char* RepliedPropertyName = "statusSuccess"; -static const char* RejectedPropertyName = "statusImportant"; - -RequestingQuoteWidget::RequestingQuoteWidget(QWidget* parent) - : QWidget(parent) - , ui_(new Ui::RequestingQuoteWidget()) - , requestTimer_(this) -{ - ui_->setupUi(this); - - ui_->labelQuoteValue->setText(tr("Waiting for quote...")); - ui_->labelDetails->clear(); - ui_->labelDetails->hide(); - ui_->labelQuoteValue->show(); - - ui_->pushButtonAccept->hide(); - ui_->labelHint->clear(); - ui_->labelHint->hide(); - - setupTimer(Indicative, QDateTime::currentDateTime().addSecs(29)); //TODO: receive end time from SettlementAdapter - - connect(ui_->pushButtonCancel, &QPushButton::clicked, this, &RequestingQuoteWidget::onCancel); - connect(ui_->pushButtonAccept, &QPushButton::clicked, this, &RequestingQuoteWidget::onAccept); -} - -RequestingQuoteWidget::~RequestingQuoteWidget() = default; - -void RequestingQuoteWidget::SetCelerClient(const std::shared_ptr &celerClient) -{ - celerClient_ = celerClient; - - connect(celerClient_.get(), &CelerClientQt::OnConnectionClosed, - this, &RequestingQuoteWidget::onCelerDisconnected); -} - -void RequestingQuoteWidget::setupTimer(RequestingQuoteWidget::Status status, const QDateTime &expTime) -{ - ui_->pushButtonAccept->setEnabled(status == Tradeable); - - timeoutReply_ = expTime; - - ui_->progressBar->setMaximum(QDateTime::currentDateTime().msecsTo(timeoutReply_)); - ui_->progressBar->setValue(ui_->progressBar->maximum()); - ticker(); - - requestTimer_.setInterval(500); - connect(&requestTimer_, &QTimer::timeout, this, &RequestingQuoteWidget::ticker); - requestTimer_.start(); -} - -void RequestingQuoteWidget::onCancel() -{ - requestTimer_.stop(); - if (quote_.quotingType == bs::network::Quote::Tradeable) { - emit requestTimedOut(); - } else { - emit cancelRFQ(); - } -} - -void RequestingQuoteWidget::ticker() -{ - auto timeDiff = QDateTime::currentDateTime().msecsTo(timeoutReply_); - - if (timeDiff < -5000) { - requestTimer_.stop(); - emit requestTimedOut(); - } - else { - ui_->progressBar->setValue(timeDiff); - ui_->progressBar->setFormat(tr("%1 second(s) remaining") - .arg(QString::number(timeDiff > 0 ? timeDiff/1000 : 0))); - - ui_->labelTimeLeft->setText(tr("%1 second(s) remaining") - .arg(QString::number(timeDiff > 0 ? timeDiff/1000 : 0))); - } -} - -void RequestingQuoteWidget::onOrderFilled(const std::string "eId) -{ - if (quote_.quoteId == quoteId) { - emit quoteFinished(); - } -} - -void RequestingQuoteWidget::onOrderFailed(const std::string& quoteId, const std::string &reason) -{ - Q_UNUSED(reason); - if (quote_.quoteId == quoteId) { - emit quoteFailed(); - } -} - -bool RequestingQuoteWidget::onQuoteReceived(const bs::network::Quote& quote) -{ - if (quote.requestId != rfq_.requestId) { - return false; - } - - quote_ = quote; - if (quote_.product.empty()) { - quote_.product = rfq_.product; - } - - if (quote_.quotingType == bs::network::Quote::Tradeable) { - if (!balanceOk_) { - return false; - } - - if (quote.assetType == bs::network::Asset::SpotFX - || quote.assetType == bs::network::Asset::DeliverableFutures) { - ui_->pushButtonAccept->show(); - setupTimer(Tradeable, quote.expirationTime.addMSecs(quote.timeSkewMs)); - } else { - onAccept(); - } - - return true; - } - - if (rfq_.side == bs::network::Side::Buy && rfq_.assetType != bs::network::Asset::SpotFX) { - double amount = 0; - if (rfq_.assetType == bs::network::Asset::PrivateMarket) { - amount = rfq_.quantity * quote_.price; - } - else if (rfq_.product != bs::network::XbtCurrency) { - amount = rfq_.quantity / quote_.price; - } - } - - timeoutReply_ = quote.expirationTime.addMSecs(quote.timeSkewMs); - - ui_->labelQuoteValue->setText(UiUtils::displayPriceForAssetType(quote.price - , quote.assetType)); - ui_->labelQuoteValue->show(); - - if (quote.assetType == bs::network::Asset::SpotFX) { - ui_->labelHint->clear(); - ui_->labelHint->hide(); - } - - double value = rfq_.quantity * quote.price; - - QString productString = QString::fromStdString(rfq_.product); - QString productAmountString = UiUtils::displayAmountForProduct(rfq_.quantity, productString, rfq_.assetType); - - QString contrProductString; - QString valueString; - - CurrencyPair cp(rfq_.security); - - if (cp.NumCurrency() != rfq_.product) { - value = rfq_.quantity / quote.price; - contrProductString = QString::fromStdString(cp.NumCurrency()); - } else { - contrProductString = QString::fromStdString(cp.DenomCurrency()); - } - - valueString = UiUtils::displayAmountForProduct(value, contrProductString, rfq_.assetType); - - if (rfq_.side == bs::network::Side::Buy) { - const auto currency = contrProductString.toStdString(); - double balance = 0; - if (assetManager_) { - balance = assetManager_->getBalance(currency, bs::UTXOReservationManager::kIncludeZcRequestor, nullptr); - } - else { - try { - balance = balances_.at(currency); - } - catch (const std::exception&) {} - } - balanceOk_ = (value < balance); - ui_->pushButtonAccept->setEnabled(balanceOk_); - if (!balanceOk_) { - ui_->labelHint->setText(tr("Insufficient balance")); - ui_->labelHint->show(); - } - } - - if (rfq_.side == bs::network::Side::Buy && !balanceOk_) { - return true; - } - - ui_->labelDetails->setText(tr("%1 %2 %3\n%4 %5 %6") - .arg((rfq_.side == bs::network::Side::Buy) ? tr("Receive") : tr("Deliver")) - .arg(productAmountString) - .arg(productString) - .arg((rfq_.side == bs::network::Side::Buy) ? tr("Deliver") : tr("Receive")) - .arg(valueString) - .arg(contrProductString)); - ui_->labelDetails->show(); - - return true; -} - -void RequestingQuoteWidget::onQuoteCancelled(const QString &reqId, bool byUser) -{ - if (!byUser && (reqId.toStdString() == rfq_.requestId)) { - quote_ = bs::network::Quote(); - ui_->labelQuoteValue->setText(tr("Waiting for quote...")); - ui_->labelDetails->clear(); - ui_->labelDetails->hide(); - } -} - -void RequestingQuoteWidget::onReject(const QString &reqId, const QString &reason) -{ - if (reqId.toStdString() == rfq_.requestId) { - ui_->pushButtonAccept->setEnabled(false); - ui_->labelQuoteValue->setText(tr("Rejected: %1").arg(reason)); - ui_->labelQuoteValue->show(); - } -} - -void RequestingQuoteWidget::onCelerDisconnected() -{ - onCancel(); -} - -void RequestingQuoteWidget::populateDetails(const bs::network::RFQ& rfq) -{ - rfq_ = rfq; - - ui_->labelProductGroup->setText(tr(bs::network::Asset::toString(rfq.assetType))); - ui_->labelSecurityId->setText(QString::fromStdString(rfq.security)); - ui_->labelProduct->setText(QString::fromStdString(rfq.product)); - ui_->labelSide->setText(tr(bs::network::Side::toString(rfq.side))); - - switch (rfq.assetType) { - case bs::network::Asset::SpotFX: - ui_->labelQuantity->setText(UiUtils::displayCurrencyAmount(rfq.quantity)); - break; - case bs::network::Asset::SpotXBT: - ui_->labelQuantity->setText(UiUtils::displayQty(rfq.quantity, rfq.product)); - break; - case bs::network::Asset::PrivateMarket: - ui_->labelQuantity->setText(UiUtils::displayCCAmount(rfq.quantity)); - break; - default: break; - } -} - -void RequestingQuoteWidget::onBalance(const std::string& currency, double balance) -{ - balances_[currency] = balance; -} - -void RequestingQuoteWidget::onMatchingLogout() -{ - onCancel(); -} - -void RequestingQuoteWidget::onAccept() -{ - requestTimer_.stop(); - ui_->progressBar->hide(); - ui_->pushButtonAccept->setEnabled(false); - if (bs::network::Asset::SpotXBT == rfq_.assetType) { - ui_->labelHint->setText(tr("Awaiting Settlement Pay-Out Execution")); - ui_->labelHint->show(); - } - ui_->labelTimeLeft->clear(); - - emit quoteAccepted(rfq_.requestId, quote_); -} - -void RequestingQuoteWidget::SetQuoteDetailsState(QuoteDetailsState state) -{ - ui_->widgetQuoteDetails->setProperty(WaitingPropertyName, false); - ui_->widgetQuoteDetails->setProperty(RepliedPropertyName, false); - ui_->widgetQuoteDetails->setProperty(RejectedPropertyName, false); - - switch (state) - { - case Waiting: - ui_->widgetQuoteDetails->setProperty(WaitingPropertyName, true); - break; - case Replied: - ui_->widgetQuoteDetails->setProperty(RepliedPropertyName, true); - break; - case Rejected: - ui_->widgetQuoteDetails->setProperty(RejectedPropertyName, true); - break; - } - - ui_->widgetQuoteDetails->style()->unpolish(ui_->widgetQuoteDetails); - ui_->widgetQuoteDetails->style()->polish(ui_->widgetQuoteDetails); -} diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.h b/BlockSettleUILib/Trading/RequestingQuoteWidget.h deleted file mode 100644 index 1fb623e36..000000000 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __REQUESTING_QUOTE_WIDGET_H__ -#define __REQUESTING_QUOTE_WIDGET_H__ - -#include -#include -#include -#include -#include "CommonTypes.h" - - -namespace Ui { - class RequestingQuoteWidget; -} -class AssetManager; -class CelerClientQt; - -class RequestingQuoteWidget : public QWidget -{ -Q_OBJECT - -public: - RequestingQuoteWidget(QWidget* parent = nullptr ); - ~RequestingQuoteWidget() override; - - [[deprecated]] void SetAssetManager(const std::shared_ptr &assetManager) { - assetManager_ = assetManager; - } - [[deprecated]] void SetCelerClient(const std::shared_ptr&); - - void populateDetails(const bs::network::RFQ& rfq); - - void onBalance(const std::string& currency, double balance); - void onMatchingLogout(); - -public slots: - void ticker(); - bool onQuoteReceived(const bs::network::Quote& quote); - void onQuoteCancelled(const QString &reqId, bool byUser); - void onOrderFilled(const std::string "eId); - void onOrderFailed(const std::string& quoteId, const std::string& reason); - void onReject(const QString &reqId, const QString &reason); - void onCelerDisconnected(); - -signals: - void cancelRFQ(); - void requestTimedOut(); - void quoteAccepted(const std::string &reqId, const bs::network::Quote& quote); - void quoteFinished(); - void quoteFailed(); - -private: - enum Status { - Indicative, - Tradeable - }; - - enum QuoteDetailsState - { - Waiting, - Rejected, - Replied - }; - - void SetQuoteDetailsState(QuoteDetailsState state); - -private: - std::unique_ptr ui_; - QTimer requestTimer_; - QDateTime timeoutReply_; - bs::network::RFQ rfq_; - bs::network::Quote quote_; - std::shared_ptr assetManager_; - bool balanceOk_ = true; - std::shared_ptr celerClient_; - std::unordered_map balances_; - -private: - void setupTimer(Status status, const QDateTime &expTime); - void onCancel(); - void onAccept(); -}; - -#endif // __REQUESTING_QUOTE_WIDGET_H__ diff --git a/BlockSettleUILib/Trading/RequestingQuoteWidget.ui b/BlockSettleUILib/Trading/RequestingQuoteWidget.ui deleted file mode 100644 index ae224c9ff..000000000 --- a/BlockSettleUILib/Trading/RequestingQuoteWidget.ui +++ /dev/null @@ -1,691 +0,0 @@ - - - - RequestingQuoteWidget - - - - 0 - 0 - 340 - 420 - - - - - 0 - 0 - - - - - 340 - 420 - - - - Form - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 100 - - - - - - - - 0 - 0 - - - - REQUEST OVERVIEW - - - true - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - Product Group - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - TextLabel - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - Security ID - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - TextLabel - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - Product - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - TextLabel - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Side - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - TextLabel - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - Quantity - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - TextLabel - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - - - 0 - 0 - - - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - - 0 - 46 - - - - true - - - - 0 - - - 20 - - - 5 - - - 20 - - - 5 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - 75 - true - - - - - - - Awaiting Quote... - - - Qt::AlignCenter - - - 0 - - - true - - - - - - - - 0 - 0 - - - - Deliver 1.0 EUR -Receive 10.28 SEK - - - Qt::AlignCenter - - - true - - - false - - - - - - - As the request is priced, the most competitive quote will be displayed. - - - Qt::AlignCenter - - - true - - - true - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 7 - - - - - 16777215 - 7 - - - - 24 - - - Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing - - - false - - - false - - - - - - - - - - Qt::AlignCenter - - - - - - - - 0 - 1 - - - - - 16777215 - 1 - - - - Qt::Horizontal - - - - - - - - - - - - - - 0 - 0 - - - - true - - - - 10 - - - 10 - - - 10 - - - 10 - - - 10 - - - - - - 80 - 0 - - - - - - - Cancel - - - - - - - false - - - - 0 - 0 - - - - - 80 - 0 - - - - false - - - - - - Accept - - - true - - - - - - - - - - - diff --git a/BlockSettleUILib/Trading/RfqStorage.cpp b/BlockSettleUILib/Trading/RfqStorage.cpp deleted file mode 100644 index 00a65a867..000000000 --- a/BlockSettleUILib/Trading/RfqStorage.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "RfqStorage.h" - -#include "SettlementContainer.h" - -RfqStorage::RfqStorage() = default; - -RfqStorage::~RfqStorage() = default; - -void RfqStorage::addSettlementContainer(std::shared_ptr rfq) -{ - const auto id = rfq->id(); - - auto deleteCb = [this, handle = validityFlag_.handle(), id] { - if (!handle.isValid()) { - return; - } - auto it = rfqs_.find(id); - if (it == rfqs_.end()) { - return; - } - it->second->deactivate(); - rfqs_.erase(it); - }; - - // Use QueuedConnection so SettlementContainer is destroyed later - QObject::connect(rfq.get(), &bs::SettlementContainer::completed, this, deleteCb, Qt::QueuedConnection); - QObject::connect(rfq.get(), &bs::SettlementContainer::failed, this, deleteCb, Qt::QueuedConnection); - QObject::connect(rfq.get(), &bs::SettlementContainer::timerExpired, this, deleteCb, Qt::QueuedConnection); - - rfqs_[rfq->id()] = std::move(rfq); -} - -bs::SettlementContainer *RfqStorage::settlementContainer(const std::string &id) const -{ - auto it = rfqs_.find(id); - if (it == rfqs_.end()) { - return nullptr; - } - return it->second.get(); -} diff --git a/BlockSettleUILib/Trading/RfqStorage.h b/BlockSettleUILib/Trading/RfqStorage.h deleted file mode 100644 index 01bc73d11..000000000 --- a/BlockSettleUILib/Trading/RfqStorage.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef RFQ_STORAGE_H -#define RFQ_STORAGE_H - -#include -#include -#include - -#include "ValidityFlag.h" - -namespace bs { - class SettlementContainer; -} - -// Use to store and release memory for bs::SettlementContainer -class RfqStorage : public QObject -{ - Q_OBJECT - -public: - RfqStorage(); - ~RfqStorage(); - - void addSettlementContainer(std::shared_ptr rfq); - - bs::SettlementContainer *settlementContainer(const std::string &id) const; - -private: - std::unordered_map> rfqs_; - - ValidityFlag validityFlag_; - -}; - -#endif diff --git a/BlockSettleUILib/Trading/SettlementContainer.cpp b/BlockSettleUILib/Trading/SettlementContainer.cpp deleted file mode 100644 index 45c38da56..000000000 --- a/BlockSettleUILib/Trading/SettlementContainer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "SettlementContainer.h" -#include "UiUtils.h" - -using namespace bs; -using namespace bs::sync; - -namespace { - - const auto kUtxoReleaseDelay = std::chrono::seconds(15); - -} // namespace - -SettlementContainer::SettlementContainer(UtxoReservationToken utxoRes - , bs::hd::Purpose walletPurpose, bool expandTxDialogInfo) - : QObject(nullptr) - , utxoRes_(std::move(utxoRes)) - , walletPurpose_(walletPurpose) - , expandTxDialogInfo_(expandTxDialogInfo) -{} - -SettlementContainer::~SettlementContainer() -{ - if (utxoRes_.isValid()) { - QTimer::singleShot(kUtxoReleaseDelay, [utxoRes = std::move(utxoRes_)] () mutable { - utxoRes.release(); - }); - } -} - -sync::PasswordDialogData SettlementContainer::toPasswordDialogData(QDateTime timestamp) const -{ - bs::sync::PasswordDialogData info; - - info.setValue(PasswordDialogData::SettlementId, id()); - info.setValue(PasswordDialogData::DurationLeft, durationMs()); - info.setValue(PasswordDialogData::DurationTotal, (int)kWaitTimeoutInSec * 1000); - - // Set timestamp that will be used by auth eid server to update timers. - info.setValue(PasswordDialogData::DurationTimestamp, static_cast(timestamp.toSecsSinceEpoch())); - - info.setValue(PasswordDialogData::ProductGroup, tr(bs::network::Asset::toString(assetType()))); - info.setValue(PasswordDialogData::Security, security()); - info.setValue(PasswordDialogData::Product, product()); - info.setValue(PasswordDialogData::Side, tr(bs::network::Side::toString(side()))); - info.setValue(PasswordDialogData::ExpandTxInfo, expandTxDialogInfo_); - - return info; -} - -sync::PasswordDialogData SettlementContainer::toPayOutTxDetailsPasswordDialogData(core::wallet::TXSignRequest payOutReq - , QDateTime timestamp) const -{ - bs::sync::PasswordDialogData dialogData = toPasswordDialogData(timestamp); - - dialogData.setValue(PasswordDialogData::Title, tr("Settlement Pay-Out")); - dialogData.setValue(PasswordDialogData::DurationLeft, static_cast(kWaitTimeoutInSec * 1000)); - dialogData.setValue(PasswordDialogData::DurationTotal, static_cast(kWaitTimeoutInSec * 1000)); - dialogData.setValue(PasswordDialogData::SettlementPayOutVisible, true); - - return dialogData; -} - -void SettlementContainer::startTimer(const unsigned int durationSeconds) -{ - timer_.stop(); - timer_.setInterval(250); - msDuration_ = durationSeconds * 1000; - startTime_ = std::chrono::steady_clock::now(); - - connect(&timer_, &QTimer::timeout, [this] { - const auto timeDiff = std::chrono::duration_cast(std::chrono::steady_clock::now() - startTime_); - msTimeLeft_ = msDuration_ - timeDiff.count(); - if (msTimeLeft_ < 0) { - timer_.stop(); - msDuration_ = 0; - msTimeLeft_ = 0; - emit timerExpired(); - } - }); - timer_.start(); - emit timerStarted(msDuration_); -} - -void SettlementContainer::stopTimer() -{ - msDuration_ = 0; - msTimeLeft_ = 0; - timer_.stop(); - emit timerStopped(); -} - -void SettlementContainer::releaseUtxoRes() -{ - utxoRes_.release(); -} diff --git a/BlockSettleUILib/Trading/SettlementContainer.h b/BlockSettleUILib/Trading/SettlementContainer.h deleted file mode 100644 index 673e3a28f..000000000 --- a/BlockSettleUILib/Trading/SettlementContainer.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __SETTLEMENT_CONTAINER_H__ -#define __SETTLEMENT_CONTAINER_H__ - -#include -#include -#include -#include - -#include "ArmoryConnection.h" -#include "CommonTypes.h" -#include "CoreWallet.h" -#include "EncryptionUtils.h" -#include "PasswordDialogData.h" -#include "UtxoReservationToken.h" -#include "ValidityFlag.h" -#include "BSErrorCode.h" - -namespace bs { - - class SettlementContainer : public QObject - { - Q_OBJECT - public: - explicit SettlementContainer(bs::UtxoReservationToken - , bs::hd::Purpose walletPurpose - , bool expandTxDialogInfo); - ~SettlementContainer() override; - - virtual bool cancel() = 0; - - virtual void activate() = 0; - virtual void deactivate() = 0; - - virtual std::string id() const = 0; - virtual bs::network::Asset::Type assetType() const = 0; - virtual std::string security() const = 0; - virtual std::string product() const = 0; - virtual bs::network::Side::Type side() const = 0; - virtual double quantity() const = 0; - virtual double price() const = 0; - virtual double amount() const = 0; - - int durationMs() const { return msDuration_; } - int timeLeftMs() const { return msTimeLeft_; } - - virtual bs::sync::PasswordDialogData toPasswordDialogData(QDateTime timestamp) const; - virtual bs::sync::PasswordDialogData toPayOutTxDetailsPasswordDialogData(bs::core::wallet::TXSignRequest payOutReq - , QDateTime timestamp) const; - - static constexpr unsigned int kWaitTimeoutInSec = 30; - - signals: - void error(const std::string &id, bs::error::ErrorCode, QString); - - void completed(const std::string &id); - void failed(const std::string &id); - - void timerExpired(); - void timerStarted(int msDuration); - void timerStopped(); - - protected slots: - void startTimer(const unsigned int durationSeconds); - void stopTimer(); - - protected: - void releaseUtxoRes(); - - ValidityFlag validityFlag_; - bs::UtxoReservationToken utxoRes_; - const bs::hd::Purpose walletPurpose_; - bool expandTxDialogInfo_{}; - - private: - QTimer timer_; - int msDuration_ = 0; - int msTimeLeft_ = 0; - std::chrono::steady_clock::time_point startTime_; - }; - -} // namespace bs - -#endif // __SETTLEMENT_CONTAINER_H__ diff --git a/BlockSettleUILib/Trading/WalletShieldBase.cpp b/BlockSettleUILib/Trading/WalletShieldBase.cpp deleted file mode 100644 index 6d38d6069..000000000 --- a/BlockSettleUILib/Trading/WalletShieldBase.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2021, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "WalletShieldBase.h" - -#include - -#include "AuthAddressManager.h" -#include "Wallets/SyncHDWallet.h" -#include "Wallets/SyncWalletsManager.h" -#include "ApplicationSettings.h" - -#include "ui_WalletShieldPage.h" - - -namespace { - // Label texts - const QString shieldCreateCCWallet = QObject::tr("Create %1 wallet"); - const QString shieldCreateAuthLeaf = QObject::tr("Create Authentication wallet"); - const QString shieldGenerateAuthAddress = QObject::tr("Generate an Authentication Address"); - - const QString shieldCreateWallet = QObject::tr("To %1 in XBT related products, you require a wallet"); - const QString shieldPromoteToPrimary = QObject::tr("To %1 in XBT related products, you're required to have a wallet which" \ - " can contain the paths required for correctly sorting your tokens and holding" \ - " your Authentication Address(es)"); - - const QString shieldAuthValidationProcessHeader = QObject::tr("Authentication Address Validation Process"); - const QString shieldAuthValidationProcessText = QObject::tr("Your Authentication Address has been submitted.\n\n" - "BlockSettle validates the public key against the UserID and executes a transaction from it's Validation Address sometime within a %1 cycle.\n\n" - "Once executed the Authentication Address need 6 blockchain confirmations to be considered as valid" - ); - - // Button texts - const QString shieldButtonPromote = QObject::tr("Promote"); - const QString shieldButtonCreate = QObject::tr("Create"); - const QString shieldButtonGenerate = QObject::tr("Generate"); - const QString shieldButtonView = QObject::tr("View"); -} - -WalletShieldBase::WalletShieldBase(QWidget *parent) : - QWidget(parent) , - ui_(new Ui::WalletShieldPage()) -{ - ui_->setupUi(this); -} - -WalletShieldBase::~WalletShieldBase() noexcept = default; - -void WalletShieldBase::setShieldButtonAction(std::function&& action, bool oneShot) -{ - ui_->shieldButton->disconnect(); - connect(ui_->shieldButton, &QPushButton::clicked, this, [act = std::move(action), this, oneShot]() { - if (oneShot) { - ui_->shieldButton->setDisabled(true); - } - act(); - }); -} - -void WalletShieldBase::init(const std::shared_ptr &walletsManager, - const std::shared_ptr &authMgr, const std::shared_ptr &appSettings) -{ - walletsManager_ = walletsManager; - authMgr_ = authMgr; - appSettings_ = appSettings; -} - -void WalletShieldBase::setTabType(QString&& tabType) -{ - tabType_ = std::move(tabType); -} - -bool WalletShieldBase::checkWalletSettings(WalletShieldBase::ProductType productType - , const QString& product) -{ - if (!walletsManager_ && !authMgr_) { - return false; // Temporary workaround for new arch - fix later if needed - } - // No primary wallet - bool hasFullWallet = false; - if (walletsManager_) { - for (const auto &wallet : walletsManager_->hdWallets()) { - if (wallet->isFullWallet()) { - hasFullWallet = true; - break; - } - } - } - - if (!walletsManager_ || (!walletsManager_->hasPrimaryWallet() && !hasFullWallet)) { - showShieldCreateWallet(); - setShieldButtonAction([this]() { - emit requestPrimaryWalletCreation(); - }); - return true; - } - - if (!walletsManager_->hasPrimaryWallet()) { - assert(hasFullWallet); - showShieldPromoteToPrimaryWallet(); - setShieldButtonAction([this]() { - emit requestPrimaryWalletCreation(); - }); - return true; - } - - if (productType == ProductType::SpotXBT) { - if (walletsManager_->getAuthWallet()) { - const bool isNoVerifiedAddresses = authMgr_->GetSubmittedAddressList().empty(); - if (isNoVerifiedAddresses && authMgr_->isAtLeastOneAwaitingVerification()) - { - showShieldAuthValidationProcess(); - setShieldButtonAction([this]() { - emit authMgr_->AuthWalletCreated({}); - }); - return true; - } - else if (isNoVerifiedAddresses) { - showShieldGenerateAuthAddress(); - setShieldButtonAction([this]() { - emit authMgr_->AuthWalletCreated({}); - }); - return true; - } - } - else { - showShield(shieldCreateAuthLeaf, shieldButtonCreate); - setShieldButtonAction([this]() { - walletsManager_->createAuthLeaf(nullptr); - }); - return true; - } - } else if (!walletsManager_->getCCWallet(product.toStdString())) { - showShieldCreateLeaf(product); - setShieldButtonAction([this, product]() { - walletsManager_->CreateCCLeaf(product.toStdString()); - }); - return true; - } - - return false; -} - -WalletShieldBase::ProductType WalletShieldBase::getProductGroup(const QString &productGroup) -{ - if (productGroup == QLatin1String("Private Market")) { - return ProductType::PrivateMarket; - } - else if (productGroup == QLatin1String("Spot XBT")) { - return ProductType::SpotXBT; - } - else if (productGroup == QLatin1String("Spot FX")) { - return ProductType::SpotFX; - } - else if (productGroup == QLatin1String("XBT 1-day deliverable")) { - return ProductType::DeliverableFutures; - } - else if (productGroup == QLatin1String("XBT 1-day rolling")) { - return ProductType::CashSettledFutures; - } -#ifndef QT_NO_DEBUG - // You need to add logic for new Product group type - Q_ASSERT(false); -#endif - return ProductType::Undefined; -} - -void WalletShieldBase::showShield(const QString& labelText, - const QString& buttonText /*= QLatin1String()*/, const QString& headerText /*= QLatin1String()*/) -{ - ui_->shieldText->setText(labelText); - - const bool isShowButton = !buttonText.isEmpty(); - ui_->shieldButton->setVisible(isShowButton); - ui_->shieldButton->setEnabled(isShowButton); - ui_->shieldButton->setText(buttonText); - - ui_->shieldHeaderText->setVisible(!headerText.isEmpty()); - ui_->shieldHeaderText->setText(headerText); - - ui_->secondInfoBlock->hide(); - ui_->thirdInfoBlock->hide(); - - raiseInStack(); -} - -void WalletShieldBase::showTwoBlockShield(const QString& headerText1, const QString& labelText1, - const QString& headerText2, const QString& labelText2) -{ - ui_->shieldButton->setVisible(false); - - ui_->shieldHeaderText->setText(headerText1); - ui_->shieldText->setText(labelText1); - ui_->shieldHeaderText->setVisible(true); - - ui_->shieldHeaderTextSecond->setText(headerText2); - ui_->shieldTextSecond->setText(labelText2); - ui_->secondInfoBlock->setVisible(true); - - ui_->thirdInfoBlock->hide(); - raiseInStack(); -} - -void WalletShieldBase::showThreeBlockShield(const QString& headerText1, const QString& labelText1, - const QString& headerText2, const QString& labelText2, - const QString& headerText3, const QString& labelText3) -{ - ui_->shieldButton->setVisible(false); - - ui_->shieldHeaderText->setText(headerText1); - ui_->shieldText->setText(labelText1); - ui_->shieldHeaderText->setVisible(true); - - ui_->shieldHeaderTextSecond->setText(headerText2); - ui_->shieldTextSecond->setText(labelText2); - ui_->secondInfoBlock->setVisible(true); - - ui_->shieldHeaderTextThird->setText(headerText3); - ui_->shieldTextThird->setText(labelText3); - ui_->thirdInfoBlock->setVisible(true); - - raiseInStack(); -} - -void WalletShieldBase::raiseInStack() -{ - QStackedWidget* stack = qobject_cast(parent()); - - // We expected that shield widget will leave only under stack widget - assert(stack); - if (!stack) { - return; - } - - stack->setCurrentWidget(this); -} - -void WalletShieldBase::showShieldPromoteToPrimaryWallet() -{ - showShield(shieldPromoteToPrimary.arg(tabType_), shieldButtonPromote); -} - -void WalletShieldBase::showShieldCreateWallet() -{ - showShield(shieldCreateWallet.arg(tabType_), shieldButtonCreate); -} - -void WalletShieldBase::showShieldCreateLeaf(const QString& product) -{ - showShield(shieldCreateCCWallet.arg(product), shieldButtonCreate); -} - -void WalletShieldBase::showShieldGenerateAuthAddress() -{ - showShield(shieldGenerateAuthAddress, shieldButtonGenerate); -} - -void WalletShieldBase::showShieldAuthValidationProcess() -{ - const bool isProd = appSettings_->get(ApplicationSettings::envConfiguration) == - static_cast(ApplicationSettings::EnvConfiguration::Production); - - showShield(shieldAuthValidationProcessText.arg(isProd ? tr("24h") : tr("15 minutes")), - shieldButtonView, shieldAuthValidationProcessHeader); -} diff --git a/BlockSettleUILib/Trading/WalletShieldBase.h b/BlockSettleUILib/Trading/WalletShieldBase.h deleted file mode 100644 index a4c9c7b0a..000000000 --- a/BlockSettleUILib/Trading/WalletShieldBase.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2019 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef WALLETSHIELDBASE_H -#define WALLETSHIELDBASE_H - -#include -#include -#include -#include "CommonTypes.h" - -namespace Ui { - class WalletShieldPage; -} -class AuthAddressManager; -namespace bs { - namespace sync { - class WalletsManager; - } -} -class ApplicationSettings; - -class WalletShieldBase : public QWidget -{ - Q_OBJECT - -public: - explicit WalletShieldBase(QWidget *parent = nullptr); - virtual ~WalletShieldBase() noexcept; - - void setShieldButtonAction(std::function&& action, bool oneShot = true); - - [[deprecated]] void init(const std::shared_ptr &walletsManager - , const std::shared_ptr &authMgr - , const std::shared_ptr &appSettings); - - using ProductType = bs::network::Asset::Type; - bool checkWalletSettings(ProductType productType, const QString &product); - static ProductType getProductGroup(const QString &productGroup); - void setTabType(QString&& tabType); - -signals: - void requestPrimaryWalletCreation(); - void loginRequested(); - -protected: - void showShield(const QString& labelText, - const QString& ButtonText = QLatin1String(), const QString& headerText = QLatin1String()); - - void showTwoBlockShield(const QString& headerText1, const QString& labelText1, - const QString& headerText2, const QString& labelText2); - - void showThreeBlockShield(const QString& headerText1, const QString& labelText1, - const QString& headerText2, const QString& labelText2, - const QString& headerText3, const QString& labelText3); - - void raiseInStack(); - - void showShieldPromoteToPrimaryWallet(); - void showShieldCreateWallet(); - void showShieldCreateLeaf(const QString& product); - void showShieldGenerateAuthAddress(); - void showShieldAuthValidationProcess(); - -protected: - std::unique_ptr ui_; - std::shared_ptr walletsManager_; - std::shared_ptr authMgr_; - std::shared_ptr appSettings_; - - QString tabType_; -}; - -#endif // WALLETSHIELDBASE_H diff --git a/BlockSettleUILib/Trading/WalletShieldPage.ui b/BlockSettleUILib/Trading/WalletShieldPage.ui deleted file mode 100644 index 3841218b0..000000000 --- a/BlockSettleUILib/Trading/WalletShieldPage.ui +++ /dev/null @@ -1,422 +0,0 @@ - - - - WalletShieldPage - - - - 0 - 0 - 402 - 406 - - - - Form - - - - - - Qt::Vertical - - - - 361 - 94 - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 10 - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - - 10 - 75 - true - - - - false - - - Header Text - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - false - - - <First block body text> - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 10 - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - - 10 - 75 - true - - - - false - - - Header Text - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - true - - - <Second block body text> - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 10 - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - - 10 - 75 - true - - - - false - - - Header Text - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - - 0 - 25 - - - - - 16777215 - 16777215 - - - - true - - - <Third block body text> - - - Qt::AlignCenter - - - true - - - true - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 100 - 25 - - - - - 100 - 35 - - - - <Unknown> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 361 - 94 - - - - - - - - - diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index ff0ee0a3b..13e4a0ded 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -122,6 +122,14 @@ namespace UiUtils { return UnifyValueString(QLocale().toString(amountToBtc(value), 'f', GetAmountPrecisionXBT())); } + template <> QString displayAmount(int value) + { + if (value == INT_MAX) { + return CommonUiUtilsText::tr("Loading..."); + } + return UnifyValueString(QLocale().toString(amountToBtc(value), 'f', GetAmountPrecisionXBT())); + } + QString displayAmount(const bs::XBTAmount &amount) { if (amount.isZero()) { @@ -518,7 +526,7 @@ double UiUtils::truncatePriceForAsset(double price, bs::network::Asset::Type at) multiplier = 10000; break; case bs::network::Asset::SpotXBT: - case bs::network::Asset::DeliverableFutures: + case bs::network::Asset::Future: multiplier = 100; break; case bs::network::Asset::PrivateMarket: @@ -538,8 +546,7 @@ QString UiUtils::displayPriceForAssetType(double price, bs::network::Asset::Type case bs::network::Asset::SpotFX: return UiUtils::displayPriceFX(price); case bs::network::Asset::SpotXBT: - case bs::network::Asset::DeliverableFutures: - case bs::network::Asset::CashSettledFutures: + case bs::network::Asset::Future: return UiUtils::displayPriceXBT(price); case bs::network::Asset::PrivateMarket: return UiUtils::displayPriceCC(price); @@ -572,8 +579,7 @@ int UiUtils::GetPricePrecisionForAssetType(const bs::network::Asset::Type& asset case bs::network::Asset::SpotFX: return GetPricePrecisionFX(); case bs::network::Asset::SpotXBT: - case bs::network::Asset::DeliverableFutures: - case bs::network::Asset::CashSettledFutures: + case bs::network::Asset::Future: return GetPricePrecisionXBT(); case bs::network::Asset::PrivateMarket: return GetPricePrecisionCC(); @@ -607,8 +613,7 @@ static void getPrecsFor(const std::string &security, const std::string &product, valuePrec = UiUtils::GetAmountPrecisionFX(); break; case bs::network::Asset::Type::SpotXBT: - case bs::network::Asset::Type::DeliverableFutures: - case bs::network::Asset::Type::CashSettledFutures: + case bs::network::Asset::Type::Future: qtyPrec = UiUtils::GetAmountPrecisionXBT(); valuePrec = UiUtils::GetAmountPrecisionFX(); @@ -807,7 +812,7 @@ ApplicationSettings::Setting UiUtils::limitRfqSetting(bs::network::Asset::Type t case bs::network::Asset::PrivateMarket : return ApplicationSettings::PmRfqLimit; - case bs::network::Asset::DeliverableFutures : + case bs::network::Asset::Future : return ApplicationSettings::FuturesLimit; default : @@ -825,7 +830,7 @@ ApplicationSettings::Setting UiUtils::limitRfqSetting(const QString &name) } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::PrivateMarket))) { return ApplicationSettings::PmRfqLimit; - } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures))) { + } else if (name == QString::fromUtf8(bs::network::Asset::toString(bs::network::Asset::Future))) { return ApplicationSettings::FuturesLimit; } else { assert(false); @@ -846,7 +851,7 @@ QString UiUtils::marketNameForLimit(ApplicationSettings::Setting s) return QObject::tr(bs::network::Asset::toString(bs::network::Asset::PrivateMarket)); case ApplicationSettings::FuturesLimit : - return QObject::tr(bs::network::Asset::toString(bs::network::Asset::DeliverableFutures)); + return QObject::tr(bs::network::Asset::toString(bs::network::Asset::Future)); default : assert(false); diff --git a/BlockSettleUILib/UserScript.cpp b/BlockSettleUILib/UserScript.cpp deleted file mode 100644 index eb41f0002..000000000 --- a/BlockSettleUILib/UserScript.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#include "UserScript.h" - -#include -#include -#include -#include -#include -#include - -#include "AssetManager.h" -#include "CurrencyPair.h" -#include "DataConnection.h" -#include "MDCallbacksQt.h" -#include "UiUtils.h" -#include "Wallets/SyncWalletsManager.h" -#include "UtxoReservationManager.h" - -UserScript::UserScript(const std::shared_ptr &logger, - const std::shared_ptr &mdCallbacks, const ExtConnections &extConns - , QObject* parent) - : QObject(parent) - , logger_(logger) - , engine_(new QQmlEngine(this)) - , component_(nullptr) - , md_(mdCallbacks ? new MarketData(mdCallbacks, this) : nullptr) - , extConns_(extConns) - , const_(new Constants(this)) - , storage_(new DataStorage(this)) -{ - if (md_) { - engine_->rootContext()->setContextProperty(QLatin1String("marketData"), md_); - } - engine_->rootContext()->setContextProperty(QLatin1String("constants"), const_); - engine_->rootContext()->setContextProperty(QLatin1String("dataStorage"), storage_); -} - -UserScript::~UserScript() -{ - delete component_; - component_ = nullptr; -} - -bool UserScript::load(const QString &filename) -{ - if (component_) component_->deleteLater(); - component_ = new QQmlComponent(engine_, QUrl::fromLocalFile(filename) - , QQmlComponent::PreferSynchronous, this); - if (!component_) { - logger_->error("Failed to load component for file {}", filename.toStdString()); - emit failed(tr("Failed to load script %1").arg(filename)); - return false; - } - - if (component_->isReady()) { - emit loaded(); - return true; - } - if (component_->isError()) { - logger_->error("Failed to load {}: {}", filename.toStdString() - , component_->errorString().toStdString()); - emit failed(component_->errorString()); - } - return false; -} - -QObject *UserScript::instantiate() -{ - auto rv = component_->create(); - if (!rv) { - emit failed(tr("Failed to instantiate: %1").arg(component_->errorString())); - } - return rv; -} - -void UserScript::setWalletsManager(std::shared_ptr walletsManager) -{ - const_->setWalletsManager(walletsManager); -} - -bool UserScript::sendExtConn(const QString &name, const QString &type, const QString &message) -{ - const auto &itConn = extConns_.find(name.toStdString()); - if (itConn == extConns_.end()) { - logger_->error("[UserScript::sendExtConn] can't find external connector {}" - , name.toStdString()); - return false; - } - if (!itConn->second->isActive()) { - logger_->error("[UserScript::sendExtConn] external connector {} is not " - "active", name.toStdString()); - return false; - } - QJsonParseError jsonError; - auto jsonDoc = QJsonDocument::fromJson(QByteArray::fromStdString(message.toStdString()) - , &jsonError); - if (jsonError.error != QJsonParseError::NoError) { - logger_->error("[UserScript::sendExtConn] invalid JSON message: {}\n{}" - , jsonError.errorString().toUtf8().toStdString(), message.toStdString()); - return false; - } - const auto messageObj = jsonDoc.object(); - QJsonObject jsonEnvelope; - jsonEnvelope[QLatin1Literal("to")] = name; - jsonEnvelope[QLatin1Literal("type")] = type; - jsonEnvelope[QLatin1Literal("message")] = messageObj; - jsonDoc.setObject(jsonEnvelope); - const auto &msgJSON = jsonDoc.toJson(QJsonDocument::JsonFormat::Compact).toStdString(); - - return itConn->second->send(msgJSON); -} - - -// -// MarketData -// - -MarketData::MarketData(const std::shared_ptr &mdCallbacks, QObject *parent) - : QObject(parent) -{ - connect(mdCallbacks.get(), &MDCallbacksQt::MDUpdate, this, &MarketData::onMDUpdated, - Qt::QueuedConnection); -} - -double MarketData::bid(const QString &sec) const -{ - auto it = data_.find(sec); - - if (it != data_.cend()) { - auto dit = it->second.find(bs::network::MDField::PriceBid); - - if (dit != it->second.cend()) { - return dit->second; - } else { - return 0.0; - } - } else { - return 0.0; - } -} - -double MarketData::ask(const QString &sec) const -{ - auto it = data_.find(sec); - - if (it != data_.cend()) { - auto dit = it->second.find(bs::network::MDField::PriceOffer); - - if (dit != it->second.cend()) { - return dit->second; - } else { - return 0.0; - } - } else { - return 0.0; - } -} - -void MarketData::onMDUpdated(bs::network::Asset::Type assetType, const QString &security, - bs::network::MDFields data) -{ - if (bs::network::Asset::isFuturesType(assetType)) { - // ignore futures prices updates - return; - } - - for (const auto &field : data) { - data_[security][field.type] = field.value; - } -} - - -// -// DataStorage -// - -DataStorage::DataStorage(QObject *parent) - : QObject(parent) -{ -} - -double DataStorage::bought(const QString ¤cy) -{ - return std::accumulate(std::begin(bought_[currency]), std::end(bought_[currency]), - 0.0, - [] (double value, const std::map::value_type& p) - { return value + p.second; }); -} - -void DataStorage::setBought(const QString ¤cy, double v, const QString &id) -{ - bought_[currency][id] = v; -} - -double DataStorage::sold(const QString ¤cy) -{ - return std::accumulate(std::begin(sold_[currency]), std::end(sold_[currency]), - 0.0, - [] (double value, const std::map::value_type& p) - { return value + p.second; }); -} - -void DataStorage::setSold(const QString ¤cy, double v, const QString &id) -{ - sold_[currency][id] = v; -} - - -// -// Constants -// - -Constants::Constants(QObject *parent) - : QObject(parent) - , walletsManager_(nullptr) -{} - -int Constants::payInTxSize() const -{ - return 125; -} - -int Constants::payOutTxSize() const -{ - return 82; -} - -float Constants::feePerByte() -{ - if (walletsManager_) { - walletsManager_->estimatedFeePerByte(2, [this](float fee) { feePerByte_ = fee; }, this); - } - return feePerByte_; //NB: sometimes returns previous value if previous call needs to wait for result from Armory -} - -QString Constants::xbtProductName() const -{ - return UiUtils::XbtCurrency; -} - -void Constants::setWalletsManager(std::shared_ptr walletsManager) -{ - walletsManager_ = walletsManager; - walletsManager_->estimatedFeePerByte(2, [this](float fee) { feePerByte_ = fee; }, this); -} - - -AutoQuoter::AutoQuoter(const std::shared_ptr &logger - , const std::shared_ptr &assetManager - , const std::shared_ptr &mdCallbacks - , const ExtConnections &extConns, QObject* parent) - : UserScript(logger, mdCallbacks, extConns, parent) - , assetManager_(assetManager) -{ - qmlRegisterType("bs.terminal", 1, 0, "BSQuoteReqReply"); - qmlRegisterUncreatableType("bs.terminal", 1, 0, "BSQuoteRequest", tr("Can't create this type")); -} - -QObject *AutoQuoter::instantiate(const bs::network::QuoteReqNotification &qrn) -{ - QObject *rv = UserScript::instantiate(); - if (rv) { - BSQuoteReqReply *qrr = qobject_cast(rv); - qrr->init(logger_, assetManager_, this); - - BSQuoteRequest *qr = new BSQuoteRequest(rv); - qr->init(QString::fromStdString(qrn.quoteRequestId), QString::fromStdString(qrn.product) - , (qrn.side == bs::network::Side::Buy), qrn.quantity, static_cast(qrn.assetType)); - - qrr->setQuoteReq(qr); - qrr->setSecurity(QString::fromStdString(qrn.security)); - - connect(qrr, &BSQuoteReqReply::sendingQuoteReply, [this](const QString &reqId, double price) { - emit sendingQuoteReply(reqId, price); - }); - connect(qrr, &BSQuoteReqReply::pullingQuoteReply, [this](const QString &reqId) { - emit pullingQuoteReply(reqId); - }); - - qrr->start(); - } - return rv; -} - - -void BSQuoteRequest::init(const QString &reqId, const QString &product, bool buy, double qty, int at) -{ - requestId_ = reqId; - product_ = product; - isBuy_ = buy; - quantity_ = qty; - assetType_ = at; -} - -void BSQuoteReqReply::init(const std::shared_ptr &logger - , const std::shared_ptr &assetManager, UserScript *parent) -{ - logger_ = logger; - assetManager_ = assetManager; - parent_ = parent; -} - -void BSQuoteReqReply::log(const QString &s) -{ - logger_->info("[BSQuoteReqReply] {}", s.toStdString()); -} - -bool BSQuoteReqReply::sendQuoteReply(double price) -{ - QString reqId = quoteReq()->requestId(); - if (reqId.isEmpty()) return false; - emit sendingQuoteReply(reqId, price); - return true; -} - -bool BSQuoteReqReply::pullQuoteReply() -{ - QString reqId = quoteReq()->requestId(); - if (reqId.isEmpty()) return false; - emit pullingQuoteReply(reqId); - return true; -} - -QString BSQuoteReqReply::product() -{ - CurrencyPair cp(security().toStdString()); - return QString::fromStdString(cp.ContraCurrency(quoteReq()->product().toStdString())); -} - -double BSQuoteReqReply::accountBalance(const QString &product) -{ - return assetManager_->getBalance(product.toStdString(), bs::UTXOReservationManager::kIncludeZcRequestor, nullptr); -} - -bool BSQuoteReqReply::sendExtConn(const QString &name, const QString &type - , const QString &message) -{ - return parent_->sendExtConn(name, type, message); -} - - -void SubmitRFQ::stop() -{ - emit stopRFQ(id_.toStdString()); -} - - -void RFQScript::log(const QString &s) -{ - if (!logger_) { - return; - } - logger_->info("[RFQScript] {}", s.toStdString()); -} - -SubmitRFQ *RFQScript::sendRFQ(const QString &symbol, bool buy, double amount) -{ - const auto id = CryptoPRNG::generateRandom(8).toHexStr(); - const auto submitRFQ = new SubmitRFQ(this); - submitRFQ->setId(id); - submitRFQ->setSecurity(symbol); - submitRFQ->setAmount(amount); - submitRFQ->setBuy(buy); - - activeRFQs_[id] = submitRFQ; - emit sendingRFQ(submitRFQ); - return submitRFQ; -} - -void RFQScript::cancelRFQ(const std::string &id) -{ - const auto &itRFQ = activeRFQs_.find(id); - if (itRFQ == activeRFQs_.end()) { - logger_->error("[RFQScript::cancelRFQ] no active RFQ with id {}", id); - return; - } - emit cancellingRFQ(id); - itRFQ->second->deleteLater(); - activeRFQs_.erase(itRFQ); -} - -void RFQScript::cancelAll() -{ - for (const auto &rfq : activeRFQs_) { - emit cancellingRFQ(rfq.first); - rfq.second->deleteLater(); - } - activeRFQs_.clear(); -} - -void RFQScript::onMDUpdate(bs::network::Asset::Type, const QString &security, - bs::network::MDFields mdFields) -{ - if (!started_) { - return; - } - - auto &mdInfo = mdInfo_[security.toStdString()]; - mdInfo.merge(bs::network::MDField::get(mdFields)); - - for (const auto &rfq : activeRFQs_) { - const auto &sec = rfq.second->security(); - if (sec.isEmpty() || (security != sec)) { - continue; - } - if (mdInfo.bidPrice > 0) { - rfq.second->setIndicBid(mdInfo.bidPrice); - } - if (mdInfo.askPrice > 0) { - rfq.second->setIndicAsk(mdInfo.askPrice); - } - if (mdInfo.lastPrice > 0) { - rfq.second->setLastPrice(mdInfo.lastPrice); - } - } -} - -SubmitRFQ *RFQScript::activeRFQ(const QString &id) -{ - if (!started_) { - return nullptr; - } - const auto &itRFQ = activeRFQs_.find(id.toStdString()); - if (itRFQ == activeRFQs_.end()) { - return nullptr; - } - return itRFQ->second; -} - -void RFQScript::onAccepted(const std::string &id) -{ - if (!started_) { - return; - } - const auto &itRFQ = activeRFQs_.find(id); - if (itRFQ == activeRFQs_.end()) { - return; - } - emit accepted(QString::fromStdString(id)); - emit itRFQ->second->accepted(); -} - -void RFQScript::onExpired(const std::string &id) -{ - if (!started_) { - return; - } - const auto &itRFQ = activeRFQs_.find(id); - if (itRFQ == activeRFQs_.end()) { - logger_->warn("[RFQScript::onExpired] failed to find id {}", id); - return; - } - emit expired(QString::fromStdString(id)); - emit itRFQ->second->expired(); -} - -void RFQScript::onCancelled(const std::string &id) -{ - if (!started_) { - return; - } - const auto &itRFQ = activeRFQs_.find(id); - if (itRFQ == activeRFQs_.end()) { - return; - } - emit cancelled(QString::fromStdString(id)); - emit itRFQ->second->cancelled(); - itRFQ->second->deleteLater(); - activeRFQs_.erase(itRFQ); -} - - -AutoRFQ::AutoRFQ(const std::shared_ptr &logger - , const std::shared_ptr &mdCallbacks, QObject* parent) - : UserScript(logger, mdCallbacks, {}, parent) -{ - qRegisterMetaType(); - qmlRegisterType("bs.terminal", 1, 0, "SubmitRFQ"); - qmlRegisterType("bs.terminal", 1, 0, "RFQScript"); -} - -QObject *AutoRFQ::instantiate() -{ - QObject *rv = UserScript::instantiate(); - if (!rv) { - logger_->error("[AutoRFQ::instantiate] failed to instantiate script"); - return nullptr; - } - RFQScript *rfq = qobject_cast(rv); - if (!rfq) { - logger_->error("[AutoRFQ::instantiate] wrong script type"); - return nullptr; - } - rfq->init(logger_); - - connect(rfq, &RFQScript::sendingRFQ, this, &AutoRFQ::onSendRFQ); - connect(rfq, &RFQScript::cancellingRFQ, this, &AutoRFQ::cancelRFQ); - - return rv; -} - -void AutoRFQ::onSendRFQ(SubmitRFQ *rfq) -{ - if (!rfq) { - logger_->error("[AutoRFQ::onSendRFQ] no RFQ passed"); - return; - } - connect(rfq, &SubmitRFQ::stopRFQ, this, &AutoRFQ::stopRFQ); - emit sendRFQ(rfq->id().toStdString(), rfq->security(), rfq->amount(), rfq->buy()); -} diff --git a/BlockSettleUILib/UserScript.h b/BlockSettleUILib/UserScript.h deleted file mode 100644 index 53367b091..000000000 --- a/BlockSettleUILib/UserScript.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ -#ifndef __USER_SCRIPT_H__ -#define __USER_SCRIPT_H__ - -#include -#include -#include "CommonTypes.h" - -#include - -namespace spdlog { - class logger; -} -namespace bs { - namespace sync { - class WalletsManager; - } -} -class AssetManager; -class DataConnection; -class MDCallbacksQt; -class QQmlComponent; - - -// -// MarketData -// - -//! Market data for user script. -class MarketData : public QObject -{ - Q_OBJECT - -public: - MarketData(const std::shared_ptr &, QObject *parent); - ~MarketData() noexcept override = default; - - Q_INVOKABLE double bid(const QString &sec) const; - Q_INVOKABLE double ask(const QString &sec) const; - -private slots: - void onMDUpdated(bs::network::Asset::Type, const QString &security, bs::network::MDFields); - -private: - std::map> data_; -}; // class MarketData - - -// -// DataStorage -// - -//! Data storage for user script. -class DataStorage : public QObject -{ - Q_OBJECT - -public: - explicit DataStorage(QObject *parent); - ~DataStorage() noexcept override = default; - - Q_INVOKABLE double bought(const QString ¤cy); - Q_INVOKABLE void setBought(const QString ¤cy, double v, const QString &id); - - Q_INVOKABLE double sold(const QString ¤cy); - Q_INVOKABLE void setSold(const QString ¤cy, double v, const QString &id); - -private: - std::map> bought_; - std::map> sold_; -}; // class DataStorage - - -// -// Constants -// - -//! Useful constants for user script. -class Constants : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int payInTxSize READ payInTxSize) // TODO: turn these to payInFeeEstimate and payOutFeeEstimate - Q_PROPERTY(int payOutTxSize READ payOutTxSize) - Q_PROPERTY(float feePerByte READ feePerByte) - Q_PROPERTY(QString xbtProductName READ xbtProductName) - -public: - explicit Constants(QObject *parent); - ~Constants() noexcept override = default; - - int payInTxSize() const; - int payOutTxSize() const; - float feePerByte(); - QString xbtProductName() const; - - void setWalletsManager(std::shared_ptr walletsManager); - -private: - std::shared_ptr walletsManager_; - mutable float feePerByte_ = 0.0; -}; // class Constants - -using ExtConnections = std::unordered_map>; - -class UserScript : public QObject -{ -Q_OBJECT - -public: - UserScript(const std::shared_ptr &, - const std::shared_ptr &, - const ExtConnections &, QObject* parent = nullptr); - ~UserScript() override; - - void setWalletsManager(std::shared_ptr walletsManager); - bool load(const QString &filename); - - bool sendExtConn(const QString &name, const QString &type, const QString &message); - -signals: - void loaded(); - void failed(const QString &desc); - -protected: - QObject *instantiate(); - -protected: - std::shared_ptr logger_; - QQmlEngine *engine_; - QQmlComponent *component_; - MarketData *md_; - ExtConnections extConns_; - Constants *const_; - DataStorage *storage_; -}; - - -class AutoQuoter : public UserScript -{ - Q_OBJECT -public: - AutoQuoter(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const ExtConnections &, QObject* parent = nullptr); - ~AutoQuoter() override = default; - - QObject *instantiate(const bs::network::QuoteReqNotification &qrn); - -signals: - void sendingQuoteReply(const QString &reqId, double price); - void pullingQuoteReply(const QString &reqId); - -private: - std::shared_ptr assetManager_; -}; - - -class BSQuoteRequest : public QObject -{ // Nested class for BSQuoteReqReply - not instantiatable - Q_OBJECT - Q_PROPERTY(QString requestId READ requestId) - Q_PROPERTY(QString product READ product) - Q_PROPERTY(bool isBuy READ isBuy) - Q_PROPERTY(double quantity READ quantity) - Q_PROPERTY(int assetType READ assetType) - -public: - explicit BSQuoteRequest(QObject *parent = nullptr) : QObject(parent) {} - ~BSQuoteRequest() override = default; - void init(const QString &reqId, const QString &product, bool buy, double qty, int at); - - QString requestId() const { return requestId_; } - QString product() const { return product_; } - bool isBuy() const { return isBuy_; } - double quantity() const { return quantity_; } - int assetType() const { return assetType_; } - - enum AssetType { - SpotFX = 1, - SpotXBT, - PrivateMarket - }; - - Q_ENUM(AssetType) - -private: - QString requestId_; - QString product_; - bool isBuy_; - double quantity_; - int assetType_; -}; - -class BSQuoteReqReply : public QObject -{ // Main QML-script facing class - Q_OBJECT - Q_PROPERTY(BSQuoteRequest* quoteReq READ quoteReq) - Q_PROPERTY(double expirationInSec READ expiration WRITE setExpiration NOTIFY expirationInSecChanged) - Q_PROPERTY(QString security READ security) - Q_PROPERTY(double indicBid READ indicBid NOTIFY indicBidChanged) - Q_PROPERTY(double indicAsk READ indicAsk NOTIFY indicAskChanged) - Q_PROPERTY(double lastPrice READ lastPrice NOTIFY lastPriceChanged) - Q_PROPERTY(double bestPrice READ bestPrice NOTIFY bestPriceChanged) - Q_PROPERTY(double isOwnBestPrice READ isOwnBestPrice) - -public: - explicit BSQuoteReqReply(QObject *parent = nullptr) : QObject(parent) {} //TODO: add dedicated AQ bs::Wallet - ~BSQuoteReqReply() override = default; - - void setQuoteReq(BSQuoteRequest *qr) { quoteReq_ = qr; } - BSQuoteRequest *quoteReq() const { return quoteReq_; } - - void setExpiration(double exp) { - if (exp != expirationInSec_) { - expirationInSec_ = exp; - emit expirationInSecChanged(); - } - } - double expiration() const { return expirationInSec_; } - - void setSecurity(const QString &security) { security_ = security; } - QString security() const { return security_; } - - void setIndicBid(double prc) { - if (indicBid_ != prc) { - indicBid_ = prc; - - if (started_) { - emit indicBidChanged(); - } - } - } - void setIndicAsk(double prc) { - if (indicAsk_ != prc) { - indicAsk_ = prc; - - if (started_) { - emit indicAskChanged(); - } - } - } - void setLastPrice(double prc) { - if (lastPrice_ != prc) { - lastPrice_ = prc; - - if (started_) { - emit lastPriceChanged(); - } - } - } - void setBestPrice(double prc, bool own) { - isOwnBestPrice_ = own; - if (bestPrice_ != prc) { - bestPrice_ = prc; - emit bestPriceChanged(); - } - } - double indicBid() const { return indicBid_; } - double indicAsk() const { return indicAsk_; } - double lastPrice() const { return lastPrice_; } - double bestPrice() const { return bestPrice_; } - bool isOwnBestPrice() const { return isOwnBestPrice_; } - - void init(const std::shared_ptr &logger - , const std::shared_ptr &assetManager, UserScript *parent); - - Q_INVOKABLE void log(const QString &); - Q_INVOKABLE bool sendQuoteReply(double price); - Q_INVOKABLE bool pullQuoteReply(); - Q_INVOKABLE QString product(); - Q_INVOKABLE double accountBalance(const QString &product); - Q_INVOKABLE bool sendExtConn(const QString &name, const QString &type, const QString &message); - - void start() { - if (!started_) { - started_ = true; - emit started(); - } - } - -signals: - void expirationInSecChanged(); - void indicBidChanged(); - void indicAskChanged(); - void lastPriceChanged(); - void bestPriceChanged(); - void sendingQuoteReply(const QString &reqId, double price); - void pullingQuoteReply(const QString &reqId); - void settled(); - void cancelled(); - void started(); - void extDataReceived(QString from, QString type, QString msg); - -private: - BSQuoteRequest *quoteReq_; - double expirationInSec_; - QString security_; - double indicBid_ = 0; - double indicAsk_ = 0; - double lastPrice_ = 0; - double bestPrice_ = 0; - bool isOwnBestPrice_ = false; - bool started_ = false; - std::shared_ptr logger_; - std::shared_ptr assetManager_; - UserScript * parent_; -}; - - -class SubmitRFQ : public QObject -{ // Container for individual RFQ submitted - Q_OBJECT - Q_PROPERTY(QString id READ id) - Q_PROPERTY(QString security READ security) - Q_PROPERTY(double amount READ amount WRITE setAmount NOTIFY amountChanged) - Q_PROPERTY(bool buy READ buy NOTIFY buyChanged) - Q_PROPERTY(double indicBid READ indicBid WRITE setIndicBid NOTIFY indicBidChanged) - Q_PROPERTY(double indicAsk READ indicAsk WRITE setIndicAsk NOTIFY indicAskChanged) - Q_PROPERTY(double lastPrice READ lastPrice WRITE setLastPrice NOTIFY lastPriceChanged) - -public: - explicit SubmitRFQ(QObject *parent = nullptr) : QObject(parent) {} - ~SubmitRFQ() override = default; - - void setId(const std::string &id) { id_ = QString::fromStdString(id); } - QString id() const { return id_; } - - void setSecurity(const QString &security) { security_ = security; } - QString security() const { return security_; } - - void setAmount(double amount) - { - if (amount_ != amount) { - amount_ = amount; - emit amountChanged(); - } - } - double amount() const { return amount_; } - - void setBuy(bool buy) - { - buy_ = buy; - emit buyChanged(); - } - bool buy() const { return buy_; } - - void setIndicBid(double prc) { - if (indicBid_ != prc) { - indicBid_ = prc; - emit indicBidChanged(); - } - } - void setIndicAsk(double prc) { - if (indicAsk_ != prc) { - indicAsk_ = prc; - emit indicAskChanged(); - } - } - void setLastPrice(double prc) { - if (lastPrice_ != prc) { - lastPrice_ = prc; - emit lastPriceChanged(); - } - } - double indicBid() const { return indicBid_; } - double indicAsk() const { return indicAsk_; } - double lastPrice() const { return lastPrice_; } - - Q_INVOKABLE void stop(); - -signals: - void amountChanged(); - void buyChanged(); - void indicBidChanged(); - void indicAskChanged(); - void lastPriceChanged(); - void stopRFQ(const std::string &id); - void cancelled(); - void accepted(); - void expired(); - -private: - QString id_; - QString security_; - double amount_ = 0; - bool buy_ = true; - double indicBid_ = 0; - double indicAsk_ = 0; - double lastPrice_ = 0; - std::shared_ptr assetManager_; -}; -Q_DECLARE_METATYPE(SubmitRFQ *) - - -class RFQScript : public QObject -{ // Main QML-script facing class - Q_OBJECT - -public: - explicit RFQScript(QObject *parent = nullptr) : QObject(parent) {} - ~RFQScript() override = default; - - void init(const std::shared_ptr &logger) - { - logger_ = logger; - } - - void start() { - if (!started_) { - started_ = true; - emit started(); - } - } - - void suspend() { - if (started_) { - started_ = false; - emit suspended(); - } - } - - Q_INVOKABLE void log(const QString &); - Q_INVOKABLE SubmitRFQ *sendRFQ(const QString &symbol, bool buy, double amount); - Q_INVOKABLE void cancelRFQ(const std::string &id); - Q_INVOKABLE SubmitRFQ *activeRFQ(const QString &id); - void cancelAll(); - - void onMDUpdate(bs::network::Asset::Type, const QString &security, - bs::network::MDFields mdFields); - - void onAccepted(const std::string &id); - void onExpired(const std::string &id); - void onCancelled(const std::string &id); - -signals: - void started(); - void suspended(); - void sendingRFQ(SubmitRFQ *); - void cancellingRFQ(const std::string &id); - - void indicBidChanged(const QString &security, double price); - void indicAskChanged(const QString &security, double price); - void lastPriceChanged(const QString &security, double price); - - void cancelled(const QString &id); - void accepted(const QString &id); - void expired(const QString &id); - -private: - bool started_ = false; - std::shared_ptr logger_; - std::unordered_map activeRFQs_; - - std::unordered_map mdInfo_; -}; - - -class AutoRFQ : public UserScript -{ - Q_OBJECT -public: - AutoRFQ(const std::shared_ptr & - , const std::shared_ptr & - , QObject* parent = nullptr); - ~AutoRFQ() override = default; - - QObject *instantiate(); - -private slots: - void onSendRFQ(SubmitRFQ *); - -signals: - void loaded(); - void failed(const QString &desc); - void sendRFQ(const std::string &id, const QString &symbol, double amount, bool buy); - void cancelRFQ(const std::string &id); - void stopRFQ(const std::string &id); -}; - - -#endif // __USER_SCRIPT_H__ diff --git a/BlockSettleUILib/UserScriptRunner.cpp b/BlockSettleUILib/UserScriptRunner.cpp deleted file mode 100644 index 8f8197c83..000000000 --- a/BlockSettleUILib/UserScriptRunner.cpp +++ /dev/null @@ -1,740 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ - -#include "UserScriptRunner.h" -#include -#include -#include -#include -#include -#include "DataConnectionListener.h" -#include "MDCallbacksQt.h" -#include "SignContainer.h" -#include "UserScript.h" -#include "Wallets/SyncWalletsManager.h" - - -// -// UserScriptHandler -// - -UserScriptHandler::UserScriptHandler(const std::shared_ptr &logger) - :logger_(logger) -{} - -UserScriptHandler::~UserScriptHandler() noexcept = default; - -void UserScriptHandler::setParent(UserScriptRunner *runner) -{ - QObject::setParent(nullptr); - - connect(runner, &UserScriptRunner::init, this, &UserScriptHandler::init, - Qt::QueuedConnection); - connect(runner, &UserScriptRunner::deinit, this, &UserScriptHandler::deinit, - Qt::QueuedConnection); -} - -void UserScriptHandler::setWalletsManager(const std::shared_ptr &walletsManager) -{ - walletsManager_ = walletsManager; -} - - -AQScriptHandler::AQScriptHandler(const std::shared_ptr "eProvider - , const std::shared_ptr &signingContainer - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr &assetManager - , const std::shared_ptr &logger) - : UserScriptHandler(logger) - , signingContainer_(signingContainer), mdCallbacks_(mdCallbacks) - , assetManager_(assetManager) - , aqEnabled_(false) - , aqTimer_(new QTimer(this)) -{ - connect(quoteProvider.get(), &QuoteProvider::quoteReqNotifReceived, - this, &AQScriptHandler::onQuoteReqNotification, Qt::QueuedConnection); - connect(quoteProvider.get(), &QuoteProvider::quoteNotifCancelled, - this, &AQScriptHandler::onQuoteNotifCancelled, Qt::QueuedConnection); - connect(quoteProvider.get(), &QuoteProvider::quoteCancelled, - this, &AQScriptHandler::onQuoteReqCancelled, Qt::QueuedConnection); - connect(quoteProvider.get(), &QuoteProvider::quoteRejected, - this, &AQScriptHandler::onQuoteReqRejected, Qt::QueuedConnection); - - connect(mdCallbacks_.get(), &MDCallbacksQt::MDUpdate, this, &AQScriptHandler::onMDUpdate, - Qt::QueuedConnection); - connect(quoteProvider.get(), &QuoteProvider::bestQuotePrice, - this, &AQScriptHandler::onBestQuotePrice, Qt::QueuedConnection); - - aqTimer_->setInterval(500); - connect(aqTimer_, &QTimer::timeout, this, &AQScriptHandler::aqTick); - aqTimer_->start(); -} - -AQScriptHandler::~AQScriptHandler() noexcept -{ - clear(); -} - -void AQScriptHandler::onThreadStopped() -{ - aqTimer_->stop(); - UserScriptHandler::onThreadStopped(); -} - -void AQScriptHandler::setWalletsManager(const std::shared_ptr &walletsManager) -{ - UserScriptHandler::setWalletsManager(walletsManager); - - if (aq_) { - aq_->setWalletsManager(walletsManager); - } -} - -void AQScriptHandler::setExtConnections(const ExtConnections &conns) -{ - if (conns.empty()) { - extConns_.clear(); - } - else { - extConns_ = conns; - } -} - -void AQScriptHandler::reload(const QString &filename) -{ - if (aq_) { - aq_->load(filename); - } -} - -void AQScriptHandler::onQuoteReqNotification(const bs::network::QuoteReqNotification &qrn) -{ - const auto &itAQObj = aqObjs_.find(qrn.quoteRequestId); - if ((qrn.status == bs::network::QuoteReqNotification::PendingAck) || (qrn.status == bs::network::QuoteReqNotification::Replied)) { - aqQuoteReqs_[qrn.quoteRequestId] = qrn; - if ((qrn.assetType != bs::network::Asset::SpotFX) && (!signingContainer_ || signingContainer_->isOffline())) { - logger_->error("[AQScriptHandler::onQuoteReqNotification] can't handle" - " non-FX quote without online signer"); - return; - } - if (aqEnabled_ && aq_ && (itAQObj == aqObjs_.end())) { - QObject *obj = aq_->instantiate(qrn); - aqObjs_[qrn.quoteRequestId] = obj; - if (thread_) { - obj->moveToThread(thread_); - } - - const auto &mdIt = mdInfo_.find(qrn.security); - auto reqReply = qobject_cast(obj); - if (!reqReply) { - logger_->error("[AQScriptHandler::onQuoteReqNotification] invalid AQ object instantiated"); - return; - } - if (mdIt != mdInfo_.end()) { - if (mdIt->second.bidPrice > 0) { - reqReply->setIndicBid(mdIt->second.bidPrice); - } - if (mdIt->second.askPrice > 0) { - reqReply->setIndicAsk(mdIt->second.askPrice); - } - if (mdIt->second.lastPrice > 0) { - reqReply->setLastPrice(mdIt->second.lastPrice); - } - } - reqReply->start(); - } - } - else if ((qrn.status == bs::network::QuoteReqNotification::Rejected) - || (qrn.status == bs::network::QuoteReqNotification::TimedOut)) { - if (itAQObj != aqObjs_.end()) { - const auto replyObj = qobject_cast(itAQObj->second); - if (replyObj) { - emit replyObj->cancelled(); - } - } - stop(qrn.quoteRequestId); - } -} - -void AQScriptHandler::stop(const std::string "eReqId) -{ - const auto &itAQObj = aqObjs_.find(quoteReqId); - aqQuoteReqs_.erase(quoteReqId); - if (itAQObj != aqObjs_.end()) { - itAQObj->second->deleteLater(); - aqObjs_.erase(itAQObj); - bestQPrices_.erase(quoteReqId); - } -} - -void AQScriptHandler::onQuoteReqCancelled(const QString &reqId, bool userCancelled) -{ - const auto itQR = aqQuoteReqs_.find(reqId.toStdString()); - if (itQR == aqQuoteReqs_.end()) { - return; - } - auto qrn = itQR->second; - - qrn.status = (userCancelled ? bs::network::QuoteReqNotification::Withdrawn : - bs::network::QuoteReqNotification::PendingAck); - onQuoteReqNotification(qrn); -} - -void AQScriptHandler::onQuoteNotifCancelled(const QString &reqId) -{ - onQuoteReqCancelled(reqId, true); -} - -void AQScriptHandler::onQuoteReqRejected(const QString &reqId, const QString &) -{ - onQuoteReqCancelled(reqId, true); -} - -void AQScriptHandler::init(const QString &fileName) -{ - if (fileName.isEmpty()) { - return; - } - aqEnabled_ = false; - - aq_ = new AutoQuoter(logger_, assetManager_, mdCallbacks_, extConns_, this); - if (walletsManager_) { - aq_->setWalletsManager(walletsManager_); - } - connect(aq_, &AutoQuoter::loaded, [this, fileName] { - emit scriptLoaded(fileName); - aqEnabled_ = true; - }); - connect(aq_, &AutoQuoter::failed, [this, fileName](const QString &err) { - logger_->error("Script loading failed: {}", err.toStdString()); - - aq_->deleteLater(); - aq_ = nullptr; - - emit failedToLoad(fileName, err); - }); - connect(aq_, &AutoQuoter::sendingQuoteReply, this, &AQScriptHandler::onAQReply); - connect(aq_, &AutoQuoter::pullingQuoteReply, this, &AQScriptHandler::onAQPull); - - aq_->load(fileName); -} - -void AQScriptHandler::deinit() -{ - clear(); - - if (aq_) { - aq_->deleteLater(); - aq_ = nullptr; - } -} - -void AQScriptHandler::clear() -{ - if (!aq_) { - return; - } - - for (auto aqObj : aqObjs_) { - aqObj.second->deleteLater(); - } - aqObjs_.clear(); - aqEnabled_ = false; - - std::vector requests; - for (const auto &aq : aqQuoteReqs_) { - switch (aq.second.status) { - case bs::network::QuoteReqNotification::PendingAck: - case bs::network::QuoteReqNotification::Replied: - requests.push_back(aq.first); - break; - case bs::network::QuoteReqNotification::Withdrawn: - case bs::network::QuoteReqNotification::Rejected: - case bs::network::QuoteReqNotification::TimedOut: - break; - case bs::network::QuoteReqNotification::StatusUndefined: - assert(false); - break; - } - } - - for (const std::string &reqId : requests) { - SPDLOG_LOGGER_INFO(logger_, "pull AQ request {}", reqId); - onAQPull(QString::fromStdString(reqId)); - } -} - -void AQScriptHandler::onMDUpdate(bs::network::Asset::Type, const QString &security, - bs::network::MDFields mdFields) -{ - auto &mdInfo = mdInfo_[security.toStdString()]; - mdInfo.merge(bs::network::MDField::get(mdFields)); - - for (auto aqObj : aqObjs_) { - QString sec = aqObj.second->property("security").toString(); - if (sec.isEmpty() || (security != sec)) { - continue; - } - - auto *reqReply = qobject_cast(aqObj.second); - if (mdInfo.bidPrice > 0) { - reqReply->setIndicBid(mdInfo.bidPrice); - } - if (mdInfo.askPrice > 0) { - reqReply->setIndicAsk(mdInfo.askPrice); - } - if (mdInfo.lastPrice > 0) { - reqReply->setLastPrice(mdInfo.lastPrice); - } - } -} - -void AQScriptHandler::onBestQuotePrice(const QString reqId, double price, bool own) -{ - const auto itAQObj = aqObjs_.find(reqId.toStdString()); - if (itAQObj != aqObjs_.end()) { - qobject_cast(itAQObj->second)->setBestPrice(price, own); - } -} - -void AQScriptHandler::onAQReply(const QString &reqId, double price) -{ - const auto itQRN = aqQuoteReqs_.find(reqId.toStdString()); - if (itQRN == aqQuoteReqs_.end()) { - logger_->warn("[UserScriptHandler::onAQReply] QuoteReqNotification with id = {} not found", reqId.toStdString()); - return; - } - - emit sendQuote(itQRN->second, price); -} - -void AQScriptHandler::onAQPull(const QString &reqId) -{ - const auto itQRN = aqQuoteReqs_.find(reqId.toStdString()); - if (itQRN == aqQuoteReqs_.end()) { - logger_->warn("[UserScriptHandler::onAQPull] QuoteReqNotification with id = {} not found", reqId.toStdString()); - return; - } - emit pullQuoteNotif(itQRN->second.settlementId, itQRN->second.quoteRequestId, itQRN->second.sessionToken); -} - -void AQScriptHandler::aqTick() -{ - if (aqObjs_.empty()) { - return; - } - QStringList expiredEntries; - const auto timeNow = QDateTime::currentDateTime(); - - for (auto aqObj : aqObjs_) { - BSQuoteRequest *qr = qobject_cast(aqObj.second)->quoteReq(); - if (!qr) continue; - auto itQRN = aqQuoteReqs_.find(qr->requestId().toStdString()); - if (itQRN == aqQuoteReqs_.end()) continue; - const auto& expTime = QDateTime::fromMSecsSinceEpoch(itQRN->second.expirationTime); - const auto timeDiff = timeNow.msecsTo(expTime.addMSecs(itQRN->second.timeSkewMs)); - if (timeDiff <= 0) { - expiredEntries << qr->requestId(); - } - else { - aqObj.second->setProperty("expirationInSec", timeDiff / 1000.0); - } - } - for (const auto & expReqId : qAsConst(expiredEntries)) { - onQuoteReqCancelled(expReqId, true); - } -} - -void AQScriptHandler::performOnReplyAndStop(const std::string "eReqId - , const std::function &cb) -{ - const auto &itAQObj = aqObjs_.find(quoteReqId); - if (itAQObj != aqObjs_.end()) { - const auto replyObj = qobject_cast(itAQObj->second); - if (replyObj && cb) { - cb(replyObj); - } - } - QTimer::singleShot(1000, [this, quoteReqId] { stop(quoteReqId); }); -} - -void AQScriptHandler::cancelled(const std::string "eReqId) -{ - performOnReplyAndStop(quoteReqId, [](BSQuoteReqReply *replyObj) { - emit replyObj->cancelled(); - }); -} - -void AQScriptHandler::settled(const std::string "eReqId) -{ - performOnReplyAndStop(quoteReqId, [](BSQuoteReqReply *replyObj) { - emit replyObj->settled(); - }); -} - -void AQScriptHandler::extMsgReceived(const std::string &data) -{ - QJsonParseError jsonError; - const auto &jsonDoc = QJsonDocument::fromJson(QByteArray::fromStdString(data) - , &jsonError); - if (jsonError.error != QJsonParseError::NoError) { - logger_->error("[AQScriptHandler::extMsgReceived] invalid JSON message: {}" - , jsonError.errorString().toUtf8().toStdString()); - return; - } - const auto &jsonObj = jsonDoc.object(); - const auto &strFrom = jsonObj[QLatin1Literal("from")].toString(); - const auto &strType = jsonObj[QLatin1Literal("type")].toString(); - const auto &msgObj = jsonObj[QLatin1Literal("message")].toObject(); - QJsonDocument msgDoc(msgObj); - const auto &strMsg = QString::fromStdString(msgDoc.toJson(QJsonDocument::Compact).toStdString()); - if (strFrom.isEmpty() || strType.isEmpty() || msgObj.isEmpty()) { - logger_->error("[AQScriptHandler::extMsgReceived] invalid data in JSON: {}" - , data); - return; - } - - for (const auto &aqObj : aqObjs_) { - const auto replyObj = qobject_cast(aqObj.second); - if (replyObj) { - emit replyObj->extDataReceived(strFrom, strType, strMsg); - } - } -} - - -// -// UserScriptRunner -// -UserScriptRunner::UserScriptRunner(const std::shared_ptr &logger - , UserScriptHandler *script, QObject *parent) - : QObject(parent) - , thread_(new QThread(this)), script_(script), logger_(logger) -{ - script_->setParent(this); - connect(thread_, &QThread::finished, script_, &UserScriptHandler::onThreadStopped - , Qt::QueuedConnection); - script_->setRunningThread(thread_); - - connect(script_, &UserScriptHandler::scriptLoaded, this, &UserScriptRunner::scriptLoaded); - connect(script_, &UserScriptHandler::failedToLoad, this, &UserScriptRunner::failedToLoad); - - thread_->start(); -} - -UserScriptRunner::~UserScriptRunner() noexcept -{ - thread_->quit(); - thread_->wait(); -} - -void UserScriptRunner::setWalletsManager(const std::shared_ptr &walletsManager) -{ - script_->setWalletsManager(walletsManager); -} - -void UserScriptRunner::enable(const QString &fileName) -{ - logger_->info("Load script {}", fileName.toStdString()); - emit init(fileName); -} - -void UserScriptRunner::disable() -{ - logger_->info("Unload script"); - emit deinit(); -} - - -AQScriptRunner::AQScriptRunner(const std::shared_ptr "eProvider - , const std::shared_ptr &signingContainer - , const std::shared_ptr &mdCallbacks - , const std::shared_ptr &assetManager - , const std::shared_ptr &logger - , QObject *parent) - : UserScriptRunner(logger, new AQScriptHandler(quoteProvider, signingContainer, - mdCallbacks, assetManager, logger), parent) -{ - thread_->setObjectName(QStringLiteral("AQScriptRunner")); - - connect((AQScriptHandler *)script_, &AQScriptHandler::pullQuoteNotif, this - , &AQScriptRunner::pullQuoteNotif); - connect((AQScriptHandler *)script_, &AQScriptHandler::sendQuote, this - , &AQScriptRunner::sendQuote); -} - -AQScriptRunner::~AQScriptRunner() -{ - const auto aqHandler = qobject_cast(script_); - if (aqHandler) { - aqHandler->setExtConnections({}); - } -} - -void AQScriptRunner::cancelled(const std::string "eReqId) -{ - const auto aqHandler = qobject_cast(script_); - if (aqHandler) { - aqHandler->cancelled(quoteReqId); - } -} - -void AQScriptRunner::settled(const std::string "eReqId) -{ - const auto aqHandler = qobject_cast(script_); - if (aqHandler) { - aqHandler->settled(quoteReqId); - } -} - -class ExtConnListener : public DataConnectionListener -{ -public: - ExtConnListener(AQScriptRunner *parent, std::shared_ptr &logger) - : parent_(parent), logger_(logger) - {} - - void OnDataReceived(const std::string &data) override - { - parent_->onExtDataReceived(data); - } - - void OnConnected() override { logger_->debug("[{}]", __func__); } - void OnDisconnected() override { logger_->debug("[{}]", __func__); } - void OnError(DataConnectionError err) override { logger_->debug("[{}] {}", __func__, (int)err); } - -private: - AQScriptRunner *parent_{nullptr}; - std::shared_ptr logger_; -}; - -void AQScriptRunner::setExtConnections(const ExtConnections &conns) -{ - const auto aqHandler = qobject_cast(script_); - if (aqHandler) { - aqHandler->setExtConnections(conns); - } -} - -std::shared_ptr AQScriptRunner::getExtConnListener() -{ - if (!extConnListener_) { - extConnListener_ = std::make_shared(this, logger_); - } - return extConnListener_; -} - -void AQScriptRunner::onExtDataReceived(const std::string &data) -{ - const auto aqHandler = qobject_cast(script_); - if (aqHandler) { - aqHandler->extMsgReceived(data); - } -} - - -RFQScriptHandler::RFQScriptHandler(const std::shared_ptr &logger - , const std::shared_ptr &mdCallbacks) - : UserScriptHandler(logger) - , mdCallbacks_(mdCallbacks) -{ - connect(mdCallbacks_.get(), &MDCallbacksQt::MDUpdate, this, &RFQScriptHandler::onMDUpdate, - Qt::QueuedConnection); -} - -RFQScriptHandler::~RFQScriptHandler() noexcept -{ - clear(); -} - - -void RFQScriptHandler::setWalletsManager(const std::shared_ptr &walletsManager) -{ - UserScriptHandler::setWalletsManager(walletsManager); - - if (rfq_) { - rfq_->setWalletsManager(walletsManager); - } -} - -void RFQScriptHandler::reload(const QString &filename) -{ - if (rfq_) { - rfq_->load(filename); - } -} - -void RFQScriptHandler::init(const QString &fileName) -{ - if (fileName.isEmpty()) { - emit failedToLoad(fileName, tr("invalid script filename")); - return; - } - if (rfq_) { - start(); - return; // already inited - } - - rfq_ = new AutoRFQ(logger_, mdCallbacks_, this); - if (walletsManager_) { - rfq_->setWalletsManager(walletsManager_); - } - connect(rfq_, &AutoQuoter::loaded, [this, fileName] { - emit scriptLoaded(fileName); - }); - connect(rfq_, &AutoQuoter::failed, [this, fileName](const QString &err) { - logger_->error("Script loading failed: {}", err.toStdString()); - if (rfq_) { - rfq_->deleteLater(); - rfq_ = nullptr; - } - emit failedToLoad(fileName, err); - }); - - connect(rfq_, &AutoRFQ::sendRFQ, this, &RFQScriptHandler::sendRFQ); - connect(rfq_, &AutoRFQ::cancelRFQ, this, &RFQScriptHandler::cancelRFQ); - connect(rfq_, &AutoRFQ::stopRFQ, this, &RFQScriptHandler::onStopRFQ); - - if (!rfq_->load(fileName)) { - emit failedToLoad(fileName, tr("script loading failed")); - } - start(); -} - -void RFQScriptHandler::deinit() -{ - clear(); - - if (rfq_) { - rfq_->deleteLater(); - rfq_ = nullptr; - } -} - -void RFQScriptHandler::clear() -{ - if (!rfq_) { - return; - } - if (rfqObj_) { - rfqObj_->cancelAll(); - rfqObj_->deleteLater(); - rfqObj_ = nullptr; - } -} - -void RFQScriptHandler::onMDUpdate(bs::network::Asset::Type at, const QString &security, - bs::network::MDFields mdFields) -{ - if (!rfqObj_) { - return; - } - rfqObj_->onMDUpdate(at, security, mdFields); -} - -void RFQScriptHandler::start() -{ - if (!rfq_) { - return; - } - if (!rfqObj_) { - auto obj = rfq_->instantiate(); - if (!obj) { - emit failedToLoad({}, tr("failed to instantiate")); - return; - } - rfqObj_ = qobject_cast(obj); - if (!rfqObj_) { - logger_->error("[RFQScriptHandler::start] {} has wrong script object"); - emit failedToLoad({}, tr("wrong script loaded")); - return; - } - } - logger_->debug("[RFQScriptHandler::start]"); - rfqObj_->moveToThread(thread_); - rfqObj_->start(); -} - -void RFQScriptHandler::suspend() -{ - if (rfqObj_) { - rfqObj_->suspend(); - } -} - -void RFQScriptHandler::rfqAccepted(const std::string &id) -{ - if (rfqObj_) { - rfqObj_->onAccepted(id); - } -} - -void RFQScriptHandler::rfqCancelled(const std::string &id) -{ - if (rfqObj_) { - rfqObj_->onCancelled(id); - } -} - -void RFQScriptHandler::rfqExpired(const std::string &id) -{ - if (rfqObj_) { - rfqObj_->onExpired(id); - } -} - -void RFQScriptHandler::onStopRFQ(const std::string &id) -{ - if (rfqObj_) { - rfqObj_->onCancelled(id); - } -} - - -RFQScriptRunner::RFQScriptRunner(const std::shared_ptr &mdCallbacks - , const std::shared_ptr &logger - , QObject *parent) - : UserScriptRunner(logger, new RFQScriptHandler(logger, mdCallbacks), parent) -{ - thread_->setObjectName(QStringLiteral("RFQScriptRunner")); - - connect((RFQScriptHandler *)script_, &RFQScriptHandler::sendRFQ, this - , &RFQScriptRunner::sendRFQ); - connect((RFQScriptHandler *)script_, &RFQScriptHandler::cancelRFQ, this - , &RFQScriptRunner::cancelRFQ); -} - -RFQScriptRunner::~RFQScriptRunner() = default; - -void RFQScriptRunner::start(const QString &filename) -{ - emit init(filename); -} - -void RFQScriptRunner::suspend() -{ - ((RFQScriptHandler *)script_)->suspend(); -} - -void RFQScriptRunner::rfqAccepted(const std::string &id) -{ - ((RFQScriptHandler *)script_)->rfqAccepted(id); -} - -void RFQScriptRunner::rfqCancelled(const std::string &id) -{ - ((RFQScriptHandler *)script_)->rfqCancelled(id); -} - -void RFQScriptRunner::rfqExpired(const std::string &id) -{ - ((RFQScriptHandler *)script_)->rfqExpired(id); -} diff --git a/BlockSettleUILib/UserScriptRunner.h b/BlockSettleUILib/UserScriptRunner.h deleted file mode 100644 index a22fe632c..000000000 --- a/BlockSettleUILib/UserScriptRunner.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - -*********************************************************************************** -* Copyright (C) 2018 - 2020, BlockSettle AB -* Distributed under the GNU Affero General Public License (AGPL v3) -* See LICENSE or http://www.gnu.org/licenses/agpl.html -* -********************************************************************************** - -*/ - -#ifndef USERSCRIPTRUNNER_H_INCLUDED -#define USERSCRIPTRUNNER_H_INCLUDED - -#include -#include - -#include -#include -#include -#include -#include - -#include "UserScript.h" -#include "QuoteProvider.h" -#include "CommonTypes.h" - -QT_BEGIN_NAMESPACE -class QThread; -QT_END_NAMESPACE - -namespace bs { - namespace sync { - class WalletsManager; - } -} -class DataConnectionListener; -class MDCallbacksQt; -class RFQScript; -class SignContainer; -class UserScriptRunner; - - -// -// UserScriptHandler -// - -//! Handler of events in user script. -class UserScriptHandler : public QObject -{ - Q_OBJECT -public: - explicit UserScriptHandler(const std::shared_ptr &); - ~UserScriptHandler() noexcept override; - - virtual void setWalletsManager(const std::shared_ptr &); - void setParent(UserScriptRunner *); - void setRunningThread(QThread *thread) { thread_ = thread; } - virtual void reload(const QString &filename) = 0; - -signals: - void scriptLoaded(const QString &fileName); - void failedToLoad(const QString &fileName, const QString &error); - -public slots: - virtual void onThreadStopped() - { - deleteLater(); - } - -protected slots: - virtual void init(const QString &fileName) {} - virtual void deinit() {} - -protected: - std::shared_ptr logger_; - std::shared_ptr walletsManager_; - QThread *thread_{nullptr}; -}; // class UserScriptHandler - - -class AQScriptHandler : public UserScriptHandler -{ - Q_OBJECT -public: - explicit AQScriptHandler(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); - ~AQScriptHandler() noexcept override; - - void setWalletsManager(const std::shared_ptr &) override; - void setExtConnections(const ExtConnections &conns); - void reload(const QString &filename) override; - - void cancelled(const std::string "eReqId); - void settled(const std::string "eReqId); - void extMsgReceived(const std::string &data); - -signals: - void pullQuoteNotif(const std::string& settlementId, const std::string& reqId, const std::string& reqSessToken); - void sendQuote(const bs::network::QuoteReqNotification &qrn, double price); - -public slots: - void onThreadStopped() override; - -protected slots: - void init(const QString &fileName) override; - void deinit() override; - -private slots: - void onQuoteReqNotification(const bs::network::QuoteReqNotification &qrn); - void onQuoteReqCancelled(const QString &reqId, bool userCancelled); - void onQuoteNotifCancelled(const QString &reqId); - void onQuoteReqRejected(const QString &reqId, const QString &); - void onMDUpdate(bs::network::Asset::Type, const QString &security, - bs::network::MDFields mdFields); - void onBestQuotePrice(const QString reqId, double price, bool own); - void onAQReply(const QString &reqId, double price); - void onAQPull(const QString &reqId); - void aqTick(); - -private: - void clear(); - void stop(const std::string "eReqId); - void performOnReplyAndStop(const std::string "eReqId - , const std::function &); - -private: - AutoQuoter *aq_ = nullptr; - std::shared_ptr signingContainer_; - std::shared_ptr mdCallbacks_; - std::shared_ptr assetManager_; - ExtConnections extConns_; - - std::unordered_map aqObjs_; - std::unordered_map aqQuoteReqs_; - std::unordered_map bestQPrices_; - - std::unordered_map mdInfo_; - - bool aqEnabled_; - QTimer *aqTimer_; -}; // class UserScriptHandler - - -class RFQScriptHandler : public UserScriptHandler -{ - Q_OBJECT -public: - explicit RFQScriptHandler(const std::shared_ptr & - , const std::shared_ptr &); - ~RFQScriptHandler() noexcept override; - - void setWalletsManager(const std::shared_ptr &) override; - void reload(const QString &filename) override; - - void suspend(); - void rfqAccepted(const std::string &id); - void rfqCancelled(const std::string &id); - void rfqExpired(const std::string &id); - -signals: - void sendRFQ(const std::string &id, const QString &symbol, double amount, bool buy); - void cancelRFQ(const std::string &id); - -protected slots: - void init(const QString &fileName) override; - void deinit() override; - -private slots: - void onMDUpdate(bs::network::Asset::Type, const QString &security, - bs::network::MDFields mdFields); - void onStopRFQ(const std::string &id); - -private: - void clear(); - void start(); - -private: - AutoRFQ *rfq_{ nullptr }; - std::shared_ptr mdCallbacks_; - RFQScript * rfqObj_{ nullptr }; -}; - - -class UserScriptRunner : public QObject -{ - Q_OBJECT -public: - UserScriptRunner(const std::shared_ptr & - , UserScriptHandler *, QObject *parent); - ~UserScriptRunner() noexcept override; - - void setWalletsManager(const std::shared_ptr &); - void setRunningThread(QThread *thread) { script_->setRunningThread(thread); } - void reload(const QString &filename) { script_->reload(filename); } - -signals: - void init(const QString &fileName); - void deinit(); - void stateChanged(bool enabled); - void scriptLoaded(const QString &fileName); - void failedToLoad(const QString &fileName, const QString &error); - -public slots: - void enable(const QString &fileName); - void disable(); - -protected: - QThread *thread_; - UserScriptHandler *script_; - std::shared_ptr logger_; -}; // class UserScriptRunner - -class AQScriptRunner : public UserScriptRunner -{ - Q_OBJECT -public: - AQScriptRunner(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &, QObject *parent = nullptr); - ~AQScriptRunner() noexcept override; - - void cancelled(const std::string "eReqId); - void settled(const std::string "eReqId); - - void setExtConnections(const ExtConnections &); - std::shared_ptr getExtConnListener(); - void onExtDataReceived(const std::string &data); - -signals: - void pullQuoteNotif(const std::string& settlementId, const std::string& reqId, const std::string& reqSessToken); - void sendQuote(const bs::network::QuoteReqNotification &qrn, double price); - -private: - std::shared_ptr extConnListener_; -}; - -class RFQScriptRunner : public UserScriptRunner -{ - Q_OBJECT -public: - RFQScriptRunner(const std::shared_ptr &, - const std::shared_ptr &, - QObject *parent); - ~RFQScriptRunner() noexcept override; - - void start(const QString &file); - void suspend(); - void rfqAccepted(const std::string &id); - void rfqCancelled(const std::string &id); - void rfqExpired(const std::string &id); - -signals: - void sendRFQ(const std::string &id, const QString &symbol, double amount, bool buy); - void cancelRFQ(const std::string &id); -}; - -#endif // USERSCRIPTRUNNER_H_INCLUDED diff --git a/BlockSettleUILib/OrderListModel.cpp b/BlockSettleUILib/unused/OrderListModel.cpp similarity index 65% rename from BlockSettleUILib/OrderListModel.cpp rename to BlockSettleUILib/unused/OrderListModel.cpp index 566315389..120e92574 100644 --- a/BlockSettleUILib/OrderListModel.cpp +++ b/BlockSettleUILib/unused/OrderListModel.cpp @@ -9,7 +9,6 @@ */ #include "OrderListModel.h" - #include "AssetManager.h" #include "QuoteProvider.h" #include "UiUtils.h" @@ -47,9 +46,9 @@ namespace { order.pendingStatus = data.status_text(); order.dateTime = QDateTime::fromMSecsSinceEpoch(data.timestamp_ms()); order.product = data.product(); - order.quantity = data.quantity(); + //FIXME: order.quantity = data.quantity(); order.security = data.product() + "/" + data.product_against(); - order.price = data.price(); + //FIXME: order.price = data.price(); return order; } @@ -82,28 +81,11 @@ QString OrderListModel::Header::toString(OrderListModel::Header::Index h) } } -QString OrderListModel::StatusGroup::toString(OrderListModel::StatusGroup::Type sg) -{ - switch (sg) { - case StatusGroup::UnSettled: return tr("UnSettled"); - case StatusGroup::Settled: return tr("Settled"); - case StatusGroup::PendingSettlements: return tr("Pending Settlements"); - default: return tr("Unknown"); - } -} - - -OrderListModel::OrderListModel(const std::shared_ptr& assetManager, QObject* parent) - : QAbstractItemModel(parent) - , assetManager_(assetManager) -{ - reset(); -} OrderListModel::OrderListModel(QObject* parent) : QAbstractItemModel(parent) { - reset(); + //reset(); } int OrderListModel::columnCount(const QModelIndex &) const @@ -154,7 +136,7 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const return static_cast(Qt::AlignRight | Qt::AlignVCenter); default : - return QVariant(); + return {}; } } @@ -162,12 +144,12 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const if (index.column() == Header::Status) { return d->statusColor_; } else { - return QVariant(); + return {}; } } default : - return QVariant(); + return {}; } } @@ -188,7 +170,7 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const case Header::Status: return g->getStatus(); default: - return QVariant(); + return {}; } } @@ -201,7 +183,7 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const case Header::Status: return static_cast(Qt::AlignLeft | Qt::AlignVCenter); default : - return QVariant(); + return {}; } } @@ -212,58 +194,18 @@ QVariant OrderListModel::data(const QModelIndex &index, int role) const case Header::Value: return g->getValueColor(); default: - return QVariant(); - } - } - - default : - return QVariant(); - } - } - - case DataType::Market : { - auto g = static_cast(idx->data_); - - switch (role) { - case Qt::DisplayRole : { - if (index.column() == Header::NameColumn) { - return g->name_; - } else { - return QVariant(); - } - } - - case Qt::FontRole : { - return g->font_; - } - - default : - return QVariant(); - } - } - - case DataType::StatusGroup : { - auto g = static_cast(idx->data_); - - switch (role) { - case Qt::DisplayRole : { - if (index.column() == Header::NameColumn) { - return g->name_; - } else { - return QVariant(); + return {}; } } - default : - return QVariant(); + default: return {}; } } - default : - return QVariant(); + default: return {}; } } else { - return QVariant(); + return {}; } } @@ -273,16 +215,6 @@ QModelIndex OrderListModel::index(int row, int column, const QModelIndex &parent auto idx = static_cast(parent.internalPointer()); switch (idx->type_) { - case DataType::Market : { - auto m = static_cast(idx->data_); - - if (row < static_cast(m->rows_.size())) { - return createIndex(row, column, &m->rows_[static_cast(row)]->idx_); - } else { - return QModelIndex(); - } - } - case DataType::Group : { auto g = static_cast(idx->data_); @@ -293,60 +225,17 @@ QModelIndex OrderListModel::index(int row, int column, const QModelIndex &parent } } - case DataType::StatusGroup : { - auto g = static_cast(idx->data_); - - if (row < static_cast(g->rows_.size())) { - return createIndex(row, column, &g->rows_[static_cast(row)]->idx_); - } else { - return QModelIndex(); - } - } - default : return QModelIndex(); } } else { switch (row) { - case StatusGroup::UnSettled : - return createIndex(row, column, &unsettled_->idx_); - - case StatusGroup::Settled : - return createIndex(row, column, &settled_->idx_); - - case StatusGroup::PendingSettlements : - return createIndex(row, column, &pendingFuturesSettlement_->idx_); - default : return QModelIndex(); } } } -int OrderListModel::findGroup(Market *market, Group *group) const -{ - const auto it = std::find_if(market->rows_.cbegin(), market->rows_.cend(), - [group] (const std::unique_ptr &g) { return (&group->idx_ == &g->idx_); }); - - if (it != market->rows_.cend()) { - return static_cast (std::distance(market->rows_.cbegin(), it)); - } else { - return -1; - } -} - -int OrderListModel::findMarket(StatusGroup *statusGroup, Market *market) const -{ - const auto it = std::find_if(statusGroup->rows_.cbegin(), statusGroup->rows_.cend(), - [market] (const std::unique_ptr &m) { return (&market->idx_ == &m->idx_); }); - - if (it != statusGroup->rows_.cend()) { - return static_cast (std::distance(statusGroup->rows_.cbegin(), it)); - } else { - return -1; - } -} - QModelIndex OrderListModel::parent(const QModelIndex &index) const { if (index.isValid()) { @@ -354,24 +243,10 @@ QModelIndex OrderListModel::parent(const QModelIndex &index) const if (idx->parent_) { switch (idx->parent_->type_) { - case DataType::Market : { - auto m = static_cast(idx->parent_->data_); - - return createIndex(findMarket(static_cast(idx->parent_->parent_->data_), - m), 0, &m->idx_); - } - case DataType::Group : { auto g = static_cast(idx->parent_->data_); - return createIndex(findGroup(static_cast(idx->parent_->parent_->data_), - g), 0, &g->idx_); - } - - case DataType::StatusGroup : { - auto g = static_cast(idx->parent_->data_); - - return createIndex(g->row_, 0, &g->idx_); + return createIndex(0 /*FIXME*/, 0, &g->idx_); } default : @@ -388,44 +263,30 @@ QModelIndex OrderListModel::parent(const QModelIndex &index) const int OrderListModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { - // Do not show Settled and UnSettled root items if not connected - return connected_ ? 3 : 0; + return 1; } auto idx = static_cast(parent.internalPointer()); switch (idx->type_) { - case DataType::Market : { - auto m = static_cast(idx->data_); - - return static_cast(m->rows_.size()); - } - case DataType::Group : { auto g = static_cast(idx->data_); return static_cast(g->rows_.size()); } - case DataType::StatusGroup : { - auto g = static_cast(idx->data_); - - return static_cast(g->rows_.size()); - } - - default : - return 0; + default: return 0; } } QVariant OrderListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal) { - return QVariant(); + return {}; } if (role != Qt::DisplayRole) { - return QVariant(); + return {}; } return Header::toString(static_cast(section)); @@ -433,27 +294,26 @@ QVariant OrderListModel::headerData(int section, Qt::Orientation orientation, in void OrderListModel::onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response) { - connected_ = true; - switch (response.data_case()) { - case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrdersObligations: - processUpdateOrders(response.update_orders_obligations()); - break; - case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrder: - processUpdateOrder(response.update_order()); - break; - default: - break; +#if 0 + case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrdersObligations: + processUpdateOrders(response.update_orders_obligations()); + break; + case Blocksettle::Communication::ProxyTerminalPb::Response::kUpdateOrder: + processUpdateOrder(response.update_order()); + break; +#endif + default: + break; } } void OrderListModel::onDisconnected() { - connected_ = false; - - reset(); + //reset(); } +#if 0 void OrderListModel::setOrderStatus(Group *group, int index, const bs::network::Order& order, bool emitUpdate) { @@ -520,21 +380,6 @@ void OrderListModel::setOrderStatus(Group *group, int index, const bs::network:: } } -OrderListModel::StatusGroup::Type OrderListModel::getStatusGroup(const bs::network::Order& order) -{ - switch (order.status) { - case bs::network::Order::New: - case bs::network::Order::Pending: - return StatusGroup::UnSettled; - - case bs::network::Order::Filled: - case bs::network::Order::Failed: - return StatusGroup::Settled; - } - - return StatusGroup::Undefined; -} - std::pair OrderListModel::findItem(const bs::network::Order &order) { const auto itGroups = groups_.find(order.exchOrderId.toStdString()); @@ -710,24 +555,28 @@ void OrderListModel::reset() settled_ = std::make_unique(StatusGroup::toString(StatusGroup::Settled), 2); endResetModel(); } +#endif //0 void OrderListModel::onOrdersUpdate(const std::vector& orders) { +#if 0 if (!connected_) { //FIXME: use BS connection event (currently matching one) to set connected_ flag connected_ = true; } +#endif // Save latest selected index first //FIXME: resetLatestChangedStatus(orders); // OrderListModel supposed to work correctly when orders states updated one by one. // We don't use this anymore (server sends all active orders every time) so just clear old caches. // Remove this if old behavior is needed - reset(); + //reset(); for (const auto& order : orders) { - onOrderUpdated(order); + //onOrderUpdated(order); } } +#if 0 void OrderListModel::processUpdateOrders(const ProxyTerminalPb::Response_UpdateOrdersAndObligations&) { reset(); @@ -812,7 +661,7 @@ void OrderListModel::onOrderUpdated(const bs::network::Order& order) setOrderStatus(groupItem, found.second, order, true); } } - +#endif //0 void OrderListModel::Group::addOrder(const bs::network::Order& order) { @@ -821,32 +670,32 @@ void OrderListModel::Group::addOrder(const bs::network::Order& order) QVariant OrderListModel::Group::getQuantity() const { - return QVariant{}; + return {}; } QVariant OrderListModel::Group::getQuantityColor() const { - return QVariant{}; + return {}; } QVariant OrderListModel::Group::getValue() const { - return QVariant{}; + return {}; } QVariant OrderListModel::Group::getValueColor() const { - return QVariant{}; + return {}; } QVariant OrderListModel::Group::getPrice() const { - return QVariant{}; + return {}; } QVariant OrderListModel::Group::getStatus() const { - return QVariant{}; + return {}; } void OrderListModel::Group::addRow(const bs::network::Order& order) @@ -865,201 +714,3 @@ void OrderListModel::Group::addRow(const bs::network::Order& order) order.exchOrderId, &idx_)); } - -void OrderListModel::FuturesGroup::addOrder(const bs::network::Order& order) -{ - quantity_ += order.quantity; - value_ += getOrderValue(order); - addRow(order); -} - -QVariant OrderListModel::FuturesGroup::getQuantity() const -{ - return UiUtils::displayAmount(quantity_); -} - -QVariant OrderListModel::FuturesGroup::getQuantityColor() const -{ - if (quantity_ < 0) { - return kFailedColor; - } - - return kSettledColor; -} - -QVariant OrderListModel::FuturesGroup::getValue() const -{ - return UiUtils::displayCurrencyAmount(value_); -} - -QVariant OrderListModel::FuturesGroup::getValueColor() const -{ - if (value_ < 0) { - return kFailedColor; - } - - return kSettledColor; -} - -void OrderListModel::DisplayFuturesDeliveryRow(const Blocksettle::Communication::ProxyTerminalPb::Response_DeliveryObligationsRequest &obligation) -{ - if (obligation.to_deliver() == 0) { - return; - } - - beginInsertRows(createIndex(pendingFuturesSettlement_->row_, 0, &pendingFuturesSettlement_->idx_), static_cast(pendingFuturesSettlement_->rows_.size()), static_cast(pendingFuturesSettlement_->rows_.size())); - pendingFuturesSettlement_->rows_.emplace_back(make_unique(bs::network::Asset::Type::DeliverableFutures, &pendingFuturesSettlement_->idx_)); - Market * marketItem = pendingFuturesSettlement_->rows_.back().get(); - endInsertRows(); - - beginInsertRows(createIndex(findMarket(pendingFuturesSettlement_.get(), marketItem), 0, &marketItem->idx_), - static_cast(marketItem->rows_.size()), static_cast(marketItem->rows_.size())); - - marketItem->rows_.emplace_back(make_unique(QStringLiteral("XBT/EUR") - , &marketItem->idx_, obligation.to_deliver(), obligation.price(), obligation.bs_address() - , obligation.status() == Blocksettle::Communication::ProxyTerminalPb::Response::DeliveryObligationsRequest::DELIVERED)); - - endInsertRows(); -} - -OrderListModel::FuturesDeliveryGroup::FuturesDeliveryGroup(const QString &sec, IndexHelper *parent - , int64_t quantity, double price - , const std::string& bsAddress - , const bool delivered) - : Group(sec, parent) -{ - quantity_ = static_cast(quantity) / BTCNumericTypes::BalanceDivider; - price_ = price; - bsAddress_ = bsAddress; - - value_ = -quantity_ * price_; - - if (quantity < 0) { - toDeliver_ = bs::XBTAmount{ static_cast(-quantity) }; - } - - delivered_ = delivered; -} - -QVariant OrderListModel::FuturesDeliveryGroup::getQuantity() const -{ - return UiUtils::displayAmount(quantity_); -} - -QVariant OrderListModel::FuturesDeliveryGroup::getQuantityColor() const -{ - if (quantity_ < 0) { - return kFailedColor; - } - - return kSettledColor; -} - -QVariant OrderListModel::FuturesDeliveryGroup::getValue() const -{ - return UiUtils::displayCurrencyAmount(value_); -} - -QVariant OrderListModel::FuturesDeliveryGroup::getValueColor() const -{ - if (value_ < 0) { - return kFailedColor; - } - - return kSettledColor; -} - -QVariant OrderListModel::FuturesDeliveryGroup::getPrice() const -{ - return UiUtils::displayPriceXBT(price_); -} - -QVariant OrderListModel::FuturesDeliveryGroup::getStatus() const -{ - if (quantity_ < 0) { - if (delivered_) { - return tr("Delivered"); - } - return tr("Create TX"); - } - - return QVariant{}; -} - -bool OrderListModel::FuturesDeliveryGroup::deliveryRequired() const -{ - return toDeliver_.GetValue() != 0 && !delivered_; -} - -OrderListModel::DeliveryObligationData OrderListModel::FuturesDeliveryGroup::getDeliveryObligationData() const -{ - return DeliveryObligationData{bsAddress_, toDeliver_}; -} - -bool OrderListModel::DeliveryRequired(const QModelIndex &index) -{ - if (!isFutureDeliveryIndex(index)) { - return false; - } - - auto futuresDelivery = GetFuturesDeliveryGroup(index); - if (futuresDelivery == nullptr) { - return false; - } - - return futuresDelivery->deliveryRequired(); -} - -bool OrderListModel::isFutureDeliveryIndex(const QModelIndex &index) const -{ - if (!index.isValid() || index.column() != Header::Status || !pendingFuturesSettlement_) { - return false; - } - - { - auto statusGroupIndex = index; - int depth = 0; - while (statusGroupIndex.parent().isValid()) { - statusGroupIndex = statusGroupIndex.parent(); - ++depth; - } - - if (statusGroupIndex.row() != pendingFuturesSettlement_->row_ || depth != 2) { - return false; - } - } - - return true; -} - -OrderListModel::FuturesDeliveryGroup* OrderListModel::GetFuturesDeliveryGroup(const QModelIndex &index) const -{ - auto marketIndex = index.parent(); - Market* market = pendingFuturesSettlement_->rows_[marketIndex.row()].get(); - - return dynamic_cast(market->rows_[index.row()].get()); -} - -OrderListModel::DeliveryObligationData OrderListModel::getDeliveryObligationData(const QModelIndex& index) const -{ - if (!isFutureDeliveryIndex(index)) { - return DeliveryObligationData{}; - } - - auto futuresDelivery = GetFuturesDeliveryGroup(index); - if (futuresDelivery == nullptr) { - return DeliveryObligationData{}; - } - - return futuresDelivery->getDeliveryObligationData(); -} - -void OrderListModel::onMatchingLogin() -{ - connected_ = true; -} - -void OrderListModel::onMatchingLogout() -{ - connected_ = false; -} diff --git a/BlockSettleUILib/unused/OrderListModel.h b/BlockSettleUILib/unused/OrderListModel.h new file mode 100644 index 000000000..35e6137be --- /dev/null +++ b/BlockSettleUILib/unused/OrderListModel.h @@ -0,0 +1,153 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2021, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef ORDERLISTMODEL_H +#define ORDERLISTMODEL_H + +#include +#include +#include +#include + +#include "CommonTypes.h" +#include +#include +#include + +namespace Blocksettle { + namespace Communication { + namespace ProxyTerminalPb { + class Response; + class Response_UpdateOrder; + } + } +} + +class AssetManager; + +class OrderListModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + struct Header { + enum Index { + Time = 0, + NameColumn = 0, + Product, + Side, + Quantity, + Price, + Value, + Status, + last + }; + static QString toString(Index); + }; + + OrderListModel(QObject* parent = nullptr); + ~OrderListModel() noexcept override = default; + + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + + //void onOrdersUpdate(const std::vector &); + +public slots: + void onMessageFromPB(const Blocksettle::Communication::ProxyTerminalPb::Response &response); + void onDisconnected(); + +signals: + void newOrder(const QPersistentModelIndex &index); + void selectRow(const QPersistentModelIndex &row); + +private: + enum class DataType { + Data = 0, + Group + }; + + struct IndexHelper { + IndexHelper *parent_; + void *data_; + DataType type_; + + IndexHelper(IndexHelper *parent, void *data, DataType type) + : parent_(parent) + , data_(data) + , type_(type) + {} + }; + + struct Data { + QString time_; + QString product_; + QString side_; + QString quantity_; + QString price_; + QString value_; + QString status_; + QString id_; + QColor statusColor_; + IndexHelper idx_; + + Data(const QString &time, const QString &prod, + const QString &side, const QString &quantity, const QString &price, + const QString &value, const QString &status, const QString &id, + IndexHelper *parent) + : time_(time) + , product_(prod) + , side_(side) + , quantity_(quantity) + , price_(price) + , value_(value) + , status_(status) + , id_(id) + , statusColor_(Qt::white) + , idx_(parent, this, DataType::Data) + { + } + }; + + struct Group + { + std::deque> rows_; + QString security_; + IndexHelper idx_; + + Group(const QString &sec, IndexHelper *parent) + : security_(sec) + , idx_(parent, this, DataType::Group) + { + } + + virtual ~Group() = default; + + virtual void addOrder(const bs::network::Order& order); + + virtual QVariant getQuantity() const; + virtual QVariant getQuantityColor() const; + + virtual QVariant getValue() const; + virtual QVariant getValueColor() const; + virtual QVariant getPrice() const; + + virtual QVariant getStatus() const; + + protected: + void addRow(const bs::network::Order& order); + }; + +#endif // ORDERLISTMODEL_H diff --git a/BlockSettleUILib/OrdersView.cpp b/BlockSettleUILib/unused/OrdersView.cpp similarity index 100% rename from BlockSettleUILib/OrdersView.cpp rename to BlockSettleUILib/unused/OrdersView.cpp diff --git a/BlockSettleUILib/OrdersView.h b/BlockSettleUILib/unused/OrdersView.h similarity index 100% rename from BlockSettleUILib/OrdersView.h rename to BlockSettleUILib/unused/OrdersView.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 511c918a4..8f075c2fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,18 +416,11 @@ ELSE ( UNIX ) SET( PROTO_LIB ${PROTOBUF_LIBRARIES} ) ENDIF ( UNIX ) -# autheid helper sources -INCLUDE_DIRECTORIES( ${TERMINAL_GUI_ROOT}/AuthAPI/utils/cpp ) -SET( AUTHEID_UTILS ${TERMINAL_GUI_ROOT}/AuthAPI/utils/cpp/autheid_utils.cpp ) - - SET( PROTO_ROOT_DIR ${TERMINAL_GUI_ROOT} ) SET( PATH_TO_GENERATED ${TERMINAL_GUI_ROOT}/generated_proto ) FILE( MAKE_DIRECTORY ${PATH_TO_GENERATED} ) -SET( AUTH_PROTO_LIB_NAME AuthAPI ) SET( BS_PROTO_LIB_NAME BsProtoLib ) -SET( CELER_PROTO_LIB_NAME CelerProtoLib ) SET( BLOCKSETTLE_APP_NAME blocksettle ) SET( SIGNER_APP_NAME blocksettle_signer ) SET( TERMINAL_CORE_NAME TerminalCore ) @@ -874,8 +867,6 @@ ENDIF() ADD_SUBDIRECTORY( common/Blocksettle_proto ) -ADD_SUBDIRECTORY( Celer ) -ADD_SUBDIRECTORY( AuthAPI ) ADD_SUBDIRECTORY(Core) ADD_SUBDIRECTORY(GUI) diff --git a/Celer b/Celer deleted file mode 160000 index d94cdcdea..000000000 --- a/Celer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d94cdcdea94653ad581e4419fc74172dd0ab3b2f diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp index 901590432..7acfb9b00 100644 --- a/Core/ApiAdapter.cpp +++ b/Core/ApiAdapter.cpp @@ -87,7 +87,7 @@ class ApiBusGateway : public ApiBusAdapter if (std::dynamic_pointer_cast(env.receiver)) { if (parent_->pushFill(envCopy)) { if (env.isRequest()) { - idMap_[envCopy.id()] = { env.id(), env.sender }; + idMap_[envCopy.foreignId()] = { env.foreignId(), env.sender }; } return true; } @@ -143,7 +143,7 @@ class ApiBusGateway : public ApiBusAdapter } bool rc = pushFill(envCopy); if (rc && env.isRequest()) { - idMap_[envCopy.id()] = { env.id(), env.sender }; + idMap_[envCopy.foreignId()] = { env.foreignId(), env.sender }; } return rc; } diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index c927c38f1..df9f6bd13 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -11,7 +11,6 @@ #include "ApiJson.h" #include #include "Address.h" -#include "BsClient.h" #include "MessageUtils.h" #include "ProtobufUtils.h" #include "SslServerConnection.h" @@ -69,7 +68,7 @@ bool ApiJsonAdapter::process(const Envelope &env) } } else { - logger_->warn("[{}] non-terminal #{} user {}", __func__, env.id() + logger_->warn("[{}] non-terminal #{} user {}", __func__, env.foreignId() , env.sender->name()); } return true; @@ -239,7 +238,7 @@ bool ApiJsonAdapter::processSettings(const Envelope &env) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -302,7 +301,7 @@ bool ApiJsonAdapter::processAdminMessage(const Envelope &env) { AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse admin msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse admin msg #{}", __func__, env.foreignId()); return false; } switch (msg.data_case()) { @@ -319,7 +318,7 @@ bool ApiJsonAdapter::processBlockchain(const Envelope &env) ArmoryMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[QtGuiAdapter::processBlockchain] failed to parse msg #{}" - , env.id()); + , env.foreignId()); if (!env.receiver) { logger_->debug("[{}] no receiver", __func__); } @@ -361,7 +360,7 @@ bool ApiJsonAdapter::processSigner(const Envelope &env) SignerMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[QtGuiAdapter::processSigner] failed to parse msg #{}" - , env.id()); + , env.foreignId()); if (!env.receiver) { logger_->debug("[{}] no receiver", __func__); } @@ -386,23 +385,23 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) { WalletsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { - case WalletsMessage::kWalletLoaded: [[fallthrough]]; - case WalletsMessage::kAuthWallet: [[fallthrough]]; + case WalletsMessage::kWalletLoaded: + case WalletsMessage::kAuthWallet: case WalletsMessage::kHdWallet: sendReplyToClient(0, msg, env.sender); break; - case WalletsMessage::kWalletAddresses: [[fallthrough]]; - case WalletsMessage::kAddrComments: [[fallthrough]]; - case WalletsMessage::kWalletData: [[fallthrough]]; - case WalletsMessage::kWalletBalances: [[fallthrough]]; - case WalletsMessage::kTxDetailsResponse: [[fallthrough]]; - case WalletsMessage::kWalletsListResponse: [[fallthrough]]; - case WalletsMessage::kUtxos: [[fallthrough]]; - case WalletsMessage::kAuthKey: [[fallthrough]]; + case WalletsMessage::kWalletAddresses: + case WalletsMessage::kAddrComments: + case WalletsMessage::kWalletData: + case WalletsMessage::kWalletBalances: + case WalletsMessage::kTxDetailsResponse: + case WalletsMessage::kWalletsListResponse: + case WalletsMessage::kUtxos: + case WalletsMessage::kAuthKey: case WalletsMessage::kReservedUtxos: if (hasRequest(env.responseId())) { sendReplyToClient(env.responseId(), msg, env.sender); @@ -417,7 +416,7 @@ bool ApiJsonAdapter::processOnChainTrack(const Envelope &env) { OnChainTrackMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -434,7 +433,7 @@ bool ApiJsonAdapter::processAssets(const bs::message::Envelope& env) { AssetsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -473,7 +472,7 @@ bool ApiJsonAdapter::processBsServer(const bs::message::Envelope& env) { BsServerMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -498,7 +497,7 @@ bool ApiJsonAdapter::processSettlement(const bs::message::Envelope& env) { SettlementMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -520,7 +519,7 @@ bool ApiJsonAdapter::processMatching(const bs::message::Envelope& env) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -542,7 +541,7 @@ bool ApiJsonAdapter::processMktData(const bs::message::Envelope& env) { MktDataMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { diff --git a/Core/AssetsAdapter.cpp b/Core/AssetsAdapter.cpp index c7117a112..a26d742b0 100644 --- a/Core/AssetsAdapter.cpp +++ b/Core/AssetsAdapter.cpp @@ -31,7 +31,7 @@ bool AssetsAdapter::process(const bs::message::Envelope &env) if (env.sender->value() == bs::message::TerminalUsers::Settings) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings message #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -45,7 +45,7 @@ bool AssetsAdapter::process(const bs::message::Envelope &env) else if (env.sender->value() == bs::message::TerminalUsers::Matching) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse matching message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse matching message #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -57,7 +57,7 @@ bool AssetsAdapter::process(const bs::message::Envelope &env) else if (env.receiver->value() == user_->value()) { AssetsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse own message #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -75,7 +75,7 @@ bool AssetsAdapter::processBroadcast(const bs::message::Envelope& env) AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[{}] failed to parse administrative message #{}" - , __func__, env.id()); + , __func__, env.foreignId()); return false; } if (msg.data_case() == AdministrativeMessage::kStart) { @@ -91,7 +91,7 @@ bool AssetsAdapter::processBroadcast(const bs::message::Envelope& env) else if (env.sender->value() == bs::message::TerminalUsers::Matching) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse matching message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse matching message #{}", __func__, env.foreignId()); return false; } switch (msg.data_case()) { @@ -103,7 +103,7 @@ bool AssetsAdapter::processBroadcast(const bs::message::Envelope& env) else if (env.sender->value() == bs::message::TerminalUsers::BsServer) { BsServerMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse BS message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse BS message #{}", __func__, env.foreignId()); return false; } switch (msg.data_case()) { diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 1e76a1942..6ba839c99 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -44,7 +44,8 @@ bool BsServerAdapter::process(const bs::message::Envelope &env) if (env.sender->value() == TerminalUsers::Settings) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings message #{}", __func__ + , env.foreignId()); return true; } if (msg.data_case() == SettingsMessage::kGetResponse) { @@ -62,7 +63,8 @@ bool BsServerAdapter::processBroadcast(const bs::message::Envelope& env) if (env.sender->isSystem()) { AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse administrative message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse administrative message #{}" + , __func__, env.foreignId()); return false; } switch (msg.data_case()) { @@ -92,14 +94,16 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) { BsServerMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own request #{}", __func__, env.id()); + logger_->error("[{}] failed to parse own request #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { case BsServerMessage::kOpenConnection: return processOpenConnection(); case BsServerMessage::kCloseConnection: +#if 0 bsClient_.reset(); +#endif break; case BsServerMessage::kStartLogin: return processStartLogin(msg.start_login()); @@ -109,6 +113,7 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) return processPuBKeyResponse(msg.pub_new_key_response()); case BsServerMessage::kTimeout: return processTimeout(msg.timeout()); +#if 0 case BsServerMessage::kSendMatching: if (bsClient_) { bsClient_->celerSend(static_cast(msg.send_matching().message_type()) @@ -123,6 +128,7 @@ bool BsServerAdapter::processOwnRequest(const Envelope &env) return processOutSignedPayin(msg.send_signed_payin()); case BsServerMessage::kSendSignedPayout: return processOutSignedPayout(msg.send_signed_payout()); +#endif default: break; } return true; @@ -177,7 +183,9 @@ bool BsServerAdapter::processOpenConnection() logger_->error("[{}] already connected", __func__); return true; } +#if 0 bsClient_ = std::make_unique(logger_, this); +#endif bs::network::BIP15xParams params; params.ephemeralPeers = true; params.authMode = bs::network::BIP15xAuthMode::OneWay; @@ -197,6 +205,7 @@ bool BsServerAdapter::processOpenConnection() auto wsConnection = std::make_unique(logger_, WsDataConnectionParams{}); auto connection = std::make_unique(logger_, std::move(wsConnection), bip15xTransport); +#if 0 if (!connection->openConnection(PubKeyLoader::serverHostName(PubKeyLoader::KeyType::Proxy, envConfig_) , PubKeyLoader::serverHttpPort(), bsClient_.get())) { logger_->error("[{}] failed to set up connection to {}", __func__ @@ -204,6 +213,7 @@ bool BsServerAdapter::processOpenConnection() return false; //TODO: send negative result response, maybe? } bsClient_->setConnection(std::move(connection)); +#endif return true; } @@ -214,10 +224,12 @@ bool BsServerAdapter::processStartLogin(const std::string& login) } if (currentLogin_.empty()) { currentLogin_ = login; +#if 0 bsClient_->startLogin(login); +#endif } else { - onStartLoginDone(true, {}); + //onStartLoginDone(true, {}); } return true; } @@ -228,134 +240,13 @@ bool BsServerAdapter::processCancelLogin() logger_->warn("[BsServerAdapter::processCancelLogin] no login started - ignoring request"); return true; } +#if 0 bsClient_->cancelLogin(); +#endif return true; } -bool BsServerAdapter::processSubmitAuthAddr(const bs::message::Envelope& env - , const std::string& addr) -{ - const auto& sendReply = [this, env](bs::error::AuthAddressSubmitResult code) - { - BsServerMessage msg; - msg.set_submit_auth_result((int)code); - pushResponse(user_, env, msg.SerializeAsString()); - }; - const auto& address = bs::Address::fromAddressString(addr); - bsClient_->signAuthAddress(address, [this, address, sendReply](const BsClient::SignResponse& response) { - if (response.userCancelled) { - logger_->error("[BsServerAdapter::processSubmitAuthAddr] signing auth " - "address cancelled: {}", response.errorMsg); - sendReply(bs::error::AuthAddressSubmitResult::AuthSignCancelled); - return; - } - if (!response.success) { - logger_->error("[BsServerAdapter::processSubmitAuthAddr] signing auth " - "address failed: {}", response.errorMsg); - sendReply(bs::error::AuthAddressSubmitResult::AuthRequestSignFailed); - return; - } - logger_->debug("[BsServerAdapter::processSubmitAuthAddr] signing auth address succeed"); - - bsClient_->confirmAuthAddress(address, [this, address, sendReply](bs::error::AuthAddressSubmitResult submitResult) { - sendReply(submitResult); - if (submitResult != bs::error::AuthAddressSubmitResult::Success) { - logger_->error("[BsServerAdapter::processSubmitAuthAddr] confirming" - " auth address failed: {}", static_cast(submitResult)); - } - else { - logger_->debug("[BsServerAdapter::processSubmitAuthAddr] confirming" - " auth address succeed"); - } - - AssetsMessage msg; - msg.set_submit_auth_address(address.display()); - pushRequest(user_, UserTerminal::create(TerminalUsers::Assets) - , msg.SerializeAsString()); - }); - }); - return true; -} - -void BsServerAdapter::processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders& orders) -{ - BsServerMessage msg; - auto msgOrders = msg.mutable_orders_update(); - for (const auto& order : orders.orders()) { - auto msgOrder = msgOrders->add_orders(); - switch (order.status()) { - case bs::types::ORDER_STATUS_PENDING: - msgOrder->set_status((int)bs::network::Order::Pending); - break; - case bs::types::ORDER_STATUS_FILLED: - msgOrder->set_status((int)bs::network::Order::Filled); - break; - case bs::types::ORDER_STATUS_VOID: - msgOrder->set_status((int)bs::network::Order::Failed); - break; - default: - break; - } - msgOrder->set_status_text(order.status_text()); - msgOrder->set_product(order.product()); - msgOrder->set_contra_product(order.product_against()); - msgOrder->set_buy(order.side() == bs::types::Side::SIDE_BUY); - msgOrder->set_quantity(order.quantity()); - msgOrder->set_price(order.price()); - msgOrder->set_timestamp(order.timestamp_ms()); - } - pushBroadcast(user_, msg.SerializeAsString()); -} - -void BsServerAdapter::processUnsignedPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_UnsignedPayinRequest& response) -{ - BsServerMessage msg; - msg.set_unsigned_payin_requested(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); - pushResponse(user_, userSettl_, msg.SerializeAsString()); -} - -void BsServerAdapter::processSignPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayinRequest& response) -{ - BsServerMessage msg; - auto msgBC = msg.mutable_signed_payin_requested(); - msgBC->set_settlement_id(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); - msgBC->set_unsigned_payin(BinaryData::fromString(response.unsigned_payin_data()).toBinStr()); - msgBC->set_payin_hash(BinaryData::fromString(response.payin_hash()).toBinStr()); - msgBC->set_timestamp(response.timestamp_ms()); - pushResponse(user_, userSettl_, msg.SerializeAsString()); -} - -void BsServerAdapter::processSignPayout(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayoutRequest& response) -{ - BsServerMessage msg; - auto msgBC = msg.mutable_signed_payout_requested(); - msgBC->set_settlement_id(BinaryData::CreateFromHex(response.settlement_id()).toBinStr()); - msgBC->set_payin_hash(BinaryData::fromString(response.payin_data()).toBinStr()); - msgBC->set_timestamp(response.timestamp_ms()); - pushResponse(user_, userSettl_, msg.SerializeAsString()); -} - -bool BsServerAdapter::processOutUnsignedPayin(const BsServerMessage_XbtTransaction& request) -{ - const auto& settlementId = BinaryData::fromString(request.settlement_id()); - bsClient_->sendUnsignedPayin(settlementId.toHexStr(), { request.tx() }); - return true; -} - -bool BsServerAdapter::processOutSignedPayin(const BsServerMessage_XbtTransaction& request) -{ - const auto& settlementId = BinaryData::fromString(request.settlement_id()); - bsClient_->sendSignedPayin(settlementId.toHexStr(), BinaryData::fromString(request.tx())); - return true; -} - -bool BsServerAdapter::processOutSignedPayout(const BsServerMessage_XbtTransaction& request) -{ - const auto& settlementId = BinaryData::fromString(request.settlement_id()); - bsClient_->sendSignedPayout(settlementId.toHexStr(), BinaryData::fromString(request.tx())); - return true; -} - +#if 0 void BsServerAdapter::startTimer(std::chrono::milliseconds timeout , const std::function&cb) { @@ -432,16 +323,6 @@ void BsServerAdapter::onGetLoginResultDone(const BsClientLoginResult& result) currentLogin_.clear(); } -void BsServerAdapter::onCelerRecv(CelerAPI::CelerMessageType messageType - , const std::string& data) -{ - BsServerMessage msg; - auto msgResp = msg.mutable_recv_matching(); - msgResp->set_message_type((int)messageType); - msgResp->set_data(data); - pushResponse(user_, userMtch_, msg.SerializeAsString()); -} - void BsServerAdapter::onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response& response) { switch (response.data_case()) { @@ -498,3 +379,4 @@ void BsServerAdapter::onTradingStatusChanged(bool tradingEnabled) msg.set_trading_enabled(tradingEnabled); pushBroadcast(user_, msg.SerializeAsString()); } +#endif diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index 83b53b16e..0f4351384 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -12,7 +12,6 @@ #define BS_SERVER_ADAPTER_H #include "ApplicationSettings.h" -#include "BsClient.h" #include "FutureValue.h" #include "Message/Adapter.h" @@ -38,7 +37,7 @@ namespace BlockSettle { class ConnectionManager; class RequestReplyCommand; -class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarget +class BsServerAdapter : public bs::message::Adapter { public: BsServerAdapter(const std::shared_ptr &); @@ -61,40 +60,21 @@ class BsServerAdapter : public bs::message::Adapter, public BsClientCallbackTarg bool processOpenConnection(); bool processStartLogin(const std::string&); bool processCancelLogin(); - bool processSubmitAuthAddr(const bs::message::Envelope&, const std::string &addr); - void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders&); - void processUnsignedPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_UnsignedPayinRequest&); - void processSignPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayinRequest&); - void processSignPayout(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayoutRequest&); + //bool processSubmitAuthAddr(const bs::message::Envelope&, const std::string &addr); + //void processUpdateOrders(const Blocksettle::Communication::ProxyTerminalPb::Response_UpdateOrders&); + //void processUnsignedPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_UnsignedPayinRequest&); + //void processSignPayin(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayinRequest&); + //void processSignPayout(const Blocksettle::Communication::ProxyTerminalPb::Response_SignPayoutRequest&); - bool processOutUnsignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); - bool processOutSignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); - bool processOutSignedPayout(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); - - //BCT callbacks - void startTimer(std::chrono::milliseconds timeout, const std::function&) override; - void onStartLoginDone(bool success, const std::string& errorMsg) override; - void onGetLoginResultDone(const BsClientLoginResult& result) override; -// void onAuthorizeDone(AuthorizeError authErr, const std::string& email) override; - void onCelerRecv(CelerAPI::CelerMessageType messageType, const std::string& data) override; - void onProcessPbMessage(const Blocksettle::Communication::ProxyTerminalPb::Response&) override; - void Connected() override; - void Disconnected() override; - void onConnectionFailed() override; -/* void onEmailHashReceived(const std::string& email, const std::string& hash) override; - void onBootstrapDataUpdated(const std::string& data) override; - void onAccountStateChanged(bs::network::UserType userType, bool enabled) override; - void onFeeRateReceived(float feeRate) override; - void onBalanceLoaded() override;*/ - void onBalanceUpdated(const std::string& currency, double balance) override; - void onTradingStatusChanged(bool tradingEnabled) override; + //bool processOutUnsignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); + //bool processOutSignedPayin(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); + //bool processOutSignedPayout(const BlockSettle::Terminal::BsServerMessage_XbtTransaction&); private: std::shared_ptr logger_; std::shared_ptr user_, userSettl_, userSettings_; std::shared_ptr userMtch_, userWallets_; std::shared_ptr connMgr_; - std::unique_ptr bsClient_; ApplicationSettings::EnvConfiguration envConfig_{ ApplicationSettings::EnvConfiguration::Unknown }; bool connected_{ false }; std::string currentLogin_; diff --git a/Core/MDHistAdapter.cpp b/Core/MDHistAdapter.cpp index 87bc3a353..92ae0aa88 100644 --- a/Core/MDHistAdapter.cpp +++ b/Core/MDHistAdapter.cpp @@ -33,7 +33,7 @@ bool MDHistAdapter::processBroadcast(const bs::message::Envelope& env) if (env.sender->isSystem()) { AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse administrative message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse administrative message #{}", __func__, env.foreignId()); return false; } if (msg.data_case() == AdministrativeMessage::kStart) { diff --git a/Core/MktDataAdapter.cpp b/Core/MktDataAdapter.cpp index ae7299f82..b5e153027 100644 --- a/Core/MktDataAdapter.cpp +++ b/Core/MktDataAdapter.cpp @@ -39,7 +39,8 @@ bool MktDataAdapter::process(const bs::message::Envelope &env) if (env.receiver->value() == user_->value()) { MktDataMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own request #{}", __func__, env.id()); + logger_->error("[{}] failed to parse own request #{}" + , __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -58,7 +59,8 @@ bool MktDataAdapter::processBroadcast(const bs::message::Envelope& env) if (env.sender->isSystem()) { AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse administrative message #{}", __func__, env.id()); + logger_->error("[{}] failed to parse administrative message #{}" + , __func__, env.foreignId()); return false; } if (msg.data_case() == AdministrativeMessage::kStart) { diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index bd11478af..2a35c6679 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -132,7 +132,7 @@ bool SettingsAdapter::process(const bs::message::Envelope &env) if (env.receiver->value() == TerminalUsers::Settings) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -194,7 +194,7 @@ bool SettingsAdapter::processBroadcast(const bs::message::Envelope& env) if (env.sender->value() == TerminalUsers::Blockchain) { ArmoryMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse armory msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse armory msg #{}", __func__, env.foreignId()); return false; } if (msg.data_case() == ArmoryMessage::kSettingsRequest) { diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 5e8860258..ac2894d40 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -42,7 +42,7 @@ bool SignerAdapter::process(const bs::message::Envelope &env) if (env.sender->value() == TerminalUsers::Settings) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -53,7 +53,7 @@ bool SignerAdapter::process(const bs::message::Envelope &env) else if (env.receiver->value() == TerminalUsers::Signer) { SignerMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse own msg #{}", __func__, env.foreignId()); return true; } if (env.isRequest()) { @@ -75,7 +75,7 @@ bool SignerAdapter::processBroadcast(const bs::message::Envelope& env) if (env.sender->value() == TerminalUsers::System) { AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse administrative msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse administrative msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { diff --git a/Core/ChatAdapter.cpp b/Core/unused/ChatAdapter.cpp similarity index 100% rename from Core/ChatAdapter.cpp rename to Core/unused/ChatAdapter.cpp diff --git a/Core/ChatAdapter.h b/Core/unused/ChatAdapter.h similarity index 100% rename from Core/ChatAdapter.h rename to Core/unused/ChatAdapter.h diff --git a/Core/MatchingAdapter.cpp b/Core/unused/MatchingAdapter.cpp similarity index 100% rename from Core/MatchingAdapter.cpp rename to Core/unused/MatchingAdapter.cpp diff --git a/Core/MatchingAdapter.h b/Core/unused/MatchingAdapter.h similarity index 100% rename from Core/MatchingAdapter.h rename to Core/unused/MatchingAdapter.h diff --git a/Core/SettlementAdapter.cpp b/Core/unused/SettlementAdapter.cpp similarity index 100% rename from Core/SettlementAdapter.cpp rename to Core/unused/SettlementAdapter.cpp diff --git a/Core/SettlementAdapter.h b/Core/unused/SettlementAdapter.h similarity index 100% rename from Core/SettlementAdapter.h rename to Core/unused/SettlementAdapter.h diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index de2812d90..9b6f7b5fe 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,6 @@ #include "ui_BSTerminalMainWindow.h" #include "ApiAdapter.h" #include "ApplicationSettings.h" -#include "AuthAddressDialog.h" #include "BSMessageBox.h" #include "CreateTransactionDialogAdvanced.h" #include "CreateTransactionDialogSimple.h" @@ -37,7 +37,6 @@ #include "InfoDialogs/SupportDialog.h" #include "LoginWindow.h" #include "NotificationCenter.h" -#include "OrderListModel.h" #include "Settings/ConfigDialog.h" #include "StatusBarView.h" #include "TabWithShortcut.h" @@ -92,9 +91,7 @@ MainWindow::MainWindow(const std::shared_ptr &logger void MainWindow::setWidgetsAuthorized(bool authorized) { // Update authorized state for some widgets - ui_->widgetPortfolio->setAuthorized(authorized); - ui_->widgetRFQ->setAuthorized(authorized); - ui_->widgetChart->setAuthorized(authorized); + //ui_->widgetPortfolio->setAuthorized(authorized); } void MainWindow::onGetGeometry(const QRect &mainGeom) @@ -164,14 +161,7 @@ void MainWindow::onSetting(int setting, const QVariant &value) } break; case ApplicationSettings::rememberLoginUserName: - if (loginDlg_) { - loginDlg_->setRememberLogin(value.toBool()); - } - break; case ApplicationSettings::celerUsername: - if (loginDlg_) { - loginDlg_->setLogin(value.toString()); - } break; default: break; } @@ -236,8 +226,6 @@ void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { ui_->widgetWallets->onHDWalletDetails(hdWallet); ui_->widgetPortfolio->onHDWalletDetails(hdWallet); - ui_->widgetRFQ->onHDWallet(hdWallet); - ui_->widgetRFQReply->onHDWallet(hdWallet); } void MainWindow::onWalletsList(const std::string &id, const std::vector& wallets) @@ -250,7 +238,7 @@ void MainWindow::onWalletsList(const std::string &id, const std::vectorwidgetRFQ->onWalletData(walletId, wd); + //ui_->widgetRFQ->onWalletData(walletId, wd); } void MainWindow::onAddresses(const std::string& walletId @@ -282,8 +270,6 @@ void MainWindow::onWalletBalance(const bs::sync::WalletBalanceData &wbd) } ui_->widgetWallets->onWalletBalance(wbd); ui_->widgetPortfolio->onWalletBalance(wbd); - ui_->widgetRFQ->onWalletBalance(wbd); - ui_->widgetRFQReply->onWalletBalance(wbd); statusBarView_->onXbtBalance(wbd); } @@ -721,7 +707,7 @@ void MainWindow::setupMenu() }; connect(ui_->actionCreateNewWallet, &QAction::triggered, this, [ww = ui_->widgetWallets]{ ww->onNewWallet(); }); - connect(ui_->actionAuthenticationAddresses, &QAction::triggered, this, &MainWindow::openAuthManagerDialog); +// connect(ui_->actionAuthenticationAddresses, &QAction::triggered, this, &MainWindow::openAuthManagerDialog); connect(ui_->actionSettings, &QAction::triggered, this, [=]() { openConfigDialog(); }); // connect(ui_->actionAccountInformation, &QAction::triggered, this, &MainWindow::openAccountInfoDialog); // connect(ui_->actionEnterColorCoinToken, &QAction::triggered, this, &MainWindow::openCCTokenDialog); @@ -731,7 +717,7 @@ void MainWindow::setupMenu() connect(ui_->actionVideoTutorials, &QAction::triggered, supportDlgCb(1, QObject::tr("Video Tutorials"))); connect(ui_->actionContact, &QAction::triggered, supportDlgCb(2, QObject::tr("Support"))); - onMatchingLogout(); + //onMatchingLogout(); #ifndef Q_OS_MAC ui_->horizontalFrame->hide(); @@ -755,13 +741,6 @@ void MainWindow::setupMenu() #endif // !PRODUCTION_BUILD } -void bs::gui::qt::MainWindow::openAuthManagerDialog() -{ - if (authAddrDlg_) { - authAddrDlg_->exec(); - } -} - void MainWindow::openConfigDialog(bool showInNetworkPage) { cfgDlg_ = new ConfigDialog(this); @@ -795,103 +774,11 @@ void MainWindow::onLoginInitiated() if (!actLogin_->isEnabled()) { return; } - //TODO: prompt for primary wallet creation if needed emit needOpenBsConnection(); - loginDlg_ = new LoginWindow(logger_, envConfig_, this); - emit getSettings({ ApplicationSettings::rememberLoginUserName, ApplicationSettings::celerUsername }); - connect(loginDlg_, &QDialog::finished, [this] { - loginDlg_->deleteLater(); - loginDlg_ = nullptr; - }); - connect(loginDlg_, &LoginWindow::putSetting, this, &MainWindow::putSetting); - connect(loginDlg_, &LoginWindow::needStartLogin, this, &MainWindow::needStartLogin); - connect(loginDlg_, &LoginWindow::needCancelLogin, this, &MainWindow::needCancelLogin); - - loginDlg_->exec(); } void bs::gui::qt::MainWindow::onLoginStarted(const std::string& login, bool success, const std::string& errMsg) { - if (loginDlg_) { - loginDlg_->onLoginStarted(login, success, errMsg); - } -} - -void MainWindow::onLoggedIn(const BsClientLoginResult& result) -{ - if (loginDlg_) { - loginDlg_->onLoggedIn(result); - } - if (result.status != AutheIDClient::ErrorType::NoError) { - onLoggedOut(); - return; - } - bool isRegistered = (result.userType == bs::network::UserType::Market - || result.userType == bs::network::UserType::Trading - || result.userType == bs::network::UserType::Dealing); - - if (!isRegistered && envConfig_ == ApplicationSettings::EnvConfiguration::Test) { - auto createTestAccountUrl = tr("async retrieval of GetAccount_UrlTest"); - BSMessageBox dlg(BSMessageBox::info, tr("Create Test Account") - , tr("Create a BlockSettle test account") - , tr("

Login requires a test account - create one in minutes on test.blocksettle.com

" - "

Once you have registered, return to login in the Terminal.

" - "
Create Test Account Now") - .arg(createTestAccountUrl).arg(BSMessageBox::kUrlColor), this); - dlg.setOkVisible(false); - dlg.setCancelVisible(true); - dlg.enableRichText(); - dlg.exec(); - return; - } - - if (!isRegistered && envConfig_ == ApplicationSettings::EnvConfiguration::Production) { - auto createAccountUrl = tr("async retrieval of GetAccount_UrlProd"); - BSMessageBox dlg(BSMessageBox::info, tr("Create Account") - , tr("Create a BlockSettle account") - , tr("

Login requires an account - create one in minutes on blocksettle.com

" - "

Once you have registered, return to login in the Terminal.

" - "Create Account Now") - .arg(createAccountUrl).arg(BSMessageBox::kUrlColor), this); - dlg.setOkVisible(false); - dlg.setCancelVisible(true); - dlg.enableRichText(); - dlg.exec(); - return; - } - - activateClient(result); -} - -void MainWindow::activateClient(const BsClientLoginResult& result) -{ - currentUserLogin_ = QString::fromStdString(result.login); - - auto tradeSettings = std::make_shared(result.tradeSettings); - emit putSetting(ApplicationSettings::SubmittedAddressXbtLimit - , static_cast(tradeSettings->xbtTier1Limit)); - - setLoginButtonText(currentUserLogin_); - setWidgetsAuthorized(true); - - ui_->widgetRFQ->onTradeSettings(tradeSettings); - - ui_->widgetWallets->setUsername(currentUserLogin_); - actLogout_->setVisible(false); - actLogin_->setEnabled(false); - - // Market data, charts and chat should be available for all Auth eID logins - emit needMdConnection(envConfig_); - - accountEnabled_ = result.enabled; - onAccountTypeChanged(result.userType, result.enabled); - - if (!authAddrDlg_) { - authAddrDlg_ = new AuthAddressDialog(logger_, this); - connect(authAddrDlg_, &AuthAddressDialog::putSetting, this, &MainWindow::putSetting); - connect(authAddrDlg_, &AuthAddressDialog::needNewAuthAddress, this, &MainWindow::needNewAuthAddress); - connect(authAddrDlg_, &AuthAddressDialog::needSubmitAuthAddress, this, &MainWindow::needSubmitAuthAddress); - } } void MainWindow::onAccountTypeChanged(bs::network::UserType userType, bool enabled) @@ -901,44 +788,12 @@ void MainWindow::onAccountTypeChanged(bs::network::UserType userType, bool enabl notifCenter_->enqueue(enabled ? bs::ui::NotifyType::AccountEnabled : bs::ui::NotifyType::AccountDisabled, {}); } - ui_->widgetChat->setUserType(enabled ? userType : bs::network::UserType::Chat); -} - -void bs::gui::qt::MainWindow::onMatchingLogin(const std::string& mtchLogin - , BaseCelerClient::CelerUserType userType, const std::string& userId) -{ - ui_->actionAccountInformation->setEnabled(true); - ui_->actionAuthenticationAddresses->setEnabled(userType != BaseCelerClient::CelerUserType::Market); - ui_->actionOneTimePassword->setEnabled(true); - ui_->actionEnterColorCoinToken->setEnabled(true); - - ui_->actionDeposits->setEnabled(true); - ui_->actionWithdrawalRequest->setEnabled(true); - ui_->actionLinkAdditionalBankAccount->setEnabled(true); - - actLogin_->setVisible(false); - actLogout_->setVisible(true); - - // ccFileManager_->ConnectToCelerClient(celerConnection_); - ui_->widgetRFQ->onMatchingLogin(mtchLogin, userType, userId); - ui_->widgetRFQReply->onUserConnected(userType); - - statusBarView_->onConnectedToMatching(); - orderListModel_->onMatchingLogin(); } void bs::gui::qt::MainWindow::onLogoutInitiated() { ui_->widgetWallets->setUsername(QString()); - /* if (chatClientServicePtr_) { - chatClientServicePtr_->LogoutFromServer(); - }*/ - ui_->widgetChart->disconnect(); - /* if (celerConnection_->IsConnected()) { - celerConnection_->CloseConnection(); - }*/ - - // mdProvider_->UnsubscribeFromMD(); +// mdProvider_->UnsubscribeFromMD(); setLoginButtonText(loginButtonText_); @@ -951,145 +806,21 @@ void MainWindow::onLoggedOut() emit needMatchingLogout(); } -void MainWindow::onMatchingLogout() -{ - emit needCloseBsConnection(); - - ui_->actionAccountInformation->setEnabled(false); - ui_->actionAuthenticationAddresses->setEnabled(false); - ui_->actionEnterColorCoinToken->setEnabled(false); - ui_->actionOneTimePassword->setEnabled(false); - - ui_->actionDeposits->setEnabled(false); - ui_->actionWithdrawalRequest->setEnabled(false); - ui_->actionLinkAdditionalBankAccount->setEnabled(false); - - actLogin_->setVisible(true); - actLogin_->setEnabled(true); - actLogout_->setVisible(false); - - statusBarView_->onDisconnectedFromMatching(); - setLoginButtonText(loginButtonText_); - - if (orderListModel_) { - orderListModel_->onMatchingLogout(); - } - ui_->widgetRFQ->onMatchingLogout(); - ui_->widgetRFQReply->onMatchingLogout(); -} - -void bs::gui::qt::MainWindow::onMDConnected() -{ - ui_->widgetPortfolio->onMDConnected(); -} - -void bs::gui::qt::MainWindow::onMDDisconnected() -{ - ui_->widgetPortfolio->onMDDisconnected(); -} - -void bs::gui::qt::MainWindow::onNewSecurity(const std::string& name, bs::network::Asset::Type at) -{ - ui_->widgetRFQ->onNewSecurity(name, at); -} - void MainWindow::onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &fields) { - ui_->widgetRFQ->onMDUpdated(assetType, security, fields); - ui_->widgetRFQReply->onMDUpdated(assetType, security, fields); - ui_->widgetPortfolio->onMDUpdated(assetType, security, fields); + //ui_->widgetPortfolio->onMDUpdated(assetType, security, fields); } void bs::gui::qt::MainWindow::onBalance(const std::string& currency, double balance) { statusBarView_->onBalanceUpdated(currency, balance); ui_->widgetPortfolio->onBalance(currency, balance); - ui_->widgetRFQ->onBalance(currency, balance); - ui_->widgetRFQReply->onBalance(currency, balance); -} - -void MainWindow::onAuthAddresses(const std::vector &addrs - , const std::map& states) -{ - if (authAddrDlg_) { - authAddrDlg_->onAuthAddresses(addrs, states); - } -} - -void MainWindow::onSubmittedAuthAddresses(const std::vector& addrs) -{ - if (authAddrDlg_) { - authAddrDlg_->onSubmittedAuthAddresses(addrs); - } -} - -void MainWindow::onVerifiedAuthAddresses(const std::vector& addrs) -{ - ui_->widgetRFQ->onVerifiedAuthAddresses(addrs); - ui_->widgetRFQReply->onVerifiedAuthAddresses(addrs); -} - -void MainWindow::onAuthKey(const bs::Address& addr, const BinaryData& authKey) -{ - ui_->widgetRFQ->onAuthKey(addr, authKey); - ui_->widgetRFQReply->onAuthKey(addr, authKey); -} - -void MainWindow::onQuoteReceived(const bs::network::Quote& quote) -{ - ui_->widgetRFQ->onQuoteReceived(quote); -} - -void MainWindow::onQuoteMatched(const std::string& rfqId, const std::string "eId) -{ - ui_->widgetRFQ->onQuoteMatched(rfqId, quoteId); - ui_->widgetRFQReply->onQuoteMatched(rfqId, quoteId); -} - -void MainWindow::onQuoteFailed(const std::string& rfqId, const std::string& quoteId - , const std::string &info) -{ - ui_->widgetRFQ->onQuoteFailed(rfqId, quoteId, info); - ui_->widgetRFQReply->onQuoteFailed(rfqId, quoteId, info); -} - -void bs::gui::qt::MainWindow::onSettlementPending(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId, int timeLeftMS) -{ - ui_->widgetRFQ->onSettlementPending(rfqId, quoteId, settlementId, timeLeftMS); - ui_->widgetRFQReply->onSettlementPending(rfqId, quoteId, settlementId, timeLeftMS); -} - -void bs::gui::qt::MainWindow::onSettlementComplete(const std::string& rfqId - , const std::string& quoteId, const BinaryData& settlementId) -{ - ui_->widgetRFQ->onSettlementComplete(rfqId, quoteId, settlementId); - ui_->widgetRFQReply->onSettlementComplete(rfqId, quoteId, settlementId); -} - -void bs::gui::qt::MainWindow::onQuoteReqNotification(const bs::network::QuoteReqNotification& qrn) -{ - ui_->widgetRFQReply->onQuoteReqNotification(qrn); -} - -void MainWindow::onOrdersUpdate(const std::vector& orders) -{ - orderListModel_->onOrdersUpdate(orders); -} - -void bs::gui::qt::MainWindow::onQuoteCancelled(const std::string& rfqId - , const std::string& quoteId, bool byUser) -{ - ui_->widgetRFQReply->onQuoteCancelled(QString::fromStdString(rfqId), byUser); } void MainWindow::onReservedUTXOs(const std::string& resId , const std::string& subId, const std::vector& utxos) -{ - ui_->widgetRFQ->onReservedUTXOs(resId, subId, utxos); - ui_->widgetRFQReply->onReservedUTXOs(resId, subId, utxos); -} +{} void MainWindow::showRunInBackgroundMessage() { @@ -1252,46 +983,11 @@ void MainWindow::initWidgets() initTransactionsView(); ui_->widgetPortfolio->init(logger_); - connect(ui_->widgetPortfolio, &PortfolioWidget::needMdConnection, this, &MainWindow::needMdConnection); - connect(ui_->widgetPortfolio, &PortfolioWidget::needMdDisconnect, this, &MainWindow::needMdDisconnect); + //connect(ui_->widgetPortfolio, &PortfolioWidget::needMdConnection, this, &MainWindow::needMdConnection); + //connect(ui_->widgetPortfolio, &PortfolioWidget::needMdDisconnect, this, &MainWindow::needMdDisconnect); - orderListModel_ = std::make_shared(this); + //orderListModel_ = std::make_shared(this); dialogMgr_ = std::make_shared(this); - - ui_->widgetRFQ->init(logger_, dialogMgr_, orderListModel_.get()); - connect(ui_->widgetRFQ, &RFQRequestWidget::requestPrimaryWalletCreation, this, &MainWindow::createNewWallet); - connect(ui_->widgetRFQ, &RFQRequestWidget::needWalletData, this, &MainWindow::needWalletData); - connect(ui_->widgetRFQ, &RFQRequestWidget::loginRequested, this, &MainWindow::onLoginInitiated); - connect(ui_->widgetRFQ, &RFQRequestWidget::needSubmitRFQ, this, &MainWindow::needSubmitRFQ); - connect(ui_->widgetRFQ, &RFQRequestWidget::needAcceptRFQ, this, &MainWindow::needAcceptRFQ); - connect(ui_->widgetRFQ, &RFQRequestWidget::needCancelRFQ, this, &MainWindow::needCancelRFQ); - connect(ui_->widgetRFQ, &RFQRequestWidget::needAuthKey, this, &MainWindow::needAuthKey); - connect(ui_->widgetRFQ, &RFQRequestWidget::needReserveUTXOs, this, &MainWindow::needReserveUTXOs); - - connect(ui_->widgetRFQReply, &RFQReplyWidget::requestPrimaryWalletCreation, this - , &MainWindow::createNewWallet); - connect(ui_->widgetRFQReply, &RFQReplyWidget::putSetting, this, &MainWindow::putSetting); - connect(ui_->widgetRFQReply, &RFQReplyWidget::submitQuote, this, &MainWindow::submitQuote); - connect(ui_->widgetRFQReply, &RFQReplyWidget::pullQuote, this, &MainWindow::pullQuote); - connect(ui_->widgetRFQReply, &RFQReplyWidget::needAuthKey, this, &MainWindow::needAuthKey); - connect(ui_->widgetRFQReply, &RFQReplyWidget::needReserveUTXOs, this, &MainWindow::needReserveUTXOs); - ui_->widgetRFQReply->init(logger_, orderListModel_.get()); - - connect(ui_->tabWidget, &QTabWidget::tabBarClicked, this, - [/*requestRFQ = QPointer(ui_->widgetRFQ) - , replyRFQ = QPointer(ui_->widgetRFQReply) - ,*/ tabWidget = QPointer(ui_->tabWidget)] (int index) - { - if (!tabWidget) { - return; - } -/* if (requestRFQ && requestRFQ == tabWidget->widget(index)) { - requestRFQ->forceCheckCondition(); - } - if (replyRFQ && replyRFQ == tabWidget->widget(index)) { - replyRFQ->forceCheckCondition(); - }*/ - }); } void MainWindow::promptSwitchEnv(bool prod) diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 4819e7929..8f5ba6afd 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -17,8 +17,6 @@ #include "Address.h" #include "ArmoryConnection.h" #include "AuthAddress.h" -#include "BsClient.h" -#include "Celer/BaseCelerClient.h" #include "CommonTypes.h" #include "SignContainer.h" #include "Settings/SignersProvider.h" @@ -96,37 +94,26 @@ namespace bs { void onSignerSettings(const QList&, const std::string& ownKey, int idxCur); void onLoginStarted(const std::string &login, bool success, const std::string &errMsg); - void onLoggedIn(const BsClientLoginResult&); + //void onLoggedIn(const BsClientLoginResult&); void onAccountTypeChanged(bs::network::UserType userType, bool enabled); - void onMatchingLogin(const std::string& mtchLogin, BaseCelerClient::CelerUserType - , const std::string &userId); - void onMatchingLogout(); + //void onMatchingLogin(const std::string& mtchLogin, BaseCelerClient::CelerUserType + // , const std::string &userId); + //void onMatchingLogout(); - void onMDConnected(); - void onMDDisconnected(); - void onNewSecurity(const std::string& name, bs::network::Asset::Type); + //void onMDConnected(); + //void onMDDisconnected(); + //void onNewSecurity(const std::string& name, bs::network::Asset::Type); void onMDUpdated(bs::network::Asset::Type assetType , const QString& security, const bs::network::MDFields &); void onBalance(const std::string& currency, double balance); - void onAuthAddresses(const std::vector& - , const std::map &); - void onSubmittedAuthAddresses(const std::vector&); - void onVerifiedAuthAddresses(const std::vector&); - void onAuthKey(const bs::Address&, const BinaryData& authKey); - - void onQuoteReceived(const bs::network::Quote&); - void onQuoteMatched(const std::string &rfqId, const std::string "eId); - void onQuoteFailed(const std::string& rfqId, const std::string& quoteId - , const std::string &info); - void onSettlementPending(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId, int timeLeftMS); - void onSettlementComplete(const std::string& rfqId, const std::string& quoteId - , const BinaryData& settlementId); - void onQuoteReqNotification(const bs::network::QuoteReqNotification&); - void onOrdersUpdate(const std::vector&); - void onQuoteCancelled(const std::string& rfqId, const std::string& quoteId - , bool byUser); + //void onAuthKey(const bs::Address&, const BinaryData& authKey); + + //void onSettlementPending(const std::string& rfqId, const std::string& quoteId + // , const BinaryData& settlementId, int timeLeftMS); + //void onSettlementComplete(const std::string& rfqId, const std::string& quoteId + // , const BinaryData& settlementId); + //void onOrdersUpdate(const std::vector&); void onReservedUTXOs(const std::string& resId, const std::string& subId , const std::vector&); @@ -135,14 +122,6 @@ namespace bs { void onReactivate(); void raiseWindow(); -/* private: - enum class AutoLoginState - { - Idle, - Connecting, - Connected, - };*/ - signals: void getSettings(const std::vector &); void putSetting(ApplicationSettings::Setting, const QVariant &); @@ -194,28 +173,16 @@ namespace bs { void needMdConnection(ApplicationSettings::EnvConfiguration); void needMdDisconnect(); - void needNewAuthAddress(); - void needSubmitAuthAddress(const bs::Address&); - void needSubmitRFQ(const bs::network::RFQ&, const std::string& reserveId = {}); - void needAcceptRFQ(const std::string& id, const bs::network::Quote&); - void needCancelRFQ(const std::string& id); void needAuthKey(const bs::Address&); void needReserveUTXOs(const std::string& reserveId, const std::string& subId , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); void needUnreserveUTXOs(const std::string& reserveId, const std::string& subId); - void submitQuote(const bs::network::QuoteNotification&); - void pullQuote(const std::string& settlementId, const std::string& reqId - , const std::string& reqSessToken); - private slots: void onSend(); void onGenerateAddress(); - void openAuthManagerDialog(); void openConfigDialog(bool showInNetworkPage = false); - // void openAccountInfoDialog(); - // void openCCTokenDialog(); void onLoginInitiated(); void onLogoutInitiated(); @@ -262,7 +229,7 @@ namespace bs { void addDeferredDialog(const std::function &); void processDeferredDialogs(); - void activateClient(const BsClientLoginResult&); + //void activateClient(const BsClientLoginResult&); private: std::unique_ptr ui_; @@ -279,13 +246,11 @@ namespace bs { std::shared_ptr sysTrayIcon_; std::shared_ptr notifCenter_; std::shared_ptr txModel_; - std::shared_ptr orderListModel_; + //std::shared_ptr orderListModel_; std::shared_ptr dialogMgr_; CreateTransactionDialog* txDlg_{ nullptr }; ConfigDialog* cfgDlg_{ nullptr }; - LoginWindow* loginDlg_{ nullptr }; - AuthAddressDialog* authAddrDlg_{ nullptr }; // std::shared_ptr walletsWizard_; diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 2bf63fd60..4422253ef 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -8,6 +8,7 @@ ********************************************************************************** */ +#include "CommonTypes.h" #include "QtGuiAdapter.h" #include #include @@ -22,9 +23,7 @@ #include #include #include -#include "Address.h" #include "AppNap.h" -#include "BsClient.h" #include "BSMessageBox.h" #include "BSTerminalSplashScreen.h" #include "MainWindow.h" @@ -40,11 +39,19 @@ using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; +#if 0 Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult) Q_DECLARE_METATYPE(std::string) Q_DECLARE_METATYPE(std::vector) Q_DECLARE_METATYPE(std::vector); Q_DECLARE_METATYPE(bs::PayoutSignatureType) +Q_DECLARE_METATYPE(bs::network::Asset::Type) +Q_DECLARE_METATYPE(bs::network::MDField) +Q_DECLARE_METATYPE(bs::network::MDFields) +Q_DECLARE_METATYPE(bs::network::SecurityDef) +Q_DECLARE_METATYPE(bs::network::CCSecurityDef) +Q_DECLARE_METATYPE(bs::PayoutSignatureType) +#endif #if defined (Q_OS_MAC) class MacOsApp : public QApplication @@ -196,11 +203,24 @@ void QtGuiAdapter::run(int &argc, char **argv) return; } +#if 0 qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType>(); + qRegisterMetaType("AssetType"); + qRegisterMetaType("Quote"); + qRegisterMetaType("Order"); + qRegisterMetaType("SecurityDef"); + qRegisterMetaType("MDField"); + qRegisterMetaType("MDFields"); + qRegisterMetaType("CCSecurityDef"); + qRegisterMetaType("NewTrade"); + qRegisterMetaType("NewPMTrade"); + qRegisterMetaType(); + qRegisterMetaType(); +#endif QString logoIcon; logoIcon = QLatin1String(":/SPLASH_LOGO"); @@ -248,8 +268,6 @@ bool QtGuiAdapter::process(const Envelope &env) return processWallets(env); case TerminalUsers::BsServer: return processBsServer(env); - case TerminalUsers::Settlement: - return processSettlement(env); case TerminalUsers::Matching: return processMatching(env); case TerminalUsers::MktData: @@ -280,8 +298,6 @@ bool QtGuiAdapter::processBroadcast(const bs::message::Envelope& env) return processWallets(env); case TerminalUsers::BsServer: return processBsServer(env); - case TerminalUsers::Settlement: - return processSettlement(env); case TerminalUsers::Matching: return processMatching(env); case TerminalUsers::MktData: @@ -300,7 +316,7 @@ bool QtGuiAdapter::processSettings(const Envelope &env) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -419,7 +435,7 @@ bool QtGuiAdapter::processAdminMessage(const Envelope &env) { AdministrativeMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse admin msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse admin msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -449,7 +465,7 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) ArmoryMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[QtGuiAdapter::processBlockchain] failed to parse msg #{}" - , env.id()); + , env.foreignId()); if (!env.receiver) { logger_->debug("[{}] no receiver", __func__); } @@ -505,7 +521,7 @@ bool QtGuiAdapter::processSigner(const Envelope &env) SignerMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[QtGuiAdapter::processSigner] failed to parse msg #{}" - , env.id()); + , env.foreignId()); if (!env.receiver) { logger_->debug("[{}] no receiver", __func__); } @@ -535,7 +551,7 @@ bool QtGuiAdapter::processWallets(const Envelope &env) { WalletsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -606,8 +622,6 @@ bool QtGuiAdapter::processWallets(const Envelope &env) return processUTXOs(msg.utxos()); case WalletsMessage::kAuthWallet: return processAuthWallet(msg.auth_wallet()); - case WalletsMessage::kAuthKey: - return processAuthKey(msg.auth_key()); case WalletsMessage::kReservedUtxos: return processReservedUTXOs(msg.reserved_utxos()); default: break; @@ -619,7 +633,7 @@ bool QtGuiAdapter::processOnChainTrack(const Envelope &env) { OnChainTrackMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -629,8 +643,6 @@ bool QtGuiAdapter::processOnChainTrack(const Envelope &env) break; case OnChainTrackMessage::kAuthState: return processAuthState(msg.auth_state()); - case OnChainTrackMessage::kVerifiedAuthAddresses: - return processVerifiedAuthAddrs(msg.verified_auth_addresses()); default: break; } return true; @@ -640,12 +652,10 @@ bool QtGuiAdapter::processAssets(const bs::message::Envelope& env) { AssetsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { - case AssetsMessage::kSubmittedAuthAddrs: - return processSubmittedAuthAddrs(msg.submitted_auth_addrs()); case AssetsMessage::kBalance: return processBalance(msg.balance()); default: break; @@ -795,16 +805,9 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelLogin, this, &QtGuiAdapter::onNeedCancelLogin); connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogout, this, &QtGuiAdapter::onNeedMatchingLogout); connect(mainWindow_, &bs::gui::qt::MainWindow::needMdConnection, this, &QtGuiAdapter::onNeedMdConnection); - connect(mainWindow_, &bs::gui::qt::MainWindow::needNewAuthAddress, this, &QtGuiAdapter::onNeedNewAuthAddress); - connect(mainWindow_, &bs::gui::qt::MainWindow::needSubmitAuthAddress, this, &QtGuiAdapter::onNeedSubmitAuthAddress); - connect(mainWindow_, &bs::gui::qt::MainWindow::needSubmitRFQ, this, &QtGuiAdapter::onNeedSubmitRFQ); - connect(mainWindow_, &bs::gui::qt::MainWindow::needAcceptRFQ, this, &QtGuiAdapter::onNeedAcceptRFQ); - connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelRFQ, this, &QtGuiAdapter::onNeedCancelRFQ); connect(mainWindow_, &bs::gui::qt::MainWindow::needAuthKey, this, &QtGuiAdapter::onNeedAuthKey); connect(mainWindow_, &bs::gui::qt::MainWindow::needReserveUTXOs, this, &QtGuiAdapter::onNeedReserveUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::needUnreserveUTXOs, this, &QtGuiAdapter::onNeedUnreserveUTXOs); - connect(mainWindow_, &bs::gui::qt::MainWindow::submitQuote, this, &QtGuiAdapter::onSubmitQuote); - connect(mainWindow_, &bs::gui::qt::MainWindow::pullQuote, this, &QtGuiAdapter::onPullQuote); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletDialog, this, &QtGuiAdapter::onNeedWalletDialog); } @@ -1184,41 +1187,6 @@ void QtGuiAdapter::onNeedMdConnection(ApplicationSettings::EnvConfiguration ec) pushRequest(user_, userMD_, msg.SerializeAsString()); } -void QtGuiAdapter::onNeedNewAuthAddress() -{ - //TODO -} - -void QtGuiAdapter::onNeedSubmitAuthAddress(const bs::Address& addr) -{ - BsServerMessage msg; - msg.set_submit_auth_address(addr.display()); - pushRequest(user_, userBS_, msg.SerializeAsString()); -} - -void QtGuiAdapter::onNeedSubmitRFQ(const bs::network::RFQ& rfq, const std::string& reserveId) -{ - SettlementMessage msg; - auto msgReq = msg.mutable_send_rfq(); - toMsg(rfq, msgReq->mutable_rfq()); - msgReq->set_reserve_id(reserveId); - pushRequest(user_, userSettl_, msg.SerializeAsString()); -} - -void QtGuiAdapter::onNeedAcceptRFQ(const std::string& id, const bs::network::Quote& quote) -{ - SettlementMessage msg; - auto msgReq = msg.mutable_accept_rfq(); - msgReq->set_rfq_id(id); - toMsg(quote, msgReq->mutable_quote()); - pushRequest(user_, userSettl_, msg.SerializeAsString()); -} - -void QtGuiAdapter::onNeedCancelRFQ(const std::string& id) -{ - //TODO -} - void QtGuiAdapter::onNeedAuthKey(const bs::Address& addr) { WalletsMessage msg; @@ -1253,24 +1221,6 @@ void QtGuiAdapter::onNeedUnreserveUTXOs(const std::string& reserveId pushRequest(user_, userWallets_, msg.SerializeAsString()); } -void QtGuiAdapter::onSubmitQuote(const bs::network::QuoteNotification& qn) -{ - SettlementMessage msg; - toMsg(qn, msg.mutable_reply_to_rfq()); - pushRequest(user_, userSettl_, msg.SerializeAsString()); -} - -void QtGuiAdapter::onPullQuote(const std::string& settlementId - , const std::string& reqId, const std::string& reqSessToken) -{ - SettlementMessage msg; - auto msgReq = msg.mutable_pull_rfq_reply(); - msgReq->set_settlement_id(settlementId); - msgReq->set_rfq_id(reqId); - msgReq->set_session_token(reqSessToken); - pushRequest(user_, userSettl_, msg.SerializeAsString()); -} - void QtGuiAdapter::onNeedWalletDialog(bs::signer::ui::GeneralDialogType dlgType , const std::string& rootId) { @@ -1553,7 +1503,7 @@ bool QtGuiAdapter::processBsServer(const bs::message::Envelope& env) { BsServerMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -1561,8 +1511,6 @@ bool QtGuiAdapter::processBsServer(const bs::message::Envelope& env) return processStartLogin(msg.start_login_result()); case BsServerMessage::kLoginResult: return processLogin(msg.login_result()); - case BsServerMessage::kOrdersUpdate: - return processOrdersUpdate(msg.orders_update()); default: break; } return true; @@ -1578,7 +1526,7 @@ bool QtGuiAdapter::processStartLogin(const BsServerMessage_StartLoginResult& res bool QtGuiAdapter::processLogin(const BsServerMessage_LoginResult& response) { - BsClientLoginResult result; +#if 0 result.login = response.login(); result.status = static_cast(response.status()); result.userType = static_cast(response.user_type()); @@ -1598,51 +1546,29 @@ bool QtGuiAdapter::processLogin(const BsServerMessage_LoginResult& response) return QMetaObject::invokeMethod(mainWindow_, [this, result] { mainWindow_->onLoggedIn(result); }); -} - -bool QtGuiAdapter::processSettlement(const bs::message::Envelope& env) -{ - SettlementMessage msg; - if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); - return true; - } - switch (msg.data_case()) { - case SettlementMessage::kQuote: - return processQuote(msg.quote()); - case SettlementMessage::kMatchedQuote: - return processMatchedQuote(msg.matched_quote()); - case SettlementMessage::kFailedSettlement: - return processFailedSettl(msg.failed_settlement()); - case SettlementMessage::kPendingSettlement: - return processPendingSettl(msg.pending_settlement()); - case SettlementMessage::kSettlementComplete: - return processSettlComplete(msg.settlement_complete()); - case SettlementMessage::kQuoteReqNotif: - return processQuoteReqNotif(msg.quote_req_notif()); - case SettlementMessage::kQuoteCancelled: - return processQuoteCancelled(msg.quote_cancelled()); - default: break; - } +#else return true; +#endif 0 } bool QtGuiAdapter::processMatching(const bs::message::Envelope& env) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { case MatchingMessage::kLoggedIn: +#if 0 return QMetaObject::invokeMethod(mainWindow_, [this, response=msg.logged_in()] { mainWindow_->onMatchingLogin(response.user_name() , static_cast(response.user_type()), response.user_id()); }); +#endif case MatchingMessage::kLoggedOut: return QMetaObject::invokeMethod(mainWindow_, [this] { - mainWindow_->onMatchingLogout(); + //mainWindow_->onMatchingLogout(); }); /* case MatchingMessage::kQuoteNotif: return processQuoteNotif(msg.quote_notif());*/ @@ -1655,20 +1581,21 @@ bool QtGuiAdapter::processMktData(const bs::message::Envelope& env) { MktDataMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { case MktDataMessage::kConnected: - return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMDConnected(); }); + //return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMDConnected(); }); case MktDataMessage::kDisconnected: mdInstrumentsReceived_ = false; - return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMDDisconnected(); }); + //return QMetaObject::invokeMethod(mainWindow_, [this] { mainWindow_->onMDDisconnected(); }); + return true; case MktDataMessage::kNewSecurity: return processSecurity(msg.new_security().name(), msg.new_security().asset_type()); case MktDataMessage::kAllInstrumentsReceived: mdInstrumentsReceived_ = true; - return sendPooledOrdersUpdate(); + return true; // sendPooledOrdersUpdate(); case MktDataMessage::kPriceUpdate: return processMdUpdate(msg.price_update()); default: break; @@ -1680,9 +1607,7 @@ bool QtGuiAdapter::processSecurity(const std::string& name, int assetType) { const auto &at = static_cast(assetType); assetTypes_[name] = at; - return QMetaObject::invokeMethod(mainWindow_, [this, name, at] { - mainWindow_->onNewSecurity(name, at); - }); + return true; } bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) @@ -1710,7 +1635,7 @@ bool QtGuiAdapter::processAuthWallet(const WalletsMessage_WalletData& authWallet catch (const std::exception&) {} } return QMetaObject::invokeMethod(mainWindow_, [this, authAddresses] { - mainWindow_->onAuthAddresses(authAddresses, {}); + //mainWindow_->onAuthAddresses(authAddresses, {}); }); } @@ -1725,21 +1650,7 @@ bool QtGuiAdapter::processAuthState(const OnChainTrackMessage_AuthState& authSta } const auto& state = static_cast(authState.state()); return QMetaObject::invokeMethod(mainWindow_, [this, addr, state] { - mainWindow_->onAuthAddresses({}, { {addr, state } }); - }); -} - -bool QtGuiAdapter::processSubmittedAuthAddrs(const AssetsMessage_SubmittedAuthAddresses& addrs) -{ - std::vector authAddresses; - for (const auto& addr : addrs.addresses()) { - try { - authAddresses.push_back(bs::Address::fromAddressString(addr)); - } - catch (const std::exception&) {} - } - return QMetaObject::invokeMethod(mainWindow_, [this, authAddresses] { - mainWindow_->onSubmittedAuthAddresses(authAddresses); + //mainWindow_->onAuthAddresses({}, { {addr, state } }); }); } @@ -1750,136 +1661,6 @@ bool QtGuiAdapter::processBalance(const AssetsMessage_Balance& bal) }); } -bool QtGuiAdapter::processVerifiedAuthAddrs(const OnChainTrackMessage_AuthAddresses& addrs) -{ - std::vector authAddresses; - for (const auto& addr : addrs.addresses()) { - try { - authAddresses.push_back(bs::Address::fromAddressString(addr)); - } catch (const std::exception&) {} - } - return QMetaObject::invokeMethod(mainWindow_, [this, authAddresses] { - mainWindow_->onVerifiedAuthAddresses(authAddresses); - }); -} - -bool QtGuiAdapter::processAuthKey(const WalletsMessage_AuthKey& response) -{ - return QMetaObject::invokeMethod(mainWindow_, [this, response] { - try { - mainWindow_->onAuthKey(bs::Address::fromAddressString(response.auth_address()) - , BinaryData::fromString(response.auth_key())); - } - catch (const std::exception&) {} - }); -} - -bool QtGuiAdapter::processQuote(const BlockSettle::Terminal::Quote& msg) -{ - const auto& quote = fromMsg(msg); - return QMetaObject::invokeMethod(mainWindow_, [this, quote] { - mainWindow_->onQuoteReceived(quote); - }); -} - -bool QtGuiAdapter::processMatchedQuote(const SettlementMessage_MatchedQuote& msg) -{ - return QMetaObject::invokeMethod(mainWindow_, [this, msg] { - mainWindow_->onQuoteMatched(msg.rfq_id(), msg.quote_id()); - }); -} - -bool QtGuiAdapter::processFailedSettl(const SettlementMessage_FailedSettlement& msg) -{ - return QMetaObject::invokeMethod(mainWindow_, [this, msg] { - if (!msg.quote_id().empty() && !msg.rfq_id().empty()) { - mainWindow_->onQuoteFailed(msg.rfq_id(), msg.quote_id(), msg.info()); - } - else { - //TODO: another type of failure - } - }); -} - -bool QtGuiAdapter::processPendingSettl(const SettlementMessage_PendingSettlement& msg) -{ - return QMetaObject::invokeMethod(mainWindow_, [this, msg] { - mainWindow_->onSettlementPending(msg.ids().rfq_id(), msg.ids().quote_id() - , BinaryData::fromString(msg.ids().settlement_id()), msg.time_left_ms()); - }); -} - -bool QtGuiAdapter::processSettlComplete(const SettlementMessage_SettlementIds& msg) -{ - return QMetaObject::invokeMethod(mainWindow_, [this, msg] { - mainWindow_->onSettlementComplete(msg.rfq_id(), msg.quote_id() - , BinaryData::fromString(msg.settlement_id())); - }); -} - -bool QtGuiAdapter::processQuoteReqNotif(const IncomingRFQ& request) -{ - const auto& qrn = fromMsg(request); - return QMetaObject::invokeMethod(mainWindow_, [this, qrn] { - mainWindow_->onQuoteReqNotification(qrn); - }); -} - -bool QtGuiAdapter::processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders& msg) -{ - // Use some fake orderId so old code works correctly - int orderId = 0; - std::vector orders; - for (const auto& o : msg.orders()) { - bs::network::Order order; - order.security = o.product() + "/" + o.contra_product(); - auto itAsset = assetTypes_.find(order.security); - if (itAsset != assetTypes_.end()) { - order.assetType = itAsset->second; - } - else { - order.assetType = bs::network::Asset::Undefined; - } - order.exchOrderId = QString::number(++orderId); - order.status = static_cast(o.status()); - order.side = o.buy() ? bs::network::Side::Buy : bs::network::Side::Sell; - order.pendingStatus = o.status_text(); - order.dateTime = QDateTime::fromMSecsSinceEpoch(o.timestamp()); - order.product = o.product(); - order.quantity = o.quantity(); - order.price = o.price(); - orders.push_back(order); - } - pooledOrders_ = std::move(orders); - return sendPooledOrdersUpdate(); -} - -bool QtGuiAdapter::sendPooledOrdersUpdate() -{ - if (!mdInstrumentsReceived_ || pooledOrders_.empty()) { - return true; - } - for (auto& order : pooledOrders_) { - if (order.assetType == bs::network::Asset::Undefined) { - const auto& itAsset = assetTypes_.find(order.security); - if (itAsset != assetTypes_.end()) { - order.assetType = itAsset->second; - } - } - } - return QMetaObject::invokeMethod(mainWindow_, [this] { - mainWindow_->onOrdersUpdate(pooledOrders_); - pooledOrders_.clear(); - }); -} - -bool QtGuiAdapter::processQuoteCancelled(const QuoteCancelled& msg) -{ - return QMetaObject::invokeMethod(mainWindow_, [this, msg]{ - mainWindow_->onQuoteCancelled(msg.rfq_id(), msg.quote_id(), msg.by_user()); - }); -} - bool QtGuiAdapter::processReservedUTXOs(const WalletsMessage_ReservedUTXOs& response) { std::vector utxos; diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 7efab5e61..39e7ed9d4 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -129,26 +129,13 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processStartLogin(const BlockSettle::Terminal::BsServerMessage_StartLoginResult&); bool processLogin(const BlockSettle::Terminal::BsServerMessage_LoginResult&); - bool processSettlement(const bs::message::Envelope&); bool processMatching(const bs::message::Envelope&); bool processMktData(const bs::message::Envelope&); bool processSecurity(const std::string&, int); bool processMdUpdate(const BlockSettle::Terminal::MktDataMessage_Prices &); bool processAuthWallet(const BlockSettle::Common::WalletsMessage_WalletData&); bool processAuthState(const BlockSettle::Common::OnChainTrackMessage_AuthState&); - bool processSubmittedAuthAddrs(const BlockSettle::Terminal::AssetsMessage_SubmittedAuthAddresses&); bool processBalance(const BlockSettle::Terminal::AssetsMessage_Balance&); - bool processVerifiedAuthAddrs(const BlockSettle::Common::OnChainTrackMessage_AuthAddresses&); - bool processAuthKey(const BlockSettle::Common::WalletsMessage_AuthKey&); - bool processQuote(const BlockSettle::Terminal::Quote&); - bool processMatchedQuote(const BlockSettle::Terminal::SettlementMessage_MatchedQuote&); - bool processFailedSettl(const BlockSettle::Terminal::SettlementMessage_FailedSettlement&); - bool processPendingSettl(const BlockSettle::Terminal::SettlementMessage_PendingSettlement&); - bool processSettlComplete(const BlockSettle::Terminal::SettlementMessage_SettlementIds&); - bool processQuoteReqNotif(const BlockSettle::Terminal::IncomingRFQ&); - bool processOrdersUpdate(const BlockSettle::Terminal::BsServerMessage_Orders&); - bool sendPooledOrdersUpdate(); - bool processQuoteCancelled(const BlockSettle::Terminal::QuoteCancelled&); bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); private slots: @@ -194,18 +181,10 @@ private slots: void onNeedCancelLogin(); void onNeedMatchingLogout(); void onNeedMdConnection(ApplicationSettings::EnvConfiguration); - void onNeedNewAuthAddress(); - void onNeedSubmitAuthAddress(const bs::Address&); - void onNeedSubmitRFQ(const bs::network::RFQ&, const std::string& reserveId = {}); - void onNeedAcceptRFQ(const std::string& id, const bs::network::Quote&); - void onNeedCancelRFQ(const std::string& id); void onNeedAuthKey(const bs::Address&); void onNeedReserveUTXOs(const std::string& reserveId, const std::string& subId , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); void onNeedUnreserveUTXOs(const std::string& reserveId, const std::string& subId); - void onSubmitQuote(const bs::network::QuoteNotification&); - void onPullQuote(const std::string& settlementId, const std::string& reqId - , const std::string& reqSessToken); void onNeedWalletDialog(bs::signer::ui::GeneralDialogType, const std::string& rootId); private: @@ -232,7 +211,6 @@ private slots: std::unordered_map assetTypes_; bool mdInstrumentsReceived_{ false }; - std::vector pooledOrders_; }; diff --git a/UnitTests/MockAssetMgr.h b/UnitTests/MockAssetMgr.h index 80e05c4d7..e7c859c82 100644 --- a/UnitTests/MockAssetMgr.h +++ b/UnitTests/MockAssetMgr.h @@ -32,7 +32,7 @@ class MockAssetManager : public AssetManager public: MockAssetManager(const std::shared_ptr& logger) - : AssetManager(logger, nullptr, nullptr, nullptr) {} + : AssetManager(logger, nullptr) {} void init() override; std::vector privateShares(bool forceExt) override; diff --git a/UnitTests/MockTerminal.cpp b/UnitTests/MockTerminal.cpp index e7dcd8ff1..1ff4ba799 100644 --- a/UnitTests/MockTerminal.cpp +++ b/UnitTests/MockTerminal.cpp @@ -14,7 +14,6 @@ #include "ArmorySettings.h" #include "Message/Adapter.h" #include "SettingsAdapter.h" -#include "SettlementAdapter.h" #include "SignerAdapter.h" #include "TerminalMessage.h" #include "TestEnv.h" @@ -81,7 +80,7 @@ class SettingsMockAdapter : public bs::message::Adapter if (env.sender->value() == TerminalUsers::Blockchain) { ArmoryMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse armory msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse armory msg #{}", __func__, env.foreignId()); return false; } if (msg.data_case() == ArmoryMessage::kSettingsRequest) { @@ -114,7 +113,7 @@ class SettingsMockAdapter : public bs::message::Adapter if (env.receiver->value() == TerminalUsers::Settings) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse settings msg #{}", __func__, env.id()); + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -218,7 +217,7 @@ MockTerminal::MockTerminal(const std::shared_ptr& logger //TODO: add TrackerMockAdapter bus_->addAdapter(std::make_shared(logger_ , userWallets, signAdapter->createClient(), userBlockchain)); - bus_->addAdapter(std::make_shared(logger)); + //bus_->addAdapter(std::make_shared(logger)); bus_->addAdapter(std::make_shared(logger, userBlockchain , armory)); } diff --git a/UnitTests/TestAdapters.cpp b/UnitTests/TestAdapters.cpp index 0b6636038..85913cd79 100644 --- a/UnitTests/TestAdapters.cpp +++ b/UnitTests/TestAdapters.cpp @@ -129,7 +129,7 @@ bool MatchingMock::process(const bs::message::Envelope& env) if (env.isRequest() && (env.receiver->value() == user_->value())) { MatchingMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own request #{}", __func__, env.id()); + logger_->error("[{}] failed to parse own request #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -211,7 +211,7 @@ bool MatchingMock::process(const bs::message::Envelope& env) if (env.isRequest() && (env.receiver->value() == userBS_->value())) { BsServerMessage msg; if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own request #{}", __func__, env.id()); + logger_->error("[{}] failed to parse own request #{}", __func__, env.foreignId()); return true; } switch (msg.data_case()) { @@ -336,9 +336,9 @@ bool MatchingMock::sendPendingOrder(const std::string& rfqId) } bs::network::Order order; order.clOrderId = CryptoPRNG::generateRandom(7).toHexStr(); - order.exchOrderId = QString::fromStdString(CryptoPRNG::generateRandom(8).toHexStr()); + order.exchOrderId = CryptoPRNG::generateRandom(8).toHexStr(); order.quoteId = itMatch->second.quote.quoteId; - order.dateTime = QDateTime::currentDateTime(); + order.dateTime = std::chrono::system_clock::now(); order.security = itMatch->second.quote.security; order.product = itMatch->second.quote.product; order.settlementId = BinaryData::CreateFromHex(itMatch->second.quote.settlementId); diff --git a/UnitTests/TestAdapters.h b/UnitTests/TestAdapters.h index e1d041abc..657ada321 100644 --- a/UnitTests/TestAdapters.h +++ b/UnitTests/TestAdapters.h @@ -54,7 +54,6 @@ class TestSupervisor : public bs::message::Adapter bs::message::SeqId send(bs::message::TerminalUsers sender, bs::message::TerminalUsers receiver , const std::string &message, bs::message::SeqId respId = 0); - bool push(const bs::message::Envelope &env) { return bs::message::Adapter::push(env); } bool pushFill(bs::message::Envelope &env) { return bs::message::Adapter::pushFill(env); } using FilterCb = std::function; diff --git a/UnitTests/TestAuthEid.cpp b/UnitTests/TestAuthEid.cpp index eb25b1b31..5f65bb2cd 100644 --- a/UnitTests/TestAuthEid.cpp +++ b/UnitTests/TestAuthEid.cpp @@ -12,7 +12,7 @@ #include #include "BtcUtils.h" #include "bs_communication.pb.h" -#include "AutheIDClient.h" +//#include "AutheIDClient.h" namespace { @@ -32,6 +32,7 @@ namespace { } +#if 0 TEST(TestAuthEid, VerifySignature) { auto testResult = R"( @@ -101,3 +102,4 @@ TEST(TestAuthEid, VerifySignature) result = AutheIDClient::verifySignature(signResultInvalid, AuthEidEnv::Prod); EXPECT_TRUE(!result.valid); } +#endif //0 diff --git a/UnitTests/TestCommon.cpp b/UnitTests/TestCommon.cpp index dc537316b..c16769d98 100644 --- a/UnitTests/TestCommon.cpp +++ b/UnitTests/TestCommon.cpp @@ -108,7 +108,7 @@ TEST(TestCommon, AssetManager) syncMgr->syncWallets(); const auto &mdCallbacks = env.mdCallbacks(); - AssetManager assetMgr(StaticLogger::loggerPtr, syncMgr, mdCallbacks, env.celerConnection()); + AssetManager assetMgr(StaticLogger::loggerPtr, nullptr); //FIXME: pass AssetMgrCT as a second arg, if needed assetMgr.connect(mdCallbacks.get(), &MDCallbacksQt::MDSecurityReceived, &assetMgr, &AssetManager::onMDSecurityReceived); assetMgr.connect(mdCallbacks.get(), &MDCallbacksQt::MDSecuritiesReceived, &assetMgr, &AssetManager::onMDSecuritiesReceived); diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index 7b7d1ed8b..f3f2457b6 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -15,12 +15,10 @@ #include "ArmoryObject.h" #include "ArmorySettings.h" #include "AuthAddressManager.h" -#include "Celer/CelerClient.h" #include "ConnectionManager.h" #include "CoreWalletsManager.h" #include "MarketDataProvider.h" #include "MDCallbacksQt.h" -#include "QuoteProvider.h" #include "SystemFileUtils.h" #include "UiUtils.h" @@ -75,8 +73,6 @@ void TestEnv::shutdown() } mdProvider_ = nullptr; - quoteProvider_ = nullptr; - celerConn_ = nullptr; assetMgr_ = nullptr; connMgr_ = nullptr; @@ -148,7 +144,6 @@ void TestEnv::requireAssets() mdCallbacks_ = std::make_shared(); mdProvider_ = std::make_shared(logger_, mdCallbacks_.get()); - quoteProvider_ = std::make_shared(assetMgr_, logger_); } void TestEnv::requireConnections() @@ -156,7 +151,6 @@ void TestEnv::requireConnections() requireArmory(); connMgr_ = std::make_shared(logger_); - celerConn_ = std::make_shared(connMgr_); } /////////////////////////////////////////////////////////////////////////////// diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index c69b3a75e..aca7a859a 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -466,12 +466,10 @@ class TestEnv [[deprecated]] std::shared_ptr assetMgr() { return assetMgr_; } std::shared_ptr blockMonitor() { return blockMonitor_; } [[deprecated]] std::shared_ptr connectionMgr() { return connMgr_; } - [[deprecated]] std::shared_ptr celerConnection() { return celerConn_; } std::shared_ptr logger() { return logger_; } std::shared_ptr walletsMgr() { return walletsMgr_; } [[deprecated]] std::shared_ptr mdProvider() { return mdProvider_; } [[deprecated]] std::shared_ptr mdCallbacks() { return mdCallbacks_; } - [[deprecated]] std::shared_ptr quoteProvider() { return quoteProvider_; } void requireArmory(bool waitForReady = true); [[deprecated]] void requireAssets(); @@ -481,11 +479,9 @@ class TestEnv std::shared_ptr appSettings_; std::shared_ptr assetMgr_; std::shared_ptr blockMonitor_; - std::shared_ptr celerConn_; std::shared_ptr connMgr_; std::shared_ptr mdCallbacks_; std::shared_ptr mdProvider_; - std::shared_ptr quoteProvider_; std::shared_ptr walletsMgr_; std::shared_ptr logger_; std::shared_ptr armoryConnection_; diff --git a/UnitTests/TestNetwork.cpp b/UnitTests/TestNetwork.cpp index e64827194..38f2585a1 100644 --- a/UnitTests/TestNetwork.cpp +++ b/UnitTests/TestNetwork.cpp @@ -13,11 +13,8 @@ #include "Bip15xDataConnection.h" #include "Bip15xServerConnection.h" -#include "Celer/CommonUtils.h" -#include "Celer/MessageMapper.h" #include "CommonTypes.h" #include "IdStringGenerator.h" -#include "QuoteProvider.h" #include "ServerConnection.h" #include "TestEnv.h" #include "TransportBIP15x.h" @@ -61,6 +58,7 @@ static bs::network::TransportBIP15xServer::TrustedClientsCallback constructTrust }; } +#if 0 TEST(TestNetwork, CelerMessageMapper) { for (int i = CelerAPI::CelerMessageTypeFirst; i < CelerAPI::CelerMessageTypeLast; i++) { @@ -87,6 +85,7 @@ TEST(TestNetwork, Types) EXPECT_EQ(bs::celer::fromCelerProductType(celerProdType), t) << "Asset type " << bs::network::Asset::toString(t); } } +#endif //0 TEST(TestNetwork, IdString) { diff --git a/UnitTests/TestOtc.cpp b/UnitTests/TestOtc.cpp index bbb771e3a..3837cb520 100644 --- a/UnitTests/TestOtc.cpp +++ b/UnitTests/TestOtc.cpp @@ -19,7 +19,7 @@ #include "TestEnv.h" #include "TradesUtils.h" #include "TradesVerification.h" -#include "Trading/OtcClient.h" +//#include "Trading/OtcClient.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -103,10 +103,11 @@ class TestPeer : public SignerCallbackTarget } const auto regIDs = syncWalletMgr_->registerWallets(); UnitTestWalletACT::waitOnRefresh(regIDs); - +#if 0 OtcClientParams params; otc_ = std::make_shared(env.logger(), syncWalletMgr_, env.armoryConnection(), signer_, nullptr, nullptr, env.appSettings(), params); otc_->setOwnContactId(name); +#endif } std::string name_; @@ -115,7 +116,7 @@ class TestPeer : public SignerCallbackTarget std::shared_ptr syncWalletMgr_; std::shared_ptr signer_; std::shared_ptr hct_; - std::shared_ptr otc_; + //std::shared_ptr otc_; bs::Address authAddress_; bs::Address nativeAddr_; bs::Address nestedAddr_; @@ -143,7 +144,7 @@ class TestOtc : public ::testing::Test auto d = response.mutable_start_otc(); d->set_request_id(request.start_otc().request_id()); d->set_settlement_id(kSettlementId); - peer.otc_->processPbMessage(response); + //peer.otc_->processPbMessage(response); break; } @@ -246,7 +247,7 @@ class TestOtc : public ::testing::Test ASSERT_TRUE(false) << std::to_string(request.data_case()); } }; - +#if 0 QObject::connect(peer1_.otc_.get(), &OtcClient::sendContactMessage, qApp, [this](const std::string &contactId, const BinaryData &data) { peer2_.otc_->processContactMessage(peer1_.name_, data); }, Qt::QueuedConnection); @@ -260,6 +261,7 @@ class TestOtc : public ::testing::Test QObject::connect(peer2_.otc_.get(), &OtcClient::sendPbMessage, qApp, [this, processPbMessage](const std::string &data) { processPbMessage(peer2_, data); }, Qt::QueuedConnection); +#endif //0 } void sendStateUpdate(ProxyTerminalPb::OtcState state) @@ -269,8 +271,8 @@ class TestOtc : public ::testing::Test d->set_settlement_id(kSettlementId); d->set_state(state); d->set_timestamp_ms(QDateTime::currentDateTime().toMSecsSinceEpoch()); - peer1_.otc_->processPbMessage(response); - peer2_.otc_->processPbMessage(response); + //peer1_.otc_->processPbMessage(response); + //peer2_.otc_->processPbMessage(response); } void mineNewBlocks(const bs::Address &dst, unsigned count) @@ -296,8 +298,8 @@ class TestOtc : public ::testing::Test manualInput_ = testNum & 0x0004; withoutChange_ = testNum & 0x0008; - peer1_.otc_->contactConnected(peer2_.name_); - peer2_.otc_->contactConnected(peer1_.name_); + //peer1_.otc_->contactConnected(peer2_.name_); + //peer2_.otc_->contactConnected(peer1_.name_); auto &sender = sellerOffers_ ? peer1_ : peer2_; auto &receiver = sellerOffers_ ? peer2_ : peer1_; @@ -327,7 +329,7 @@ class TestOtc : public ::testing::Test // needed to be able sign pay-in and pay-out const bs::core::WalletPasswordScoped lock1(peer1_.wallet_, kPassword); const bs::core::WalletPasswordScoped lock2(peer2_.wallet_, kPassword); - +#if 0 { bs::network::otc::Offer offer; offer.price = 100; @@ -370,6 +372,7 @@ class TestOtc : public ::testing::Test ASSERT_TRUE(payoutDone_); ASSERT_TRUE(payinSealDone_); ASSERT_TRUE(payinDone_); +#endif //0 } std::unique_ptr env_; diff --git a/UnitTests/TestUi.cpp b/UnitTests/TestUi.cpp index 10f2a3b56..a101f391c 100644 --- a/UnitTests/TestUi.cpp +++ b/UnitTests/TestUi.cpp @@ -21,8 +21,6 @@ #include "CustomControls/CustomDoubleSpinBox.h" #include "CustomControls/CustomDoubleValidator.h" #include "InprocSigner.h" -#include "Trading/RequestingQuoteWidget.h" -#include "Trading/RFQTicketXBT.h" #include "TestEnv.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" diff --git a/common b/common index 0534f37f2..ba5e6be50 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 0534f37f24aecb0136e31da4dadf31276e566eb7 +Subproject commit ba5e6be50ab8568845d56fd127bca873ccff8551 From 046fb6a7a3f92a96b6268c656c8b82f6361f8ee2 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 5 Aug 2022 20:17:10 +0300 Subject: [PATCH 137/146] [LEV-2349] Basic wallet/TX functionality --- BlockSettleApp/CMakeLists.txt | 3 +- BlockSettleApp/main.cpp | 10 +- BlockSettleHW/hwdevicemanager.cpp | 5 +- BlockSettleHW/ledger/ledgerDevice.cpp | 4 +- BlockSettleHW/trezor/trezorClient.cpp | 19 +- BlockSettleHW/trezor/trezorDevice.cpp | 2 +- BlockSettleSigner/CMakeLists.txt | 1 - BlockSettleSigner/HeadlessApp.cpp | 15 +- BlockSettleSigner/HeadlessApp.h | 3 +- BlockSettleSigner/PdfBackupQmlPrinter.h | 2 +- BlockSettleSigner/QMLStatusUpdater.h | 2 +- BlockSettleSigner/QmlBridge.cpp | 2 +- BlockSettleSigner/QmlFactory.h | 6 +- BlockSettleSigner/SignerAdapter.cpp | 2 +- BlockSettleSigner/SignerAdapter.h | 6 +- BlockSettleSigner/SignerAdapterContainer.cpp | 2 +- BlockSettleSigner/SignerAdapterContainer.h | 44 +- BlockSettleSigner/SignerAdapterListener.cpp | 33 +- BlockSettleSigner/SignerAdapterListener.h | 3 +- BlockSettleSigner/SignerInterfaceListener.h | 2 +- BlockSettleSigner/TXInfo.cpp | 24 +- BlockSettleSigner/TXInfo.h | 4 +- BlockSettleSigner/WalletsProxy.cpp | 6 +- BlockSettleSigner/WalletsProxy.h | 2 +- .../interfaces/GUI_QML/QMLApp.cpp | 30 +- BlockSettleSigner/interfaces/GUI_QML/QMLApp.h | 2 +- .../interfaces/GUI_QML/QmlWalletsViewModel.h | 2 +- BlockSettleSigner/main.cpp | 56 +-- BlockSettleTracker/CMakeLists.txt | 1 - BlockSettleUILib/AddressDetailDialog.h | 2 +- BlockSettleUILib/AddressDetailsWidget.cpp | 350 ++----------- BlockSettleUILib/AddressDetailsWidget.h | 16 +- BlockSettleUILib/AddressListModel.cpp | 7 +- BlockSettleUILib/AddressListModel.h | 2 +- BlockSettleUILib/CCPortfolioModel.h | 2 +- BlockSettleUILib/CCWidget.h | 2 +- BlockSettleUILib/CoinControlDialog.cpp | 2 +- BlockSettleUILib/CoinControlModel.cpp | 2 +- BlockSettleUILib/CoinControlWidget.cpp | 2 +- BlockSettleUILib/CreateTransactionDialog.cpp | 284 ++++------- BlockSettleUILib/CreateTransactionDialog.h | 21 +- .../CreateTransactionDialogAdvanced.cpp | 186 +++---- .../CreateTransactionDialogAdvanced.h | 8 +- .../CreateTransactionDialogSimple.cpp | 33 +- .../CreateTransactionDialogSimple.h | 4 +- .../CreateTransactionDialogSimple.ui | 86 +++- BlockSettleUILib/ExplorerWidget.cpp | 25 - BlockSettleUILib/ExplorerWidget.h | 7 +- .../RootWalletPropertiesDialog.cpp | 2 +- .../RootWalletPropertiesDialog.h | 6 +- BlockSettleUILib/NewAddressDialog.cpp | 47 +- BlockSettleUILib/NewAddressDialog.h | 5 +- BlockSettleUILib/OpenURIDialog.cpp | 3 +- BlockSettleUILib/PortfolioWidget.h | 2 +- BlockSettleUILib/SeedDialog.cpp | 92 ++++ BlockSettleUILib/SeedDialog.h | 74 +++ BlockSettleUILib/SeedDialog.ui | 467 ++++++++++++++++++ BlockSettleUILib/SelectWalletDialog.h | 2 +- .../Settings/ArmoryServersViewModel.h | 1 - BlockSettleUILib/Settings/ConfigDialog.cpp | 21 +- BlockSettleUILib/Settings/ConfigDialog.h | 5 +- .../Settings/GeneralSettingsPage.cpp | 4 +- .../Settings/HeadlessSettings.cpp | 21 +- BlockSettleUILib/Settings/HeadlessSettings.h | 5 +- .../Settings/NetworkSettingsPage.cpp | 2 +- BlockSettleUILib/Settings/SignerSettings.cpp | 8 +- BlockSettleUILib/Settings/SignerSettings.h | 7 +- .../Settings/SignerSettingsPage.cpp | 4 +- BlockSettleUILib/Settings/SignersProvider.cpp | 2 +- BlockSettleUILib/StatusBarView.cpp | 2 +- BlockSettleUILib/StatusBarView.h | 2 +- BlockSettleUILib/TransactionDetailDialog.cpp | 14 +- BlockSettleUILib/TransactionDetailDialog.h | 1 - BlockSettleUILib/TransactionDetailsWidget.cpp | 80 +-- BlockSettleUILib/TransactionDetailsWidget.h | 14 +- BlockSettleUILib/TransactionsViewModel.h | 2 +- BlockSettleUILib/TransactionsWidget.cpp | 2 +- .../TransactionsWidgetInterface.cpp | 133 +---- .../TransactionsWidgetInterface.h | 1 - BlockSettleUILib/UiUtils.cpp | 26 +- BlockSettleUILib/UiUtils.h | 7 +- BlockSettleUILib/UtxoReservationManager.cpp | 36 +- BlockSettleUILib/UtxoReservationManager.h | 4 - BlockSettleUILib/WalletsViewModel.cpp | 7 +- BlockSettleUILib/WalletsViewModel.h | 5 +- BlockSettleUILib/WalletsWidget.cpp | 122 +---- BlockSettleUILib/WalletsWidget.h | 7 +- CMakeLists.txt | 54 +- Core/ApiJson.cpp | 2 - Core/ApiJson.h | 2 +- Core/AssetsAdapter.cpp | 62 --- Core/AssetsAdapter.h | 10 +- Core/SettingsAdapter.cpp | 61 --- Core/SettingsAdapter.h | 4 +- Core/SignerAdapter.cpp | 303 +++--------- Core/SignerAdapter.h | 31 +- Core/TerminalMessage.cpp | 3 +- GUI/QtWidgets/MainWindow.cpp | 21 + GUI/QtWidgets/MainWindow.h | 11 +- GUI/QtWidgets/QtGuiAdapter.cpp | 111 ++--- GUI/QtWidgets/QtGuiAdapter.h | 12 +- UnitTests/BlockchainMonitor.cpp | 2 +- UnitTests/BlockchainMonitor.h | 3 +- UnitTests/CMakeLists.txt | 1 - UnitTests/TestAdapters.cpp | 2 +- UnitTests/TestAddress.cpp | 25 +- UnitTests/TestAuth.cpp | 10 +- UnitTests/TestAuth.h | 7 +- UnitTests/TestCCoin.cpp | 6 +- UnitTests/TestCCoin.h | 6 +- UnitTests/TestCCoinAsync.cpp | 8 +- UnitTests/TestCCoinAsync.h | 7 +- UnitTests/TestCommon.cpp | 6 +- UnitTests/TestEnv.cpp | 86 ++-- UnitTests/TestEnv.h | 44 +- UnitTests/TestOtc.cpp | 6 +- UnitTests/TestSettlement.cpp | 8 +- UnitTests/TestSettlement.h | 2 +- UnitTests/TestUi.cpp | 2 +- UnitTests/TestWallet.cpp | 23 +- UnitTests/TestWalletArmory.cpp | 41 +- UnitTests/TestWebSockets.cpp | 8 +- UnitTests/main.cpp | 4 +- common | 2 +- 124 files changed, 1465 insertions(+), 2110 deletions(-) create mode 100644 BlockSettleUILib/SeedDialog.cpp create mode 100644 BlockSettleUILib/SeedDialog.h create mode 100644 BlockSettleUILib/SeedDialog.ui diff --git a/BlockSettleApp/CMakeLists.txt b/BlockSettleApp/CMakeLists.txt index 9581b766c..b55e0c22f 100644 --- a/BlockSettleApp/CMakeLists.txt +++ b/BlockSettleApp/CMakeLists.txt @@ -54,7 +54,6 @@ TARGET_LINK_LIBRARIES( ${BLOCKSETTLE_APP_NAME} ${CPP_WALLET_LIB_NAME} ${CRYPTO_LIB_NAME} ${BOTAN_LIB} - ${ZMQ_LIB} ${QRENCODE_LIB} ${QT_LINUX_LIBS} ${WS_LIB} @@ -62,6 +61,8 @@ TARGET_LINK_LIBRARIES( ${BLOCKSETTLE_APP_NAME} Qt5::Core Qt5::Widgets Qt5::Gui + Qt5::QSQLiteDriverPlugin + Qt5::Sql Qt5::Network Qt5::PrintSupport Qt5::Core diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 9852ccebe..cc59b06ba 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -23,9 +23,7 @@ #include "BSTerminalSplashScreen.h" #include "EncryptionUtils.h" -#include "Adapters/AuthEidAdapter.h" #include "Adapters/BlockchainAdapter.h" -#include "Adapters/OnChainTrackerAdapter.h" #include "Adapters/WalletsAdapter.h" #include "ApiAdapter.h" #include "ApiJson.h" @@ -35,7 +33,6 @@ #include "SettingsAdapter.h" #include "SignerAdapter.h" -#include "btc/ecc.h" #include //#include "AppNap.h" @@ -55,10 +52,8 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) Q_IMPORT_PLUGIN(QCupsPrinterSupportPlugin) #endif // USE_QXcbIntegrationPlugin -#ifdef STATIC_BUILD Q_IMPORT_PLUGIN(QSQLiteDriverPlugin) Q_IMPORT_PLUGIN(QICOPlugin) -#endif // STATIC_BUILD Q_DECLARE_METATYPE(ArmorySettings) Q_DECLARE_METATYPE(AsyncClient::LedgerDelegate) @@ -147,7 +142,7 @@ int main(int argc, char** argv) // Initialize libbtc, BIP 150, and BIP 151. 150 uses the proprietary "public" // Armory setting designed to allow the ArmoryDB server to not have to verify // clients. Prevents us from having to import tons of keys into the server. - btc_ecc_start(); + CryptoECDSA::setupContext(); startupBIP151CTX(); startupBIP150CTX(4); @@ -178,9 +173,6 @@ int main(int argc, char** argv) const auto& userBlockchain = bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain); const auto& userWallets = bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets); - inprocBus.addAdapter(std::make_shared(logMgr->logger("trk") - , bs::message::UserTerminal::create(bs::message::TerminalUsers::OnChainTracker) - , userBlockchain, userWallets, adSettings->createOnChainPlug())); inprocBus.addAdapter(std::make_shared(logMgr->logger())); inprocBus.addAdapter(std::make_shared(logMgr->logger() , userWallets, signAdapter->createClient(), userBlockchain)); diff --git a/BlockSettleHW/hwdevicemanager.cpp b/BlockSettleHW/hwdevicemanager.cpp index 7acb4cad0..88396ca11 100644 --- a/BlockSettleHW/hwdevicemanager.cpp +++ b/BlockSettleHW/hwdevicemanager.cpp @@ -14,12 +14,11 @@ #include "ledger/ledgerClient.h" #include "ledger/ledgerDevice.h" #include "ConnectionManager.h" -#include "WalletManager.h" #include "Wallets/SyncWalletsManager.h" #include "Wallets/SyncHDWallet.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" -using namespace ArmorySigner; +using namespace Armory::Signer; HwDeviceManager::HwDeviceManager(const std::shared_ptr& connectionManager, std::shared_ptr walletManager, bool testNet, QObject* parent /*= nullptr*/) diff --git a/BlockSettleHW/ledger/ledgerDevice.cpp b/BlockSettleHW/ledger/ledgerDevice.cpp index 1001c81b1..aa5214db3 100644 --- a/BlockSettleHW/ledger/ledgerDevice.cpp +++ b/BlockSettleHW/ledger/ledgerDevice.cpp @@ -12,7 +12,7 @@ #include "ledger/ledgerDevice.h" #include "ledger/ledgerClient.h" #include "Assets.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "CoreWallet.h" #include "Wallets/SyncWalletsManager.h" #include "Wallets/SyncHDWallet.h" @@ -539,7 +539,7 @@ BIP32_Node LedgerCommandThread::getPublicKeyApdu(bs::hd::Path&& derivationPath, bool result = pubKey.parseFromResponse(response); auto data = SecureBinaryData::fromString(pubKey.pubKey_.toStdString()); - Asset_PublicKey pubKeyAsset(data); + Armory::Assets::Asset_PublicKey pubKeyAsset(data); SecureBinaryData chainCode = SecureBinaryData::fromString(pubKey.chainCode_.toStdString()); uint32_t fingerprint = 0; diff --git a/BlockSettleHW/trezor/trezorClient.cpp b/BlockSettleHW/trezor/trezorClient.cpp index e1c8bd840..c1a440089 100644 --- a/BlockSettleHW/trezor/trezorClient.cpp +++ b/BlockSettleHW/trezor/trezorClient.cpp @@ -306,15 +306,17 @@ void TrezorClient::post(QByteArray&& urlMethod, std::functionGetNAM()->post(request, input); - auto connection = connect(reply, &QNetworkReply::finished, this, [cbCopy = cb, repCopy = reply, sender = QPointer(this)]{ - if (!sender) { - return; // TREZOR client already destroyed - } + QNetworkReply *reply = QNetworkAccessManager().post(request, input); + auto connection = connect(reply, &QNetworkReply::finished, this + , [cbCopy = cb, repCopy = reply, sender = QPointer(this)] + { + if (!sender) { + return; // TREZOR client already destroyed + } - cbCopy(repCopy); - repCopy->deleteLater(); - }); + cbCopy(repCopy); + repCopy->deleteLater(); + }); // Timeout if (timeout) { @@ -325,7 +327,6 @@ void TrezorClient::post(QByteArray&& urlMethod, std::functionabort(); }); } - } void TrezorClient::cleanDeviceData() diff --git a/BlockSettleHW/trezor/trezorDevice.cpp b/BlockSettleHW/trezor/trezorDevice.cpp index 56e09f03e..1f580e25a 100644 --- a/BlockSettleHW/trezor/trezorDevice.cpp +++ b/BlockSettleHW/trezor/trezorDevice.cpp @@ -12,7 +12,7 @@ #include "trezorClient.h" #include "ConnectionManager.h" #include "headless.pb.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "CoreWallet.h" #include "Wallets/SyncWalletsManager.h" #include "Wallets/SyncHDWallet.h" diff --git a/BlockSettleSigner/CMakeLists.txt b/BlockSettleSigner/CMakeLists.txt index 154f00109..a0b2e54e9 100644 --- a/BlockSettleSigner/CMakeLists.txt +++ b/BlockSettleSigner/CMakeLists.txt @@ -80,7 +80,6 @@ TARGET_LINK_LIBRARIES(${SIGNER_APP_NAME} ${CPP_WALLET_LIB_NAME} ${CRYPTO_LIB_NAME} ${BOTAN_LIB} - ${ZMQ_LIB} ${WS_LIB} ${QRENCODE_LIB} ${QT_LINUX_LIBS} diff --git a/BlockSettleSigner/HeadlessApp.cpp b/BlockSettleSigner/HeadlessApp.cpp index 9dec4072f..1d71fcb78 100644 --- a/BlockSettleSigner/HeadlessApp.cpp +++ b/BlockSettleSigner/HeadlessApp.cpp @@ -25,7 +25,7 @@ #include "DispatchQueue.h" #include "GenoaStreamServerConnection.h" #include "HeadlessApp.h" -#include "HeadlessContainerListener.h" +#include "Wallets/HeadlessContainerListener.h" #include "Settings/HeadlessSettings.h" #include "SignerAdapterListener.h" #include "SignerVersion.h" @@ -322,13 +322,7 @@ void HeadlessAppObj::startTerminalsProcessing() if (!result) { logger_->error("Failed to bind to {}:{}" , settings_->listenAddress(), settings_->listenPort()); - - // Abort only if litegui used, fullgui should just show error message instead - if (settings_->runMode() == bs::signer::RunMode::litegui) { - throw std::runtime_error("failed to bind listening socket"); - } } - signerBindStatus_ = result ? bs::signer::BindStatus::Succeed : bs::signer::BindStatus::Failed; } @@ -481,13 +475,6 @@ void HeadlessAppObj::passwordReceived(const std::string &walletId, ErrorCode res } } -void HeadlessAppObj::windowVisibilityChanged(bool visible) -{ - if (terminalListener_ && settings_->runMode() == bs::signer::RunMode::litegui) { - terminalListener_->windowVisibilityChanged(visible); - } -} - void HeadlessAppObj::close() { queue_->quit(); diff --git a/BlockSettleSigner/HeadlessApp.h b/BlockSettleSigner/HeadlessApp.h index 4858d7db5..f070d1d57 100644 --- a/BlockSettleSigner/HeadlessApp.h +++ b/BlockSettleSigner/HeadlessApp.h @@ -15,7 +15,7 @@ #include #include -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include "BSErrorCode.h" namespace spdlog { @@ -61,7 +61,6 @@ class HeadlessAppObj void reloadWallets(bool notifyGUI, const std::function & = nullptr); void setLimits(bs::signer::Limits); void passwordReceived(const std::string &walletId, bs::error::ErrorCode result, const SecureBinaryData &); - void windowVisibilityChanged(bool visible); bs::error::ErrorCode activateAutoSign(const std::string &walletId, bool activate, const SecureBinaryData& password); diff --git a/BlockSettleSigner/PdfBackupQmlPrinter.h b/BlockSettleSigner/PdfBackupQmlPrinter.h index 252d47a4b..9c9429022 100644 --- a/BlockSettleSigner/PdfBackupQmlPrinter.h +++ b/BlockSettleSigner/PdfBackupQmlPrinter.h @@ -13,7 +13,7 @@ #include -#include "QSeed.h" +#include "Wallets/QSeed.h" #include "PaperBackupWriter.h" #include diff --git a/BlockSettleSigner/QMLStatusUpdater.h b/BlockSettleSigner/QMLStatusUpdater.h index 30333862d..f4b5e76e8 100644 --- a/BlockSettleSigner/QMLStatusUpdater.h +++ b/BlockSettleSigner/QMLStatusUpdater.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include "Settings/SignerSettings.h" namespace spdlog { diff --git a/BlockSettleSigner/QmlBridge.cpp b/BlockSettleSigner/QmlBridge.cpp index 61e6e49bc..9747ff2f2 100644 --- a/BlockSettleSigner/QmlBridge.cpp +++ b/BlockSettleSigner/QmlBridge.cpp @@ -9,7 +9,7 @@ */ #include "QmlBridge.h" -#include "SignerUiDefs.h" +#include "Wallets/SignerUiDefs.h" #include diff --git a/BlockSettleSigner/QmlFactory.h b/BlockSettleSigner/QmlFactory.h index 5cebdf35c..2712fe4f3 100644 --- a/BlockSettleSigner/QmlFactory.h +++ b/BlockSettleSigner/QmlFactory.h @@ -15,9 +15,9 @@ #include #include #include "BSErrorCode.h" -#include "QSeed.h" -#include "QPasswordData.h" -#include "QWalletInfo.h" +#include "Wallets/QSeed.h" +#include "Wallets/QPasswordData.h" +#include "Wallets/QWalletInfo.h" #include "bs_signer.pb.h" diff --git a/BlockSettleSigner/SignerAdapter.cpp b/BlockSettleSigner/SignerAdapter.cpp index a20a5c6d6..deb90af78 100644 --- a/BlockSettleSigner/SignerAdapter.cpp +++ b/BlockSettleSigner/SignerAdapter.cpp @@ -14,7 +14,7 @@ #include #include "Bip15xDataConnection.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "SignerAdapterContainer.h" #include "SignerInterfaceListener.h" #include "SystemFileUtils.h" diff --git a/BlockSettleSigner/SignerAdapter.h b/BlockSettleSigner/SignerAdapter.h index 72505e8e8..74aaba156 100644 --- a/BlockSettleSigner/SignerAdapter.h +++ b/BlockSettleSigner/SignerAdapter.h @@ -16,9 +16,9 @@ #include "CoreWallet.h" #include "QmlBridge.h" #include "QmlFactory.h" -#include "QPasswordData.h" -#include "HeadlessContainer.h" -#include "SignerDefs.h" +#include "Wallets/QPasswordData.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/SignerDefs.h" #include "bs_signer.pb.h" diff --git a/BlockSettleSigner/SignerAdapterContainer.cpp b/BlockSettleSigner/SignerAdapterContainer.cpp index 29ccd3eee..63deacbc3 100644 --- a/BlockSettleSigner/SignerAdapterContainer.cpp +++ b/BlockSettleSigner/SignerAdapterContainer.cpp @@ -16,7 +16,7 @@ #include "DataConnection.h" #include "DataConnectionListener.h" #include "HeadlessApp.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "SignerInterfaceListener.h" #include "Wallets/SyncWalletsManager.h" diff --git a/BlockSettleSigner/SignerAdapterContainer.h b/BlockSettleSigner/SignerAdapterContainer.h index 8870668e8..bb07153c0 100644 --- a/BlockSettleSigner/SignerAdapterContainer.h +++ b/BlockSettleSigner/SignerAdapterContainer.h @@ -15,7 +15,7 @@ #include #include "CoreWallet.h" #include "SignerAdapter.h" -#include "WalletSignerContainer.h" +#include "Wallets/WalletSignerContainer.h" namespace spdlog { class logger; @@ -34,58 +34,24 @@ class SignAdapterContainer : public WalletSignerContainer, public SignerCallback bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & , const SecureBinaryData &password); - [[deprecated]] bs::signer::RequestId signTXRequest(const bs::core::wallet::TXSignRequest & - , TXSignMode = TXSignMode::Full, bool = false) override { return 0; } void signTXRequest(const bs::core::wallet::TXSignRequest& , const std::function& , TXSignMode mode = TXSignMode::Full, bool keepDuplicatedRecipients = false) override {} - void createSettlementWallet(const bs::Address & - , const std::function &) override {} - void setSettlementID(const std::string &, const SecureBinaryData & - , const std::function &) override {} - void getSettlementPayinAddress(const std::string &, - const bs::core::wallet::SettlementData &, const std::function &) override {} void getRootPubkey(const std::string& , const std::function &) override {} - bs::signer::RequestId signSettlementTXRequest(const bs::core::wallet::TXSignRequest & - , const bs::sync::PasswordDialogData & - , TXSignMode - , bool - , const std::function &) override {return 0; } - - bs::signer::RequestId signSettlementPartialTXRequest(const bs::core::wallet::TXSignRequest & - , const bs::sync::PasswordDialogData & - , const std::function & ) override { return 0; } - - bs::signer::RequestId signSettlementPayoutTXRequest(const bs::core::wallet::TXSignRequest & - , const bs::core::wallet::SettlementData &, const bs::sync::PasswordDialogData & - , const std::function &) override { return 0; } - - bs::signer::RequestId signAuthRevocation(const std::string &walletId, const bs::Address &authAddr - , const UTXO &, const bs::Address &bsAddr, const SignTxCb &cb = nullptr) override { return 0; } - bs::signer::RequestId resolvePublicSpenders(const bs::core::wallet::TXSignRequest & , const SignerStateCb &) override { return 0; } bs::signer::RequestId updateDialogData(const bs::sync::PasswordDialogData &, uint32_t = 0) override { return 0; } bs::signer::RequestId CancelSignTx(const BinaryData &tx) override { return 0; } - bs::signer::RequestId setUserId(const BinaryData &, const std::string &) override { return 0; } - bs::signer::RequestId syncCCNames(const std::vector &) override { return 0; } - bool createHDLeaf(const std::string&, const bs::hd::Path& , const std::vector& = {} , bs::sync::PasswordDialogData = {}, const CreateHDLeafCb & = nullptr) override { return false; } - bool enableTradingInHDWallet(const std::string &, const BinaryData & - , bs::sync::PasswordDialogData = {}, const UpdateWalletStructureCB& = nullptr) override { return false; } - - bool promoteWalletToPrimary(const std::string& rootWalletId - , bs::sync::PasswordDialogData dialogData = {}, const UpdateWalletStructureCB& cb = nullptr) override { return false; } - bs::signer::RequestId DeleteHDRoot(const std::string &rootWalletId) override; bs::signer::RequestId DeleteHDLeaf(const std::string &) override { return 0; } bs::signer::RequestId GetInfo(const std::string &) override { return 0; } @@ -103,14 +69,6 @@ class SignAdapterContainer : public WalletSignerContainer, public SignerCallback const std::function> &)> &) override {} void syncNewAddresses(const std::string &, const std::vector & , const std::function> &)> &) override {} - void getChatNode(const std::string &walletID, const std::function &) override {} - void setSettlAuthAddr(const std::string &walletId, const BinaryData &, const bs::Address &addr) override {} - void getSettlAuthAddr(const std::string &walletId, const BinaryData & - , const std::function &) override {} - void setSettlCP(const std::string &walletId, const BinaryData &payinHash, const BinaryData &settlId - , const BinaryData &cpPubKey) override {} - void getSettlCP(const std::string &walletId, const BinaryData &payinHash - , const std::function &) override {} bool isWalletOffline(const std::string &id) const override { return (woWallets_.find(id) != woWallets_.end()); } diff --git a/BlockSettleSigner/SignerAdapterListener.cpp b/BlockSettleSigner/SignerAdapterListener.cpp index f0612298d..4e4f7442a 100644 --- a/BlockSettleSigner/SignerAdapterListener.cpp +++ b/BlockSettleSigner/SignerAdapterListener.cpp @@ -17,9 +17,9 @@ #include "CoreWalletsManager.h" #include "DispatchQueue.h" #include "HeadlessApp.h" -#include "HeadlessContainerListener.h" -#include "OfflineSigner.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/HeadlessContainerListener.h" +#include "Wallets/OfflineSigner.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "ScopeGuard.h" #include "ServerConnection.h" #include "Settings/HeadlessSettings.h" @@ -58,11 +58,6 @@ class HeadlessContainerCallbacksImpl : public HeadlessContainerCallbacks signer::ClientDisconnected evt; evt.set_client_id(clientId); owner_->sendData(signer::PeerDisconnectedType, evt.SerializeAsString()); - - if (owner_->settings_->runMode() == bs::signer::RunMode::litegui) { - owner_->logger_->info("Quit because terminal disconnected unexpectedly and litegui used"); - owner_->queue_->quit(); - } } void ccNamesReceived(bool result) override @@ -258,9 +253,6 @@ void SignerAdapterListener::processData(const std::string &clientId, const std:: case signer::ChangeControlPasswordType: rc = onChangeControlPassword(packet.data(), packet.id()); break; - case signer::WindowStatusType: - rc = onWindowsStatus(packet.data(), packet.id()); - break; case signer::VerifyOfflineTxRequestType: rc = onVerifyOfflineTx(packet.data(), packet.id()); break; @@ -413,7 +405,7 @@ bool SignerAdapterListener::onSyncHDWallet(const std::string &data, bs::signer:: throw std::runtime_error("unexpected leaf type"); } const auto rootAsset = settlLeaf->getRootAsset(); - const auto rootSingle = std::dynamic_pointer_cast(rootAsset); + const auto rootSingle = std::dynamic_pointer_cast(rootAsset); if (rootSingle == nullptr) { throw std::runtime_error("invalid root asset"); } @@ -511,12 +503,12 @@ bool SignerAdapterListener::onGetDecryptedNode(const std::string &data, bs::sign seedStr = seed.seed().toBinStr(); privKeyStr = seed.toXpriv().toBinStr(); } - catch (const WalletException &e) { + catch (const Armory::Wallets::WalletException &e) { logger_->error("[SignerAdapterListener::onGetDecryptedNode] failed to decrypt wallet with id {}: {}" , request.wallet_id(), e.what()); return false; } - catch (const DecryptedDataContainerException &e) { + catch (const Armory::Wallets::Encryption::DecryptedDataContainerException &e) { logger_->error("[SignerAdapterListener::onGetDecryptedNode] wallet {} decryption failure: {}" , request.wallet_id(), e.what()); return false; @@ -589,17 +581,6 @@ bool SignerAdapterListener::onChangeControlPassword(const std::string &data, bs: return true; } -bool SignerAdapterListener::onWindowsStatus(const std::string &data, bs::signer::RequestId) -{ - headless::WindowStatus msg; - if (!msg.ParseFromString(data)) { - logger_->error("[SignerAdapterListener::{}] failed to parse request", __func__); - return false; - } - app_->windowVisibilityChanged(msg.visible()); - return true; -} - bool SignerAdapterListener::onVerifyOfflineTx(const std::string &data, bs::signer::RequestId reqId) { signer::VerifyOfflineTxRequest request; @@ -961,7 +942,7 @@ void SignerAdapterListener::onStarted() void SignerAdapterListener::shutdownIfNeeded() { - if (settings_->runMode() == bs::signer::RunMode::litegui && app_) { + if (app_) { logger_->info("terminal disconnect detected, shutdown..."); app_->close(); } diff --git a/BlockSettleSigner/SignerAdapterListener.h b/BlockSettleSigner/SignerAdapterListener.h index 9e6ef8129..1488f50e3 100644 --- a/BlockSettleSigner/SignerAdapterListener.h +++ b/BlockSettleSigner/SignerAdapterListener.h @@ -13,7 +13,7 @@ #include #include "CoreWallet.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include "ServerConnectionListener.h" #include "BSErrorCode.h" @@ -109,7 +109,6 @@ class SignerAdapterListener : public ServerConnectionListener bool onSyncSettings(const std::string &data); bool onControlPasswordReceived(const std::string &data); bool onChangeControlPassword(const std::string &data, bs::signer::RequestId); - bool onWindowsStatus(const std::string &data, bs::signer::RequestId); bool onVerifyOfflineTx(const std::string &data, bs::signer::RequestId); void shutdownIfNeeded(); diff --git a/BlockSettleSigner/SignerInterfaceListener.h b/BlockSettleSigner/SignerInterfaceListener.h index c563cff45..9bcdfab76 100644 --- a/BlockSettleSigner/SignerInterfaceListener.h +++ b/BlockSettleSigner/SignerInterfaceListener.h @@ -13,7 +13,7 @@ #include #include "DataConnectionListener.h" -#include "SignerUiDefs.h" +#include "Wallets/SignerUiDefs.h" #include "TXInfo.h" #include "bs_signer.pb.h" diff --git a/BlockSettleSigner/TXInfo.cpp b/BlockSettleSigner/TXInfo.cpp index 899d7897d..226a83ed6 100644 --- a/BlockSettleSigner/TXInfo.cpp +++ b/BlockSettleSigner/TXInfo.cpp @@ -11,12 +11,12 @@ #include "TXInfo.h" #include "CheckRecipSigner.h" -#include "OfflineSigner.h" +#include "Wallets/OfflineSigner.h" #include "TxClasses.h" #include "ScriptRecipient.h" #include "Wallets/SyncHDWallet.h" -#include "QWalletInfo.h" +#include "Wallets/QWalletInfo.h" #include #include @@ -99,22 +99,6 @@ double TXInfo::outputAmountFull() const return txReq_.armorySigner_.getTotalOutputsValue() / BTCNumericTypes::BalanceDivider; } -double TXInfo::amountCCReceived(const QString &cc) const -{ - ContainsAddressCb &containsCCAddressCb = [this, cc](const bs::Address &address){ - const auto &wallet = walletsMgr_->getCCWallet(cc.toStdString()); - return wallet->containsAddress(address); - }; - - return txReq_.amountReceived(containsCCAddressCb) / BTCNumericTypes::BalanceDivider; -} - -double TXInfo::amountCCSent() const -{ - return txReq_.totalSpent(containsAnyOurCCAddressCb_) / BTCNumericTypes::BalanceDivider; - -} - double TXInfo::amountXBTReceived() const { // calculate received amount from counterparty outputs @@ -197,7 +181,7 @@ QStringList TXInfo::counterPartyRecipients() const // Get recipients not listed in our wallets // Usable for settlement tx dialog - std::vector> recipientsList; + std::vector> recipientsList; recipientsList = txReq_.getRecipients(containsCounterPartyAddressCb_); QStringList result; @@ -215,7 +199,7 @@ QStringList TXInfo::allRecipients() const // Get all recipients from this tx (minus change) // Usable for regular tx sign dialog - std::vector> recipientsList; + std::vector> recipientsList; recipientsList = txReq_.getRecipients([this](const bs::Address &addr){ return (addr != txReq_.change.address); }); diff --git a/BlockSettleSigner/TXInfo.h b/BlockSettleSigner/TXInfo.h index 0960c9e78..917474dd7 100644 --- a/BlockSettleSigner/TXInfo.h +++ b/BlockSettleSigner/TXInfo.h @@ -12,7 +12,7 @@ #define __TX_INFO_H__ #include "CoreWallet.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "Wallets/SyncWalletsManager.h" #include "Wallets/SyncHDWallet.h" @@ -89,8 +89,6 @@ class TXInfo : public QObject double inputAmountFull() const; double outputAmountFull() const; - Q_INVOKABLE double amountCCReceived(const QString &cc) const; - Q_INVOKABLE double amountCCSent() const; Q_INVOKABLE double amountXBTReceived() const; Q_INVOKABLE bool saveToFile(const QString &fileName) const; diff --git a/BlockSettleSigner/WalletsProxy.cpp b/BlockSettleSigner/WalletsProxy.cpp index b6efdf10e..9585c8725 100644 --- a/BlockSettleSigner/WalletsProxy.cpp +++ b/BlockSettleSigner/WalletsProxy.cpp @@ -31,7 +31,7 @@ #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" #include "BSErrorCodeStrings.h" -#include "OfflineSigner.h" +#include "Wallets/OfflineSigner.h" #include "signer.pb.h" @@ -288,8 +288,8 @@ bool WalletsProxy::backupPrivateKey(const QString &walletId, QString fileName, b if (!wallet) { throw std::runtime_error("failed to find wallet with id " + walletId.toStdString()); } - const auto& encoded = ArmoryBackups::BackupEasy16::encode(chainCode - , ArmoryBackups::BackupType::BIP32_Seed_Structured); + const auto& encoded = Armory::Backups::BackupEasy16::encode(chainCode + , Armory::Backups::BackupType::BIP32_Seed_Structured); if (encoded.size() != 2) { throw std::runtime_error("failed to encode easy16"); } diff --git a/BlockSettleSigner/WalletsProxy.h b/BlockSettleSigner/WalletsProxy.h index dfda694d6..88a2dff66 100644 --- a/BlockSettleSigner/WalletsProxy.h +++ b/BlockSettleSigner/WalletsProxy.h @@ -16,7 +16,7 @@ #include #include "BSErrorCode.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include "hwcommonstructure.h" namespace spdlog { diff --git a/BlockSettleSigner/interfaces/GUI_QML/QMLApp.cpp b/BlockSettleSigner/interfaces/GUI_QML/QMLApp.cpp index 5cafe588d..a00697208 100644 --- a/BlockSettleSigner/interfaces/GUI_QML/QMLApp.cpp +++ b/BlockSettleSigner/interfaces/GUI_QML/QMLApp.cpp @@ -19,14 +19,14 @@ #include "QmlFactory.h" #include "QMLStatusUpdater.h" #include "QmlWalletsViewModel.h" -#include "QPasswordData.h" -#include "QSeed.h" -#include "QWalletInfo.h" +#include "Wallets/QPasswordData.h" +#include "Wallets/QSeed.h" +#include "Wallets/QWalletInfo.h" #include "SignerAdapter.h" #include "Settings/SignerSettings.h" #include "SignerVersion.h" -#include "SignerUiDefs.h" -#include "SignContainer.h" +#include "Wallets/SignerUiDefs.h" +#include "Wallets/SignContainer.h" #include "TXInfo.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -111,21 +111,19 @@ QMLAppObj::QMLAppObj(SignerAdapter *adapter, const std::shared_ptrrunMode() != bs::signer::ui::RunMode::litegui) { - trayIconOptional_ = new QSystemTrayIcon(QIcon(QStringLiteral(":/images/bs_logo.png")), this); - connect(trayIconOptional_, &QSystemTrayIcon::messageClicked, this, &QMLAppObj::onSysTrayMsgClicked); - connect(trayIconOptional_, &QSystemTrayIcon::activated, this, &QMLAppObj::onSysTrayActivated); + trayIconOptional_ = new QSystemTrayIcon(QIcon(QStringLiteral(":/images/bs_logo.png")), this); + connect(trayIconOptional_, &QSystemTrayIcon::messageClicked, this, &QMLAppObj::onSysTrayMsgClicked); + connect(trayIconOptional_, &QSystemTrayIcon::activated, this, &QMLAppObj::onSysTrayActivated); #ifdef BS_USE_DBUS - if (dbus_->isValid()) { - notifMode_ = Freedesktop; + if (dbus_->isValid()) { + notifMode_ = Freedesktop; - QObject::disconnect(trayIconOptional_, &QSystemTrayIcon::messageClicked, - this, &QMLAppObj::onSysTrayMsgClicked); - connect(dbus_, &DBusNotification::messageClicked, this, &QMLAppObj::onSysTrayMsgClicked); - } -#endif // BS_USE_DBUS + QObject::disconnect(trayIconOptional_, &QSystemTrayIcon::messageClicked, + this, &QMLAppObj::onSysTrayMsgClicked); + connect(dbus_, &DBusNotification::messageClicked, this, &QMLAppObj::onSysTrayMsgClicked); } +#endif // BS_USE_DBUS if (adapter) { settingsConnections(); diff --git a/BlockSettleSigner/interfaces/GUI_QML/QMLApp.h b/BlockSettleSigner/interfaces/GUI_QML/QMLApp.h index 0ff470b9f..c0df9cfc7 100644 --- a/BlockSettleSigner/interfaces/GUI_QML/QMLApp.h +++ b/BlockSettleSigner/interfaces/GUI_QML/QMLApp.h @@ -15,7 +15,7 @@ #include #include #include -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace bs { namespace wallet { diff --git a/BlockSettleSigner/interfaces/GUI_QML/QmlWalletsViewModel.h b/BlockSettleSigner/interfaces/GUI_QML/QmlWalletsViewModel.h index c8cb71a34..4b44d8b0e 100644 --- a/BlockSettleSigner/interfaces/GUI_QML/QmlWalletsViewModel.h +++ b/BlockSettleSigner/interfaces/GUI_QML/QmlWalletsViewModel.h @@ -17,7 +17,7 @@ #include #include #include -#include "QWalletInfo.h" +#include "Wallets/QWalletInfo.h" namespace bs { diff --git a/BlockSettleSigner/main.cpp b/BlockSettleSigner/main.cpp index 7acbced82..9d9a50aae 100644 --- a/BlockSettleSigner/main.cpp +++ b/BlockSettleSigner/main.cpp @@ -46,8 +46,6 @@ #include #include -#include - #include #include #include @@ -327,42 +325,32 @@ static int QMLApp(int argc, char **argv QMLAppObj qmlAppObj(&adapter, logger, settings, splashScreen, engine.rootContext()); - switch (settings->runMode()) { - case bs::signer::ui::RunMode::fullgui: - engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); - break; - case bs::signer::ui::RunMode::litegui: - engine.load(QUrl(QStringLiteral("qrc:/qml/mainLite.qml"))); - - terminalConnectionTimer.setSingleShot(true); - //BST-2786 - terminalConnectionTimer.setInterval(std::chrono::milliseconds{ 25000 }); - - QObject::connect(&adapter, &SignerAdapter::ready, [&timerStarted, &terminalConnectionTimer]() - { - if (!timerStarted) { - terminalConnectionTimer.start(); - timerStarted = true; - } - }); + engine.load(QUrl(QStringLiteral("qrc:/qml/mainLite.qml"))); + terminalConnectionTimer.setSingleShot(true); + //BST-2786 + terminalConnectionTimer.setInterval(std::chrono::milliseconds{ 25000 }); - QObject::connect(&adapter, &SignerAdapter::peerConnected, [&terminalConnected] { - terminalConnected = true; - }); - QObject::connect(&adapter, &SignerAdapter::peerDisconnected, [&terminalConnected] { - terminalConnected = false; - }); - QObject::connect(&terminalConnectionTimer, &QTimer::timeout, [&terminalConnected] { - if (!terminalConnected) { - QCoreApplication::quit(); + QObject::connect(&adapter, &SignerAdapter::ready, [&timerStarted, &terminalConnectionTimer]() + { + if (!timerStarted) { + terminalConnectionTimer.start(); + timerStarted = true; } }); - break; - default: - return EXIT_FAILURE; - } + + QObject::connect(&adapter, &SignerAdapter::peerConnected, [&terminalConnected] { + terminalConnected = true; + }); + QObject::connect(&adapter, &SignerAdapter::peerDisconnected, [&terminalConnected] { + terminalConnected = false; + }); + QObject::connect(&terminalConnectionTimer, &QTimer::timeout, [&terminalConnected] { + if (!terminalConnected) { + QCoreApplication::quit(); + } + }); if (engine.rootObjects().isEmpty()) { throw std::runtime_error("Failed to load main QML file"); @@ -400,7 +388,7 @@ int main(int argc, char** argv) srand(std::time(nullptr)); - btc_ecc_start(); + CryptoECDSA::setupContext(); bs::LogManager logMgr; auto loggerStdout = logMgr.logger("settings"); diff --git a/BlockSettleTracker/CMakeLists.txt b/BlockSettleTracker/CMakeLists.txt index 3cbb43848..f276b7829 100644 --- a/BlockSettleTracker/CMakeLists.txt +++ b/BlockSettleTracker/CMakeLists.txt @@ -37,7 +37,6 @@ ENDIF() TARGET_LINK_LIBRARIES(${BLOCKSETTLE_TRACKER} ${BS_NETWORK_LIB_NAME} - ${ZMQ_LIB} ${WS_LIB} ${OPENSSL_LIBS} ${OS_SPECIFIC_LIBS_TRACKER} diff --git a/BlockSettleUILib/AddressDetailDialog.h b/BlockSettleUILib/AddressDetailDialog.h index 4e62cddc9..0ebd391f6 100644 --- a/BlockSettleUILib/AddressDetailDialog.h +++ b/BlockSettleUILib/AddressDetailDialog.h @@ -16,7 +16,7 @@ #include "Address.h" #include "AsyncClient.h" #include "CoreWallet.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace spdlog { class logger; diff --git a/BlockSettleUILib/AddressDetailsWidget.cpp b/BlockSettleUILib/AddressDetailsWidget.cpp index 65330bca2..28605d5c1 100644 --- a/BlockSettleUILib/AddressDetailsWidget.cpp +++ b/BlockSettleUILib/AddressDetailsWidget.cpp @@ -13,9 +13,7 @@ #include #include -#include "AddressVerificator.h" #include "CheckRecipSigner.h" -#include "ColoredCoinLogic.h" #include "UiUtils.h" #include "Wallets/SyncPlainWallet.h" #include "Wallets/SyncWallet.h" @@ -48,41 +46,11 @@ AddressDetailsWidget::AddressDetailsWidget(QWidget *parent) AddressDetailsWidget::~AddressDetailsWidget() = default; -// Initialize the widget and related widgets (block, address, Tx) -void AddressDetailsWidget::init(const std::shared_ptr &armory - , const std::shared_ptr &inLogger - , const std::shared_ptr &resolver - , const std::shared_ptr &walletsMgr) -{ - armory_ = armory; - logger_ = inLogger; - ccResolver_ = resolver; - walletsMgr_ = walletsMgr; - - act_ = make_unique(this); - act_->init(armory_.get()); -} - void AddressDetailsWidget::init(const std::shared_ptr &logger) { logger_ = logger; } -void AddressDetailsWidget::setBSAuthAddrs(const std::unordered_set &bsAuthAddrs) -{ - if (bsAuthAddrs.empty()) { - return; - } - bsAuthAddrs_ = bsAuthAddrs; - - addrVerify_ = std::make_shared(logger_, armory_ - , [this](const bs::Address &address, AddressVerificationState state) { - authAddrStates_[address] = state; - QMetaObject::invokeMethod(this, &AddressDetailsWidget::updateFields); - }); - addrVerify_->SetBSAddressList(bsAuthAddrs); -} - // Set the address to be queried and perform initial setup. void AddressDetailsWidget::setQueryAddr(const bs::Address &inAddrVal) { @@ -98,121 +66,16 @@ void AddressDetailsWidget::setQueryAddr(const bs::Address &inAddrVal) // Armory can't directly take an address and return all the required data. // Work around this by creating a dummy wallet, adding the explorer address, // registering the wallet, and getting the required data. - if (armory_) { - const auto walletId = CryptoPRNG::generateRandom(8).toHexStr(); - const auto dummyWallet = std::make_shared(walletId - , "temporary", "Dummy explorer wallet", nullptr, logger_); - dummyWallet->addAddress(inAddrVal, {}, false); - const auto regIds = dummyWallet->registerWallet(armory_, true); - for (const auto& regId : regIds) { - dummyWallets_[regId] = dummyWallet; - } - } - else { - ui_->addressId->setText(QString::fromStdString(currentAddrStr_)); - emit needAddressHistory(inAddrVal); - } + ui_->addressId->setText(QString::fromStdString(currentAddrStr_)); + emit needAddressHistory(inAddrVal); updateFields(); } void AddressDetailsWidget::updateFields() { - if (!ccFound_.security.empty()) { - if (!ccFound_.isGenesisAddr) { - ui_->addressId->setText(tr("%1 [Private Market: %2]") - .arg(QString::fromStdString(currentAddrStr_)) - .arg(QString::fromStdString(ccFound_.security))); - } else { - ui_->addressId->setText(tr("%1 [Private Market: Genesis Address %2]") - .arg(QString::fromStdString(currentAddrStr_)) - .arg(QString::fromStdString(ccFound_.security))); - } - return; - } - - if (bsAuthAddrs_.find(currentAddrStr_) != bsAuthAddrs_.end()) { - ui_->addressId->setText(tr("%1 [Authentication: BlockSettle funding address]") - .arg(QString::fromStdString(currentAddrStr_))); - return; - } - - if (isAuthAddr_) { - const auto authIt = authAddrStates_.find(currentAddr_); - if ((authIt != authAddrStates_.end()) && (authIt->second != AddressVerificationState::VerificationFailed)) { - ui_->addressId->setText(tr("%1 [Authentication: %2]").arg(QString::fromStdString(currentAddrStr_)) - .arg(QString::fromStdString(to_string(authIt->second)))); - } - return; - } - ui_->addressId->setText(QString::fromStdString(currentAddrStr_)); } -void AddressDetailsWidget::searchForCC() -{ - for (const auto &ccSecurity : ccResolver_->securities()) { - const auto &genesisAddr = ccResolver_->genesisAddrFor(ccSecurity); - if (currentAddr_ == genesisAddr) { - ccFound_.security = ccSecurity; - ccFound_.lotSize = ccResolver_->lotSizeFor(ccSecurity); - ccFound_.isGenesisAddr = true; - return; - } - } - - // If currentAddr_ was a valid CC address then it must been a valid CC outpoint at least once. - // Collect possible candidates here. - std::map outPoints; - for (const auto &txPair : txMap_) { - const auto &tx = txPair.second; - if (!tx || !tx->isInitialized()) { - continue; - } - - for (size_t i = 0; i < tx->getNumTxOut(); ++i) { - const auto &txOut = tx->getTxOutCopy(int(i)); - try { - const auto &addr = bs::Address::fromTxOut(txOut); - if (addr == currentAddr_) { - // Only first outputs could be CC - outPoints[tx->getThisHash()] = uint32_t(i); - break; - } - } catch (...) { - } - } - } - - for (const auto &ccSecurity : ccResolver_->securities()) { - const auto &tracker = walletsMgr_->tracker(ccSecurity); - if (!tracker) { - SPDLOG_LOGGER_WARN(logger_, "CC tracker {} is not found", ccSecurity); - continue; - } - - for (const auto &outPoint : outPoints) { - const bool isValid = tracker->isTxHashValidHistory(outPoint.first, outPoint.second); - if (isValid) { - ccFound_.tracker = tracker; - ccFound_.security = ccSecurity; - ccFound_.lotSize = ccResolver_->lotSizeFor(ccSecurity); - ccFound_.isGenesisAddr = false; - return; - } - } - } -} - -void AddressDetailsWidget::searchForAuth() -{ - if (!addrVerify_) { - return; - } - - addrVerify_->addAddress(currentAddr_); - addrVerify_->startAddressVerification(); -} - // The function that gathers all the data to place in the UI. void AddressDetailsWidget::loadTransactions() { @@ -221,9 +84,6 @@ void AddressDetailsWidget::loadTransactions() uint64_t totCount = 0; - searchForCC(); - const bool isCcAddress = !ccFound_.security.empty(); - // Go through each TXEntry object and calculate all required UI data. for (const auto &curTXEntry : txEntryHashSet_) { QTreeWidgetItem *item = new QTreeWidgetItem(tree); @@ -258,83 +118,28 @@ void AddressDetailsWidget::loadTransactions() UiUtils::displayDateTime(QDateTime::fromTime_t(curTXEntry.second.txTime))); item->setText(colTxId, // Flip Armory's TXID byte order: internal -> RPC QString::fromStdString(curTXEntry.first.toHexStr(true))); - item->setData(colConfs, Qt::DisplayRole, armory_->getConfirmationsNumber(curTXEntry.second.blockNum)); item->setText(colInputsNum, QString::number(tx->getNumTxIn())); item->setText(colOutputsNum, QString::number(tx->getNumTxOut())); item->setText(colFees, UiUtils::displayAmount(fees)); item->setText(colFeePerByte, QString::number(std::nearbyint(feePerByte))); item->setText(colTxSize, QString::number(tx->getSize())); - // isTxHashValidHistory is not absolutly accurate to detect invalid CC transactions but should be good enough - const bool isCcTx = isCcAddress && (ccFound_.isGenesisAddr || (ccFound_.tracker && ccFound_.tracker->isTxHashValidHistory(curTXEntry.second.txHash))); - - if (!isCcTx) { - item->setText(colOutputAmt, UiUtils::displayAmount(curTXEntry.second.value)); - } else { - const auto ccAmount = curTXEntry.second.value / int64_t(ccFound_.lotSize); - item->setText(colOutputAmt, tr("%1 %2").arg(QString::number(ccAmount)).arg(QString::fromStdString(ccFound_.security))); - } + item->setText(colOutputAmt, UiUtils::displayAmount(curTXEntry.second.value)); item->setTextAlignment(colOutputAmt, Qt::AlignRight); QFont font = item->font(colOutputAmt); font.setBold(true); item->setFont(colOutputAmt, font); - if (isCcAddress && !isCcTx) { - // Mark invalid CC transactions - item->setTextColor(colOutputAmt, Qt::red); - } - - // Check the total received or sent. - // Account only valid TXs for CC address. - if (!isCcAddress) { - if (curTXEntry.second.value > 0) { - totalReceived_ += curTXEntry.second.value; - } - else { - totalSpent_ -= curTXEntry.second.value; // Negative, so fake that out. - } - } else if (isCcTx) { - if (curTXEntry.second.value > 0) { - totalReceived_ += curTXEntry.second.value / int64_t(ccFound_.lotSize); - } - else { - totalSpent_ -= curTXEntry.second.value / int64_t(ccFound_.lotSize); - } - } totCount++; - // Detect if this is an auth address - if (curTXEntry.second.value == kAuthAddrValue) { - for (size_t i = 0; i < tx->getNumTxOut(); ++i) { - const auto &txOut = tx->getTxOutCopy(static_cast(i)); - try { - const auto addr = bs::Address::fromTxOut(txOut); - if (bsAuthAddrs_.find(addr.display()) != bsAuthAddrs_.end()) { - isAuthAddr_ = true; - AddressDetailsWidget::searchForAuth(); - break; - } - } catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "auth address detection failed: {}", e.what()); - } - } - } - setConfirmationColor(item); tree->addTopLevelItem(item); } - if (!isCcAddress) { - ui_->totalReceived->setText(UiUtils::displayAmount(totalReceived_)); - ui_->totalSent->setText(UiUtils::displayAmount(totalSpent_)); - ui_->balance->setText(UiUtils::displayAmount(totalReceived_ - totalSpent_)); - } else { - ui_->totalReceived->setText(QString::number(totalReceived_)); - ui_->totalSent->setText(QString::number(totalSpent_)); - ui_->balance->setText(QString::number(totalReceived_ - totalSpent_)); - } - + ui_->totalReceived->setText(UiUtils::displayAmount(totalReceived_)); + ui_->totalSent->setText(UiUtils::displayAmount(totalSpent_)); + ui_->balance->setText(UiUtils::displayAmount(totalReceived_ - totalSpent_)); emit finished(); // Set up the display for total rcv'd/spent. @@ -413,59 +218,7 @@ void AddressDetailsWidget::getTxData(const std::shared_ptrgetTXsByHash(prevTxHashSet, cbCollectPrevTXs, true); - } - }; - - // Callback to process ledger entries (pages) from the ledger delegate. Gets - // Tx entries from Armory. - const auto &cbLedger = [this, cbCollectTXs] - (ReturnMessage> entries) - { - auto result = std::make_shared>(); - try { - *result = entries.get(); - } - catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "Return data error - {}", e.what()); - return; - } - - // Process entries on main thread because this callback is called from background - QMetaObject::invokeMethod(this, [this, cbCollectTXs, result] { - std::set txHashSet; // Hashes assoc'd with a given address. - - // Get the hash and TXEntry object for each relevant Tx hash. - for (const auto &entry : *result) { - BinaryData searchHash(entry.getTxHash()); - const auto &itTX = txMap_.find(searchHash); - if (itTX == txMap_.end()) { - txHashSet.insert(searchHash); - txEntryHashSet_[searchHash] = bs::TXEntry::fromLedgerEntry(entry); - } - } - if (txHashSet.empty()) { - SPDLOG_LOGGER_INFO(logger_, "address participates in no TXs"); - cbCollectTXs({}, nullptr); - } else { - armory_->getTXsByHash(txHashSet, cbCollectTXs, true); - } - }); - }; - - const auto &cbPageCnt = [this, delegate, cbLedger] (ReturnMessage pageCnt) { - try { - uint64_t inPageCnt = pageCnt.get(); - for(uint64_t i = 0; i < inPageCnt; i++) { - delegate->getHistoryPage(uint32_t(i), cbLedger); - } - } - catch (const std::exception &e) { - SPDLOG_LOGGER_ERROR(logger_, "Return data error (getPageCount) - {}", e.what()); - } }; - delegate->getPageCount(cbPageCnt); } // Function that grabs the TX data for the address. Used in callback. @@ -477,16 +230,6 @@ void AddressDetailsWidget::refresh(const std::shared_ptr , wallet->walletId(), wallet->getUsedAddressCount()); return; } - - // Process TX data for the "first" (i.e., only) address in the wallet. - const auto &cbLedgerDelegate = [this](const std::shared_ptr &delegate) { - getTxData(delegate); - }; - const auto addr = wallet->getUsedAddressList().at(0); - if (!wallet->getLedgerDelegateForAddress(addr, cbLedgerDelegate)) { - SPDLOG_LOGGER_DEBUG(logger_, "failed to get ledger delegate for wallet ID {} - address {}" - , wallet->walletId(), addr.display()); - } } // Called when Armory has finished registering a wallet. Kicks off the function @@ -509,17 +252,11 @@ void AddressDetailsWidget::OnRefresh(std::vector ids, bool online) // Clear out all address details. void AddressDetailsWidget::clear() { - for (const auto &dummyWallet : dummyWallets_) { - dummyWallet.second->unregisterWallet(); - } totalReceived_ = 0; totalSpent_ = 0; dummyWallets_.clear(); txMap_.clear(); txEntryHashSet_.clear(); - ccFound_ = {}; - isAuthAddr_ = false; - authAddrStates_.clear(); ui_->addressId->clear(); ui_->treeAddressTransactions->clear(); @@ -566,7 +303,7 @@ void AddressDetailsWidget::onAddressHistory(const bs::Address& addr, uint32_t cu void AddressDetailsWidget::onTXDetails(const std::vector& txDet) { for (const auto& tx : txDet) { - if (txEntryHashSet_.find(tx.txHash) == txEntryHashSet_.end()) { + if (!tx.txHash.empty() && (txEntryHashSet_.find(tx.txHash) == txEntryHashSet_.end())) { return; // not our TX details } } @@ -588,44 +325,49 @@ void AddressDetailsWidget::onTXDetails(const std::vectorerror("[{}] can't find TXEntry for {}", __func__, tx.txHash.toHexStr(true)); - continue; - } - - // Populate the transaction entries. - item->setText(colDate, - UiUtils::displayDateTime(QDateTime::fromTime_t(itEntry->second.txTime))); - item->setText(colTxId, // Flip Armory's TXID byte order: internal -> RPC - QString::fromStdString(tx.txHash.toHexStr(true))); - item->setData(colConfs, Qt::DisplayRole, itEntry->second.nbConf); - item->setText(colInputsNum, QString::number(tx.tx.getNumTxIn())); - item->setText(colOutputsNum, QString::number(tx.tx.getNumTxOut())); - item->setText(colFees, UiUtils::displayAmount(fees)); - item->setText(colFeePerByte, QString::number(std::nearbyint(feePerByte))); - item->setText(colTxSize, QString::number(tx.tx.getSize())); - - item->setText(colOutputAmt, UiUtils::displayAmount(itEntry->second.value)); - item->setTextAlignment(colOutputAmt, Qt::AlignRight); - - QFont font = item->font(colOutputAmt); - font.setBold(true); - item->setFont(colOutputAmt, font); + if (!tx.txHash.empty()) { + const auto& itEntry = txEntryHashSet_.find(tx.txHash); + if (itEntry == txEntryHashSet_.end()) { + logger_->error("[{}] can't find TXEntry for {}", __func__, tx.txHash.toHexStr(true)); + continue; + } - if (!tx.isValid) { - // Mark invalid transactions - item->setTextColor(colOutputAmt, Qt::red); - } + // Populate the transaction entries. + item->setText(colDate, + UiUtils::displayDateTime(QDateTime::fromTime_t(itEntry->second.txTime))); + item->setText(colTxId, // Flip Armory's TXID byte order: internal -> RPC + QString::fromStdString(tx.txHash.toHexStr(true))); + item->setData(colConfs, Qt::DisplayRole, itEntry->second.nbConf); + item->setText(colInputsNum, QString::number(tx.tx.getNumTxIn())); + item->setText(colOutputsNum, QString::number(tx.tx.getNumTxOut())); + item->setText(colFees, UiUtils::displayAmount(fees)); + item->setText(colFeePerByte, QString::number(std::nearbyint(feePerByte))); + item->setText(colTxSize, QString::number(tx.tx.getSize())); + item->setText(colOutputAmt, UiUtils::displayAmount(itEntry->second.value)); + item->setTextAlignment(colOutputAmt, Qt::AlignRight); + + QFont font = item->font(colOutputAmt); + font.setBold(true); + item->setFont(colOutputAmt, font); + + if (!tx.isValid) { + // Mark invalid transactions + item->setTextColor(colOutputAmt, Qt::red); + } - // Check the total received or sent. - if (itEntry->second.value > 0) { - totalReceived_ += itEntry->second.value; + // Check the total received or sent. + if (itEntry->second.value > 0) { + totalReceived_ += itEntry->second.value; + } + else { + totalSpent_ -= itEntry->second.value; // Negative, so fake that out. + } + totCount++; } - else { - totalSpent_ -= itEntry->second.value; // Negative, so fake that out. + else if (!tx.comment.empty()) { + item->setTextColor(colTxId, Qt::red); + item->setText(colTxId, QString::fromStdString(tx.comment)); } - totCount++; setConfirmationColor(item); tree->addTopLevelItem(item); diff --git a/BlockSettleUILib/AddressDetailsWidget.h b/BlockSettleUILib/AddressDetailsWidget.h index 7e9e2f28f..5f4a5405d 100644 --- a/BlockSettleUILib/AddressDetailsWidget.h +++ b/BlockSettleUILib/AddressDetailsWidget.h @@ -14,7 +14,7 @@ #include "Address.h" #include "AuthAddress.h" #include "ArmoryConnection.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include #include @@ -42,14 +42,9 @@ class AddressDetailsWidget : public QWidget explicit AddressDetailsWidget(QWidget *parent = nullptr); ~AddressDetailsWidget() override; - [[deprecated]] void init(const std::shared_ptr &armory - , const std::shared_ptr &inLogger - , const std::shared_ptr & - , const std::shared_ptr &); void init(const std::shared_ptr& inLogger); void setQueryAddr(const bs::Address& inAddrVal); - void setBSAuthAddrs(const std::unordered_set &bsAuthAddrs); void clear(); void onNewBlock(unsigned int blockNum); @@ -86,8 +81,6 @@ private slots: [[deprecated]] void getTxData(const std::shared_ptr &); [[deprecated]] void refresh(const std::shared_ptr &); [[deprecated]] void loadTransactions(); - [[deprecated]] void searchForCC(); - [[deprecated]] void searchForAuth(); private: // NB: Right now, the code is slightly inefficient. There are two maps with @@ -124,15 +117,8 @@ private slots: AsyncClient::TxBatchResult txMap_; // A wallet's Tx hash / Tx map. std::map txEntryHashSet_; // A wallet's Tx hash / Tx entry map. - std::shared_ptr armory_; std::shared_ptr logger_; - std::shared_ptr ccResolver_; std::shared_ptr walletsMgr_; - CcData ccFound_; - std::shared_ptr addrVerify_; - std::map authAddrStates_; - std::unordered_set bsAuthAddrs_; - bool isAuthAddr_{false}; uint32_t topBlock_{ 0 }; std::mutex mutex_; diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index e0758f3a3..f505a9dad 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -199,7 +199,7 @@ void AddressListModel::updateWallet(const bs::sync::WalletInfo &wallet) } } -void AddressListModel::onAddresses(const std::string & +void AddressListModel::onAddresses(const std::string &/*walletId*/ , const std::vector &addrs) { if (addrs.empty()) { //TODO: check against walletId (first arg) @@ -216,8 +216,11 @@ void AddressListModel::onAddresses(const std::string & newAddrs.push_back(addr); } } + if (newAddrs.empty()) { + return; + } beginInsertRows(QModelIndex(), addressRows_.size() - , addressRows_.size() + newAddrs.size() - 1); + , addressRows_.size() + newAddrs.size() -1); for (const auto &addr : newAddrs) { const auto &itWallet = std::find_if(wallets_.cbegin(), wallets_.cend() , [walletId = addr.walletId](const bs::sync::WalletInfo &wi){ diff --git a/BlockSettleUILib/AddressListModel.h b/BlockSettleUILib/AddressListModel.h index da0c6545d..67ca01221 100644 --- a/BlockSettleUILib/AddressListModel.h +++ b/BlockSettleUILib/AddressListModel.h @@ -15,7 +15,7 @@ #include #include #include "CoreWallet.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include "ValidityFlag.h" namespace bs { diff --git a/BlockSettleUILib/CCPortfolioModel.h b/BlockSettleUILib/CCPortfolioModel.h index a625e6568..31f786eac 100644 --- a/BlockSettleUILib/CCPortfolioModel.h +++ b/BlockSettleUILib/CCPortfolioModel.h @@ -13,7 +13,7 @@ #include #include -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace bs { namespace sync { diff --git a/BlockSettleUILib/CCWidget.h b/BlockSettleUILib/CCWidget.h index d1008a8ef..b46ae1e1f 100644 --- a/BlockSettleUILib/CCWidget.h +++ b/BlockSettleUILib/CCWidget.h @@ -13,7 +13,7 @@ #include #include -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace Ui { class CCWidget; diff --git a/BlockSettleUILib/CoinControlDialog.cpp b/BlockSettleUILib/CoinControlDialog.cpp index 3b2acb570..43ba44a4d 100644 --- a/BlockSettleUILib/CoinControlDialog.cpp +++ b/BlockSettleUILib/CoinControlDialog.cpp @@ -11,7 +11,7 @@ #include "ui_CoinControlDialog.h" #include "CoinControlDialog.h" #include -#include "SelectedTransactionInputs.h" +#include "Wallets/SelectedTransactionInputs.h" CoinControlDialog::CoinControlDialog(const std::shared_ptr &inputs, bool allowAutoSel, QWidget* parent) diff --git a/BlockSettleUILib/CoinControlModel.cpp b/BlockSettleUILib/CoinControlModel.cpp index 3e2ce37ab..efa8e374f 100644 --- a/BlockSettleUILib/CoinControlModel.cpp +++ b/BlockSettleUILib/CoinControlModel.cpp @@ -15,7 +15,7 @@ #include #include "BTCNumericTypes.h" #include "BtcUtils.h" -#include "SelectedTransactionInputs.h" +#include "Wallets/SelectedTransactionInputs.h" #include "TxClasses.h" #include "UiUtils.h" #include "Wallets/SyncWallet.h" diff --git a/BlockSettleUILib/CoinControlWidget.cpp b/BlockSettleUILib/CoinControlWidget.cpp index 720d1452c..b643d0aea 100644 --- a/BlockSettleUILib/CoinControlWidget.cpp +++ b/BlockSettleUILib/CoinControlWidget.cpp @@ -13,7 +13,7 @@ #include "UiUtils.h" #include "CoinControlModel.h" -#include "SelectedTransactionInputs.h" +#include "Wallets/SelectedTransactionInputs.h" #include #include diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index f5a5f0c9b..accc858cb 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -30,10 +30,10 @@ #include "Address.h" #include "ArmoryConnection.h" #include "BSMessageBox.h" -#include "OfflineSigner.h" -#include "SelectedTransactionInputs.h" -#include "SignContainer.h" -#include "TransactionData.h" +#include "Wallets/OfflineSigner.h" +#include "Wallets/SelectedTransactionInputs.h" +#include "Wallets/SignContainer.h" +#include "Wallets/TransactionData.h" #include "UiUtils.h" #include "UsedInputsModel.h" #include "UtxoReservationManager.h" @@ -113,22 +113,9 @@ void CreateTransactionDialog::updateCreateButtonText() { if (HaveSignedImportedTransaction()) { pushButtonCreate()->setText(tr("Broadcast")); - if (signContainer_->isOffline()) { - pushButtonCreate()->setEnabled(false); - } return; } - const auto walletId = UiUtils::getSelectedWalletId(comboBoxWallets()); - - if (walletsManager_) { - auto walletPtr = walletsManager_->getHDWalletById(walletId); - if (walletPtr && !walletPtr->isHardwareWallet() && (signContainer_->isOffline() - || signContainer_->isWalletOffline(walletId))) { - pushButtonCreate()->setText(tr("Export")); - } else { - selectedWalletChanged(-1); - } - } + const auto walletId = UiUtils::getSelectedWalletId(comboBoxWallets(), comboBoxWallets()->currentIndex()); } void CreateTransactionDialog::onSignerAuthenticated() @@ -156,8 +143,8 @@ void CreateTransactionDialog::reject() return; } - if (txReq_.isValid() && signContainer_) { - signContainer_->CancelSignTx(BinaryData::fromString(txReq_.serializeState().SerializeAsString())); + if (txReq_.isValid()) { + //FIXME: cancelSignTx(txReq_.serializeState().SerializeAsString()); } } @@ -176,31 +163,18 @@ int CreateTransactionDialog::SelectWallet(const std::string& walletId, UiUtils:: auto index = UiUtils::selectWalletInCombobox(comboBoxWallets(), walletId , static_cast(type)); if (index < 0) { - if (walletsManager_) { - const auto rootWallet = walletsManager_->getHDRootForLeaf(walletId); - if (rootWallet) { - index = UiUtils::selectWalletInCombobox(comboBoxWallets(), rootWallet->walletId()); - } - } - else { - //TODO: select root wallet if needed - } + //TODO: select root wallet if needed } return index; } void CreateTransactionDialog::populateWalletsList() { - if (walletsManager_) { - int index = UiUtils::fillHDWalletsComboBox(comboBoxWallets(), walletsManager_, UiUtils::WalletsTypes::All_AllowHwLegacy); - selectedWalletChanged(index); - } - else { - emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy, "CreateTX"); - } + emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy, "CreateTX"); } -void CreateTransactionDialog::onWalletsList(const std::string &id, const std::vector& hdWallets) +void CreateTransactionDialog::onWalletsList(const std::string &id + , const std::vector& hdWallets) { if (id != "CreateTX") { return; @@ -209,13 +183,17 @@ void CreateTransactionDialog::onWalletsList(const std::string &id, const std::ve auto comboBox = comboBoxWallets(); comboBox->clear(); - const auto &addRow = [comboBox] + const auto &addRow = [comboBox, this] (const std::string& label, const std::string& walletId, UiUtils::WalletsTypes type) { + bool blocked = comboBox->blockSignals(true); int i = comboBox->count(); + logger_->debug("[CreateTransactionDialog::onWalletsList::addRow] #{}: {}", i, walletId); comboBox->addItem(QString::fromStdString(label)); comboBox->setItemData(i, QString::fromStdString(walletId), UiUtils::WalletIdRole); comboBox->setItemData(i, QVariant::fromValue(static_cast(type)), UiUtils::WalletType); + logger_->debug("[CreateTransactionDialog::onWalletsList::addRow] {} #{} get: {}", i, (void*)comboBox, comboBox->itemData(i, UiUtils::WalletIdRole).toString().toStdString()); + comboBox->blockSignals(blocked); }; hdWallets_.clear(); @@ -251,6 +229,7 @@ void CreateTransactionDialog::onWalletsList(const std::string &id, const std::ve } } comboBox->setCurrentIndex(selected); + selectedWalletChanged(selected, true); } void CreateTransactionDialog::onFeeLevels(const std::map& feeLevels) @@ -279,46 +258,12 @@ void CreateTransactionDialog::populateFeeList() comboBoxFeeSuggestions()->setCurrentIndex(0); comboBoxFeeSuggestions()->setEnabled(false); - if (walletsManager_) { - connect(this, &CreateTransactionDialog::feeLoadingCompleted - , this, &CreateTransactionDialog::onFeeSuggestionsLoaded - , Qt::QueuedConnection); - - loadFees(); - } - else { - std::vector feeLevels; - feeLevels.reserve(kFeeLevels.size()); - for (const auto& level : kFeeLevels) { - feeLevels.push_back(level.first); - } - emit needFeeLevels(feeLevels); - } -} - -void CreateTransactionDialog::loadFees() -{ - struct Result { - std::map values; - std::set levels; - }; - auto result = std::make_shared(); - - for (const auto &feeLevel : kFeeLevels) { - result->levels.insert(feeLevel.first); - } - for (const auto &feeLevel : kFeeLevels) { - const auto &cbFee = [this, result, level=feeLevel.first](float fee) { - result->levels.erase(level); - if (fee < std::numeric_limits::infinity()) { - result->values[level] = fee; - } - if (result->levels.empty()) { - emit feeLoadingCompleted(result->values); - } - }; - walletsManager_->estimatedFeePerByte(feeLevel.first, cbFee, this); + std::vector feeLevels; + feeLevels.reserve(kFeeLevels.size()); + for (const auto& level : kFeeLevels) { + feeLevels.push_back(level.first); } + emit needFeeLevels(feeLevels); } void CreateTransactionDialog::onFeeSuggestionsLoaded(const std::map &feeValues) @@ -348,50 +293,20 @@ void CreateTransactionDialog::feeSelectionChanged(int currentIndex) transactionData_->setFeePerByte(comboBoxFeeSuggestions()->itemData(currentIndex).toFloat()); } -void CreateTransactionDialog::selectedWalletChanged(int, bool resetInputs, const std::function &cbInputsReset) +void CreateTransactionDialog::selectedWalletChanged(int index, bool resetInputs, const std::function &cbInputsReset) { if (!comboBoxWallets()->count()) { pushButtonCreate()->setText(tr("No wallets")); return; } - const auto walletId = UiUtils::getSelectedWalletId(comboBoxWallets()); - if (walletsManager_) { - const auto rootWallet = walletsManager_->getHDWalletById(walletId); - if (!rootWallet) { - logger_->error("[{}] wallet with id {} not found", __func__, walletId); - return; - } - if (!rootWallet->isHardwareWallet() && (signContainer_->isWalletOffline(rootWallet->walletId()) - || !rootWallet || signContainer_->isWalletOffline(rootWallet->walletId()))) { - pushButtonCreate()->setText(tr("Export")); - } else { - pushButtonCreate()->setText(tr("Broadcast")); - } - - auto group = rootWallet->getGroup(rootWallet->getXBTGroupType()); - const bool isHardware = rootWallet->isHardwareWallet() || rootWallet->isHardwareOfflineWallet(); - bs::hd::Purpose hwPurpose; - if (isHardware) { - hwPurpose = UiUtils::getSelectedHwPurpose(comboBoxWallets()); - } - - if (transactionData_->getGroup() != group || isHardware || resetInputs) { - if (isHardware) { - transactionData_->setWallet(group->getLeaf(hwPurpose), armory_->topBlock() - , resetInputs, cbInputsReset); - } else { - transactionData_->setGroup(group, armory_->topBlock(), false - , resetInputs, cbInputsReset); - } - } - } - else { - if (walletId.empty()) { - return; - } - pushButtonCreate()->setText(tr("Broadcast")); - emit needUTXOs("CreateTX", walletId); + const auto walletId = UiUtils::getSelectedWalletId(comboBoxWallets(), index); + if (walletId.empty()) { + logger_->debug("[{}] no walletId from #{} of {}", __func__, index, (void*)comboBoxWallets()); + return; } + //emit needWalletBalances(walletId); + pushButtonCreate()->setText(tr("Broadcast")); + emit needUTXOs("CreateTX", walletId); emit walletChanged(); } @@ -446,6 +361,36 @@ void CreateTransactionDialog::onTransactionUpdated() pushButtonCreate()->setEnabled(transactionData_->IsTransactionValid()); } +void CreateTransactionDialog::onAddressBalances(const std::string& walletId + , const std::vector& balances) +{ + auto& summary = transactionData_->GetTransactionSummary(); + for (const auto& bal : balances) { + summary.availableBalance += bal.balTotal / BTCNumericTypes::BalanceDivider; + } + logger_->debug("[{}] balance {}", __func__, summary.availableBalance); + onTransactionUpdated(); +} + +void CreateTransactionDialog::getChangeAddress(AddressCb cb) +{ + if (transactionData_->GetTransactionSummary().hasChange) { + changeAddrCb_ = std::move(cb); + emit needChangeAddress(*transactionData_->getWallets().cbegin()); + return; + } + cb({}); +} + +void CreateTransactionDialog::onChangeAddress(const std::string& /*walletId*/ + , const bs::Address& addr) +{ + if (changeAddrCb_) { + changeAddrCb_(addr); + changeAddrCb_ = nullptr; + } +} + void CreateTransactionDialog::onMaxPressed() { pushButtonMax()->setEnabled(false); @@ -558,23 +503,19 @@ void CreateTransactionDialog::onTXSigned(unsigned int id, BinaryData signedTX, b } if (result == bs::error::ErrorCode::NoError) { - if (!armory_->broadcastZC(signedTX).empty()) { - if (!textEditComment()->document()->isEmpty()) { - const auto &comment = textEditComment()->document()->toPlainText().toStdString(); - transactionData_->getWallet()->setTransactionComment(signedTX, comment); - } - accept(); - return; + //TODO: broadcast signedTX + if (!textEditComment()->document()->isEmpty()) { + const auto &comment = textEditComment()->document()->toPlainText().toStdString(); + //FIXME: transactionData_->getWallet()->setTransactionComment(signedTX, comment); } - - detailedText = tr("Failed to communicate to BlockSettleDB to broadcast transaction. Maybe BlockSettleDB is offline"); + //accept(); + return; } else { detailedText = bs::error::ErrorCodeToString(result); } MessageBoxBroadcastError(detailedText, result, this).exec(); - stopBroadcasting(); } @@ -602,13 +543,12 @@ bool CreateTransactionDialog::BroadcastImportedTx() return false; } startBroadcasting(); - if (!armory_->broadcastZC(importedSignedTX_).empty()) { - if (!textEditComment()->document()->isEmpty()) { - const auto &comment = textEditComment()->document()->toPlainText().toStdString(); - transactionData_->getWallet()->setTransactionComment(importedSignedTX_, comment); - } - return true; + //TODO: broadcast importedSignedTX_ + if (!textEditComment()->document()->isEmpty()) { + const auto &comment = textEditComment()->document()->toPlainText().toStdString(); + //transactionData_->getWallet()->setTransactionComment(importedSignedTX_, comment); } + //return and accept if broadcast was successful importedSignedTX_.clear(); stopBroadcasting(); BSMessageBox(BSMessageBox::critical, tr("Transaction broadcast"), tr("Failed to broadcast imported transaction"), this).exec(); @@ -617,7 +557,8 @@ bool CreateTransactionDialog::BroadcastImportedTx() void CreateTransactionDialog::CreateTransaction(const CreateTransactionCb &cb) { - if (signContainer_ && walletsManager_) { // old code +#if 0 + if (walletsManager_) { // old code /* BSMessageBox(BSMessageBox::critical, tr("Error") , tr("Signer is invalid - unable to send transaction"), this).exec(); return;*/ @@ -692,8 +633,10 @@ void CreateTransactionDialog::CreateTransaction(const CreateTransactionCb &cb) }); return; } +#endif //0 getChangeAddress([this, cb](const bs::Address &changeAddress) { + logger_->debug("[CreateTransactionDialog::CreateTransaction] change addr: {}", changeAddress.display()); try { txReq_ = transactionData_->createTXRequest(checkBoxRBF()->checkState() == Qt::Checked, changeAddress); @@ -706,7 +649,8 @@ void CreateTransactionDialog::CreateTransaction(const CreateTransactionCb &cb) } //TODO: implement supporting TXs collection - const auto serializedUnsigned = txReq_.armorySigner_.serializeUnsignedTx().toHexStr(); + //const auto serializedUnsigned = txReq_.armorySigner_.serializeUnsignedTx().toHexStr(); + const auto serializedUnsigned = txReq_.armorySigner_.toPSBT().toHexStr(); const auto estimatedSize = txReq_.estimateTxVirtSize(); cb(true, "", serializedUnsigned, estimatedSize); } catch (const std::exception& e) { @@ -774,56 +718,14 @@ bool CreateTransactionDialog::createTransactionImpl() txReq_.txHash = txReq_.txId(); } - if (walletsManager_ && signContainer_) { - const auto hdWallet = walletsManager_->getHDWalletById(UiUtils::getSelectedWalletId(comboBoxWallets())); - if (hdWallet->isOffline() && !hdWallet->isHardwareWallet()) { - QString offlineFilePath; - QString signerOfflineDir = applicationSettings_->get(ApplicationSettings::signerOfflineDir); - - const qint64 timestamp = QDateTime::currentDateTime().toSecsSinceEpoch(); - const std::string fileName = fmt::format("{}_{}.bin", hdWallet->walletId(), timestamp); - - QString defaultFilePath = QDir(signerOfflineDir).filePath(QString::fromStdString(fileName)); - offlineFilePath = QFileDialog::getSaveFileName(this, tr("Save Offline TX as...") - , defaultFilePath, tr("TX files (*.bin);; All files (*)")); - - if (offlineFilePath.isEmpty()) { - return true; - } - - QFileInfo exportFileIndo(offlineFilePath); - QString newSignerOfflineDir = exportFileIndo.absoluteDir().path(); - if (signerOfflineDir != newSignerOfflineDir) { - applicationSettings_->set(ApplicationSettings::signerOfflineDir, newSignerOfflineDir); - } - if (exportFileIndo.suffix() != QLatin1String("bin")) { - offlineFilePath += QLatin1String(".bin"); - } - - bs::error::ErrorCode result = bs::core::wallet::ExportTxToFile(txReq_, offlineFilePath); - if (result == bs::error::ErrorCode::NoError) { - BSMessageBox(BSMessageBox::info, tr("Offline Transaction") - , tr("Request was successfully exported") - , tr("Saved to %1").arg(offlineFilePath), this).exec(); - return false; // export was success so we could close the dialog - } else { - BSMessageBox(BSMessageBox::warning, tr("Offline Transaction") - , tr("Failed to save offline Tx request") - , tr("Filename: %1").arg(offlineFilePath), this).exec(); - } - } else { - startBroadcasting(); - pendingTXSignId_ = signContainer_->signTXRequest(txReq_, SignContainer::TXSignMode::Full, true); - if (!pendingTXSignId_) { - throw std::logic_error("Signer failed to send request"); - } - } - } - else { - //TODO: add implementation for HW wallets - startBroadcasting(); - emit needSignTX("CreateTX", txReq_, true); + //TODO: add implementation for HW wallets (if needed) + startBroadcasting(); + SecureBinaryData passphrase; + if (lineEditPassphrase()) { + passphrase = SecureBinaryData::fromString(lineEditPassphrase()->text().toStdString()); } + emit needSignTX("CreateTX", txReq_, true, SignContainer::TXSignMode::Full + , passphrase); return true; } catch (const std::runtime_error &e) { @@ -845,7 +747,7 @@ bool CreateTransactionDialog::createTransactionImpl() std::vector CreateTransactionDialog::ImportTransactions() { - QString signerOfflineDir = applicationSettings_->get(ApplicationSettings::signerOfflineDir); + QString signerOfflineDir = {}; // applicationSettings_->get(ApplicationSettings::signerOfflineDir); const QString reqFile = QFileDialog::getOpenFileName(this, tr("Select Transaction file"), signerOfflineDir , tr("TX files (*.bin);; All files (*)")); @@ -854,10 +756,7 @@ std::vector CreateTransactionDialog::ImportTran } // Update latest used directory if needed - QString newSignerOfflineDir = QFileInfo(reqFile).absoluteDir().path(); - if (signerOfflineDir != newSignerOfflineDir) { - applicationSettings_->set(ApplicationSettings::signerOfflineDir, newSignerOfflineDir); - } + //QString newSignerOfflineDir = QFileInfo(reqFile).absoluteDir().path(); const auto title = tr("Transaction file"); QFile f(reqFile); @@ -883,17 +782,6 @@ std::vector CreateTransactionDialog::ImportTran return {}; } - const auto envConf = static_cast(applicationSettings_->get(ApplicationSettings::envConfiguration)); - const bool isProd = (envConf == ApplicationSettings::EnvConfiguration::Production); - const bool isTest = (envConf == ApplicationSettings::EnvConfiguration::Test); - if ((isProd || isTest) && !transactions.at(0).allowBroadcasts) { - BSMessageBox errorMessage(BSMessageBox::warning, tr("Warning"), tr("Import failure") - , tr("You are trying to import a settlement transaction into a BlockSettle Terminal. " - "Settlement transactions must be imported into a BlockSettle Signer if signed offline."), this); - errorMessage.exec(); - return {}; - } - clear(); return transactions; } diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index 5112eac1d..d1f04c705 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -24,7 +24,7 @@ #include "Bip21Types.h" #include "BSErrorCodeStrings.h" #include "CoreWallet.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "UtxoReservationToken.h" #include "ValidityFlag.h" @@ -36,7 +36,6 @@ namespace bs { } class ApplicationSettings; class ArmoryConnection; -class HeadlessContainer; class QCheckBox; class QComboBox; class QLabel; @@ -72,7 +71,8 @@ Q_OBJECT virtual void onAddressComments(const std::string& walletId , const std::map&) {} virtual void onAddressBalances(const std::string& walletId - , const std::vector&) {} + , const std::vector&); + virtual void onChangeAddress(const std::string& walletId, const bs::Address&); virtual void onWalletsList(const std::string &id, const std::vector&); void onFeeLevels(const std::map&); @@ -80,9 +80,9 @@ Q_OBJECT void onSignedTX(const std::string& id, BinaryData signedTX, bs::error::ErrorCode result); signals: - void feeLoadingCompleted(const std::map&); //deprecated void walletChanged(); void needWalletsList(UiUtils::WalletsTypes, const std::string &id); + void needChangeAddress(const std::string& walletId); void needFeeLevels(const std::vector&); void needUTXOs(const std::string &id, const std::string& walletId , bool confOnly=false, bool swOnly=false); @@ -92,7 +92,8 @@ Q_OBJECT void needAddrComments(const std::string& walletId, const std::vector&); void needWalletBalances(const std::string& walletId); void needSignTX(const std::string& id, const bs::core::wallet::TXSignRequest & - , bool keepDupRecips = false, SignContainer::TXSignMode mode = SignContainer::TXSignMode::Full); + , bool keepDupRecips = false, SignContainer::TXSignMode mode = SignContainer::TXSignMode::Full + , const SecureBinaryData& passphrase = {}); void needBroadcastZC(const std::string &id, const BinaryData &); void needSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string &comment); @@ -119,12 +120,13 @@ Q_OBJECT virtual QLabel *labelTxSize() const = 0; virtual QPushButton *pushButtonCreate() const = 0; virtual QPushButton *pushButtonCancel() const = 0; + virtual QLineEdit* lineEditPassphrase() const { return nullptr; }; virtual QLabel* feePerByteLabel() const { return nullptr; } virtual QLabel* changeLabel() const {return nullptr; } using AddressCb = std::function; - virtual void getChangeAddress(AddressCb) const = 0; + virtual void getChangeAddress(AddressCb); virtual void onTransactionUpdated(); @@ -156,18 +158,14 @@ protected slots: static bool canUseSimpleMode(const Bip21::PaymentRequestInfo& paymentInfo); private: - void loadFees(); void populateWalletsList(); void startBroadcasting(); void stopBroadcasting(); protected: - std::shared_ptr armory_; - std::shared_ptr walletsManager_; - std::shared_ptr signContainer_; std::shared_ptr transactionData_; std::shared_ptr logger_; - std::shared_ptr applicationSettings_; + //std::shared_ptr applicationSettings_; std::shared_ptr utxoReservationManager_; bs::UtxoReservationToken utxoRes_; uint32_t topBlock_; @@ -199,6 +197,7 @@ protected slots: private: bs::core::wallet::TXSignRequest txReq_; + AddressCb changeAddrCb_{ nullptr }; }; #endif // __CREATE_TRANSACTION_DIALOG_H__ diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp index a0eac416e..02c884aea 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.cpp @@ -13,14 +13,13 @@ #include "Address.h" #include "ArmoryConnection.h" -#include "BitPayRequests.h" #include "BSMessageBox.h" #include "CoinControlDialog.h" #include "CreateTransactionDialogSimple.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "SelectAddressDialog.h" -#include "SelectedTransactionInputs.h" -#include "TransactionData.h" +#include "Wallets/SelectedTransactionInputs.h" +#include "Wallets/TransactionData.h" #include "TransactionOutputsModel.h" #include "UiUtils.h" #include "UsedInputsModel.h" @@ -141,10 +140,6 @@ std::shared_ptr CreateTransactionDialogAdvanced::Create } dlg->validateAddOutputButton(); - - connect(dlg.get(), &CreateTransactionDialogAdvanced::VerifyBitPayUnsignedTx, dlg.get(), &CreateTransactionDialogAdvanced::onVerifyBitPayUnsignedTx); - connect(dlg.get(), &CreateTransactionDialogAdvanced::BitPayTxVerified, dlg.get(), &CreateTransactionDialogAdvanced::onBitPayTxVerified); - return dlg; } @@ -187,6 +182,7 @@ void CreateTransactionDialogAdvanced::setCPFPinputs(const Tx &tx, const std::sha for (size_t i = 0; i < tx.getNumTxOut(); i++) { auto out = tx.getTxOutCopy(i); const auto addr = bs::Address::fromTxOut(out); +#if 0 const auto wallet = walletsManager_->getWalletByAddress(addr); if (wallet != nullptr && wallet->walletId() == cpfpWallet->walletId()) { if (selInputs->SetUTXOSelection(tx.getThisHash(), @@ -194,6 +190,7 @@ void CreateTransactionDialogAdvanced::setCPFPinputs(const Tx &tx, const std::sha cntOutputs++; } } +#endif origFee -= out.getValue(); } @@ -230,11 +227,11 @@ void CreateTransactionDialogAdvanced::setCPFPinputs(const Tx &tx, const std::sha SetMinimumFee(originalFee_ + addedFee_, advisedFeePerByte_); onTransactionUpdated(); }; - walletsManager_->estimatedFeePerByte(2, cbFee, this); + //walletsManager_->estimatedFeePerByte(2, cbFee, this); }; SetFixedWallet(wallet->walletId(), [this, txHashSet, cbTXs] { - armory_->getTXsByHash(txHashSet, cbTXs, true); + //armory_->getTXsByHash(txHashSet, cbTXs, true); }); } @@ -274,6 +271,7 @@ void CreateTransactionDialogAdvanced::setRBFinputs(const Tx &tx) totalVal += prevOut.getValue(); const auto addr = bs::Address::fromTxOut(prevOut); +#if 0 const auto wallet = walletsManager_->getWalletByAddress(addr); if (wallet) { const auto group = walletsManager_->getGroupByWalletId(wallet->walletId()); @@ -291,6 +289,7 @@ void CreateTransactionDialogAdvanced::setRBFinputs(const Tx &tx) inputWallets.insert(wallet); utxoSelected.push_back({ txHash, txOutIdx }); } +#endif } } @@ -352,12 +351,14 @@ void CreateTransactionDialogAdvanced::setRBFinputs(const Tx &tx) TxOut out = tx.getTxOutCopy(i); const auto addr = bs::Address::fromTxOut(out); bs::XBTAmount amount{(int64_t)out.getValue()}; - +#if 0 const auto wallet = walletsManager_->getWalletByAddress(addr); if (wallet) { ownOutputs.push_back({ addr, amount , bs::hd::Path::fromString(wallet->getAddressIndex(addr)) }); - } else { + } else +#endif + { AddRecipient({ addr, amount}); } totalVal -= out.getValue(); @@ -447,8 +448,7 @@ void CreateTransactionDialogAdvanced::setRBFinputs(const Tx &tx) wallet->getRBFTxOutList(cbRBFUtxos); } }; - - armory_->getTXsByHash(txHashSet, cbTXs, true); + //armory_->getTXsByHash(txHashSet, cbTXs, true); } void CreateTransactionDialogAdvanced::onUpdateChangeWidget() @@ -1184,7 +1184,7 @@ void CreateTransactionDialogAdvanced::feeSelectionChanged(int currentIndex) setTxFees(); } -void CreateTransactionDialogAdvanced::getChangeAddress(AddressCb cb) const +void CreateTransactionDialogAdvanced::getChangeAddress(AddressCb cb) { if (transactionData_->GetTransactionSummary().hasChange) { if (changeAddressFixed_) { @@ -1193,6 +1193,7 @@ void CreateTransactionDialogAdvanced::getChangeAddress(AddressCb cb) const } else if (ui_->radioButtonNewAddrNative->isChecked() || ui_->radioButtonNewAddrNested->isChecked() || ui_->radioButtonNewAddrLegacy->isChecked()) { +#if 0 const auto group = transactionData_->getGroup(); std::shared_ptr wallet; if (group) { @@ -1204,7 +1205,6 @@ void CreateTransactionDialogAdvanced::getChangeAddress(AddressCb cb) const if (!wallet) { wallet = transactionData_->getWallet(); } - const auto &cbAddr = [this, cb = std::move(cb), wallet, handle = validityFlag_.handle()](const bs::Address &addr) { if (!handle.isValid()) { return; @@ -1213,10 +1213,12 @@ void CreateTransactionDialogAdvanced::getChangeAddress(AddressCb cb) const , addr.display()); wallet->setAddressComment(addr , bs::sync::wallet::Comment::toString(bs::sync::wallet::Comment::ChangeAddress)); - transactionData_->getWallet()->syncAddresses(); + //FIXME: transactionData_->getWallet()->syncAddresses(); cb(addr); }; wallet->getNewChangeAddress(cbAddr); +#endif //0 + CreateTransactionDialog::getChangeAddress(cb); return; } else { cb(selectedChangeAddress_); @@ -1260,12 +1262,7 @@ void CreateTransactionDialogAdvanced::onCreatePressed() , tr("Transaction error"), QString::fromStdString(errorMsg)).exec(); reject(); } - - if (!paymentInfo_.requestURL.isEmpty()) { - emit VerifyBitPayUnsignedTx(unsignedTx, virtSize); - } else { - createTransactionImpl(); - } + createTransactionImpl(); }); } @@ -1357,10 +1354,12 @@ void CreateTransactionDialogAdvanced::SetImportedTransactions(const std::vector< utxos.push_back(utxo); totalVal += prevOut.getValue(); if (!wallet) { +#if 0 const auto &addrWallet = thisPtr->walletsManager_->getWalletByAddress(addr); if (addrWallet) { wallet = addrWallet; } +#endif } } } @@ -1407,7 +1406,7 @@ void CreateTransactionDialogAdvanced::SetImportedTransactions(const std::vector< thisPtr->AddRecipients(recipients); } }; - armory_->getTXsByHash(txHashSet, cbTXs, true); + //armory_->getTXsByHash(txHashSet, cbTXs, true); } else { // unsigned TX importedSignedTX_.clear(); @@ -1420,12 +1419,14 @@ void CreateTransactionDialogAdvanced::SetImportedTransactions(const std::vector< std::string selectedWalletId; unsigned int foundWallets = 0; for (const auto &walletId : tx.walletIds) { +#if 0 if (thisPtr->walletsManager_->getWalletById(walletId)) { if (selectedWalletId.empty()) { selectedWalletId = walletId; } foundWallets++; } +#endif } ui_->textEditComment->insertPlainText(QString::fromStdString(tx.comment)); @@ -1467,7 +1468,7 @@ void CreateTransactionDialogAdvanced::SetImportedTransactions(const std::vector< } AddRecipients(recipients); - broadcasting_ = signContainer_->isOffline() || !tx.isValid() || (foundWallets < tx.walletIds.size()); + broadcasting_ = !tx.isValid() || (foundWallets < tx.walletIds.size()); } ui_->checkBoxRBF->setChecked(tx.RBF); @@ -1524,76 +1525,47 @@ void CreateTransactionDialogAdvanced::onAddressBalances(const std::string& walle void CreateTransactionDialogAdvanced::onExistingAddressSelectedForChange() { - if (walletsManager_) { - if (!transactionData_->getWallet()) { - SPDLOG_LOGGER_ERROR(logger_, "wallet not found"); - return; - } - const auto hdWallet = walletsManager_->getHDRootForLeaf(transactionData_->getWallet()->walletId()); - std::shared_ptr group; - if (hdWallet) { - group = hdWallet->getGroup(hdWallet->getXBTGroupType()); - } - - if (group) { - selChangeAddrDlg_ = new SelectAddressDialog(group, this, AddressListModel::AddressType::Internal); - } else { - selChangeAddrDlg_ = new SelectAddressDialog(walletsManager_, transactionData_->getWallet() - , this, AddressListModel::AddressType::Internal); - } - - if (selChangeAddrDlg_->exec() == QDialog::Accepted) { - selectedChangeAddress_ = selChangeAddrDlg_->getSelectedAddress(); - showExistingChangeAddress(true); - } else { - if (!selectedChangeAddress_.isValid()) { - ui_->radioButtonNewAddrNative->setChecked(true); - } - } - } - else { - selChangeAddrDlg_ = new SelectAddressDialog(this, AddressListModel::AddressType::Internal); - connect(selChangeAddrDlg_, &SelectAddressDialog::needExtAddresses, this, &CreateTransactionDialog::needExtAddresses); - connect(selChangeAddrDlg_, &SelectAddressDialog::needIntAddresses, this, &CreateTransactionDialog::needIntAddresses); - connect(selChangeAddrDlg_, &SelectAddressDialog::needUsedAddresses, this, &CreateTransactionDialog::needUsedAddresses); - connect(selChangeAddrDlg_, &SelectAddressDialog::needAddrComments, this, &CreateTransactionDialog::needAddrComments); - - std::vector wallets; - std::unordered_set balanceIds; - for (const auto& walletId : transactionData_->getWallets()) { - const auto& itHdWallet = hdWallets_.find(walletId); - if (itHdWallet != hdWallets_.end()) { - for (const auto& group : itHdWallet->second.groups) { - for (const auto& leaf : group.leaves) { - bs::sync::WalletInfo wi; - wi.format = bs::sync::WalletFormat::Plain; - wi.ids = leaf.ids; - if (leaf.ids.size() == 2) { - balanceIds.insert(leaf.ids.at(1)); - } - else { - balanceIds.insert(leaf.ids.at(0)); - } - wi.name = leaf.name; - wi.type = bs::core::wallet::Type::Bitcoin; - wi.primary = itHdWallet->second.primary; - wallets.push_back(wi); + selChangeAddrDlg_ = new SelectAddressDialog(this, AddressListModel::AddressType::Internal); + connect(selChangeAddrDlg_, &SelectAddressDialog::needExtAddresses, this, &CreateTransactionDialog::needExtAddresses); + connect(selChangeAddrDlg_, &SelectAddressDialog::needIntAddresses, this, &CreateTransactionDialog::needIntAddresses); + connect(selChangeAddrDlg_, &SelectAddressDialog::needUsedAddresses, this, &CreateTransactionDialog::needUsedAddresses); + connect(selChangeAddrDlg_, &SelectAddressDialog::needAddrComments, this, &CreateTransactionDialog::needAddrComments); + + std::vector wallets; + std::unordered_set balanceIds; + for (const auto& walletId : transactionData_->getWallets()) { + const auto& itHdWallet = hdWallets_.find(walletId); + if (itHdWallet != hdWallets_.end()) { + for (const auto& group : itHdWallet->second.groups) { + for (const auto& leaf : group.leaves) { + bs::sync::WalletInfo wi; + wi.format = bs::sync::WalletFormat::Plain; + wi.ids = leaf.ids; + if (leaf.ids.size() == 2) { + balanceIds.insert(leaf.ids.at(1)); + } + else { + balanceIds.insert(leaf.ids.at(0)); } + wi.name = leaf.name; + wi.type = bs::core::wallet::Type::Bitcoin; + wi.primary = itHdWallet->second.primary; + wallets.push_back(wi); } } } - selChangeAddrDlg_->setWallets(wallets); - for (const auto& walletId : balanceIds) { - emit needWalletBalances(walletId); - } + } + selChangeAddrDlg_->setWallets(wallets); + for (const auto& walletId : balanceIds) { + emit needWalletBalances(walletId); + } - if (selChangeAddrDlg_->exec() == QDialog::Accepted) { - selectedChangeAddress_ = selChangeAddrDlg_->getSelectedAddress(); - showExistingChangeAddress(true); - } else { - if (!selectedChangeAddress_.isValid()) { - ui_->radioButtonNewAddrNative->setChecked(true); - } + if (selChangeAddrDlg_->exec() == QDialog::Accepted) { + selectedChangeAddress_ = selChangeAddrDlg_->getSelectedAddress(); + showExistingChangeAddress(true); + } else { + if (!selectedChangeAddress_.isValid()) { + ui_->radioButtonNewAddrNative->setChecked(true); } } if (selChangeAddrDlg_) { @@ -1604,6 +1576,7 @@ void CreateTransactionDialogAdvanced::onExistingAddressSelectedForChange() void CreateTransactionDialogAdvanced::SetFixedWallet(const std::string& walletId, const std::function &cbInputsReset) { +#if 0 auto hdWallet = walletsManager_->getHDWalletById(walletId); auto walletType = UiUtils::WalletsTypes::None; if (!hdWallet) { @@ -1619,6 +1592,9 @@ void CreateTransactionDialogAdvanced::SetFixedWallet(const std::string& walletId } const int idx = hdWallet ? SelectWallet(hdWallet->walletId(), walletType) : -1; +#else + const int idx = -1; +#endif selectedWalletChanged(idx, true, cbInputsReset); ui_->comboBoxWallets->setEnabled(false); } @@ -1633,7 +1609,7 @@ void CreateTransactionDialogAdvanced::setFixedGroupInputs(const std::shared_ptr< SelectWallet(leaves.front()->walletId(), UiUtils::WalletsTypes::None); ui_->comboBoxWallets->setEnabled(false); disableInputSelection(); - transactionData_->setGroupAndInputs(group, inputs, armory_->topBlock()); + transactionData_->setGroupAndInputs(group, inputs, topBlock_); } void CreateTransactionDialogAdvanced::disableOutputsEditing() @@ -1826,7 +1802,7 @@ std::shared_ptr CreateTransactionDialogAdvanced::Switch auto simpleDialog = std::make_shared(topBlock_ , logger_, parentWidget()); - simpleDialog->SelectWallet(UiUtils::getSelectedWalletId(ui_->comboBoxWallets), + simpleDialog->SelectWallet(UiUtils::getSelectedWalletId(ui_->comboBoxWallets, ui_->comboBoxWallets->currentIndex()), UiUtils::getSelectedWalletType(ui_->comboBoxWallets)); const auto recipientIdList = transactionData_->allRecipientIds(); @@ -1855,32 +1831,6 @@ std::shared_ptr CreateTransactionDialogAdvanced::Switch return simpleDialog; } -void CreateTransactionDialogAdvanced::onVerifyBitPayUnsignedTx(const std::string& unsignedTx, uint64_t virtSize) -{ - // send request - QNetworkRequest request = BitPay::getBTCPaymentVerificationRequest(paymentInfo_.requestURL); - QNetworkReply *reply = nam_->post(request, BitPay::getBTCPaymentVerificationPayload(unsignedTx, virtSize)); - - connect(reply, &QNetworkReply::finished, this, [this, reply] { - if (reply->error() == QNetworkReply::NoError) { - emit BitPayTxVerified(true); - return; - } - - emit BitPayTxVerified(false); - }); - - connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); - connect(this, &QDialog::finished, reply, &QNetworkReply::abort); -} - -void CreateTransactionDialogAdvanced::onBitPayTxVerified(bool result) -{ - if (result) { - createTransactionImpl(); - } -} - void CreateTransactionDialogAdvanced::validateAmountLine() { setValidationStateOnAmount(isCurrentAmountValid()); diff --git a/BlockSettleUILib/CreateTransactionDialogAdvanced.h b/BlockSettleUILib/CreateTransactionDialogAdvanced.h index ae6d954f0..40fd85dba 100644 --- a/BlockSettleUILib/CreateTransactionDialogAdvanced.h +++ b/BlockSettleUILib/CreateTransactionDialogAdvanced.h @@ -98,7 +98,7 @@ Q_OBJECT virtual QLabel *changeLabel() const override; void onTransactionUpdated() override; - void getChangeAddress(AddressCb cb) const override; + void getChangeAddress(AddressCb cb) override; bool HaveSignedImportedTransaction() const override; @@ -129,12 +129,6 @@ private slots: void onOutputsClicked(const QModelIndex &index); void onSimpleDialogRequested(); void onUpdateChangeWidget(); - void onBitPayTxVerified(bool result); - void onVerifyBitPayUnsignedTx(const std::string& unsignedTx, uint64_t virtSize); - -signals: - void VerifyBitPayUnsignedTx(const std::string& unsignedTx, uint64_t virtSize); - void BitPayTxVerified(bool result); private: void clear() override; diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.cpp b/BlockSettleUILib/CreateTransactionDialogSimple.cpp index a41170659..6e45c9b43 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.cpp +++ b/BlockSettleUILib/CreateTransactionDialogSimple.cpp @@ -16,8 +16,8 @@ #include "ArmoryConnection.h" #include "BSMessageBox.h" #include "CreateTransactionDialogAdvanced.h" -#include "SignContainer.h" -#include "TransactionData.h" +#include "Wallets/SignContainer.h" +#include "Wallets/TransactionData.h" #include "UiUtils.h" #include "Wallets/SyncWalletsManager.h" #include "XbtAmountValidator.h" @@ -46,6 +46,7 @@ void CreateTransactionDialogSimple::initUI() connect(ui_->pushButtonImport, &QPushButton::clicked, this, &CreateTransactionDialogSimple::onImportPressed); connect(ui_->pushButtonShowAdvanced, &QPushButton::clicked, this, &CreateTransactionDialogSimple::showAdvanced); + connect(ui_->lineEditPassphrase, &QLineEdit::textChanged, this, &CreateTransactionDialogSimple::onPassphraseChanged); } QComboBox *CreateTransactionDialogSimple::comboBoxWallets() const @@ -153,6 +154,13 @@ void CreateTransactionDialogSimple::onXBTAmountChanged(const QString &text) transactionData_->UpdateRecipientAmount(recipientId_, value); } +void CreateTransactionDialogSimple::onPassphraseChanged(const QString& text) +{ + if (!text.isEmpty()) { + ui_->labelPassphraseHint->clear(); + } +} + void CreateTransactionDialogSimple::onMaxPressed() { transactionData_->UpdateRecipientAmount(recipientId_, {}, false); @@ -180,19 +188,13 @@ void CreateTransactionDialogSimple::showAdvanced() accept(); } -void CreateTransactionDialogSimple::getChangeAddress(AddressCb cb) const +void CreateTransactionDialogSimple::createTransaction() { - if (transactionData_->GetTransactionSummary().hasChange) { - transactionData_->getWallet()->getNewChangeAddress([cb = std::move(cb)](const bs::Address &addr) { - cb(addr); - }); + if (ui_->lineEditPassphrase->text().isEmpty()) { + ui_->lineEditPassphrase->setFocus(); + ui_->labelPassphraseHint->setText(tr("Enter wallet password")); return; } - cb({}); -} - -void CreateTransactionDialogSimple::createTransaction() -{ if (!importedSignedTX_.empty()) { if (BroadcastImportedTx()) { accept(); @@ -236,7 +238,7 @@ std::shared_ptr CreateTransactionDialogSimple::SwitchMo advancedDialog->SetImportedTransactions(offlineTransactions_); } else { // select wallet - advancedDialog->SelectWallet(UiUtils::getSelectedWalletId(ui_->comboBoxWallets), + advancedDialog->SelectWallet(UiUtils::getSelectedWalletId(ui_->comboBoxWallets, ui_->comboBoxWallets->currentIndex()), UiUtils::getSelectedWalletType(ui_->comboBoxWallets)); // set inputs and amounts @@ -275,6 +277,11 @@ QLabel* CreateTransactionDialogSimple::labelTxOutputs() const return ui_->labelTXOutputs; } +QLineEdit* CreateTransactionDialogSimple::lineEditPassphrase() const +{ + return ui_->lineEditPassphrase; +} + void CreateTransactionDialogSimple::preSetAddress(const QString& address) { ui_->lineEditAddress->setText(address); diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.h b/BlockSettleUILib/CreateTransactionDialogSimple.h index 452f16b87..8a8ae07db 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.h +++ b/BlockSettleUILib/CreateTransactionDialogSimple.h @@ -62,8 +62,7 @@ Q_OBJECT QLabel *changeLabel() const override; QLabel* labelTXAmount() const override; QLabel* labelTxOutputs() const override; - - void getChangeAddress(AddressCb cb) const override; + QLineEdit* lineEditPassphrase() const override; protected slots: void onMaxPressed() override; @@ -73,6 +72,7 @@ private slots: void showAdvanced(); void onAddressTextChanged(const QString &address); void onXBTAmountChanged(const QString& text); + void onPassphraseChanged(const QString& text); void createTransaction(); void onImportPressed(); diff --git a/BlockSettleUILib/CreateTransactionDialogSimple.ui b/BlockSettleUILib/CreateTransactionDialogSimple.ui index c72de6c20..98f303483 100644 --- a/BlockSettleUILib/CreateTransactionDialogSimple.ui +++ b/BlockSettleUILib/CreateTransactionDialogSimple.ui @@ -1,14 +1,4 @@ - CreateTransactionDialogSimple @@ -16,8 +6,8 @@ 0 0 - 715 - 381 + 824 + 452 @@ -798,6 +788,78 @@ + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + 75 + true + + + + Wallet unlock + + + + + + + + 0 + 0 + + + + Passphrase: + + + + + + + + 0 + 0 + + + + QLineEdit::Password + + + + + + + false + + + + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + diff --git a/BlockSettleUILib/ExplorerWidget.cpp b/BlockSettleUILib/ExplorerWidget.cpp index 38d13345f..730cbcd5c 100644 --- a/BlockSettleUILib/ExplorerWidget.cpp +++ b/BlockSettleUILib/ExplorerWidget.cpp @@ -10,7 +10,6 @@ */ #include "ExplorerWidget.h" #include "ui_ExplorerWidget.h" -#include "AuthAddressManager.h" #include "BSMessageBox.h" #include "TransactionDetailsWidget.h" #include "UiUtils.h" @@ -65,30 +64,6 @@ ExplorerWidget::ExplorerWidget(QWidget *parent) : ExplorerWidget::~ExplorerWidget() = default; -// Initialize the widget and related widgets (block, address, Tx). Blocks won't -// be set up for now. -void ExplorerWidget::init(const std::shared_ptr &armory - , const std::shared_ptr &inLogger - , const std::shared_ptr &walletsMgr - , const std::shared_ptr &ccFileMgr - , const std::shared_ptr &authMgr) -{ - logger_ = inLogger; - authMgr_ = authMgr; - ui_->Transaction->init(armory, inLogger, walletsMgr, ccFileMgr->getResolver()); - ui_->Address->init(armory, inLogger, ccFileMgr->getResolver(), walletsMgr); -// ui_->Block->init(armory, inLogger); - - connect(authMgr_.get(), &AuthAddressManager::gotBsAddressList, [this] { - ui_->Address->setBSAuthAddrs(authMgr_->GetBSAddresses()); - }); - - // With Armory and the logger set, we can start accepting text input. - ui_->searchBox->setReadOnly(false); - ui_->searchBox->setPlaceholderText(QString::fromStdString( - "Search for a transaction or address.")); -} - void ExplorerWidget::init(const std::shared_ptr &logger) { logger_ = logger; diff --git a/BlockSettleUILib/ExplorerWidget.h b/BlockSettleUILib/ExplorerWidget.h index 42466e46d..8175e8296 100644 --- a/BlockSettleUILib/ExplorerWidget.h +++ b/BlockSettleUILib/ExplorerWidget.h @@ -13,7 +13,7 @@ #include "TabWithShortcut.h" #include "ArmoryConnection.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include #include @@ -37,11 +37,6 @@ Q_OBJECT ExplorerWidget(QWidget *parent = nullptr); ~ExplorerWidget() override; - [[deprecated]] void init(const std::shared_ptr &armory - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); void init(const std::shared_ptr&); void shortcutActivated(ShortcutType s) override; diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index c9c938221..16ede7477 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -20,7 +20,7 @@ #include "ApplicationSettings.h" #include "AssetManager.h" #include "BSMessageBox.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 941d36a80..5f885d0c5 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -14,9 +14,9 @@ #include #include #include "BinaryData.h" -#include "SignerDefs.h" -#include "SignerUiDefs.h" -#include "QWalletInfo.h" +#include "Wallets/SignerDefs.h" +#include "Wallets/SignerUiDefs.h" +#include "Wallets/QWalletInfo.h" namespace Ui { class WalletPropertiesDialog; diff --git a/BlockSettleUILib/NewAddressDialog.cpp b/BlockSettleUILib/NewAddressDialog.cpp index 8fcb4de12..c43a6a6af 100644 --- a/BlockSettleUILib/NewAddressDialog.cpp +++ b/BlockSettleUILib/NewAddressDialog.cpp @@ -19,51 +19,6 @@ #include "Wallets/SyncWallet.h" -NewAddressDialog::NewAddressDialog(const std::shared_ptr &wallet - , QWidget* parent) - : QDialog(parent) - , ui_(new Ui::NewAddressDialog()) - , wallet_(wallet) -{ - ui_->setupUi(this); - ui_->labelWallet->setText(QString::fromStdString(wallet->name())); - - auto copyButton = ui_->buttonBox->addButton(tr("Copy to clipboard"), QDialogButtonBox::ActionRole); - connect(copyButton, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); - connect(ui_->pushButtonCopyToClipboard, &QPushButton::clicked, this, &NewAddressDialog::copyToClipboard); - - const auto closeButton = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Close); - if (closeButton) { - connect(closeButton, &QPushButton::clicked, this, &NewAddressDialog::onClose); - } - - const auto &cbAddr = [this, copyButton, closeButton](const bs::Address &addr) { - if (addr.isValid()) { - address_ = addr; - QMetaObject::invokeMethod(this, [this, copyButton, closeButton] { - closeButton->setEnabled(true); - displayAddress(); - copyButton->setEnabled(true); - }); - wallet_->syncAddresses(); - } - else { - QMetaObject::invokeMethod(this, [this] { - ui_->lineEditNewAddress->setText(tr("Invalid address")); - }); - } - }; - wallet_->getNewExtAddress(cbAddr); - - if (address_.empty()) { - copyButton->setEnabled(false); - closeButton->setEnabled(false); - } - else { - displayAddress(); - } -} - NewAddressDialog::NewAddressDialog(const bs::sync::WalletInfo &wallet , QWidget* parent) : QDialog(parent) @@ -118,7 +73,7 @@ void NewAddressDialog::onClose() { const auto comment = ui_->textEditDescription->toPlainText(); if (!comment.isEmpty()) { - wallet_->setAddressComment(address_, comment.toStdString()); + //TODO: setAddressComment(address_, comment.toStdString()); } } diff --git a/BlockSettleUILib/NewAddressDialog.h b/BlockSettleUILib/NewAddressDialog.h index 056e62a9b..4c22c873b 100644 --- a/BlockSettleUILib/NewAddressDialog.h +++ b/BlockSettleUILib/NewAddressDialog.h @@ -14,7 +14,7 @@ #include #include #include "Address.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace Ui { class NewAddressDialog; @@ -31,8 +31,6 @@ class NewAddressDialog : public QDialog Q_OBJECT public: - [[deprecated]] NewAddressDialog(const std::shared_ptr& wallet - , QWidget* parent = nullptr); NewAddressDialog(const bs::sync::WalletInfo &, QWidget* parent = nullptr); ~NewAddressDialog() override; @@ -51,7 +49,6 @@ private slots: private: std::unique_ptr ui_; - [[deprecated]] std::shared_ptr wallet_; QPushButton* copyButton_{ nullptr }; QPushButton* closeButton_{ nullptr }; std::string walletId_; diff --git a/BlockSettleUILib/OpenURIDialog.cpp b/BlockSettleUILib/OpenURIDialog.cpp index fcba2d711..461e3e08b 100644 --- a/BlockSettleUILib/OpenURIDialog.cpp +++ b/BlockSettleUILib/OpenURIDialog.cpp @@ -13,7 +13,6 @@ #include "ui_OpenURIDialog.h" #include "Address.h" -#include "BitPayRequests.h" #include "JsonTools.h" #include "UiUtils.h" @@ -93,6 +92,7 @@ void OpenURIDialog::LoadPaymentOptions() ui_->lineEditURI->setEnabled(false); // send request +#if 0 //BitPay is not supported now QNetworkRequest request = BitPay::getBTCPaymentRequest(requestInfo_.requestURL); QNetworkReply *reply = nam_->post(request, BitPay::getBTCPaymentRequestPayload()); @@ -285,6 +285,7 @@ void OpenURIDialog::LoadPaymentOptions() connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(this, &OpenURIDialog::finished, reply, &QNetworkReply::abort); +#endif //0 } bool OpenURIDialog::ParseURI() diff --git a/BlockSettleUILib/PortfolioWidget.h b/BlockSettleUILib/PortfolioWidget.h index 550cb8e47..32f42831e 100644 --- a/BlockSettleUILib/PortfolioWidget.h +++ b/BlockSettleUILib/PortfolioWidget.h @@ -16,7 +16,7 @@ #include #include "ApplicationSettings.h" #include "CommonTypes.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include "TransactionsWidgetInterface.h" namespace spdlog { diff --git a/BlockSettleUILib/SeedDialog.cpp b/BlockSettleUILib/SeedDialog.cpp new file mode 100644 index 000000000..008dceac4 --- /dev/null +++ b/BlockSettleUILib/SeedDialog.cpp @@ -0,0 +1,92 @@ +/* + +*********************************************************************************** +* Copyright (C) 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "ui_SeedDialog.h" + +#include +#include + +#include "SeedDialog.h" +#include "UiUtils.h" +#include "Wallets/SyncWallet.h" + +using namespace bs::gui::qt; + +SeedDialog::SeedDialog(const std::string& rootId + , QWidget* parent) + : QDialog(parent) + , ui_(new Ui::SeedDialog()) + , walletId_(rootId) +{ + ui_->setupUi(this); + + connect(ui_->pushButtonGenSeed, &QPushButton::clicked, this, &SeedDialog::generateSeed); + connect(ui_->lineEditSeed, &QLineEdit::editingFinished, this, &SeedDialog::onDataAvail); + connect(ui_->lineEditWalletName, &QLineEdit::editingFinished, this, &SeedDialog::onDataAvail); + connect(ui_->lineEditWalletDesc, &QLineEdit::editingFinished, this, &SeedDialog::onDataAvail); + connect(ui_->lineEditPass1, &QLineEdit::editingFinished, this, &SeedDialog::onPasswordEdited); + connect(ui_->lineEditPass2, &QLineEdit::editingFinished, this, &SeedDialog::onPasswordEdited); + + okButton_ = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Ok); + if (okButton_) { + connect(okButton_, &QPushButton::clicked, this, &SeedDialog::onClose); + } + okButton_->setEnabled(false); +} + +SeedDialog::~SeedDialog() = default; + +void SeedDialog::generateSeed() +{ + const auto& seed = CryptoPRNG::generateRandom(32).toHexStr(); + ui_->lineEditSeed->setText(QString::fromStdString(seed)); +} + +void SeedDialog::onClose() +{ + data_.xpriv = ui_->textEditXPriv->toPlainText().toStdString(); + data_.seed = SecureBinaryData::CreateFromHex(ui_->lineEditSeed->text().toStdString()); + data_.name = ui_->lineEditWalletName->text().toStdString(); + data_.description = ui_->lineEditWalletDesc->text().toStdString(); + data_.password = SecureBinaryData::fromString(ui_->lineEditPass1->text().toStdString()); +} + +void SeedDialog::onDataAvail() +{ + std::cout << "test\n"; + okButton_->setEnabled(!ui_->lineEditWalletName->text().isEmpty() && + (!ui_->lineEditSeed->text().isEmpty() || !ui_->textEditXPriv->toPlainText().isEmpty())); +} + +void bs::gui::qt::SeedDialog::onPasswordEdited() +{ + const auto& pass1 = ui_->lineEditPass1->text().toStdString(); + const auto& pass2 = ui_->lineEditPass2->text().toStdString(); + bool isValid = false; + if (!pass1.empty() && pass2.empty()) { + ui_->labelPass->setText(tr("enter same password in the second line")); + } + else if (pass1.empty() && pass2.empty()) { + ui_->labelPass->setText(tr("type password twice")); + } + else if (pass1 != pass2) { + ui_->labelPass->setText(tr("passwords don't match")); + } + else { + ui_->labelPass->clear(); + isValid = true; + } + okButton_->setEnabled(isValid); +} + +void SeedDialog::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); +} diff --git a/BlockSettleUILib/SeedDialog.h b/BlockSettleUILib/SeedDialog.h new file mode 100644 index 000000000..2213b2010 --- /dev/null +++ b/BlockSettleUILib/SeedDialog.h @@ -0,0 +1,74 @@ +/* + +*********************************************************************************** +* Copyright (C) 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef __SEED_DIALOG_H__ +#define __SEED_DIALOG_H__ + +#include +#include +#include "SecureBinaryData.h" + +namespace Ui { + class SeedDialog; +} +namespace bs { + namespace sync { + class Wallet; + } +} +class QPushButton; + +namespace bs { + namespace gui { + struct WalletSeedData + { + std::string name; + std::string description; + SecureBinaryData password; + SecureBinaryData seed; + std::string xpriv; + + bool empty() const + { + return (name.empty() || (seed.empty() && xpriv.empty())); + } + }; + + namespace qt { + class SeedDialog : public QDialog + { + Q_OBJECT + + public: + SeedDialog(const std::string& rootId, QWidget* parent = nullptr); + ~SeedDialog() override; + + WalletSeedData getData() const { return data_; } + + protected: + void showEvent(QShowEvent* event) override; + + private slots: + void onClose(); + void generateSeed(); + void onDataAvail(); + void onPasswordEdited(); + + private: + std::unique_ptr ui_; + std::string walletId_; + QPushButton* okButton_; + WalletSeedData data_; + }; + } + } +} + +#endif // __SEED_DIALOG_H__ diff --git a/BlockSettleUILib/SeedDialog.ui b/BlockSettleUILib/SeedDialog.ui new file mode 100644 index 000000000..72f9989e9 --- /dev/null +++ b/BlockSettleUILib/SeedDialog.ui @@ -0,0 +1,467 @@ + + + SeedDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 743 + 387 + + + + + 0 + 0 + + + + Generate Address + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 20 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 5 + + + + + + 0 + 0 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Wallet name: + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Description + + + + + + + + + + Wallet seed: + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 260 + 0 + + + + false + + + + + + + Generate random + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + XPriv private key: + + + Qt::PlainText + + + true + + + false + + + + + + + + 0 + 0 + + + + + 320 + 40 + + + + + 16777215 + 40 + + + + true + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + Password: + + + + 10 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + QLineEdit::Password + + + + + + + + 0 + 0 + + + + QLineEdit::Password + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + 0 + + + + true + + + + 10 + + + 5 + + + 10 + + + 5 + + + 10 + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + buttonBox + accepted() + SeedDialog + accept() + + + 488 + 412 + + + 432 + 392 + + + + + buttonBox + rejected() + SeedDialog + reject() + + + 590 + 412 + + + 570 + 393 + + + + + diff --git a/BlockSettleUILib/SelectWalletDialog.h b/BlockSettleUILib/SelectWalletDialog.h index a018315f3..c707cb817 100644 --- a/BlockSettleUILib/SelectWalletDialog.h +++ b/BlockSettleUILib/SelectWalletDialog.h @@ -13,7 +13,7 @@ #include #include -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace Ui { class SelectWalletDialog; diff --git a/BlockSettleUILib/Settings/ArmoryServersViewModel.h b/BlockSettleUILib/Settings/ArmoryServersViewModel.h index c4d27a4b4..04da7fe70 100644 --- a/BlockSettleUILib/Settings/ArmoryServersViewModel.h +++ b/BlockSettleUILib/Settings/ArmoryServersViewModel.h @@ -15,7 +15,6 @@ #include #include "AuthAddress.h" -#include "AuthAddressManager.h" #include "BinaryData.h" #include "ApplicationSettings.h" #include "ArmoryServersProvider.h" diff --git a/BlockSettleUILib/Settings/ConfigDialog.cpp b/BlockSettleUILib/Settings/ConfigDialog.cpp index 56377d1bb..a6ebba26d 100644 --- a/BlockSettleUILib/Settings/ConfigDialog.cpp +++ b/BlockSettleUILib/Settings/ConfigDialog.cpp @@ -15,7 +15,7 @@ #include "GeneralSettingsPage.h" #include "NetworkSettingsPage.h" #include "SignersProvider.h" -#include "WalletSignerContainer.h" +#include "Wallets/WalletSignerContainer.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -217,25 +217,6 @@ void ConfigDialog::decryptData(const std::shared_ptr & cb(EncryptError::NoEncryptionKey, {}); } -void ConfigDialog::getChatPrivKey(const std::shared_ptr &walletsMgr - , const std::shared_ptr &signContainer - , const ConfigDialog::EncryptCb &cb) -{ - const auto &primaryWallet = walletsMgr->getPrimaryWallet(); - if (!primaryWallet) { - cb(EncryptError::NoPrimaryWallet, {}); - return; - } - auto walletSigner = std::dynamic_pointer_cast(signContainer); - walletSigner->getChatNode(primaryWallet->walletId(), [cb](const BIP32_Node &node) { - if (node.getPrivateKey().empty()) { - cb(EncryptError::NoEncryptionKey, {}); - return; - } - cb(EncryptError::NoError, node.getPrivateKey()); - }); -} - void ConfigDialog::onDisplayDefault() { // reset only currently selected page - maybe a subject to change pages_[ui_->stackedWidget->currentIndex()]->reset(); diff --git a/BlockSettleUILib/Settings/ConfigDialog.h b/BlockSettleUILib/Settings/ConfigDialog.h index 172ec46fb..a66cff364 100644 --- a/BlockSettleUILib/Settings/ConfigDialog.h +++ b/BlockSettleUILib/Settings/ConfigDialog.h @@ -16,7 +16,7 @@ #include "ApplicationSettings.h" #include "ArmoryServersProvider.h" #include "Settings/SignersProvider.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" class ArmoryServersProvider; class SignersProvider; @@ -118,9 +118,6 @@ private slots: void setSigner(int); private: - static void getChatPrivKey(const std::shared_ptr &walletsMgr - , const std::shared_ptr &signContainer, const EncryptCb &cb); - std::unique_ptr ui_; std::shared_ptr appSettings_; std::shared_ptr armoryServersProvider_; diff --git a/BlockSettleUILib/Settings/GeneralSettingsPage.cpp b/BlockSettleUILib/Settings/GeneralSettingsPage.cpp index 050973b74..39ed38ec0 100644 --- a/BlockSettleUILib/Settings/GeneralSettingsPage.cpp +++ b/BlockSettleUILib/Settings/GeneralSettingsPage.cpp @@ -90,7 +90,7 @@ void GeneralSettingsPage::display() } if (setFirstWalletAsDefault) { - walletId = UiUtils::getSelectedWalletId(ui_->comboBox_defaultWallet); + walletId = UiUtils::getSelectedWalletId(ui_->comboBox_defaultWallet, ui_->comboBox_defaultWallet->currentIndex()); appSettings_->setDefaultWalletId(walletId); } } @@ -153,7 +153,7 @@ static inline QString logLevel(int level) void GeneralSettingsPage::apply() { - const auto walletId = UiUtils::getSelectedWalletId(ui_->comboBox_defaultWallet); + const auto walletId = UiUtils::getSelectedWalletId(ui_->comboBox_defaultWallet, ui_->comboBox_defaultWallet->currentIndex()); if (appSettings_) { appSettings_->set(ApplicationSettings::launchToTray, ui_->checkBoxLaunchToTray->isChecked()); diff --git a/BlockSettleUILib/Settings/HeadlessSettings.cpp b/BlockSettleUILib/Settings/HeadlessSettings.cpp index b5ff19fd5..4b5460a56 100644 --- a/BlockSettleUILib/Settings/HeadlessSettings.cpp +++ b/BlockSettleUILib/Settings/HeadlessSettings.cpp @@ -10,9 +10,9 @@ */ #include #include - +#include "ArmoryConfig.h" #include "BIP150_151.h" -#include "BlockDataManagerConfig.h" +#include "BTCNumericTypes.h" #include "BtcUtils.h" #include "cxxopts.hpp" #include "HeadlessSettings.h" @@ -61,7 +61,6 @@ bool HeadlessSettings::loadSettings(int argc, char **argv) std::string walletsDir; cxxopts::Options options("BlockSettle Signer", "Headless Signer process"); - std::string guiMode; options.add_options() ("h,help", "Print help") ("a,listen", "IP address to listen on" @@ -80,8 +79,6 @@ bool HeadlessSettings::loadSettings(int argc, char **argv) , cxxopts::value()->default_value("true")) ("auto_sign_spend_limit", "Spend limit expressed in XBT for auto-sign operations" , cxxopts::value(autoSignSpendLimit)) - ("g,guimode", "GUI run mode" - , cxxopts::value(guiMode)->default_value("fullgui")) ; try { @@ -130,20 +127,10 @@ bool HeadlessSettings::loadSettings(int argc, char **argv) exit(0); } - if (guiMode == "litegui") { - runMode_ = bs::signer::RunMode::litegui; - } - else if (guiMode == "fullgui") { - runMode_ = bs::signer::RunMode::fullgui; - } - else if (guiMode == "headless") { - runMode_ = bs::signer::RunMode::headless; - } - if (testNet()) { - NetworkConfig::selectNetwork(NETWORK_MODE_TESTNET); + Armory::Config::NetworkSettings::selectNetwork(Armory::Config::NETWORK_MODE_TESTNET); } else { - NetworkConfig::selectNetwork(NETWORK_MODE_MAINNET); + Armory::Config::NetworkSettings::selectNetwork(Armory::Config::NETWORK_MODE_MAINNET); } return true; } diff --git a/BlockSettleUILib/Settings/HeadlessSettings.h b/BlockSettleUILib/Settings/HeadlessSettings.h index 2e89029d4..a4bfc5325 100644 --- a/BlockSettleUILib/Settings/HeadlessSettings.h +++ b/BlockSettleUILib/Settings/HeadlessSettings.h @@ -13,7 +13,7 @@ #include #include "BtcDefinitions.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include namespace spdlog { @@ -49,8 +49,6 @@ class HeadlessSettings bool twoWaySignerAuth() const; bool offline() const; - bs::signer::RunMode runMode() const { return runMode_; } - BinaryData serverIdKey() const { return serverIdKey_; } void setServerIdKey(const BinaryData &key) { serverIdKey_ = key; } @@ -67,7 +65,6 @@ class HeadlessSettings std::string logFile_; std::string termIDKeyStr_; - bs::signer::RunMode runMode_; std::string walletsDir_; std::unique_ptr d_; BinaryData serverIdKey_; diff --git a/BlockSettleUILib/Settings/NetworkSettingsPage.cpp b/BlockSettleUILib/Settings/NetworkSettingsPage.cpp index faf4724a6..f8fe5d9c1 100644 --- a/BlockSettleUILib/Settings/NetworkSettingsPage.cpp +++ b/BlockSettleUILib/Settings/NetworkSettingsPage.cpp @@ -19,7 +19,7 @@ #include "ApplicationSettings.h" #include "ArmoryServersWidget.h" #include "WebSocketClient.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "ArmoryServersViewModel.h" #include "Settings/SignerSettings.h" #include "SignersProvider.h" diff --git a/BlockSettleUILib/Settings/SignerSettings.cpp b/BlockSettleUILib/Settings/SignerSettings.cpp index 0ad382b7b..ca1f3bf18 100644 --- a/BlockSettleUILib/Settings/SignerSettings.cpp +++ b/BlockSettleUILib/Settings/SignerSettings.cpp @@ -13,9 +13,9 @@ #include #include #include +#include "ArmoryConfig.h" #include "BIP150_151.h" #include "BtcDefinitions.h" -#include "BlockDataManagerConfig.h" #include "BtcUtils.h" #include "SignerSettings.h" #include "SystemFileUtils.h" @@ -193,18 +193,16 @@ bool SignerSettings::loadSettings(const std::shared_ptr &mainS if (!mainSettings) { return false; } - runMode_ = static_cast(mainSettings->runMode()); srvIDKey_ = mainSettings->serverIdKey().toHexStr(); signerPort_ = mainSettings->interfacePort(); d_->set_test_net(mainSettings->testNet()); if (d_->test_net()) { - NetworkConfig::selectNetwork(NETWORK_MODE_TESTNET); + Armory::Config::NetworkSettings::selectNetwork(Armory::Config::NETWORK_MODE_TESTNET); } else { - NetworkConfig::selectNetwork(NETWORK_MODE_MAINNET); + Armory::Config::NetworkSettings::selectNetwork(Armory::Config::NETWORK_MODE_MAINNET); } - return true; } diff --git a/BlockSettleUILib/Settings/SignerSettings.h b/BlockSettleUILib/Settings/SignerSettings.h index f46d3c7c9..622209b71 100644 --- a/BlockSettleUILib/Settings/SignerSettings.h +++ b/BlockSettleUILib/Settings/SignerSettings.h @@ -13,8 +13,8 @@ #include #include -#include "SignerDefs.h" -#include "SignerUiDefs.h" +#include "Wallets/SignerDefs.h" +#include "Wallets/SignerUiDefs.h" namespace Blocksettle { namespace Communication { namespace signer { class Settings; @@ -81,7 +81,6 @@ class SignerSettings : public QObject bool twoWaySignerAuth() const; QString dirDocuments() const; - bs::signer::ui::RunMode runMode() const { return runMode_; } bool closeHeadless() const { return true; } void setOffline(bool val); @@ -134,9 +133,7 @@ class SignerSettings : public QObject std::string fileName_; std::string srvIDKey_; int signerPort_{}; - bs::signer::ui::RunMode runMode_{}; std::unique_ptr d_; - }; diff --git a/BlockSettleUILib/Settings/SignerSettingsPage.cpp b/BlockSettleUILib/Settings/SignerSettingsPage.cpp index e0d64fc63..ef1c0079f 100644 --- a/BlockSettleUILib/Settings/SignerSettingsPage.cpp +++ b/BlockSettleUILib/Settings/SignerSettingsPage.cpp @@ -18,9 +18,9 @@ #include "ApplicationSettings.h" #include "BtcUtils.h" #include "BSMessageBox.h" -#include "SignContainer.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/SignContainer.h" #include "SignersManageWidget.h" -#include "HeadlessContainer.h" SignerSettingsPage::SignerSettingsPage(QWidget* parent) : SettingsPage{parent} diff --git a/BlockSettleUILib/Settings/SignersProvider.cpp b/BlockSettleUILib/Settings/SignersProvider.cpp index 9c0eaf354..5b568c717 100644 --- a/BlockSettleUILib/Settings/SignersProvider.cpp +++ b/BlockSettleUILib/Settings/SignersProvider.cpp @@ -12,7 +12,7 @@ #include #include -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "SystemFileUtils.h" #include "TransportBIP15x.h" diff --git a/BlockSettleUILib/StatusBarView.cpp b/BlockSettleUILib/StatusBarView.cpp index 6aef81a5e..a410d4d44 100644 --- a/BlockSettleUILib/StatusBarView.cpp +++ b/BlockSettleUILib/StatusBarView.cpp @@ -10,7 +10,7 @@ */ #include "StatusBarView.h" #include "AssetManager.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" diff --git a/BlockSettleUILib/StatusBarView.h b/BlockSettleUILib/StatusBarView.h index 015d75899..503a4f755 100644 --- a/BlockSettleUILib/StatusBarView.h +++ b/BlockSettleUILib/StatusBarView.h @@ -20,7 +20,7 @@ #include #include "ArmoryConnection.h" #include "CircleProgressBar.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" namespace bs { namespace sync { diff --git a/BlockSettleUILib/TransactionDetailDialog.cpp b/BlockSettleUILib/TransactionDetailDialog.cpp index 6336070c0..710728c63 100644 --- a/BlockSettleUILib/TransactionDetailDialog.cpp +++ b/BlockSettleUILib/TransactionDetailDialog.cpp @@ -184,19 +184,7 @@ void TransactionDetailDialog::addAddress(TxOut out // can't use const ref due walletName = QString::fromStdString(addressWallet->name()); } else { - bool isCCaddress = false; - if (ccLeaf_) { - if (walletsManager_->isValidCCOutpoint(ccLeaf_->shortName(), txHash, out.getIndex(), out.getValue())) { - isCCaddress = true; - } - } - if (isCCaddress) { - valueStr += ccLeaf_->displayTxValue(int64_t(out.getValue())); - walletName = QString::fromStdString(ccLeaf_->shortName()); - } - else { - valueStr += UiUtils::displayAmount(out.getValue()); - } + valueStr += UiUtils::displayAmount(out.getValue()); } QStringList items; items << addressType; diff --git a/BlockSettleUILib/TransactionDetailDialog.h b/BlockSettleUILib/TransactionDetailDialog.h index c7206f80e..bbcfb66c6 100644 --- a/BlockSettleUILib/TransactionDetailDialog.h +++ b/BlockSettleUILib/TransactionDetailDialog.h @@ -67,7 +67,6 @@ Q_OBJECT QTreeWidgetItem *itemSender_ = nullptr; QTreeWidgetItem *itemReceiver_ = nullptr; ValidityFlag validityFlag_; - std::shared_ptr ccLeaf_; }; #endif // __TRANSACTION_DETAIL_DIALOG_H__ diff --git a/BlockSettleUILib/TransactionDetailsWidget.cpp b/BlockSettleUILib/TransactionDetailsWidget.cpp index 9ec05db60..7cf9d920e 100644 --- a/BlockSettleUILib/TransactionDetailsWidget.cpp +++ b/BlockSettleUILib/TransactionDetailsWidget.cpp @@ -20,7 +20,7 @@ #include #include -using namespace ArmorySigner; +using namespace Armory::Signer; Q_DECLARE_METATYPE(Tx); @@ -55,21 +55,6 @@ TransactionDetailsWidget::TransactionDetailsWidget(QWidget *parent) : TransactionDetailsWidget::~TransactionDetailsWidget() = default; -// Initialize the widget and related widgets (block, address, Tx) -void TransactionDetailsWidget::init( - const std::shared_ptr &armory - , const std::shared_ptr &inLogger - , const std::shared_ptr &walletsMgr - , const std::shared_ptr &resolver) -{ - armoryPtr_ = armory; - logger_ = inLogger; - walletsMgr_ = walletsMgr; - ccResolver_ = resolver; - act_ = make_unique(this); - act_->init(armoryPtr_.get()); -} - void TransactionDetailsWidget::init(const std::shared_ptr &logger) { logger_ = logger; @@ -170,13 +155,18 @@ void TransactionDetailsWidget::populateTransactionWidget(const TxHash &rpcTXID void TransactionDetailsWidget::onTXDetails(const std::vector &txDet) { - if ((txDet.size() > 1) || (!txDet.empty() && (txDet[0].txHash != curTxHash_))) { + if (txDet.empty()) { + return; + } + if ((txDet.size() > 1) || (!txDet.empty() && !txDet[0].txHash.empty() && (txDet[0].txHash != curTxHash_))) { logger_->debug("[{}] not our TX details", __func__); return; // not our data } - if (!txDet.empty()) { - curTx_ = txDet[0].tx; + if ((txDet[0].txHash.empty() || !txDet[0].tx.getSize()) && !txDet[0].comment.empty()) { + ui_->tranID->setText(QString::fromStdString(txDet[0].comment)); + return; } + curTx_ = txDet[0].tx; if (!curTx_.isInitialized()) { ui_->tranID->setText(tr("Loading...")); return; @@ -372,54 +362,6 @@ void TransactionDetailsWidget::loadInputs() loadTreeOut(ui_->treeOutput); } -void TransactionDetailsWidget::updateTreeCC(QTreeWidget *tree - , const std::string &cc, uint64_t lotSize) -{ - for (int i = 0; i < tree->topLevelItemCount(); ++i) { - auto item = tree->topLevelItem(i); - const uint64_t amt = item->data(1, Qt::UserRole).toULongLong(); - if (amt && ((amt % lotSize) == 0)) { - item->setData(1, Qt::DisplayRole, QString::number(amt / lotSize)); - const auto addrWallet = item->data(2, Qt::DisplayRole).toString(); - if (addrWallet.isEmpty()) { - item->setData(2, Qt::DisplayRole, QString::fromStdString(cc)); - } - for (int j = 0; j < item->childCount(); ++j) { - auto outItem = item->child(j); - const uint64_t outAmt = outItem->data(1, Qt::UserRole).toULongLong(); - outItem->setData(1, Qt::DisplayRole, QString::number(outAmt / lotSize)); - outItem->setData(2, Qt::DisplayRole, QString::fromStdString(cc)); - } - } - } -} - -void TransactionDetailsWidget::checkTxForCC(const Tx &tx, QTreeWidget *treeWidget) -{ - for (const auto &cc : ccResolver_->securities()) { - const auto &genesisAddr = ccResolver_->genesisAddrFor(cc); - auto txChecker = std::make_shared(genesisAddr, armoryPtr_); - const auto &cbHasGA = [this, txChecker, treeWidget, cc](bool found) { - if (!found) { - return; - } - QMetaObject::invokeMethod(this, [this, treeWidget, cc] { - updateTreeCC(treeWidget, cc, ccResolver_->lotSizeFor(cc)); - }); - }; - txChecker->containsInputAddress(tx, cbHasGA, ccResolver_->lotSizeFor(cc)); - } -} - -void TransactionDetailsWidget::updateCCInputs() -{ - for (int i = 0; i < curTx_.getNumTxIn(); ++i) { - const OutPoint op = curTx_.getTxInCopy(i).getOutPoint(); - const auto &prevTx = prevTxMap_[op.getTxHash()]; - checkTxForCC(*prevTx, ui_->treeInput); - } -} - // Input widget population. void TransactionDetailsWidget::loadTreeIn(CustomTreeWidget *tree) { @@ -463,8 +405,6 @@ void TransactionDetailsWidget::loadTreeIn(CustomTreeWidget *tree) , (hashCounts[intPrevTXID] > 1) ? op.getTxOutIndex() : -1); } tree->resizeColumns(); - - updateCCInputs(); } // Output widget population. @@ -499,8 +439,6 @@ void TransactionDetailsWidget::loadTreeOut(CustomTreeWidget *tree) // add the item to the tree } tree->resizeColumns(); - - checkTxForCC(curTx_, ui_->treeOutput); } void TransactionDetailsWidget::addItem(QTreeWidget *tree, const QString &address diff --git a/BlockSettleUILib/TransactionDetailsWidget.h b/BlockSettleUILib/TransactionDetailsWidget.h index effc6214e..d50c97200 100644 --- a/BlockSettleUILib/TransactionDetailsWidget.h +++ b/BlockSettleUILib/TransactionDetailsWidget.h @@ -13,8 +13,7 @@ #include "ArmoryConnection.h" #include "BinaryData.h" -#include "CCFileManager.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" #include "TxClasses.h" #include @@ -75,10 +74,6 @@ class TransactionDetailsWidget : public QWidget explicit TransactionDetailsWidget(QWidget *parent = nullptr); ~TransactionDetailsWidget() override; - [[deprecated]] void init(const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr & - , const std::shared_ptr &); void init(const std::shared_ptr&); void populateTransactionWidget(const TxHash &rpcTXID, @@ -113,22 +108,17 @@ protected slots: void loadInputs(); void setTxGUIValues(); void clear(); - void updateCCInputs(); - void checkTxForCC(const Tx &, QTreeWidget *); - [[deprecated]] void processTxData(const Tx &tx); + void processTxData(const Tx &tx); void addItem(QTreeWidget *tree, const QString &address, const uint64_t amount , const QString &wallet, const BinaryData &txHash, const int txIndex = -1); - static void updateTreeCC(QTreeWidget *, const std::string &product, uint64_t lotSize); - private: std::unique_ptr ui_; std::shared_ptr armoryPtr_; std::shared_ptr logger_; std::shared_ptr walletsMgr_; - std::shared_ptr ccResolver_; Tx curTx_; // The Tx being analyzed in the widget. BinaryData curTxHash_; diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index 290e60d25..56e39ae74 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -21,7 +21,7 @@ #include #include #include "AsyncClient.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" namespace spdlog { class logger; diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index cd53ee621..f7e068a27 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -20,7 +20,7 @@ #include "BSMessageBox.h" #include "CreateTransactionDialogAdvanced.h" #include "PasswordDialogDataWrapper.h" -#include "TradesUtils.h" +#include "Wallets/TradesUtils.h" #include "TransactionsViewModel.h" #include "TransactionDetailDialog.h" #include "Wallets/SyncHDWallet.h" diff --git a/BlockSettleUILib/TransactionsWidgetInterface.cpp b/BlockSettleUILib/TransactionsWidgetInterface.cpp index 98ff7bf78..296805e5b 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.cpp +++ b/BlockSettleUILib/TransactionsWidgetInterface.cpp @@ -13,9 +13,9 @@ #include "ApplicationSettings.h" #include "BSMessageBox.h" #include "CreateTransactionDialogAdvanced.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "PasswordDialogDataWrapper.h" -#include "TradesUtils.h" +#include "Wallets/TradesUtils.h" #include "TransactionsViewModel.h" #include "TransactionDetailDialog.h" #include "Wallets/SyncHDWallet.h" @@ -40,9 +40,6 @@ TransactionsWidgetInterface::TransactionsWidgetInterface(QWidget *parent) qApp->clipboard()->setText(curTx_); }); - actionRevoke_ = new QAction(tr("Revoke"), this); - connect(actionRevoke_, &QAction::triggered, this, &TransactionsWidgetInterface::onRevokeSettlement); - actionRBF_ = new QAction(tr("Replace-By-Fee (RBF)"), this); connect(actionRBF_, &QAction::triggered, this, &TransactionsWidgetInterface::onCreateRBFDialog); @@ -55,132 +52,6 @@ void TransactionsWidgetInterface::init(const std::shared_ptr &lo logger_ = logger; } -void TransactionsWidgetInterface::onRevokeSettlement() -{ - auto txItem = model_->getItem(actionRevoke_->data().toModelIndex()); - if (!txItem) { - SPDLOG_LOGGER_ERROR(logger_, "item not found"); - return; - } - auto args = std::make_shared(); - - auto payoutCb = bs::tradeutils::PayoutResultCb([this, args, txItem] - (bs::tradeutils::PayoutResult result) - { - const auto ×tamp = QDateTime::currentDateTimeUtc(); - QMetaObject::invokeMethod(qApp, [this, args, txItem, timestamp, result] { - if (!result.success) { - SPDLOG_LOGGER_ERROR(logger_, "creating payout failed: {}", result.errorMsg); - BSMessageBox(BSMessageBox::critical, tr("Revoke Transaction") - , tr("Revoke failed") - , tr("failed to create pay-out TX"), this).exec(); - return; - } - - constexpr int kRevokeTimeout = 60; - const auto &settlementIdHex = args->settlementId.toHexStr(); - bs::sync::PasswordDialogData dlgData; - dlgData.setValue(bs::sync::PasswordDialogData::SettlementId, QString::fromStdString(settlementIdHex)); - dlgData.setValue(bs::sync::PasswordDialogData::Title, tr("Settlement Revoke")); - dlgData.setValue(bs::sync::PasswordDialogData::DurationLeft, kRevokeTimeout * 1000); - dlgData.setValue(bs::sync::PasswordDialogData::DurationTotal, kRevokeTimeout * 1000); - dlgData.setValue(bs::sync::PasswordDialogData::SettlementPayOutVisible, true); - - // Set timestamp that will be used by auth eid server to update timers. - dlgData.setValue(bs::sync::PasswordDialogData::DurationTimestamp, static_cast(timestamp.toSecsSinceEpoch())); - - dlgData.setValue(bs::sync::PasswordDialogData::ProductGroup, tr(bs::network::Asset::toString(bs::network::Asset::SpotXBT))); - dlgData.setValue(bs::sync::PasswordDialogData::Security, txItem->comment); - dlgData.setValue(bs::sync::PasswordDialogData::Product, "XXX"); - dlgData.setValue(bs::sync::PasswordDialogData::Side, tr("Revoke")); - dlgData.setValue(bs::sync::PasswordDialogData::Price, tr("N/A")); - - dlgData.setValue(bs::sync::PasswordDialogData::Market, "XBT"); - dlgData.setValue(bs::sync::PasswordDialogData::SettlementId, settlementIdHex); - dlgData.setValue(bs::sync::PasswordDialogData::RequesterAuthAddressVerified, true); - dlgData.setValue(bs::sync::PasswordDialogData::ResponderAuthAddressVerified, true); - dlgData.setValue(bs::sync::PasswordDialogData::SigningAllowed, true); - - dlgData.setValue(bs::sync::PasswordDialogData::ExpandTxInfo, - appSettings_->get(ApplicationSettings::AdvancedTxDialogByDefault).toBool()); - - const auto amount = args->amount.GetValueBitcoin(); - SPDLOG_LOGGER_DEBUG(logger_, "revoke fee={}, qty={} ({}), recv addr: {}" - ", settl addr: {}", result.signRequest.fee, amount - , amount * BTCNumericTypes::BalanceDivider, args->recvAddr.display() - , result.settlementAddr.display()); - - //note: signRequest should be a shared_ptr - auto signObj = result.signRequest; - const auto reqId = signContainer_->signSettlementPayoutTXRequest(signObj - , { args->settlementId, args->cpAuthPubKey, false }, dlgData); - if (reqId) { - revokeIds_.insert(reqId); - } - else { - BSMessageBox(BSMessageBox::critical, tr("Revoke Transaction") - , tr("Revoke failed") - , tr("failed to send TX request to signer"), this).exec(); - } - }); - }); - - const auto &cbSettlAuth = [this, args, payoutCb](const bs::Address &ownAuthAddr) - { - if (ownAuthAddr.empty()) { - QMetaObject::invokeMethod(this, [this] { - BSMessageBox(BSMessageBox::critical, tr("Revoke Transaction") - , tr("Failed to create revoke transaction") - , tr("auth wallet doesn't contain settlement metadata"), this).exec(); - }); - return; - } - args->ourAuthAddress = ownAuthAddr; - bs::tradeutils::createPayout(*args, payoutCb, false); - }; - const auto &cbSettlCP = [this, args, cbSettlAuth] - (const BinaryData &settlementId, const BinaryData &dealerAuthKey) - { - if (settlementId.empty() || dealerAuthKey.empty()) { - cbSettlAuth({}); - return; - } - args->settlementId = settlementId; - args->cpAuthPubKey = dealerAuthKey; - signContainer_->getSettlAuthAddr(walletsManager_->getPrimaryWallet()->walletId() - , settlementId, cbSettlAuth); - }; - const auto &cbDialog = [this, args, cbSettlCP] - (const TransactionPtr &txItem) - { - for (int i = 0; i < txItem->tx.getNumTxOut(); ++i) { - const auto &txOut = txItem->tx.getTxOutCopy(i); - const auto &addr = bs::Address::fromTxOut(txOut); - if (addr.getType() == AddressEntryType_P2WSH) { - args->amount = bs::XBTAmount{ (int64_t)txOut.getValue() }; - break; - } - } - - const auto &xbtWallet = walletsManager_->getDefaultWallet(); - args->walletsMgr = walletsManager_; - args->armory = armory_; - args->signContainer = signContainer_; - args->payinTxId = txItem->txEntry.txHash; - args->outputXbtWallet = xbtWallet; - - xbtWallet->getNewExtAddress([this, args, cbSettlCP](const bs::Address &addr) { - args->recvAddr = addr; - signContainer_->getSettlCP(walletsManager_->getPrimaryWallet()->walletId() - , args->payinTxId, cbSettlCP); - }); - }; - - if (txItem->initialized) { - cbDialog(txItem); - } -} - void TransactionsWidgetInterface::onCreateRBFDialog() { const auto &txItem = model_->getItem(actionRBF_->data().toModelIndex()); diff --git a/BlockSettleUILib/TransactionsWidgetInterface.h b/BlockSettleUILib/TransactionsWidgetInterface.h index d1b6a4f47..54b13c77a 100644 --- a/BlockSettleUILib/TransactionsWidgetInterface.h +++ b/BlockSettleUILib/TransactionsWidgetInterface.h @@ -41,7 +41,6 @@ class TransactionsWidgetInterface : public TabWithShortcut { void init(const std::shared_ptr &); protected slots: - void onRevokeSettlement(); void onCreateRBFDialog(); void onCreateCPFPDialog(); void onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode, std::string error); diff --git a/BlockSettleUILib/UiUtils.cpp b/BlockSettleUILib/UiUtils.cpp index 13e4a0ded..a2d8948ce 100644 --- a/BlockSettleUILib/UiUtils.cpp +++ b/BlockSettleUILib/UiUtils.cpp @@ -11,14 +11,12 @@ #include "UiUtils.h" #include "ApplicationSettings.h" -#include "AuthAddressManager.h" #include "BinaryData.h" -#include "BlockDataManagerConfig.h" #include "BTCNumericTypes.h" #include "BtcUtils.h" #include "CoinControlModel.h" #include "CustomControls/QtAwesome.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "TxClasses.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" @@ -356,23 +354,6 @@ int UiUtils::fillHDWalletsComboBox(QComboBox* comboBox return selected; } -void UiUtils::fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox, const std::shared_ptr &authAddressManager) -{ - comboBox->clear(); - const auto &addrList = authAddressManager->GetSubmittedAddressList(); - if (!addrList.empty()) { - const auto b = comboBox->blockSignals(true); - for (const auto &address : addrList) { - comboBox->addItem(QString::fromStdString(address.display())); - } - comboBox->blockSignals(b); - QMetaObject::invokeMethod(comboBox, "setCurrentIndex", Q_ARG(int, authAddressManager->getDefaultIndex())); - comboBox->setEnabled(true); - } else { - comboBox->setEnabled(false); - } -} - void UiUtils::fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox , const std::vector& addrs, int defaultIdx) { @@ -667,9 +648,10 @@ QString UiUtils::displayShortAddress(const QString &addr, const uint maxLength) } -std::string UiUtils::getSelectedWalletId(QComboBox* comboBox) +std::string UiUtils::getSelectedWalletId(QComboBox* comboBox, int index) { - return comboBox->currentData(WalletIdRole).toString().toStdString(); + return comboBox->itemData(index, WalletIdRole).toString().toStdString(); + //return comboBox->currentData().toString().toStdString(); } UiUtils::WalletsTypes UiUtils::getSelectedWalletType(QComboBox* comboBox) diff --git a/BlockSettleUILib/UiUtils.h b/BlockSettleUILib/UiUtils.h index dadedfb0c..86f5c3026 100644 --- a/BlockSettleUILib/UiUtils.h +++ b/BlockSettleUILib/UiUtils.h @@ -22,7 +22,7 @@ #include "ApplicationSettings.h" #include "CommonTypes.h" #include "HDPath.h" -#include "SignerDefs.h" +#include "Wallets/SignerDefs.h" QT_BEGIN_NAMESPACE class QAbstractItemModel; @@ -37,9 +37,7 @@ namespace bs { } } } -class AuthAddressManager; class BinaryData; -class PyBlockDataManager; class QComboBox; class QDateTime; class QPixmap; @@ -139,7 +137,6 @@ namespace UiUtils , int walletTypes); int fillHDWalletsComboBox(QComboBox* comboBox, const std::vector&, int walletTypes); - [[deprecated]] void fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox, const std::shared_ptr& authAddressManager); void fillAuthAddressesComboBoxWithSubmitted(QComboBox* comboBox , const std::vector &, int defaultIdx = 0); @@ -149,7 +146,7 @@ namespace UiUtils void fillRecvAddressesComboBoxHDWallet(QComboBox* comboBox, const std::vector& wallet); int selectWalletInCombobox(QComboBox* comboBox, const std::string& walletId, WalletsTypes type = WalletsTypes::None); - std::string getSelectedWalletId(QComboBox* comboBox); + std::string getSelectedWalletId(QComboBox* comboBox, int index); WalletsTypes getSelectedWalletType(QComboBox* comboBox); bs::hd::Purpose getSelectedHwPurpose(QComboBox* comboBox); diff --git a/BlockSettleUILib/UtxoReservationManager.cpp b/BlockSettleUILib/UtxoReservationManager.cpp index c10b4a23b..d52b873b6 100644 --- a/BlockSettleUILib/UtxoReservationManager.cpp +++ b/BlockSettleUILib/UtxoReservationManager.cpp @@ -18,8 +18,8 @@ #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" #include "Wallets/SyncHDLeaf.h" -#include "TradesUtils.h" -#include "ArmoryObject.h" +#include "Wallets/TradesUtils.h" +#include "Wallets/ArmoryObject.h" #include "WalletUtils.h" using namespace bs; @@ -232,36 +232,6 @@ void bs::UTXOReservationManager::getBestXbtUtxoSet(const HDWalletId& walletId, b getBestXbtFromUtxos(walletUtxos, quantity, std::move(cb), checkPbFeeFloor, checkAmount); } -BTCNumericTypes::balance_type bs::UTXOReservationManager::getAvailableCCUtxoSum(const CCProductName& CCProduct) const -{ - const auto& ccWallet = walletsManager_ ? walletsManager_->getCCWallet(CCProduct) : nullptr; - if (!ccWallet) { - return {}; - } - - BTCNumericTypes::satoshi_type sum = 0; - auto const availableUtxos = getAvailableCCUTXOs(ccWallet->walletId()); - for (const auto &utxo : availableUtxos) { - sum += utxo.getValue(); - } - - return ccWallet->getTxBalance(sum); -} - -std::vector bs::UTXOReservationManager::getAvailableCCUTXOs(const CCWalletId& walletId) const -{ - std::vector utxos; - auto const availableUtxos = availableCCUTXOs_.find(walletId); - if (availableUtxos == availableCCUTXOs_.end()) { - return {}; - } - - utxos = availableUtxos->second; - std::vector filtered; - UtxoReservation::instance()->filter(utxos, filtered); - return utxos; -} - bs::FixedXbtInputs UTXOReservationManager::convertUtxoToFixedInput(const HDWalletId& walletId, const std::vector& utxos) { FixedXbtInputs fixedXbtInputs; @@ -471,7 +441,7 @@ void bs::UTXOReservationManager::getBestXbtFromUtxos(const std::vector &in QMetaObject::invokeMethod(mgr, [mgr, quantity, inputUtxo , fee, utxos = std::move(utxos), cb = std::move(cbCopy), checkPbFeeFloor, checkAmount]() mutable { - float feePerByte = ArmoryConnection::toFeePerByte(fee); + float feePerByte = BTCNumericTypes::toFeePerByte(fee); if (checkPbFeeFloor) { feePerByte = std::max(mgr->feeRatePb(), feePerByte); } diff --git a/BlockSettleUILib/UtxoReservationManager.h b/BlockSettleUILib/UtxoReservationManager.h index 07de3dd56..97689bc74 100644 --- a/BlockSettleUILib/UtxoReservationManager.h +++ b/BlockSettleUILib/UtxoReservationManager.h @@ -94,10 +94,6 @@ namespace bs { void getBestXbtUtxoSet(const HDWalletId& walletId, bs::hd::Purpose purpose, BTCNumericTypes::satoshi_type quantity, std::function&&)>&& cb, bool checkPbFeeFloor, CheckAmount checkAmount); - // CC specific implementation - BTCNumericTypes::balance_type getAvailableCCUtxoSum(const CCProductName& CCProduct) const; - std::vector getAvailableCCUTXOs(const CCWalletId& walletId) const; - // Mutual functions FixedXbtInputs convertUtxoToFixedInput(const HDWalletId& walletId, const std::vector& utxos); FixedXbtInputs convertUtxoToPartialFixedInput(const HDWalletId& walletId, const std::vector& utxos); diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index 6b87aabb7..cf1de945f 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -14,7 +14,7 @@ #include #include -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "UiUtils.h" #include "ValidityFlag.h" #include "Wallets/SyncHDWallet.h" @@ -517,11 +517,6 @@ Qt::ItemFlags WalletsViewModel::flags(const QModelIndex &index) const return flags; } -std::shared_ptr WalletsViewModel::getAuthWallet() const -{ - return walletsManager_->getAuthWallet(); -} - static WalletNode::Type getHDWalletType(const std::shared_ptr &hdWallet , const std::shared_ptr &walletsMgr) { diff --git a/BlockSettleUILib/WalletsViewModel.h b/BlockSettleUILib/WalletsViewModel.h index f8df80c73..97fc92112 100644 --- a/BlockSettleUILib/WalletsViewModel.h +++ b/BlockSettleUILib/WalletsViewModel.h @@ -17,8 +17,8 @@ #include #include #include -#include "QWalletInfo.h" -#include "SignerDefs.h" +#include "Wallets/QWalletInfo.h" +#include "Wallets/SignerDefs.h" namespace bs { @@ -108,7 +108,6 @@ Q_OBJECT std::string selectedWallet() const { return selectedWalletId_; } bool showRegularWallets() const { return showRegularWallets_; } - std::shared_ptr getAuthWallet() const; [[deprecated]] void LoadWallets(bool keepSelection = false); void onHDWallet(const bs::sync::WalletInfo &); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 649e52c00..1ba841999 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -27,11 +27,11 @@ #include "ApplicationSettings.h" #include "AssetManager.h" #include "BSMessageBox.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "NewAddressDialog.h" #include "NewWalletDialog.h" #include "SelectWalletDialog.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "WalletsViewModel.h" #include "WalletWarningDialog.h" #include "Wallets/SyncHDWallet.h" @@ -39,7 +39,7 @@ #include "TreeViewWithEnterKey.h" #include "ManageEncryption/RootWalletPropertiesDialog.h" -#include "SignerUiDefs.h" +#include "Wallets/SignerUiDefs.h" class AddressSortFilterModel : public QSortFilterProxyModel { @@ -161,9 +161,6 @@ WalletsWidget::WalletsWidget(QWidget* parent) actEditComment_ = new QAction(tr("&Edit Comment")); connect(actEditComment_, &QAction::triggered, this, &WalletsWidget::onEditAddrComment); - actRevokeSettl_ = new QAction(tr("&Revoke Settlement")); - connect(actRevokeSettl_, &QAction::triggered, this, &WalletsWidget::onRevokeSettlement); - // actDeleteWallet_ = new QAction(tr("&Delete Permanently")); // connect(actDeleteWallet_, &QAction::triggered, this, &WalletsWidget::onDeleteWallet); @@ -221,7 +218,7 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) ui_->treeViewWallets->setExpandsOnDoubleClick(false); // show the column as per BST-1520 //ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnID)); - if (walletsManager_ && signingContainer_) { + if (walletsManager_) { walletsModel_->LoadWallets(); } @@ -554,20 +551,7 @@ void WalletsWidget::onAddressContextMenu(const QPoint &p) } contextMenu->addAction(actEditComment_); - const auto &cbAddrBalance = [this, p, contextMenu](std::vector balances) - { - if (/*(curWallet_ == walletsManager_->getSettlementWallet()) &&*/ walletsManager_->getAuthWallet() - /*&& (curWallet_->getAddrTxN(curAddress_) == 1)*/ && balances[0]) { - contextMenu->addAction(actRevokeSettl_); - } - emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); - }; - - auto balanceVec = curWallet_->getAddrBalance(curAddress_); - if (balanceVec.size() == 0) - emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); - else - cbAddrBalance(balanceVec); + emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); return; } @@ -585,15 +569,6 @@ void WalletsWidget::onAddressContextMenu(const QPoint &p) } contextMenu->addAction(actEditComment_); - const auto &cbAddrBalance = [this, p, contextMenu](std::vector balances) - { - if (/*walletsManager_->getAuthWallet() && balances[0]*/false) { //FIXME - contextMenu->addAction(actRevokeSettl_); - } - emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); - }; - - //TODO: get address balance and add revoke action if needed contextMenu ->exec(ui_->treeViewAddresses->mapToGlobal(p)); } @@ -739,76 +714,38 @@ void WalletsWidget::onWalletBalanceChanged(std::string walletId) void WalletsWidget::onNewWallet() { emit newWalletCreationRequest(); - if (signingContainer_) { - if (!signingContainer_->isOffline()) { - NewWalletDialog newWalletDialog(false, appSettings_, this); - - switch (newWalletDialog.exec()) { - case NewWalletDialog::CreateNew: - CreateNewWallet(); - break; - case NewWalletDialog::ImportExisting: - ImportNewWallet(); - break; - case NewWalletDialog::ImportHw: - ImportHwWallet(); - break; - case NewWalletDialog::Cancel: - break; - } - } else { - ImportNewWallet(); - } - } - else { - NewWalletDialog newWalletDialog(false, this); - switch (newWalletDialog.exec()) { - case NewWalletDialog::CreateNew: - CreateNewWallet(); - break; - case NewWalletDialog::ImportExisting: - ImportNewWallet(); - break; - case NewWalletDialog::ImportHw: - ImportHwWallet(); - break; - case NewWalletDialog::Cancel: - break; - default: - showError(tr("Unknown new wallet choice")); - break; - } + NewWalletDialog newWalletDialog(false, this); + switch (newWalletDialog.exec()) { + case NewWalletDialog::CreateNew: + CreateNewWallet(); + break; + case NewWalletDialog::ImportExisting: + ImportNewWallet(); + break; + case NewWalletDialog::ImportHw: + ImportHwWallet(); + break; + case NewWalletDialog::Cancel: + break; + default: + showError(tr("Unknown new wallet choice")); + break; } } void WalletsWidget::CreateNewWallet() { - if (signingContainer_) { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::CreateWallet); - } - else { - emit needWalletDialog(bs::signer::ui::GeneralDialogType::CreateWallet); - } + emit needWalletDialog(bs::signer::ui::GeneralDialogType::CreateWallet); } void WalletsWidget::ImportNewWallet() { - if (signingContainer_) { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ImportWallet); - } - else { - emit needWalletDialog(bs::signer::ui::GeneralDialogType::ImportWallet); - } + emit needWalletDialog(bs::signer::ui::GeneralDialogType::ImportWallet); } void WalletsWidget::ImportHwWallet() { - if (signingContainer_) { - signingContainer_->customDialogRequest(bs::signer::ui::GeneralDialogType::ImportHwWallet); - } - else { - emit needWalletDialog(bs::signer::ui::GeneralDialogType::ImportHwWallet); - } + emit needWalletDialog(bs::signer::ui::GeneralDialogType::ImportHwWallet); } void WalletsWidget::shortcutActivated(ShortcutType s) @@ -832,9 +769,9 @@ void WalletsWidget::shortcutActivated(ShortcutType s) void WalletsWidget::onFilterSettingsChanged() { auto filterSettings = getUIFilterSettings(); - - appSettings_->set(ApplicationSettings::WalletFiltering, filterSettings); - + if (appSettings_) { + appSettings_->set(ApplicationSettings::WalletFiltering, filterSettings); + } updateAddressFilters(filterSettings); } @@ -919,11 +856,6 @@ void WalletsWidget::onEditAddrComment() } } -void WalletsWidget::onRevokeSettlement() -{ - BSMessageBox(BSMessageBox::info, tr("Settlement Revoke"), tr("Doesn't work currently"), this).exec(); -} - void WalletsWidget::onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode result) { if (!revokeReqId_ || (revokeReqId_ != id)) { diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index d4f908e95..ef1c77995 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -16,8 +16,8 @@ #include #include #include "Address.h" -#include "SignerDefs.h" -#include "SignerUiDefs.h" +#include "Wallets/SignerDefs.h" +#include "Wallets/SignerUiDefs.h" #include "TabWithShortcut.h" #include "BSErrorCode.h" #include "BSErrorCodeStrings.h" @@ -134,7 +134,6 @@ private slots: //void onWalletContextMenu(const QPoint &); void onCopyAddress(); void onEditAddrComment(); - void onRevokeSettlement(); void onTXSigned(unsigned int id, BinaryData signedTX, bs::error::ErrorCode result); //void onDeleteWallet(); void onFilterSettingsChanged(); @@ -152,7 +151,6 @@ private slots: std::shared_ptr logger_; std::shared_ptr walletsManager_; - std::shared_ptr signingContainer_; std::shared_ptr appSettings_; std::shared_ptr connectionManager_; std::shared_ptr assetManager_; @@ -165,7 +163,6 @@ private slots: NewAddressDialog* newAddrDlg_{ nullptr }; QAction * actCopyAddr_ = nullptr; QAction * actEditComment_ = nullptr; - QAction * actRevokeSettl_ = nullptr; //QAction * actDeleteWallet_ = nullptr; bs::Address curAddress_; [[deprecated]] std::shared_ptr curWallet_; diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f075c2fe..d4a578be5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,12 @@ IF (NOT CMAKE_BUILD_TYPE) endif() option(BSTERMINAL_SHARED_LIBS "Build shared libraries" OFF) +option(BUILD_WALLETS "Build wallets code" ON) +option(BUILD_TEST_TOOLS "Build test tools" ON) add_definitions(-DSTATIC_BUILD) add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG) +add_definitions(-DBUILD_WALLETS) IF(CMAKE_BUILD_TYPE STREQUAL "Debug") IF(BSTERMINAL_SHARED_LIBS) @@ -201,43 +204,6 @@ IF(NOT WIN32) ADD_DEFINITIONS(-Wno-multichar -Wextra -Wall -Wformat=2) ENDIF(NOT WIN32) -#setup zeromq -SET(ZEROMQ_ROOT ${THIRD_PARTY_COMMON_DIR}/ZeroMQ) -SET(ZEROMQ_INCLUDE_DIR ${ZEROMQ_ROOT}/include) -SET(ZEROMQ_LIB_DIR ${ZEROMQ_ROOT}/lib) - -INCLUDE_DIRECTORIES( ${ZEROMQ_INCLUDE_DIR} ) - -SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${ZEROMQ_ROOT}/lib) -IF( WIN32 ) - IF(CMAKE_BUILD_TYPE STREQUAL "Debug") - SET( ZMQ_LIB_NAME "libzmq-v142-mt-gd-4_3_3" ) - ELSE() - SET( ZMQ_LIB_NAME "libzmq-v142-mt-4_3_3" ) - ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") -ELSE () - IF(BSTERMINAL_SHARED_LIBS) - SET( ZMQ_LIB_NAME "libzmq.so" "zmq" ) - ELSE() - SET( ZMQ_LIB_NAME "libzmq.a" "zmq" ) - ENDIF() -ENDIF( WIN32 ) -FIND_LIBRARY(ZMQ_LIB NAMES ${ZMQ_LIB_NAME} NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) - -IF( NOT ZMQ_LIB ) - IF( WIN32 ) - IF(CMAKE_BUILD_TYPE STREQUAL "Debug") - SET( ZMQ_LIB_NAME "libzmq-v141-mt-gd-4_3_3" ) - ELSE() - SET( ZMQ_LIB_NAME "libzmq-v141-mt-4_3_3" ) - ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug") - ENDIF( WIN32 ) - FIND_LIBRARY(ZMQ_LIB NAMES ${ZMQ_LIB_NAME} NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) - IF( NOT ZMQ_LIB) - MESSAGE(FATAL_ERROR "Could not find ZMQ lib") - ENDIF( NOT ZMQ_LIB) -ENDIF( NOT ZMQ_LIB) - # OpenSSL libs SET(OPENSSL_ROOT_DIR ${THIRD_PARTY_COMMON_DIR}/OpenSSL) SET(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${OPENSSL_ROOT_DIR}/lib) @@ -345,9 +311,9 @@ SET(LIBBTC_LIB_DIR ${LIBBTC_PACKAGE_ROOT}/lib) SET(LIBBTC_INCLUDE_DIR ${LIBBTC_PACKAGE_ROOT}/include) IF (WIN32) - SET(LIBBTC_LIB_NAME libbtc) + SET(LIBBTC_LIB_NAME btc) ELSE(WIN32) - SET(LIBBTC_LIB_NAME liblibbtc.a) + SET(LIBBTC_LIB_NAME libbtc.a) ENDIF(WIN32) FIND_LIBRARY( LIBBTC_LIB NAMES ${LIBBTC_LIB_NAME} PATHS ${LIBBTC_LIB_DIR} NO_DEFAULT_PATH ) @@ -811,11 +777,6 @@ IF(BSTERMINAL_SHARED_LIBS) COMMAND ${QT5_WINDEPLOYQT_EXECUTABLE} --no-opengl-sw --compiler-runtime --no-angle --qmldir ${CMAKE_SOURCE_DIR} ${EXECUTABLE_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) add_dependencies(${COPY_SHARED_LIBS_NAME} ${SIGNER_APP_NAME} ${BLOCKSETTLE_APP_NAME}) - # libzmq - STRING(REPLACE ".lib" ".dll" ZMQ_LIB_DLL ${ZMQ_LIB}) - add_custom_command(TARGET ${COPY_SHARED_LIBS_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $<$:${ZMQ_LIB_DLL}> $<$>:${ZMQ_LIB_DLL}> ${LIBRARY_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) - # botan STRING(REPLACE ".lib" ".dll" BOTAN_LIB_DLL ${BOTAN_LIB}) add_custom_command(TARGET ${COPY_SHARED_LIBS_NAME} POST_BUILD @@ -857,11 +818,6 @@ ELSE() # Static version IF(WIN32) set(COPY_SHARED_LIBS_NAME CopySharedLibs) add_custom_target(${COPY_SHARED_LIBS_NAME} ALL) - - # libzmq - STRING(REPLACE ".lib" ".dll" ZMQ_LIB_DLL ${ZMQ_LIB}) - add_custom_command(TARGET ${COPY_SHARED_LIBS_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $<$:${ZMQ_LIB_DLL}> $<$>:${ZMQ_LIB_DLL}> ${LIBRARY_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}) ENDIF() ENDIF() diff --git a/Core/ApiJson.cpp b/Core/ApiJson.cpp index df9f6bd13..97bfd9096 100644 --- a/Core/ApiJson.cpp +++ b/Core/ApiJson.cpp @@ -390,7 +390,6 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) } switch (msg.data_case()) { case WalletsMessage::kWalletLoaded: - case WalletsMessage::kAuthWallet: case WalletsMessage::kHdWallet: sendReplyToClient(0, msg, env.sender); break; @@ -401,7 +400,6 @@ bool ApiJsonAdapter::processWallets(const Envelope &env) case WalletsMessage::kTxDetailsResponse: case WalletsMessage::kWalletsListResponse: case WalletsMessage::kUtxos: - case WalletsMessage::kAuthKey: case WalletsMessage::kReservedUtxos: if (hasRequest(env.responseId())) { sendReplyToClient(env.responseId(), msg, env.sender); diff --git a/Core/ApiJson.h b/Core/ApiJson.h index a25477e66..46bbad45e 100644 --- a/Core/ApiJson.h +++ b/Core/ApiJson.h @@ -17,7 +17,7 @@ #include "ApiAdapter.h" #include "ServerConnection.h" #include "ServerConnectionListener.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "WsConnection.h" namespace BlockSettle { diff --git a/Core/AssetsAdapter.cpp b/Core/AssetsAdapter.cpp index a26d742b0..f633376cf 100644 --- a/Core/AssetsAdapter.cpp +++ b/Core/AssetsAdapter.cpp @@ -37,8 +37,6 @@ bool AssetsAdapter::process(const bs::message::Envelope &env) switch (msg.data_case()) { case SettingsMessage::kGetResponse: return processGetSettings(msg.get_response()); - case SettingsMessage::kBootstrap: - return processBootstrap(msg.bootstrap()); default: break; } } @@ -143,76 +141,16 @@ void AssetsAdapter::onSecuritiesChanged() { } -void AssetsAdapter::onCCSecurityDef(const bs::network::CCSecurityDef& sd) -{ - AssetsMessage msg; - auto msgCC = msg.mutable_cc_definition(); - msgCC->set_security_id(sd.securityId); - msgCC->set_product(sd.product); - msgCC->set_genesis_address(sd.genesisAddr.display()); - msgCC->set_lot_size(sd.nbSatoshis); - pushBroadcast(user_, msg.SerializeAsString()); -} - -void AssetsAdapter::onLoaded() -{ - logger_->debug("[AssetsAdapter::onLoaded]"); - AdministrativeMessage admMsg; - admMsg.set_component_loading(user_->value()); - pushBroadcast(UserTerminal::create(TerminalUsers::System) - , admMsg.SerializeAsString()); - - AssetsMessage msg; - msg.mutable_loaded(); - pushBroadcast(user_, msg.SerializeAsString()); -} - bool AssetsAdapter::processGetSettings(const SettingsMessage_SettingsResponse& response) { for (const auto& setting : response.responses()) { switch (setting.request().index()) { - case SetIdx_BlockSettleSignAddress: - onBSSignAddress(setting.s()); - break; default: break; } } return true; } -void AssetsAdapter::onBSSignAddress(const std::string& address) -{ - ccFileMgr_ = std::make_unique(logger_, this, address); - - SettingsMessage msgSet; - auto msgReq = msgSet.mutable_get_bootstrap(); - pushRequest(user_, UserTerminal::create(TerminalUsers::Settings) - , msgSet.SerializeAsString()); -} - -bool AssetsAdapter::processBootstrap(const SettingsMessage_BootstrapData& response) -{ - if (!ccFileMgr_) { - logger_->debug("[{}] CC file manager is not ready, yet", __func__); - return false; - } - logger_->debug("[{}]", __func__); - std::vector ccDefs; - ccDefs.reserve(response.cc_definitions_size()); - for (const auto& ccDef : response.cc_definitions()) { - try { - ccDefs.push_back({ ccDef.security_id(), ccDef.product() - , bs::Address::fromAddressString(ccDef.genesis_address()) - , ccDef.lot_size() }); - } - catch (const std::exception& e) { - logger_->error("[{}] failed to decode CC definition: {}", __func__, e.what()); - } - } - ccFileMgr_->SetLoadedDefinitions(ccDefs); - return true; -} - bool AssetsAdapter::onMatchingLogin(const MatchingMessage_LoggedIn&) { MatchingMessage msg; diff --git a/Core/AssetsAdapter.h b/Core/AssetsAdapter.h index 78096551f..f67e397d2 100644 --- a/Core/AssetsAdapter.h +++ b/Core/AssetsAdapter.h @@ -13,7 +13,6 @@ #include "Message/Adapter.h" #include "AssetManager.h" -#include "CCFileManager.h" namespace spdlog { class logger; @@ -28,7 +27,7 @@ namespace BlockSettle { } class AssetsAdapter : public bs::message::Adapter - , public AssetCallbackTarget, public CCCallbackTarget + , public AssetCallbackTarget { public: AssetsAdapter(const std::shared_ptr &); @@ -52,14 +51,8 @@ class AssetsAdapter : public bs::message::Adapter void onTotalChanged() override; void onSecuritiesChanged() override; - //CC callbacks override - void onCCSecurityDef(const bs::network::CCSecurityDef& sd) override; - void onLoaded() override; - //internal processing bool processGetSettings(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); - void onBSSignAddress(const std::string&); - bool processBootstrap(const BlockSettle::Terminal::SettingsMessage_BootstrapData&); bool onMatchingLogin(const BlockSettle::Terminal::MatchingMessage_LoggedIn&); bool processSubmittedAuth(const BlockSettle::Terminal::MatchingMessage_SubmittedAuthAddresses&); bool processSubmitAuth(const std::string&); @@ -69,7 +62,6 @@ class AssetsAdapter : public bs::message::Adapter std::shared_ptr logger_; std::shared_ptr user_; std::unique_ptr assetMgr_; - std::unique_ptr ccFileMgr_; }; diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index 2a35c6679..a83dbe696 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -28,62 +28,6 @@ using namespace BlockSettle::Terminal; using namespace bs::message; -class OnChainPlug : public OnChainExternalPlug -{ -public: - explicit OnChainPlug(const std::shared_ptr& queue) - : OnChainExternalPlug(queue) {} - - bool tryProcess(const bs::message::Envelope& env) override - { - if (env.sender->isSystem()) { - AdministrativeMessage msg; - if (!msg.ParseFromString(env.message)) { - return false; - } - if (msg.data_case() == AdministrativeMessage::kStart) { - parent_->onStart(); - } - return true; - } - else if (env.sender->value() == bs::message::TerminalUsers::Settings) { - SettingsMessage msg; - if (!msg.ParseFromString(env.message)) { - return true; - } - switch (msg.data_case()) { - case SettingsMessage::kBootstrap: - return processBootstrap(msg.bootstrap()); - default: break; - } - return true; - } - return false; - } - - void sendAuthValidationListRequest() override - { - SettingsMessage msg; - msg.mutable_get_bootstrap(); - auto env = Envelope::makeRequest(user_, UserTerminal::create(TerminalUsers::Settings) - , msg.SerializeAsString()); - queue_->pushFill(env); - } - -private: - bool processBootstrap(const SettingsMessage_BootstrapData& response) - { - std::vector bsAddresses; - bsAddresses.reserve(response.auth_validations_size()); - for (const auto& bsAddr : response.auth_validations()) { - bsAddresses.push_back(bsAddr); - } - parent_->onAuthValidationAddresses(bsAddresses); - return true; - } -}; - - SettingsAdapter::SettingsAdapter(const std::shared_ptr &settings , const QStringList &appArgs) : appSettings_(settings) @@ -376,11 +320,6 @@ bool SettingsAdapter::processApiClientsList(const bs::message::Envelope& env) return pushResponse(user_, env, msg.SerializeAsString()); } -std::shared_ptr SettingsAdapter::createOnChainPlug() const -{ - return std::make_shared(queue_); -} - bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env , const SettingsMessage_SettingsRequest &request) { diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index 8d96c5166..ca976ade5 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -14,7 +14,7 @@ #include #include #include -#include "Adapters/OnChainTrackerAdapter.h" +#include "Message/Adapter.h" #include "TerminalMessage.h" namespace spdlog { @@ -68,8 +68,6 @@ class SettingsAdapter : public bs::message::Adapter std::shared_ptr logManager() const { return logMgr_; } - std::shared_ptr createOnChainPlug() const; - private: bool processGetRequest(const bs::message::Envelope & , const BlockSettle::Terminal::SettingsMessage_SettingsRequest &); diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index ac2894d40..717876e60 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -11,9 +11,9 @@ #include "SignerAdapter.h" #include #include "Adapters/SignerClient.h" -#include "ConnectionManager.h" -#include "HeadlessContainer.h" -#include "ProtobufHeadlessUtils.h" +#include "CoreWalletsManager.h" +#include "Wallets/InprocSigner.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "TerminalMessage.h" #include "common.pb.h" @@ -39,7 +39,15 @@ std::unique_ptr SignerAdapter::createClient() const bool SignerAdapter::process(const bs::message::Envelope &env) { - if (env.sender->value() == TerminalUsers::Settings) { + if (env.isRequest()) { + SignerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse own msg #{}", __func__, env.foreignId()); + return true; + } + return processOwnRequest(env, msg); + } + else if (env.sender->value() == TerminalUsers::Settings) { SettingsMessage msg; if (!msg.ParseFromString(env.message)) { logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); @@ -48,23 +56,9 @@ bool SignerAdapter::process(const bs::message::Envelope &env) switch (msg.data_case()) { case SettingsMessage::kSignerResponse: return processSignerSettings(msg.signer_response()); - } - } - else if (env.receiver->value() == TerminalUsers::Signer) { - SignerMessage msg; - if (!msg.ParseFromString(env.message)) { - logger_->error("[{}] failed to parse own msg #{}", __func__, env.foreignId()); - return true; - } - if (env.isRequest()) { - return processOwnRequest(env, msg); - } - else { - switch (msg.data_case()) { - case SignerMessage::kNewKeyResponse: - return processNewKeyResponse(msg.new_key_response()); - default: break; - } + case SettingsMessage::kNewKeyResponse: + return processNewKeyResponse(msg.new_key_response()); + default: break; } } return true; @@ -123,8 +117,6 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processSyncAddrComment(request.sync_addr_comment()); case SignerMessage::kSyncTxComment: return processSyncTxComment(request.sync_tx_comment()); - case SignerMessage::kSetSettlId: - return processSetSettlId(env, request.set_settl_id()); case SignerMessage::kGetRootPubkey: return processGetRootPubKey(env, request.get_root_pubkey()); case SignerMessage::kDelHdRoot: @@ -133,22 +125,15 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processDelHdLeaf(request.del_hd_leaf()); case SignerMessage::kSignTxRequest: return processSignTx(env, request.sign_tx_request()); - case SignerMessage::kSetUserId: - return processSetUserId(request.set_user_id().user_id() - , request.set_user_id().wallet_id()); - case SignerMessage::kCreateSettlWallet: - return processCreateSettlWallet(env, request.create_settl_wallet()); - case SignerMessage::kGetSettlPayinAddr: - return processGetPayinAddress(env, request.get_settl_payin_addr()); case SignerMessage::kResolvePubSpenders: return processResolvePubSpenders(env , bs::signer::pbTxRequestToCore(request.resolve_pub_spenders())); - case SignerMessage::kSignSettlementTx: - return processSignSettlementTx(env, request.sign_settlement_tx()); case SignerMessage::kAutoSign: return processAutoSignRequest(env, request.auto_sign()); case SignerMessage::kDialogRequest: return processDialogRequest(env, request.dialog_request()); + case SignerMessage::kCreateWallet: + return processCreateWallet(env, request.create_wallet()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -156,124 +141,29 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return true; } -std::shared_ptr SignerAdapter::makeRemoteSigner( - const BlockSettle::Terminal::SettingsMessage_SignerServer &response) -{ - const auto &netType = static_cast(response.network_type()); - const auto &connMgr = std::make_shared(logger_); - const auto &cbOurNewKey = [this](const std::string &oldKey, const std::string &newKey - , const std::string &srvAddrPort - , const std::shared_ptr> &newKeyProm) - { - connFuture_ = newKeyProm; - connKey_ = newKey; - - SignerMessage msg; - auto msgReq = msg.mutable_new_key_request(); - msgReq->set_old_key(oldKey); - msgReq->set_new_key(newKey); - msgReq->set_server_id(srvAddrPort); - pushBroadcast(user_, msg.SerializeAsString(), true); - }; - - auto remoteSigner = std::make_shared(logger_ - , QString::fromStdString(response.host()), QString::fromStdString(response.port()) - , netType, connMgr, this, SignContainer::OpMode::Remote, false - , response.remote_keys_dir(), response.remote_keys_file(), cbOurNewKey); - - bs::network::BIP15xPeers peers; - for (const auto &clientKey : response.client_keys()) { - try { - const BinaryData signerKey = BinaryData::CreateFromHex(clientKey.value()); - peers.push_back(bs::network::BIP15xPeer(clientKey.key(), signerKey)); - } catch (const std::exception &e) { - logger_->warn("[{}] invalid signer key: {}", __func__, e.what()); - } - } - remoteSigner->updatePeerKeys(peers); - return remoteSigner; -} - bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &response) { curServerId_ = response.id(); - if (response.is_local()) { - QLatin1String localSignerHost("127.0.0.1"); - QString localSignerPort; - const auto &netType = static_cast(response.network_type()); - - for (int attempts = 0; attempts < 10; ++attempts) { - // https://tools.ietf.org/html/rfc6335 - // the Dynamic Ports, also known as the Private or Ephemeral Ports, - // from 49152-65535 (never assigned) - auto port = 49152 + rand() % 16000; - - auto portToTest = QString::number(port); - - if (!SignerConnectionExists(localSignerHost, portToTest)) { - localSignerPort = portToTest; - break; - } else { - logger_->debug("[SignerAdapter::processSignerSettings] attempt #{}:" - " port {} used", port); - } - } - - if (localSignerPort.isEmpty()) { - logger_->error("[SignerAdapter::processSignerSettings] failed to find not busy port"); - SignerMessage msg; - auto msgError = msg.mutable_state(); - msgError->set_code((int)SignContainer::SocketFailed); - msgError->set_text("failed to bind local port"); - return pushBroadcast(user_, msg.SerializeAsString(), true); + netType_ = static_cast(response.network_type()); + walletsDir_ = response.home_dir(); + walletsMgr_ = std::make_shared(logger_); + signer_ = std::make_shared(walletsMgr_, logger_, this + , walletsDir_, netType_, [this](const std::string& walletId) + -> std::unique_ptr { + const auto& hdWallet = walletsMgr_->getHDWalletById(walletId); + if (!hdWallet) { + return nullptr; } - - const auto &connMgr = std::make_shared(logger_); - const bool startLocalSignerProcess = true; - signer_ = std::make_shared(logger_ - , QString::fromStdString(response.home_dir()), netType, localSignerPort - , connMgr, this, startLocalSignerProcess, "", "" - , response.auto_sign_spend_limit()); - signer_->Start(); - return sendComponentLoading(); - } - else { - signer_ = makeRemoteSigner(response); - signer_->Start(); - return sendComponentLoading(); - } - return true; -} - - -void SignerAdapter::connError(SignContainer::ConnectionError errCode, const QString &errMsg) -{ - SignerMessage msg; - auto msgErr = msg.mutable_state(); - msgErr->set_code((int)errCode); - msgErr->set_text(errMsg.toStdString()); - pushBroadcast(user_, msg.SerializeAsString(), true); -} - -void SignerAdapter::connTorn() -{ - SignerMessage msg; - auto msgState = msg.mutable_state(); - msgState->set_code((int)SignContainer::ConnectionError::SignerGoesOffline); - msgState->set_text("disconnected"); - pushBroadcast(user_, msg.SerializeAsString(), true); -} - -void SignerAdapter::authLeafAdded(const std::string &walletId) -{ - SignerMessage msg; - msg.set_auth_leaf_added(walletId); - pushBroadcast(user_, msg.SerializeAsString(), true); + return std::make_unique(hdWallet, passphrase_); + }); + logger_->info("[{}] loading wallets from {}", __func__, walletsDir_); + signer_->Start(); + walletsChanged(); + return sendComponentLoading(); } void SignerAdapter::walletsChanged() { - logger_->debug("[{}]", __func__); SignerMessage msg; msg.mutable_wallets_list_updated(); pushBroadcast(user_, msg.SerializeAsString(), true); @@ -329,13 +219,6 @@ void SignerAdapter::autoSignStateChanged(bs::error::ErrorCode code autoSignRequests_.erase(itAS); } -void SignerAdapter::windowIsVisible(bool flag) -{ - SignerMessage msg; - msg.set_window_visible_changed(flag); - pushBroadcast(user_, msg.SerializeAsString(), true); -} - bool SignerAdapter::sendComponentLoading() { static const auto &adminUser = UserTerminal::create(TerminalUsers::System); @@ -584,55 +467,6 @@ bool SignerAdapter::processSyncTxComment(const SignerMessage_SyncTxComment &requ return true; } -bool SignerAdapter::processSetSettlId(const bs::message::Envelope &env - , const SignerMessage_SetSettlementId &request) -{ - requests_.put(env.foreignId(), env.sender); - const auto &cb = [this, msgId=env.foreignId()](bool result) - { - auto sender = requests_.take(msgId); - if (!sender) { - return; - } - SignerMessage msg; - msg.set_settl_id_set(result); - pushResponse(user_, sender, msg.SerializeAsString(), msgId); - }; - signer_->setSettlementID(request.wallet_id() - , BinaryData::fromString(request.settlement_id()), cb); - return true; -} - -bool SignerAdapter::processSignSettlementTx(const bs::message::Envelope& env - , const SignerMessage_SignSettlementTx& request) -{ - const auto& signCb = [this, env, settlementId=request.settlement_id()] - (bs::error::ErrorCode result, const BinaryData& signedTx) - { - SignerMessage msg; - auto msgResp = msg.mutable_sign_tx_response(); - msgResp->set_id(settlementId); - msgResp->set_error_code((int)result); - msgResp->set_signed_tx(signedTx.toBinStr()); - pushResponse(user_, env, msg.SerializeAsString()); - }; - - auto txReq = bs::signer::pbTxRequestToCore(request.tx_request(), logger_); - const bs::sync::PasswordDialogData dlgData(request.details()); - if (request.contra_auth_pubkey().empty()) { - signer_->signSettlementTXRequest(txReq, dlgData - , SignContainer::TXSignMode::Full, false, signCb); - } - else { - txReq.txHash = txReq.txId(); - const bs::core::wallet::SettlementData sd{ - BinaryData::fromString(request.settlement_id()), - BinaryData::fromString(request.contra_auth_pubkey()), request.own_key_first() }; - signer_->signSettlementPayoutTXRequest(txReq, sd, dlgData, signCb); - } - return true; -} - bool SignerAdapter::processGetRootPubKey(const bs::message::Envelope &env , const std::string &walletId) { @@ -672,6 +506,7 @@ bool SignerAdapter::processSignTx(const bs::message::Envelope& env const auto& cbSigned = [this, env, id=request.id()] (BinaryData signedTX, bs::error::ErrorCode result, const std::string& errorReason) { + passphrase_.clear(); SignerMessage msg; auto msgResp = msg.mutable_sign_tx_response(); msgResp->set_id(id); @@ -681,55 +516,13 @@ bool SignerAdapter::processSignTx(const bs::message::Envelope& env pushResponse(user_, env, msg.SerializeAsString()); }; const auto& txReq = bs::signer::pbTxRequestToCore(request.tx_request(), logger_); + passphrase_ = SecureBinaryData::fromString(request.passphrase()); signer_->signTXRequest(txReq, cbSigned , static_cast(request.sign_mode()) , request.keep_dup_recips()); return true; } -bool SignerAdapter::processSetUserId(const std::string& userId, const std::string& walletId) -{ - return (signer_->setUserId(BinaryData::fromString(userId), walletId) != 0); -} - -bool SignerAdapter::processCreateSettlWallet(const bs::message::Envelope& env - , const std::string& addrStr) -{ - bs::Address authAddr; - try { - authAddr = bs::Address::fromAddressString(addrStr); - } - catch (const std::exception& e) { - logger_->error("[{}] invalid auth address {}: {}", __func__, addrStr, e.what()); - return true; - } - const auto& cb = [this, env](const SecureBinaryData& authPubKey) - { - SignerMessage msg; - msg.set_auth_pubkey(authPubKey.toBinStr()); - pushResponse(user_, env, msg.SerializeAsString()); - }; - signer_->createSettlementWallet(authAddr, cb); - return true; -} - -bool SignerAdapter::processGetPayinAddress(const bs::message::Envelope& env - , const SignerMessage_GetSettlPayinAddr& request) -{ - const auto& cbAddr = [this, env](bool success, const bs::Address& settlAddr) - { - SignerMessage msg; - auto msgResp = msg.mutable_payin_address(); - msgResp->set_success(success); - msgResp->set_address(settlAddr.display()); - pushResponse(user_, env, msg.SerializeAsString()); - }; - bs::core::wallet::SettlementData settlData{ BinaryData::fromString(request.settlement_id()) - , BinaryData::fromString(request.contra_auth_pubkey()), request.own_key_first() }; - signer_->getSettlementPayinAddress(request.wallet_id(), settlData, cbAddr); - return true; -} - bool SignerAdapter::processResolvePubSpenders(const bs::message::Envelope& env , const bs::core::wallet::TXSignRequest& txReq) { @@ -769,3 +562,31 @@ bool SignerAdapter::processDialogRequest(const bs::message::Envelope& } return (signer_->customDialogRequest(dlgType, data) != 0); } + +bool SignerAdapter::processCreateWallet(const bs::message::Envelope& env + , const SignerMessage_CreateWalletRequest& w) +{ + bs::wallet::PasswordData pwdData; + pwdData.password = SecureBinaryData::fromString(w.password()); + pwdData.metaData = { bs::wallet::EncryptionType::Password }; + //FIXME: pwdData.controlPassword = controlPassword(); + + SignerMessage msg; + auto msgResp = msg.mutable_created_wallet(); + try { + const auto& seed = w.xpriv_key().empty() ? bs::core::wallet::Seed(SecureBinaryData::fromString(w.seed()), netType_) + : bs::core::wallet::Seed::fromXpriv(SecureBinaryData::fromString(w.xpriv_key()), netType_); + + const auto& wallet = walletsMgr_->createWallet(w.name(), w.description(), seed + , walletsDir_, pwdData, w.primary()); + msgResp->set_wallet_id(wallet->walletId()); + walletsChanged(); + logger_->debug("[{}] wallet {} created", __func__, wallet->walletId()); + } + catch (const std::exception& e) { + logger_->error("[{}] failed to create wallet: {}", __func__, e.what()); + msgResp->set_error_msg(e.what()); + } + pushResponse(user_, env, msg.SerializeAsString()); + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 770b248e9..01c3c2355 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -11,18 +11,25 @@ #ifndef SIGNER_ADAPTER_H #define SIGNER_ADAPTER_H +#include "BtcDefinitions.h" #include "FutureValue.h" -#include "HeadlessContainer.h" +#include "Wallets/HeadlessContainer.h" #include "Message/Adapter.h" #include "ThreadSafeContainers.h" namespace spdlog { class logger; } +namespace bs { + namespace core { + class WalletsManager; + } +} namespace BlockSettle { namespace Common { class SignerMessage; class SignerMessage_AutoSign; + class SignerMessage_CreateWalletRequest; class SignerMessage_DialogRequest; class SignerMessage_ExtendAddrChain; class SignerMessage_GetSettlPayinAddr; @@ -62,23 +69,17 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget void start(); // HCT overrides - void connError(SignContainer::ConnectionError, const QString &) override; - void connTorn() override; - void authLeafAdded(const std::string &walletId) override; void walletsChanged() override; void onReady() override; void walletsReady() override; void newWalletPrompt() override; void autoSignStateChanged(bs::error::ErrorCode , const std::string& walletId) override; - void windowIsVisible(bool) override; bool processOwnRequest(const bs::message::Envelope & , const BlockSettle::Common::SignerMessage &); bool processSignerSettings(const BlockSettle::Terminal::SettingsMessage_SignerServer &); bool processNewKeyResponse(bool); - std::shared_ptr makeRemoteSigner( - const BlockSettle::Terminal::SettingsMessage_SignerServer &); bool sendComponentLoading(); bool processStartWalletSync(const bs::message::Envelope &); @@ -92,30 +93,27 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget bool processSyncHdWallet(const bs::message::Envelope &, const std::string &walletId); bool processSyncAddrComment(const BlockSettle::Common::SignerMessage_SyncAddressComment &); bool processSyncTxComment(const BlockSettle::Common::SignerMessage_SyncTxComment &); - bool processSetSettlId(const bs::message::Envelope & - , const BlockSettle::Common::SignerMessage_SetSettlementId &); - bool processSignSettlementTx(const bs::message::Envelope& - , const BlockSettle::Common::SignerMessage_SignSettlementTx&); bool processGetRootPubKey(const bs::message::Envelope &, const std::string &walletId); bool processDelHdRoot(const std::string &walletId); bool processDelHdLeaf(const std::string &walletId); bool processSignTx(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_SignTxRequest&); - bool processSetUserId(const std::string& userId, const std::string& walletId); - bool processCreateSettlWallet(const bs::message::Envelope&, const std::string &); - bool processGetPayinAddress(const bs::message::Envelope& - , const BlockSettle::Common::SignerMessage_GetSettlPayinAddr&); bool processResolvePubSpenders(const bs::message::Envelope& , const bs::core::wallet::TXSignRequest&); bool processAutoSignRequest(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_AutoSign&); bool processDialogRequest(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_DialogRequest&); + bool processCreateWallet(const bs::message::Envelope& + , const BlockSettle::Common::SignerMessage_CreateWalletRequest&); private: std::shared_ptr logger_; std::shared_ptr user_; - std::shared_ptr signer_; + NetworkType netType_{NetworkType::Invalid}; + std::string walletsDir_; + std::shared_ptr walletsMgr_; + std::shared_ptr signer_; std::shared_ptr> connFuture_; std::string curServerId_; @@ -123,6 +121,7 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget bs::ThreadSafeMap> requests_; std::unordered_map autoSignRequests_; + SecureBinaryData passphrase_; }; diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index d5e21fd9a..371fcf1f6 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -46,8 +46,7 @@ std::string UserTerminal::name() const TerminalInprocBus::TerminalInprocBus(const std::shared_ptr &logger) : logger_(logger) { // we can create multiple queues if needed and distribute them on adapters - queue_ = std::make_shared(std::make_shared(logger), logger - , "Main", kTerminalUsersMapping); + queue_ = std::make_shared(std::make_shared(logger), logger, "Main"); } TerminalInprocBus::~TerminalInprocBus() diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 9b6f7b5fe..f1c667f0e 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -339,6 +339,15 @@ void MainWindow::onAddressHistory(const bs::Address& addr, uint32_t curBlock, co ui_->widgetExplorer->onAddressHistory(addr, curBlock, entries); } +void bs::gui::qt::MainWindow::onChangeAddress(const std::string& walletId + , const bs::Address& addr) +{ + logger_->debug("[{}] {} {}", __func__, walletId, addr.display()); + if (txDlg_) { + txDlg_->onChangeAddress(walletId, addr); + } +} + void MainWindow::onFeeLevels(const std::map& feeLevels) { if (txDlg_) { @@ -668,6 +677,7 @@ void MainWindow::onSend() connect(txDlg_, &CreateTransactionDialog::needSignTX, this, &MainWindow::needSignTX); connect(txDlg_, &CreateTransactionDialog::needBroadcastZC, this, &MainWindow::needBroadcastZC); connect(txDlg_, &CreateTransactionDialog::needSetTxComment, this, &MainWindow::needSetTxComment); + connect(txDlg_, &CreateTransactionDialog::needChangeAddress, this, &MainWindow::needChangeAddress); txDlg_->initUI(); if (!selectedWalletId.empty()) { @@ -822,6 +832,17 @@ void MainWindow::onReservedUTXOs(const std::string& resId , const std::string& subId, const std::vector& utxos) {} +bs::gui::WalletSeedData MainWindow::getWalletSeed(const std::string& rootId) const +{ + auto seedDialog = new bs::gui::qt::SeedDialog(rootId, (QWidget*)this); + const int rc = seedDialog->exec(); + seedDialog->deleteLater(); + if (rc == QDialog::Accepted) { + return seedDialog->getData(); + } + return {}; +} + void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index 8f5ba6afd..ed821a339 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -18,7 +18,8 @@ #include "ArmoryConnection.h" #include "AuthAddress.h" #include "CommonTypes.h" -#include "SignContainer.h" +#include "SeedDialog.h" +#include "Wallets/SignContainer.h" #include "Settings/SignersProvider.h" #include "UiUtils.h" @@ -86,6 +87,7 @@ namespace bs { void onZCsInvalidated(const std::vector& txHashes); void onAddressHistory(const bs::Address&, uint32_t curBlock , const std::vector&); + void onChangeAddress(const std::string& walletId, const bs::Address&); void onFeeLevels(const std::map&); void onUTXOs(const std::string& id, const std::string& walletId, const std::vector&); @@ -118,6 +120,8 @@ namespace bs { void onReservedUTXOs(const std::string& resId, const std::string& subId , const std::vector&); + bs::gui::WalletSeedData getWalletSeed(const std::string& rootId) const; + public slots: void onReactivate(); void raiseWindow(); @@ -145,6 +149,7 @@ namespace bs { void createExtAddress(const std::string& walletId); void needExtAddresses(const std::string &walletId); + void needChangeAddress(const std::string& walletId); void needIntAddresses(const std::string &walletId); void needUsedAddresses(const std::string &walletId); void needAddrComments(const std::string &walletId, const std::vector &); @@ -161,7 +166,8 @@ namespace bs { , bool confOnly = false, bool swOnly = false); void needSignTX(const std::string& id, const bs::core::wallet::TXSignRequest& - , bool keepDupRecips = false, SignContainer::TXSignMode mode = SignContainer::TXSignMode::Full); + , bool keepDupRecips = false, SignContainer::TXSignMode mode = SignContainer::TXSignMode::Full + , const SecureBinaryData& passphrase = {}); void needBroadcastZC(const std::string& id, const BinaryData&); void needSetTxComment(const std::string& walletId, const BinaryData& txHash, const std::string& comment); @@ -173,7 +179,6 @@ namespace bs { void needMdConnection(ApplicationSettings::EnvConfiguration); void needMdDisconnect(); - void needAuthKey(const bs::Address&); void needReserveUTXOs(const std::string& reserveId, const std::string& subId , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); void needUnreserveUTXOs(const std::string& reserveId, const std::string& subId); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 4422253ef..8fde71bbe 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -28,7 +28,7 @@ #include "BSTerminalSplashScreen.h" #include "MainWindow.h" #include "MessageUtils.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "SettingsAdapter.h" #include "TradesVerification.h" @@ -584,6 +584,7 @@ bool QtGuiAdapter::processWallets(const Envelope &env) case WalletsMessage::kWalletAddresses: { std::vector addresses; + logger_->debug("[kWalletAddresses] #{}", env.responseId()); for (const auto &addr : msg.wallet_addresses().addresses()) { try { addresses.push_back({ std::move(bs::Address::fromAddressString(addr.address())) @@ -591,7 +592,16 @@ bool QtGuiAdapter::processWallets(const Envelope &env) } catch (const std::exception &) {} } - QMetaObject::invokeMethod(mainWindow_, [this, addresses, walletId=msg.wallet_addresses().wallet_id()] { + const auto& walletId = msg.wallet_addresses().wallet_id(); + auto itReq = needChangeAddrReqs_.find(env.responseId()); + if (itReq != needChangeAddrReqs_.end()) { + QMetaObject::invokeMethod(mainWindow_, [this, addresses, walletId]{ + mainWindow_->onChangeAddress(walletId, addresses.cbegin()->address); + }); + needChangeAddrReqs_.erase(itReq); + break; + } + QMetaObject::invokeMethod(mainWindow_, [this, addresses, walletId] { mainWindow_->onAddresses(walletId, addresses); }); } @@ -620,8 +630,6 @@ bool QtGuiAdapter::processWallets(const Envelope &env) return processWalletsList(msg.wallets_list_response()); case WalletsMessage::kUtxos: return processUTXOs(msg.utxos()); - case WalletsMessage::kAuthWallet: - return processAuthWallet(msg.auth_wallet()); case WalletsMessage::kReservedUtxos: return processReservedUTXOs(msg.reserved_utxos()); default: break; @@ -641,8 +649,6 @@ bool QtGuiAdapter::processOnChainTrack(const Envelope &env) loadingComponents_.insert(env.sender->value()); updateSplashProgress(); break; - case OnChainTrackMessage::kAuthState: - return processAuthState(msg.auth_state()); default: break; } return true; @@ -787,6 +793,7 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::createExtAddress, this, &QtGuiAdapter::onCreateExtAddress); connect(mainWindow_, &bs::gui::qt::MainWindow::needExtAddresses, this, &QtGuiAdapter::onNeedExtAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needIntAddresses, this, &QtGuiAdapter::onNeedIntAddresses); + connect(mainWindow_, &bs::gui::qt::MainWindow::needChangeAddress, this, &QtGuiAdapter::onNeedChangeAddress); connect(mainWindow_, &bs::gui::qt::MainWindow::needUsedAddresses, this, &QtGuiAdapter::onNeedUsedAddresses); connect(mainWindow_, &bs::gui::qt::MainWindow::needAddrComments, this, &QtGuiAdapter::onNeedAddrComments); connect(mainWindow_, &bs::gui::qt::MainWindow::setAddrComment, this, &QtGuiAdapter::onSetAddrComment); @@ -805,7 +812,6 @@ void QtGuiAdapter::makeMainWinConnections() connect(mainWindow_, &bs::gui::qt::MainWindow::needCancelLogin, this, &QtGuiAdapter::onNeedCancelLogin); connect(mainWindow_, &bs::gui::qt::MainWindow::needMatchingLogout, this, &QtGuiAdapter::onNeedMatchingLogout); connect(mainWindow_, &bs::gui::qt::MainWindow::needMdConnection, this, &QtGuiAdapter::onNeedMdConnection); - connect(mainWindow_, &bs::gui::qt::MainWindow::needAuthKey, this, &QtGuiAdapter::onNeedAuthKey); connect(mainWindow_, &bs::gui::qt::MainWindow::needReserveUTXOs, this, &QtGuiAdapter::onNeedReserveUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::needUnreserveUTXOs, this, &QtGuiAdapter::onNeedUnreserveUTXOs); connect(mainWindow_, &bs::gui::qt::MainWindow::needWalletDialog, this, &QtGuiAdapter::onNeedWalletDialog); @@ -975,6 +981,15 @@ void QtGuiAdapter::onNeedIntAddresses(const std::string &walletId) pushRequest(user_, userWallets_, msg.SerializeAsString()); } +void QtGuiAdapter::onNeedChangeAddress(const std::string& walletId) +{ + WalletsMessage msg; + msg.set_get_change_addr(walletId); + const auto msgId = pushRequest(user_, userWallets_, msg.SerializeAsString()); + needChangeAddrReqs_.insert(msgId); + logger_->debug("[{}] {} #{}", __func__, walletId, msgId); +} + void QtGuiAdapter::onNeedUsedAddresses(const std::string &walletId) { logger_->debug("[{}] {}", __func__, walletId); @@ -1022,6 +1037,7 @@ void QtGuiAdapter::onNeedTXDetails(const std::vector &txWall auto msgReq = msg.mutable_tx_details_request(); for (const auto &txw : txWallet) { auto request = msgReq->add_requests(); + logger_->debug("[{}] {}", __func__, txw.txHash.toHexStr()); request->set_tx_hash(txw.txHash.toBinStr()); request->set_wallet_id(txw.walletId); request->set_value(txw.value); @@ -1090,15 +1106,15 @@ void QtGuiAdapter::onNeedUTXOs(const std::string& id, const std::string& walletI void QtGuiAdapter::onNeedSignTX(const std::string& id , const bs::core::wallet::TXSignRequest& txReq, bool keepDupRecips - , SignContainer::TXSignMode mode) + , SignContainer::TXSignMode mode, const SecureBinaryData& passphrase) { - logger_->debug("[{}] {}", __func__, id); SignerMessage msg; auto msgReq = msg.mutable_sign_tx_request(); msgReq->set_id(id); *msgReq->mutable_tx_request() = bs::signer::coreTxRequestToPb(txReq, keepDupRecips); msgReq->set_sign_mode((int)mode); msgReq->set_keep_dup_recips(keepDupRecips); + msgReq->set_passphrase(passphrase.toBinStr()); pushRequest(user_, userSigner_, msg.SerializeAsString()); } @@ -1187,13 +1203,6 @@ void QtGuiAdapter::onNeedMdConnection(ApplicationSettings::EnvConfiguration ec) pushRequest(user_, userMD_, msg.SerializeAsString()); } -void QtGuiAdapter::onNeedAuthKey(const bs::Address& addr) -{ - WalletsMessage msg; - msg.set_get_auth_key(addr.display()); - pushRequest(user_, userWallets_, msg.SerializeAsString()); -} - void QtGuiAdapter::onNeedReserveUTXOs(const std::string& reserveId , const std::string& subId, uint64_t amount, bool withZC , const std::vector& utxos) @@ -1224,15 +1233,28 @@ void QtGuiAdapter::onNeedUnreserveUTXOs(const std::string& reserveId void QtGuiAdapter::onNeedWalletDialog(bs::signer::ui::GeneralDialogType dlgType , const std::string& rootId) { - SignerMessage msg; - auto msgReq = msg.mutable_dialog_request(); - msgReq->set_dialog_type((int)dlgType); - if (!rootId.empty()) { - auto msgData = msgReq->add_data(); - msgData->set_key("rootId"); - msgData->set_value(rootId); + switch (dlgType) { + case bs::signer::ui::GeneralDialogType::CreateWallet: + if (mainWindow_) { + QMetaObject::invokeMethod(mainWindow_, [this, rootId] { + const auto& seedData = mainWindow_->getWalletSeed(rootId); + if (!seedData.empty()) { + SignerMessage msg; + auto msgReq = msg.mutable_create_wallet(); + msgReq->set_name(seedData.name); + msgReq->set_description(seedData.description); + msgReq->set_xpriv_key(seedData.xpriv); + msgReq->set_seed(seedData.seed.toBinStr()); + msgReq->set_password(seedData.password.toBinStr()); + pushRequest(user_, userSigner_, msg.SerializeAsString()); + } + }); + } + break; + default: + logger_->debug("[{}] {} ({})", __func__, (int)dlgType, rootId); + break; } - pushRequest(user_, userSigner_, msg.SerializeAsString()); } void QtGuiAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) @@ -1263,7 +1285,7 @@ bool QtGuiAdapter::processWalletData(uint64_t msgId return false; } -bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope &env +bool QtGuiAdapter::processWalletBalances(const bs::message::Envelope & , const WalletsMessage_WalletBalances &response) { bs::sync::WalletBalanceData wbd; @@ -1290,6 +1312,9 @@ bool QtGuiAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetai , resp.wallet_name(), static_cast(resp.wallet_type()) , resp.wallet_symbol(), static_cast(resp.direction()) , resp.comment(), resp.valid(), resp.amount() }; + if (!response.error_msg().empty()) { + txDet.comment = response.error_msg(); + } const auto &ownTxHash = BinaryData::fromString(resp.tx_hash()); try { @@ -1341,7 +1366,12 @@ bool QtGuiAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetai , resp.change_address().out_index() }; } catch (const std::exception &) {} - txDetails.push_back(txDet); + txDetails.emplace_back(std::move(txDet)); + } + if (!response.responses_size() && !response.error_msg().empty()) { + bs::sync::TXWalletDetails txDet; + txDet.comment = response.error_msg(); + txDetails.emplace_back(std::move(txDet)); } const auto& itZC = newZCs_.find(msgId); if (itZC != newZCs_.end()) { @@ -1625,35 +1655,6 @@ bool QtGuiAdapter::processMdUpdate(const MktDataMessage_Prices& msg) }); } -bool QtGuiAdapter::processAuthWallet(const WalletsMessage_WalletData& authWallet) -{ - std::vector authAddresses; - for (const auto& addr : authWallet.used_addresses()) { - try { - authAddresses.push_back(bs::Address::fromAddressString(addr.address())); - } - catch (const std::exception&) {} - } - return QMetaObject::invokeMethod(mainWindow_, [this, authAddresses] { - //mainWindow_->onAuthAddresses(authAddresses, {}); - }); -} - -bool QtGuiAdapter::processAuthState(const OnChainTrackMessage_AuthState& authState) -{ - bs::Address addr; - try { - addr = bs::Address::fromAddressString(authState.address()); - } - catch (const std::exception&) { - return true; - } - const auto& state = static_cast(authState.state()); - return QMetaObject::invokeMethod(mainWindow_, [this, addr, state] { - //mainWindow_->onAuthAddresses({}, { {addr, state } }); - }); -} - bool QtGuiAdapter::processBalance(const AssetsMessage_Balance& bal) { return QMetaObject::invokeMethod(mainWindow_, [this, bal] { diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 39e7ed9d4..0ffa70fda 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -15,7 +15,7 @@ #include #include "Address.h" #include "ApiAdapter.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "ThreadSafeClasses.h" #include "UiUtils.h" @@ -69,8 +69,6 @@ class BSTerminalSplashScreen; class GuiThread; -using GuiQueue = ArmoryThreading::TimedQueue; - class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRuner { Q_OBJECT @@ -133,8 +131,6 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processMktData(const bs::message::Envelope&); bool processSecurity(const std::string&, int); bool processMdUpdate(const BlockSettle::Terminal::MktDataMessage_Prices &); - bool processAuthWallet(const BlockSettle::Common::WalletsMessage_WalletData&); - bool processAuthState(const BlockSettle::Common::OnChainTrackMessage_AuthState&); bool processBalance(const BlockSettle::Terminal::AssetsMessage_Balance&); bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); @@ -158,6 +154,7 @@ private slots: void onCreateExtAddress(const std::string& walletId); void onNeedExtAddresses(const std::string &walletId); void onNeedIntAddresses(const std::string &walletId); + void onNeedChangeAddress(const std::string& walletId); void onNeedUsedAddresses(const std::string &walletId); void onNeedAddrComments(const std::string &walletId, const std::vector &); void onSetAddrComment(const std::string &walletId, const bs::Address & @@ -171,7 +168,7 @@ private slots: void onNeedUTXOs(const std::string& id, const std::string& walletId , bool confOnly, bool swOnly); void onNeedSignTX(const std::string& id, const bs::core::wallet::TXSignRequest& - , bool keepDupRecips, SignContainer::TXSignMode); + , bool keepDupRecips, SignContainer::TXSignMode, const SecureBinaryData& passphrase); void onNeedBroadcastZC(const std::string& id, const BinaryData&); void onNeedSetTxComment(const std::string& walletId, const BinaryData& txHash , const std::string& comment); @@ -181,7 +178,6 @@ private slots: void onNeedCancelLogin(); void onNeedMatchingLogout(); void onNeedMdConnection(ApplicationSettings::EnvConfiguration); - void onNeedAuthKey(const bs::Address&); void onNeedReserveUTXOs(const std::string& reserveId, const std::string& subId , uint64_t amount, bool withZC = false, const std::vector& utxos = {}); void onNeedUnreserveUTXOs(const std::string& reserveId, const std::string& subId); @@ -211,6 +207,8 @@ private slots: std::unordered_map assetTypes_; bool mdInstrumentsReceived_{ false }; + + std::set needChangeAddrReqs_; }; diff --git a/UnitTests/BlockchainMonitor.cpp b/UnitTests/BlockchainMonitor.cpp index 8d6c0d242..c3a6cef6a 100644 --- a/UnitTests/BlockchainMonitor.cpp +++ b/UnitTests/BlockchainMonitor.cpp @@ -53,7 +53,7 @@ std::vector BlockchainMonitor::waitForZC() auto zcVec = zcQueue_.pop_front(); return zcVec; } - catch (ArmoryThreading::IsEmpty&) + catch (Armory::Threading::IsEmpty&) {} std::this_thread::sleep_for(std::chrono::milliseconds{10}); diff --git a/UnitTests/BlockchainMonitor.h b/UnitTests/BlockchainMonitor.h index 5d77be270..2346789dc 100644 --- a/UnitTests/BlockchainMonitor.h +++ b/UnitTests/BlockchainMonitor.h @@ -16,7 +16,6 @@ #include #include "ArmoryConnection.h" #include "BinaryData.h" -#include "ClientClasses.h" #include "ThreadSafeClasses.h" @@ -53,7 +52,7 @@ class BlockchainMonitor : public ArmoryCallbackTarget private: std::atomic_bool receivedNewBlock_{ false }; - ArmoryThreading::BlockingQueue> zcQueue_; + Armory::Threading::BlockingQueue> zcQueue_; }; #endif // __BLOCKCHAIN_MONITOR_H__ diff --git a/UnitTests/CMakeLists.txt b/UnitTests/CMakeLists.txt index b95a0ecc0..c7adb7c63 100644 --- a/UnitTests/CMakeLists.txt +++ b/UnitTests/CMakeLists.txt @@ -79,7 +79,6 @@ TARGET_LINK_LIBRARIES( ${UNIT_TESTS} ${BOTAN_LIB} ${COMMON_LIB} ${PROTO_LIB} - ${ZMQ_LIB} ${GTEST_LIB} ${BS_PROTO_LIB_NAME} ${AUTH_PROTO_LIB} diff --git a/UnitTests/TestAdapters.cpp b/UnitTests/TestAdapters.cpp index 85913cd79..46350581a 100644 --- a/UnitTests/TestAdapters.cpp +++ b/UnitTests/TestAdapters.cpp @@ -12,7 +12,7 @@ #include #include #include "MessageUtils.h" -#include "ProtobufHeadlessUtils.h" +#include "Wallets/ProtobufHeadlessUtils.h" #include "TestEnv.h" #include "common.pb.h" diff --git a/UnitTests/TestAddress.cpp b/UnitTests/TestAddress.cpp index 0550ae29f..2b0851bd3 100644 --- a/UnitTests/TestAddress.cpp +++ b/UnitTests/TestAddress.cpp @@ -9,8 +9,9 @@ */ #include - #include "Address.h" +#include "BitcoinSettings.h" + TEST(TestAddress, ValidScenarios) { @@ -35,7 +36,7 @@ TEST(TestAddress, ValidScenarios) auto addr = bs::Address::fromPubKey(pubkey1, AddressEntryType_P2PKH); BinaryData prefixedHash; - prefixedHash.append(NetworkConfig::getPubkeyHashPrefix()); + prefixedHash.append(Armory::Config::BitcoinSettings::getPubkeyHashPrefix()); prefixedHash.append(pubkeyHash1); EXPECT_EQ(prefixedHash, addr.prefixed()); @@ -73,12 +74,12 @@ TEST(TestAddress, ValidScenarios) { //P2SH BinaryData prefixed; - prefixed.append(NetworkConfig::getScriptHashPrefix()); + prefixed.append(Armory::Config::BitcoinSettings::getScriptHashPrefix()); prefixed.append(rando20); auto addr = bs::Address::fromHash(prefixed); BinaryData prefixedHash; - prefixedHash.append(NetworkConfig::getScriptHashPrefix()); + prefixedHash.append(Armory::Config::BitcoinSettings::getScriptHashPrefix()); prefixedHash.append(rando20); EXPECT_EQ(prefixedHash, addr.prefixed()); @@ -100,7 +101,7 @@ TEST(TestAddress, ValidScenarios) BinaryData script = BtcUtils::getP2PKHScript(pubkeyHash1); BinaryData prefixedHash; - prefixedHash.append(NetworkConfig::getScriptHashPrefix()); + prefixedHash.append(Armory::Config::BitcoinSettings::getScriptHashPrefix()); prefixedHash.append(BtcUtils::getHash160(script)); EXPECT_EQ(prefixedHash, addr.prefixed()); @@ -121,7 +122,7 @@ TEST(TestAddress, ValidScenarios) BinaryData script = BtcUtils::getP2WPKHOutputScript(pubkeyHash1); BinaryData prefixedHash; - prefixedHash.append(NetworkConfig::getScriptHashPrefix()); + prefixedHash.append(Armory::Config::BitcoinSettings::getScriptHashPrefix()); prefixedHash.append(BtcUtils::getHash160(script)); EXPECT_EQ(prefixedHash, addr.prefixed()); @@ -145,7 +146,7 @@ TEST(TestAddress, ValidScenarios) EXPECT_EQ(hash, addr.unprefixed()); BinaryData prefixedHash; - prefixedHash.append(NetworkConfig::getScriptHashPrefix()); + prefixedHash.append(Armory::Config::BitcoinSettings::getScriptHashPrefix()); prefixedHash.append(hash); EXPECT_EQ(prefixedHash, addr.prefixed()); @@ -177,7 +178,7 @@ TEST(TestAddress, ValidScenarios) EXPECT_EQ(addr.unprefixed(), hash); BinaryData prefixedHash; - prefixedHash.append(NetworkConfig::getScriptHashPrefix()); + prefixedHash.append(Armory::Config::BitcoinSettings::getScriptHashPrefix()); prefixedHash.append(hash); EXPECT_EQ(prefixedHash, addr.prefixed()); @@ -612,7 +613,7 @@ TEST(TestAddress, P2SH_ImplicitDetection) const auto& prefixed = addr.prefixed(); ASSERT_EQ(prefixed.getSize(), 21); EXPECT_EQ(prefixed, scriptHash); - EXPECT_EQ(prefixed.getPtr()[0], NetworkConfig::getScriptHashPrefix()); + EXPECT_EQ(prefixed.getPtr()[0], Armory::Config::BitcoinSettings::getScriptHashPrefix()); } TEST(TestAddress, P2SH_ExplicitDetection) @@ -649,7 +650,7 @@ TEST(TestAddress, P2SH_ExplicitDetection) const auto& prefixed = addr.prefixed(); ASSERT_EQ(prefixed.getSize(), 21); EXPECT_EQ(prefixed.getSliceCopy(1, 20), scriptHash); - EXPECT_EQ(prefixed.getPtr()[0], NetworkConfig::getScriptHashPrefix()); + EXPECT_EQ(prefixed.getPtr()[0], Armory::Config::BitcoinSettings::getScriptHashPrefix()); } { @@ -667,7 +668,7 @@ TEST(TestAddress, P2SH_ExplicitDetection) const auto& prefixed = addr.prefixed(); ASSERT_EQ(prefixed.getSize(), 21); EXPECT_EQ(prefixed.getSliceCopy(1, 20), scriptHash); - EXPECT_EQ(prefixed.getPtr()[0], NetworkConfig::getScriptHashPrefix()); + EXPECT_EQ(prefixed.getPtr()[0], Armory::Config::BitcoinSettings::getScriptHashPrefix()); } { @@ -685,7 +686,7 @@ TEST(TestAddress, P2SH_ExplicitDetection) const auto& prefixed = addr.prefixed(); ASSERT_EQ(prefixed.getSize(), 21); EXPECT_EQ(prefixed.getSliceCopy(1, 20), scriptHash); - EXPECT_EQ(prefixed.getPtr()[0], NetworkConfig::getScriptHashPrefix()); + EXPECT_EQ(prefixed.getPtr()[0], Armory::Config::BitcoinSettings::getScriptHashPrefix()); } } diff --git a/UnitTests/TestAuth.cpp b/UnitTests/TestAuth.cpp index b66c58f70..27e79a161 100644 --- a/UnitTests/TestAuth.cpp +++ b/UnitTests/TestAuth.cpp @@ -58,17 +58,17 @@ check unflagged return #include "CheckRecipSigner.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "Wallets/SyncHDLeaf.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" -#include "AddressVerificator.h" #include "TestAuth.h" -using namespace ArmorySigner; +using namespace Armory::Signer; +#if 0 // Auth address code turned off /////////////////////////////////////////////////////////////////////////////// void TestValidationACT::onRefresh(const std::vector& ids, bool online) { @@ -1294,5 +1294,5 @@ TEST_F(TestAuth, Concurrency_WithACT) EXPECT_FALSE(AuthAddressLogic::isValid(*vam, authAddresses[4])); EXPECT_FALSE(AuthAddressLogic::isValid(*vam, authAddresses[5])); } - +#endif //0 //reorg & zc replacement test diff --git a/UnitTests/TestAuth.h b/UnitTests/TestAuth.h index 76337f12e..5f2574ec4 100644 --- a/UnitTests/TestAuth.h +++ b/UnitTests/TestAuth.h @@ -19,10 +19,9 @@ #include #include "Address.h" #include "BlockchainMonitor.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "TestEnv.h" -#include "AuthAddressLogic.h" namespace bs { namespace sync { @@ -40,6 +39,7 @@ namespace bs { class QtHCT; //////////////////////////////////////////////////////////////////////////////// +#if 0 // Auth address code turned off class TestValidationACT : public ValidationAddressACT { private: @@ -153,5 +153,6 @@ class TestAuth : public ::testing::Test, public SignerCallbackTarget SecureBinaryData passphrase_; }; +#endif //0 -#endif \ No newline at end of file +#endif diff --git a/UnitTests/TestCCoin.cpp b/UnitTests/TestCCoin.cpp index 5bb9c6708..279bfcbca 100644 --- a/UnitTests/TestCCoin.cpp +++ b/UnitTests/TestCCoin.cpp @@ -15,14 +15,15 @@ #include "CheckRecipSigner.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "Wallets/SyncHDLeaf.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" using namespace ArmorySigner; +#if 0 // CC code turned off TestCCoin::TestCCoin() {} @@ -3649,6 +3650,7 @@ TEST_F(TestCCoin, processZC_whileMined) // While obtaining ZCs in processZcBatch, one of the transactions got mined ASSERT_TRUE(false) << "Forcefully fails - to be implemented (see comments in source code)"; } +#endif //0 //TODO: //over assign cc diff --git a/UnitTests/TestCCoin.h b/UnitTests/TestCCoin.h index 9a9be66d2..cb31ba1f3 100644 --- a/UnitTests/TestCCoin.h +++ b/UnitTests/TestCCoin.h @@ -18,8 +18,8 @@ #include "Address.h" #include "BlockchainMonitor.h" -#include "ColoredCoinLogic.h" -#include "SignContainer.h" +//#include "ColoredCoinLogic.h" +#include "Wallets/SignContainer.h" #include "TestEnv.h" namespace bs { @@ -53,6 +53,7 @@ struct CCoinSpender uint64_t xbtValue_ = 0; }; +#if 0 // no CC tests since all CC code is turned off class ColoredCoinTestACT : public ColoredCoinACT { private: @@ -228,5 +229,6 @@ class TestCCoin : public ::testing::Test, public SignerCallbackTarget void zcUpdate(std::shared_ptr); void reorg(std::shared_ptr); }; +#endif #endif // __TEST_CCOIN_H__ diff --git a/UnitTests/TestCCoinAsync.cpp b/UnitTests/TestCCoinAsync.cpp index 15cb3f43c..902fb2f18 100644 --- a/UnitTests/TestCCoinAsync.cpp +++ b/UnitTests/TestCCoinAsync.cpp @@ -15,14 +15,15 @@ #include "CheckRecipSigner.h" #include "CoreHDWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "Wallets/SyncHDLeaf.h" #include "Wallets/SyncHDWallet.h" #include "Wallets/SyncWalletsManager.h" -using namespace ArmorySigner; +using namespace Armory::Signer; +#if 0 // CC code turned off TestCCoinAsync::TestCCoinAsync() {} @@ -1474,3 +1475,4 @@ TEST_F(TestCCoinAsync, Reorg) EXPECT_EQ(cct->getCcValueForAddress(userCCAddresses_[c]), y*ccLotSize_); } } +#endif //0 diff --git a/UnitTests/TestCCoinAsync.h b/UnitTests/TestCCoinAsync.h index 65350121b..dc3ffe137 100644 --- a/UnitTests/TestCCoinAsync.h +++ b/UnitTests/TestCCoinAsync.h @@ -18,8 +18,7 @@ #include "Address.h" #include "BlockchainMonitor.h" -#include "CCLogicAsync.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "TestEnv.h" namespace bs { @@ -43,7 +42,7 @@ namespace bs { } class QtHCT; - +#if 0 // CC code turned off class AsyncCCT : public ColoredCoinTrackerAsync { public: @@ -179,5 +178,5 @@ class AsyncCCT_ACT : public SingleUTWalletACT private: std::map> refreshCb_; }; - +#endif //0 #endif // TEST_CCOIN_ASYNC_H diff --git a/UnitTests/TestCommon.cpp b/UnitTests/TestCommon.cpp index c16769d98..61f09ff62 100644 --- a/UnitTests/TestCommon.cpp +++ b/UnitTests/TestCommon.cpp @@ -28,8 +28,8 @@ #include "CacheFile.h" #include "CurrencyPair.h" #include "EasyCoDec.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "MDCallbacksQt.h" #include "MarketDataProvider.h" #include "PriceAmount.h" @@ -320,6 +320,8 @@ TEST(TestCommon, BotanSerpent) } #include "AssetEncryption.h" +using namespace Armory::Wallets::Encryption; + TEST(TestCommon, BotanSerpent_KDF_Romix) { Botan::AutoSeeded_RNG rng; diff --git a/UnitTests/TestEnv.cpp b/UnitTests/TestEnv.cpp index f3f2457b6..675de4822 100644 --- a/UnitTests/TestEnv.cpp +++ b/UnitTests/TestEnv.cpp @@ -12,9 +12,10 @@ #include "TestEnv.h" #include "ApplicationSettings.h" -#include "ArmoryObject.h" +#include "ArmoryConfig.h" +#include "AuthorizedPeers.h" +#include "Wallets/ArmoryObject.h" #include "ArmorySettings.h" -#include "AuthAddressManager.h" #include "ConnectionManager.h" #include "CoreWalletsManager.h" #include "MarketDataProvider.h" @@ -29,6 +30,9 @@ #include #include +using namespace Armory::Signer; +using namespace Armory::Wallets; + const BinaryData testnetGenesisBlock = READHEX("0100000000000000000000000000000000000\ 000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51\ 323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae18010100000001000000000000000000000000000\ @@ -39,7 +43,7 @@ e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0f 112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"); std::shared_ptr StaticLogger::loggerPtr = nullptr; -ArmoryThreading::TimedQueue> ACTqueue::notifQueue_; +Armory::Threading::TimedQueue> ACTqueue::notifQueue_; TestEnv::TestEnv(const std::shared_ptr &logger) { @@ -172,11 +176,9 @@ void ArmoryInstance::init() SystemFileUtils::mkPath(ldbdir_); //setup env - NetworkConfig::selectNetwork(NETWORK_MODE_TESTNET); - BlockDataManagerConfig::setServiceType(SERVICE_WEBSOCKET); - BlockDataManagerConfig::setDbType(ARMORY_DB_SUPER); - BlockDataManagerConfig::setOperationMode(OPERATION_UNITTEST); - auto& magicBytes = NetworkConfig::getMagicBytes(); + Armory::Config::NetworkSettings::selectNetwork(Armory::Config::NETWORK_MODE_TESTNET); + Armory::Config::DBSettings::setServiceType(SERVICE_WEBSOCKET); + auto& magicBytes = Armory::Config::BitcoinSettings::getMagicBytes(); //create block file with testnet genesis block auto blk0dat = BtcUtils::getBlkFilename(blkdir_, 0); @@ -194,21 +196,17 @@ void ArmoryInstance::init() fs.close(); //setup config - config_.blkFileLocation_ = blkdir_; - config_.dbDir_ = ldbdir_; - config_.threadCount_ = 3; - config_.dataDir_ = homedir_; - config_.ephemeralPeers_ = false; - port_ = UNITTEST_DB_PORT; - std::stringstream port_ss; - port_ss << port_; - config_.listenPort_ = port_ss.str(); //setup bip151 context startupBIP150CTX(4); - const auto lbdEmptyPassphrase = [](const std::set &) { + std::vector args{ "--db-type=DB_SUPER", "--public" + , "--satoshi-datadir=" + blkdir_, "--dbdir=" + ldbdir_, "--datadir=" + homedir_ + , "--thread-count=3", "--listen-port=" + std::to_string(port_) }; + Armory::Config::parseArgs(args, Armory::Config::ProcessType::DB); + + const auto lbdEmptyPassphrase = [](const std::set&) { return SecureBinaryData{}; }; @@ -220,53 +218,57 @@ void ArmoryInstance::init() auto& clientPubkey = clientPeers.getOwnPublicKey(); std::stringstream serverAddr; - serverAddr << "127.0.0.1:" << config_.listenPort_; + serverAddr << "127.0.0.1:" << port_; clientPeers.addPeer(serverPubkey, serverAddr.str()); serverPeers.addPeer(clientPubkey, "127.0.0.1"); - //init bdm - nodePtr_ = - std::make_shared(*(unsigned int*)magicBytes.getPtr(), false); - const auto watchNode = std::make_shared(0, true); - config_.bitcoinNodes_ = { nodePtr_, watchNode }; - config_.rpcNode_ = std::make_shared(nodePtr_, watchNode); + //init bdm & network nodes + nodePtr_ = std::dynamic_pointer_cast( + Armory::Config::NetworkSettings::bitcoinNodes().first); + + nodePtr_ = std::dynamic_pointer_cast( + Armory::Config::NetworkSettings::bitcoinNodes().first); - theBDMt_ = new BlockDataManagerThread(config_); + rpcNode_ = std::dynamic_pointer_cast( + Armory::Config::NetworkSettings::rpcNode()); + + theBDMt_ = new BlockDataManagerThread(); iface_ = theBDMt_->bdm()->getIFace(); - auto nodePtr = std::dynamic_pointer_cast(nodePtr_); - nodePtr->setBlockchain(theBDMt_->bdm()->blockchain()); - nodePtr->setBlockFiles(theBDMt_->bdm()->blockFiles()); + nodePtr_->setBlockchain(theBDMt_->bdm()->blockchain()); + nodePtr_->setBlockFiles(theBDMt_->bdm()->blockFiles()); nodePtr_->setIface(iface_); - theBDMt_->start(config_.initMode_); + theBDMt_->start(INIT_RESUME); - WebSocketServer::initAuthPeers(lbdEmptyPassphrase); //start server + WebSocketServer::initAuthPeers(lbdEmptyPassphrase); WebSocketServer::start(theBDMt_, true); } //// void ArmoryInstance::shutdown() { - if (!theBDMt_) { + if (theBDMt_ == nullptr) { return; } + //shutdown server - auto&& bdvObj2 = AsyncClient::BlockDataViewer::getNewBDV("127.0.0.1" - , config_.listenPort_, BlockDataManagerConfig::getDataDir() - , [](const std::set &) { return SecureBinaryData{}; } - , BlockDataManagerConfig::ephemeralPeers_, true, nullptr); -// auto&& serverPubkey = WebSocketServer::getPublicKey(); // sometimes crash here -// bdvObj2->addPublicKey(serverPubkey); + auto&& bdvObj2 = AsyncClient::BlockDataViewer::getNewBDV( + "127.0.0.1", std::to_string(port_), Armory::Config::getDataDir() + , [](const std::set&) { return SecureBinaryData{}; } + , Armory::Config::NetworkSettings::ephemeralPeers(), true, nullptr); + auto&& serverPubkey = WebSocketServer::getPublicKey(); + bdvObj2->addPublicKey(serverPubkey); bdvObj2->connectToRemote(); - bdvObj2->shutdown(config_.cookie_); + bdvObj2->shutdown(Armory::Config::NetworkSettings::cookie()); WebSocketServer::waitOnShutdown(); //shutdown bdm delete theBDMt_; theBDMt_ = nullptr; + nodePtr_ = nullptr; //clean up dirs SystemFileUtils::rmDir(blkdir_); @@ -275,7 +277,7 @@ void ArmoryInstance::shutdown() } std::map ArmoryInstance::mineNewBlock( - ArmorySigner::ScriptRecipient* rec, unsigned count) + ScriptRecipient* rec, unsigned count) { return nodePtr_->mineNewBlock(theBDMt_->bdm(), count, rec); } @@ -372,7 +374,7 @@ std::vector UnitTestWalletACT::waitOnZC(bool soft) } return notif->zc_; } - catch (const ArmoryThreading::StackTimedOutException &) { + catch (const Armory::Threading::StackTimedOutException &) { return {}; } } @@ -390,7 +392,7 @@ int UnitTestWalletACT::waitOnBroadcastError(const std::string &reqId) } return notif->errCode_; } - catch (const ArmoryThreading::StackTimedOutException &) { + catch (const Armory::Threading::StackTimedOutException &) { return 0; } } diff --git a/UnitTests/TestEnv.h b/UnitTests/TestEnv.h index aca7a859a..74424054f 100644 --- a/UnitTests/TestEnv.h +++ b/UnitTests/TestEnv.h @@ -11,11 +11,10 @@ #ifndef __TEST_ENV_H__ #define __TEST_ENV_H__ -#include "ArmoryObject.h" -#include "AuthAddressLogic.h" +#include "ArmoryConfig.h" +#include "Wallets/ArmoryObject.h" #include "BDM_mainthread.h" #include "BlockchainMonitor.h" -#include "BlockDataManagerConfig.h" #include "gtest/NodeUnitTest.h" #include "MockAssetMgr.h" #include "Server.h" @@ -30,6 +29,8 @@ #include #include +using namespace Armory::Signer; + #define UNITTEST_DB_PORT 59095 struct StaticLogger @@ -56,7 +57,7 @@ namespace ArmorySigner { class BIP32_AssetPath; }; -class ResolverOneAddress : public ArmorySigner::ResolverFeed +class ResolverOneAddress : public Armory::Signer::ResolverFeed { private: SecureBinaryData privKey_; @@ -87,18 +88,11 @@ class ResolverOneAddress : public ArmorySigner::ResolverFeed return privKey_; } - void setBip32PathForPubkey(const BinaryData &, const ArmorySigner::BIP32_AssetPath&) override - { - throw std::runtime_error("not implemented"); - } - - ArmorySigner::BIP32_AssetPath resolveBip32PathForPubkey(const BinaryData&) override - { - throw std::runtime_error("not implemented"); - } + void setBip32PathForPubkey(const BinaryData&, const BIP32_AssetPath&) override {} + BIP32_AssetPath resolveBip32PathForPubkey(const BinaryData&) override { return { {}, {}, {}, nullptr }; } }; -class ResolverManyAddresses : public ArmorySigner::ResolverFeed +class ResolverManyAddresses : public Armory::Signer::ResolverFeed { private: std::map hashToPubKey_; @@ -134,20 +128,13 @@ class ResolverManyAddresses : public ArmorySigner::ResolverFeed return iter->second; } - void setBip32PathForPubkey(const BinaryData &, const ArmorySigner::BIP32_AssetPath&) override - { - throw std::runtime_error("not implemented"); - } - - ArmorySigner::BIP32_AssetPath resolveBip32PathForPubkey(const BinaryData&) override - { - throw std::runtime_error("not implemented"); - } + void setBip32PathForPubkey(const BinaryData&, const BIP32_AssetPath&) override {} + BIP32_AssetPath resolveBip32PathForPubkey(const BinaryData&) override { return { {}, {}, {}, nullptr }; } }; struct ACTqueue { - static ArmoryThreading::TimedQueue> notifQueue_; + static Armory::Threading::TimedQueue> notifQueue_; }; class SingleUTWalletACT : public ArmoryCallbackTarget @@ -282,7 +269,7 @@ class UnitTestWalletACT : public bs::sync::WalletACT struct UnitTestLocalACT : public bs::sync::WalletACT { - ArmoryThreading::BlockingQueue> notifQueue_; + Armory::Threading::BlockingQueue> notifQueue_; public: UnitTestLocalACT(ArmoryConnection *armory, bs::sync::Wallet *leaf) : @@ -401,9 +388,8 @@ struct ArmoryInstance const std::string ldbdir_; int port_; - std::shared_ptr nodePtr_; - - BlockDataManagerConfig config_; + std::shared_ptr nodePtr_; + std::shared_ptr rpcNode_; BlockDataManagerThread* theBDMt_; LMDBBlockDatabase* iface_; @@ -411,7 +397,7 @@ struct ArmoryInstance ArmoryInstance(); ~ArmoryInstance(void) { shutdown(); } - std::map mineNewBlock(ArmorySigner::ScriptRecipient*, unsigned); + std::map mineNewBlock(Armory::Signer::ScriptRecipient*, unsigned); void pushZC(const BinaryData &, unsigned int blocksUntilMined = 0, bool stage = false); void setReorgBranchPoint(const BinaryData&); diff --git a/UnitTests/TestOtc.cpp b/UnitTests/TestOtc.cpp index 3837cb520..86770a8c8 100644 --- a/UnitTests/TestOtc.cpp +++ b/UnitTests/TestOtc.cpp @@ -12,12 +12,12 @@ #include "CoreHDWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "SettableField.h" #include "StringUtils.h" #include "TestEnv.h" -#include "TradesUtils.h" +#include "Wallets/TradesUtils.h" #include "TradesVerification.h" //#include "Trading/OtcClient.h" #include "Wallets/SyncHDWallet.h" diff --git a/UnitTests/TestSettlement.cpp b/UnitTests/TestSettlement.cpp index f50351a25..82e0925af 100644 --- a/UnitTests/TestSettlement.cpp +++ b/UnitTests/TestSettlement.cpp @@ -12,8 +12,8 @@ #include #include "CoreHDWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "MessageUtils.h" #include "MockTerminal.h" #include "TestAdapters.h" @@ -22,7 +22,7 @@ #include "common.pb.h" #include "terminal.pb.h" -using namespace ArmorySigner; +using namespace Armory::Signer; using namespace bs::message; using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; @@ -125,7 +125,7 @@ void TestSettlement::SetUp() settlLeaf = hdWallet->createSettlementLeaf(authAddr); const auto assetPtr = settlLeaf->getRootAsset(); - const auto assetSingle = std::dynamic_pointer_cast(assetPtr); + const auto assetSingle = std::dynamic_pointer_cast(assetPtr); if (assetSingle) { authKey = assetSingle->getPubKey()->getCompressedKey(); } diff --git a/UnitTests/TestSettlement.h b/UnitTests/TestSettlement.h index ec2c43876..69e73ae88 100644 --- a/UnitTests/TestSettlement.h +++ b/UnitTests/TestSettlement.h @@ -19,7 +19,7 @@ #include #include "Address.h" #include "BlockchainMonitor.h" -#include "SignContainer.h" +#include "Wallets/SignContainer.h" #include "TestEnv.h" diff --git a/UnitTests/TestUi.cpp b/UnitTests/TestUi.cpp index a101f391c..e3f759713 100644 --- a/UnitTests/TestUi.cpp +++ b/UnitTests/TestUi.cpp @@ -20,7 +20,7 @@ #include "CoreWalletsManager.h" #include "CustomControls/CustomDoubleSpinBox.h" #include "CustomControls/CustomDoubleValidator.h" -#include "InprocSigner.h" +#include "Wallets/InprocSigner.h" #include "TestEnv.h" #include "UiUtils.h" #include "Wallets/SyncHDWallet.h" diff --git a/UnitTests/TestWallet.cpp b/UnitTests/TestWallet.cpp index 7cd1e987d..cb3a4e3bc 100644 --- a/UnitTests/TestWallet.cpp +++ b/UnitTests/TestWallet.cpp @@ -19,8 +19,8 @@ #include "CoreHDWallet.h" #include "CoreWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "SystemFileUtils.h" #include "TestEnv.h" #include "UiUtils.h" @@ -666,8 +666,9 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) EXPECT_EQ(syncWallet->getUsedAddressCount(), 10); EXPECT_EQ(syncWallet->getExtAddressCount(), 5); EXPECT_EQ(syncWallet->getIntAddressCount(), 5); +#if 0 syncWallet->syncAddresses(); - +#endif //check address maps BIP32_Node extNode = base_node; extNode.derivePrivate(0); @@ -790,8 +791,9 @@ TEST_F(TestWallet, CreateDestroyLoad_SyncWallet) EXPECT_EQ(syncWallet->getUsedAddressCount(), 12); EXPECT_EQ(syncWallet->getExtAddressCount(), 6); EXPECT_EQ(syncWallet->getIntAddressCount(), 6); +#if 0 syncWallet->syncAddresses(); - +#endif //create WO copy auto WOcopy = walletPtr->createWatchingOnly(); filename = WOcopy->getFileName(); @@ -1694,7 +1696,7 @@ TEST_F(TestWallet, ImportExport_xpriv) wallet2->getDecryptedSeed()); ASSERT_TRUE(false); } - catch (const WalletException &) {} + catch (const Armory::Wallets::WalletException &) {} //shut it all down, reload, check seeds again filename = wallet2->getFileName(); @@ -1765,9 +1767,9 @@ TEST_F(TestWallet, TxIdNativeSegwit) UTXO input; input.unserialize(BinaryData::CreateFromHex( "cc16060000000000741618000300010020d5921cfa9b95c9fdafa9dca6d2765b5d7d2285914909b8f5f74f0b137259153b16001428d45f4ef82103691ea40c26b893a4566729b335ffffffff")); - request.armorySigner_.addSpender(std::make_shared(input)); + request.armorySigner_.addSpender(std::make_shared(input)); - auto recipient = ArmorySigner::ScriptRecipient::fromScript(BinaryData::CreateFromHex( + auto recipient = Armory::Signer::ScriptRecipient::fromScript(BinaryData::CreateFromHex( "a086010000000000220020aa38b39ed9b524967159ad2bd488d14c1b9ccd70364655a7d9f35cb83e4dc6ed")); request.armorySigner_.addRecipient(recipient); @@ -1822,10 +1824,11 @@ TEST_F(TestWallet, TxIdNestedSegwit) ASSERT_NE(syncHdWallet, nullptr); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 const auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); ASSERT_FALSE(regIDs.empty()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto syncWallet = syncMgr->getWalletById(coreLeaf->walletId()); auto syncLeaf = std::dynamic_pointer_cast(syncWallet); ASSERT_TRUE(syncLeaf != nullptr); @@ -1856,9 +1859,9 @@ TEST_F(TestWallet, TxIdNestedSegwit) ASSERT_TRUE(input.isInitialized()); bs::core::wallet::TXSignRequest request; - request.armorySigner_.addSpender(std::make_shared(input)); + request.armorySigner_.addSpender(std::make_shared(input)); - auto recipient = ArmorySigner::ScriptRecipient::fromScript(BinaryData::CreateFromHex( + auto recipient = Armory::Signer::ScriptRecipient::fromScript(BinaryData::CreateFromHex( "a086010000000000220020d35c94ed03ae988841bd990124e176dae3928ba41f5a684074a857e788d768ba")); request.armorySigner_.addRecipient(recipient); diff --git a/UnitTests/TestWalletArmory.cpp b/UnitTests/TestWalletArmory.cpp index 3ee843265..8b1d21efd 100644 --- a/UnitTests/TestWalletArmory.cpp +++ b/UnitTests/TestWalletArmory.cpp @@ -19,8 +19,8 @@ #include "CoreHDWallet.h" #include "CoreWallet.h" #include "CoreWalletsManager.h" -#include "HeadlessContainer.h" -#include "InprocSigner.h" +#include "Wallets/HeadlessContainer.h" +#include "Wallets/InprocSigner.h" #include "SystemFileUtils.h" #include "TestEnv.h" #include "UiUtils.h" @@ -90,9 +90,10 @@ TEST_F(TestWalletWithArmory, AddressChainExtension) ASSERT_NE(syncHdWallet, nullptr); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto syncWallet = syncMgr->getWalletById(leafPtr_->walletId()); auto syncLeaf = std::dynamic_pointer_cast(syncWallet); ASSERT_TRUE(syncLeaf != nullptr); @@ -269,8 +270,10 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) ASSERT_TRUE(syncLeaf != nullptr); syncLeaf->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncLeaf->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); +#endif //check wallet has 10 assets per account ASSERT_EQ(syncLeaf->getAddressPoolSize(), 20); @@ -474,9 +477,10 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) ASSERT_NE(syncHdWallet, nullptr); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto syncWallet = syncMgr->getWalletById(leafPtr_->walletId()); auto syncLeaf = std::dynamic_pointer_cast(syncWallet); ASSERT_TRUE(syncLeaf != nullptr); @@ -605,9 +609,10 @@ TEST_F(TestWalletWithArmory, RestoreWallet_CheckChainLength) ASSERT_NE(syncHdWallet, nullptr); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto trackProm = std::make_shared>(); auto trackFut = trackProm->get_future(); auto trackLbd = [trackProm](bool result)->void @@ -684,9 +689,10 @@ TEST_F(TestWalletWithArmory, Comments) auto syncHdWallet = syncMgr->getHDWalletById(walletPtr_->walletId()); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto syncWallet = syncMgr->getWalletById(leafPtr_->walletId()); ASSERT_EQ(syncWallet->getUsedAddressCount(), 2); EXPECT_EQ(syncWallet->getUsedAddressList()[0], addr); @@ -750,13 +756,14 @@ TEST_F(TestWalletWithArmory, ZCBalance) auto syncLeaf = syncMgr->getWalletById(leafPtr_->walletId()); syncWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); regIDs = syncWallet->setUnconfirmedTargets(); ASSERT_EQ(regIDs.size(), 2); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif //check balances are 0 auto balProm = std::make_shared>(); auto balFut = balProm->get_future(); @@ -942,9 +949,10 @@ TEST_F(TestWalletWithArmory, SimpleTX_bech32) auto syncLeaf = syncMgr->getWalletById(leafPtr_->walletId()); syncWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif //mine some coins auto armoryInstance = envPtr_->armoryInstance(); unsigned blockCount = 6; @@ -1038,6 +1046,7 @@ TEST_F(TestWalletWithArmory, SimpleTX_bech32) EXPECT_EQ(zcVec2[0].txHash, txObj2.getThisHash()); } +#if 0 TEST_F(TestWalletWithArmory, SignSettlement) { /*create settlement leaf*/ @@ -1184,6 +1193,7 @@ TEST_F(TestWalletWithArmory, SignSettlement) } } } +#endif //0 TEST_F(TestWalletWithArmory, GlobalDelegateConf) { @@ -1207,9 +1217,10 @@ TEST_F(TestWalletWithArmory, GlobalDelegateConf) ASSERT_NE(syncHdWallet, nullptr); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto syncWallet = syncMgr->getWalletById(leafPtr_->walletId()); auto syncLeaf = std::dynamic_pointer_cast(syncWallet); ASSERT_TRUE(syncLeaf != nullptr); @@ -1270,7 +1281,7 @@ TEST_F(TestWalletWithArmory, GlobalDelegateConf) ASSERT_NE(globalLedger, nullptr); const auto &lbdGetLDEntries = [](const std::shared_ptr &ledger) - -> std::shared_ptr> + -> std::shared_ptr> { auto promLDPageCnt1 = std::make_shared>(); auto futLDPageCnt1 = promLDPageCnt1->get_future(); @@ -1286,11 +1297,11 @@ TEST_F(TestWalletWithArmory, GlobalDelegateConf) auto pageCnt1 = futLDPageCnt1.get(); EXPECT_GE(pageCnt1, 1); - auto ledgerEntries = std::make_shared>(); + auto ledgerEntries = std::make_shared>(); auto promLDEntries1 = std::make_shared>(); auto futLDEntries1 = promLDEntries1->get_future(); const auto &cbHistPage1 = [&pageCnt1, promLDEntries1, ledgerEntries] - (ReturnMessage> msg) + (ReturnMessage> msg) { try { const auto &entries = msg.get(); @@ -1386,9 +1397,10 @@ TEST_F(TestWalletWithArmory, CallbackReturnTxCrash) auto syncHdWallet = syncMgr->getHDWalletById(walletPtr_->walletId()); syncHdWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncHdWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif auto recipient = addr.getRecipient(bs::XBTAmount{ (int64_t)(50 * COIN) }); envPtr_->armoryInstance()->mineNewBlock(recipient.get(), 1); UnitTestWalletACT::waitOnNewBlock(); @@ -1428,13 +1440,14 @@ TEST_F(TestWalletWithArmory, PushZC_retry) auto syncLeaf = syncMgr->getWalletById(leafPtr_->walletId()); syncWallet->setCustomACT(envPtr_->armoryConnection()); +#if 0 auto regIDs = syncWallet->registerWallet(envPtr_->armoryConnection()); UnitTestWalletACT::waitOnRefresh(regIDs); regIDs = syncWallet->setUnconfirmedTargets(); ASSERT_EQ(regIDs.size(), 2); UnitTestWalletACT::waitOnRefresh(regIDs); - +#endif //mine some coins auto armoryInstance = envPtr_->armoryInstance(); unsigned blockCount = 6; diff --git a/UnitTests/TestWebSockets.cpp b/UnitTests/TestWebSockets.cpp index 4b49132cc..99647dfd4 100644 --- a/UnitTests/TestWebSockets.cpp +++ b/UnitTests/TestWebSockets.cpp @@ -99,7 +99,7 @@ namespace { std::shared_ptr logger_; std::promise connected_; std::promise disconnected_; - ArmoryThreading::TimedQueue> data_; + Armory::Threading::TimedQueue> data_; }; class TestClientConnListener : public DataConnectionListener @@ -132,7 +132,7 @@ namespace { std::shared_ptr logger_; std::promise connected_; std::promise disconnected_; - ArmoryThreading::TimedQueue data_; + Armory::Threading::TimedQueue data_; std::promise error_; }; @@ -162,7 +162,7 @@ namespace { const auto kDefaultTimeout = 10000ms; template - T getFeature(ArmoryThreading::TimedQueue &data, std::chrono::milliseconds timeout = kDefaultTimeout) + T getFeature(Armory::Threading::TimedQueue &data, std::chrono::milliseconds timeout = kDefaultTimeout) { return data.pop_front(timeout); } @@ -357,7 +357,7 @@ TEST_F(TestWebSocket, BrokenData) auto packet = CryptoPRNG::generateRandom(rand() % 10000).toBinStr(); ASSERT_TRUE(client_->send(packet)); - ASSERT_THROW(getFeature(serverListener_->data_, 100ms), ArmoryThreading::StackTimedOutException); + ASSERT_THROW(getFeature(serverListener_->data_, 100ms), Armory::Threading::StackTimedOutException); } TEST_F(TestWebSocket, ClientStartsFirst) diff --git a/UnitTests/main.cpp b/UnitTests/main.cpp index 237a150ea..aea4ba7ef 100644 --- a/UnitTests/main.cpp +++ b/UnitTests/main.cpp @@ -26,6 +26,7 @@ #include #include "TestEnv.h" +#include "ArmoryConfig.h" #include "BinaryData.h" #include @@ -109,8 +110,7 @@ int main(int argc, char** argv) qRegisterMetaType(); //::testing::AddGlobalTestEnvironment(new TestEnv(logger)); - - NetworkConfig::selectNetwork(NETWORK_MODE_TESTNET); + Armory::Config::NetworkSettings::selectNetwork(Armory::Config::NETWORK_MODE_TESTNET); QTimer::singleShot(0, [] { rc = RUN_ALL_TESTS(); diff --git a/common b/common index ba5e6be50..4bdd98aa6 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ba5e6be50ab8568845d56fd127bca873ccff8551 +Subproject commit 4bdd98aa6dfecf2c0e2b06d1726da1ee26fc7a3c From 61ae495b8136cfbd035efb8623d9a126c0c7aa13 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 22 Aug 2022 20:35:35 +0300 Subject: [PATCH 138/146] BIP39 wallet import/create support --- BlockSettleSigner/Bip39EntryValidator.cpp | 4 +- BlockSettleSigner/QmlFactory.cpp | 4 + BlockSettleUILib/CMakeLists.txt | 1 + BlockSettleUILib/ImportWalletDialog.cpp | 104 ++++++++++++++++++++++ BlockSettleUILib/ImportWalletDialog.h | 48 ++++++++++ BlockSettleUILib/SeedDialog.cpp | 46 ++++++++-- BlockSettleUILib/SeedDialog.h | 1 + BlockSettleUILib/SeedDialog.ui | 19 ++++ CMakeLists.txt | 5 +- Core/ApiAdapter.cpp | 5 +- Core/ApiAdapter.h | 9 +- Core/ApiJson.h | 4 +- Core/AssetsAdapter.h | 4 +- Core/BsServerAdapter.h | 4 +- Core/MDHistAdapter.h | 4 +- Core/MktDataAdapter.h | 4 +- Core/SettingsAdapter.h | 4 +- Core/SignerAdapter.cpp | 2 + Core/SignerAdapter.h | 4 +- GUI/QtWidgets/MainWindow.cpp | 12 +++ GUI/QtWidgets/MainWindow.h | 1 + GUI/QtWidgets/QtGuiAdapter.cpp | 17 ++++ GUI/QtWidgets/QtGuiAdapter.h | 4 +- common | 2 +- 24 files changed, 267 insertions(+), 45 deletions(-) create mode 100644 BlockSettleUILib/ImportWalletDialog.cpp create mode 100644 BlockSettleUILib/ImportWalletDialog.h diff --git a/BlockSettleSigner/Bip39EntryValidator.cpp b/BlockSettleSigner/Bip39EntryValidator.cpp index b0b1b0c6f..f8e9c1008 100644 --- a/BlockSettleSigner/Bip39EntryValidator.cpp +++ b/BlockSettleSigner/Bip39EntryValidator.cpp @@ -9,7 +9,6 @@ */ #include "Bip39EntryValidator.h" -#include "Bip39.h" #include "QmlFactory.h" Bip39EntryValidator::Bip39EntryValidator(QObject *parent) @@ -33,10 +32,11 @@ QValidator::State Bip39EntryValidator::validate(QString &input, int &pos) const return State::Intermediate; } +#if 0 //FIXME: use another BIP39 implementation if (!validateMnemonic(input.toStdString(), dictionaries_)) { return State::Invalid; } - +#endif return State::Acceptable; } diff --git a/BlockSettleSigner/QmlFactory.cpp b/BlockSettleSigner/QmlFactory.cpp index 2d2b7cb17..84ce9fbc5 100644 --- a/BlockSettleSigner/QmlFactory.cpp +++ b/BlockSettleSigner/QmlFactory.cpp @@ -47,9 +47,13 @@ void QmlFactory::setWalletsManager(const std::shared_ptr +#include +#include "bip39.h" +#include "ImportWalletDialog.h" +#include "UiUtils.h" +#include "Wallets/SyncWallet.h" + +using namespace bs::gui::qt; + +ImportWalletDialog::ImportWalletDialog(const std::string& rootId + , QWidget* parent) + : QDialog(parent) + , ui_(new Ui::SeedDialog()) + , walletId_(rootId) +{ + ui_->setupUi(this); + setWindowTitle(tr("Import wallet")); + ui_->lineEditSeed->setReadOnly(true); + ui_->pushButtonGenSeed->hide(); + connect(ui_->lineEditSeed, &QLineEdit::textChanged, this, &ImportWalletDialog::onDataAvail); + connect(ui_->lineEditWalletName, &QLineEdit::editingFinished, this, &ImportWalletDialog::onDataAvail); + connect(ui_->lineEditWalletDesc, &QLineEdit::editingFinished, this, &ImportWalletDialog::onDataAvail); + connect(ui_->lineEditPass1, &QLineEdit::editingFinished, this, &ImportWalletDialog::onPasswordEdited); + connect(ui_->lineEditPass2, &QLineEdit::editingFinished, this, &ImportWalletDialog::onPasswordEdited); + connect(ui_->textEdit12Words, &QTextEdit::textChanged, this, &ImportWalletDialog::on12WordsChanged); + + okButton_ = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Ok); + if (okButton_) { + connect(okButton_, &QPushButton::clicked, this, &ImportWalletDialog::onClose); + } + okButton_->setEnabled(false); +} + +ImportWalletDialog::~ImportWalletDialog() = default; + +void ImportWalletDialog::onClose() +{ + data_.xpriv = ui_->textEditXPriv->toPlainText().toStdString(); + data_.seed = SecureBinaryData::CreateFromHex(ui_->lineEditSeed->text().toStdString()); + data_.name = ui_->lineEditWalletName->text().toStdString(); + data_.description = ui_->lineEditWalletDesc->text().toStdString(); + data_.password = SecureBinaryData::fromString(ui_->lineEditPass1->text().toStdString()); +} + +void ImportWalletDialog::onDataAvail() +{ + std::cout << "test\n"; + okButton_->setEnabled(!ui_->lineEditWalletName->text().isEmpty() && + (!ui_->lineEditSeed->text().isEmpty() || !ui_->textEditXPriv->toPlainText().isEmpty())); +} + +void ImportWalletDialog::onPasswordEdited() +{ + const auto& pass1 = ui_->lineEditPass1->text().toStdString(); + const auto& pass2 = ui_->lineEditPass2->text().toStdString(); + bool isValid = false; + if (!pass1.empty() && pass2.empty()) { + ui_->labelPass->setText(tr("enter same password in the second line")); + } + else if (pass1.empty() && pass2.empty()) { + ui_->labelPass->setText(tr("type password twice")); + } + else if (pass1 != pass2) { + ui_->labelPass->setText(tr("passwords don't match")); + } + else { + ui_->labelPass->clear(); + isValid = true; + } + okButton_->setEnabled(isValid); +} + +void ImportWalletDialog::on12WordsChanged() +{ + const auto& words = ui_->textEdit12Words->toPlainText().split(QLatin1Char(' ') + , QString::SkipEmptyParts); + BIP39::word_list wordList; + for (const auto& word : words) { + wordList.add(word.toStdString()); + } + if (BIP39::valid_mnemonic(wordList)) { + ui_->labelPass->clear(); + } else { + ui_->labelPass->setText(tr("invalid 12-word seed")); + ui_->lineEditSeed->clear(); + return; + } + + const auto& seed = BIP39::seed_from_mnemonic(wordList); + ui_->lineEditSeed->setText(QString::fromStdString(seed.toHexStr())); + onDataAvail(); +} diff --git a/BlockSettleUILib/ImportWalletDialog.h b/BlockSettleUILib/ImportWalletDialog.h new file mode 100644 index 000000000..afd5644b9 --- /dev/null +++ b/BlockSettleUILib/ImportWalletDialog.h @@ -0,0 +1,48 @@ +/* + +*********************************************************************************** +* Copyright (C) 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef __IMPORT_WALLET_DIALOG_H__ +#define __IMPORT_WALLET_DIALOG_H__ + +#include +#include +#include "SeedDialog.h" +#include "SecureBinaryData.h" + +namespace bs { + namespace gui { + namespace qt { + class ImportWalletDialog : public QDialog + { + Q_OBJECT + + public: + ImportWalletDialog(const std::string& rootId, QWidget* parent = nullptr); + ~ImportWalletDialog() override; + + WalletSeedData getData() const { return data_; } + + private slots: + void onClose(); + void onDataAvail(); + void onPasswordEdited(); + void on12WordsChanged(); + + private: + std::unique_ptr ui_; + std::string walletId_; + QPushButton* okButton_; + WalletSeedData data_; + }; + } + } +} + +#endif // __IMPORT_WALLET_DIALOG_H__ diff --git a/BlockSettleUILib/SeedDialog.cpp b/BlockSettleUILib/SeedDialog.cpp index 008dceac4..ed9f70506 100644 --- a/BlockSettleUILib/SeedDialog.cpp +++ b/BlockSettleUILib/SeedDialog.cpp @@ -12,13 +12,14 @@ #include #include - +#include "bip39.h" #include "SeedDialog.h" #include "UiUtils.h" #include "Wallets/SyncWallet.h" using namespace bs::gui::qt; + SeedDialog::SeedDialog(const std::string& rootId , QWidget* parent) : QDialog(parent) @@ -26,13 +27,16 @@ SeedDialog::SeedDialog(const std::string& rootId , walletId_(rootId) { ui_->setupUi(this); + setWindowTitle(tr("Create new wallet")); + ui_->lineEditSeed->setReadOnly(true); connect(ui_->pushButtonGenSeed, &QPushButton::clicked, this, &SeedDialog::generateSeed); - connect(ui_->lineEditSeed, &QLineEdit::editingFinished, this, &SeedDialog::onDataAvail); + connect(ui_->lineEditSeed, &QLineEdit::textChanged, this, &SeedDialog::onDataAvail); connect(ui_->lineEditWalletName, &QLineEdit::editingFinished, this, &SeedDialog::onDataAvail); connect(ui_->lineEditWalletDesc, &QLineEdit::editingFinished, this, &SeedDialog::onDataAvail); connect(ui_->lineEditPass1, &QLineEdit::editingFinished, this, &SeedDialog::onPasswordEdited); connect(ui_->lineEditPass2, &QLineEdit::editingFinished, this, &SeedDialog::onPasswordEdited); + connect(ui_->textEdit12Words, &QTextEdit::textChanged, this, &SeedDialog::on12WordsChanged); okButton_ = ui_->buttonBox->button(QDialogButtonBox::StandardButton::Ok); if (okButton_) { @@ -45,8 +49,18 @@ SeedDialog::~SeedDialog() = default; void SeedDialog::generateSeed() { - const auto& seed = CryptoPRNG::generateRandom(32).toHexStr(); - ui_->lineEditSeed->setText(QString::fromStdString(seed)); + auto seed = CryptoPRNG::generateRandom(16); + std::vector seedData; + for (int i = 0; i < (int)seed.getSize(); ++i) { + seedData.push_back(seed.getPtr()[i]); + } + const auto& words = BIP39::create_mnemonic(seedData); + ui_->textEdit12Words->blockSignals(true); + ui_->textEdit12Words->setText(QString::fromStdString(words.to_string())); + ui_->textEdit12Words->blockSignals(false); + + seed = BIP39::seed_from_mnemonic(words); + ui_->lineEditSeed->setText(QString::fromStdString(seed.toHexStr())); } void SeedDialog::onClose() @@ -65,7 +79,7 @@ void SeedDialog::onDataAvail() (!ui_->lineEditSeed->text().isEmpty() || !ui_->textEditXPriv->toPlainText().isEmpty())); } -void bs::gui::qt::SeedDialog::onPasswordEdited() +void SeedDialog::onPasswordEdited() { const auto& pass1 = ui_->lineEditPass1->text().toStdString(); const auto& pass2 = ui_->lineEditPass2->text().toStdString(); @@ -86,6 +100,28 @@ void bs::gui::qt::SeedDialog::onPasswordEdited() okButton_->setEnabled(isValid); } +void SeedDialog::on12WordsChanged() +{ + const auto& words = ui_->textEdit12Words->toPlainText().split(QLatin1Char(' ') + , QString::SkipEmptyParts); + BIP39::word_list wordList; + for (const auto& word : words) { + wordList.add(word.toStdString()); + } + if (BIP39::valid_mnemonic(wordList)) { + ui_->labelPass->clear(); + } else { + ui_->labelPass->setText(tr("invalid 12-word seed")); + ui_->lineEditSeed->clear(); + return; + } + + const auto& seed = BIP39::seed_from_mnemonic(wordList); + + ui_->lineEditSeed->setText(QString::fromStdString(seed.toHexStr())); + onDataAvail(); +} + void SeedDialog::showEvent(QShowEvent* event) { QDialog::showEvent(event); diff --git a/BlockSettleUILib/SeedDialog.h b/BlockSettleUILib/SeedDialog.h index 2213b2010..ce4bea45c 100644 --- a/BlockSettleUILib/SeedDialog.h +++ b/BlockSettleUILib/SeedDialog.h @@ -60,6 +60,7 @@ namespace bs { void generateSeed(); void onDataAvail(); void onPasswordEdited(); + void on12WordsChanged(); private: std::unique_ptr ui_; diff --git a/BlockSettleUILib/SeedDialog.ui b/BlockSettleUILib/SeedDialog.ui index 72f9989e9..c88170d7a 100644 --- a/BlockSettleUILib/SeedDialog.ui +++ b/BlockSettleUILib/SeedDialog.ui @@ -293,6 +293,25 @@ + + + + 12 words seed mnemonic representation: + + + + + + + + 17 + + + + false + + + diff --git a/CMakeLists.txt b/CMakeLists.txt index d4a578be5..f4c1f5062 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -407,6 +407,7 @@ SET( BS_NETWORK_INCLUDE_DIR ${TERMINAL_GUI_ROOT}/common/BlocksettleNetworkingLib SET( COMMON_UI_LIB_INCLUDE_DIR ${TERMINAL_GUI_ROOT}/CommonUI ) SET( COMMON_LIB_INCLUDE_DIR ${TERMINAL_GUI_ROOT}/common/CommonLib ) SET( BS_HW_LIB_INCLUDE_DIR ${TERMINAL_GUI_ROOT}/BlockSettleHW ) +SET( BIP39_INCLUDE_DIR ${TERMINAL_GUI_ROOT}/common/WalletsLib/bip39 ) SET(MDB_DIR ${CRYPTO_LIB_DIR}/lmdb/libraries/liblmdb) @@ -832,10 +833,10 @@ ADD_SUBDIRECTORY(common/WalletsLib) ADD_SUBDIRECTORY(common/cppForSwig) ADD_SUBDIRECTORY(common/CommonLib) ADD_SUBDIRECTORY(CommonUI) -ADD_SUBDIRECTORY(BlockSettleHW) +#ADD_SUBDIRECTORY(BlockSettleHW) ADD_SUBDIRECTORY(BlockSettleApp) -ADD_SUBDIRECTORY(BlockSettleSigner) +#ADD_SUBDIRECTORY(BlockSettleSigner) IF(BUILD_TESTS) ADD_SUBDIRECTORY(UnitTests) diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp index 7acfb9b00..5d4118e35 100644 --- a/Core/ApiAdapter.cpp +++ b/Core/ApiAdapter.cpp @@ -67,10 +67,7 @@ class ApiBusGateway : public ApiBusAdapter , userTermBroadcast_(std::make_shared(bs::message::TerminalUsers::BROADCAST)) {} - std::set> supportedReceivers() const override - { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "APIbusGW"; } bool processBroadcast(const bs::message::Envelope& env) override diff --git a/Core/ApiAdapter.h b/Core/ApiAdapter.h index 9887aa9ea..46da55d5e 100644 --- a/Core/ApiAdapter.h +++ b/Core/ApiAdapter.h @@ -64,10 +64,7 @@ class ApiBusGateway; class ApiBusAdapter : public bs::message::Adapter { public: - std::set> supportedReceivers() const override - { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } void setUserId(const bs::message::UserValue userVal) { @@ -94,9 +91,7 @@ class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner return process(env); } - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "API"; } void add(const std::shared_ptr &); diff --git a/Core/ApiJson.h b/Core/ApiJson.h index 46bbad45e..f10527c5e 100644 --- a/Core/ApiJson.h +++ b/Core/ApiJson.h @@ -35,9 +35,7 @@ class ApiJsonAdapter : public ApiBusAdapter, public ServerConnectionListener bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "JSON API"; } protected: // ServerConnectionListener overrides diff --git a/Core/AssetsAdapter.h b/Core/AssetsAdapter.h index f67e397d2..ae88c1905 100644 --- a/Core/AssetsAdapter.h +++ b/Core/AssetsAdapter.h @@ -36,9 +36,7 @@ class AssetsAdapter : public bs::message::Adapter bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "Assets"; } private: // AssetMgr callbacks override diff --git a/Core/BsServerAdapter.h b/Core/BsServerAdapter.h index 0f4351384..27480f787 100644 --- a/Core/BsServerAdapter.h +++ b/Core/BsServerAdapter.h @@ -46,9 +46,7 @@ class BsServerAdapter : public bs::message::Adapter bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "BS Servers"; } private: diff --git a/Core/MDHistAdapter.h b/Core/MDHistAdapter.h index e059f1de2..3737b248e 100644 --- a/Core/MDHistAdapter.h +++ b/Core/MDHistAdapter.h @@ -26,9 +26,7 @@ class MDHistAdapter : public bs::message::Adapter bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "MDHistory"; } private: diff --git a/Core/MktDataAdapter.h b/Core/MktDataAdapter.h index d958fd223..e2bee9398 100644 --- a/Core/MktDataAdapter.h +++ b/Core/MktDataAdapter.h @@ -28,9 +28,7 @@ class MktDataAdapter : public bs::message::Adapter, public MDCallbackTarget bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "MktData"; } protected: //MD callbacks override diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index ca976ade5..4abc9ab6a 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -61,9 +61,7 @@ class SettingsAdapter : public bs::message::Adapter bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "Settings"; } std::shared_ptr logManager() const { return logMgr_; } diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 717876e60..53d84175a 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -134,6 +134,8 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processDialogRequest(env, request.dialog_request()); case SignerMessage::kCreateWallet: return processCreateWallet(env, request.create_wallet()); + case SignerMessage::kImportWallet: + return processCreateWallet(env, request.import_wallet()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 01c3c2355..93500c6a2 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -58,9 +58,7 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "Signer"; } std::unique_ptr createClient() const; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index f1c667f0e..a9c109629 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -35,6 +35,7 @@ #include "InfoDialogs/AboutDialog.h" #include "InfoDialogs/StartupDialog.h" #include "InfoDialogs/SupportDialog.h" +#include "ImportWalletDialog.h" #include "LoginWindow.h" #include "NotificationCenter.h" #include "Settings/ConfigDialog.h" @@ -843,6 +844,17 @@ bs::gui::WalletSeedData MainWindow::getWalletSeed(const std::string& rootId) con return {}; } +bs::gui::WalletSeedData MainWindow::importWallet(const std::string& rootId) const +{ + auto seedDialog = new bs::gui::qt::ImportWalletDialog(rootId, (QWidget*)this); + const int rc = seedDialog->exec(); + seedDialog->deleteLater(); + if (rc == QDialog::Accepted) { + return seedDialog->getData(); + } + return {}; +} + void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index ed821a339..e964b236f 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -121,6 +121,7 @@ namespace bs { , const std::vector&); bs::gui::WalletSeedData getWalletSeed(const std::string& rootId) const; + bs::gui::WalletSeedData importWallet(const std::string& rootId) const; public slots: void onReactivate(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 8fde71bbe..b0554c708 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -1251,6 +1251,23 @@ void QtGuiAdapter::onNeedWalletDialog(bs::signer::ui::GeneralDialogType dlgType }); } break; + case bs::signer::ui::GeneralDialogType::ImportWallet: + if (mainWindow_) { + QMetaObject::invokeMethod(mainWindow_, [this, rootId] { + const auto& seedData = mainWindow_->importWallet(rootId); + if (!seedData.empty()) { + SignerMessage msg; + auto msgReq = msg.mutable_import_wallet(); + msgReq->set_name(seedData.name); + msgReq->set_description(seedData.description); + msgReq->set_xpriv_key(seedData.xpriv); + msgReq->set_seed(seedData.seed.toBinStr()); + msgReq->set_password(seedData.password.toBinStr()); + pushRequest(user_, userSigner_, msg.SerializeAsString()); + } + }); + } + break; default: logger_->debug("[{}] {} ({})", __func__, (int)dlgType, rootId); break; diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 0ffa70fda..1c1d5e48a 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -80,9 +80,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool process(const bs::message::Envelope &) override; bool processBroadcast(const bs::message::Envelope&) override; - std::set> supportedReceivers() const override { - return { user_ }; - } + Users supportedReceivers() const override { return { user_ }; } std::string name() const override { return "QtGUI"; } void run(int &argc, char **argv) override; diff --git a/common b/common index 4bdd98aa6..eaa332c8a 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 4bdd98aa6dfecf2c0e2b06d1726da1ee26fc7a3c +Subproject commit eaa332c8a0ef1e89e0a6dc14c9158f59008bc54f From ef28a8a3abfe18cb80a47df5a36226db503d60d8 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 23 Aug 2022 16:34:38 +0300 Subject: [PATCH 139/146] [LEV-2510] Armory connection refactor --- BlockSettleApp/main.cpp | 11 +++-- Core/ApiAdapter.cpp | 20 +++++++- Core/ApiAdapter.h | 11 ++--- Core/TerminalMessage.cpp | 83 +++++++++++++++++++++++++++------- Core/TerminalMessage.h | 6 ++- GUI/QtWidgets/QtGuiAdapter.cpp | 2 + common | 2 +- 7 files changed, 103 insertions(+), 32 deletions(-) diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index cc59b06ba..847fb5ea2 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -164,18 +164,19 @@ int main(int argc, char** argv) const auto &apiAdapter = std::make_shared(logMgr->logger("API")); const auto &guiAdapter = std::make_shared(logMgr->logger("ui")); + //const auto& guiAdapter = std::make_shared(logMgr->logger("ui")); apiAdapter->add(guiAdapter); apiAdapter->add(std::make_shared(logMgr->logger("json"))); inprocBus.addAdapter(apiAdapter); const auto &signAdapter = std::make_shared(logMgr->logger()); - inprocBus.addAdapter(signAdapter); + inprocBus.addAdapterWithQueue(signAdapter, "signer"); const auto& userBlockchain = bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain); const auto& userWallets = bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets); inprocBus.addAdapter(std::make_shared(logMgr->logger())); - inprocBus.addAdapter(std::make_shared(logMgr->logger() - , userWallets, signAdapter->createClient(), userBlockchain)); + inprocBus.addAdapterWithQueue(std::make_shared(logMgr->logger() + , userWallets, signAdapter->createClient(), userBlockchain), "wallets"); inprocBus.addAdapter(std::make_shared(logMgr->logger("bscon"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("match"))); @@ -183,8 +184,8 @@ int main(int argc, char** argv) //inprocBus.addAdapter(std::make_shared(logMgr->logger("md"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("mdh"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("chat"))); - inprocBus.addAdapter(std::make_shared(logMgr->logger() - , userBlockchain)); + inprocBus.addAdapterWithQueue(std::make_shared(logMgr->logger() + , userBlockchain), "blkchain_conn"); if (!inprocBus.run(argc, argv)) { logMgr->logger()->error("No runnable adapter found on main inproc bus"); diff --git a/Core/ApiAdapter.cpp b/Core/ApiAdapter.cpp index 5d4118e35..5a8f9e04d 100644 --- a/Core/ApiAdapter.cpp +++ b/Core/ApiAdapter.cpp @@ -84,6 +84,7 @@ class ApiBusGateway : public ApiBusAdapter if (std::dynamic_pointer_cast(env.receiver)) { if (parent_->pushFill(envCopy)) { if (env.isRequest()) { + std::unique_lock lock(mtxIdMap_); idMap_[envCopy.foreignId()] = { env.foreignId(), env.sender }; } return true; @@ -98,6 +99,7 @@ class ApiBusGateway : public ApiBusAdapter return parent_->pushFill(envCopy); } else { + std::unique_lock lock(mtxIdMap_); const auto &itId = idMap_.find(env.responseId()); if (itId == idMap_.end()) { envCopy.receiver = userTermBroadcast_; @@ -124,6 +126,7 @@ class ApiBusGateway : public ApiBusAdapter envCopy.setId(0); envCopy.receiver.reset(); if (!env.isRequest() && env.receiver) { + std::unique_lock lock(mtxIdMap_); const auto& itIdMap = idMap_.find(env.responseId()); if (itIdMap != idMap_.end()) { envCopy = bs::message::Envelope::makeResponse(env.sender @@ -140,6 +143,7 @@ class ApiBusGateway : public ApiBusAdapter } bool rc = pushFill(envCopy); if (rc && env.isRequest()) { + std::unique_lock lock(mtxIdMap_); idMap_[envCopy.foreignId()] = { env.foreignId(), env.sender }; } return rc; @@ -155,6 +159,7 @@ class ApiBusGateway : public ApiBusAdapter uint64_t id; std::shared_ptr requester; }; + std::mutex mtxIdMap_; std::map idMap_; }; @@ -163,6 +168,7 @@ ApiAdapter::ApiAdapter(const std::shared_ptr &logger) : logger_(logger) , user_(std::make_shared(bs::message::TerminalUsers::API)) { + fallbackUser_ = std::make_shared(bs::message::TerminalUsers::Unknown); // RelayAdapter member apiBus_ = std::make_shared(logger); gwAdapter_ = std::make_shared(logger, this); apiBus_->addAdapter(gwAdapter_); @@ -200,5 +206,17 @@ void ApiAdapter::add(const std::shared_ptr &adapter) bool ApiAdapter::process(const bs::message::Envelope &env) { - return gwAdapter_->pushToApiBus(env); + RelayAdapter::process(env); + if (env.receiver->value() == user_->value()) { + return gwAdapter_->pushToApiBus(env); + } + return true; +} + +bool ApiAdapter::processBroadcast(const bs::message::Envelope& env) +{ + if (RelayAdapter::processBroadcast(env)) { + return gwAdapter_->pushToApiBus(env); + } + return false; } diff --git a/Core/ApiAdapter.h b/Core/ApiAdapter.h index 46da55d5e..f95782014 100644 --- a/Core/ApiAdapter.h +++ b/Core/ApiAdapter.h @@ -77,7 +77,7 @@ class ApiBusAdapter : public bs::message::Adapter }; -class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner +class ApiAdapter : public bs::message::RelayAdapter, public bs::MainLoopRuner { friend class ApiBusGateway; public: @@ -85,13 +85,9 @@ class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner ~ApiAdapter() override = default; bool process(const bs::message::Envelope &) override; + bool processBroadcast(const bs::message::Envelope& env) override; - bool processBroadcast(const bs::message::Envelope& env) override - { - return process(env); - } - - Users supportedReceivers() const override { return { user_ }; } + Users supportedReceivers() const override { return { user_, fallbackUser_ }; } std::string name() const override { return "API"; } void add(const std::shared_ptr &); @@ -107,5 +103,4 @@ class ApiAdapter : public bs::message::Adapter, public bs::MainLoopRuner bs::message::UserValue nextApiUser_{ 0 }; }; - #endif // API_ADAPTER_H diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index 371fcf1f6..23b4bb8af 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -19,9 +19,9 @@ using namespace BlockSettle::Common; static const std::map kTerminalUsersMapping = { { static_cast(TerminalUsers::BROADCAST), "Broadcast" }, - { static_cast(TerminalUsers::System), "System " }, - { static_cast(TerminalUsers::Signer), "Signer " }, - { static_cast(TerminalUsers::API), "API " }, + { static_cast(TerminalUsers::System), "System" }, + { static_cast(TerminalUsers::Signer), "Signer" }, + { static_cast(TerminalUsers::API), "API" }, { static_cast(TerminalUsers::Settings), "Settings" }, { static_cast(TerminalUsers::BsServer), "BsServer" }, { static_cast(TerminalUsers::Matching), "Matching" }, @@ -32,8 +32,9 @@ static const std::map kTerminalUsersMapping = { { static_cast(TerminalUsers::Wallets), "Wallets" }, { static_cast(TerminalUsers::OnChainTracker), "OnChainTrk" }, { static_cast(TerminalUsers::Settlement), "Settlement" }, - { static_cast(TerminalUsers::Chat), "Chat " } + { static_cast(TerminalUsers::Chat), "Chat" } }; +static const std::string kMainQueue = "Main"; std::string UserTerminal::name() const { @@ -45,8 +46,9 @@ std::string UserTerminal::name() const TerminalInprocBus::TerminalInprocBus(const std::shared_ptr &logger) : logger_(logger) -{ // we can create multiple queues if needed and distribute them on adapters - queue_ = std::make_shared(std::make_shared(logger), logger, "Main"); +{ + queues_[kMainQueue] = std::make_shared(std::make_shared(logger) + , logger, kMainQueue); } TerminalInprocBus::~TerminalInprocBus() @@ -56,35 +58,84 @@ TerminalInprocBus::~TerminalInprocBus() void TerminalInprocBus::addAdapter(const std::shared_ptr &adapter) { - queue_->bindAdapter(adapter); - adapter->setQueue(queue_); + const auto& queue = queues_.at(kMainQueue); + queue->bindAdapter(adapter); + adapter->setQueue(queue); const auto &runner = std::dynamic_pointer_cast(adapter); if (runner) { + logger_->info("[{}] set runnable adapter {}", __func__, adapter->name()); runnableAdapter_ = runner; } - static const auto &adminUser = UserTerminal::create(TerminalUsers::System); - for (const auto &user : adapter->supportedReceivers()) { - AdministrativeMessage msg; - msg.set_component_created(user->value()); - auto env = bs::message::Envelope::makeBroadcast(adminUser, msg.SerializeAsString()); - queue_->pushFill(env); + const auto& relay = std::dynamic_pointer_cast(adapter); + if (relay) { + logger_->info("[{}] set relay adapter {}", __func__, adapter->name()); + relayAdapter_ = relay; + } + + sendLoading(adapter, queue); +} + +void bs::message::TerminalInprocBus::addAdapterWithQueue(const std::shared_ptr& adapter + , const std::string& qName) +{ + if (qName == kMainQueue) { + throw std::runtime_error("main queue name reused"); + } + std::shared_ptr queue; + const auto& itQueue = queues_.find(qName); + if (itQueue == queues_.end()) { + queue = std::make_shared(std::make_shared(logger_) + , logger_, qName); + queues_[qName] = queue; + } + else { + queue = itQueue->second; } + + queue->bindAdapter(adapter); + adapter->setQueue(queue); + + if (relayAdapter_) { + queue->bindAdapter(relayAdapter_); + relayAdapter_->setQueue(queue); + logger_->debug("[{}] relay {} bound to {}", __func__, relayAdapter_->name(), qName); + } + else { + logger_->warn("[{}] no relay adapter attached to {}", __func__, qName); + } + sendLoading(adapter, queues_.at(kMainQueue)); } void TerminalInprocBus::start() { + logger_->debug("[TerminalInprocBus::start]"); static const auto &adminUser = UserTerminal::create(TerminalUsers::System); AdministrativeMessage msg; msg.mutable_start(); auto env = bs::message::Envelope::makeBroadcast(adminUser, msg.SerializeAsString()); - queue_->pushFill(env); + queues_.at(kMainQueue)->pushFill(env); +} + +void bs::message::TerminalInprocBus::sendLoading(const std::shared_ptr& adapter + , const std::shared_ptr& queue) +{ + static const auto& adminUser = UserTerminal::create(TerminalUsers::System); + for (const auto& user : adapter->supportedReceivers()) { + AdministrativeMessage msg; + msg.set_component_created(user->value()); + auto env = bs::message::Envelope::makeBroadcast(adminUser, msg.SerializeAsString()); + queue->pushFill(env); + } } void TerminalInprocBus::shutdown() { runnableAdapter_.reset(); - queue_->terminate(); + for (const auto& q : queues_) { + q.second->terminate(); + } + relayAdapter_.reset(); } bool TerminalInprocBus::run(int &argc, char **argv) diff --git a/Core/TerminalMessage.h b/Core/TerminalMessage.h index e0d7c7eb0..206c373cf 100644 --- a/Core/TerminalMessage.h +++ b/Core/TerminalMessage.h @@ -86,17 +86,21 @@ namespace bs { ~TerminalInprocBus() override; void addAdapter(const std::shared_ptr &) override; + void addAdapterWithQueue(const std::shared_ptr& + , const std::string& qName); void shutdown(); bool run(int &argc, char **argv); private: void start(); + void sendLoading(const std::shared_ptr&, const std::shared_ptr&); private: std::shared_ptr logger_; - std::shared_ptr queue_; + std::map> queues_; std::shared_ptr runnableAdapter_; + std::shared_ptr relayAdapter_; }; } // namespace message diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index b0554c708..7fe6e55f6 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -235,7 +235,9 @@ void QtGuiAdapter::run(int &argc, char **argv) updateSplashProgress(); splashScreen_->show(); + logger_->debug("[QtGuiAdapter::run] creating main window"); mainWindow_ = new bs::gui::qt::MainWindow(logger_, queue_, user_); + logger_->debug("[QtGuiAdapter::run] start main window connections"); makeMainWinConnections(); updateStates(); diff --git a/common b/common index eaa332c8a..911d124f4 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit eaa332c8a0ef1e89e0a6dc14c9158f59008bc54f +Subproject commit 911d124f4c3ca214fd3f09fe19ccefe5faade8b7 From 5d35fc3d8cef54d49c2475efbad93088f9cead19 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 24 Aug 2022 00:27:53 +0300 Subject: [PATCH 140/146] [LEV-2511] Delete wallet --- BlockSettleUILib/CommonMessageBoxDialog.h | 2 +- .../RootWalletPropertiesDialog.cpp | 7 +++++ .../RootWalletPropertiesDialog.h | 1 + BlockSettleUILib/WalletsWidget.cpp | 3 ++ Core/SignerAdapter.cpp | 17 +++++++++++ Core/SignerAdapter.h | 1 + GUI/QtWidgets/MainWindow.cpp | 11 +++++++ GUI/QtWidgets/MainWindow.h | 1 + GUI/QtWidgets/QtGuiAdapter.cpp | 30 +++++++++++++++++++ common | 2 +- 10 files changed, 73 insertions(+), 2 deletions(-) diff --git a/BlockSettleUILib/CommonMessageBoxDialog.h b/BlockSettleUILib/CommonMessageBoxDialog.h index c086f8240..e255cdf8e 100644 --- a/BlockSettleUILib/CommonMessageBoxDialog.h +++ b/BlockSettleUILib/CommonMessageBoxDialog.h @@ -25,4 +25,4 @@ Q_OBJECT void UpdateSize(); }; -#endif // __COMMON_MESSAGE_BOX_DIALOG_H__ \ No newline at end of file +#endif // __COMMON_MESSAGE_BOX_DIALOG_H__ diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 16ede7477..5599d9357 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -262,6 +262,13 @@ void RootWalletPropertiesDialog::onSpendableUTXOs() // ui_->labelUTXOs->setText(QString::number(sizeUTXOs)); } +void RootWalletPropertiesDialog::walletDeleted(const std::string& rootId) +{ + if (walletInfo_.rootId().toStdString() == rootId) { + close(); + } +} + void RootWalletPropertiesDialog::onWalletBalances(const bs::sync::WalletBalanceData&) { // ui_->labelAddressesUsed->setText(QString::number(wallet->getUsedAddressCount())); diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h index 5f885d0c5..97ecdd2ab 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.h @@ -53,6 +53,7 @@ Q_OBJECT void onHDWalletDetails(const bs::sync::HDWalletData&); void onWalletBalances(const bs::sync::WalletBalanceData&); void onSpendableUTXOs(); + void walletDeleted(const std::string& rootId); signals: void startRescan(std::string walletId); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 1ba841999..78e72b800 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -311,6 +311,9 @@ void WalletsWidget::onHDWallet(const bs::sync::WalletInfo &wi) void WalletsWidget::onWalletDeleted(const bs::sync::WalletInfo& wi) { + if (rootDlg_) { + rootDlg_->walletDeleted(*wi.ids.cbegin()); + } walletsModel_->onWalletDeleted(wi); wallets_.erase(wi); } diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index 53d84175a..de1f73b25 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -136,6 +136,8 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env return processCreateWallet(env, request.create_wallet()); case SignerMessage::kImportWallet: return processCreateWallet(env, request.import_wallet()); + case SignerMessage::kDeleteWallet: + return processDeleteWallet(env, request.delete_wallet()); default: logger_->warn("[{}] unknown signer request: {}", __func__, request.data_case()); break; @@ -592,3 +594,18 @@ bool SignerAdapter::processCreateWallet(const bs::message::Envelope& env pushResponse(user_, env, msg.SerializeAsString()); return true; } + +bool SignerAdapter::processDeleteWallet(const bs::message::Envelope& env + , const std::string& rootId) +{ + SignerMessage msg; + const auto& hdWallet = walletsMgr_->getHDWalletById(rootId); + if (walletsMgr_->deleteWalletFile(hdWallet)) { + msg.set_wallet_deleted(rootId); + } + else { + msg.set_wallet_deleted(""); + } + pushResponse(user_, env, msg.SerializeAsString()); + return true; +} diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 93500c6a2..3ed09dc52 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -104,6 +104,7 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget , const BlockSettle::Common::SignerMessage_DialogRequest&); bool processCreateWallet(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_CreateWalletRequest&); + bool processDeleteWallet(const bs::message::Envelope&, const std::string& rootId); private: std::shared_ptr logger_; diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index a9c109629..6e25b3033 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -855,6 +855,17 @@ bs::gui::WalletSeedData MainWindow::importWallet(const std::string& rootId) cons return {}; } +bool bs::gui::qt::MainWindow::deleteWallet(const std::string& rootId, const std::string& name) const +{ + BSMessageBox mBox(BSMessageBox::question, tr("Wallet delete") + , tr("Are you sure you want to delete wallet %1 with id %2?") + .arg(QString::fromStdString(name)).arg(QString::fromStdString(rootId)) + , (QWidget*)this); + mBox.setConfirmButtonText(tr("Yes")); + mBox.setCancelButtonText(tr("No")); + return (mBox.exec() == QDialog::Accepted); +} + void MainWindow::showRunInBackgroundMessage() { sysTrayIcon_->showMessage(tr("BlockSettle is running") diff --git a/GUI/QtWidgets/MainWindow.h b/GUI/QtWidgets/MainWindow.h index e964b236f..f2ff40c34 100644 --- a/GUI/QtWidgets/MainWindow.h +++ b/GUI/QtWidgets/MainWindow.h @@ -122,6 +122,7 @@ namespace bs { bs::gui::WalletSeedData getWalletSeed(const std::string& rootId) const; bs::gui::WalletSeedData importWallet(const std::string& rootId) const; + bool deleteWallet(const std::string& rootId, const std::string& name) const; public slots: void onReactivate(); diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 7fe6e55f6..ab6af696c 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -544,6 +544,23 @@ bool QtGuiAdapter::processSigner(const Envelope &env) break; case SignerMessage::kSignTxResponse: return processSignTX(msg.sign_tx_response()); + case SignerMessage::kWalletDeleted: + if (mainWindow_) { + const auto& itWallet = hdWallets_.find(msg.wallet_deleted()); + QMetaObject::invokeMethod(mainWindow_, [this, itWallet, rootId=msg.wallet_deleted()] { + bs::sync::WalletInfo wi; + if (itWallet == hdWallets_.end()) { + wi.ids.push_back(rootId); + } else { + wi = itWallet->second; + } + mainWindow_->onWalletDeleted(wi); + if (itWallet != hdWallets_.end()) { + hdWallets_.erase(itWallet); + } + }); + } + break; default: break; } return true; @@ -1270,6 +1287,19 @@ void QtGuiAdapter::onNeedWalletDialog(bs::signer::ui::GeneralDialogType dlgType }); } break; + case bs::signer::ui::GeneralDialogType::DeleteWallet: + if (mainWindow_) { + const auto& itWallet = hdWallets_.find(rootId); + const std::string walletName = (itWallet == hdWallets_.end()) ? "" : itWallet->second.name; + QMetaObject::invokeMethod(mainWindow_, [this, rootId, walletName] { + if (mainWindow_->deleteWallet(rootId, walletName)) { + SignerMessage msg; + msg.set_delete_wallet(rootId); + pushRequest(user_, userSigner_, msg.SerializeAsString()); + } + }); + } + break; default: logger_->debug("[{}] {} ({})", __func__, (int)dlgType, rootId); break; diff --git a/common b/common index 911d124f4..0d9e29b74 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 911d124f4c3ca214fd3f09fe19ccefe5faade8b7 +Subproject commit 0d9e29b7489c8507eb5a5eb3d92a4291451d49fe From af0de2afe69fa7731f82a3d014237e9d25bd5f53 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 24 Aug 2022 10:55:24 +0300 Subject: [PATCH 141/146] fixup --- Core/SignerAdapter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index de1f73b25..f60ad8869 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -600,7 +600,7 @@ bool SignerAdapter::processDeleteWallet(const bs::message::Envelope& env { SignerMessage msg; const auto& hdWallet = walletsMgr_->getHDWalletById(rootId); - if (walletsMgr_->deleteWalletFile(hdWallet)) { + if (hdWallet && walletsMgr_->deleteWalletFile(hdWallet)) { msg.set_wallet_deleted(rootId); } else { From 45f8fbdee63be795d2f7fb5bd6dbcc99611ba5b0 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Wed, 24 Aug 2022 21:10:11 +0300 Subject: [PATCH 142/146] [LEV-2513] wallet/address balance update fixes --- BlockSettleUILib/AddressListModel.cpp | 25 --- BlockSettleUILib/AddressListModel.h | 2 - .../RootWalletPropertiesDialog.cpp | 3 +- BlockSettleUILib/SelectAddressDialog.cpp | 24 +-- BlockSettleUILib/SelectAddressDialog.h | 6 - BlockSettleUILib/WalletsViewModel.cpp | 146 +++--------------- BlockSettleUILib/WalletsViewModel.h | 7 +- BlockSettleUILib/WalletsWidget.cpp | 5 +- common | 2 +- 9 files changed, 31 insertions(+), 189 deletions(-) diff --git a/BlockSettleUILib/AddressListModel.cpp b/BlockSettleUILib/AddressListModel.cpp index f505a9dad..fd366c0d8 100644 --- a/BlockSettleUILib/AddressListModel.cpp +++ b/BlockSettleUILib/AddressListModel.cpp @@ -55,25 +55,6 @@ bool AddressListModel::AddressRow::operator==(const AddressRow& other) const isExternal == other.isExternal; } -AddressListModel::AddressListModel(const std::shared_ptr &walletsMgr - , QObject* parent, AddressType addrType) - : QAbstractTableModel(parent) - , walletsMgr_(walletsMgr) - , addrType_(addrType) - , processing_(false) -{ - if (walletsMgr_) { - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletsReady, this - , &AddressListModel::updateWallets); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletChanged, this - , &AddressListModel::updateWallets); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::blockchainEvent, this - , &AddressListModel::updateWallets); - connect(walletsMgr_.get(), &bs::sync::WalletsManager::walletBalanceUpdated - , this, &AddressListModel::updateWallets); - } -} - AddressListModel::AddressListModel(QObject* parent, AddressType addrType) : QAbstractTableModel(parent) , addrType_(addrType) @@ -282,9 +263,6 @@ void AddressListModel::onAddressComments(const std::string & void AddressListModel::onAddressBalances(const std::string &walletId , const std::vector &balances) { - if (balances.empty()) { - return; - } const auto &lbdSaveBalToPool = [this, walletId, balances] { auto &walletBal = pooledBalances_[walletId]; @@ -318,9 +296,6 @@ void AddressListModel::onAddressBalances(const std::string &walletId addressRows_[itAddr->second].balance = bal.balTotal; addressRows_[itAddr->second].transactionCount = bal.txn; } - if (!nbFound) { - return; - } for (auto &addrRow : addressRows_) { if ((addrRow.balance > 0) || (addrRow.transactionCount > 0)) { continue; diff --git a/BlockSettleUILib/AddressListModel.h b/BlockSettleUILib/AddressListModel.h index 67ca01221..585f10b16 100644 --- a/BlockSettleUILib/AddressListModel.h +++ b/BlockSettleUILib/AddressListModel.h @@ -89,8 +89,6 @@ class AddressListModel : public QAbstractTableModel typedef std::vector Wallets; - [[deprecated]] AddressListModel(const std::shared_ptr &, QObject* parent - , AddressType addrType = AddressType::All); AddressListModel(QObject* parent, AddressType addrType = AddressType::All); ~AddressListModel() noexcept = default; diff --git a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp index 5599d9357..0a64d5a77 100644 --- a/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp +++ b/BlockSettleUILib/ManageEncryption/RootWalletPropertiesDialog.cpp @@ -151,8 +151,7 @@ static inline QString encTypeToString(bs::wallet::EncryptionType enc) case bs::wallet::EncryptionType::Hardware : return QObject::tr("Hardware Security Module"); }; - - //no default entry in switch statment nor default return value + return QObject::tr("Unknown"); } void RootWalletPropertiesDialog::onHDWalletInfo(unsigned int id, const bs::hd::WalletInfo &walletInfo) diff --git a/BlockSettleUILib/SelectAddressDialog.cpp b/BlockSettleUILib/SelectAddressDialog.cpp index 718ea8dc4..a4558b971 100644 --- a/BlockSettleUILib/SelectAddressDialog.cpp +++ b/BlockSettleUILib/SelectAddressDialog.cpp @@ -16,28 +16,6 @@ #include "Wallets/SyncWalletsManager.h" -SelectAddressDialog::SelectAddressDialog(const std::shared_ptr &walletsManager - , const std::shared_ptr& wallet - , QWidget* parent, AddressListModel::AddressType addrType) - : QDialog(parent) - , ui_(new Ui::SelectAddressDialog) - , wallets_({ wallet }) - , walletsMgr_(walletsManager) - , addrType_(addrType) -{ - init(); -} - -SelectAddressDialog::SelectAddressDialog(const std::shared_ptr &group - , QWidget* parent, AddressListModel::AddressType addrType) - : QDialog(parent) - , ui_(new Ui::SelectAddressDialog) - , wallets_(group->getAllLeaves()) - , addrType_(addrType) -{ - init(); -} - SelectAddressDialog::SelectAddressDialog(QWidget* parent , AddressListModel::AddressType addrType) : QDialog(parent) @@ -89,6 +67,7 @@ void SelectAddressDialog::onAddressBalances(const std::string& walletId model_->onAddressBalances(walletId, addrBal); } +#if 0 void SelectAddressDialog::init() { ui_->setupUi(this); @@ -112,6 +91,7 @@ void SelectAddressDialog::init() onSelectionChanged(); } +#endif //0 bs::Address SelectAddressDialog::getAddress(const QModelIndex& index) const { diff --git a/BlockSettleUILib/SelectAddressDialog.h b/BlockSettleUILib/SelectAddressDialog.h index aee446f69..1367247c8 100644 --- a/BlockSettleUILib/SelectAddressDialog.h +++ b/BlockSettleUILib/SelectAddressDialog.h @@ -35,11 +35,6 @@ class SelectAddressDialog : public QDialog Q_OBJECT public: - [[deprecated]] SelectAddressDialog(const std::shared_ptr & - , const std::shared_ptr &, QWidget* parent = nullptr - , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); - [[deprecated]] SelectAddressDialog(const std::shared_ptr &, QWidget* parent = nullptr - , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); SelectAddressDialog(QWidget* parent , AddressListModel::AddressType addrType = AddressListModel::AddressType::All); ~SelectAddressDialog() override; @@ -64,7 +59,6 @@ public slots: void onDoubleClicked(const QModelIndex& index); private: - [[deprecated]] void init(); bs::Address getAddress(const QModelIndex& index) const; private: diff --git a/BlockSettleUILib/WalletsViewModel.cpp b/BlockSettleUILib/WalletsViewModel.cpp index cf1de945f..ee0a9b558 100644 --- a/BlockSettleUILib/WalletsViewModel.cpp +++ b/BlockSettleUILib/WalletsViewModel.cpp @@ -154,27 +154,27 @@ class WalletRootNode : public WalletNode return ret; } - BTCNumericTypes::balance_type getBalanceTotal() const { return balTotal_; } - BTCNumericTypes::balance_type getBalanceUnconf() const { return balUnconf_; } - BTCNumericTypes::balance_type getBalanceSpend() const { return balSpend_; } - size_t getNbUsedAddresses() const { return nbAddr_; } + BTCNumericTypes::balance_type getBalanceTotal() const override { return balTotal_; } + BTCNumericTypes::balance_type getBalanceUnconf() const override { return balUnconf_; } + BTCNumericTypes::balance_type getBalanceSpend() const override { return balSpend_; } + size_t getNbUsedAddresses() const override { return nbAddr_; } bs::sync::WalletInfo hdWallet() const override { return hdWallet_; } - void updateCounters(WalletRootNode *node) { - if (!node) { - return; - } - if (node->getBalanceTotal() > 0) { - balTotal_.store(balTotal_.load() + node->getBalanceTotal()); - } - if (node->getBalanceUnconf() > 0) { - balUnconf_.store(balUnconf_.load() + node->getBalanceUnconf()); - } - if (node->getBalanceSpend() > 0) { - balSpend_.store(balSpend_.load() + node->getBalanceSpend()); - } - if (type() != Type::GroupCC) { - nbAddr_ += node->getNbUsedAddresses(); + void updateCounters() + { + balTotal_ = 0; + balUnconf_ = 0; + balSpend_ = 0; + nbAddr_ = 0; + + for (const auto& child : children_) { + balTotal_.store(balTotal_.load() + child->getBalanceTotal()); + balUnconf_.store(balUnconf_.load() + child->getBalanceUnconf()); + balSpend_.store(balSpend_.load() + child->getBalanceSpend()); + + if (type() != Type::GroupCC) { + nbAddr_ += child->getNbUsedAddresses(); + } } } @@ -314,7 +314,7 @@ class WalletGroupNode : public WalletRootNode const auto leafNode = new WalletLeafNode(viewModel_, leaf, hdWallet_, nbChildren(), this); leafNode->setBalances(wallet); add(leafNode); - updateCounters(leafNode); + updateCounters(); wallets_.push_back(leaf); } @@ -690,111 +690,7 @@ void WalletsViewModel::onWalletBalances(const bs::sync::WalletBalanceData &wbd) if (!groupNode) { return; } - groupNode->updateCounters(leafNode); + groupNode->updateCounters(); emit dataChanged(createIndex(groupNode->row(), (int)WalletColumns::ColumnSpendableBalance, static_cast(groupNode->parent())) , createIndex(leafNode->row(), (int)WalletColumns::ColumnNbAddresses, static_cast(groupNode->parent()))); } - -void WalletsViewModel::LoadWallets(bool keepSelection) -{ - const auto treeView = qobject_cast(QObject::parent()); - std::string selectedWalletId; - if (keepSelection && (treeView != nullptr)) { - selectedWalletId = "empty"; - const auto sel = treeView->selectionModel()->selectedRows(); - if (!sel.empty()) { - const auto fltModel = qobject_cast(treeView->model()); - const auto index = fltModel ? fltModel->mapToSource(sel[0]) : sel[0]; - const auto node = getNode(index); - if (node != nullptr) { - const auto &wallets = node->wallets(); - if (wallets.size() == 1) { - selectedWalletId = *wallets[0].ids.cbegin(); - } - } - } - } - - beginResetModel(); - rootNode_->clear(); - for (const auto &hdWallet : walletsManager_->hdWallets()) { - if (!hdWallet) { - continue; - } - const auto hdNode = new WalletRootNode(this, bs::sync::WalletInfo::fromWallet(hdWallet) - , hdWallet->name(), hdWallet->description() - , getHDWalletType(hdWallet, walletsManager_), rootNode_->nbChildren(), rootNode_.get()); - rootNode_->add(hdNode); - - // filter groups - // don't display Settlement - for (const auto &group : hdWallet->getGroups()) { - if (group->type() == bs::core::wallet::Type::Settlement) { - continue; - } - auto groupNode = hdNode->addGroup(group->type(), group->name(), group->description()); - if (groupNode) { - for (const auto &leaf : group->getLeaves()) { - groupNode->addLeaf(bs::sync::WalletInfo::fromLeaf(leaf), leaf); - } - } - } - /*TODO: if (signContainer_) { - if (signContainer_->isOffline()) { - hdNode->setState(WalletNode::State::Offline); - } - else if (hdWallet->isHardwareWallet()) { - hdNode->setState(WalletNode::State::Hardware); - } - else if (signContainer_->isWalletOffline(hdWallet->walletId())) { - hdNode->setState(WalletNode::State::Offline); - } - else if (hdWallet->isPrimary()) { - hdNode->setState(WalletNode::State::Primary); - } else { - hdNode->setState(WalletNode::State::Full); - } - }*/ - } - -/* const auto stmtWallet = walletsManager_->getSettlementWallet(); - if (!showRegularWallets() && (stmtWallet != nullptr)) { - const auto stmtNode = new WalletLeafNode(this, stmtWallet, rootNode_->nbChildren(), rootNode_.get()); - rootNode_->add(stmtNode); - }*/ //TODO: add later if decided - endResetModel(); - - QModelIndexList selection; - if (selectedWalletId.empty()) { - selectedWalletId = defaultWalletId_; - } - auto node = rootNode_->findByWalletId(selectedWalletId); - if (node != nullptr) { - selection.push_back(createIndex(node->row(), 0, static_cast(node))); - } - else if(rootNode_->hasChildren()) { - node = rootNode_->child(0); - selection.push_back(createIndex(node->row(), 0, static_cast(node))); - } - - if (treeView != nullptr) { - for (int i = 0; i < rowCount(); i++) { - treeView->expand(index(i, 0)); - // Expand XBT leaves - treeView->expand(index(0, 0, index(i, 0))); - } - - if (!selection.empty()) { - treeView->setCurrentIndex(selection[0]); - treeView->selectionModel()->select(selection[0], QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - treeView->expand(selection[0]); - treeView->scrollTo(selection[0]); - } - } - emit updateAddresses(); -} - -void WalletsViewModel::onWalletChanged() -{ - LoadWallets(true); -} diff --git a/BlockSettleUILib/WalletsViewModel.h b/BlockSettleUILib/WalletsViewModel.h index 97fc92112..98f9fd9ca 100644 --- a/BlockSettleUILib/WalletsViewModel.h +++ b/BlockSettleUILib/WalletsViewModel.h @@ -74,7 +74,12 @@ class WalletNode const std::string &name() const { return name_; } Type type() const { return type_; } State state() const { return state_; } + virtual void setState(State state) { state_ = state; } + virtual BTCNumericTypes::balance_type getBalanceTotal() const { return 0; } + virtual BTCNumericTypes::balance_type getBalanceUnconf() const { return 0; } + virtual BTCNumericTypes::balance_type getBalanceSpend() const { return 0; } + virtual size_t getNbUsedAddresses() const { return 0; } WalletNode *findByWalletId(const std::string &walletId); @@ -109,7 +114,6 @@ Q_OBJECT std::string selectedWallet() const { return selectedWalletId_; } bool showRegularWallets() const { return showRegularWallets_; } - [[deprecated]] void LoadWallets(bool keepSelection = false); void onHDWallet(const bs::sync::WalletInfo &); void onWalletDeleted(const bs::sync::WalletInfo&); void onHDWalletDetails(const bs::sync::HDWalletData &); @@ -134,7 +138,6 @@ Q_OBJECT void needWalletBalances(const std::string &walletId); private slots: - void onWalletChanged(); void onNewWalletAdded(const std::string &walletId); void onWalletInfo(unsigned int id, bs::hd::WalletInfo); void onHDWalletError(unsigned int id, std::string err); diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index 78e72b800..cb93c6bff 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -218,9 +218,6 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) ui_->treeViewWallets->setExpandsOnDoubleClick(false); // show the column as per BST-1520 //ui_->treeViewWallets->hideColumn(static_cast(WalletsViewModel::WalletColumns::ColumnID)); - if (walletsManager_) { - walletsModel_->LoadWallets(); - } connect(ui_->walletPropertiesButton, &QPushButton::clicked, this, &WalletsWidget::showSelectedWalletProperties); connect(ui_->createWalletButton, &QPushButton::clicked, this, &WalletsWidget::onNewWallet); @@ -234,7 +231,7 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) // No need to connect to wallet manager in AddressListModel explicitly in this case // so just put nullptr pointer in function - addressModel_ = new AddressListModel(nullptr, this); + addressModel_ = new AddressListModel(this); connect(addressModel_, &AddressListModel::needExtAddresses, this, &WalletsWidget::needExtAddresses); connect(addressModel_, &AddressListModel::needIntAddresses, this, &WalletsWidget::needIntAddresses); connect(addressModel_, &AddressListModel::needUsedAddresses, this, &WalletsWidget::needUsedAddresses); diff --git a/common b/common index 0d9e29b74..0012a75d0 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 0d9e29b7489c8507eb5a5eb3d92a4291451d49fe +Subproject commit 0012a75d021abef60b1dea3b0c4cf472cba48f2e From 50227cf2d3eb30b7d06289d40dbc7a8289642685 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Fri, 26 Aug 2022 23:09:10 +0300 Subject: [PATCH 143/146] [LEV-2512] wallet rescan improvements --- BlockSettleUILib/CreateTransactionDialog.cpp | 59 +++++----- BlockSettleUILib/CreateTransactionDialog.h | 3 +- BlockSettleUILib/TransactionsViewModel.cpp | 55 +++++---- BlockSettleUILib/TransactionsViewModel.h | 5 +- BlockSettleUILib/TransactionsWidget.cpp | 87 +++++++------- BlockSettleUILib/TransactionsWidget.h | 7 +- BlockSettleUILib/WalletsWidget.cpp | 116 ++++--------------- BlockSettleUILib/WalletsWidget.h | 3 - Core/SignerAdapter.cpp | 14 +-- Core/SignerAdapter.h | 4 +- Core/TerminalMessage.cpp | 2 +- GUI/QtWidgets/MainWindow.cpp | 5 + GUI/QtWidgets/QtGuiAdapter.cpp | 8 +- common | 2 +- 14 files changed, 165 insertions(+), 205 deletions(-) diff --git a/BlockSettleUILib/CreateTransactionDialog.cpp b/BlockSettleUILib/CreateTransactionDialog.cpp index accc858cb..0249d38df 100644 --- a/BlockSettleUILib/CreateTransactionDialog.cpp +++ b/BlockSettleUILib/CreateTransactionDialog.cpp @@ -92,7 +92,7 @@ void CreateTransactionDialog::init() xbtValidator_ = new XbtAmountValidator(this); lineEditAmount()->setValidator(xbtValidator_); - populateWalletsList(); + emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy, "CreateTX"); if (loadFeeSuggestions_) { populateFeeList(); } @@ -170,42 +170,29 @@ int CreateTransactionDialog::SelectWallet(const std::string& walletId, UiUtils:: void CreateTransactionDialog::populateWalletsList() { - emit needWalletsList(UiUtils::WalletsTypes::All_AllowHwLegacy, "CreateTX"); -} - -void CreateTransactionDialog::onWalletsList(const std::string &id - , const std::vector& hdWallets) -{ - if (id != "CreateTX") { - return; - } int selected = 0; auto comboBox = comboBoxWallets(); comboBox->clear(); - const auto &addRow = [comboBox, this] - (const std::string& label, const std::string& walletId, UiUtils::WalletsTypes type) + const auto& addRow = [comboBox, this] + (const std::string& label, const std::string& walletId, UiUtils::WalletsTypes type) { bool blocked = comboBox->blockSignals(true); int i = comboBox->count(); - logger_->debug("[CreateTransactionDialog::onWalletsList::addRow] #{}: {}", i, walletId); comboBox->addItem(QString::fromStdString(label)); comboBox->setItemData(i, QString::fromStdString(walletId), UiUtils::WalletIdRole); comboBox->setItemData(i, QVariant::fromValue(static_cast(type)), UiUtils::WalletType); - logger_->debug("[CreateTransactionDialog::onWalletsList::addRow] {} #{} get: {}", i, (void*)comboBox, comboBox->itemData(i, UiUtils::WalletIdRole).toString().toStdString()); comboBox->blockSignals(blocked); }; - hdWallets_.clear(); - for (const auto& hdWallet : hdWallets) { - hdWallets_[hdWallet.id] = hdWallet; - if (hdWallet.primary) { + for (const auto& hdWallet : hdWallets_) { + if (hdWallet.second.primary) { selected = comboBox->count(); } UiUtils::WalletsTypes type = UiUtils::WalletsTypes::None; - if ((hdWallet.groups.size() == 1) && (hdWallet.groups[0].leaves.size() == 1)) { - const auto& leaf = hdWallet.groups[0].leaves.at(0); - std::string label = hdWallet.name; + if ((hdWallet.second.groups.size() == 1) && (hdWallet.second.groups[0].leaves.size() == 1)) { + const auto& leaf = hdWallet.second.groups[0].leaves.at(0); + std::string label = hdWallet.second.name; const auto purpose = static_cast(leaf.path.get(0) & ~bs::hd::hardFlag); if (purpose == bs::hd::Purpose::Native) { label += " Native"; @@ -217,21 +204,41 @@ void CreateTransactionDialog::onWalletsList(const std::string &id label += " Legacy"; type = UiUtils::WalletsTypes::HardwareLegacy; } - addRow(label, hdWallet.id, type); - } - else { - if (hdWallet.offline) { + addRow(label, hdWallet.second.id, type); + } else { + if (hdWallet.second.offline) { type = UiUtils::WalletsTypes::WatchOnly; } else { type = UiUtils::WalletsTypes::Full; } - addRow(hdWallet.name, hdWallet.id, type); + addRow(hdWallet.second.name, hdWallet.second.id, type); } } comboBox->setCurrentIndex(selected); selectedWalletChanged(selected, true); } +void CreateTransactionDialog::onWalletsList(const std::string &id + , const std::vector& hdWallets) +{ + if (id != "CreateTX") { + return; + } + + hdWallets_.clear(); + for (const auto& hdWallet : hdWallets) { + hdWallets_[hdWallet.id] = hdWallet; + } + populateWalletsList(); +} + +void CreateTransactionDialog::onWalletDeleted(const bs::sync::WalletInfo& wi) +{ + const auto& rootId = *wi.ids.cbegin(); + hdWallets_.erase(rootId); + populateWalletsList(); +} + void CreateTransactionDialog::onFeeLevels(const std::map& feeLevels) { comboBoxFeeSuggestions()->clear(); diff --git a/BlockSettleUILib/CreateTransactionDialog.h b/BlockSettleUILib/CreateTransactionDialog.h index d1f04c705..3d61af13c 100644 --- a/BlockSettleUILib/CreateTransactionDialog.h +++ b/BlockSettleUILib/CreateTransactionDialog.h @@ -75,6 +75,7 @@ Q_OBJECT virtual void onChangeAddress(const std::string& walletId, const bs::Address&); virtual void onWalletsList(const std::string &id, const std::vector&); + virtual void onWalletDeleted(const bs::sync::WalletInfo&); void onFeeLevels(const std::map&); void onUTXOs(const std::string& id, const std::string& walletId, const std::vector&); void onSignedTX(const std::string& id, BinaryData signedTX, bs::error::ErrorCode result); @@ -152,13 +153,13 @@ protected slots: protected: void populateFeeList(); + void populateWalletsList(); bool createTransactionImpl(); static bool canUseSimpleMode(const Bip21::PaymentRequestInfo& paymentInfo); private: - void populateWalletsList(); void startBroadcasting(); void stopBroadcasting(); diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index e7e1331a2..afbb447ce 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -272,10 +272,10 @@ unsigned int TXNode::level() const TransactionsViewModel::TransactionsViewModel(const std::shared_ptr &logger , QObject* parent) : QAbstractItemModel(parent), logger_(logger) - , allWallets_(true), filterAddress_() + , filterAddress_{} { stopped_ = std::make_shared(false); - rootNode_.reset(new TXNode); + rootNode_ = std::make_unique(); } TransactionsViewModel::~TransactionsViewModel() noexcept @@ -388,24 +388,6 @@ QVariant TransactionsViewModel::headerData(int section, Qt::Orientation orientat return QVariant(); } -void TransactionsViewModel::refresh() -{ - updatePage(); -} - -void TransactionsViewModel::onWalletDeleted(std::string) -{ - clear(); - updatePage(); -} - -void TransactionsViewModel::updatePage() -{ - if (allWallets_) { -// loadAllWallets(); - } -} - void TransactionsViewModel::clear() { *stopped_ = true; @@ -513,7 +495,7 @@ void TransactionsViewModel::onDelRows(std::vector rows) { // optimize for contiguous ranges, if needed std::sort(rows.begin(), rows.end()); int rowCnt = rowCount(); - QMutexLocker locker(&updateMutex_); + //QMutexLocker locker(&updateMutex_); for (int i = 0; i < rows.size(); ++i) { const int row = rows[i] - i; // special hack for correcting row index after previous row deletion if ((row < 0) || row >= rowCnt) { @@ -522,6 +504,19 @@ void TransactionsViewModel::onDelRows(std::vector rows) beginRemoveRows(QModelIndex(), row, row); rootNode_->del(row); + auto itIndex = itemIndex_.begin(); + while (itIndex != itemIndex_.end()) { + if (itIndex->second < row) { + itIndex++; + continue; + } + if (itIndex->second == row) { + itIndex = itemIndex_.erase(itIndex); + continue; + } + itIndex->second--; + itIndex++; + } endRemoveRows(); rowCnt--; } @@ -703,6 +698,24 @@ void TransactionsViewModel::onTXDetails(const std::vector delRows; + for (const auto& group : wallet.groups) { + for (const auto& leaf : group.leaves) { + for (const auto& id : leaf.ids) { + for (const auto& index : itemIndex_) { + if (index.first.walletId == id) { + delRows.push_back(index.second); + } + } + } + } + } + onDelRows(delRows); + return delRows.size(); +} + static bool isSpecialWallet(const std::shared_ptr &wallet) { diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index 56e39ae74..4eafc5f59 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -161,14 +161,12 @@ Q_OBJECT void onZCsInvalidated(const std::vector& txHashes); void onNewBlock(unsigned int topBlock); void onTXDetails(const std::vector &); + size_t removeEntriesFor(const bs::sync::HDWalletData&); signals: void needTXDetails(const std::vector &, bool useCache, const bs::Address &); private slots: - void updatePage(); - void refresh(); - void onWalletDeleted(std::string walletId); void onNewItems(const std::vector &); void onDelRows(std::vector rows); void onItemConfirmed(const TransactionPtr); @@ -214,7 +212,6 @@ private slots: mutable QMutex updateMutex_; std::shared_ptr defaultWallet_; std::atomic_bool signalOnEndLoading_{ false }; - const bool allWallets_; std::shared_ptr stopped_; std::atomic_bool initialLoadCompleted_{ true }; diff --git a/BlockSettleUILib/TransactionsWidget.cpp b/BlockSettleUILib/TransactionsWidget.cpp index f7e068a27..cf9143a2b 100644 --- a/BlockSettleUILib/TransactionsWidget.cpp +++ b/BlockSettleUILib/TransactionsWidget.cpp @@ -28,7 +28,7 @@ #include "UiUtils.h" #include "UtxoReservationManager.h" -static const QString c_allWalletsId = QLatin1String("all"); +static const QString kAllWalletsId = QLatin1String("all"); using namespace bs::sync; @@ -161,12 +161,12 @@ class TransactionsSortFilterModel : public QSortFilterProxyModel this->walletIds = walletIds; this->searchString = searchString; this->transactionDirection = direction; - - appSettings_->set(ApplicationSettings::TransactionFilter, - QVariantList() << (this->walletIds.isEmpty() ? - QStringList() << c_allWalletsId : this->walletIds) << - static_cast(direction)); - + if (appSettings_) { + appSettings_->set(ApplicationSettings::TransactionFilter, + QVariantList() << (this->walletIds.isEmpty() ? + QStringList() << kAllWalletsId : this->walletIds) << + static_cast(direction)); + } invalidateFilter(); } @@ -314,6 +314,7 @@ void TransactionsWidget::init(const std::shared_ptr &logger ui_->treeViewTransactions->sortByColumn(static_cast(TransactionsViewModel::Columns::Status), Qt::AscendingOrder); } +#if 0 void TransactionsWidget::SetTransactionsModel(const std::shared_ptr& model) { model_ = model; @@ -351,6 +352,26 @@ void TransactionsWidget::SetTransactionsModel(const std::shared_ptrtreeViewTransactions->hideColumn(static_cast(TransactionsViewModel::Columns::MissedBlocks)); } +#endif + +void TransactionsWidget::onHDWalletDetails(const bs::sync::HDWalletData& wallet) +{ + wallets_[wallet.id] = wallet; + walletsChanged(); +} + +void TransactionsWidget::onWalletDeleted(const bs::sync::WalletInfo& wi) +{ + const auto& itWallet = wallets_.find(*wi.ids.cbegin()); + if (itWallet != wallets_.end()) { + const auto nbRemoved = model_->removeEntriesFor(itWallet->second); + logger_->debug("[{}] removed {} entries from wallet {}", __func__, nbRemoved + , itWallet->second.name); + wallets_.erase(itWallet); + } + walletsChanged(); +} + void TransactionsWidget::onDataLoaded(int count) { @@ -419,44 +440,32 @@ static inline bool exactlyThisLeaf(const QStringList &ids, const QStringList &wa void TransactionsWidget::walletsChanged() { - QStringList walletIds; - int direction; - - const auto varList = appSettings_ ? appSettings_->get(ApplicationSettings::TransactionFilter).toList() - : QVariantList{}; - if (!varList.empty()) { - walletIds = varList.first().toStringList(); - direction = varList.last().toInt(); - } - int currentIndex = -1; - int primaryWalletIndex = 0; ui_->walletBox->clear(); ui_->walletBox->addItem(tr("All Wallets")); int index = 1; - for (const auto &hdWallet : walletsManager_->hdWallets()) { - ui_->walletBox->addItem(QString::fromStdString(hdWallet->name())); - QStringList allLeafIds = walletLeavesIds(hdWallet); - - if (exactlyThisLeaf(walletIds, allLeafIds)) { - currentIndex = index; - } - - if (hdWallet == walletsManager_->getPrimaryWallet()) { - primaryWalletIndex = index; + for (const auto& hdWallet : wallets_) { + ui_->walletBox->addItem(QString::fromStdString(hdWallet.second.name)); + QStringList allLeafIds; + for (const auto& group : hdWallet.second.groups) { + for (const auto& leaf : group.leaves) { + for (const auto& id : leaf.ids) { + allLeafIds.append(QString::fromStdString(id)); + } + } } - ui_->walletBox->setItemData(index++, allLeafIds, UiUtils::WalletIdRole); - for (const auto &group : hdWallet->getGroups()) { +#if 0 + for (const auto& group : hdWallet->groups) { if (group->type() == bs::core::wallet::Type::Settlement) { continue; } ui_->walletBox->addItem(QString::fromStdString(" " + group->name())); const auto groupIndex = index++; QStringList groupLeafIds; - for (const auto &leaf : group->getLeaves()) { + for (const auto& leaf : group->getLeaves()) { groupLeafIds << QString::fromStdString(leaf->walletId()); ui_->walletBox->addItem(QString::fromStdString(" " + leaf->shortName())); @@ -480,21 +489,13 @@ void TransactionsWidget::walletsChanged() } ui_->typeFilterComboBox->setCurrentIndex(direction); - +#else //0 + } +#endif //0 if (currentIndex >= 0) { ui_->walletBox->setCurrentIndex(currentIndex); } else { - if (walletIds.contains(c_allWalletsId)) { - ui_->walletBox->setCurrentIndex(0); - } else if (walletsManager_) { - const auto primaryWallet = walletsManager_->getPrimaryWallet(); - - if (primaryWallet) { - ui_->walletBox->setCurrentIndex(primaryWalletIndex); - } else { - ui_->walletBox->setCurrentIndex(0); - } - } + ui_->walletBox->setCurrentIndex(0); } } diff --git a/BlockSettleUILib/TransactionsWidget.h b/BlockSettleUILib/TransactionsWidget.h index 1f7310f31..a3a98203d 100644 --- a/BlockSettleUILib/TransactionsWidget.h +++ b/BlockSettleUILib/TransactionsWidget.h @@ -18,6 +18,7 @@ #include "BinaryData.h" #include "BSErrorCode.h" #include "TransactionsWidgetInterface.h" +#include "Wallets/SignerDefs.h" namespace spdlog { class logger; @@ -49,10 +50,12 @@ Q_OBJECT void init(const std::shared_ptr & , const std::shared_ptr &); - [[deprecated]] void SetTransactionsModel(const std::shared_ptr &); void shortcutActivated(ShortcutType s) override; + void onHDWalletDetails(const bs::sync::HDWalletData&); + void onWalletDeleted(const bs::sync::WalletInfo&); + private slots: void showTransactionDetails(const QModelIndex& index); void updateResultCount(); @@ -66,9 +69,9 @@ private slots: private: void scheduleDateFilterCheck(); std::unique_ptr ui_; + std::unordered_map wallets_; TransactionsSortFilterModel * sortFilterModel_; - }; diff --git a/BlockSettleUILib/WalletsWidget.cpp b/BlockSettleUILib/WalletsWidget.cpp index cb93c6bff..02d8b0a0f 100644 --- a/BlockSettleUILib/WalletsWidget.cpp +++ b/BlockSettleUILib/WalletsWidget.cpp @@ -145,7 +145,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(AddressSortFilterModel::Filter) WalletsWidget::WalletsWidget(QWidget* parent) : TabWithShortcut(parent) , ui_(new Ui::WalletsWidget()) - , walletsManager_(nullptr) , walletsModel_(nullptr) , addressModel_(nullptr) , addressSortFilterModel_(nullptr) @@ -251,9 +250,6 @@ void WalletsWidget::InitWalletsView(const std::string& defaultWalletId) connect(ui_->treeViewWallets->selectionModel(), &QItemSelectionModel::selectionChanged , this, &WalletsWidget::updateAddresses); connect(walletsModel_, &WalletsViewModel::updateAddresses, this, &WalletsWidget::updateAddresses); - if (walletsManager_) { - connect(walletsManager_.get(), &bs::sync::WalletsManager::walletBalanceUpdated, this, &WalletsWidget::onWalletBalanceChanged, Qt::QueuedConnection); - } connect(ui_->treeViewAddresses->model(), &QAbstractItemModel::layoutChanged, this, &WalletsWidget::treeViewAddressesLayoutChanged); connect(ui_->treeViewAddresses->selectionModel(), &QItemSelectionModel::selectionChanged, this, &WalletsWidget::treeViewAddressesSelectionChanged); @@ -485,45 +481,29 @@ void WalletsWidget::showAddressProperties(const QModelIndex& index) { auto sourceIndex = addressSortFilterModel_->mapToSource(index); const auto &walletId = addressModel_->data(sourceIndex, AddressListModel::WalletIdRole).toString().toStdString(); - if (walletsManager_ && armory_) { - const auto &wallet = walletsManager_->getWalletById(walletId); - if (!wallet || (wallet->type() == bs::core::wallet::Type::Authentication)) { - return; - } - - const auto &addresses = wallet->getUsedAddressList(); - const size_t addrIndex = addressModel_->data(sourceIndex, AddressListModel::AddrIndexRole).toUInt(); - const auto address = (addrIndex < addresses.size()) ? addresses[addrIndex] : bs::Address(); - - wallet->onBalanceAvailable([this, address, wallet] { -//FIXME: auto dialog = new AddressDetailDialog(address, wallet, walletsManager_, armory_, logger_, this); -// QMetaObject::invokeMethod(this, [dialog] { dialog->exec(); }); + const auto &addrStr = addressModel_->data(sourceIndex, AddressListModel::AddressRole).toString().toStdString(); + const auto &ledgerFilter = walletId + "." + addrStr; + emit needLedgerEntries(ledgerFilter); + + const auto wltType = static_cast(addressModel_->data(sourceIndex, AddressListModel::WalletTypeRole).toInt()); + const auto txn = addressModel_->data(sourceIndex, AddressListModel::TxNRole).toInt(); + const uint64_t balance = addressModel_->data(sourceIndex, AddressListModel::BalanceRole).toULongLong(); + const auto &walletName = addressModel_->data(sourceIndex, AddressListModel::WalletNameRole).toString(); + const auto &path = addressModel_->data(sourceIndex, AddressListModel::AddressIndexRole).toString().toStdString(); + const auto &comment = addressModel_->data(sourceIndex, AddressListModel::AddressCommentRole).toString().toStdString(); + try { + const auto &address = bs::Address::fromAddressString(addrStr); + auto dialog = new AddressDetailDialog(address, logger_, wltType, balance + , txn, walletName, path, comment, this); + addrDetDialogs_[ledgerFilter] = dialog; + connect(dialog, &AddressDetailDialog::needTXDetails, this, &WalletsWidget::needTXDetails); + connect(dialog, &QDialog::finished, [dialog, ledgerFilter, this](int) { + dialog->deleteLater(); + addrDetDialogs_.erase(ledgerFilter); }); + dialog->show(); } - else { - const auto &addrStr = addressModel_->data(sourceIndex, AddressListModel::AddressRole).toString().toStdString(); - const auto &ledgerFilter = walletId + "." + addrStr; - emit needLedgerEntries(ledgerFilter); - const auto wltType = static_cast(addressModel_->data(sourceIndex, AddressListModel::WalletTypeRole).toInt()); - const auto txn = addressModel_->data(sourceIndex, AddressListModel::TxNRole).toInt(); - const uint64_t balance = addressModel_->data(sourceIndex, AddressListModel::BalanceRole).toULongLong(); - const auto &walletName = addressModel_->data(sourceIndex, AddressListModel::WalletNameRole).toString(); - const auto &index = addressModel_->data(sourceIndex, AddressListModel::AddressIndexRole).toString().toStdString(); - const auto &comment = addressModel_->data(sourceIndex, AddressListModel::AddressCommentRole).toString().toStdString(); - try { - const auto &address = bs::Address::fromAddressString(addrStr); - auto dialog = new AddressDetailDialog(address, logger_, wltType, balance - , txn, walletName, index, comment, this); - addrDetDialogs_[ledgerFilter] = dialog; - connect(dialog, &AddressDetailDialog::needTXDetails, this, &WalletsWidget::needTXDetails); - connect(dialog, &QDialog::finished, [dialog, ledgerFilter, this](int) { - dialog->deleteLater(); - addrDetDialogs_.erase(ledgerFilter); - }); - dialog->show(); - } - catch (const std::exception &) {} - } + catch (const std::exception &) {} } void WalletsWidget::onAddressContextMenu(const QPoint &p) @@ -537,23 +517,6 @@ void WalletsWidget::onAddressContextMenu(const QPoint &p) curAddress_.clear(); return; } - if (walletsManager_) { - curWallet_ = walletsManager_->getWalletByAddress(curAddress_); - - if (!curWallet_) { - logger_->warn("Failed to find wallet for address {}", curAddress_.display()); - return; - } - auto contextMenu = new QMenu(this); - - if ((curWallet_->type() == bs::core::wallet::Type::Bitcoin) || (getSelectedWallets().size() == 1)) { - contextMenu->addAction(actCopyAddr_); - } - contextMenu->addAction(actEditComment_); - - emit showContextMenu(contextMenu, ui_->treeViewAddresses->mapToGlobal(p)); - return; - } curWalletId_ = addressModel_->data(addressIndex, AddressListModel::Role::WalletIdRole).toString().toStdString(); if (curWalletId_.empty()) { @@ -678,24 +641,6 @@ void WalletsWidget::scrollChanged() } } -void WalletsWidget::onWalletsSynchronized() -{ - if (walletsManager_->hasPrimaryWallet()) { - int i = 0; - for (const auto &hdWallet : walletsManager_->hdWallets()) { - if (hdWallet->isPrimary()) { - ui_->treeViewWallets->selectionModel()->select(walletsModel_->index(i, 0) - , QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - break; - } - i++; - } - } - else if (!walletsManager_->hdWallets().empty()){ - ui_->treeViewWallets->selectionModel()->select(walletsModel_->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); - } -} - void WalletsWidget::onWalletBalanceChanged(std::string walletId) { const auto &selectedWallets = getSelectedWallets(); @@ -830,29 +775,17 @@ void WalletsWidget::onCopyAddress() void WalletsWidget::onEditAddrComment() { - if ((!curWallet_ && curWalletId_.empty()) || curAddress_.empty()) { + if (curWalletId_.empty() || curAddress_.empty()) { return; } bool isOk = false; std::string oldComment; - if (curWallet_) { - oldComment = curWallet_->getAddressComment(curAddress_); - } - else { - oldComment = curComment_; - } + oldComment = curComment_; const auto comment = QInputDialog::getText(this, tr("Edit Comment") , tr("Enter new comment for address %1:").arg(QString::fromStdString(curAddress_.display())) , QLineEdit::Normal, QString::fromStdString(oldComment), &isOk); if (isOk) { - if (curWallet_) { - if (!curWallet_->setAddressComment(curAddress_, comment.toStdString())) { - BSMessageBox(BSMessageBox::critical, tr("Address Comment"), tr("Failed to save comment")).exec(); - } - } - else { - emit setAddrComment(curWalletId_, curAddress_, comment.toStdString()); - } + emit setAddrComment(curWalletId_, curAddress_, comment.toStdString()); } } @@ -869,7 +802,6 @@ void WalletsWidget::onTXSigned(unsigned int id, BinaryData signedTX, bs::error:: } if (!armory_->broadcastZC(signedTX).empty()) { -// walletsManager_->getSettlementWallet()->setTransactionComment(signedTX, "Settlement Revoke"); //TODO later } else { BSMessageBox(BSMessageBox::critical, title, tr("Failed to send transaction to mempool")).exec(); diff --git a/BlockSettleUILib/WalletsWidget.h b/BlockSettleUILib/WalletsWidget.h index ef1c77995..46c0367ed 100644 --- a/BlockSettleUILib/WalletsWidget.h +++ b/BlockSettleUILib/WalletsWidget.h @@ -144,13 +144,11 @@ private slots: void treeViewAddressesSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void treeViewAddressesLayoutChanged(); void scrollChanged(); - void onWalletsSynchronized(); private: std::unique_ptr ui_; std::shared_ptr logger_; - std::shared_ptr walletsManager_; std::shared_ptr appSettings_; std::shared_ptr connectionManager_; std::shared_ptr assetManager_; @@ -165,7 +163,6 @@ private slots: QAction * actEditComment_ = nullptr; //QAction * actDeleteWallet_ = nullptr; bs::Address curAddress_; - [[deprecated]] std::shared_ptr curWallet_; std::set wallets_; std::unordered_map walletDetails_; std::unordered_map walletBalances_; diff --git a/Core/SignerAdapter.cpp b/Core/SignerAdapter.cpp index f60ad8869..35b1de300 100644 --- a/Core/SignerAdapter.cpp +++ b/Core/SignerAdapter.cpp @@ -133,9 +133,9 @@ bool SignerAdapter::processOwnRequest(const bs::message::Envelope &env case SignerMessage::kDialogRequest: return processDialogRequest(env, request.dialog_request()); case SignerMessage::kCreateWallet: - return processCreateWallet(env, request.create_wallet()); + return processCreateWallet(env, false, request.create_wallet()); case SignerMessage::kImportWallet: - return processCreateWallet(env, request.import_wallet()); + return processCreateWallet(env, true, request.import_wallet()); case SignerMessage::kDeleteWallet: return processDeleteWallet(env, request.delete_wallet()); default: @@ -166,10 +166,10 @@ bool SignerAdapter::processSignerSettings(const SettingsMessage_SignerServer &re return sendComponentLoading(); } -void SignerAdapter::walletsChanged() +void SignerAdapter::walletsChanged(bool rescan) { SignerMessage msg; - msg.mutable_wallets_list_updated(); + msg.set_wallets_list_updated(rescan); pushBroadcast(user_, msg.SerializeAsString(), true); } @@ -568,7 +568,7 @@ bool SignerAdapter::processDialogRequest(const bs::message::Envelope& } bool SignerAdapter::processCreateWallet(const bs::message::Envelope& env - , const SignerMessage_CreateWalletRequest& w) + , bool rescan, const SignerMessage_CreateWalletRequest& w) { bs::wallet::PasswordData pwdData; pwdData.password = SecureBinaryData::fromString(w.password()); @@ -584,7 +584,7 @@ bool SignerAdapter::processCreateWallet(const bs::message::Envelope& env const auto& wallet = walletsMgr_->createWallet(w.name(), w.description(), seed , walletsDir_, pwdData, w.primary()); msgResp->set_wallet_id(wallet->walletId()); - walletsChanged(); + walletsChanged(rescan); logger_->debug("[{}] wallet {} created", __func__, wallet->walletId()); } catch (const std::exception& e) { @@ -606,6 +606,6 @@ bool SignerAdapter::processDeleteWallet(const bs::message::Envelope& env else { msg.set_wallet_deleted(""); } - pushResponse(user_, env, msg.SerializeAsString()); + pushBroadcast(user_, msg.SerializeAsString()); return true; } diff --git a/Core/SignerAdapter.h b/Core/SignerAdapter.h index 3ed09dc52..1a942dc57 100644 --- a/Core/SignerAdapter.h +++ b/Core/SignerAdapter.h @@ -67,7 +67,7 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget void start(); // HCT overrides - void walletsChanged() override; + void walletsChanged(bool rescan = false) override; void onReady() override; void walletsReady() override; void newWalletPrompt() override; @@ -102,7 +102,7 @@ class SignerAdapter : public bs::message::Adapter, public SignerCallbackTarget , const BlockSettle::Common::SignerMessage_AutoSign&); bool processDialogRequest(const bs::message::Envelope& , const BlockSettle::Common::SignerMessage_DialogRequest&); - bool processCreateWallet(const bs::message::Envelope& + bool processCreateWallet(const bs::message::Envelope&, bool rescan , const BlockSettle::Common::SignerMessage_CreateWalletRequest&); bool processDeleteWallet(const bs::message::Envelope&, const std::string& rootId); diff --git a/Core/TerminalMessage.cpp b/Core/TerminalMessage.cpp index 23b4bb8af..2a71896a7 100644 --- a/Core/TerminalMessage.cpp +++ b/Core/TerminalMessage.cpp @@ -132,10 +132,10 @@ void bs::message::TerminalInprocBus::sendLoading(const std::shared_ptr& void TerminalInprocBus::shutdown() { runnableAdapter_.reset(); + relayAdapter_.reset(); for (const auto& q : queues_) { q.second->terminate(); } - relayAdapter_.reset(); } bool TerminalInprocBus::run(int &argc, char **argv) diff --git a/GUI/QtWidgets/MainWindow.cpp b/GUI/QtWidgets/MainWindow.cpp index 6e25b3033..873c93300 100644 --- a/GUI/QtWidgets/MainWindow.cpp +++ b/GUI/QtWidgets/MainWindow.cpp @@ -221,12 +221,17 @@ void MainWindow::onHDWallet(const bs::sync::WalletInfo &wi) void bs::gui::qt::MainWindow::onWalletDeleted(const bs::sync::WalletInfo& wi) { ui_->widgetWallets->onWalletDeleted(wi); + if (txDlg_) { + txDlg_->onWalletDeleted(wi); + } + ui_->widgetTransactions->onWalletDeleted(wi); } void MainWindow::onHDWalletDetails(const bs::sync::HDWalletData &hdWallet) { ui_->widgetWallets->onHDWalletDetails(hdWallet); ui_->widgetPortfolio->onHDWalletDetails(hdWallet); + ui_->widgetTransactions->onHDWalletDetails(hdWallet); } void MainWindow::onWalletsList(const std::string &id, const std::vector& wallets) diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index ab6af696c..54be56619 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -603,7 +603,6 @@ bool QtGuiAdapter::processWallets(const Envelope &env) case WalletsMessage::kWalletAddresses: { std::vector addresses; - logger_->debug("[kWalletAddresses] #{}", env.responseId()); for (const auto &addr : msg.wallet_addresses().addresses()) { try { addresses.push_back({ std::move(bs::Address::fromAddressString(addr.address())) @@ -651,6 +650,11 @@ bool QtGuiAdapter::processWallets(const Envelope &env) return processUTXOs(msg.utxos()); case WalletsMessage::kReservedUtxos: return processReservedUTXOs(msg.reserved_utxos()); + case WalletsMessage::kWalletChanged: + if (walletsReady_) { + onNeedLedgerEntries({}); + } + break; default: break; } return true; @@ -1056,7 +1060,7 @@ void QtGuiAdapter::onNeedTXDetails(const std::vector &txWall auto msgReq = msg.mutable_tx_details_request(); for (const auto &txw : txWallet) { auto request = msgReq->add_requests(); - logger_->debug("[{}] {}", __func__, txw.txHash.toHexStr()); + //logger_->debug("[{}] {}", __func__, txw.txHash.toHexStr()); request->set_tx_hash(txw.txHash.toBinStr()); request->set_wallet_id(txw.walletId); request->set_value(txw.value); diff --git a/common b/common index 0012a75d0..926470b1d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 0012a75d021abef60b1dea3b0c4cf472cba48f2e +Subproject commit 926470b1df7f379c83071f045fa7b4c8273941f6 From 189a3d6c5e9cd399ef424db79a63f408774cdb29 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Mon, 29 Aug 2022 15:21:57 +0300 Subject: [PATCH 144/146] update common --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 926470b1d..23393706d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 926470b1df7f379c83071f045fa7b4c8273941f6 +Subproject commit 23393706d2ee9a5ac00d8c7ebbd4c5db9892e1fe From a0c8639be23cc329ec7fe904daa3eac1dcea6485 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Tue, 30 Aug 2022 17:48:59 +0300 Subject: [PATCH 145/146] [LEV-2519] TX split across HD wallets --- BlockSettleUILib/TransactionsViewModel.cpp | 17 +++++++++++------ BlockSettleUILib/TransactionsViewModel.h | 4 ++-- GUI/QtWidgets/QtGuiAdapter.cpp | 10 +++++----- GUI/QtWidgets/QtGuiAdapter.h | 4 ++-- common | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/BlockSettleUILib/TransactionsViewModel.cpp b/BlockSettleUILib/TransactionsViewModel.cpp index afbb447ce..15398dae1 100644 --- a/BlockSettleUILib/TransactionsViewModel.cpp +++ b/BlockSettleUILib/TransactionsViewModel.cpp @@ -604,16 +604,20 @@ void TransactionsViewModel::onLedgerEntries(const std::string &, uint32_t void TransactionsViewModel::onZCsInvalidated(const std::vector& txHashes) { for (const auto& txHash : txHashes) { - const auto node = rootNode_->find(txHash); - if (node) { + for (const auto& node : rootNode_->nodesByTxHash(txHash)) { const int row = node->row(); beginRemoveRows(QModelIndex(), row, row); auto removedNode = rootNode_->take(row); itemIndex_.erase({ node->item()->txEntry.txHash, node->item()->walletID.toStdString() }); + for (auto& idx : itemIndex_) { + if (idx.second > row) { + idx.second--; + } + } endRemoveRows(); if (removedNode) { removedNode->item()->curBlock = curBlock_; - invalidatedNodes_[txHash] = removedNode; + invalidatedNodes_[{ txHash, node->item()->walletID.toStdString() }] = removedNode; } } } @@ -635,12 +639,13 @@ void TransactionsViewModel::onTXDetails(const std::vectorsecond); - item = itInv->second->item(); + const auto& invNode = itInv->second; + newNodes.push_back(invNode); + item = invNode->item(); invalidatedNodes_.erase(itInv); } else { diff --git a/BlockSettleUILib/TransactionsViewModel.h b/BlockSettleUILib/TransactionsViewModel.h index 4eafc5f59..f66949689 100644 --- a/BlockSettleUILib/TransactionsViewModel.h +++ b/BlockSettleUILib/TransactionsViewModel.h @@ -206,7 +206,6 @@ private slots: private: std::unique_ptr rootNode_; - std::map invalidatedNodes_; TransactionPtr oldestItem_; std::shared_ptr logger_; mutable QMutex updateMutex_; @@ -229,7 +228,8 @@ private slots: return ((txHash == other.txHash) && (walletId == other.walletId)); } }; - std::map itemIndex_; + std::map itemIndex_; + std::map invalidatedNodes_; // If set, amount field will show only related address balance changes // (without fees because fees are related to transaction, not address). diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 54be56619..41969d462 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -503,8 +503,6 @@ bool QtGuiAdapter::processBlockchain(const Envelope &env) }); } break; - case ArmoryMessage::kLedgerEntries: - return processLedgerEntries(msg.ledger_entries()); case ArmoryMessage::kAddressHistory: return processAddressHist(msg.address_history()); case ArmoryMessage::kFeeLevelsResponse: @@ -655,6 +653,8 @@ bool QtGuiAdapter::processWallets(const Envelope &env) onNeedLedgerEntries({}); } break; + case WalletsMessage::kLedgerEntries: + return processLedgerEntries(msg.ledger_entries()); default: break; } return true; @@ -1048,9 +1048,9 @@ void QtGuiAdapter::onSetAddrComment(const std::string &walletId, const bs::Addre void QtGuiAdapter::onNeedLedgerEntries(const std::string &filter) { - ArmoryMessage msg; + WalletsMessage msg; msg.set_get_ledger_entries(filter); - pushRequest(user_, userBlockchain_, msg.SerializeAsString()); + pushRequest(user_, userWallets_, msg.SerializeAsString()); } void QtGuiAdapter::onNeedTXDetails(const std::vector &txWallet @@ -1438,7 +1438,7 @@ bool QtGuiAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetai }); } -bool QtGuiAdapter::processLedgerEntries(const ArmoryMessage_LedgerEntries &response) +bool QtGuiAdapter::processLedgerEntries(const LedgerEntries &response) { std::vector entries; for (const auto &entry : response.entries()) { diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 1c1d5e48a..927c6d976 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -30,9 +30,9 @@ namespace BlockSettle { namespace Common { class ArmoryMessage_AddressHistory; class ArmoryMessage_FeeLevelsResponse; - class ArmoryMessage_LedgerEntries; class ArmoryMessage_ZCInvalidated; class ArmoryMessage_ZCReceived; + class LedgerEntries; class OnChainTrackMessage_AuthAddresses; class OnChainTrackMessage_AuthState; class SignerMessage_SignTxResponse; @@ -112,7 +112,7 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processWalletBalances(const bs::message::Envelope & , const BlockSettle::Common::WalletsMessage_WalletBalances &); bool processTXDetails(uint64_t msgId, const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); - bool processLedgerEntries(const BlockSettle::Common::ArmoryMessage_LedgerEntries &); + bool processLedgerEntries(const BlockSettle::Common::LedgerEntries &); bool processAddressHist(const BlockSettle::Common::ArmoryMessage_AddressHistory&); bool processFeeLevels(const BlockSettle::Common::ArmoryMessage_FeeLevelsResponse&); bool processWalletsList(const BlockSettle::Common::WalletsMessage_WalletsListResponse&); diff --git a/common b/common index 23393706d..1ce16aa45 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 23393706d2ee9a5ac00d8c7ebbd4c5db9892e1fe +Subproject commit 1ce16aa45b5556d67f46ab8f0ef0bd3d998c8d8f From 53109be51b82712fe1f5e0b5d05d4555ca240f13 Mon Sep 17 00:00:00 2001 From: Sergey Chernikov Date: Thu, 1 Sep 2022 10:21:46 +0300 Subject: [PATCH 146/146] [LEV-2522] Optional QtQuick GUI (now empty) --- BlockSettleApp/CMakeLists.txt | 1 + BlockSettleApp/main.cpp | 38 +- CMakeLists.txt | 2 + Core/BsServerAdapter.cpp | 5 +- Core/SettingsAdapter.cpp | 11 +- Core/SettingsAdapter.h | 1 + GUI/CMakeLists.txt | 3 +- GUI/QtQuick/CMakeLists.txt | 75 ++ GUI/QtQuick/QtQuickAdapter.cpp | 906 ++++++++++++++++++ GUI/QtQuick/QtQuickAdapter.h | 146 +++ GUI/QtQuick/images/bs_logo.png | Bin 0 -> 3134 bytes GUI/QtQuick/images/full_logo.png | Bin 0 -> 16034 bytes GUI/QtQuick/images/notification_critical.png | Bin 0 -> 876 bytes GUI/QtQuick/images/notification_info.png | Bin 0 -> 747 bytes GUI/QtQuick/images/notification_question.png | Bin 0 -> 1035 bytes GUI/QtQuick/images/notification_success.png | Bin 0 -> 834 bytes GUI/QtQuick/images/notification_warning.png | Bin 0 -> 753 bytes GUI/QtQuick/qml/BsStyles/BSStyle.qml | 76 ++ GUI/QtQuick/qml/BsStyles/qmldir | 3 + GUI/QtQuick/qml/InfoBanner.qml | 53 + GUI/QtQuick/qml/InfoBannerComponent.qml | 55 ++ GUI/QtQuick/qml/InfoBar.qml | 74 ++ .../qml/StyledControls/CustomButton.qml | 94 ++ .../qml/StyledControls/CustomButtonBar.qml | 20 + .../qml/StyledControls/CustomCheckBox.qml | 46 + .../qml/StyledControls/CustomComboBox.qml | 110 +++ .../qml/StyledControls/CustomContainer.qml | 25 + .../qml/StyledControls/CustomContextMenu.qml | 61 ++ .../qml/StyledControls/CustomDialog.qml | 287 ++++++ .../qml/StyledControls/CustomDialogWindow.qml | 31 + .../qml/StyledControls/CustomHeader.qml | 37 + .../qml/StyledControls/CustomHeaderPanel.qml | 42 + .../qml/StyledControls/CustomLabel.qml | 23 + .../CustomLabelCopyableValue.qml | 58 ++ .../qml/StyledControls/CustomLabelValue.qml | 22 + .../CustomPasswordTextInput.qml | 50 + .../qml/StyledControls/CustomProgressBar.qml | 40 + .../qml/StyledControls/CustomRadioButton.qml | 48 + .../qml/StyledControls/CustomSwitch.qml | 130 +++ .../qml/StyledControls/CustomTabButton.qml | 46 + .../qml/StyledControls/CustomTextArea.qml | 32 + .../qml/StyledControls/CustomTextInput.qml | 34 + .../CustomTitleDialogWindow.qml | 47 + .../CustomTitleDialogWindowWithExpander.qml | 89 ++ GUI/QtQuick/qml/StyledControls/qmldir | 23 + GUI/QtQuick/qml/main.qml | 135 +++ GUI/QtQuick/qtquick.qrc | 29 + GUI/QtWidgets/QtGuiAdapter.cpp | 49 +- GUI/QtWidgets/QtGuiAdapter.h | 1 - common | 2 +- 50 files changed, 3000 insertions(+), 60 deletions(-) create mode 100644 GUI/QtQuick/CMakeLists.txt create mode 100644 GUI/QtQuick/QtQuickAdapter.cpp create mode 100644 GUI/QtQuick/QtQuickAdapter.h create mode 100644 GUI/QtQuick/images/bs_logo.png create mode 100644 GUI/QtQuick/images/full_logo.png create mode 100644 GUI/QtQuick/images/notification_critical.png create mode 100644 GUI/QtQuick/images/notification_info.png create mode 100644 GUI/QtQuick/images/notification_question.png create mode 100644 GUI/QtQuick/images/notification_success.png create mode 100644 GUI/QtQuick/images/notification_warning.png create mode 100644 GUI/QtQuick/qml/BsStyles/BSStyle.qml create mode 100644 GUI/QtQuick/qml/BsStyles/qmldir create mode 100644 GUI/QtQuick/qml/InfoBanner.qml create mode 100644 GUI/QtQuick/qml/InfoBannerComponent.qml create mode 100644 GUI/QtQuick/qml/InfoBar.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomButton.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomButtonBar.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomCheckBox.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomComboBox.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomContainer.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomContextMenu.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomDialog.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomDialogWindow.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomHeader.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomHeaderPanel.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomLabel.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomLabelCopyableValue.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomLabelValue.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomPasswordTextInput.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomProgressBar.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomRadioButton.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomSwitch.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomTabButton.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomTextArea.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomTextInput.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindow.qml create mode 100644 GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindowWithExpander.qml create mode 100644 GUI/QtQuick/qml/StyledControls/qmldir create mode 100644 GUI/QtQuick/qml/main.qml create mode 100644 GUI/QtQuick/qtquick.qrc diff --git a/BlockSettleApp/CMakeLists.txt b/BlockSettleApp/CMakeLists.txt index b55e0c22f..315ab5303 100644 --- a/BlockSettleApp/CMakeLists.txt +++ b/BlockSettleApp/CMakeLists.txt @@ -49,6 +49,7 @@ ENDIF () TARGET_LINK_LIBRARIES( ${BLOCKSETTLE_APP_NAME} ${TERMINAL_CORE_NAME} ${TERMINAL_GUI_QT_NAME} + ${TERMINAL_GUI_QTQUICK_NAME} ${BLOCKSETTLE_UI_LIBRARY_NAME} ${BS_NETWORK_LIB_NAME} ${CPP_WALLET_LIB_NAME} diff --git a/BlockSettleApp/main.cpp b/BlockSettleApp/main.cpp index 847fb5ea2..ebc4e6579 100644 --- a/BlockSettleApp/main.cpp +++ b/BlockSettleApp/main.cpp @@ -30,9 +30,10 @@ #include "AssetsAdapter.h" #include "BsServerAdapter.h" #include "QtGuiAdapter.h" +#include "QtQuickAdapter.h" #include "SettingsAdapter.h" #include "SignerAdapter.h" - +#include #include //#include "AppNap.h" @@ -55,6 +56,23 @@ Q_IMPORT_PLUGIN(QCupsPrinterSupportPlugin) Q_IMPORT_PLUGIN(QSQLiteDriverPlugin) Q_IMPORT_PLUGIN(QICOPlugin) +#ifdef STATIC_BUILD +#if defined (Q_OS_LINUX) +Q_IMPORT_PLUGIN(QtQuick2PrivateWidgetsPlugin) +#endif + +Q_IMPORT_PLUGIN(QtQuick2Plugin) +Q_IMPORT_PLUGIN(QtQuick2WindowPlugin) +Q_IMPORT_PLUGIN(QtQuickControls2Plugin) +Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin) +//Q_IMPORT_PLUGIN(QtQuickControls1Plugin) +Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin) +Q_IMPORT_PLUGIN(QtQmlModelsPlugin) +Q_IMPORT_PLUGIN(QmlFolderListModelPlugin) +Q_IMPORT_PLUGIN(QmlSettingsPlugin) +//Q_IMPORT_PLUGIN(QtLabsPlatformPlugin) +#endif // STATIC_BUILD + Q_DECLARE_METATYPE(ArmorySettings) Q_DECLARE_METATYPE(AsyncClient::LedgerDelegate) Q_DECLARE_METATYPE(BinaryData) @@ -158,13 +176,22 @@ int main(int argc, char** argv) + QDir::separator() + ApplicationSettings::appSubDir()); const auto &adSettings = std::make_shared(settings, args); const auto &logMgr = adSettings->logManager(); + spdlog::set_default_logger(logMgr->logger()); bs::message::TerminalInprocBus inprocBus(logMgr->logger()); inprocBus.addAdapter(adSettings); const auto &apiAdapter = std::make_shared(logMgr->logger("API")); - const auto &guiAdapter = std::make_shared(logMgr->logger("ui")); - //const auto& guiAdapter = std::make_shared(logMgr->logger("ui")); + std::shared_ptr guiAdapter; + if (adSettings->guiMode() == "qtwidgets") { + guiAdapter = std::make_shared(logMgr->logger("ui")); + } + else if (adSettings->guiMode() == "qtquick") { + guiAdapter = std::make_shared(logMgr->logger("ui")); + } + else { + throw std::runtime_error("unknown GUI mode " + adSettings->guiMode()); + } apiAdapter->add(guiAdapter); apiAdapter->add(std::make_shared(logMgr->logger("json"))); inprocBus.addAdapter(apiAdapter); @@ -174,18 +201,17 @@ int main(int argc, char** argv) const auto& userBlockchain = bs::message::UserTerminal::create(bs::message::TerminalUsers::Blockchain); const auto& userWallets = bs::message::UserTerminal::create(bs::message::TerminalUsers::Wallets); - inprocBus.addAdapter(std::make_shared(logMgr->logger())); + //inprocBus.addAdapter(std::make_shared(logMgr->logger())); inprocBus.addAdapterWithQueue(std::make_shared(logMgr->logger() , userWallets, signAdapter->createClient(), userBlockchain), "wallets"); inprocBus.addAdapter(std::make_shared(logMgr->logger("bscon"))); - //inprocBus.addAdapter(std::make_shared(logMgr->logger("match"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("settl"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("md"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("mdh"))); //inprocBus.addAdapter(std::make_shared(logMgr->logger("chat"))); inprocBus.addAdapterWithQueue(std::make_shared(logMgr->logger() - , userBlockchain), "blkchain_conn"); + , userBlockchain), /*"blkchain_conn"*/"signer"); if (!inprocBus.run(argc, argv)) { logMgr->logger()->error("No runnable adapter found on main inproc bus"); diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c1f5062..fc5e14c8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,7 @@ FIND_PACKAGE(Qt5Widgets REQUIRED) FIND_PACKAGE(Qt5Network REQUIRED) FIND_PACKAGE(Qt5PrintSupport REQUIRED) FIND_PACKAGE(Qt5Qml REQUIRED) +FIND_PACKAGE(Qt5QmlWorkerScript REQUIRED) FIND_PACKAGE(Qt5Quick REQUIRED) FIND_PACKAGE(Qt5QuickControls2 REQUIRED) FIND_PACKAGE(Qt5Charts REQUIRED) @@ -391,6 +392,7 @@ SET( BLOCKSETTLE_APP_NAME blocksettle ) SET( SIGNER_APP_NAME blocksettle_signer ) SET( TERMINAL_CORE_NAME TerminalCore ) SET( TERMINAL_GUI_QT_NAME TerminalGUI_Qt ) +SET( TERMINAL_GUI_QTQUICK_NAME TerminalGUI_QtQuick ) SET( BLOCKSETTLE_UI_LIBRARY_NAME bsuilib ) SET( BLOCKSETTLE_HW_LIBRARY_NAME HWIntegrations ) SET( CRYPTO_LIB_NAME ArmoryCryptoLib ) diff --git a/Core/BsServerAdapter.cpp b/Core/BsServerAdapter.cpp index 6ba839c99..db008164e 100644 --- a/Core/BsServerAdapter.cpp +++ b/Core/BsServerAdapter.cpp @@ -86,7 +86,7 @@ void BsServerAdapter::start() req->set_source(SettingSource_Local); req->set_type(SettingType_Int); req->set_index(SetIdx_Environment); - pushRequest(user_, UserTerminal::create(TerminalUsers::Settings) + const auto msgId = pushRequest(user_, UserTerminal::create(TerminalUsers::Settings) , msg.SerializeAsString()); } @@ -143,8 +143,9 @@ bool BsServerAdapter::processLocalSettings(const SettingsMessage_SettingsRespons { AdministrativeMessage admMsg; admMsg.set_component_loading(user_->value()); - pushBroadcast(UserTerminal::create(TerminalUsers::System) + const auto msgId = pushBroadcast(UserTerminal::create(TerminalUsers::System) , admMsg.SerializeAsString()); + logger_->debug("[BsServerAdapter::processLocalSettings] #{}", msgId); } break; diff --git a/Core/SettingsAdapter.cpp b/Core/SettingsAdapter.cpp index a83dbe696..504efd497 100644 --- a/Core/SettingsAdapter.cpp +++ b/Core/SettingsAdapter.cpp @@ -34,7 +34,8 @@ SettingsAdapter::SettingsAdapter(const std::shared_ptr &set , user_(std::make_shared(TerminalUsers::Settings)) { if (!appSettings_->LoadApplicationSettings(appArgs)) { - throw std::runtime_error("failed to load settings"); + std::cerr << "failed to load settings: " << appSettings_->ErrorText().toStdString() << "\n"; + throw std::runtime_error("failed to load settings: " + appSettings_->ErrorText().toStdString()); } logMgr_ = std::make_shared(); logMgr_->add(appSettings_->GetLogsConfig()); @@ -320,6 +321,14 @@ bool SettingsAdapter::processApiClientsList(const bs::message::Envelope& env) return pushResponse(user_, env, msg.SerializeAsString()); } +std::string SettingsAdapter::guiMode() const +{ + if (!appSettings_) { + return {}; + } + return appSettings_->guiMode().toStdString(); +} + bool SettingsAdapter::processGetRequest(const bs::message::Envelope &env , const SettingsMessage_SettingsRequest &request) { diff --git a/Core/SettingsAdapter.h b/Core/SettingsAdapter.h index 4abc9ab6a..5999dc89b 100644 --- a/Core/SettingsAdapter.h +++ b/Core/SettingsAdapter.h @@ -65,6 +65,7 @@ class SettingsAdapter : public bs::message::Adapter std::string name() const override { return "Settings"; } std::shared_ptr logManager() const { return logMgr_; } + std::string guiMode() const; private: bool processGetRequest(const bs::message::Envelope & diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 769469d40..249175835 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -1,7 +1,7 @@ # # # *********************************************************************************** -# * Copyright (C) 2020 - 2021, BlockSettle AB +# * Copyright (C) 2020 - 2022, BlockSettle AB # * Distributed under the GNU Affero General Public License (AGPL v3) # * See LICENSE or http://www.gnu.org/licenses/agpl.html # * @@ -10,3 +10,4 @@ # ADD_SUBDIRECTORY(QtWidgets) +ADD_SUBDIRECTORY(QtQuick) diff --git a/GUI/QtQuick/CMakeLists.txt b/GUI/QtQuick/CMakeLists.txt new file mode 100644 index 000000000..2aa835366 --- /dev/null +++ b/GUI/QtQuick/CMakeLists.txt @@ -0,0 +1,75 @@ +# +# +# *********************************************************************************** +# * Copyright (C) 2020 - 2021, BlockSettle AB +# * Distributed under the GNU Affero General Public License (AGPL v3) +# * See LICENSE or http://www.gnu.org/licenses/agpl.html +# * +# ********************************************************************************** +# +# + +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) + +PROJECT(${TERMINAL_GUI_QTQUICK_NAME}) + +FILE(GLOB SOURCES + *.cpp +) +FILE(GLOB HEADERS + *.h +) + +INCLUDE_DIRECTORIES(${COMMON_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${BOTAN_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${WALLET_LIB_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${COMMON_UI_LIB_INCLUDE_DIR} ) +INCLUDE_DIRECTORIES(${BS_COMMON_ENUMS_INCLUDE_DIR} ) +INCLUDE_DIRECTORIES(${BS_COMMUNICATION_INCLUDE_DIR} ) +INCLUDE_DIRECTORIES(${BLOCKSETTLE_UI_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${Qt5Svg_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Widgets_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Core_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${Qt5Network_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Qml_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5DBus_INCLUDE_DIRS} ) +INCLUDE_DIRECTORIES(${Qt5Charts_INCLUDE_DIRS} ) + +qt5_add_resources(GENERATED_RESOURCES qtquick.qrc) + +ADD_LIBRARY(${TERMINAL_GUI_QTQUICK_NAME} + ${SOURCES} + ${HEADERS} + ${GENERATED_RESOURCES} +) + +TARGET_INCLUDE_DIRECTORIES(${TERMINAL_GUI_QTQUICK_NAME} + PUBLIC ${BLOCK_SETTLE_ROOT}/GUI/QtQuick + PRIVATE ${BLOCK_SETTLE_ROOT}/Core + PRIVATE ${BLOCK_SETTLE_ROOT}/common/BlocksettleNetworkingLib + PRIVATE ${BLOCK_SETTLE_ROOT}/BlockSettleUILib +) + +TARGET_LINK_LIBRARIES(${TERMINAL_GUI_QTQUICK_NAME} + ${BLOCKSETTLE_UI_LIBRARY_NAME} + ${BS_NETWORK_LIB_NAME} + ${CRYPTO_LIB_NAME} + ${QT_LINUX_LIBS} + ${QT_QUICK_LIBS} + Qt5::Network + Qt5::Core + Qt5::Widgets + Qt5::Qml + Qt5::QmlWorkerScript + Qt5::Quick + Qt5::Gui + Qt5::Network + Qt5::PrintSupport + Qt5::Core + Qt5::Svg + Qt5::DBus + ${QT_LIBS} + ${OS_SPECIFIC_LIBS} + ${OPENSSL_LIBS} +) diff --git a/GUI/QtQuick/QtQuickAdapter.cpp b/GUI/QtQuick/QtQuickAdapter.cpp new file mode 100644 index 000000000..b5f3e464d --- /dev/null +++ b/GUI/QtQuick/QtQuickAdapter.cpp @@ -0,0 +1,906 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020 - 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#include "CommonTypes.h" +#include "QtQuickAdapter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BSMessageBox.h" +#include "BSTerminalSplashScreen.h" +#include "Wallets/ProtobufHeadlessUtils.h" +#include "SettingsAdapter.h" + +#include "common.pb.h" +#include "terminal.pb.h" + +using namespace BlockSettle::Common; +using namespace BlockSettle::Terminal; +using namespace bs::message; + + +namespace { + std::shared_ptr staticLogger; +} +// redirect qDebug() to the log +// stdout redirected to parent process +void qMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + switch (type) { + case QtDebugMsg: + staticLogger->debug("[QML] {}", localMsg.constData()); + break; + case QtInfoMsg: + staticLogger->info("[QML] {}", localMsg.constData()); + break; + case QtWarningMsg: + staticLogger->warn("[QML] {}", localMsg.constData()); + break; + case QtCriticalMsg: + staticLogger->error("[QML] {}", localMsg.constData()); + break; + case QtFatalMsg: + staticLogger->critical("[QML] {}", localMsg.constData()); + break; + } +} + +static void checkStyleSheet(QApplication& app) +{ + QLatin1String styleSheetFileName = QLatin1String("stylesheet.css"); + + QFileInfo info = QFileInfo(QLatin1String(styleSheetFileName)); + + static QDateTime lastTimestamp = info.lastModified(); + + if (lastTimestamp == info.lastModified()) { + return; + } + + lastTimestamp = info.lastModified(); + + QFile stylesheetFile(styleSheetFileName); + + bool result = stylesheetFile.open(QFile::ReadOnly); + assert(result); + + app.setStyleSheet(QString::fromLatin1(stylesheetFile.readAll())); +} + +QtQuickAdapter::QtQuickAdapter(const std::shared_ptr &logger) + : QObject(nullptr), logger_(logger) + , userSettings_(std::make_shared(TerminalUsers::Settings)) + , userWallets_(std::make_shared(TerminalUsers::Wallets)) + , userBlockchain_(std::make_shared(TerminalUsers::Blockchain)) + , userSigner_(std::make_shared(TerminalUsers::Signer)) +{ + staticLogger = logger; +} + +QtQuickAdapter::~QtQuickAdapter() +{} + +void QtQuickAdapter::run(int &argc, char **argv) +{ + logger_->debug("[QtQuickAdapter::run]"); + Q_INIT_RESOURCE(armory); + Q_INIT_RESOURCE(qtquick); + +// QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + QApplication app(argc, argv); + + QApplication::setOrganizationDomain(QLatin1String("blocksettle.com")); + QApplication::setWindowIcon(QIcon(QStringLiteral(":/images/bs_logo.png"))); + + const QFileInfo localStyleSheetFile(QLatin1String("stylesheet.css")); + QFile stylesheetFile(localStyleSheetFile.exists() + ? localStyleSheetFile.fileName() : QLatin1String(":/STYLESHEET")); + + if (stylesheetFile.open(QFile::ReadOnly)) { + app.setStyleSheet(QString::fromLatin1(stylesheetFile.readAll())); + QPalette p = QApplication::palette(); + p.setColor(QPalette::Disabled, QPalette::Light, QColor(10, 22, 25)); + QApplication::setPalette(p); + } + + // Start monitoring to update stylesheet live when file is changed on the disk + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, &app, [&app] { + checkStyleSheet(app); + }); + timer.start(100); + + const QString location = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + const QString lockFilePath = location + QLatin1String("/blocksettle.lock"); + QLockFile lockFile(lockFilePath); + lockFile.setStaleLockTime(0); + + if (!lockFile.tryLock()) { + BSMessageBox box(BSMessageBox::info, app.tr("BlockSettle Terminal") + , app.tr("BlockSettle Terminal is already running") + , app.tr("Stop the other BlockSettle Terminal instance. If no other " \ + "instance is running, delete the lockfile (%1).").arg(lockFilePath)); + box.exec(); + return; + } + + qInstallMessageHandler(qMessageHandler); + + QString logoIcon; + logoIcon = QLatin1String(":/FULL_BS_LOGO"); + + QPixmap splashLogo(logoIcon); + const int splashScreenWidth = 400; + { + std::lock_guard lock(mutex_); + splashScreen_ = new BSTerminalSplashScreen(splashLogo.scaledToWidth(splashScreenWidth + , Qt::SmoothTransformation)); + } + updateSplashProgress(); + splashScreen_->show(); + QMetaObject::invokeMethod(splashScreen_, [this] { + QTimer::singleShot(5000, [this] { + splashProgressCompleted(); + }); + }); + + logger_->debug("[QtGuiAdapter::run] creating QML app"); + QQmlApplicationEngine engine; + QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering); + const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); + engine.rootContext()->setContextProperty(QStringLiteral("fixedFont"), fixedFont); + engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + if (engine.rootObjects().empty()) { + BSMessageBox box(BSMessageBox::critical, app.tr("BlockSettle Terminal") + , app.tr("Failed to load QML GUI"), app.tr("See log for details")); + box.exec(); + return; + } + rootObj_ = engine.rootObjects().at(0); + if (loadingDone_) { + auto window = qobject_cast(rootObj_); + if (window) { + window->show(); + } + } + + updateStates(); + + requestInitialSettings(); + logger_->debug("[QtGuiAdapter::run] initial setup done"); + app.exec(); +} + +bool QtQuickAdapter::process(const Envelope &env) +{ + if (std::dynamic_pointer_cast(env.sender)) { + switch (env.sender->value()) { + case TerminalUsers::Settings: + return processSettings(env); + case TerminalUsers::Blockchain: + return processBlockchain(env); + case TerminalUsers::Signer: + return processSigner(env); + case TerminalUsers::Wallets: + return processWallets(env); + default: break; + } + } + return true; +} + +bool QtQuickAdapter::processBroadcast(const bs::message::Envelope& env) +{ + if (std::dynamic_pointer_cast(env.sender)) { + switch (env.sender->value()) { + case TerminalUsers::System: + return processAdminMessage(env); + case TerminalUsers::Settings: + return processSettings(env); + case TerminalUsers::Blockchain: + return processBlockchain(env); + case TerminalUsers::Signer: + return processSigner(env); + case TerminalUsers::Wallets: + return processWallets(env); + default: break; + } + } + return false; +} + +bool QtQuickAdapter::processSettings(const Envelope &env) +{ + SettingsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse settings msg #{}", __func__, env.foreignId()); + return true; + } + switch (msg.data_case()) { + case SettingsMessage::kGetResponse: + return processSettingsGetResponse(msg.get_response()); + case SettingsMessage::kSettingsUpdated: + return processSettingsGetResponse(msg.settings_updated()); + case SettingsMessage::kState: + return processSettingsState(msg.state()); + case SettingsMessage::kArmoryServers: + return processArmoryServers(msg.armory_servers()); + default: break; + } + return true; +} + +bool QtQuickAdapter::processSettingsGetResponse(const SettingsMessage_SettingsResponse &response) +{ + std::map settings; + for (const auto &setting : response.responses()) { + switch (setting.request().index()) { + case SetIdx_GUI_MainGeom: { + QRect mainGeometry(setting.rect().left(), setting.rect().top() + , setting.rect().width(), setting.rect().height()); + if (splashScreen_) { +// const auto ¤tDisplay = getDisplay(mainGeometry.center()); +// auto splashGeometry = splashScreen_->geometry(); +// splashGeometry.moveCenter(currentDisplay->geometry().center()); +// QMetaObject::invokeMethod(splashScreen_, [ss=splashScreen_, splashGeometry] { +// ss->setGeometry(splashGeometry); +// }); + } + } + break; + + case SetIdx_Initialized: + if (!setting.b()) { +#ifdef _WIN32 + // Read registry value in case it was set with installer. Could be used only on Windows for now. + QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Software\\blocksettle\\blocksettle"), QSettings::NativeFormat); + bool showLicense = !settings.value(QLatin1String("license_accepted"), false).toBool(); +#else + bool showLicense = true; +#endif // _WIN32 + //onResetSettings({}); + } + break; + + default: + settings[setting.request().index()] = fromResponse(setting); + break; + } + } + if (!settings.empty()) { + //TODO: propagate settings to GUI + } + return true; +} + +bool QtQuickAdapter::processSettingsState(const SettingsMessage_SettingsResponse& response) +{ + ApplicationSettings::State state; + for (const auto& setting : response.responses()) { + state[static_cast(setting.request().index())] = + fromResponse(setting); + } + //TODO: process setting + return true; +} + +bool QtQuickAdapter::processArmoryServers(const SettingsMessage_ArmoryServers& response) +{ + QList servers; + for (const auto& server : response.servers()) { + servers << ArmoryServer{ QString::fromStdString(server.server_name()) + , static_cast(server.network_type()) + , QString::fromStdString(server.server_address()) + , std::stoi(server.server_port()), QString::fromStdString(server.server_key()) + , SecureBinaryData::fromString(server.password()) + , server.run_locally(), server.one_way_auth() }; + } + logger_->debug("[{}] {} servers, cur: {}, conn: {}", __func__, servers.size() + , response.idx_current(), response.idx_connected()); + //TODO + return true; +} + +bool QtQuickAdapter::processAdminMessage(const Envelope &env) +{ + AdministrativeMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse admin msg #{}", __func__, env.foreignId()); + return true; + } + switch (msg.data_case()) { + case AdministrativeMessage::kComponentCreated: + switch (static_cast(msg.component_created())) { + case TerminalUsers::Unknown: + case TerminalUsers::API: + case TerminalUsers::Settings: + break; + default: + createdComponents_.insert(msg.component_created()); + break; + } + break; + case AdministrativeMessage::kComponentLoading: { + std::lock_guard lock(mutex_); + loadingComponents_.insert(msg.component_loading()); + break; + } + default: break; + } + updateSplashProgress(); + return true; +} + +bool QtQuickAdapter::processBlockchain(const Envelope &env) +{ + ArmoryMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[QtGuiAdapter::processBlockchain] failed to parse msg #{}" + , env.foreignId()); + if (!env.receiver) { + logger_->debug("[{}] no receiver", __func__); + } + return true; + } + switch (msg.data_case()) { + case ArmoryMessage::kLoading: + loadingComponents_.insert(env.sender->value()); + updateSplashProgress(); + break; + case ArmoryMessage::kStateChanged: + armoryState_ = msg.state_changed().state(); + blockNum_ = msg.state_changed().top_block(); + //TODO + break; + case ArmoryMessage::kNewBlock: + blockNum_ = msg.new_block().top_block(); + //TODO + break; + case ArmoryMessage::kWalletRegistered: + if (msg.wallet_registered().success() && msg.wallet_registered().wallet_id().empty()) { + walletsReady_ = true; + //TODO + } + break; + case ArmoryMessage::kAddressHistory: + return processAddressHist(msg.address_history()); + case ArmoryMessage::kFeeLevelsResponse: + return processFeeLevels(msg.fee_levels_response()); + case ArmoryMessage::kZcReceived: + return processZC(msg.zc_received()); + case ArmoryMessage::kZcInvalidated: + return processZCInvalidated(msg.zc_invalidated()); + default: break; + } + return true; +} + +bool QtQuickAdapter::processSigner(const Envelope &env) +{ + SignerMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[QtGuiAdapter::processSigner] failed to parse msg #{}" + , env.foreignId()); + if (!env.receiver) { + logger_->debug("[{}] no receiver", __func__); + } + return true; + } + switch (msg.data_case()) { + case SignerMessage::kState: + signerState_ = msg.state().code(); + signerDetails_ = msg.state().text(); + //TODO + break; + case SignerMessage::kNeedNewWalletPrompt: + createWallet(true); + break; + case SignerMessage::kSignTxResponse: + return processSignTX(msg.sign_tx_response()); + case SignerMessage::kWalletDeleted: + { + const auto& itWallet = hdWallets_.find(msg.wallet_deleted()); + bs::sync::WalletInfo wi; + if (itWallet == hdWallets_.end()) { + wi.ids.push_back(msg.wallet_deleted()); + } else { + wi = itWallet->second; + } + //TODO + } + break; + default: break; + } + return true; +} + +bool QtQuickAdapter::processWallets(const Envelope &env) +{ + WalletsMessage msg; + if (!msg.ParseFromString(env.message)) { + logger_->error("[{}] failed to parse msg #{}", __func__, env.foreignId()); + return true; + } + switch (msg.data_case()) { + case WalletsMessage::kLoading: + loadingComponents_.insert(env.sender->value()); + updateSplashProgress(); + break; + + case WalletsMessage::kWalletLoaded: { + const auto &wi = bs::sync::WalletInfo::fromCommonMsg(msg.wallet_loaded()); + processWalletLoaded(wi); + } + break; + + case WalletsMessage::kHdWallet: { + const auto &hdw = bs::sync::HDWalletData::fromCommonMessage(msg.hd_wallet()); + //TODO + } + break; + + case WalletsMessage::kWalletDeleted: { + const auto& wi = bs::sync::WalletInfo::fromCommonMsg(msg.wallet_deleted()); + //TODO + } + break; + + case WalletsMessage::kWalletAddresses: { + std::vector addresses; + for (const auto &addr : msg.wallet_addresses().addresses()) { + try { + addresses.push_back({ std::move(bs::Address::fromAddressString(addr.address())) + , addr.index(), addr.wallet_id() }); + } + catch (const std::exception &) {} + } + const auto& walletId = msg.wallet_addresses().wallet_id(); + auto itReq = needChangeAddrReqs_.find(env.responseId()); + if (itReq != needChangeAddrReqs_.end()) { + //TODO + needChangeAddrReqs_.erase(itReq); + } + else { + //TODO + } + } + break; + + case WalletsMessage::kAddrComments: { + std::map comments; + for (const auto &addrComment : msg.addr_comments().comments()) { + try { + comments[bs::Address::fromAddressString(addrComment.address())] = addrComment.comment(); + } + catch (const std::exception &) {} + } + //TODO + } + break; + case WalletsMessage::kWalletData: + return processWalletData(env.responseId(), msg.wallet_data()); + case WalletsMessage::kWalletBalances: + return processWalletBalances(env, msg.wallet_balances()); + case WalletsMessage::kTxDetailsResponse: + return processTXDetails(env.responseId(), msg.tx_details_response()); + case WalletsMessage::kWalletsListResponse: + return processWalletsList(msg.wallets_list_response()); + case WalletsMessage::kUtxos: + return processUTXOs(msg.utxos()); + case WalletsMessage::kReservedUtxos: + return processReservedUTXOs(msg.reserved_utxos()); + case WalletsMessage::kWalletChanged: + if (walletsReady_) { + //TODO: onNeedLedgerEntries({}); + } + break; + case WalletsMessage::kLedgerEntries: + return processLedgerEntries(msg.ledger_entries()); + default: break; + } + return true; +} + +void QtQuickAdapter::updateStates() +{ + //TODO +} + +//#define DEBUG_LOADING_PROGRESS +void QtQuickAdapter::updateSplashProgress() +{ + std::lock_guard lock(mutex_); + if (createdComponents_.empty()) { + if (splashScreen_) { + QMetaObject::invokeMethod(splashScreen_, [this] { + QTimer::singleShot(100, [this] { + splashScreen_->hide(); + splashScreen_->deleteLater(); + splashScreen_ = nullptr; + }); + }); + } + return; + } + auto percent = unsigned(100 * loadingComponents_.size() / createdComponents_.size()); +#ifdef DEBUG_LOADING_PROGRESS + std::string l, c; + for (const auto &lc : loadingComponents_) { + l += std::to_string(lc) + " "; + } + for (const auto &cc : createdComponents_) { + c += std::to_string(cc) + " "; + } + logger_->debug("[{}] {} / {} = {}%", __func__, l, c, percent); +#endif + if (splashScreen_) { + QMetaObject::invokeMethod(splashScreen_, [this, percent] { + splashScreen_->SetProgress(percent); + }); + } + if (percent >= 100) { + splashProgressCompleted(); + } +} + +void QtQuickAdapter::splashProgressCompleted() +{ + { + std::lock_guard lock(mutex_); + loadingDone_ = true; + loadingComponents_.clear(); + createdComponents_.clear(); + } + if (!splashScreen_) { + return; + } + QMetaObject::invokeMethod(splashScreen_, [this] { + auto window = qobject_cast(rootObj_); + if (window) { + window->show(); + } + else { + logger_->error("[QtQuickAdapter::splashProgressCompleted] no main window found"); + } + QTimer::singleShot(100, [this] { + splashScreen_->hide(); + splashScreen_->deleteLater(); + splashScreen_ = nullptr; + }); + }); +} + +void QtQuickAdapter::requestInitialSettings() +{ + SettingsMessage msg; + auto msgReq = msg.mutable_get_request(); + auto setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_GUI_MainGeom); + setReq->set_type(SettingType_Rect); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_Initialized); + setReq->set_type(SettingType_Bool); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_GUI_MainTab); + setReq->set_type(SettingType_Int); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_ShowInfoWidget); + setReq->set_type(SettingType_Bool); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_AdvancedTXisDefault); + setReq->set_type(SettingType_Bool); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_CloseToTray); + setReq->set_type(SettingType_Bool); + + setReq = msgReq->add_requests(); + setReq->set_source(SettingSource_Local); + setReq->set_index(SetIdx_Environment); + setReq->set_type(SettingType_Int); + + pushRequest(user_, userSettings_, msg.SerializeAsString()); +} + +void QtQuickAdapter::createWallet(bool primary) +{ + logger_->debug("[{}] primary: {}", __func__, primary); +} + +void QtQuickAdapter::processWalletLoaded(const bs::sync::WalletInfo &wi) +{ + hdWallets_[*wi.ids.cbegin()] = wi; + //TODO +} + +bool QtQuickAdapter::processWalletData(uint64_t msgId + , const WalletsMessage_WalletData& response) +{ + const auto& itWallet = walletGetMap_.find(msgId); + if (itWallet == walletGetMap_.end()) { + return true; + } + const auto& walletId = itWallet->second; + const auto& walletData = bs::sync::WalletData::fromCommonMessage(response); + //TODO + { + walletGetMap_.erase(itWallet); + return true; + } + return false; +} + +bool QtQuickAdapter::processWalletBalances(const bs::message::Envelope & + , const WalletsMessage_WalletBalances &response) +{ + bs::sync::WalletBalanceData wbd; + wbd.id = response.wallet_id(); + wbd.balTotal = response.total_balance(); + wbd.balSpendable = response.spendable_balance(); + wbd.balUnconfirmed = response.unconfirmed_balance(); + wbd.nbAddresses = response.nb_addresses(); + for (const auto &addrBal : response.address_balances()) { + wbd.addrBalances.push_back({ BinaryData::fromString(addrBal.address()) + , addrBal.tx_count(), (int64_t)addrBal.total_balance(), (int64_t)addrBal.spendable_balance() + , (int64_t)addrBal.unconfirmed_balance() }); + } + //TODO + return true; +} + +bool QtQuickAdapter::processTXDetails(uint64_t msgId, const WalletsMessage_TXDetailsResponse &response) +{ + std::vector txDetails; + for (const auto &resp : response.responses()) { + bs::sync::TXWalletDetails txDet{ BinaryData::fromString(resp.tx_hash()), resp.wallet_id() + , resp.wallet_name(), static_cast(resp.wallet_type()) + , resp.wallet_symbol(), static_cast(resp.direction()) + , resp.comment(), resp.valid(), resp.amount() }; + if (!response.error_msg().empty()) { + txDet.comment = response.error_msg(); + } + + const auto &ownTxHash = BinaryData::fromString(resp.tx_hash()); + try { + if (!resp.tx().empty()) { + Tx tx(BinaryData::fromString(resp.tx())); + if (tx.isInitialized()) { + txDet.tx = std::move(tx); + } + } + } catch (const std::exception &e) { + logger_->warn("[QtGuiAdapter::processTXDetails] TX deser error: {}", e.what()); + } + for (const auto &addrStr : resp.out_addresses()) { + try { + txDet.outAddresses.push_back(std::move(bs::Address::fromAddressString(addrStr))); + } catch (const std::exception &e) { + logger_->warn("[QtGuiAdapter::processTXDetails] out deser error: {}", e.what()); + } + } + for (const auto &inAddr : resp.input_addresses()) { + try { + txDet.inputAddresses.push_back({ bs::Address::fromAddressString(inAddr.address()) + , inAddr.value(), inAddr.value_string(), inAddr.wallet_name() + , static_cast(inAddr.script_type()) + , BinaryData::fromString(inAddr.out_hash()), inAddr.out_index() }); + } catch (const std::exception &e) { + logger_->warn("[QtGuiAdapter::processTXDetails] input deser error: {}", e.what()); + } + } + for (const auto &outAddr : resp.output_addresses()) { + try { + txDet.outputAddresses.push_back({ bs::Address::fromAddressString(outAddr.address()) + , outAddr.value(), outAddr.value_string(), outAddr.wallet_name() + , static_cast(outAddr.script_type()) + , BinaryData::fromString(outAddr.out_hash()), outAddr.out_index() }); + } catch (const std::exception &) { // OP_RETURN data for valueStr + txDet.outputAddresses.push_back({ bs::Address{} + , outAddr.value(), outAddr.address(), outAddr.wallet_name() + , static_cast(outAddr.script_type()), ownTxHash + , outAddr.out_index() }); + } + } + try { + txDet.changeAddress = { bs::Address::fromAddressString(resp.change_address().address()) + , resp.change_address().value(), resp.change_address().value_string() + , resp.change_address().wallet_name() + , static_cast(resp.change_address().script_type()) + , BinaryData::fromString(resp.change_address().out_hash()) + , resp.change_address().out_index() }; + } + catch (const std::exception &) {} + txDetails.emplace_back(std::move(txDet)); + } + if (!response.responses_size() && !response.error_msg().empty()) { + bs::sync::TXWalletDetails txDet; + txDet.comment = response.error_msg(); + txDetails.emplace_back(std::move(txDet)); + } + const auto& itZC = newZCs_.find(msgId); + if (itZC != newZCs_.end()) { + newZCs_.erase(itZC); + //TODO + } + else { + //TODO + } + return true; +} + +bool QtQuickAdapter::processLedgerEntries(const LedgerEntries &response) +{ + std::vector entries; + for (const auto &entry : response.entries()) { + bs::TXEntry txEntry; + txEntry.txHash = BinaryData::fromString(entry.tx_hash()); + txEntry.value = entry.value(); + txEntry.blockNum = entry.block_num(); + txEntry.txTime = entry.tx_time(); + txEntry.isRBF = entry.rbf(); + txEntry.isChainedZC = entry.chained_zc(); + txEntry.nbConf = entry.nb_conf(); + for (const auto &walletId : entry.wallet_ids()) { + txEntry.walletIds.insert(walletId); + } + for (const auto &addrStr : entry.addresses()) { + try { + const auto &addr = bs::Address::fromAddressString(addrStr); + txEntry.addresses.push_back(addr); + } + catch (const std::exception &) {} + } + entries.push_back(std::move(txEntry)); + } + //TODO + return true; +} + + +bool QtQuickAdapter::processAddressHist(const ArmoryMessage_AddressHistory& response) +{ + bs::Address addr; + try { + addr = std::move(bs::Address::fromAddressString(response.address())); + } + catch (const std::exception& e) { + logger_->error("[{}] invalid address: {}", __func__, e.what()); + return true; + } + std::vector entries; + for (const auto& entry : response.entries()) { + bs::TXEntry txEntry; + txEntry.txHash = BinaryData::fromString(entry.tx_hash()); + txEntry.value = entry.value(); + txEntry.blockNum = entry.block_num(); + txEntry.txTime = entry.tx_time(); + txEntry.isRBF = entry.rbf(); + txEntry.isChainedZC = entry.chained_zc(); + txEntry.nbConf = entry.nb_conf(); + for (const auto& walletId : entry.wallet_ids()) { + txEntry.walletIds.insert(walletId); + } + for (const auto& addrStr : entry.addresses()) { + try { + const auto& addr = bs::Address::fromAddressString(addrStr); + txEntry.addresses.push_back(addr); + } + catch (const std::exception&) {} + } + entries.push_back(std::move(txEntry)); + } + //TODO + return true; +} + +bool QtQuickAdapter::processFeeLevels(const ArmoryMessage_FeeLevelsResponse& response) +{ + std::map feeLevels; + for (const auto& pair : response.fee_levels()) { + feeLevels[pair.level()] = pair.fee(); + } + //TODO + return true; +} + +bool QtQuickAdapter::processWalletsList(const WalletsMessage_WalletsListResponse& response) +{ + std::vector wallets; + for (const auto& wallet : response.wallets()) { + wallets.push_back(bs::sync::HDWalletData::fromCommonMessage(wallet)); + } + //TODO + return true; +} + +bool QtQuickAdapter::processUTXOs(const WalletsMessage_UtxoListResponse& response) +{ + std::vector utxos; + for (const auto& serUtxo : response.utxos()) { + UTXO utxo; + utxo.unserialize(BinaryData::fromString(serUtxo)); + utxos.push_back(std::move(utxo)); + } + //TODO + return true; +} + +bool QtQuickAdapter::processSignTX(const BlockSettle::Common::SignerMessage_SignTxResponse& response) +{ + //TODO + return true; +} + +bool QtQuickAdapter::processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived& zcs) +{ + WalletsMessage msg; + auto msgReq = msg.mutable_tx_details_request(); + for (const auto& zcEntry : zcs.tx_entries()) { + auto txReq = msgReq->add_requests(); + txReq->set_tx_hash(zcEntry.tx_hash()); + if (zcEntry.wallet_ids_size() > 0) { + txReq->set_wallet_id(zcEntry.wallet_ids(0)); + } + txReq->set_value(zcEntry.value()); + } + const auto msgId = pushRequest(user_, userWallets_, msg.SerializeAsString()); + if (!msgId) { + return false; + } + newZCs_.insert(msgId); + return true; +} + +bool QtQuickAdapter::processZCInvalidated(const ArmoryMessage_ZCInvalidated& zcInv) +{ + std::vector txHashes; + for (const auto& hashStr : zcInv.tx_hashes()) { + txHashes.push_back(BinaryData::fromString(hashStr)); + } + //TODO + return true; +} + +bool QtQuickAdapter::processReservedUTXOs(const WalletsMessage_ReservedUTXOs& response) +{ + std::vector utxos; + for (const auto& utxoSer : response.utxos()) { + UTXO utxo; + utxo.unserialize(BinaryData::fromString(utxoSer)); + utxos.push_back(std::move(utxo)); + } + //TODO + return true; +} diff --git a/GUI/QtQuick/QtQuickAdapter.h b/GUI/QtQuick/QtQuickAdapter.h new file mode 100644 index 000000000..03312cd93 --- /dev/null +++ b/GUI/QtQuick/QtQuickAdapter.h @@ -0,0 +1,146 @@ +/* + +*********************************************************************************** +* Copyright (C) 2020 - 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +#ifndef QT_QUICK_ADAPTER_H +#define QT_QUICK_ADAPTER_H + +#include +#include +#include "Address.h" +#include "ApiAdapter.h" +#include "Wallets/SignContainer.h" +#include "ThreadSafeClasses.h" +#include "UiUtils.h" + +namespace bs { + namespace gui { + namespace qt { + class MainWindow; + } + } +} +namespace BlockSettle { + namespace Common { + class ArmoryMessage_AddressHistory; + class ArmoryMessage_FeeLevelsResponse; + class ArmoryMessage_ZCInvalidated; + class ArmoryMessage_ZCReceived; + class LedgerEntries; + class OnChainTrackMessage_AuthAddresses; + class OnChainTrackMessage_AuthState; + class SignerMessage_SignTxResponse; + class WalletsMessage_AuthKey; + class WalletsMessage_ReservedUTXOs; + class WalletsMessage_TXDetailsResponse; + class WalletsMessage_UtxoListResponse; + class WalletsMessage_WalletBalances; + class WalletsMessage_WalletData; + class WalletsMessage_WalletsListResponse; + } + namespace Terminal { + class AssetsMessage_Balance; + class AssetsMessage_SubmittedAuthAddresses; + class BsServerMessage_LoginResult; + class BsServerMessage_Orders; + class BsServerMessage_StartLoginResult; + class MatchingMessage_LoggedIn; + class Quote; + class QuoteCancelled; + class IncomingRFQ; + class MatchingMessage_Order; + class MktDataMessage_Prices; + class SettlementMessage_FailedSettlement; + class SettlementMessage_MatchedQuote; + class SettlementMessage_PendingSettlement; + class SettlementMessage_SettlementIds; + class SettingsMessage_ArmoryServers; + class SettingsMessage_SettingsResponse; + class SettingsMessage_SignerServers; + } +} + +class BSTerminalSplashScreen; + +class QtQuickAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRuner +{ + Q_OBJECT + friend class GuiThread; +public: + QtQuickAdapter(const std::shared_ptr &); + ~QtQuickAdapter() override; + + bool process(const bs::message::Envelope &) override; + bool processBroadcast(const bs::message::Envelope&) override; + + Users supportedReceivers() const override { return { user_ }; } + std::string name() const override { return "QtQuick"; } + + void run(int &argc, char **argv) override; + +private: + bool processSettings(const bs::message::Envelope &); + bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); + bool processSettingsState(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); + bool processArmoryServers(const BlockSettle::Terminal::SettingsMessage_ArmoryServers&); + bool processAdminMessage(const bs::message::Envelope &); + bool processBlockchain(const bs::message::Envelope &); + bool processSigner(const bs::message::Envelope &); + bool processWallets(const bs::message::Envelope &); + + void requestInitialSettings(); + void updateSplashProgress(); + void splashProgressCompleted(); + void updateStates(); + + void createWallet(bool primary); + + void processWalletLoaded(const bs::sync::WalletInfo &); + bool processWalletData(const uint64_t msgId + , const BlockSettle::Common::WalletsMessage_WalletData&); + bool processWalletBalances(const bs::message::Envelope & + , const BlockSettle::Common::WalletsMessage_WalletBalances &); + bool processTXDetails(uint64_t msgId, const BlockSettle::Common::WalletsMessage_TXDetailsResponse &); + bool processLedgerEntries(const BlockSettle::Common::LedgerEntries &); + bool processAddressHist(const BlockSettle::Common::ArmoryMessage_AddressHistory&); + bool processFeeLevels(const BlockSettle::Common::ArmoryMessage_FeeLevelsResponse&); + bool processWalletsList(const BlockSettle::Common::WalletsMessage_WalletsListResponse&); + bool processUTXOs(const BlockSettle::Common::WalletsMessage_UtxoListResponse&); + bool processSignTX(const BlockSettle::Common::SignerMessage_SignTxResponse&); + bool processZC(const BlockSettle::Common::ArmoryMessage_ZCReceived&); + bool processZCInvalidated(const BlockSettle::Common::ArmoryMessage_ZCInvalidated&); + bool processReservedUTXOs(const BlockSettle::Common::WalletsMessage_ReservedUTXOs&); + +private: + std::shared_ptr logger_; + BSTerminalSplashScreen* splashScreen_{ nullptr }; + QObject* rootObj_{ nullptr }; + std::shared_ptr userSettings_, userWallets_; + std::shared_ptr userBlockchain_, userSigner_; + bool loadingDone_{ false }; + + std::recursive_mutex mutex_; + std::set createdComponents_; + std::set loadingComponents_; + int armoryState_{ -1 }; + uint32_t blockNum_{ 0 }; + int signerState_{ -1 }; + std::string signerDetails_; + bool walletsReady_{ false }; + + std::map walletGetMap_; + std::unordered_map hdWallets_; + std::set newZCs_; + + std::unordered_map assetTypes_; + std::set needChangeAddrReqs_; +}; + + +#endif // QT_QUICK_ADAPTER_H diff --git a/GUI/QtQuick/images/bs_logo.png b/GUI/QtQuick/images/bs_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..640473792e366e39f53ad1ac64fd028f6ebcdf09 GIT binary patch literal 3134 zcmeHKSv(Ys9vwmGTOMHxdA`HedF^VkN zB9yTvdsFtogkkJ+yYKh$KHi6O&eJ*P_xnHnPrMZjB*1rx4*&oNfX(k&|JKa^;0(`i zHsHYg_$?gItU<H*0W0DylFeD5wim}5Pk7j5e)(qs9_jML4TKjMz@nN%`k*!oiD zu*=8Ej|m?mR&N!(MZBqkxl@)YBd{txDR*xY)~j7~J@OyKOKTJ=yq59jRhczsD5pkh zboi2ig}eD|%iQzn?PgXhI~zBT!9V+O$PQ~{ISlLz;t!ceK3q|p%8G-Z-Cv+ja~tyk zfZ?x-0BzPUMPMAF;#Lm;sPa#IL;(NUvYP-h+%3i?oSO$O3_vQ>cF4DJo>KU25V5BT zqarv&xB;07m1sV4TX+H|0BVZR;YOnFm~w=dTvN>B1m608?Em}-5nLiQ98V`pO$7zk zhckH=-K^(e*`w9&Y9-p1MU=&OVKGp^$y3K=8+%9SpM4~+@py$vOmiBRP+3eG=ah-= zbifEyUf5qR^U2LTr}89hPqVtXjxaX3S>ZkD5LVz#ugM`uw!eHgUtc2c^ofWutdqZL zbQIO%*dH8QgdS?Vjtr#m_JRXHPxtaC+E{I!p^6)o#nZ>37#X-OAFc`CnO z9Pdx=U@L?)jIPLo3yHm;l;n~)o~1^_u&SY6>Ry15g8U;hZ$XQelJHC;t>jkKVv^6o zPVIH4$gwM4{qy%iP6{HJU0Fr378lsf^*;Ison6i{gWeOi{sC1wR@Do$Het^N5K(C< zB4WeZjfTSIA`~Cf7a?9cvNyUws~yYD8K*6WhWfl;yB>#GU1>{$1baWay!utEcA)dK z%I<5?-s&CK4z;tU`IIjm-B@xhM`(ZfxD2GN~&UqmD$;5MOkIuIzhIFAWm&CkDk zjLRe0n0cq4Ecz)Vm1C;PB6sHJw1-Y?mU4w#%ai2edEuj1le$GbM*a#tBtj%pkh9|- z)Q>$T3QDInC*@GWLD~|CU{B~p!|fWW!uCP4@hx1mJC(78Ep6QeK zjT%ft#7VpYl5-3zoq=_s_YDlv>5ye-|2(SXTuP4WS%e%QCvvRGB*cqrrjM{T6)+Dp ztVi>YNt`X||6Ud9Q8f)Zwa2T4u~L@B5WkPc^keb7qbFsNf$DuN?dTm}f?2w6cf`Rj z#op1{hb7`t2U~r|W>z!HzVtVIX_mm8noA57pNsK0X;sbMwXw%PmF2L9e3fey;XWTfL%(h5^QfGhX0>sAzNX%z?oy+@ z0k1S2rK}dtR!;f^@3G128Ja)o=t%ZqEp>v@8@UeZX!N19I7}fl7!KE0>cO309%E;k z$S-c>aR1@{UaLkX;SxgbRJmMK*7)JS$jR_f?6KSw!#de?PQs*4S`uP7>F^=PFQ1zk zUUn{3)T6|~2;)DBG!9D)4meVL{_G}!Ac}aK_A~E~g}vK}-Lxpn2rXrP2m`^pa8Tn1-b1ytiy3(5!&*c^A(xF~`MIPFw8I;SlwDw`2e(Q@{%iX8?NrIlt&b}Wa zoQ=9K`46c7#jWJhq-B`a>ucHt(hclGQ6R7L{?Jj9hWo}Py+vc=n7{Q528P78rI-r2 zMTJ+DeQA^-IBNUAfA#2<@3sugbsddTCw%9)uSMY>@6!3=rw7&+L%MWQHS*rBfg)-N7 z0@wt*!JM=a0sONgn?4q&ry%%D0MH&Tgn;7sOy9F$f74^sbY&G23DQ`^14onerOKG!I(HZA;v|vP-$4jgH(`onhexWD9qi){?1Kj{>?jCWodOP_o;ZYwaFt)tc`-iyjB=jQ@RU zTWvvze-TQSNix#x)w^Yape16@-ydFIzps|$(jLm28O=MDJ%4#piQ0gkym_EdGxMD% zJbn{X82zy6X|@N?#Qx?%*jz+LXQKZR8eUjcy&$&cT6BQnJE~zw-1i}fQ~R&%lumzD z@;#2#|Nio&q5RO5bmCfGo!1z8eER@PXT|qGRQQX*I^-E2tAuqKha2je*{y#%qzr;e zqeR#B%-G*8%Q?8Viqk0U6&^CQvH0{weoI!yBPSlGwG_mfcM56%i7WS&EV8ZRH*u? zg|>FOC z`mi{OvB$*wfy|W`Vrw(qu8wa`i1XFse~qn3{eDw}Rg(E6+ z+6sn?l&vxcz5sxXQPWotPBN!${%qsL@P+*MXW(D};#I|nzV<0{vyE7U0&g21JTPR* dm2uY>3clG>S#e61MQX{Et*1R>%N(oa<%*K zgwI^%At-)>ZidfSG+A!B;746BlG^pfTPryeR$p$^J7g3~t{j(1z8|C}ZaQ!$Tp%NR zw2H6-R`C=DEP<2azr2WM2F8)V{eS1;-(`;9%lz-I<)1aj-^~4Y_T?XWj(^r1PhmrU zkdb{@0<8Oj>c70WCjpGdl9Q4Bor`~$Iestmzq^)y)*OE`_utu*)7e{l|Q)4u}Zzrw8F{Pv$Q2PcIJ;7R``x%mITI0v}tUjgx7Vb*Vc z`_Gu;1ywTONq^!0jjk3% zRO2De4VRQMy`N>^Z=+M8=etch?_+W+pKr+VxtFEQmk@QM{INBM#E_6fl!MxyL`5pz zac@Kfr-+KMoB!qjipbcxzoaV0YjKW5_mJi#*C@>ufw>SgZy@`j{~_at_nKEBon~*KPgs1x^b36gJn08(oDS zdpThS*p0DF^7hsr)0=;O~*B8p#M7EpZ z)smH2qm*5`p@Ip7Wm_`Npv%4K(}_Eu*@x=8L>fN$VdwRyeEnT zjI)?3T?O4}%s96wLy%U$e#67L9;Lko+dGcLz1ol>zT}Ims$b@S-!I)hj@sEd!XH6s zGS;u!@JBAL4#D}=C^s4JmM(JEY@UH9J=uw)oRXXr#lN4sVjgb^IGV}@7mY1uw9s^T z`p!{32H1b6mm7H^4G)xX#%0`|O$)Enx7jqH2Sp!~ef^@DPc?231ewp}!jqFP>RGqV zeWeSYdB6G>^@)F4bHh53uzDiP{1viRQ<=JUci14-ci*e4Zl{?%LFsTS8nRI zd7Wh(pJ%-(?VY|^qVuS}Uie$jR%or+8#F-j{)! zw@Tys5YW6bWLA$j-vC$L(*p*8b7E#+H31>9`)pGtd-F$pQpz2(`y@N6>X} z>FOY0B$N2d(aelr+1_qSyNV9*T{|khuw|+UjxP<5G}&0DVatav-$F0u)eq9|HrI$W zOgm8kx32;}!MSk%jT4!$;*XhYx0ps+pWoD28tN$6jaw&2nZk*^A=GNVF2ha~FFL)b z;;&Pebx$ra&nW~=yi@!s0Bn_7ZR&7kHSW{A^dPGgGXOED(&BQ{7JwcVCIS8(Nd?M) zj)i0I5l?Uqh|#Z4yL)~nb7s)^C})Z7aY(;J%}N518)mO=NyhU9trRaL#p{>qrlqRQ zP8LzgH9XoQG%6&A`D%rlHCnI9v$cOUI9Yoknt6R?NrAzcs^8?=A6r%H!@?!JEf`{P zfCJNsdCbByV6F(Xk~VGSefa@tCQkjY@vu_UradGY?G#nRy7RRJUI2m=Sl;HGX!BFcKgF&-g}p|f^i9IJf)u!d@^9fuGkp?&*|1Q+o-nYzK` z>+rG6AbdI&;zxzTES;>(JG)z{e&ttSHLb?)iCxPV=ZQVVl=JnAzce!PnV3wZw7=x;U0-sNc;Sx>x0-Iowlfe=xDU=3N+}*01 ztS#y;lYsZuedQ~mLClz=KO5{%<%%4gbLi>k0MaDxgY4?WL0*z$G4*;+$C)uUqx@D}6oi{9U zj>;!!=E}CBK_#hR>+X0MfRL6^fMG~aZsf8Q=Me0;e5hzZ(^J3j=;)R&ds|RlMp37? zE>^EGgA)nZM;#pFtvY2E@CaU^2?d z+o$p=kqWq$na_&_MLBg#WLM!KcQ~`jc@KPc@jE@#yK!@O?Y<8nMxL}A58Ocr`(__V zc%MEumhNXfXHq+Za&?#;83py^h((bz*iCUq ztyN!mSRS*|`Jib|gM87Pn$++u} zA!^1^E76{?*>74?9-Yi?SX+KRdoaTDnH^jhROl`?H6x{yreWv{1S9{7wy;Md8+04z zRX08}?CGmUOSfR;i_bo--CiB~zG0S9a`ct26*aRs!(?>Yp$Kz9S?xuE9If}Bxg|UJ zo%zJRk)eu6K~ulfan}6I3lkYo{W9HrzdW-cl9`crib7{6a(rQwq4{~spI$F?-{ap= zS_g1x3jw4+NZ?G#?jr++*BZmaQQZ94w4)3k8=uC);IDMcfm-*OdEe4N>&^(IC_ye% z(;&kc)4SM{Ubd@QRPwM@o4Equ82umeUICQw?O6E>+tuJcPJuU*VN2>@r`}(W;!oF| z3|&CO7qsgdN^Nmc^|`3X^$OwJDh!lU?RTv%U8-B!Ug#Gvfd7=v*6L%o5L{d@+~)P4 zpCNf$q*(L~!B;t%TyJTZ##okkck?!x9Kd2BWyPVhokm{2bil$|u5lHoZcq92&MZxf z>IET!Yflj}M=n}35k*hpDC$bYwl=|sNJx%cu_;4PjztsFL*mcgOkS|TrjhKf=I*xQ zq5|%kjJ3P^`xpX982K@~IF!I;3AW{|_r>)V`fR{-?=jW!-G)_7sWh3tIlt^~I&yDvkiROtK%frMN!JVZ0uN1)t{ zVdiCW-q)eicq;5$_v+{^1ZRx_V#CYi%(5~o-}ueJ;(Kaph2!uC4nVFGd<1Lq4{#uM zn(hKo-z9xzT~cP;{+c?jSSAk} z#(0x8^y`vP)Ba?(?w|+aTzeYR3Xtn3-fS}7JXR|Xrs?vbzGuKLim-SZ3C2R?ESw!; zQ?NJM#c>IMl|hn43CY?$<$AlOm?-_g9$XR8`i6ylH2dX>!J#=*=2y+yep7BNFBb2-owSPvx8*~1Wr>0 z1=hv(UWJw){!iKm&E(F?FDIo02KiFW#6ogtcTdgatSc^0wkjNj)4P##tk+p?Tg07z zq+qSPYEThQMM=Bs3Ath5A(NHbO?3>?GfUAGFs#ek7qMi!>*KRe$lDmFzP*e{(aPtC zY#f1R4BV+
Osn<*G3=45;gn7aI!neq3-aR?4G+SQB&+6onYk|9LMOg(W0JX1vX zP_YWs*FyE^?Pz_FBmn5B?I*I)!Q>3O#{hJJNhe`|mjd*_2yLAcET+xNMHNtX{(@aK zcYXVInxBm4RX>HTy* z8-QDmSYvvObsfB&9Yi!IzMdZ}<$ZBZ$UYOWgLV3(*q={#E3Im;SQ7j++8PDqq)Wj< zpg2bvMQ;Ow){c|P%6PDN12?#JYTvEC*L(9W=qUh2Y65u{2(Pq?7|7M-&ds#4k@dAR zDo|P5`0^h=8^IkNQ9%W~n}#PHQfs9%@1@hMt20(R@2el<4Uz@`tX=HkPtq1!fHMvgOPw#NVfw_XIyC7a{pE~Nf}bYABT(BpJhdEE2vHuSKa`M0(dD@7`mtrR6abhS6d^^^iy z$YIA=JdXnXs0gY>Rq{-JEi0rkbSDYSyZWqS>KA1USzI{%u)SBt-7SyNz}4AB?)9_A zdNZ+(dL`J)#Z*5#?5K5QsobMCtOKp!&JD_A-%T4DO*+$yTuuHrp{ia&x1xc!~5EDf25SB2*A$( z27-ceXD{)1R$V=fZ|T+?%6E87q^q%$^WN;^mMJBkhNKa0s%ZQo zMJ+&8$L+=pwe^%;vUYPmL@2=QPbZpp zqAzW(9w_M@J@(+Dy}AROh#q){-D~<1n{odVXfLlV7VOwFh^wjfeU@{yIS0?Lc+Mvw ztbWA$#*CO+N z_G{SXaTY#DQ_o;lk-^OoXnt`!921LrmK;=z9N7*@KNT65jl7+_l~oUyqUq=L?dtmK z1Q6d_1v*I%9~s097&yJ~>O=i`HSy|uCRhPyO&-n9LADt(AANRQn_EPV6xS6ONXYNFUidChQi^Ua=ZB=~R*8n!% zCy2eCeE!M`c{_>U!~KOJ1uYBHFH@zfV8?ACKXWaM$Fbd?Z?o1sK=58SK&itr9nW0r z)@8tljT1CQGV9-qkRz6+oJKp`sPhypkUK#lPsEszE&j@lZh+2@<`it~g333w+5n&& z^azu4ZtA&VyNCXM3CW7jrKG;f2(88NsG2fv1>$g3#3)SPahQSvIYnRj@3a@n=m7Xr(q zh}^ncw$>+^6S6f29OYXG$X)bVDaba6vy6euOl_Vo*p%^O)~UmD6vLv$EkOotXi^Rj z1ez#Y+D?CXwH@NE6^nWZH7@nynmx}^S~57-lK>=*!A(BX=5idXjNW>gR;$ z_267aB6S8aTURA!oi{SFCxD9;){=XtR+BQ#xTnt2t>sZVC^yCHany}2wTJ{oMfc;k zK+dAJso?n*+q}f0QaOfT_uDwETU*ZyR>5FDm05cC?m@ikSGA91gpAN9(T{D}=BhyNmislDZ;2TA#*FI$(n*`9?IWI1A88M=u2h6Z$4!+4T%j|8}*VUO>S*g!eP3 zeVwenyALX@RQ2_KOqxCBF-;h|AUC=k#n)m7XAE)F>&SPwYU;qhIy)#@6S;KrC( zCpG0wDa&Ot4HgYrIa_@DWNPI;FF6io<|wu$9!&u1iVbR(_ep__&Jf353+l$PdCY)7 z>0sIzH(#}zcC15{@58jcxRb#QWU}qqxS^bMas_dNQaK`%M$@h89<9%G&(C#0gzQi^ zAeMdxjU*nVOOtWkcfA5o{6v|m;->T>R?{MHXX-of{k9m~y!kvgBT(wCxv z3VQM%=H&nyd1`{&WB==q4Xa(gmT8qSvZn|Wehi!D8mowmKe#$BX1wH_mFJh#?A!{l z-~cKfs8W&E=wTFj!Gp7XNN#|8|%)M@#woPCRKT=dT@=6TB$MU z(I<(yU*}V&0WOKW1<+f50TSAS8`N|puYFylc9YvoIm(#8Xm>h%XGPGsqVM{Pz#|x1 zGZ-scXtHibQSx#=(c|HVC3SK@ZN0@B>#YTTsR#F5WLkN-2vId__Bj0{ziLjrqH$?C zXyPZMpwlm?$+deDV-KX6RW;t|=}_}7M^p}MEe@=lAC#W}$dg-rLn`ZfU7?kbzi)TO&mVXK1OgAcw^&*51Nb;?#Zv_J1_ z8Tib*wA`k{0eZ*uf(ebL?(Np)T3?*fHvZxZfP3tX)DkL4I)tS5#E3I;Ws}}NQBBv< z6%Kn_)A-#w*c@drFYmC@gN;Yo%>a>ri}Jn_->1xLo*ooB_!NCjC#wSTFryfjvXskN<9B7M+oO?vDS4YDPC zBbHY zR4)b*owQ-m%vMKS2Inl1dnlU1L2R`~(Gnu8Iaj0>Ch*;2ZeH&5%%@*%-OG2Ey-xfz z0s?w&>}I%~V=#ASjABGH`dg}uxbMYBe}882X^G4t{z_uSa>XRy6H_qq_MWom(3kxp zt@p|0*o7lN_|JBgnirL>Yiv%V0O}v|mcb1$OswQ!P-y8Htp>lMzaUJodOzgTwF3p% zw+kgpWG$Qiw`W^;s*iY2Z5|ap!}_IGf1d_~?>NWDfwskIbK7+9y|0c2V0D?38+QO4MvRtbJ2|}M&v0LQGQMY3NJ1ip9BfHiOln8Z zU_JR2Er0xUjM>F5Eft#__cul#)n6Q(y{I)g$&ONbc zv*iQ2r|s^(R#uG!i?-S=t#cWhq7xMxDxULq-2G}D7tz)41Lsn40*|hxQ^?n2!W-2h zOf8Strw>_S3?{xMp4&yF2`M^Ze4YK$02w0M@$wV!FKfB_+KL zg{GtnFRJt4CfQNNgQ`znP}MTD`(>1R#T8ReH`z6CzlRn-&)RV9!F*`3hT~iIxiR&Z zC!0a7=g;^^S{(qm&-htaWu}-*94BmTZYC_nZv;OE-Js@j5QqVPL`7j+D2>%?_SZ~e zx5iifmxi9UB#W80-ub3E*TGL(9$YVHYm$akR+OZMRrx6D93q z4xWX(hO@7fySwY%eC}m~M(Yd%f&gu-ml{^2qii_x@;o4l1sJ$s;> z=JUn7dI#|RzEo$rOKz#6yPC^A#y%(9Wwn=ek0FpeoV5~@&oAg1Pz}n4?z^kQte)Ej zZ*Y$nyx;AzQNsWNT4Yu|d7HteZr`7UwDB`lmWjpP)MNtHQzCbi%pWqRY? zTm+OG4A<)jwMJ`Z-Zpurh>v~BumQ>C|D51byn=vn6!u5W9+m}p8hWgl>X_?pg!mR} z$8zJIv_p(o&|i9NKhnH0gcVjc>USc`Q$6|li1s5z7tzKepSm_zU9v-!rrP94Dd5Gt zWE%~uQMv6|IezVBu>xv9@f~`lynF2GlmdS4?wyDIJoB7QCO55SsMFBo9n=Bpr%|If zoHC0sgpI>lr}wm4pnP($jW}K3IPgOM;8M2Q^lJB(XQAQoH-3qOB3q3wHGL;(Yex|? z>A@osc4NrUu^OMYS?JaayglRxQEVao#x9h8I6}TNi91efe_+jOA1kV|VPBNT;}T`PgX@3sQx$_1Y{i*Zg*xdl!ARvTGJ z)tuuv$k|7$6K4b=5iyH5A4F`m`fcZD5b!O6MdTJfT@#3)O@eU9efOHG#^Q2 z0-Gd};2T>tYTAv&6E)w|eaL0s8g?qTvkk*eyROrvS&^xI4Jp;N0RKUPuMAbkiYd|N zN1W(}IlYWTbvka6w3~t=VGv5!_t6l~T~N>ZmCQ>=-)z|U1m^0X_7iW(*G{UssT55j zQw3#qa9OxrWcOfQ(^p#B_WjM=edy=wl6qHH5?;PEUh|y!NkN(I5aot(L37KF=1~Tq z)5er=5-f>^%-!rdyW93sD|hUvRlGfcZ1tS9zS$d!07c48?t532WyYdbLLA2Dj*+ZV zw3SlYgghsnZqaiWY_(tSiRVilOhP{_OH=};_=q)3Yd zIxBYL_+?|NzlPM@G=;_F$sO&y0m5wZflQh4EiQ?J`QpZn#(dM(VCWW?`~E@TorTa6 zST;xSZ78;yk2(!`QvocWvtpnoyP`eoymCi(MD>w`@Z&>QxY%Zs4ct~de1Zu$LTBnz zpi@S3)F_+1u}1IZhdv!a2Ixt~)<04~dYZbS>2EMd(Tb=6Vi%83_6vh0;Vkx7%gr-4 z=hS?{_as&4K5fxHN;TK9u@4Jal@8Upn(S_^j3gcP8&MI6 z<(5W=t9vnRB7ka_MwCDrK&#^{-5$uy?vHF9Bczz4W@p4qTJ-xQMUq<@%BXYvf-|h( zcRaDN`Xfb59~T-YYzeDa0|6mhWh$*7K(^tt&vwqTlnQ3=OIv+b#f8cVc^i-tSx(T! z4A`-qsZ8ki6CEZ>neQ4I56aGnX(IX=17AF&Y;YX4SUheq^T;YbdVm62I-K@shw;Ok z#-!!I^nm65!uT&Gc2cwaFlT{>`np^|?5O4eKk&BE|dPqLn@K>Nr<(iR-e$lCg zk%F`KV#~ItMcS)?B~DbKa$!)uW<|Z`o)onyJ$=bKaj)y5m`a!EdCkyFY4vq&RGIeN znqk7ZE9TgNxNjli6=B}4PTXEx0J5PeaM-LA@cKSiKl|`N)UQDEtDjh*SkPKdnB1d2 zO&4;VthFk}l22~_R4U#E>Xoc!QwRNJTxov}Uj^!_cjMol7(O~Yx`9OSpKtERU9?n{ zDNM#xZMq?>pm!AthCF!&;rOZ3R!d3UsQKOG&Df5{gL&c#6T03VA6;4Zs(oN^v~2%j zy2`LU;Z0~;QKnBv_66$HWLttF(Lh@*Hc`!6Zz%Gb@g_eDCU=*+y(QL2-|5r%F|m(* zBkNOwadPSSr=J67L>0!t;_p?;v5@`o(~pn`DcN(I?KPw^bElwp0C~TBym-HOp;ty` zaO;4<$+4QN7%qPI^nNJHfyC)7viJv|Jv7{M60$vM{cOi84dHEfS2<#Xtp+6~>2!FK zP7O3@0}Vz4@XTN7$*jWN;Y6qa=P323-zLgOTrRwaReQsa*hJ(RkP<9 z{jPHi7A_V|YQ1R9GC?L!JJXk;qt+zfG=bXzo0|tc$WL}!8&pw9vNji^ZGR1y1w`n}PyS}Im`|FKk|kxd4@sSml!kfkB>~pH(YOhFCTL zBHX0+xCp(FmC4>f=&S43$jP+fm1&%)p0Bo39H*OmKc<2+htJ>AhGm8_O4k=e9%B6? zHPq})x^B*YfETnDJQMc4wQ&C}#BFpmg^~4|aa`G51`T9RSBi)qf-oSDU^6Y0cYRXb zr0+MhHDksrAw2X^GmbJneCESW^xWC@_~YL7^waeF0M2fBU8L2`ut*Ve6Dt>UkbgE@ zeT9*pRcSQ_+ir7i;yZ^x>_i`#i_c;dU%{+?a!up8t!k;L(Y8VJ^jjj7)aZyMIA$%x zQgF4i4PYy9!&MG}LO|(w_mag(m2E0_M}2VbYRrj^k8;Tt#?;+$L77Szj207=mMz!^ z1nZJN39{V`m|S_NazaD0lPAM5_GjB1lS0sCI6erP54Aq$wDv`*Pb~ z&c`kD2#U*rtL5132>%>ekAq{18JIYON8=aY>~_oJ40CM6TOG$G(`@sV4rl(9 znoxl+=o)?WGDlgtrFdBQzb!k9-gDs5S}}R*mLt!+`y@kSvSp{Y72%m<^P4bXx>M+w z+qfw9sK|U8${i0Df44XB>$Hdil^PM7gHoOfTPFFi&KJ&wAt~27*|cS=D-0?LtLei6 z2?p!49}3ro=1W$h(Th#fDxbEsf4zI^?k;!o6{5oCV7Wahu@wt}$nM@6cg74NQk7L! ziU~J_nX*cZ^!io)NVaP)ejhe$NjH2B_{1tMBjtoUObmxFVNRLb%K-a)H-cMnatF3ZMI;^tEL8 zOUs_#gHY+l-aW@l?JxI~8#0qdo8&JagJyd7JE>AnIem*wS+!SMPI!10bIaU8v=qX< zndum7q;>bHy`c%%E$)Ci-!mmz_o?qCgd^&K_C}Hy z4g)6!;OncKNW|CrsW-G6^0y4X>N>S9xKbS_(oByZy0A@^i?>GTi{!`QAf=?;T# zx+c4wVP_|+u4byST#Yt!;PY)Ri|$jP%;f%B8-?&obpW~z$Lz-ce#MYJ2Uo(uYN>MPcGl-W2Al zotq(UlRY#@(mIPDf#Q}jcHcQ21w!O+Z2$`QVnESPP#<0iZ5(b@xn&~$5Q~+>gGt2m zwWQ+sXTvkHhH~dD1v=C7`KwDqjqZzQ-P@t0`Sxwo(IrP+>bTr6RoMApOQN{N^W>$W zKY(bN$2XyJIQdTl${k>OM@@$M`f9Z2?~*v&xP^o>yODeC$S6S*(y=KeVRiT;TN%%z z%{gg+c&gRRc$s=9Rdf9R$4La*B2G#!e(oDASPi5B z7eVb-mOI_T0vSEj!`JEDPNWZb2h)XQ4Yg0?<&E(A4$f<*2!6vOe=sv%!#EaFFtLd{ zu6PeyJ@|9@LksSWq`!tyGq&xb?+(;YYWOF38>r9#b<}mDZU=# zayEJBhD4kJ1z`91>o&7wM1`#qJYjYmoNlvBqS~LiI)!=7O zVHxGIB4$9VxKE-;+v2fF1v;b!qep>+Phiqc(~uJKdN zrZ|rd*bXEequu?8BZuq#iDW+oqJTGrWBbyF8^fC@8+mIW?duC#v!0`#(g}#gs!hl5Dq<@BC0O|k7*GKwaT%ArQK8t(}>3o>X$3QH{ zSdNj=WDo4*$bmd6vYoHJD<97uCi5k*0;blZBk*Ee&wy*I%@%lwifq%@ZZBAYOenp# z1yYp9@(cZ9l=l~uFSrMiKG2~vYz3b>V9TVUBmS1YV|^8q!S z&%nwD(xG=IdcUK>XVvEAi?D~uJOO^m|hfqj|jTLY&mv>Q-B~5J`VGYnDu(%YH zp&~JzqDHR~*a<5`{~HAw4>9a*B4LrEfPcC|q6TYq`thC~wM9JqM1zyQIQnc*pzFU0 z3Ly5@Vt->luXxrN{^zL3V!QESGXI8}E7PdR-h83j^c*Jh95qW+!ol`zBIi|uodxP; zaxv%&nv0%ALo;nZF>jt>#j7LW@xlj{q9*aF53m6`rCj}LBK9AFk#JREDc`1XE|npR zeSjUHaZ4y^j`JvAolYm6J-q_ksK{nR8}WXB4*+WNjBVIsA4M&|<4{mTjyCLwYK9*Q{5lXQ!Zk8s zgGGSsNq!LsT9LtEB}VPpB=ueCT%uw3sN&8`93RsS0V|y)GvPbK?xQ-RIp+!?mZJURfd=E+^KO(|P%(1Cz8PYEkAwgskA)(ip~ z0xTR;RC2XD!-~{WvLQeNfNLr18CJ@NJ#c%$yL$V_cdfU9ona^TZ-IT6vZg5kPf-P( z_Y!IVuW3$6wf)4r=_m)tAV$dsFVZlzDu^M4%O`q;O191C5RzUjz)(juF z$;!2`W|(;Z{!!gUw(gI;!_6>r0UW6ABU|@6!wR4`09IX0jsU!2?9c0fOJV8y{{-EP z;r5nc5fk`tSx7vK2;g1>`({@y6XyRhS@KsHj8fcB3u7z`+j8Wu^DDN z#&Ezq{Uch5`&R(YJvWmF;GKE+oq8wXyk`I_@|430gS$OFxoObbiYxKl!*Uf103QQ3 z)UiQtYu$tg&qB1WRGQT{l`R@`}90QFon#?cL!V4lgfy|a|as_tdgdZyTeL` zwG@N39$?whNm=k3iiCi{x*#oDPFh___GEb|SvBDt4wxdg&$ce--vYRbx7@00jsIk; z`nA^@!MZ7cH9I9;<=mjR?a-_lt3lIo>#))}^vJt@gWe{u9yq@fgbhCEtoXdRy0EG2{RM002ovPDHLkV1km2SIhta literal 0 HcmV?d00001 diff --git a/GUI/QtQuick/images/notification_question.png b/GUI/QtQuick/images/notification_question.png new file mode 100644 index 0000000000000000000000000000000000000000..f73f3dbbf46bc7b13109f6c7a9b18dfdac6fcfd8 GIT binary patch literal 1035 zcmV+m1oZofP)cC6|VXoo9>;OsymMgGRLFfuh zS741*5{z-W=8Vo^T@wnJs>_Qj2-HtvevuFmyKnPqN z8TJaSfE-x*rdah_OmWeLY?(y?kN`7a;=I}do0=(M&A0`S`lfj4#FUm~9g^4D^$INY zmp3h}b7SNKjWa*>O_7XBw#=eCB|1SgllZ1sbXmla6|n!&Yhd2iJdTnrvuFyWj^KNG zZeNY;05Jz^>o;u_P)k;c=D;UJo3o+LQ3u_%2Yr0>O>x&W{(x+S=o*N}(B?6eSrqDe z6X2U~iuBl6ucj~MOhf^6$700k9?C459~--;S!Y|eKzM4RF3K#5fu9KJ&^N`VI>vJb z2Z*PF*y!1a0{EuL5htD?&UXOU3=$o9e+p-61C6_D;7U)(J$Ue@)oWz?j~e)yU?ai- zkBi-IN0~(nglwyLSA{yo9pac484{QC9`bS|27mQUv9$fx3}(Pd$T{B?38H~5@X^B)L9@P}@GC z(9Q{L#|LdUJ%rKZkb36Z_nmjmH^r*VB5=soooo%!`B<_~q1h$x z$M$?o!K0GZFmnk!cdf5#WKFAm9-5L35$qt=c8vVuAd-0cFwtaYER2y}TCyA4;ZYNO z2Ovihm@!h!*pUgq#h>Mu5cmaTz9|lJ$o~Pg*WbWa^9%qyO|-Xw8EOh;7A?yxdg+;I zxv)yxJ4|deSZLc=3O7L`fkDF7PzOu2-IL=vov*;{0PU_+2K39|fVZOc5;;&d?mG`a zi$nVri$06^rg-s9F$F&NJq*-V6Mamo@81rEO0m}490nGf_VjyfEX5UbZ6EZEKO4#{ zN|AJRO^sHeLRPhMqmTKy2y67XMQnb4?9SpKH_#mQ5!sHsYyH#}s?jHeXxwB*O;?@? z@LeD8hbAU9@~4Q6UbP_#|F}%n?McyE3%DC2k*-`&x4uwrbMqarfARZ_TwZjFrK-u9 zlDyRJN2D@tmH4#&14Qo^QfHwpl4Rt-s;U_!7wRu&{|+ESc4^aU@vdYmr012z+hB{> zXxf#eMQ5@uG*ZM!1sc>0St0poKE}=DAhPGiNlNik;V(2otYQAKb4CCF002ovPDHLk FV1g+uvp#2XYg z@RMJyOwf3O+8fjpqz}4aAtd}KC~^8bhr>Zgx*t~;bS3UyUtjqw`tp{>nNAZx$&tu0 zwyNxbog5m_m0ItvwRd-oXEUAVz#N!3w+_IIbv*^XI(4?dvQq2vQo9$y3S`02ds|?I z@tE#ur!d~1aeTl%P+S6i7Hp=|DNtI5DvWntf{pt7ngf{>ZGrrpzD~l5H!=l7xlE0RQlXL>Q!yOunQYld5SSmz0(Ud%5P*th*-<~ElkAg0&9vZOL6`(ecf+K7K&3A5aT?uOQB+!OE49o~z4{GUv z13RXj;SZt#KoPDn_@~=JG!SS@QD9?$!Vz|lS=@OIOEz?tB7jbXuYujsU4_<~0h>&x z5CZr7D{FUNysV&s4TC3W0I>Wv_D;)l+2am;R*032B^c)q^zw4W2Vy`X0`}=SL($a zdfTh}O0AQ+JhZIoO~5S=FNOwn>=$;SZz*2KZj>FEQ?hnwnpmynn{IPVrt@^k%r%6> z-jmqR-TNI_iPbu|R}w$bC3fnSfp_!Io5;x4%_C;2)3H4){cDbUU8S#HFzb@du^Md%V5x$z12IRQ44-%m)dAgs zR6(E)s0s{qU>|u>E=vu}@K2DkX7Xk<{NT5EZ}G68?#|~kZp8#{$ZiVkH7}o{ZkFP6Gnf1bPIp2Jpb^k?%F@U7ZFBz`VqR zCNrV7Jgxu)SEB(r`Uk)V09h7ldei`z0r)Y5!$WBh^IFMCzV{3Drv;mt@DhGcM!UMY zEZ+l|v)y!HGZQWVJTv@3Nx3X(7O?&^+fU}%Bwt*fjEp^r%!Da`x6Fj84T|+tq5ZgE zGZO|3|G|?{T;jbs@8?CYv;Ki}2u20V@G6GWFRAjg;TdfA0YFvI#hGUlrl8mke-~rB znEfQ9fXz(k16Tq$dNNW&QV`o80W8_CJFrabu@y!acFDbMw_?#T{4bu2QWr?s&r26N zh!$R5ac@5j4Sxk4P5_=bPJghL^m7^hDmo-hbZGLdVwkorMRdWt8o-w0s9s>Hs5T6f z!nl^xVZ(9TgDr;Nh01U6?t2?Lh?<#xr=rrBz&OsHOx=Dt1XbW}Ws#EJgxCZbFVC`_BCib_k)YU3xy4pET4ynBSrbb#qjb(oUDr`~&A@j$$00000NkvXXu0mjf%_dp7 literal 0 HcmV?d00001 diff --git a/GUI/QtQuick/qml/BsStyles/BSStyle.qml b/GUI/QtQuick/qml/BsStyles/BSStyle.qml new file mode 100644 index 000000000..5fcf78014 --- /dev/null +++ b/GUI/QtQuick/qml/BsStyles/BSStyle.qml @@ -0,0 +1,76 @@ +/* + +*********************************************************************************** +* Copyright (C) 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +pragma Singleton +import QtQuick 2.0 + +//color names taken from http://chir.ag/projects/name-that-color + +QtObject { + readonly property color backgroundColor: "#1c2835" + readonly property color backgroundPressedColor: "#2c3845" + readonly property color backgroundModalColor: "#737373" + readonly property color backgroundModeLessColor: "#939393" + + readonly property color disabledColor: "#41484f" + readonly property color disabledTextColor: "#71787f" + readonly property color disabledBgColor: "#31383f" + + readonly property color textColor: "white" + readonly property color textPressedColor: "#3a8ab4" + readonly property color disabledHeaderColor: "#909090" + + readonly property color labelsTextColor: "#939393" + readonly property color labelsTextDisabledColor: "#454E53" + readonly property color inputsBorderColor: "#757E83" + readonly property color inputsFontColor: "white" + readonly property color inputsInvalidColor: "red" + readonly property color inputsValidColor: "green" + readonly property color inputsPendingColor: "#f6a724" + + readonly property color buttonsMainColor: "transparent" + readonly property color buttonsPressedColor: "#55000000" + readonly property color buttonsHoveredColor: "#22000000" + + readonly property color buttonsPrimaryMainColor: "#247dac" + readonly property color buttonsPrimaryPressedColor: "#22C064" + readonly property color buttonsPrimaryHoveredColor: "#449dcc" + + readonly property color buttonsUncheckedColor: "#81888f" + readonly property color buttonsBorderColor: "#247dac" + + readonly property color progressBarColor: "#22C064" + readonly property color progressBarBgColor: "black" + + readonly property color switchBgColor: "transparent" + //readonly property color switchCheckedColor: "#22C064" + readonly property color switchCheckedColor: "#247dac" + readonly property color switchOrangeColor: "#f6a724" + readonly property color switchUncheckedColor: "#b1b8bf" + readonly property color switchDisabledBgColor: disabledColor + readonly property color switchDisabledColor: disabledTextColor + + readonly property color dialogHeaderColor: "#0A1619" + readonly property color dialogTitleGreenColor: "#38C673" + readonly property color dialogTitleOrangeColor: "#f7b03a" + readonly property color dialogTitleRedColor: "#EE2249" + readonly property color dialogTitleWhiteColor: "white" + + readonly property color comboBoxBgColor: "transparent" + readonly property color comboBoxItemBgColor: "#17262b" + readonly property color comboBoxItemBgHighlightedColor: "#27363b" + readonly property color comboBoxItemTextColor: textColor + readonly property color comboBoxItemTextHighlightedColor: textColor + + readonly property color mainnetColor: "#fe9727" + readonly property color testnetColor: "#22c064" + readonly property color mainnetTextColor: "white" + readonly property color testnetTextColor: "black" +} diff --git a/GUI/QtQuick/qml/BsStyles/qmldir b/GUI/QtQuick/qml/BsStyles/qmldir new file mode 100644 index 000000000..ad71c3261 --- /dev/null +++ b/GUI/QtQuick/qml/BsStyles/qmldir @@ -0,0 +1,3 @@ +module BlockSettleStyle + +singleton BSStyle 1.0 BSStyle.qml diff --git a/GUI/QtQuick/qml/InfoBanner.qml b/GUI/QtQuick/qml/InfoBanner.qml new file mode 100644 index 000000000..7cedd5c37 --- /dev/null +++ b/GUI/QtQuick/qml/InfoBanner.qml @@ -0,0 +1,53 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 + +Loader { + id: messages + property color bgColor + + function displayMessage(message) { + messages.source = ""; + messages.source = Qt.resolvedUrl("InfoBannerComponent.qml"); + messages.item.message = message; + messages.item.bgColor = bgColor + } + + width: parent.width + anchors.bottom: parent.top + z: 5 + onLoaded: { + messages.item.state = "portrait"; + timer.running = true + messages.state = "show" + } + + Timer { + id: timer + + interval: 7000 + onTriggered: { + messages.state = "" + } + } + + states: [ + State { + name: "show" + AnchorChanges { target: messages; anchors { bottom: undefined; top: parent.top } } + PropertyChanges { target: messages; anchors.topMargin: 100 } + } + ] + + transitions: Transition { + AnchorAnimation { easing.type: Easing.OutQuart; duration: 300 } + } +} diff --git a/GUI/QtQuick/qml/InfoBannerComponent.qml b/GUI/QtQuick/qml/InfoBannerComponent.qml new file mode 100644 index 000000000..b43bad00d --- /dev/null +++ b/GUI/QtQuick/qml/InfoBannerComponent.qml @@ -0,0 +1,55 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 + +Item { + id: banner + + property alias message: messageText.text + property alias bgColor: background.color + + height: 70 + + Rectangle { + id: background + + anchors.fill: banner + smooth: true + opacity: 0.8 + } + + Text { + font.pixelSize: 20 + renderType: Text.QtRendering + width: 150 + height: 40 + id: messageText + + + anchors.fill: banner + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + color: "white" + } + + states: State { + name: "portrait" + PropertyChanges { target: banner; height: 100 } + } + + MouseArea { + anchors.fill: parent + onClicked: { + messageText.state = "" + } + } +} diff --git a/GUI/QtQuick/qml/InfoBar.qml b/GUI/QtQuick/qml/InfoBar.qml new file mode 100644 index 000000000..1a96c8cfe --- /dev/null +++ b/GUI/QtQuick/qml/InfoBar.qml @@ -0,0 +1,74 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.12 +import QtQuick.Layouts 1.3 + +import "BsStyles" + +Item { + id: infoBarRoot + height: 30 + + property bool showChangeApplyMessage: false + + RowLayout { + anchors.fill: parent + spacing: 10 + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Text { + visible: infoBarRoot.showChangeApplyMessage + anchors { + fill: parent + leftMargin: 10 + } + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + text: qsTr("Changes will take effect after the application is restarted.") + color: BSStyle.inputsPendingColor + } + } + + Rectangle { + id: netLabel + property bool bInitAsTestNet: signerSettings.testNet + + radius: 5 + color: bInitAsTestNet ? BSStyle.testnetColor : BSStyle.mainnetColor + width: 100 + height: 20 + Layout.alignment: Qt.AlignVCenter + + Text { + text: netLabel.bInitAsTestNet ? qsTr("Testnet") : qsTr("Mainnet") + color: netLabel.bInitAsTestNet ? BSStyle.testnetTextColor : BSStyle.mainnetTextColor + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Component.onCompleted: { + bInitAsTestNet = signerSettings.testNet + } + } + } + + Rectangle { + height: 1 + width: parent.width + color: Qt.rgba(1, 1, 1, 0.1) + anchors.bottom: parent.bottom + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomButton.qml b/GUI/QtQuick/qml/StyledControls/CustomButton.qml new file mode 100644 index 000000000..80adca47a --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomButton.qml @@ -0,0 +1,94 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Button { + id: control + property bool capitalize: true + property bool primary: false + text: parent.text + leftPadding: 15 + rightPadding: 15 + anchors.margins: 5 + + contentItem: Text { + text: control.text + opacity: enabled ? 1.0 : 0.3 + color: BSStyle.textColor + font.capitalization: capitalize ? Font.AllUppercase : Font.MixedCase + font.pixelSize: 11 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + id: rect + implicitWidth: 110 + implicitHeight: 35 + opacity: primary ? 1 : (control.enabled ? 1 : 0.3) + border.color: BSStyle.buttonsBorderColor + color: primary ? BSStyle.buttonsPrimaryMainColor : (control.highlighted ? BSStyle.buttonsPrimaryMainColor : BSStyle.buttonsMainColor) + border.width: primary ? 0 : 1 + } + + states: [ + State { + name: "" + PropertyChanges { + target: rect + opacity: primary ? 1 : (control.enabled ? 1 : 0.3) + color: primary ? BSStyle.buttonsPrimaryMainColor : (control.highlighted ? BSStyle.buttonsPrimaryMainColor : BSStyle.buttonsMainColor) + } + }, + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: rect + opacity: primary ? 0.7 : (control.enabled ? 1 : 0.3) + color: primary ? BSStyle.buttonsPrimaryMainColor : (control.highlighted ? BSStyle.buttonsPrimaryPressedColor : BSStyle.buttonsPressedColor) + } + }, + State { + name: "hovered" + when: control.hovered + PropertyChanges { + target: rect + opacity: primary ? 0.85 : (control.enabled ? 1 : 0.3) + color: primary ? BSStyle.buttonsPrimaryMainColor : (control.highlighted ? BSStyle.buttonsPrimaryHoveredColor : BSStyle.buttonsHoveredColor) + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: rect + opacity: primary ? 0.3 : (control.enabled ? 1 : 0.3) + color: primary ? BSStyle.buttonsPrimaryMainColor : "gray" + } + } + ] + + transitions: [ + Transition { + from: ""; to: "hovered" + ColorAnimation { duration: 100 } + }, + Transition { + from: "*"; to: "pressed" + ColorAnimation { duration: 10 } + } + ] +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomButtonBar.qml b/GUI/QtQuick/qml/StyledControls/CustomButtonBar.qml new file mode 100644 index 000000000..763b98c91 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomButtonBar.qml @@ -0,0 +1,20 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 + +import "../BsStyles" + +Rectangle { + width: parent.width + color:"#55000000" + height: 45 +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomCheckBox.qml b/GUI/QtQuick/qml/StyledControls/CustomCheckBox.qml new file mode 100644 index 000000000..49d89f54b --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomCheckBox.qml @@ -0,0 +1,46 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +CheckBox { + id: control + text: parent.text + + indicator: Rectangle { + implicitWidth: 16 + implicitHeight: 16 + y: parent.height / 2 - height / 2 + radius: 0 + border.color: control.checked ? BSStyle.buttonsBorderColor : BSStyle.buttonsUncheckedColor + color: "transparent" + + Rectangle { + width: 8 + height: 8 + x: 4 + y: 4 + radius: 0 + color: control.checked ? BSStyle.buttonsPrimaryMainColor : BSStyle.buttonsUncheckedColor + visible: control.checked + } + } + + contentItem: Text { + text: control.text + font.pixelSize: 11 + opacity: enabled ? 1.0 : 0.3 + color: control.checked ? BSStyle.textColor : BSStyle.buttonsUncheckedColor + verticalAlignment: Text.AlignVCenter + leftPadding: control.indicator.width + control.spacing + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomComboBox.qml b/GUI/QtQuick/qml/StyledControls/CustomComboBox.qml new file mode 100644 index 000000000..9f1faec97 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomComboBox.qml @@ -0,0 +1,110 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +ComboBox { + id: control + spacing: 3 + rightPadding: 30 // workaround to decrease width of TextInput + property alias maximumLength: input.maximumLength + + contentItem: TextInput { + id: input + text: control.displayText + font: control.font + color: { control.enabled ? BSStyle.comboBoxItemTextHighlightedColor : BSStyle.disabledTextColor } + leftPadding: 7 + rightPadding: control.indicator.width + control.spacing + verticalAlignment: Text.AlignVCenter + clip: true + readOnly: !editable + validator: control.validator + } + + indicator: Canvas { + id: canvas + x: control.width - width + y: control.topPadding + (control.availableHeight - height) / 2 + width: 30 + height: 8 + contextType: "2d" + + Connections { + target: control + onPressedChanged: canvas.requestPaint() + } + + onPaint: { + context.reset(); + context.moveTo(0, 0); + context.lineTo(8,8); + context.lineTo(16, 0); + context.lineTo(15, 0); + context.lineTo(8,7); + context.lineTo(1, 0); + context.closePath(); + context.fillStyle = BSStyle.comboBoxItemTextHighlightedColor; + context.fill(); + } + } + + background: Rectangle { + implicitWidth: 120 + color: { control.enabled ? BSStyle.comboBoxBgColor : BSStyle.disabledBgColor } + implicitHeight: 25 + border.color: { control.enabled ? BSStyle.inputsBorderColor : BSStyle.disabledColor } + border.width: control.visualFocus ? 2 : 1 + radius: 2 + } + + delegate: ItemDelegate { + width: control.width + id: menuItem + + contentItem: Text { + text: modelData + color: menuItem.highlighted ? BSStyle.comboBoxItemTextColor : BSStyle.comboBoxItemTextHighlightedColor + font: control.font + elide: Text.ElideNone + verticalAlignment: Text.AlignVCenter + } + highlighted: control.highlightedIndex === index + + background: Rectangle { + color: menuItem.highlighted ? BSStyle.comboBoxItemBgHighlightedColor : BSStyle.comboBoxItemBgColor + } + } + + popup: Popup { + y: control.height - 1 + width: control.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: control.popup.visible ? control.delegateModel : null + currentIndex: control.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: BSStyle.comboBoxItemBgColor + border.color: BSStyle.inputsBorderColor + radius: 0 + } + } +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomContainer.qml b/GUI/QtQuick/qml/StyledControls/CustomContainer.qml new file mode 100644 index 000000000..cc4df0ef5 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomContainer.qml @@ -0,0 +1,25 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Pane { + width: parent.width + height: parent.height + + Rectangle { + color: "black" + width: parent.width + height: parent.height + } +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomContextMenu.qml b/GUI/QtQuick/qml/StyledControls/CustomContextMenu.qml new file mode 100644 index 000000000..0af639061 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomContextMenu.qml @@ -0,0 +1,61 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +MouseArea { + hoverEnabled: true + acceptedButtons: Qt.RightButton + cursorShape: Qt.IBeamCursor + onClicked: { + if (mouse.button === Qt.RightButton) { + let selectStart = root.selectionStart + let selectEnd = root.selectionEnd + let curPos = root.cursorPosition + contextMenu.popup() + root.cursorPosition = curPos + root.select(selectStart,selectEnd) + } + } + onPressAndHold: { + if (mouse.source === Qt.MouseEventNotSynthesized) { + let selectStart = root.selectionStart + let selectEnd = root.selectionEnd + let curPos = root.cursorPosition + contextMenu.popup() + root.cursorPosition = curPos + root.select(selectStart,selectEnd) + } + } + + Menu { + id: contextMenu + MenuItem { + text: qsTr("Cut") + onTriggered: { + root.cut() + } + } + MenuItem { + text: qsTr("Copy") + onTriggered: { + root.copy() + } + } + MenuItem { + text: qsTr("Paste") + onTriggered: { + root.paste() + } + } + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomDialog.qml b/GUI/QtQuick/qml/StyledControls/CustomDialog.qml new file mode 100644 index 000000000..9995b302e --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomDialog.qml @@ -0,0 +1,287 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.0 +import QtQuick.Window 2.1 +import com.blocksettle.QmlFactory 1.0 +import QtQuick.Controls 1.4 as FirstControl + +import "../BsStyles" +import "../BsControls" +import "../js/helper.js" as JsHelper + +CustomDialogWindow { + id: root + + property bool isPrepared: false + property bool acceptable: false + property bool rejectable: false + property bool abortConfirmation: false + property int abortBoxType + + property int cContentHeight: customContentContainer.height + property int cFooterHeight: customFooterContainer.height + property int cHeaderHeight: customHeaderContainer.height + + property alias customContentContainer: customContentContainer + property alias customFooterContainer: customFooterContainer + property alias customHeaderContainer: customHeaderContainer + property alias runSpinner: busyIndicator.running + +// onCContentHeightChanged: { +// console.log("onCContentHeightChanged " + root + " " + cContentHeight) +// } + +// onCFooterHeightChanged: { +// console.log("onCFooterHeightChanged " + root + " " + cFooterHeight) +// } + +// onCHeaderHeightChanged: { +// console.log("onCHeaderHeightChanged " + root + " " + cHeaderHeight) +// } + + /////////////////// + // suggested to use these functions to close dialog popup with animation + // dialog will be rejected on animatin finished + // or after next dialog in chain will send dialogsChainFinished signal + signal bsAccepted() + signal bsRejected() + + function acceptAnimated(){ + bsAccepted() + closeTimer.start() + closeAnimation.start() + } + + function rejectAnimated(){ + bsRejected() + closeTimer.start() + closeAnimation.start() + } + + function closeAnimated(result){ + if (result) acceptAnimated() + else rejectAnimated() + } + + function hideMainWindow() { + if (applyDialogClosing()) { + rejectAnimated(); + } + } + + // override this function where needed + function applyDialogClosing() { + return true; + } + + property int animationDuration: 100 + + default property alias cContentItem: customContentContainer.data + property alias cHeaderItem: customHeaderContainer.data + property alias cFooterItem: customFooterContainer.data + + signal enterPressed() + + // this signal used in light mode to inform mainwindow if size of dialog is changed + // (for example if it's multipage dialog, or another popup doalog shown above current + signal sizeChanged(int w, int h) + + onWidthChanged: { + //console.log("CustomDialog.qml onWidthChanged " + root + " " + root.width + " " + root.height) + + if (root.width > Screen.desktopAvailableWidth) { + //console.log("CustomDialog.qml Screen width fix") + root.width = Screen.desktopAvailableWidth - 16 + } + sizeChanged(root.width, root.height) + } + onHeightChanged: { + //console.log("CustomDialog.qml onHeightChanged " + root + " " + root.width + " " + root.height) + + if (root.height > Screen.desktopAvailableHeight) { + //console.log("CustomDialog.qml Screen height fix") + let h = qmlFactory.titleBarHeight() + 16 // + extra window margins + root.height = Screen.desktopAvailableHeight - h + } + sizeChanged(root.width, root.height) + } + + //////////////////////////// + /// Dialogs chain management + + // if isNextChainDialogSet then listen next dialog for dialogsChainFinished + property bool isNextChainDialogSet: false + property var nextChainDialog: ({}) + + // when some dialog call second one we should listen second dialog for finished signal + function setNextChainDialog(dialog) { + isNextChainDialogSet = true + nextChainDialog = dialog + nextChainDialogChangedOverloaded(dialog) + dialog.dialogsChainFinished.connect(function(){ + dialogsChainFinished() + reject() + }) + dialog.nextChainDialogChangedOverloaded.connect(function(nextDialog){ + nextChainDialogChangedOverloaded(nextDialog) + }) + } + + signal nextChainDialogChangedOverloaded(var nextDialog) + + // emitted if this is signle dialog and it finished or if dioalgs chain finished + signal dialogsChainFinished() + + Component.onCompleted: { + cContentItem.parent = customContentContainer + cHeaderItem.parent = customHeaderContainer + cFooterItem.parent = customFooterContainer + } + + header: Item{} + footer: Item{} + + onClosed: { + if (!isNextChainDialogSet) { + root.destroy() + } + else { + dialogsChainFinished.connect(function(){ root.destroy() }) + } + } + + onOpened: PropertyAnimation { + id: showAnimation + target: root + property: "opacity"; + duration: animationDuration; + from: 0; to: 1 + } + + //onAboutToHide: closeAnimation + onBsAccepted: closeAnimation + onBsRejected: closeAnimation + + + PropertyAnimation { + id: closeAnimation + target: root + property: "opacity"; + duration: animationDuration; + from: 1; to: 0 + } + + Timer { + // used to close dialog when close animation completed + id: closeTimer + interval: animationDuration + onTriggered: { + if (!isNextChainDialogSet) { + dialogsChainFinished() + reject() + } + else { + reject() + } + } + } + + contentItem: FocusScope { + id: contentItemScope + anchors.fill: parent + anchors.margins: 0 + focus: true +// Layout.alignment: Qt.AlignTop +// Layout.fillHeight: true +// Layout.margins: 0 + + Keys.onPressed: { + event.accepted = true + if (event.modifiers === Qt.ControlModifier) + switch (event.key) { + case Qt.Key_A: + // detailedText.selectAll() + break + case Qt.Key_C: + // detailedText.copy() + break + case Qt.Key_Period: + if (Qt.platform.os === "osx") { + if (rejectable) rejectAnimated() + if (abortConfirmation) JsHelper.openAbortBox(root, abortBoxType) + } + break + } else switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + if (rejectable) rejectAnimated() + if (abortConfirmation) JsHelper.openAbortBox(root, abortBoxType) + break + case Qt.Key_Enter: + case Qt.Key_Return: + if (acceptable) acceptAnimated() + else enterPressed() + break + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 0 + spacing: 0 + Layout.alignment: Qt.AlignTop + Layout.margins: 0 + //Layout.fillHeight: true + clip: true + + ColumnLayout { + id: customHeaderContainer + Layout.alignment: Qt.AlignTop + Layout.margins: 0 + spacing: 0 + clip: true + } + ColumnLayout { + id: customContentContainer + //Layout.fillHeight: true + Layout.alignment: Qt.AlignTop + spacing: 0 + Layout.margins: 0 + clip: true + } + ColumnLayout { + id: customFooterContainer + Layout.alignment: Qt.AlignBottom + spacing: 0 + Layout.margins: 0 + clip: true + } + } + + FirstControl.BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + running: false + + height: 50 + width: 50 + } + } + + Behavior on contentWidth { + NumberAnimation { duration: 20 } + } + Behavior on contentHeight { + NumberAnimation { duration: 20 } + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomDialogWindow.qml b/GUI/QtQuick/qml/StyledControls/CustomDialogWindow.qml new file mode 100644 index 000000000..e476852ae --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomDialogWindow.qml @@ -0,0 +1,31 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +// styled dialog popup +Dialog { + x: (width > parent.width) ? 0 : (parent.width - width) / 2 + y: (height > parent.height) ? 0 : (parent.height - height) / 2 + + focus: true + modal: true + closePolicy: Popup.NoAutoClose + spacing: 0 + margins: 0 + + background: Rectangle { + color: BSStyle.backgroundColor + border.color: BSStyle.dialogHeaderColor + border.pixelAligned: true + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomHeader.qml b/GUI/QtQuick/qml/StyledControls/CustomHeader.qml new file mode 100644 index 000000000..ea72d1bbc --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomHeader.qml @@ -0,0 +1,37 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Button { + leftPadding: 0 + focusPolicy: Qt.NoFocus + + property color textColor: BSStyle.textColor + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: parent.text + font.capitalization: Font.AllUppercase + color: { parent.enabled ? textColor : BSStyle.disabledHeaderColor } + font.pixelSize: 11 + } + + Rectangle { + height: 1 + width: parent.width + color: Qt.rgba(1, 1, 1, 0.1) + anchors.bottom: parent.bottom + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomHeaderPanel.qml b/GUI/QtQuick/qml/StyledControls/CustomHeaderPanel.qml new file mode 100644 index 000000000..8dab2e4bd --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomHeaderPanel.qml @@ -0,0 +1,42 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Rectangle { +// property bool qmlTitleVisible: true //!mainWindow.isLiteMode + property string text + clip: true + color: "transparent" + height: 40 + + Rectangle { +// visible: qmlTitleVisible +// height: qmlTitleVisible ? 40 : 0 + anchors.fill: parent + color: BSStyle.dialogHeaderColor + } + + Text { +// visible: qmlTitleVisible +// height: qmlTitleVisible ? 40 : 0 + anchors.fill: parent + leftPadding: 10 + rightPadding: 10 + + text: parent.text + font.capitalization: Font.AllUppercase + color: BSStyle.textColor + font.pixelSize: 11 + verticalAlignment: Text.AlignVCenter + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomLabel.qml b/GUI/QtQuick/qml/StyledControls/CustomLabel.qml new file mode 100644 index 000000000..088570855 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomLabel.qml @@ -0,0 +1,23 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Label { + horizontalAlignment: Text.AlignHLeft + font.pixelSize: 11 + color: { enabled ? BSStyle.labelsTextColor : BSStyle.disabledColor } + wrapMode: Text.WordWrap + topPadding: 5 + bottomPadding: 5 +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomLabelCopyableValue.qml b/GUI/QtQuick/qml/StyledControls/CustomLabelCopyableValue.qml new file mode 100644 index 000000000..97a33f77c --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomLabelCopyableValue.qml @@ -0,0 +1,58 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 + +import com.blocksettle.QmlFactory 1.0 + +import "../BsStyles" + +Label { + id: root + + property alias mouseArea: mouseArea + property string textForCopy + + font.pixelSize: 11 + color: "white" + wrapMode: Text.WordWrap + padding: 5 + onLinkActivated: Qt.openUrlExternally(link) + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + + acceptedButtons: Qt.RightButton + onClicked: { + if (mouse.button === Qt.RightButton) { + contextMenu.popup() + } + } + onPressAndHold: { + if (mouse.source === Qt.MouseEventNotSynthesized) { + contextMenu.popup() + } + } + + Menu { + id: contextMenu + MenuItem { + text: qsTr("Copy") + onTriggered: { + qmlFactory.setClipboard(root.textForCopy.length > 0 ? root.textForCopy : root.text) + } + } + } + } +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomLabelValue.qml b/GUI/QtQuick/qml/StyledControls/CustomLabelValue.qml new file mode 100644 index 000000000..3b021ddaf --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomLabelValue.qml @@ -0,0 +1,22 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Label { + font.pixelSize: 11 + color: "white" + wrapMode: Text.WordWrap + topPadding: 5 + bottomPadding: 5 + onLinkActivated: Qt.openUrlExternally(link) +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomPasswordTextInput.qml b/GUI/QtQuick/qml/StyledControls/CustomPasswordTextInput.qml new file mode 100644 index 000000000..ef0f05b1e --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomPasswordTextInput.qml @@ -0,0 +1,50 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + + +TextField { + property bool allowShowPass: true + + horizontalAlignment: Text.AlignHLeft + font.pixelSize: 11 + color: BSStyle.inputsFontColor + padding: 0 + echoMode: button.pressed ? TextInput.Normal : TextInput.Password + selectByMouse: false + + background: Rectangle { + implicitWidth: 200 + implicitHeight: 25 + color:"transparent" + border.color: BSStyle.inputsBorderColor + + Button { + id: button + visible: allowShowPass + contentItem: Rectangle { + color: "transparent" + Image { + fillMode: Image.PreserveAspectFit + anchors.fill: parent + source: "qrc:/resources/eye.png" + } + } + padding: 2 - (button.pressed ? 1 : 0) + background: Rectangle {color: "transparent"} + anchors.right: parent.right + width: 23 + height: 23 + } + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomProgressBar.qml b/GUI/QtQuick/qml/StyledControls/CustomProgressBar.qml new file mode 100644 index 000000000..08387d148 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomProgressBar.qml @@ -0,0 +1,40 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +ProgressBar { + id: control + value: 0.5 + topPadding: 1 + bottomPadding: 1 + + background: Rectangle { + implicitWidth: 200 + implicitHeight: 6 + color: BSStyle.progressBarBgColor + radius: 3 + } + + contentItem: Item { + implicitWidth: 200 + implicitHeight: 4 + + Rectangle { + width: control.visualPosition * parent.width + height: parent.height + radius: 2 + color: BSStyle.progressBarColor + } + } +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomRadioButton.qml b/GUI/QtQuick/qml/StyledControls/CustomRadioButton.qml new file mode 100644 index 000000000..87f4abaa4 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomRadioButton.qml @@ -0,0 +1,48 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +RadioButton { + id: control + text: parent.text + focusPolicy: Qt.NoFocus + + indicator: Rectangle { + implicitWidth: 16 + implicitHeight: 16 + x: control.leftPadding + y: parent.height / 2 - height / 2 + radius: 11 + border.color: control.checked ? BSStyle.buttonsBorderColor : BSStyle.buttonsUncheckedColor + color: "transparent" + + Rectangle { + width: 8 + height: 8 + x: 4 + y: 4 + radius: 7 + color: control.checked ? BSStyle.buttonsPrimaryMainColor : BSStyle.buttonsUncheckedColor + visible: control.checked + } + } + + contentItem: Text { + text: control.text + font: control.font + opacity: enabled ? 1.0 : 0.3 + color: control.checked ? BSStyle.textColor : BSStyle.buttonsUncheckedColor + verticalAlignment: Text.AlignVCenter + leftPadding: control.indicator.width + control.spacing + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomSwitch.qml b/GUI/QtQuick/qml/StyledControls/CustomSwitch.qml new file mode 100644 index 000000000..df19894c7 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomSwitch.qml @@ -0,0 +1,130 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +Switch { + id: control + text: parent.text + checked: true + + contentItem: Text { + rightPadding: control.indicator.width + control.spacing + text: control.text + font: control.font + opacity: enabled ? 1.0 : 0.3 + color: control.checked ? BSStyle.switchCheckedColor : (signerStatus.socketOk ? BSStyle.switchUncheckedColor : BSStyle.switchOrangeColor) + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + indicator: Rectangle { + id: border_ + implicitWidth: 40 + implicitHeight: 20 + x: control.width - width - control.rightPadding + y: parent.height / 2 - height / 2 + radius: 10 + color: { + if (control.enabled) { + if (control.checked) { + return BSStyle.switchCheckedColor + } + else { + return BSStyle.switchBgColor + } + } + else { + return BSStyle.switchDisabledBgColor + + } + } + border.color: { + if (control.enabled) { + if (control.checked) { + return BSStyle.switchCheckedColor + } + else { + return BSStyle.switchUncheckedColor + } + } + else { + return BSStyle.switchDisabledBgColor + } + } + Rectangle { + id: circle_ + x: control.checked ? parent.width - width : 0 + width: 20 + height: 20 + radius: 10 + + color: { + if (control.enabled) { + if (control.checked) { + return BSStyle.textColor + } + else { + return BSStyle.switchUncheckedColor + } + } + else { + return "#71787f" + + } + } + + border.color: { + if (control.enabled) { + if (control.checked) { + return BSStyle.switchCheckedColor + } + else { + return BSStyle.backgroundColor + } + } + else { + return BSStyle.switchDisabledBgColor + } + } + } + } + + background: Rectangle { + implicitWidth: 80 + implicitHeight: 20 + visible: control.down + color: BSStyle.switchBgColor + } + +// states: [ +// State { +// name: "checked" +// when: control.checked +// }, +// State { +// name: "uncheked" +// when: !control.checked +// } +// ] + +// transitions: [ +// Transition { +// to: "checked" +// NumberAnimation { +// target: circle_ +// properties: "color" +// duration: 300 +// } +// } +// ] +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomTabButton.qml b/GUI/QtQuick/qml/StyledControls/CustomTabButton.qml new file mode 100644 index 000000000..0abd48a18 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomTabButton.qml @@ -0,0 +1,46 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +TabButton { + id: control + text: parent.text + property alias cText: text_ + focusPolicy: Qt.NoFocus + + contentItem: Text { + id: text_ + text: control.text + font.capitalization: Font.AllUppercase + font.pointSize: 10 + color: control.checked ? (control.down ? BSStyle.textPressedColor : BSStyle.textColor) : BSStyle.buttonsUncheckedColor + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + implicitWidth: 100 + implicitHeight: 50 + opacity: enabled ? 1 : 0.3 + color: control.checked ? (control.down ? BSStyle.backgroundPressedColor : BSStyle.backgroundColor) : "0f1f24" + + Rectangle { + width: parent.width + height: 2 + color: control.checked ? (control.down ? BSStyle.textPressedColor : BSStyle.buttonsPrimaryMainColor) : "transparent" + anchors.top: parent.top + } + } +} + diff --git a/GUI/QtQuick/qml/StyledControls/CustomTextArea.qml b/GUI/QtQuick/qml/StyledControls/CustomTextArea.qml new file mode 100644 index 000000000..0654d4621 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomTextArea.qml @@ -0,0 +1,32 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import "../BsStyles" + +TextArea { + id: root + horizontalAlignment: Text.AlignHLeft + font.pixelSize: 11 + color: "white" + wrapMode: TextEdit.WordWrap + + background: Rectangle { + implicitWidth: 200 + implicitHeight: 50 + color:"transparent" + border.color: BSStyle.inputsBorderColor + } + + CustomContextMenu { + anchors.fill: parent + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomTextInput.qml b/GUI/QtQuick/qml/StyledControls/CustomTextInput.qml new file mode 100644 index 000000000..644c77d7c --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomTextInput.qml @@ -0,0 +1,34 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 + +import "../BsStyles" + +TextField { + id: root + horizontalAlignment: Text.AlignHLeft + font.pixelSize: 11 + color: BSStyle.inputsFontColor + padding: 0 + selectByMouse: true + + background: Rectangle { + implicitWidth: 200 + implicitHeight: 25 + color: "transparent" + border.color: BSStyle.inputsBorderColor + } + + CustomContextMenu { + anchors.fill: parent + } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindow.qml b/GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindow.qml new file mode 100644 index 000000000..b8918134a --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindow.qml @@ -0,0 +1,47 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.0 + +import "../BsStyles" + +// dialog window with header +CustomDialog { + id: root + // property bool qmlTitleVisible: true //: !mainWindow.isLiteMode + property alias headerPanel: headerPanel + property bool fixedHeight: false + property var customHeader: ColumnLayout { + id: layout + spacing: 0 + Layout.alignment: Qt.AlignTop + Layout.margins: 0 + + CustomHeaderPanel { + id: headerPanel + Layout.fillWidth: true + //qmlTitleVisible: root.qmlTitleVisible + text: root.title + } + } + + height: fixedHeight ? undefined: cHeaderHeight + cContentHeight + cFooterHeight + + function isApplicationWindow(item) { + return item instanceof ApplicationWindow + } + + cHeaderItem: customHeader +// onNextChainDialogChangedOverloaded: { +// nextDialog.qmlTitleVisible = qmlTitleVisible +// } +} diff --git a/GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindowWithExpander.qml b/GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindowWithExpander.qml new file mode 100644 index 000000000..dc09e8ce5 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/CustomTitleDialogWindowWithExpander.qml @@ -0,0 +1,89 @@ +/* + +*********************************************************************************** +* Copyright (C) 2018 - 2020, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2.9 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.0 + +import "../BsStyles" +import "../BsControls" + +BSWalletHandlerDialog { + id: root + + property string headerButtonText: "" + signal headerButtonClicked() + + customHeader: ColumnLayout { + id: layout + spacing: 0 + Layout.alignment: Qt.AlignTop + Layout.margins: 0 + + Rectangle { + id: rect + property string text : root.title + clip: true + color: "transparent" + height: 40 + + Layout.fillWidth: true + + Rectangle { + anchors.fill: rect + color: BSStyle.dialogHeaderColor + } + + Text { + anchors.fill: rect + leftPadding: 10 + rightPadding: 10 + + text: rect.text + font.capitalization: Font.AllUppercase + color: BSStyle.textColor + font.pixelSize: 11 + verticalAlignment: Text.AlignVCenter + } + + Button { + id: btnExpand + width: 100 + anchors.right: rect.right + anchors.top: rect.top + anchors.bottom: rect.bottom + + contentItem: Text { + text: headerButtonText + color: BSStyle.textColor + font.pixelSize: 11 + font.underline: true + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + elide: Text.ElideNone + } + + background: Rectangle { + implicitWidth: 70 + implicitHeight: 35 + color: "transparent" + } + + MouseArea { + anchors.fill: parent + enabled: false + cursorShape: Qt.PointingHandCursor + } + + onClicked: headerButtonClicked() + } + } + } +} diff --git a/GUI/QtQuick/qml/StyledControls/qmldir b/GUI/QtQuick/qml/StyledControls/qmldir new file mode 100644 index 000000000..c5f3cae76 --- /dev/null +++ b/GUI/QtQuick/qml/StyledControls/qmldir @@ -0,0 +1,23 @@ +module CustomControls + +CustomButton 1.0 CustomButton.qml +CustomButtonBar 1.0 CustomButtonBar.qml +CustomCheckBox 1.0 CustomCheckBox.qml +CustomComboBox 1.0 CustomComboBox.qml +CustomContainer 1.0 CustomContainer.qml +CustomDialogWindow 1.0 CustomDialogWindow.qml +CustomDialog 1.0 CustomDialog.qml +CustomHeader 1.0 CustomHeader.qml +CustomHeaderPanel 1.0 CustomHeaderPanel.qml +CustomLabel 1.0 CustomLabel.qml +CustomLabelValue 1.0 CustomLabelValue.qml +CustomLabelCopyableValue 1.0 CustomLabelCopyableValue.qml +CustomProgressBar 1.0 CustomProgressBar.qml +CustomRadioButton 1.0 CustomRadioButton.qml +CustomSwitch 1.0 CustomSwitch.qml +CustomTabButton 1.0 CustomTabButton.qml +CustomTextArea 1.0 CustomTextArea.qml +CustomTextInput 1.0 CustomTextInput.qml +CustomPasswordTextInput 1.0 CustomPasswordTextInput.qml +CustomTitleDialogWindow 1.0 CustomTitleDialogWindow.qml +CustomTitleDialogWindowWithExpander 1.0 CustomTitleDialogWindowWithExpander.qml diff --git a/GUI/QtQuick/qml/main.qml b/GUI/QtQuick/qml/main.qml new file mode 100644 index 000000000..0d9e070aa --- /dev/null +++ b/GUI/QtQuick/qml/main.qml @@ -0,0 +1,135 @@ +/* + +*********************************************************************************** +* Copyright (C) 2022, BlockSettle AB +* Distributed under the GNU Affero General Public License (AGPL v3) +* See LICENSE or http://www.gnu.org/licenses/agpl.html +* +********************************************************************************** + +*/ +import QtQuick 2 +import QtQuick.Controls 2 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2 +import "StyledControls" 1 +import "BsStyles" 1 +//import Qt.labs.settings 1.0 + +/* +import "BsControls" +import "BsDialogs" +import "js/helper.js" as JsHelper +*/ + +ApplicationWindow { + id: mainWindow + width: 800 + height: 600 + + visible: false + title: qsTr("BlockSettle Terminal") + + property var currentDialog: ({}) + readonly property int resizeAnimationDuration: 25 + + Component.onCompleted: { + mainWindow.flags = Qt.CustomizeWindowHint | Qt.MSWindowsFixedSizeDialogHint | + Qt.Dialog | Qt.WindowSystemMenuHint | + Qt.WindowTitleHint | Qt.WindowCloseButtonHint + hide() +// qmlFactory.installEventFilterToObj(mainWindow) +// qmlFactory.applyWindowFix(mainWindow) + } + + color: BSStyle.backgroundColor + + overlay.modal: Rectangle { + color: BSStyle.backgroundModalColor + } + overlay.modeless: Rectangle { + color: BSStyle.backgroundModeLessColor + } + + // attached to use from c++ + function messageBoxCritical(title, text, details) { + return JsHelper.messageBoxCritical(title, text, details) + } + + InfoBanner { + id: ibSuccess + bgColor: "darkgreen" + } + InfoBanner { + id: ibFailure + bgColor: "darkred" + } + +/* function raiseWindow() { + JsHelper.raiseWindow(mainWindow) + } + function hideWindow() { + JsHelper.hideWindow(mainWindow) + } + + function customDialogRequest(dialogName, data) { + var newDialog = JsHelper.customDialogRequest(dialogName, data) + if (newDialog) { + raiseWindow() + JsHelper.prepareDialog(newDialog) + } + } + + function invokeQmlMethod(method, cppCallback, argList) { + JsHelper.evalWorker(method, cppCallback, argList) + }*/ + + function moveMainWindowToScreenCenter() { + mainWindow.x = Screen.virtualX + (Screen.width - mainWindow.width) / 2 + mainWindow.y = Screen.virtualY + (Screen.height - mainWindow.height) / 2 + } + + function resizeAnimated(w,h) { + mwWidthAnimation.from = mainWindow.width + mwWidthAnimation.to = w + mwWidthAnimation.restart() + + mwHeightAnimation.from = mainWindow.height + mwHeightAnimation.to = h + mwHeightAnimation.restart() + + mwXAnimation.from = mainWindow.x + mwXAnimation.to = Screen.virtualX + (Screen.width - w) / 2 + mwXAnimation.restart() + + mwYAnimation.from = mainWindow.y + mwYAnimation.to = Screen.virtualY + (Screen.height - h) / 2 + mwYAnimation.restart() + } + + NumberAnimation { + id: mwWidthAnimation + target: mainWindow + property: "width" + duration: resizeAnimationDuration + } + NumberAnimation { + id: mwHeightAnimation + target: mainWindow + property: "height" + duration: resizeAnimationDuration + } + + NumberAnimation { + id: mwXAnimation + target: mainWindow + property: "x" + duration: resizeAnimationDuration + } + NumberAnimation { + id: mwYAnimation + target: mainWindow + property: "y" + duration: resizeAnimationDuration + } +} diff --git a/GUI/QtQuick/qtquick.qrc b/GUI/QtQuick/qtquick.qrc new file mode 100644 index 000000000..22130886b --- /dev/null +++ b/GUI/QtQuick/qtquick.qrc @@ -0,0 +1,29 @@ + + + images/full_logo.png + images/bs_logo.png + qml/main.qml + qml/InfoBanner.qml + qml/InfoBannerComponent.qml + qml/InfoBar.qml + qml/StyledControls/CustomButton.qml + qml/StyledControls/CustomButtonBar.qml + qml/StyledControls/CustomCheckBox.qml + qml/StyledControls/CustomComboBox.qml + qml/StyledControls/CustomContainer.qml + qml/StyledControls/CustomDialog.qml + qml/StyledControls/CustomHeader.qml + qml/StyledControls/CustomHeaderPanel.qml + qml/StyledControls/CustomLabel.qml + qml/StyledControls/CustomLabelValue.qml + qml/StyledControls/CustomProgressBar.qml + qml/StyledControls/CustomRadioButton.qml + qml/StyledControls/CustomSwitch.qml + qml/StyledControls/CustomTabButton.qml + qml/StyledControls/CustomTextArea.qml + qml/StyledControls/CustomTextInput.qml + qml/StyledControls/qmldir + qml/BsStyles/BSStyle.qml + qml/BsStyles/qmldir + + diff --git a/GUI/QtWidgets/QtGuiAdapter.cpp b/GUI/QtWidgets/QtGuiAdapter.cpp index 41969d462..b494fe42b 100644 --- a/GUI/QtWidgets/QtGuiAdapter.cpp +++ b/GUI/QtWidgets/QtGuiAdapter.cpp @@ -39,20 +39,6 @@ using namespace BlockSettle::Common; using namespace BlockSettle::Terminal; using namespace bs::message; -#if 0 -Q_DECLARE_METATYPE(bs::error::AuthAddressSubmitResult) -Q_DECLARE_METATYPE(std::string) -Q_DECLARE_METATYPE(std::vector) -Q_DECLARE_METATYPE(std::vector); -Q_DECLARE_METATYPE(bs::PayoutSignatureType) -Q_DECLARE_METATYPE(bs::network::Asset::Type) -Q_DECLARE_METATYPE(bs::network::MDField) -Q_DECLARE_METATYPE(bs::network::MDFields) -Q_DECLARE_METATYPE(bs::network::SecurityDef) -Q_DECLARE_METATYPE(bs::network::CCSecurityDef) -Q_DECLARE_METATYPE(bs::PayoutSignatureType) -#endif - #if defined (Q_OS_MAC) class MacOsApp : public QApplication { @@ -203,25 +189,6 @@ void QtGuiAdapter::run(int &argc, char **argv) return; } -#if 0 - qRegisterMetaType(); - qRegisterMetaType>(); - qRegisterMetaType(); - qRegisterMetaType>(); - qRegisterMetaType>(); - qRegisterMetaType("AssetType"); - qRegisterMetaType("Quote"); - qRegisterMetaType("Order"); - qRegisterMetaType("SecurityDef"); - qRegisterMetaType("MDField"); - qRegisterMetaType("MDFields"); - qRegisterMetaType("CCSecurityDef"); - qRegisterMetaType("NewTrade"); - qRegisterMetaType("NewPMTrade"); - qRegisterMetaType(); - qRegisterMetaType(); -#endif - QString logoIcon; logoIcon = QLatin1String(":/SPLASH_LOGO"); @@ -330,8 +297,6 @@ bool QtGuiAdapter::processSettings(const Envelope &env) return processSettingsState(msg.state()); case SettingsMessage::kArmoryServers: return processArmoryServers(msg.armory_servers()); - case SettingsMessage::kSignerServers: - return processSignerServers(msg.signer_servers()); default: break; } return true; @@ -420,19 +385,6 @@ bool QtGuiAdapter::processArmoryServers(const SettingsMessage_ArmoryServers& res }); } -bool QtGuiAdapter::processSignerServers(const SettingsMessage_SignerServers& response) -{ - QList servers; - for (const auto& server : response.servers()) { - servers << SignerHost{ QString::fromStdString(server.name()) - , QString::fromStdString(server.host()), std::stoi(server.port()) - , QString::fromStdString(server.key()) }; - } - return QMetaObject::invokeMethod(mainWindow_, [mw = mainWindow_, servers, response] { - mw->onSignerSettings(servers, response.own_key(), response.idx_current()); - }); -} - bool QtGuiAdapter::processAdminMessage(const Envelope &env) { AdministrativeMessage msg; @@ -443,6 +395,7 @@ bool QtGuiAdapter::processAdminMessage(const Envelope &env) switch (msg.data_case()) { case AdministrativeMessage::kComponentCreated: switch (static_cast(msg.component_created())) { + case TerminalUsers::Unknown: case TerminalUsers::API: case TerminalUsers::Settings: break; diff --git a/GUI/QtWidgets/QtGuiAdapter.h b/GUI/QtWidgets/QtGuiAdapter.h index 927c6d976..9ac8fd566 100644 --- a/GUI/QtWidgets/QtGuiAdapter.h +++ b/GUI/QtWidgets/QtGuiAdapter.h @@ -90,7 +90,6 @@ class QtGuiAdapter : public QObject, public ApiBusAdapter, public bs::MainLoopRu bool processSettingsGetResponse(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); bool processSettingsState(const BlockSettle::Terminal::SettingsMessage_SettingsResponse&); bool processArmoryServers(const BlockSettle::Terminal::SettingsMessage_ArmoryServers&); - bool processSignerServers(const BlockSettle::Terminal::SettingsMessage_SignerServers&); bool processAdminMessage(const bs::message::Envelope &); bool processBlockchain(const bs::message::Envelope &); bool processSigner(const bs::message::Envelope &); diff --git a/common b/common index 1ce16aa45..7a79d2caa 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1ce16aa45b5556d67f46ab8f0ef0bd3d998c8d8f +Subproject commit 7a79d2caa5f8541efeeb11ab3861df1dbe7cf8ba